diff --git a/packages/app/src/cli/commands/app/logs.ts b/packages/app/src/cli/commands/app/logs.ts index c82097da1e8..e7afd4bc986 100644 --- a/packages/app/src/cli/commands/app/logs.ts +++ b/packages/app/src/cli/commands/app/logs.ts @@ -32,6 +32,11 @@ export default class Logs extends Command { options: ['success', 'failure'], env: 'SHOPIFY_FLAG_STATUS', }), + json: Flags.boolean({ + char: 'j', + description: 'Log the run result as a JSON object.', + env: 'SHOPIFY_FLAG_JSON', + }), } public async run(): Promise { @@ -53,6 +58,7 @@ export default class Logs extends Command { status: flags.status, configName: flags.config, reset: flags.reset, + json: flags.json, } await logs(logOptions) diff --git a/packages/app/src/cli/services/context.ts b/packages/app/src/cli/services/context.ts index 51d26845b44..273d48d31ca 100644 --- a/packages/app/src/cli/services/context.ts +++ b/packages/app/src/cli/services/context.ts @@ -42,6 +42,7 @@ import {outputContent} from '@shopify/cli-kit/node/output' import {getOrganization} from '@shopify/cli-kit/node/environment' import {basename, joinPath} from '@shopify/cli-kit/node/path' import {glob} from '@shopify/cli-kit/node/fs' +import {sniffForJson} from '@shopify/cli-kit/node/path' export const InvalidApiKeyErrorMessage = (apiKey: string) => { return { @@ -816,6 +817,7 @@ interface ReusedValuesOptions { */ function showReusedDevValues({organization, selectedApp, selectedStore, cachedInfo}: ReusedValuesOptions) { if (!cachedInfo) return + if (sniffForJson()) return let updateURLs = 'Not yet configured' if (cachedInfo.updateURLs !== undefined) updateURLs = cachedInfo.updateURLs ? 'Yes' : 'No' diff --git a/packages/app/src/cli/services/logs.ts b/packages/app/src/cli/services/logs.ts index 0b3d334fd1e..f2444b75da5 100644 --- a/packages/app/src/cli/services/logs.ts +++ b/packages/app/src/cli/services/logs.ts @@ -4,6 +4,24 @@ import {subscribeToAppLogs} from './app-logs/utils.js' import {selectDeveloperPlatformClient, DeveloperPlatformClient} from '../utilities/developer-platform-client.js' import {loadAppConfiguration} from '../models/app/loader.js' import {AppInterface} from '../models/app/app.js' +import {pollAppLogs} from './app-logs/logs-command/poll-app-logs.js' +import {PollOptions, SubscribeOptions} from './app-logs/types.js' +import { + POLLING_ERROR_RETRY_INTERVAL_MS, + ONE_MILLION, + POLLING_INTERVAL_MS, + POLLING_THROTTLE_RETRY_INTERVAL_MS, + parseFunctionRunPayload, + LOG_TYPE_FUNCTION_RUN, + LOG_TYPE_RESPONSE_FROM_CACHE, + parseNetworkAccessResponseFromCachePayload, + LOG_TYPE_REQUEST_EXECUTION_IN_BACKGROUND, + parseNetworkAccessRequestExecutionInBackgroundPayload, + LOG_TYPE_REQUEST_EXECUTION, + parseNetworkAccessRequestExecutedPayload, +} from './app-logs/utils.js' +import {ErrorResponse, SuccessResponse, AppLogOutput, PollFilters, AppLogPayload} from './app-logs/types.js' +import {outputInfo} from '@shopify/cli-kit/node/output' interface LogsOptions { directory: string @@ -37,13 +55,21 @@ export async function logs(commandOptions: LogsOptions) { filters, } - await renderLogs({ + await renderJsonLogs({ options: { variables, developerPlatformClient: logsConfig.developerPlatformClient, }, pollOptions, }) + + // await renderLogs({ + // options: { + // variables, + // developerPlatformClient: logsConfig.developerPlatformClient, + // }, + // pollOptions, + // }) } async function prepareForLogs(commandOptions: LogsOptions): Promise<{ @@ -71,3 +97,55 @@ async function prepareForLogs(commandOptions: LogsOptions): Promise<{ localApp, } } + +async function renderJsonLogs({ + pollOptions: {cursor, filters, jwtToken}, + options: {variables, developerPlatformClient}, +}: { + pollOptions: PollOptions + options: SubscribeOptions +}): Promise { + const response = await pollAppLogs({cursor, filters, jwtToken}) + let nextInterval = POLLING_INTERVAL_MS + let nextJwtToken = jwtToken + + const {errors} = response as ErrorResponse + + if (errors && errors.length > 0) { + if (errors.some((error) => error.status === 401)) { + const nextJwtToken = await subscribeToAppLogs(developerPlatformClient, variables) + } else if (errors.some((error) => error.status === 429)) { + nextInterval = POLLING_THROTTLE_RETRY_INTERVAL_MS + } else { + nextInterval = POLLING_ERROR_RETRY_INTERVAL_MS + + outputInfo( + JSON.stringify({ + errors: errors, + retrying_in_ms: nextInterval, + }), + ) + } + } + + const {cursor: nextCursor, appLogs} = response as SuccessResponse + + if (appLogs) { + appLogs.forEach((log) => { + outputInfo(JSON.stringify(log)) + }) + } + + setTimeout(() => { + renderJsonLogs({ + options: {variables: variables, developerPlatformClient: developerPlatformClient}, + pollOptions: { + jwtToken: nextJwtToken || jwtToken, + cursor: nextCursor || cursor, + filters, + }, + }).catch((error) => { + throw error + }) + }, nextInterval) +} diff --git a/packages/cli/README.md b/packages/cli/README.md index 429d6d41cae..10ad1777a5c 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -763,8 +763,8 @@ USAGE [--entry ] [--lockfile-check] [--path ] [--sourcemap] [--watch] FLAGS - --[no-]bundle-stats Show a bundle size summary after building. Defaults to true, use `--no-bundle-stats` to - disable. + --[no-]bundle-stats [Classic Remix Compiler] Show a bundle size summary after building. Defaults to true, + use `--no-bundle-stats` to disable. --codegen Automatically generates GraphQL types for your project’s Storefront API queries. --codegen-config-path= Specifies a path to a codegen configuration file. Defaults to `/codegen.ts` if this file exists. @@ -869,44 +869,38 @@ Builds and deploys a Hydrogen storefront to Oxygen. ``` USAGE - $ shopify hydrogen deploy [--auth-bypass-token-duration --auth-bypass-token] [--build-command ] - [--entry ] [--env | --env-branch ] [--env-file ] [-f] [--json-output] - [--lockfile-check] [--metadata-description ] [--metadata-user ] [--no-verify] [--path ] - [--preview] [-s ] [-t ] - -FLAGS - -f, --force Forces a deployment to proceed if there are uncommited changes in its Git - repository. - -s, --shop= Shop URL. It can be the shop prefix (janes-apparel) or the full - myshopify.com URL (janes-apparel.myshopify.com, - https://janes-apparel.myshopify.com). - -t, --token= Oxygen deployment token. Defaults to the linked storefront's token if - available. - --auth-bypass-token Generate an authentication bypass token, which can be used to perform - end-to-end tests against the deployment. - --auth-bypass-token-duration= Specify the duration (in hours) up to 12 hours for the authentication bypass - token. Defaults to `2` - --build-command= Specify a build command to run before deploying. If not specified, `shopify - hydrogen build` will be used. - --entry= Entry file for the worker. Defaults to `./server`. - --env= Specifies the environment to perform the operation using its handle. Fetch - the handle using the `env list` command. - --env-branch= Specifies the environment to perform the operation using its Git branch - name. - --env-file= Path to an environment file to override existing environment variables for - the deployment. - --[no-]json-output Create a JSON file containing the deployment details in CI environments. - Defaults to true, use `--no-json-output` to disable. - --[no-]lockfile-check Checks that there is exactly one valid lockfile in the project. Defaults to - `true`. Deactivate with `--no-lockfile-check`. - --metadata-description= Description of the changes in the deployment. Defaults to the commit message - of the latest commit if there are no uncommited changes. - --metadata-user= User that initiated the deployment. Will be saved and displayed in the - Shopify admin - --no-verify Skip the routability verification step after deployment. - --path= The path to the directory of the Hydrogen storefront. Defaults to the - current directory where the command is run. - --preview Deploys to the Preview environment. Overrides --env-branch and Git metadata. + $ shopify hydrogen deploy [--auth-bypass-token] [--build-command ] [--entry ] [--env | + --env-branch ] [--env-file ] [-f] [--json-output] [--lockfile-check] [--metadata-description ] + [--metadata-user ] [--no-verify] [--path ] [--preview] [-s ] [-t ] + +FLAGS + -f, --force Forces a deployment to proceed if there are uncommited changes in its Git + repository. + -s, --shop= Shop URL. It can be the shop prefix (janes-apparel) or the full myshopify.com URL + (janes-apparel.myshopify.com, https://janes-apparel.myshopify.com). + -t, --token= Oxygen deployment token. Defaults to the linked storefront's token if available. + --auth-bypass-token Generate an authentication bypass token, which can be used to perform end-to-end + tests against the deployment. + --build-command= Specify a build command to run before deploying. If not specified, `shopify + hydrogen build` will be used. + --entry= Entry file for the worker. Defaults to `./server`. + --env= Specifies the environment to perform the operation using its handle. Fetch the + handle using the `env list` command. + --env-branch= Specifies the environment to perform the operation using its Git branch name. + --env-file= Path to an environment file to override existing environment variables for the + deployment. + --[no-]json-output Create a JSON file containing the deployment details in CI environments. Defaults + to true, use `--no-json-output` to disable. + --[no-]lockfile-check Checks that there is exactly one valid lockfile in the project. Defaults to + `true`. Deactivate with `--no-lockfile-check`. + --metadata-description= Description of the changes in the deployment. Defaults to the commit message of + the latest commit if there are no uncommited changes. + --metadata-user= User that initiated the deployment. Will be saved and displayed in the Shopify + admin + --no-verify Skip the routability verification step after deployment. + --path= The path to the directory of the Hydrogen storefront. Defaults to the current + directory where the command is run. + --preview Deploys to the Preview environment. Overrides --env-branch and Git metadata. DESCRIPTION Builds and deploys a Hydrogen storefront to Oxygen. @@ -1059,7 +1053,7 @@ Creates a new Hydrogen storefront. ``` USAGE $ shopify hydrogen init [-f] [--git] [--install-deps] [--language ] [--markets ] [--mock-shop] - [--path ] [--quickstart] [--routes] [--shortcut] [--styling ] [--template ] + [--path ] [--quickstart] [--routes] [--shortcut] [--template ] FLAGS -f, --force Overwrites the destination directory and files if they already exist. @@ -1076,8 +1070,6 @@ FLAGS --[no-]routes Generate routes for all pages. --[no-]shortcut Creates a global h2 shortcut for Shopify CLI using shell aliases. Deactivate with `--no-shortcut`. - --styling= Sets the styling strategy to use. One of `tailwind`, `vanilla-extract`, `css-modules`, - `postcss`, `none`. --template= Scaffolds project based on an existing template or example from the Hydrogen repository. DESCRIPTION @@ -1216,8 +1208,8 @@ USAGE $ shopify hydrogen setup css [STRATEGY] [-f] [--install-deps] [--path ] ARGUMENTS - STRATEGY (tailwind|vanilla-extract|css-modules|postcss) The CSS strategy to setup. One of - tailwind,vanilla-extract,css-modules,postcss + STRATEGY (tailwind|css-modules|vanilla-extract|postcss) The CSS strategy to setup. One of + tailwind,css-modules,vanilla-extract,postcss FLAGS -f, --force Overwrites the destination directory and files if they already exist. diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 43f6bc6472a..44d3e641b95 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -1620,6 +1620,14 @@ "name": "graphiql-port", "type": "option" }, + "json": { + "allowNo": false, + "char": "j", + "description": "Log the run result as a JSON object.", + "env": "SHOPIFY_FLAG_JSON", + "name": "json", + "type": "boolean" + }, "no-color": { "allowNo": false, "description": "Disable color output.", @@ -2514,7 +2522,7 @@ "flags": { "bundle-stats": { "allowNo": true, - "description": "Show a bundle size summary after building. Defaults to true, use `--no-bundle-stats` to disable.", + "description": "[Classic Remix Compiler] Show a bundle size summary after building. Defaults to true, use `--no-bundle-stats` to disable.", "name": "bundle-stats", "type": "boolean" }, @@ -2807,23 +2815,10 @@ "auth-bypass-token": { "allowNo": false, "description": "Generate an authentication bypass token, which can be used to perform end-to-end tests against the deployment.", - "env": "AUTH_BYPASS_TOKEN", "name": "auth-bypass-token", "required": false, "type": "boolean" }, - "auth-bypass-token-duration": { - "dependsOn": [ - "auth-bypass-token" - ], - "description": "Specify the duration (in hours) up to 12 hours for the authentication bypass token. Defaults to `2`", - "env": "AUTH_BYPASS_TOKEN_DURATION", - "hasDynamicHelp": false, - "multiple": false, - "name": "auth-bypass-token-duration", - "required": false, - "type": "option" - }, "build-command": { "description": "Specify a build command to run before deploying. If not specified, `shopify hydrogen build` will be used.", "hasDynamicHelp": false, @@ -3544,14 +3539,6 @@ "name": "shortcut", "type": "boolean" }, - "styling": { - "description": "Sets the styling strategy to use. One of `tailwind`, `vanilla-extract`, `css-modules`, `postcss`, `none`.", - "env": "SHOPIFY_HYDROGEN_FLAG_STYLING", - "hasDynamicHelp": false, - "multiple": false, - "name": "styling", - "type": "option" - }, "template": { "description": "Scaffolds project based on an existing template or example from the Hydrogen repository.", "env": "SHOPIFY_HYDROGEN_FLAG_TEMPLATE", @@ -3920,12 +3907,12 @@ ], "args": { "strategy": { - "description": "The CSS strategy to setup. One of tailwind,vanilla-extract,css-modules,postcss", + "description": "The CSS strategy to setup. One of tailwind,css-modules,vanilla-extract,postcss", "name": "strategy", "options": [ "tailwind", - "vanilla-extract", "css-modules", + "vanilla-extract", "postcss" ] }