Skip to content

Commit

Permalink
Do custom env validation for custom deploys
Browse files Browse the repository at this point in the history
  • Loading branch information
WPprodigy committed May 4, 2024
1 parent 1f4c9ee commit de9bf64
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 64 deletions.
78 changes: 25 additions & 53 deletions src/bin/vip-app-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import gql from 'graphql-tag';
/**
* Internal dependencies
*/
import { App, AppEnvironment, AppEnvironmentCustomDeployInput } from '../graphqlTypes';
import { AppEnvironmentCustomDeployInput } from '../graphqlTypes';
import API from '../lib/api';
import command from '../lib/cli/command';
import * as exit from '../lib/cli/exit';
Expand All @@ -23,37 +23,9 @@ import {
WithId,
UploadArguments,
} from '../lib/client-file-uploader';
import {
isSupportedApp,
validateCustomDeployKey,
validateFile,
} from '../lib/custom-deploy/custom-deploy';
import { validateCustomDeployKey, validateFile } from '../lib/custom-deploy/custom-deploy';
import { trackEventWithEnv } from '../lib/tracker';

const appQuery = `
id,
name,
type,
typeId
organization { id, name },
environments{
id
appId
type
name
launched
isK8sResident
syncProgress { status }
primaryDomain { name }
wpSites {
nodes {
homeUrl
id
}
}
}
`;

const START_DEPLOY_MUTATION = gql`
mutation StartCustomDeploy($input: AppEnvironmentCustomDeployInput) {
startCustomDeploy(input: $input) {
Expand Down Expand Up @@ -108,29 +80,31 @@ export async function promptToContinue( params: PromptToContinueParams ) {
}

export async function appDeployCmd( arg: string[] = [], opts: Record< string, unknown > = {} ) {
const app = opts.app as App;
const env = opts.env as AppEnvironment;
const app = opts.app as string | number;
const env = opts.env as string | number;

const [ fileName ] = arg;
const fileMeta = await getFileMeta( fileName );
const inputBasename = fileMeta.basename;
const fileInputBasename = fileMeta.basename;

debug( 'Options: ', opts );
debug( 'Args: ', arg );

const appId = env.appId as number;
const envId = env.id as number;
debug( 'Validating custom deploy key...' );
const { appId, envId, ...validatedArgs } = await validateCustomDeployKey( app, env );
console.log( { appId, envId, validatedArgs } );

const track = trackEventWithEnv.bind( null, appId, envId );

if ( ! isSupportedApp( app ) ) {
if ( ! validatedArgs.customDeploysEnabled ) {
await track( 'deploy_app_command_error', { error_type: 'unsupported-app' } );
exit.withError( 'The type of application you specified does not currently support deploys.' );
exit.withError(
'The type of application you specified does not currently support custom deploys.'
);
}

debug( 'Validating custom deploy key if present...' );
await validateCustomDeployKey( envId );

await validateFile( app, env, fileMeta );
debug( 'Validating file...' );
await validateFile( appId, envId, fileMeta );

await track( 'deploy_app_command_execute' );

Expand All @@ -145,13 +119,12 @@ export async function appDeployCmd( arg: string[] = [], opts: Record< string, un
const deployMessage = ( opts.message as string ) ?? '';
const forceDeploy = opts.force;

const domain = env?.primaryDomain?.name ? env.primaryDomain.name : `#${ env.id }`;
if ( ! forceDeploy ) {
const promptParams: PromptToContinueParams = {
launched: Boolean( env.launched ),
formattedEnvironment: formatEnvironment( env.type as string ),
launched: Boolean( validatedArgs.launched ),
formattedEnvironment: formatEnvironment( validatedArgs.envType ),
track,
domain,
domain: validatedArgs.primaryDomainName,
};

await promptToContinue( promptParams );
Expand Down Expand Up @@ -220,8 +193,8 @@ Processing the file for deployment to your environment...
} = await uploadImportSqlFileToS3( uploadParams );

