Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vip app deploy - Allow passing in a deploy key with env variable #1718

Merged
merged 13 commits into from
Mar 6, 2024
Merged
17 changes: 14 additions & 3 deletions __tests__/bin/vip-app-deploy.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { appDeployCmd } from '../../src/bin/vip-app-deploy';
import * as exit from '../../src/lib/cli/exit';
import { uploadImportSqlFileToS3 } from '../../src/lib/client-file-uploader';
import { gates, promptToContinue } from '../../src/lib/custom-deploy/custom-deploy';
import {
validateFile,
promptToContinue,
isSupportedApp,
validateCustomDeployKey,
} from '../../src/lib/custom-deploy/custom-deploy';
import { validateDeployFileExt, validateFilename } from '../../src/lib/validations/custom-deploy';

jest.mock( '../../src/lib/client-file-uploader', () => ( {
Expand All @@ -13,9 +18,11 @@ jest.mock( '../../src/lib/client-file-uploader', () => ( {
} ) );

jest.mock( '../../src/lib/custom-deploy/custom-deploy', () => ( {
gates: jest.fn(),
validateFile: jest.fn(),
renameFile: jest.fn(),
promptToContinue: jest.fn().mockResolvedValue( true ),
isSupportedApp: jest.fn().mockResolvedValue( true ),
validateCustomDeployKey: jest.fn(),
} ) );

jest.mock( '../../src/lib/cli/command', () => {
Expand Down Expand Up @@ -85,7 +92,11 @@ describe( 'vip-app-deploy', () => {
it( 'should call expected functions', async () => {
await appDeployCmd( args, opts );

expect( gates ).toHaveBeenCalledTimes( 1 );
expect( isSupportedApp ).toHaveBeenCalledTimes( 1 );

expect( validateCustomDeployKey ).toHaveBeenCalledTimes( 1 );

expect( validateFile ).toHaveBeenCalledTimes( 1 );

expect( promptToContinue ).not.toHaveBeenCalled();

Expand Down
13 changes: 7 additions & 6 deletions __tests__/commands/backup-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ const mutationMock = jest.fn( async () => {
} );

jest.mock( '../../src/lib/api' );
jest.mocked( API ).mockImplementation( () => {
return Promise.resolve( {
query: queryMock,
mutate: mutationMock,
} as any );
} );
jest.mocked( API ).mockImplementation(
() =>
( {
query: queryMock,
mutate: mutationMock,
} as any )
);

describe( 'commands/BackupDBCommand', () => {
beforeEach( () => {} );
Expand Down
13 changes: 7 additions & 6 deletions __tests__/commands/phpmyadmin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ const pmaEnabledQueryMockTrue = jest.fn( async () => {
} );

jest.mock( '../../src/lib/api' );
jest.mocked( API ).mockImplementation( () => {
return Promise.resolve( {
mutate: generatePMAAccessMutationMock,
query: pmaEnabledQueryMockTrue,
} as any );
} );
jest.mocked( API ).mockImplementation(
() =>
( {
mutate: generatePMAAccessMutationMock,
query: pmaEnabledQueryMockTrue,
} as any )
);

describe( 'commands/PhpMyAdminCommand', () => {
beforeEach( () => {} );
Expand Down
1 change: 1 addition & 0 deletions docs/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ TODO: Update description of the variables.
- `NO_PROXY`:
- `VIP_PROXY`: [For internal VIP use](TESTING.md#local-testing).
- `VIP_USE_SYSTEM_PROXY`:
- `WPVIP_DEPLOY_TOKEN`: For use with `vip app deploy` on sites that have custom deploys enabled.

### Configuration files

Expand Down
18 changes: 15 additions & 3 deletions src/bin/vip-app-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ import {
WithId,
UploadArguments,
} from '../lib/client-file-uploader';
import { gates } from '../lib/custom-deploy/custom-deploy';
import {
isSupportedApp,
validateCustomDeployKey,
validateFile,
} from '../lib/custom-deploy/custom-deploy';
import { trackEventWithEnv } from '../lib/tracker';

const appQuery = `
Expand Down Expand Up @@ -118,7 +122,15 @@ export async function appDeployCmd( arg: string[] = [], opts: Record< string, un
const envId = env.id as number;
const track = trackEventWithEnv.bind( null, appId, envId );

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

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

await validateFile( app, env, fileMeta );

await track( 'deploy_app_command_execute' );

Expand Down Expand Up @@ -183,7 +195,7 @@ Processing the file for deployment to your environment...
progressTracker.stepRunning( 'upload' );

// Call the Public API
const api = await API();
const api = API();

const progressCallback = ( percentage: string ) => {
progressTracker.setUploadPercentage( percentage );
Expand Down
2 changes: 1 addition & 1 deletion src/bin/vip-app-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import command from '../lib/cli/command';
import { trackEvent } from '../lib/tracker';

command( { format: true } ).argv( process.argv, async () => {
const api = await API();
const api = API();

await trackEvent( 'app_list_command_execute' );

Expand Down
2 changes: 1 addition & 1 deletion src/bin/vip-import-media-abort.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ ${ chalk.red.bold( 'Are you sure you want to abort this Media Import?' ) }
'The type of application you specified does not currently support Media imports.'
);
}
const api = await API();
const api = API();

await track( 'import_media_abort_execute' );

Expand Down
2 changes: 1 addition & 1 deletion src/bin/vip-import-media.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Error:
}

const track = trackEventWithEnv.bind( null, app.id, env.id );
const api = await API();
const api = API();

debug( 'Options: ', opts );
debug( 'Args:', args );
Expand Down
2 changes: 1 addition & 1 deletion src/bin/vip-import-sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ Processing the SQL import for your environment...
progressTracker.stepRunning( 'upload' );

// Call the Public API
const api = await API();
const api = API();

const startImportVariables = {};

Expand Down
2 changes: 1 addition & 1 deletion src/bin/vip-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ command( {
module: 'sync',
requireConfirm: 'Are you sure you want to sync from production?',
} ).argv( process.argv, async ( arg, opts ) => {
const api = await API();
const api = API();
let syncing = false;

await trackEvent( 'sync_command_execute' );
Expand Down
2 changes: 1 addition & 1 deletion src/bin/vip-validate-preflight.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function logToConsole( ...messages ) {
}

async function getBuildConfiguration( application, environment ) {
const api = await API();
const api = API();

// Disable the global GraphQL error handling, so we can catch Unauthorized errors and recommend next steps.
disableGlobalGraphQLErrorHandling();
Expand Down
4 changes: 2 additions & 2 deletions src/bin/vip-wp.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const bindStreamEvents = ( { subShellRl, commonTrackingParams, isSubShell, stdou
};

const getTokenForCommand = async ( appId, envId, command ) => {
const api = await API();
const api = API();

return api.mutate( {
mutation: gql`
Expand All @@ -109,7 +109,7 @@ const getTokenForCommand = async ( appId, envId, command ) => {

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const cancelCommand = async guid => {
const api = await API();
const api = API();
return api.mutate( {
mutation: gql`
mutation cancelWPCLICommand($input: CancelWPCLICommandInput) {
Expand Down
6 changes: 5 additions & 1 deletion src/bin/vip.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ if ( config && config.environment !== 'production' ) {

// Config
const tokenURL = 'https://dashboard.wpvip.com/me/cli/token';
const customDeployToken = process.env.WPVIP_DEPLOY_TOKEN;

const runCmd = async function () {
const cmd = command();
Expand Down Expand Up @@ -63,6 +64,8 @@ const rootCmd = async function () {
const isDevEnvCommandWithoutEnv =
doesArgvHaveAtLeastOneParam( process.argv, [ 'dev-env' ] ) &&
! containsAppEnvArgument( process.argv );
const isCustomDeployCmdWithKey =
doesArgvHaveAtLeastOneParam( process.argv, [ 'deploy' ] ) && Boolean( customDeployToken );

debug( 'Argv:', process.argv );

Expand All @@ -72,7 +75,8 @@ const rootCmd = async function () {
isHelpCommand ||
isVersionCommand ||
isDevEnvCommandWithoutEnv ||
token?.valid() )
token?.valid() ||
isCustomDeployCmdWithKey )
) {
await runCmd();
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/commands/backup-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const DB_BACKUP_JOB_STATUS_QUERY = gql`
`;

async function getBackupJob( appId: number, envId: number ) {
const api = await API();
const api = API();

const response = await api.query< AppBackupJobStatusQuery >( {
query: DB_BACKUP_JOB_STATUS_QUERY,
Expand All @@ -70,7 +70,7 @@ async function createBackupJob( appId: number, envId: number ) {
// Disable global error handling so that we can handle errors ourselves
disableGlobalGraphQLErrorHandling();

const api = await API();
const api = API();
await api.mutate( {
mutation: CREATE_DB_BACKUP_JOB_MUTATION,
variables: {
Expand Down
6 changes: 3 additions & 3 deletions src/commands/export-sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const CREATE_EXPORT_JOB_MUTATION = gql`
* @return {Promise} A promise which resolves to the latest backup and job status
*/
async function fetchLatestBackupAndJobStatus( appId, envId ) {
const api = await API();
const api = API();

const response = await api.query( {
query: BACKUP_AND_JOB_STATUS_QUERY,
Expand Down Expand Up @@ -112,7 +112,7 @@ async function fetchLatestBackupAndJobStatus( appId, envId ) {
* @return {Promise} A promise which resolves to the download link
*/
async function generateDownloadLink( appId, envId, backupId ) {
const api = await API();
const api = API();
const response = await api.mutate( {
mutation: GENERATE_DOWNLOAD_LINK_MUTATION,
variables: {
Expand Down Expand Up @@ -146,7 +146,7 @@ async function createExportJob( appId, envId, backupId ) {
// Disable global error handling so that we can handle errors ourselves
disableGlobalGraphQLErrorHandling();

const api = await API();
const api = API();
await api.mutate( {
mutation: CREATE_EXPORT_JOB_MUTATION,
variables: {
Expand Down
6 changes: 3 additions & 3 deletions src/commands/phpmyadmin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async function generatePhpMyAdminAccess( envId: number ): Promise< string > {
// Disable global error handling so that we can handle errors ourselves
disableGlobalGraphQLErrorHandling();

const api: ApolloClient< NormalizedCacheObject > = await API();
const api: ApolloClient< NormalizedCacheObject > = API();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const resp: FetchResult< any, Record< string, any >, Record< string, any > > = await api.mutate( {
mutation: GENERATE_PHP_MY_ADMIN_URL_MUTATION,
Expand All @@ -79,7 +79,7 @@ async function enablePhpMyAdmin( envId: number ): Promise< string > {
// Disable global error handling so that we can handle errors ourselves
disableGlobalGraphQLErrorHandling();

const api: ApolloClient< NormalizedCacheObject > = await API();
const api: ApolloClient< NormalizedCacheObject > = API();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const resp: FetchResult< any, Record< string, any >, Record< string, any > > = await api.mutate( {
mutation: ENABLE_PHP_MY_ADMIN_MUTATION,
Expand All @@ -101,7 +101,7 @@ async function getPhpMyAdminStatus( appId: number, envId: number ): Promise< str
// Disable global error handling so that we can handle errors ourselves
disableGlobalGraphQLErrorHandling();

const api: ApolloClient< NormalizedCacheObject > = await API();
const api: ApolloClient< NormalizedCacheObject > = API();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const resp: ApolloQueryResult< any > = await api.query( {
Expand Down
32 changes: 14 additions & 18 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,11 @@ export function enableGlobalGraphQLErrorHandling(): void {
globalGraphQLErrorHandlingEnabled = true;
}

export default async function API( { exitOnError = true } = {} ): Promise<
ApolloClient< NormalizedCacheObject >
> {
const authToken = await Token.get();
const headers = {
'User-Agent': env.userAgent,
Authorization: `Bearer ${ authToken.raw }`,
};

export default function API( {
exitOnError = true,
}: {
exitOnError?: boolean;
} = {} ): ApolloClient< NormalizedCacheObject > {
const errorLink = onError( ( { networkError, graphQLErrors } ) => {
if ( networkError && 'statusCode' in networkError && networkError.statusCode === 401 ) {
console.error(
Expand All @@ -59,21 +55,22 @@ export default async function API( { exitOnError = true } = {} ): Promise<
}
} );

const withToken = setContext( async (): Promise< { token: Token } > => {
const token = await Token.get();
const withToken = setContext( async (): Promise< { token: string } > => {
const token = ( await Token.get() ).raw;

return { token };
} );

const authLink = new ApolloLink( ( operation, forward ) => {
const ctx = operation.getContext();
const token = ctx.token as Token;

operation.setContext( {
headers: {
Authorization: `Bearer ${ token.raw }`,
},
} );
const headers = {
'User-Agent': env.userAgent,
Authorization: `Bearer ${ ctx.token }`,
...ctx.headers,
} as Record< string, string >;

operation.setContext( { headers } );

return forward( operation );
} );
Expand All @@ -82,7 +79,6 @@ export default async function API( { exitOnError = true } = {} ): Promise<

const httpLink = new HttpLink( {
uri: API_URL,
headers,
fetch: http,
fetchOptions: {
agent: proxyAgent,
Expand Down
Loading