-
Notifications
You must be signed in to change notification settings - Fork 63
Apply decoupled config strategy to preview sites #2807
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
Changes from 12 commits
8a993d8
4f40c98
18c1bac
c758246
05f2c0e
6788928
df9f4c0
0683f63
5bef546
f2e49dc
d178ad3
d160808
a713016
94c0124
4256aa7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,20 @@ | ||
| import os from 'os'; | ||
| import path from 'path'; | ||
| import { SNAPSHOT_EVENTS } from '@studio/common/lib/cli-events'; | ||
| import { getWordPressVersion } from '@studio/common/lib/get-wordpress-version'; | ||
| import { PreviewCommandLoggerAction as LoggerAction } from '@studio/common/logger-actions'; | ||
| import { __, sprintf } from '@wordpress/i18n'; | ||
| import { uploadArchive, waitForSiteReady } from 'cli/lib/api'; | ||
| import { getAuthToken } from 'cli/lib/appdata'; | ||
| import { archiveSiteContent, cleanup } from 'cli/lib/archive'; | ||
| import { getSiteByFolder } from 'cli/lib/cli-config'; | ||
| import { saveSnapshotToAppdata } from 'cli/lib/snapshots'; | ||
| import { getSiteByFolder, getNextSnapshotSequence } from 'cli/lib/cli-config'; | ||
| import { emitCliEvent } from 'cli/lib/daemon-client'; | ||
| import { getSnapshotsFromConfig, saveSnapshotToConfig } from 'cli/lib/snapshots'; | ||
| import { validateSiteSize } from 'cli/lib/validation'; | ||
| import { Logger, LoggerError } from 'cli/logger'; | ||
| import { StudioArgv } from 'cli/types'; | ||
|
|
||
| export async function runCommand( siteFolder: string ): Promise< void > { | ||
| export async function runCommand( siteFolder: string, name?: string ): Promise< void > { | ||
| const archivePath = path.join( | ||
| os.tmpdir(), | ||
| `${ path.basename( siteFolder ) }-${ Date.now() }.zip` | ||
|
|
@@ -42,12 +44,27 @@ export async function runCommand( siteFolder: string ): Promise< void > { | |
| ); | ||
|
|
||
| logger.reportStart( LoggerAction.APPDATA, __( 'Saving preview site to Studio…' ) ); | ||
| const snapshot = await saveSnapshotToAppdata( | ||
| let snapshotName = name; | ||
| if ( ! snapshotName ) { | ||
| const site = await getSiteByFolder( siteFolder ); | ||
| const snapshots = await getSnapshotsFromConfig( token.id ); | ||
| const sequence = getNextSnapshotSequence( site.id, snapshots, token.id ); | ||
| snapshotName = sprintf( | ||
| /* translators: 1: Site name 2: Sequence number (e.g. "My Site Name Preview 1") */ | ||
| __( '%1$s Preview %2$d' ), | ||
| site.name, | ||
| sequence | ||
| ); | ||
| } | ||
| const snapshot = await saveSnapshotToConfig( | ||
| siteFolder, | ||
| uploadResponse.site_id, | ||
| uploadResponse.site_url | ||
| uploadResponse.site_url, | ||
| token.id, | ||
| snapshotName | ||
| ); | ||
| logger.reportSuccess( __( 'Preview site saved to Studio' ) ); | ||
| await emitCliEvent( { event: SNAPSHOT_EVENTS.CREATED, data: { snapshotUrl: snapshot.url } } ); | ||
|
|
||
| logger.reportKeyValuePair( 'name', snapshot.name ?? '' ); | ||
| logger.reportKeyValuePair( 'url', snapshot.url ); | ||
|
|
@@ -67,8 +84,14 @@ export const registerCommand = ( yargs: StudioArgv ) => { | |
| return yargs.command( { | ||
| command: 'create', | ||
| describe: __( 'Create a preview site' ), | ||
| builder: ( yargs ) => { | ||
| return yargs.option( 'name', { | ||
| type: 'string', | ||
| description: __( 'Preview site name' ), | ||
| } ); | ||
| }, | ||
|
Comment on lines
+88
to
+93
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice addition 👍 |
||
| handler: async ( argv ) => { | ||
| await runCommand( argv.path ); | ||
| await runCommand( argv.path, argv.name ); | ||
| }, | ||
| } ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,10 +3,10 @@ import { __, _n, sprintf } from '@wordpress/i18n'; | |
| import Table from 'cli-table3'; | ||
| import { format } from 'date-fns'; | ||
| import { getAuthToken } from 'cli/lib/appdata'; | ||
| import { getSiteByFolder } from 'cli/lib/cli-config'; | ||
| import { readCliConfig } from 'cli/lib/cli-config/core'; | ||
| import { | ||
| formatDurationUntilExpiry, | ||
| getSnapshotsFromAppdata, | ||
| getSnapshotsFromConfig, | ||
| isSnapshotExpired, | ||
| } from 'cli/lib/snapshots'; | ||
| import { getColumnWidths } from 'cli/lib/utils'; | ||
|
|
@@ -20,13 +20,20 @@ export async function runCommand( | |
| const logger = new Logger< LoggerAction >(); | ||
|
|
||
| try { | ||
| if ( outputFormat === 'json' ) { | ||
| const config = await readCliConfig(); | ||
| const json = JSON.stringify( config.snapshots ); | ||
| console.log( json ); | ||
| logger.reportKeyValuePair( 'snapshots', json ); | ||
| return; | ||
| } | ||
|
Comment on lines
+23
to
+29
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we also log to stdout here? I know I've argued that using |
||
|
|
||
| logger.reportStart( LoggerAction.VALIDATE, __( 'Validating…' ) ); | ||
| await getSiteByFolder( siteFolder ); | ||
| const token = await getAuthToken(); | ||
| logger.reportSuccess( __( 'Validation successful' ), true ); | ||
|
|
||
| logger.reportStart( LoggerAction.LOAD, __( 'Loading preview sites…' ) ); | ||
| const snapshots = await getSnapshotsFromAppdata( token.id, siteFolder ); | ||
| const snapshots = await getSnapshotsFromConfig( token.id, siteFolder ); | ||
|
|
||
| if ( snapshots.length === 0 ) { | ||
| logger.reportSuccess( __( 'No preview sites found' ) ); | ||
|
|
@@ -51,42 +58,31 @@ export async function runCommand( | |
| logger.reportSuccess( snapshotsMessage ); | ||
| } | ||
|
|
||
| if ( outputFormat === 'table' ) { | ||
| const colWidths = getColumnWidths( [ 0.4, 0.25, 0.175, 0.175 ] ); | ||
| const table = new Table( { | ||
| head: [ __( 'URL' ), __( 'Site Name' ), __( 'Updated' ), __( 'Expires in' ) ], | ||
| wordWrap: true, | ||
| wrapOnWordBoundary: false, | ||
| colWidths, | ||
| style: { | ||
| head: [], | ||
| border: [], | ||
| }, | ||
| } ); | ||
|
|
||
| for ( const snapshot of snapshots ) { | ||
| const durationUntilExpiry = formatDurationUntilExpiry( snapshot.date ); | ||
| const url = `https://${ snapshot.url }`; | ||
|
|
||
| table.push( [ | ||
| { href: url, content: url }, | ||
| snapshot.name, | ||
| format( snapshot.date, 'yyyy-MM-dd HH:mm' ), | ||
| durationUntilExpiry, | ||
| ] ); | ||
| } | ||
| const colWidths = getColumnWidths( [ 0.4, 0.25, 0.175, 0.175 ] ); | ||
| const table = new Table( { | ||
| head: [ __( 'URL' ), __( 'Site Name' ), __( 'Updated' ), __( 'Expires in' ) ], | ||
| wordWrap: true, | ||
| wrapOnWordBoundary: false, | ||
| colWidths, | ||
| style: { | ||
| head: [], | ||
| border: [], | ||
| }, | ||
| } ); | ||
|
|
||
| console.log( table.toString() ); | ||
| } else { | ||
| const output = snapshots.map( ( snapshot ) => ( { | ||
| url: `https://${ snapshot.url }`, | ||
| name: snapshot.name, | ||
| date: format( snapshot.date, 'yyyy-MM-dd HH:mm' ), | ||
| expiresIn: formatDurationUntilExpiry( snapshot.date ), | ||
| } ) ); | ||
| for ( const snapshot of snapshots ) { | ||
| const durationUntilExpiry = formatDurationUntilExpiry( snapshot.date ); | ||
| const url = `https://${ snapshot.url }`; | ||
|
|
||
| console.log( JSON.stringify( output, null, 2 ) ); | ||
| table.push( [ | ||
| { href: url, content: url }, | ||
| snapshot.name, | ||
| format( snapshot.date, 'yyyy-MM-dd HH:mm' ), | ||
| durationUntilExpiry, | ||
| ] ); | ||
| } | ||
|
|
||
| console.log( table.toString() ); | ||
| } catch ( error ) { | ||
| if ( error instanceof LoggerError ) { | ||
| logger.reportError( error ); | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still expect the CLI and the app to use a shared config file that they can both read from and write to. I don't think we can get away from that. The language setting and the auth data are two good examples of things that the app shouldn't or couldn't use the CLI to write to the shared config. With that in mind, I'd argue it's a bit overkill to introduce a CLI command just to update the name of a preview site. It's possible that CLI-only users would also want this ability, it just seems much less important in the CLI than it does in the UI. I'm not necessarily arguing we need to walk back this change – if it works, then it works. Still, I think there are better arguments for why we need the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was also wondering that during development. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import { SNAPSHOT_EVENTS } from '@studio/common/lib/cli-events'; | ||
| import { PreviewCommandLoggerAction as LoggerAction } from '@studio/common/logger-actions'; | ||
| import { __ } from '@wordpress/i18n'; | ||
| import { setSnapshotInConfig } from 'cli/lib/cli-config'; | ||
| import { emitCliEvent } from 'cli/lib/daemon-client'; | ||
| import { normalizeHostname } from 'cli/lib/utils'; | ||
| import { Logger, LoggerError } from 'cli/logger'; | ||
| import { StudioArgv } from 'cli/types'; | ||
|
|
||
| export interface SetCommandOptions { | ||
| name?: string; | ||
| } | ||
|
|
||
| export async function runCommand( host: string, options: SetCommandOptions ): Promise< void > { | ||
| const { name } = options; | ||
| const logger = new Logger< LoggerAction >(); | ||
|
|
||
| if ( name === undefined ) { | ||
| throw new LoggerError( __( 'At least one option (--name) is required.' ) ); | ||
| } | ||
|
|
||
| if ( name !== undefined && ! name.trim() ) { | ||
| throw new LoggerError( __( 'Preview site name cannot be empty.' ) ); | ||
| } | ||
|
|
||
| logger.reportStart( LoggerAction.SET, __( 'Updating preview site…' ) ); | ||
| await setSnapshotInConfig( host, { name } ); | ||
| await emitCliEvent( { event: SNAPSHOT_EVENTS.UPDATED, data: { snapshotUrl: host } } ); | ||
| logger.reportSuccess( __( 'Preview site updated' ) ); | ||
| } | ||
|
|
||
| export const registerCommand = ( yargs: StudioArgv ) => { | ||
| return yargs.command( { | ||
| command: 'set <host>', | ||
| describe: __( 'Configure preview site settings' ), | ||
| builder: ( yargs ) => { | ||
| return yargs | ||
| .positional( 'host', { | ||
| type: 'string', | ||
| description: __( 'Hostname of the preview site to configure' ), | ||
| demandOption: true, | ||
| } ) | ||
| .option( 'name', { | ||
| type: 'string', | ||
| description: __( 'Preview site name' ), | ||
| } ) | ||
| .option( 'path', { | ||
| hidden: true, | ||
| } ); | ||
| }, | ||
| handler: async ( argv ) => { | ||
| try { | ||
| const normalizedHost = normalizeHostname( argv.host ); | ||
| await runCommand( normalizedHost, { name: argv.name } ); | ||
| } catch ( error ) { | ||
| if ( error instanceof LoggerError ) { | ||
| logger.reportError( error ); | ||
| } else { | ||
| const loggerError = new LoggerError( __( 'Failed to configure preview site' ), error ); | ||
| logger.reportError( loggerError ); | ||
| } | ||
| process.exit( 1 ); | ||
| } | ||
| }, | ||
| } ); | ||
| }; | ||
|
|
||
| const logger = new Logger< LoggerAction >(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice 👍