Skip to content

Commit 27a1545

Browse files
fredrikekelundbcotrimclaude
authored
Move WordPress dependencies (server-files) to CLI (#2873)
* Read site data from CLI instead of appdata (#2701) * STU-1350: Read site data from CLI instead of appdata Studio now reads site data via `site list --format json` at startup and maintains state through CLI events subscriber. Removes direct appdata reads for site details, simplifies SiteServer create/start flows, and removes waitForSiteEvent synchronization. * Fix lint: indentation in index.test.ts * Fix typecheck: narrow SiteDetails union before accessing url * Remove dead url branch in SiteServer.create — events subscriber handles it * Address PR review feedback: shared schemas, keyValuePair output, simplify start/delete * Move CLI site data to dedicated config file (#2731) * STU-1350: Read site data from CLI instead of appdata Studio now reads site data via `site list --format json` at startup and maintains state through CLI events subscriber. Removes direct appdata reads for site details, simplifies SiteServer create/start flows, and removes waitForSiteEvent synchronization. * Fix lint: indentation in index.test.ts * STU-1350: Move CLI site data to dedicated cli.json config file Split site data from appdata-v1.json into a new CLI-owned config at ~/.studio/cli.json. Studio now reads sites exclusively via `studio site list` and _events. The CLI is the source of truth for site configuration. Auth tokens, snapshots, locale remain in appdata. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * Fix delete test after trunk merge: use saveCliConfig instead of saveAppdata * Extend require-lock-before-save eslint rule to support cli-config lock/save pairs * Use shared lockfile constants and extract DEFAULT_CLI_CONFIG * Fix typecheck: narrow SiteDetails union before accessing url * Remove dead url branch in SiteServer.create — events subscriber handles it * Fix lint: remove extra blank line in cli-config.ts * triggert ci * Update e2e tests to read site data from cli.json * Address PR review feedback for cli-config - Use structuredClone for DEFAULT_CLI_CONFIG deep copy - Rename daemon-paths.ts to paths.ts, extract STUDIO_CLI_HOME - Split config schema for version mismatch detection - Improve readCliConfig error handling with version check - Rename removeSite to removeSiteFromConfig - Revert UPDATED event handler to warn for unknown sites * Fix AI files to import site functions from cli-config instead of appdata --------- Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com> * Fix AI tools test mocks to use cli-config instead of appdata * Apply decoupled config strategy to preview sites (#2807) * Apply decoupled config strategy to preview sites (STU-1350) - CLI stores/reads preview site data from cli.json instead of appdata - Studio reads preview sites via CLI commands (preview list --format json) - Added preview set command for updating snapshot settings (--name) - Added --name option to preview create command - Moved refreshSnapshots to store initialization with test guard - Fixed snapshot-slice test mock setup to avoid module-load timing issues * Emit snapshot events to Studio via _events for realtime updates * Move snapshot name generation from cli-config to preview create command * Split cli-config.ts into folder structure (core, sites, snapshots) * Rename site-events to cli-events and enrich snapshot events with inline data (STU-1350) * Fix snapshot update infinite loop and preview set error handling * Unify CLI event emitter, share event schemas in cli-events, and clean up daemon-client (STU-1350) * trigger ci * Fix import order lint errors in core.ts and site-server.ts * Address PR review: add stdout json output, addSnapshot action, reusable usage count helper, fix listener state bug * Remove barrel file, replace z.nativeEnum with z.enum, update imports to direct paths * Fix import order in cli-events.ts and remove unused test imports * Migrate appdata to ~/.studio/appdata.json with extensible migration framework * Move appdata migration from CLI to Studio Desktop * Wire auth and locale to shared.json, AI settings to cli.json (#2821) * Wire auth and locale to shared.json, AI settings to cli.json Move auth token reads/writes from appdata to shared-config for Desktop+CLI. Move locale reads/writes to shared-config (Desktop saves, CLI reads). Move aiProvider and anthropicApiKey from shared-config to cli-config (CLI-only). Remove auth and locale fields from appdata schema and types since they're now in separate files. Update all imports and tests accordingly. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * Move CLI telemetry stats from appdata to cli-config * Fix import order lint errors * Remove CLI appdata dependency and move infrastructure paths to ~/.studio * Revert server-files and certificate paths to appdata directory * Watch shared config for auth changes from CLI * Align shared config watcher with user-data watcher pattern * trigger ci * Address PR review feedback: events-based auth, rename types, deduplicate schemas * Fix prettier formatting in auth test * Fix shared-config mock in tests to export authTokenSchema * Use z.literal for config version validation, remove unused re-export, update test mocks * Extract authTokenSchema to avoid Node.js imports in renderer bundle * trigger ci * Fix daemon test to use os.tmpdir() for Windows compatibility * Make CLI event emission best-effort in auth commands * Throw on shared config version mismatch instead of silently returning defaults * Add line break before version mismatch error message * Handle version mismatch errors through standard Logger flow in auth commands * Show only display name in AI chat status bar --------- Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com> * Rewrite appdata migration to split into shared.json, cli.json, and appdata.json * trigger ci * Ensure .studio directory exists before lockfile and fix import order * Fix readFile mock type to return Buffer instead of string * Refactor appdata to store only Desktop-specific state with sites as Record<id, AppdataSiteData> * Address PR feedback: rename to app.json and siteMetadata, use zod parsing in migration * Fix prettier formatting in user-data test * Fix e2e migration by respecting E2E_APP_DATA_PATH in getOldAppdataPath * new migration interface * studio migrations * studio migrations * cli migrations * Rename appdata references, centralize config paths and lockfile constants * Isolate e2e config directory to fix test failures * Move site metadata cleanup to SiteServer, fix typecheck and test failures * HTTPS certificate migration * Rename file * Initial server-files migration implementation In this implementation, Studio copies the entire server-files directory to a well-known location. This, by itself, is not enough to make the CLI standalone. We need to actually distribute the wp-files files with the CLI. * Add APP_CONFIG_LOCKFILE_NAME constant and fix test failures * Actually move server-files to CLI management * Fix test * Fix merge conflict * Exclude wp-files from app bundle * Load sites from `SiteServer.getAll()` * Various fixes * Fix lint * Fix * Improved `updateServerFiles` error handling * Remove fs-extra * Abstract GitHub release fetching * Include wp-files in package.json files * Fix errors * Remove outdated wp-now migration * Fix tests * Fix issues identified by agentic review * Setup server-files in middleware * Address issues raised by AI review * Fix more review issues * Set up and update in parallel * Remove unused code * Remove unused import --------- Co-authored-by: Bernardo Cotrim <bmmcotrim@gmail.com> Co-authored-by: bcotrim <bernardo.cotrim@a8c.com> Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 5ba18d4 commit 27a1545

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+703
-625
lines changed

apps/cli/commands/site/create.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
removeDbConstants,
3939
} from '@studio/common/lib/remove-default-db-constants';
4040
import { sortSites } from '@studio/common/lib/sort-sites';
41+
import { getServerFilesPath } from '@studio/common/lib/well-known-paths';
4142
import {
4243
isValidWordPressVersion,
4344
isWordPressVersionAtLeast,
@@ -63,8 +64,9 @@ import {
6364
updateSiteLatestCliPid,
6465
} from 'cli/lib/cli-config/sites';
6566
import { connectToDaemon, disconnectFromDaemon, emitCliEvent } from 'cli/lib/daemon-client';
67+
import { updateServerFiles } from 'cli/lib/dependency-management/setup';
6668
import { copyLanguagePackToSite } from 'cli/lib/language-packs';
67-
import { getAiInstructionsPath, getServerFilesPath } from 'cli/lib/server-files';
69+
import { getAiInstructionsPath } from 'cli/lib/server-files';
6870
import { getPreferredSiteLanguage } from 'cli/lib/site-language';
6971
import { generateSiteName } from 'cli/lib/site-name';
7072
import { getDefaultSitePath } from 'cli/lib/site-paths';
@@ -103,6 +105,26 @@ export async function runCommand(
103105
sitePath: string,
104106
options: CreateCommandOptions
105107
): Promise< void > {
108+
const isOnlineStatus = await isOnline();
109+
110+
try {
111+
if ( isOnlineStatus ) {
112+
logger.reportStart(
113+
LoggerAction.CHECKING_DEPENDENCY_UPDATES,
114+
__( 'Checking for dependency updates…' )
115+
);
116+
117+
await updateServerFiles();
118+
}
119+
} catch ( error ) {
120+
// Swallow errors in production. They aren't critical and likely relate to things outside the
121+
// user's control, like network issues or bad API responses.
122+
if ( process.env.NODE_ENV !== 'production' ) {
123+
const loggerError = new LoggerError( 'Failed to update dependencies', error );
124+
logger.reportError( loggerError );
125+
}
126+
}
127+
106128
try {
107129
logger.reportStart( LoggerAction.VALIDATE, __( 'Validating site configuration…' ) );
108130

@@ -199,8 +221,6 @@ export async function runCommand(
199221
logger.reportSuccess( __( 'Site directory created' ) );
200222
}
201223

202-
const isOnlineStatus = await isOnline();
203-
204224
if ( options.wpVersion === 'latest' ) {
205225
const bundledWPPath = path.join( getServerFilesPath(), 'wordpress-versions', 'latest' );
206226

apps/cli/commands/site/tests/create.test.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { isOnline } from '@studio/common/lib/network-utils';
1414
import { portFinder } from '@studio/common/lib/port-finder';
1515
import { normalizeLineEndings } from '@studio/common/lib/remove-default-db-constants';
16+
import { getServerFilesPath } from '@studio/common/lib/well-known-paths';
1617
import { Blueprint, BlueprintV1Declaration, StepDefinition } from '@wp-playground/blueprints';
1718
import { vi, type MockInstance } from 'vitest';
1819
import {
@@ -24,8 +25,8 @@ import {
2425
} from 'cli/lib/cli-config/core';
2526
import { removeSiteFromConfig, updateSiteAutoStart } from 'cli/lib/cli-config/sites';
2627
import { connectToDaemon, disconnectFromDaemon } from 'cli/lib/daemon-client';
28+
import { updateServerFiles } from 'cli/lib/dependency-management/setup';
2729
import { copyLanguagePackToSite } from 'cli/lib/language-packs';
28-
import { getServerFilesPath } from 'cli/lib/server-files';
2930
import { getPreferredSiteLanguage } from 'cli/lib/site-language';
3031
import { logSiteDetails, openSiteInBrowser, setupCustomDomain } from 'cli/lib/site-utils';
3132
import { keepSqliteIntegrationUpdated } from 'cli/lib/sqlite-integration';
@@ -68,10 +69,14 @@ vi.mock( 'cli/lib/cli-config/sites', async () => {
6869
} );
6970
vi.mock( 'cli/lib/language-packs' );
7071
vi.mock( 'cli/lib/daemon-client' );
71-
vi.mock( 'cli/lib/server-files', () => ( {
72-
getAppdataDirectory: vi.fn().mockReturnValue( '/test/appdata' ),
73-
getServerFilesPath: vi.fn().mockReturnValue( '/test/server-files' ),
74-
} ) );
72+
vi.mock( 'cli/lib/dependency-management/setup' );
73+
vi.mock( import( '@studio/common/lib/well-known-paths' ), async ( importOriginal ) => {
74+
const actual = await importOriginal();
75+
return {
76+
...actual,
77+
getServerFilesPath: vi.fn().mockReturnValue( '/test/server-files' ),
78+
};
79+
} );
7580
vi.mock( 'cli/lib/site-language' );
7681
vi.mock( 'cli/lib/site-utils' );
7782
vi.mock( '@studio/common/lib/agent-skills' );
@@ -156,6 +161,7 @@ describe( 'CLI: studio site create', () => {
156161
vi.mocked( keepSqliteIntegrationUpdated ).mockResolvedValue( true );
157162
vi.mocked( connectToDaemon ).mockResolvedValue( undefined );
158163
vi.mocked( disconnectFromDaemon ).mockResolvedValue( undefined );
164+
vi.mocked( updateServerFiles ).mockResolvedValue( undefined );
159165
vi.mocked( setupCustomDomain ).mockResolvedValue( undefined );
160166
vi.mocked( startWordPressServer ).mockResolvedValue( mockProcessDescription );
161167
vi.mocked( runBlueprint ).mockResolvedValue( undefined );

apps/cli/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { suppressPunycodeWarning } from '@studio/common/lib/suppress-punycode-wa
33
import { __ } from '@wordpress/i18n';
44
import yargs from 'yargs';
55
import { bumpAggregatedUniqueStat, getPlatformMetric } from 'cli/lib/bump-stat';
6+
import { setupServerFiles } from 'cli/lib/dependency-management/setup';
67
import { loadTranslations } from 'cli/lib/i18n';
78
import { StatsGroup, StatsMetric } from 'cli/lib/types/bump-stats';
89
import { untildify } from 'cli/lib/utils';
@@ -66,6 +67,9 @@ async function main() {
6667
}
6768
}
6869
} )
70+
.middleware( async () => {
71+
await setupServerFiles();
72+
} )
6973
.command( 'auth', __( 'Manage authentication' ), async ( authYargs ) => {
7074
const [
7175
{ registerCommand: registerAuthLoginCommand },

apps/cli/lib/certificate-manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import fs from 'node:fs';
44
import path from 'node:path';
55
import { domainToASCII } from 'node:url';
66
import { promisify } from 'node:util';
7-
import { getCertificatesPath } from '@studio/common/lib/config-paths';
7+
import { getCertificatesPath } from '@studio/common/lib/well-known-paths';
88
import sudo from '@vscode/sudo-prompt';
99
import { __ } from '@wordpress/i18n';
1010
import forge from 'node-forge';

apps/cli/lib/cli-config/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import {
66
LOCKFILE_WAIT_TIME,
77
} from '@studio/common/constants';
88
import { siteDetailsSchema } from '@studio/common/lib/cli-events';
9-
import { getCliConfigPath, getConfigDirectory } from '@studio/common/lib/config-paths';
109
import { lockFileAsync, unlockFileAsync } from '@studio/common/lib/lockfile';
10+
import { getCliConfigPath, getConfigDirectory } from '@studio/common/lib/well-known-paths';
1111
import { snapshotSchema } from '@studio/common/types/snapshot';
1212
import { __ } from '@wordpress/i18n';
1313
import { readFile, writeFile } from 'atomically';
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import { recursiveCopyDirectory } from '@studio/common/lib/fs-utils';
4+
import semver from 'semver';
5+
import { getSqliteVersionFromInstallation } from 'cli/lib/sqlite-integration';
6+
import {
7+
getAiInstructionsPath,
8+
getLanguagePacksPath,
9+
getPhpMyAdminPath,
10+
getSqliteCommandPath,
11+
getSqlitePluginPath,
12+
getWordPressVersionPath,
13+
getWpCliPharPath,
14+
getWpFilesPath,
15+
} from '../server-files';
16+
import { updateLatestSqliteCommandVersion } from './sqlite-command';
17+
import { getWordPressVersionFromInstallation, updateLatestWordPressVersion } from './wordpress';
18+
import { updateLatestWpCliVersion } from './wp-cli';
19+
20+
// Compare the WordPress version in the bundled `wp-files/latest/wordpress` directory (that ships
21+
// with the CLI) to `~/.studio/server-files/wordpress-versions/latest`. If the bundled directory is
22+
// newer, rename the old `wordpress-versions/latest` directory to `wordpress-versions/$VERSION` and
23+
// copy the bundled directory to `wordpress-versions/latest`.
24+
async function copyBundledLatestWpVersion() {
25+
const bundledWpVersionPath = path.join( getWpFilesPath(), 'latest', 'wordpress' );
26+
const bundledWpVersion = await getWordPressVersionFromInstallation( bundledWpVersionPath );
27+
const bundledWpSemver = semver.coerce( bundledWpVersion );
28+
29+
if ( ! bundledWpVersion || ! bundledWpSemver ) {
30+
return;
31+
}
32+
33+
const latestWpVersionPath = getWordPressVersionPath( 'latest' );
34+
const latestWpVersion = await getWordPressVersionFromInstallation( latestWpVersionPath );
35+
const latestWpSemver = semver.coerce( latestWpVersion );
36+
37+
if ( ! latestWpVersion || ! latestWpSemver ) {
38+
await recursiveCopyDirectory( bundledWpVersionPath, latestWpVersionPath );
39+
} else if ( semver.gt( bundledWpSemver, latestWpSemver ) ) {
40+
try {
41+
await fs.promises.rename( latestWpVersionPath, getWordPressVersionPath( latestWpVersion ) );
42+
} catch {
43+
// Assume the target directory already exists. Do nothing
44+
}
45+
await recursiveCopyDirectory( bundledWpVersionPath, latestWpVersionPath );
46+
}
47+
}
48+
49+
const SQLITE_FILENAME = 'sqlite-database-integration';
50+
51+
async function copyBundledSqlite() {
52+
const bundledSqlitePath = path.join( getWpFilesPath(), SQLITE_FILENAME );
53+
const bundledSqliteVersion = semver.coerce(
54+
await getSqliteVersionFromInstallation( bundledSqlitePath ),
55+
{ includePrerelease: true }
56+
);
57+
if ( ! bundledSqliteVersion ) {
58+
return;
59+
}
60+
const installedSqlitePath = getSqlitePluginPath();
61+
const isSqliteInstalled = fs.existsSync( installedSqlitePath );
62+
const installedSqliteVersion = semver.coerce(
63+
await getSqliteVersionFromInstallation( installedSqlitePath ),
64+
{ includePrerelease: true }
65+
);
66+
const isBundledVersionNewer =
67+
installedSqliteVersion && semver.gt( bundledSqliteVersion, installedSqliteVersion );
68+
if ( ! isSqliteInstalled || isBundledVersionNewer ) {
69+
console.log( `Copying bundled SQLite version ${ bundledSqliteVersion }…` );
70+
await recursiveCopyDirectory( bundledSqlitePath, getSqlitePluginPath() );
71+
}
72+
}
73+
74+
async function copyBundledWpCli() {
75+
const bundledWpCLIInstalled = fs.existsSync( getWpCliPharPath() );
76+
if ( bundledWpCLIInstalled ) {
77+
return;
78+
}
79+
const bundledWpCLIPath = path.join( getWpFilesPath(), 'wp-cli', 'wp-cli.phar' );
80+
await fs.promises.copyFile( bundledWpCLIPath, getWpCliPharPath() );
81+
}
82+
83+
async function copyBundledSqliteCommand() {
84+
const bundledSqliteCommandPath = path.join( getWpFilesPath(), 'sqlite-command' );
85+
if ( ! fs.existsSync( bundledSqliteCommandPath ) ) {
86+
return;
87+
}
88+
// Always copy to ensure files are complete and up-to-date
89+
await recursiveCopyDirectory( bundledSqliteCommandPath, getSqliteCommandPath() );
90+
}
91+
92+
async function copyBundledTranslations() {
93+
const bundledTranslationsPath = path.join(
94+
getWpFilesPath(),
95+
'latest',
96+
'available-site-translations.json'
97+
);
98+
if ( ! fs.existsSync( bundledTranslationsPath ) ) {
99+
return;
100+
}
101+
const installedTranslationsPath = path.join(
102+
getWordPressVersionPath( 'latest' ),
103+
'available-site-translations.json'
104+
);
105+
106+
await fs.promises.copyFile( bundledTranslationsPath, installedTranslationsPath );
107+
}
108+
109+
async function copyBundledAiInstructions() {
110+
const bundledAiInstructionsPath = path.join( getWpFilesPath(), 'skills' );
111+
if ( ! fs.existsSync( bundledAiInstructionsPath ) ) {
112+
return;
113+
}
114+
await recursiveCopyDirectory( bundledAiInstructionsPath, getAiInstructionsPath() );
115+
}
116+
117+
async function copyBundledPhpMyAdmin() {
118+
const bundledPath = path.join( getWpFilesPath(), 'phpmyadmin' );
119+
if ( ! fs.existsSync( bundledPath ) ) {
120+
return;
121+
}
122+
// Always copy to ensure files are complete and up-to-date
123+
await recursiveCopyDirectory( bundledPath, getPhpMyAdminPath() );
124+
}
125+
126+
async function copyBundledLanguagePacks() {
127+
const bundledLanguagePacksPath = path.join( getWpFilesPath(), 'latest', 'languages' );
128+
if ( ! fs.existsSync( bundledLanguagePacksPath ) ) {
129+
return;
130+
}
131+
const installedLanguagePacksPath = getLanguagePacksPath();
132+
await fs.promises.mkdir( installedLanguagePacksPath, { recursive: true } );
133+
await recursiveCopyDirectory( bundledLanguagePacksPath, installedLanguagePacksPath );
134+
}
135+
136+
export async function setupServerFiles() {
137+
const steps: [ string, () => Promise< void > ][] = [
138+
[ 'WordPress version', copyBundledLatestWpVersion ],
139+
[ 'SQLite integration', copyBundledSqlite ],
140+
[ 'WP-CLI', copyBundledWpCli ],
141+
[ 'SQLite command', copyBundledSqliteCommand ],
142+
[ 'translations', copyBundledTranslations ],
143+
[ 'language packs', copyBundledLanguagePacks ],
144+
[ 'AI instructions', copyBundledAiInstructions ],
145+
[ 'phpMyAdmin', copyBundledPhpMyAdmin ],
146+
];
147+
148+
await Promise.all(
149+
steps.map( ( [ name, step ] ) =>
150+
step().catch( ( error ) => {
151+
console.error( `Failed to set up dependency ${ name }:`, error );
152+
} )
153+
)
154+
);
155+
}
156+
157+
export async function updateServerFiles() {
158+
const steps: [ string, () => Promise< void > ][] = [
159+
[ 'WordPress version', updateLatestWordPressVersion ],
160+
[ 'WP-CLI', updateLatestWpCliVersion ],
161+
[ 'SQLite integration', updateLatestSqliteCommandVersion ],
162+
];
163+
164+
await Promise.all(
165+
steps.map( ( [ name, step ] ) =>
166+
step().catch( ( error ) => {
167+
console.error( `Failed to update dependency ${ name }:`, error );
168+
} )
169+
)
170+
);
171+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import fs from 'fs';
2+
import os from 'os';
3+
import path from 'path';
4+
import { cacheFunctionTTL } from '@studio/common/lib/cache-function-ttl';
5+
import { extractZip } from '@studio/common/lib/extract-zip';
6+
import semver from 'semver';
7+
import { getSqliteCommandPath } from '../server-files';
8+
import { downloadFile, fetchLatestGithubRelease } from './utils';
9+
10+
async function needsUpdate(): Promise< boolean > {
11+
const installationPath = getSqliteCommandPath();
12+
13+
if ( ! fs.existsSync( installationPath ) ) {
14+
return true;
15+
}
16+
17+
const currentVersion = await getSqliteCommandVersion( installationPath );
18+
19+
if ( ! currentVersion ) {
20+
return true;
21+
}
22+
23+
const latestRelease = await getLatestSqliteCommandRelease();
24+
return semver.lt( currentVersion, latestRelease.tag_name );
25+
}
26+
27+
async function getSqliteCommandVersion( installationPath: string ) {
28+
try {
29+
const versionValue = await fs.promises.readFile(
30+
path.join( installationPath, 'version' ),
31+
'utf8'
32+
);
33+
return semver.coerce( versionValue );
34+
} catch ( _error ) {
35+
return null;
36+
}
37+
}
38+
39+
const getLatestSqliteCommandRelease = cacheFunctionTTL( () =>
40+
fetchLatestGithubRelease( 'automattic/wp-cli-sqlite-command' )
41+
);
42+
43+
export async function updateLatestSqliteCommandVersion() {
44+
const isNeedingUpdate = await needsUpdate();
45+
46+
if ( ! isNeedingUpdate ) {
47+
return;
48+
}
49+
50+
const latestRelease = await getLatestSqliteCommandRelease();
51+
const downloadUrl = latestRelease.assets?.[ 0 ].browser_download_url;
52+
const tmpDownloadPath = path.join( os.tmpdir(), `sqlite-command-${ crypto.randomUUID() }.zip` );
53+
54+
try {
55+
await downloadFile( downloadUrl, tmpDownloadPath );
56+
await extractZip( tmpDownloadPath, getSqliteCommandPath() );
57+
} finally {
58+
await fs.promises.rm( tmpDownloadPath, { force: true } );
59+
}
60+
}

0 commit comments

Comments
 (0)