Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-cli-version-reporting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopify/shop-cli": patch
---

Fix `shop --version` and the User-Agent header reporting a stale hard-coded version.
13 changes: 12 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { createRequire } from 'node:module'

function readPackageVersion(): string {
const packageJson = createRequire(import.meta.url)('../package.json') as { version?: unknown }
if (typeof packageJson.version !== 'string' || packageJson.version.length === 0) {
throw new Error('package.json is missing a valid "version" field')
}
return packageJson.version
}

export const CLIENT_ID = '5c733ab2-1903-400a-891e-7ba20c09e2a3'
export const DEFAULT_AGENT_NAME = 'Shop CLI'
export const DEFAULT_COUNTRY = 'US'
export const DEFAULT_PROFILE_URL =
'https://shopify.dev/ucp/agent-profiles/2026-04-08/valid-with-capabilities.json'
export const GLOBAL_CATALOG_MCP_URL = 'https://catalog.shopify.com/api/ucp/mcp'
export const CLI_VERSION = '0.1.0'
// Changesets updates package.json, so use it as the single source of truth.
export const CLI_VERSION = readPackageVersion()
export const USER_AGENT = `shop-cli/${CLI_VERSION}`
// Authenticated global-catalog access uses a brokered RFC 8693 token exchange:
// audience=api.shopify.com + requested_token_type=...access_token returns a
Expand Down
25 changes: 25 additions & 0 deletions tests/version.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { readFileSync } from 'node:fs'
import { describe, it } from 'node:test'

import { createProgram } from '../src/cli.js'
import { USER_AGENT } from '../src/constants.js'
import { expect } from './harness.js'

const packageJson = JSON.parse(
readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
) as { version?: unknown }

describe('version metadata', () => {
it('the package version looks like a real semver, not a missing/blank field', () => {
expect(typeof packageJson.version).toBe('string')
expect(packageJson.version).toMatch(/^\d+\.\d+\.\d+/)
})

it('uses the package version for the CLI', () => {
expect(createProgram().version()).toBe(packageJson.version)
})

it('uses the package version for the User-Agent', () => {
expect(USER_AGENT).toBe(`shop-cli/${packageJson.version}`)
})
})
Loading