Add typed JSON output to app info - #8575
gonzaloriestra wants to merge 6 commits into
Conversation
b31faa2 to
7304cb4
Compare
|
/snapit |
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/public/node/testing/output.d.ts@@ -8,6 +8,19 @@ interface OutputMock {
error: () => string;
clear: () => void;
}
+interface StandardStreamsMock {
+ stdout: () => string;
+ stderr: () => string;
+ restore: () => void;
+}
+/**
+ * Captures writes to stdout and stderr, including console warnings intercepted by Vitest.
+ * Call restore in a finally block. This replaces process globals and must not be used in concurrent tests.
+ * CLI output tests must disable SHOPIFY_UNIT_TEST and reset modules before loading the command.
+ *
+ * @returns Captured output and a function to restore the original writers.
+ */
+export declare function mockAndCaptureStandardStreams(): StandardStreamsMock;
/**
* Returns a set of functions to get the outputs ocurred during a test run.
*
|
| experience: zod.enum(['extension', 'configuration']), | ||
| uidStrategy: zod.enum(['single', 'dynamic', 'uuid']), | ||
| }) | ||
| .passthrough() |
There was a problem hiding this comment.
Could we make the dependency on ExtensionSpecification explicit instead of coupling the public contract through .passthrough() and JSON.stringify? This schema documents only a subset, but the snapshot shows passthrough fields such as clientSteps and dependency; future enumerable internal fields could therefore enter the public JSON without appearing in --json-schema.
I'm wondering if we should be fully-explicit with what we support, rather than using passthrough?
There was a problem hiding this comment.
Nit (not on this file) -- missing changeset?
| configuration: configurationSchema, | ||
| webs: zod.array(webSchema), | ||
| dotenv: zod | ||
| .object({path: zod.string(), variables: zod.record(zod.string())}) |
There was a problem hiding this comment.
I wonder if we need to redact sensitive fields before making this a public contract?
Agent lingo about it: The legacy object spread already emits dotenv.variables and _hiddenConfig, but that does not make them safe to formalize. .env values routinely contain credentials, and the newly added raw config-file contents need the same review. remoteAppInfoSchema deliberately excludes apiSecretKeys; The broader result needs an equivalent allow-list, with tests proving secrets are not emitted, even if that requires an explicit compatibility decision.
| } | ||
| ), | ||
| ) | ||
| return appInfoJsonOutputSchema.validate(result) |
There was a problem hiding this comment.
Nit from the LLM: Do we want normal text execution to depend on constructing, JSON-stringifying, and validating the entire public JSON payload? The text branch in renderAppInfoResult still renders from the original app, remoteApp, organization, and project context, using only result.account. A JSON-only serialization or schema problem can therefore make shopify app info fail even without --json. I think either the text presenter should consume AppInfoResult as its source of truth, completing the boundary, or JSON-only materialization and validation should remain on the JSON path.
WHY are these changes introduced?
Related to https://github.com/shop/issues-develop/issues/23670.
app infolacks a declared JSON contract and omits public data already loaded by the command.WHAT is this pull request doing?
Return typed app information and move terminal formatting into a presenter. Preserve existing JSON fields and
--web-env, while adding remote app details, cached account identity, discovered configuration files, extension metadata, resolved dev store and system information without additional API requests.Expose the schema in help and refresh generated documentation. Share
mockAndCaptureStandardStreams()in CLI Kit for command output tests. Text output, extension filtering and invalid-app exit behavior are preserved.Output example: text vs JSON
Illustrative linked app. Both outputs are excerpts; the terminal border, other text sections and additional JSON fields are omitted.
Text —
shopify app info --path /path/to/appJSON —
shopify app info --path /path/to/app --json{ "name": "Example app", "directory": "/path/to/app", "configPath": "/path/to/app/shopify.app.toml", "configuration": { "client_id": "example-client-id", "name": "Example app", "application_url": "https://example.com", "embedded": true, "access_scopes": { "scopes": "read_products" }, "build": { "dev_store_url": "example.myshopify.com", "automatically_update_urls_on_dev": true }, "webhooks": { "api_version": "2026-07" } }, "remoteApp": { "id": "456", "title": "Example app", "apiKey": "example-client-id", "organizationId": "123", "grantedScopes": ["read_products"], "flags": [] }, "account": { "type": "UserAccount", "email": "developer@example.com" }, "devStoreUrl": "example.myshopify.com", "system": { "cliVersion": "3.90.0", "nodeVersion": "v22.18.0", "platform": "darwin", "arch": "arm64", "shell": "/bin/zsh" } }How to manually test your changes?
Checklist