Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@
"vip-sync": "dist/bin/vip-sync.js",
"vip-whoami": "dist/bin/vip-whoami.js",
"vip-wp": "dist/bin/vip-wp.js",
"vip-logout": "dist/bin/vip-logout.js"
"vip-logout": "dist/bin/vip-logout.js",
"vip-internal": "dist/bin/vip-internal.js",
"vip-internal-is-logged-in": "dist/bin/vip-internal-is-logged-in.js",
"vip-internal-fetch-integrations": "dist/bin/vip-internal-fetch-integrations.js"
},
"scripts": {
"typescript:codegen:install-dependencies": "npm install --no-save @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/near-operation-file-preset @graphql-codegen/typescript-operations",
Expand Down
34 changes: 34 additions & 0 deletions src/bin/vip-internal-fetch-integrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env node

import { disableGlobalGraphQLErrorHandling } from '../lib/api';
import command from '../lib/cli/command';
import { fetchIntegrations } from '../lib/dev-environment/integrations';

interface Options {
app?: string;
env?: string;
}

async function fetchIntegrationsCommand( _args: string[], opts: Options ): Promise< void > {
let response: Record< string, unknown >;
if ( opts.app && opts.env ) {
disableGlobalGraphQLErrorHandling();
try {
response = await fetchIntegrations( opts.app, opts.env );
} catch ( error: unknown ) {
const err = error instanceof Error ? error : new Error( String( error ) );
response = { error: err.message };
process.exitCode = 1;
}
} else {
response = { error: 'Required parameters missing' };
process.exitCode = 1;
}

process.stdout.write( JSON.stringify( response ) );
}

void command( { usage: 'vip internal fetch-integrations' } ).argv(
process.argv,
fetchIntegrationsCommand
);
34 changes: 34 additions & 0 deletions src/bin/vip-internal-is-logged-in.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env node

import { disableGlobalGraphQLErrorHandling } from '../lib/api';
import { getCurrentUserInfo } from '../lib/api/user';
import command from '../lib/cli/command';

import type { Me } from '../graphqlTypes';

async function isLoggedInCommand(): Promise< void > {
let currentUser: Me;
let response: Record< string, unknown >;

disableGlobalGraphQLErrorHandling();

try {
currentUser = await getCurrentUserInfo( true );
response = {
displayName: currentUser.displayName,
id: currentUser.id,
isVIP: currentUser.isVIP,
};
} catch ( err: unknown ) {
const error = err instanceof Error ? err : new Error( 'Unknown error' );
response = {
error: error.message,
};

process.exitCode = 1;
}

process.stdout.write( JSON.stringify( response ) );
}

void command( { usage: 'vip internal is-logged-in' } ).argv( process.argv, isLoggedInCommand );
8 changes: 8 additions & 0 deletions src/bin/vip-internal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env node

import command from '../lib/cli/command';

command( { usage: 'vip internal' } )
.command( 'is-logged-in', 'Check if the user is logged in' )
.command( 'fetch-integrations', 'Fetch integrations for the given environment' )
.argv( process.argv );
15 changes: 12 additions & 3 deletions src/bin/vip.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,18 @@ const runCmd = async function () {
.command( 'db', "Access an environment's database." )
.command( 'sync', 'Sync the database from production to a non-production environment.' )
.command( 'whoami', 'Retrieve details about the current authenticated VIP-CLI user.' )
.command( 'wp', 'Execute a WP-CLI command against an environment.' );

cmd.argv( process.argv );
.command(
'validate',
'Scan a Node.js codebase for issues that could prevent building or deploying.'
)
.command( 'wp', 'Execute a WP-CLI command against an environment.' )
.command( 'internal', 'Internal commands used by VIP automation tools.' );

cmd.argv( process.argv, null, {
usageFilter: usage =>
// eslint-disable-next-line no-control-regex
`${ usage }`.replace( /^ {4}(\u001B\[..m)?internal(\u001B\[..m)?(.+)$\n/m, '' ),
} );
};

/**
Expand Down
6 changes: 5 additions & 1 deletion src/lib/cli/command.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ declare class Args {
showHelp(): void;
showVersion(): void;

argv: ( argv: string[], cb: unknown ) => Promise< unknown >;
argv: (
argv: string[],
cb: unknown,
options?: Partial< ConfigurationOptions >
) => Promise< unknown >;

// utils.js
handleType( value: unknown ): [ string, ( ( v: unknown ) => unknown )? ];
Expand Down
4 changes: 3 additions & 1 deletion src/lib/cli/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ let alreadyConfirmedDebugAttachment = false;
* @param {string[]} argv
*/
// eslint-disable-next-line complexity
args.argv = async function ( argv, cb ) {
args.argv = async function ( argv, cb, config = {} ) {
if ( process.platform !== 'win32' && argv[ 1 ]?.endsWith( '.js' ) ) {
argv[ 1 ] = argv[ 1 ].slice( 0, -3 );
}
Expand All @@ -55,6 +55,8 @@ args.argv = async function ( argv, cb ) {
}
const parsedAlias = parseEnvAliasFromArgv( argv );

this.config = { ...this.config, ...config };

// A usage option allows us to override the default usage text, which isn't
// accurate for subcommands. By default, it will display something like (note
// the hyphen):
Expand Down
8 changes: 4 additions & 4 deletions src/lib/dev-environment/dev-environment-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,8 @@ export function getEnvironmentPath( name: string ): string {
}

export async function getApplicationInformation(
appId: number,
envType: string | null
appId: number | string,
envType: string | null | undefined
): Promise< AppInfo > {
// $FlowFixMe: gql template is not supported by flow
const fieldsQuery = `
Expand Down Expand Up @@ -670,10 +670,10 @@ export async function getApplicationInformation(
},
softwareSettings {
php {
...Software
...Software
}
wordpress {
...Software
...Software
}
}
}`;
Expand Down
52 changes: 52 additions & 0 deletions src/lib/dev-environment/integrations.generated.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as Types from '../graphqlTypes';

export type AppByNameQueryVariables = Types.Exact< {
app?: Types.InputMaybe< Types.Scalars[ 'String' ][ 'input' ] >;
env?: Types.InputMaybe< Types.Scalars[ 'String' ][ 'input' ] >;
} >;

export type AppByNameQuery = {
__typename?: 'Query';
apps?: {
__typename?: 'AppList';
edges?: Array< {
__typename?: 'App';
id?: number | null;
name?: string | null;
environments?: Array< {
__typename?: 'AppEnvironment';
id?: number | null;
name?: string | null;
type?: string | null;
getIntegrationsDevEnvConfig?: {
__typename?: 'IntegrationDevEnvConfig';
data?: any | null;
} | null;
} | null > | null;
} | null > | null;
} | null;
};

export type AppByIdQueryVariables = Types.Exact< {
id?: Types.InputMaybe< Types.Scalars[ 'Int' ][ 'input' ] >;
env?: Types.InputMaybe< Types.Scalars[ 'String' ][ 'input' ] >;
} >;

export type AppByIdQuery = {
__typename?: 'Query';
app?: {
__typename?: 'App';
id?: number | null;
name?: string | null;
environments?: Array< {
__typename?: 'AppEnvironment';
id?: number | null;
name?: string | null;
type?: string | null;
getIntegrationsDevEnvConfig?: {
__typename?: 'IntegrationDevEnvConfig';
data?: any | null;
} | null;
} | null > | null;
} | null;
};
80 changes: 80 additions & 0 deletions src/lib/dev-environment/integrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import gql from 'graphql-tag';

import API from '../api';

import type {
AppByIdQuery,
AppByIdQueryVariables,
AppByNameQuery,
AppByNameQueryVariables,
} from './integrations.generated';

const queryAppByName = gql`
query AppByName($app: String, $env: String) {
apps(first: 1, name: $app) {
edges {
id
name
environments(type: $env) {
id
name
type
getIntegrationsDevEnvConfig {
data
}
}
}
}
}
`;

const queryAppByID = gql`
query AppByID($id: Int, $env: String) {
app(id: $id) {
id
name
environments(type: $env) {
id
name
type
getIntegrationsDevEnvConfig {
data
}
}
}
}
`;

export async function fetchIntegrations(
app: string,
env: string
): Promise< Record< string, unknown > > {
type Integrations = Record< string, unknown >;
const api = API( { exitOnError: false, silenceAuthErrors: true } );
if ( isNaN( Number( app ) ) ) {
const res = await api.query< AppByNameQuery, AppByNameQueryVariables >( {
query: queryAppByName,
variables: {
app,
env,
},
} );

return (
( res.data.apps?.edges?.[ 0 ]?.environments?.[ 0 ]?.getIntegrationsDevEnvConfig
?.data as Integrations ) ?? {}
);
}

const res = await api.query< AppByIdQuery, AppByIdQueryVariables >( {
query: queryAppByID,
variables: {
id: Number( app ),
env,
},
} );

return (
( res.data.app?.environments?.[ 0 ]?.getIntegrationsDevEnvConfig?.data as Integrations ) ?? {}
);
}