From 51de06491fdc1d7c8939745517b7961c2c22fa06 Mon Sep 17 00:00:00 2001 From: Nick Wesselman <27013789+nickwesselman@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:57:04 -0400 Subject: [PATCH] Show the organization name and ID in app info The organization was already resolved by linkedAppContext and passed to info(), but only reached the --web-env path, where it was used solely for metadata logging. Forward it to infoApp so both output formats can report it. Text output gains an "Organization" row in the current app configuration section, using the "name (id)" form that `store list` already uses. JSON output gains an additive `organization` key alongside the existing injected project fields. Co-Authored-By: Claude Opus 5 (1M context) Assisted-By: devx/4874be33-ff52-4bcc-beaa-5ff567199ec5 --- .changeset/app-info-organization.md | 5 ++++ packages/app/src/cli/services/info.test.ts | 28 ++++++++++++++++++++++ packages/app/src/cli/services/info.ts | 17 ++++++++++--- 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 .changeset/app-info-organization.md diff --git a/.changeset/app-info-organization.md b/.changeset/app-info-organization.md new file mode 100644 index 00000000000..9ad7d7bba21 --- /dev/null +++ b/.changeset/app-info-organization.md @@ -0,0 +1,5 @@ +--- +'@shopify/app': minor +--- + +Show the organization name and ID in `app info` diff --git a/packages/app/src/cli/services/info.test.ts b/packages/app/src/cli/services/info.test.ts index 2b7acb04be0..354741b7e77 100644 --- a/packages/app/src/cli/services/info.test.ts +++ b/packages/app/src/cli/services/info.test.ts @@ -265,6 +265,34 @@ describe('info', () => { expect(rawResult.usesWorkspaces).toBe(false) }) }) + + test('returns the organization name and ID using the default output format', async () => { + await inTemporaryDirectory(async (tmp) => { + // Given + const app = mockApp({directory: tmp}) + + // When + const result = (await info(app, remoteApp, ORG1, testProject(), infoOptions())) as AlertCustomSection[] + const configData = tabularDataSectionFromInfo(result, 'CURRENT APP CONFIGURATION\n') + + // Then + expect(configData).toContainEqual(['Organization', 'test (123)']) + }) + }) + + test('returns the organization name and ID using the json output format', async () => { + await inTemporaryDirectory(async (tmp) => { + // Given + const app = mockApp({directory: tmp}) + + // When + const result = await info(app, remoteApp, ORG1, testProject(), {...infoOptions(), format: 'json'}) + + // Then + const resultObject = JSON.parse((result as TokenizedString).value) + expect(resultObject.organization).toEqual({id: '123', businessName: 'test'}) + }) + }) }) function mockApp({ diff --git a/packages/app/src/cli/services/info.ts b/packages/app/src/cli/services/info.ts index b616c4ab583..e08bbc1e5d1 100644 --- a/packages/app/src/cli/services/info.ts +++ b/packages/app/src/cli/services/info.ts @@ -37,7 +37,7 @@ export async function info( if (options.webEnv) { return infoWeb(app, remoteApp, organization, options) } else { - return infoApp(app, remoteApp, project, options) + return infoApp(app, remoteApp, organization, project, options) } } @@ -53,6 +53,7 @@ async function infoWeb( async function infoApp( app: AppLinkedInterface, remoteApp: OrganizationApp, + organization: Organization, project: Project, options: InfoOptions, ): Promise { @@ -63,6 +64,7 @@ async function infoApp( packageManager: project.packageManager, nodeDependencies: project.nodeDependencies, usesWorkspaces: project.usesWorkspaces, + organization: {id: organization.id, businessName: organization.businessName}, allExtensions: extensionsInfo, } if ('realExtensions' in appWithSupportedExtensions) { @@ -88,7 +90,7 @@ async function infoApp( 2, )}` } else { - const appInfo = new AppInfo(app, remoteApp, project, options) + const appInfo = new AppInfo(app, remoteApp, organization, project, options) return appInfo.output() } } @@ -120,12 +122,20 @@ const NOT_LOADED_TEXT = 'NOT LOADED' class AppInfo { private readonly app: AppLinkedInterface private readonly remoteApp: OrganizationApp + private readonly organization: Organization private readonly project: Project private readonly options: InfoOptions - constructor(app: AppLinkedInterface, remoteApp: OrganizationApp, project: Project, options: InfoOptions) { + constructor( + app: AppLinkedInterface, + remoteApp: OrganizationApp, + organization: Organization, + project: Project, + options: InfoOptions, + ) { this.app = app this.remoteApp = remoteApp + this.organization = organization this.project = project this.options = options } @@ -160,6 +170,7 @@ class AppInfo { ['Configuration file', {filePath: basename(this.app.configPath) || configurationFileNames.app}], ['App name', this.remoteApp.title ? {userInput: this.remoteApp.title} : NOT_CONFIGURED_TOKEN], ['Client ID', this.remoteApp.apiKey || NOT_CONFIGURED_TOKEN], + ['Organization', `${this.organization.businessName} (${this.organization.id})`], ['Access scopes', getAppScopes(this.app.configuration)], [ 'Dev store',