diff --git a/src/commands/commerce/init.js b/src/commands/commerce/init.js index 5faeef3..49c7f3e 100644 --- a/src/commands/commerce/init.js +++ b/src/commands/commerce/init.js @@ -17,7 +17,11 @@ import { promptConfirm } from '../../utils/prompt.js' import config from '@adobe/aio-lib-core-config' import { createRepo, modifyFstab, modifySidekickConfig } from '../../utils/github.js' import { initialization } from '../../utils/initialization.js' -import { createMesh, checkAndRetryMeshUpdate, getMeshDetailsPage, confirmAPIMeshCreation } from '../../utils/mesh.js' +import { createMesh, getMeshDetailsPage, confirmAPIMeshCreation } from '../../utils/mesh.js' +import { spawn } from 'child_process' +import { openSync } from 'fs' +import Logger from '@adobe/aio-lib-core-logging' +const aioLogger = Logger('commerce:init.js') const reset = '\x1b[0m' const boldWhite = '\x1b[1m\x1b[37m' @@ -38,6 +42,34 @@ export class InitCommand extends Command { if (shouldCreateMesh) { const installedPlugins = this.config.plugins await createMesh(runAIOCommand, installedPlugins) + console.log( + '⏳ Verifying Mesh provisioning behind the scenes. Please check mesh-verify.log for details, or run "aio commerce:mesh-verify" if there are failures.' + ) + // Spawn detached child process to verify mesh in the background, without disrupting user's CLI session. + try { + const out = openSync('./mesh-verify.log', 'w') + const err = openSync('./mesh-verify.log', 'a') + const { org, project, workspace } = config.get('console') + const orgID = org.id + const projectID = project.id + const workspaceID = workspace.id + + const childProcess = spawn( + 'aio', + ['commerce:mesh-verify', '--orgId', orgID, '--projectId', projectID, '--workspaceId', workspaceID], + { + detached: false, + stdio: ['ignore', out, err] + } + ) + // Detach from the parent process + childProcess.unref() + } catch (error) { + aioLogger.debug(error) + console.log( + '❌ Unable to verify mesh provisioning. Please try again with "aio commerce:mesh-verify"' + ) + } } else { // this means the user chose a non-demo endpoint and still opted out of // API Mesh creation. Use their endpoints in configs.js @@ -50,6 +82,9 @@ export class InitCommand extends Command { console.log('Not creating API Mesh - will use demo environment.') } + const meshDetailsPageURL = getMeshDetailsPage() + const meshUrl = config.get('commerce.datasource.meshUrl') + await createRepo() await modifyFstab() await modifySidekickConfig() @@ -65,28 +100,16 @@ export class InitCommand extends Command { await previewContent(filePaths) await publishContent() - // TODO: this fails with - // 2025-02-04T17:42:36.664Z [commerce:mesh.js] error: TypeError: Cannot read properties of undefined (reading 'id') - // at getMeshDetailsPage (aio-cli-plugin-commerce/src/utils/mesh.js:378:35) - // const meshDetailsPageURL = getMeshDetailsPage() - const meshUrl = config.get('commerce.datasource.meshUrl') - console.log(`🎉 ${boldWhite}Setup complete!${reset} 🎉`) console.log(`${boldWhite}Customize your code:${reset} https://github.com/${githubOrg}/${githubRepo}`) console.log(`${boldWhite}Edit your content:${reset} https://da.live/#/${githubOrg}/${githubRepo}`) console.log(`${boldWhite}Manage your config:${reset} https://da.live/sheet#/${githubOrg}/${githubRepo}/configs-stage`) console.log(`${boldWhite}Preview your storefront:${reset} https://main--${githubRepo}--${githubOrg}.aem.page/`) meshUrl && console.log(`${boldWhite}Try out your API:${reset} ${meshUrl}`) - // meshDetailsPageURL && console.log(`${boldWhite}View your Mesh details:${reset} ${meshDetailsPageURL}`) + meshDetailsPageURL && console.log(`${boldWhite}View your Mesh details:${reset} ${meshDetailsPageURL}`) console.log(`${boldWhite}Run locally:${reset} "aio commerce:dev"`) console.log('For next steps, including how to customize your storefront and make it your own, check out our docs:\nhttps://experienceleague.adobe.com/developer/commerce/storefront/') - // if we created a mesh, wait for verification to complete before exiting - // TODO: Replace with detached childProcess. - if (shouldCreateMesh) { - await checkAndRetryMeshUpdate(runAIOCommand) - } - // cleanup config.delete('commerce') // reset github org and repo, for aio commerce:dev command diff --git a/src/commands/commerce/mesh-verify.js b/src/commands/commerce/mesh-verify.js new file mode 100644 index 0000000..bf96629 --- /dev/null +++ b/src/commands/commerce/mesh-verify.js @@ -0,0 +1,70 @@ +/* +Copyright 2024 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { Command, Flags } from '@oclif/core' + +import { checkAndRetryMeshUpdate } from '../../utils/mesh.js' +import { runCommand } from '../../utils/runCommand.js' + +const MESH_RETRIES = 2 +const MESH_RETRY_INTERVAL = 90000 // 1.5 minutes + +export class MeshVerify extends Command { + async run () { + const { flags } = await this.parse(MeshVerify) + const runAIOCommand = async (command, args) => { + return await this.config.runCommand(command, args) + } + + try { + const { orgId, projectId, workspaceId } = flags + + console.log(`Setting orgId: ${orgId}`) + await runCommand(`aio console org select ${orgId}`) + + console.log(`Setting projectId: ${projectId}`) + await runCommand(`aio console project select ${projectId}`) + + console.log(`Setting workspaceId: ${workspaceId}`) + await runCommand(`aio console workspace select ${workspaceId}`) + } catch (e) { + // If the user does not provide the required arguments, we will not attempt to select them and depend on preselected options from aio config. + } + + await checkAndRetryMeshUpdate( + runAIOCommand, + MESH_RETRIES, + MESH_RETRY_INTERVAL + ) + } +} + +MeshVerify.flags = { + orgId: Flags.string({ + name: 'orgId', + char: 'o', + required: false, + description: 'Organization ID' + }), + projectId: Flags.string({ + name: 'projectId', + required: false, + description: 'Project ID' + }), + workspaceId: Flags.string({ + name: 'workspaceId', + required: false, + description: 'Workspace ID' + }) +} + +MeshVerify.description = + 'Verifies that the Mesh is deployed and attempts to recreate it if deployment fails.' diff --git a/src/commands/commerce/test.js b/src/commands/commerce/test.js index 64ccf47..d6cd089 100644 --- a/src/commands/commerce/test.js +++ b/src/commands/commerce/test.js @@ -10,15 +10,31 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ import { Command, Help } from '@oclif/core' -import { checkAndRetryMeshUpdate } from '../../utils/mesh.js' +import { spawn } from 'child_process' +import { openSync } from 'fs' + export class TestCommand extends Command { async run () { - const runAIOCommand = async (command, args) => { - return await this.config.runCommand(command, args) - } - await checkAndRetryMeshUpdate(runAIOCommand) + const { args } = this.parse(TestCommand) + + // Spawn a detached child process to run the background task + try { + const out = openSync('./mesh-verify.log', 'w') + const err = openSync('./mesh-verify.log', 'a') + const childProcess = spawn( + 'aio', + ['commerce:mesh-verify'], + { + detached: true, + stdio: ['ignore', out, err] + } + ) + // Detach from the parent process + childProcess.unref() + } catch (error) {} } } -TestCommand.description = 'Spin up an Adobe Commerce Storefront on EDS using this CLI tool' +TestCommand.description = + "Spin up an Adobe Commerce Storefront on EDS using this CLI tool";