startDeployVariables.input = {
id: app.id,
environmentId: env.id,
id: appId,
environmentId: envId,
basename: fileMeta.basename,
checksum,
deployMessage,
Expand Down Expand Up @@ -275,12 +248,12 @@ Processing the file for deployment to your environment...
progressTracker.suffix = '';
progressTracker.print( { clearAfter: true } );

const deploymentsUrl = `https://dashboard.wpvip.com/apps/${ appId }/${ env.type }/code/deployments`;
const deploymentsUrl = `https://dashboard.wpvip.com/apps/${ appId }/${ validatedArgs.envType }/code/deployments`;
console.log(
`\n✅ ${ chalk.bold(
chalk.underline( chalk.magenta( inputBasename ) )
chalk.underline( chalk.magenta( fileInputBasename ) )
) } has been sent for deployment to ${ chalk.bold(
chalk.blue( domain )
chalk.blue( validatedArgs.primaryDomainName )
) }. \nTo check deployment status, go to ${ chalk.bold(
'VIP Dashboard'
) }: ${ deploymentsUrl }`
Expand All @@ -305,12 +278,11 @@ const examples = [
];

void command( {
appContext: true,
appQuery,
envContext: true,
requiredArgs: 1,
} )
.examples( examples )
.option( 'message', 'Custom message for deploy' )
.option( 'force', 'Skip prompt' )
.option( 'app', 'The application name or ID' )
.option( 'env', 'The environment name or ID' )
.argv( process.argv, appDeployCmd );
49 changes: 38 additions & 11 deletions src/lib/custom-deploy/custom-deploy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs';
import gql from 'graphql-tag';

import { App, AppEnvironment } from '../../graphqlTypes';
import { App } from '../../graphqlTypes';
import API from '../../lib/api';
import * as exit from '../../lib/cli/exit';
import { checkFileAccess, getFileSize, isFile, FileMeta } from '../../lib/client-file-uploader';
Expand All @@ -13,47 +13,74 @@ import { validateDeployFileExt, validateFilename } from '../../lib/validations/c
const DEPLOY_MAX_FILE_SIZE = 4 * GB_IN_BYTES;
const WPVIP_DEPLOY_TOKEN = process.env.WPVIP_DEPLOY_TOKEN;

type CustomDeployInfo = {
success: boolean;
appId: number;
envId: number;
envType: string;
primaryDomainName: string;
launched: boolean;
customDeploysEnabled: boolean;
};

type ValidateMutationPayload = {
data?: {
validateCustomDeployAccess: CustomDeployInfo;
} | null;
};

export function isSupportedApp( app: App ): boolean {
return WORDPRESS_SITE_TYPE_IDS.includes( app.typeId as number );
}

export async function validateCustomDeployKey( envId: number ): Promise< void > {
export async function validateCustomDeployKey(
app: string | number,
env: string | number
): Promise< CustomDeployInfo > {
if ( ! WPVIP_DEPLOY_TOKEN ) {
exit.withError( 'Valid custom deploy key is required.' );
}

const VALIDATE_CUSTOM_DEPLOY_ACCESS_MUTATION = gql`
mutation ValidateCustomDeployAccess {
validateCustomDeployAccess( input: { environmentIds: ${ envId } } ) {
success
validateCustomDeployAccess( input: { app: "${ String( app ) }", env: "${ String( env ) }" } ) {
success,
appId,
envId,
envType,
primaryDomainName,
launched,
customDeploysEnabled
}
}
`;

const api = API();
try {
await api.mutate( {
const result: ValidateMutationPayload = await api.mutate( {
mutation: VALIDATE_CUSTOM_DEPLOY_ACCESS_MUTATION,
context: {
headers: {
Authorization: `Bearer ${ WPVIP_DEPLOY_TOKEN }`,
},
},
} );

if ( ! result.data?.validateCustomDeployAccess ) {
throw new Error( 'Not found' );
}

return result.data?.validateCustomDeployAccess;
} catch ( error ) {
exit.withError(
`Unauthorized: Invalid or non-existent custom deploy key for environment ${ envId }.`
);
exit.withError( `Unauthorized: Invalid or non-existent custom deploy key for environment.` );
}
}

/**
* @param {FileMeta} fileMeta
*/
export async function validateFile( app: App, env: AppEnvironment, fileMeta: FileMeta ) {
export async function validateFile( appId: number, envId: number, fileMeta: FileMeta ) {
const { fileName, basename, isCompressed } = fileMeta;
const appId = env.appId as number;
const envId = env.id as number;
const track = trackEventWithEnv.bind( null, appId, envId );

if ( ! fs.existsSync( fileName ) ) {
Expand Down

0 comments on commit de9bf64

Please sign in to comment.