Skip to content

Add typed JSON output to app info - #8575

Open
gonzaloriestra wants to merge 6 commits into
mainfrom
gonzalo/json-app-info
Open

gonzaloriestra wants to merge 6 commits into
mainfrom
gonzalo/json-app-info

Conversation

@gonzaloriestra

@gonzaloriestra gonzaloriestra commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

Related to https://github.com/shop/issues-develop/issues/23670.

app info lacks 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.

Textshopify app info --path /path/to/app

CURRENT APP CONFIGURATION

Configuration file  shopify.app.toml
App name            Example app
Client ID           example-client-id
Organization        Example organization (123)
Access scopes       read_products
Dev store           example.myshopify.com
Update URLs         Yes
User                developer@example.com

JSONshopify 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?

pnpm shopify app info --path /path/to/app
pnpm shopify app info --path /path/to/app --json
pnpm shopify app info --path /path/to/app --web-env
pnpm shopify app info --path /path/to/app --web-env --json
pnpm shopify app info --json-schema

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact

@github-actions github-actions Bot added the Area: @shopify/cli @shopify/cli package issues label Sep 17, 2026
@github-actions github-actions Bot added no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. and removed Area: @shopify/cli @shopify/cli package issues labels Sep 18, 2026
@gonzaloriestra gonzaloriestra changed the title Add typed JSON result contracts to app info Add typed JSON output to app info Sep 18, 2026
@gonzaloriestra

Copy link
Copy Markdown
Contributor Author

/snapit

@github-actions

Copy link
Copy Markdown
Contributor

Differences in type declarations

We 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:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/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.
  *

@gonzaloriestra
gonzaloriestra marked this pull request as ready for review September 21, 2026 08:52
@gonzaloriestra
gonzaloriestra requested review from a team as code owners September 21, 2026 08:52
experience: zod.enum(['extension', 'configuration']),
uidStrategy: zod.enum(['single', 'dynamic', 'uuid']),
})
.passthrough()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (not on this file) -- missing changeset?

configuration: configurationSchema,
webs: zod.array(webSchema),
dotenv: zod
.object({path: zod.string(), variables: zod.record(zod.string())})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants