From c36d5c2ebf1aab82401329c0cdc10a2b280ec0dd Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Mon, 11 May 2026 13:32:11 +0200 Subject: [PATCH] Remove deprecated --force flag from `shopify app release` The --force (-f) flag and SHOPIFY_FLAG_FORCE env var on `shopify app release` were deprecated in favor of --allow-updates / --allow-deletes. This change removes them along with the related deprecation warning and tests. The internal `force` (skip-confirmation-prompt) parameter passed to the release service is now derived as `--allow-updates && --allow-deletes`, preserving the prior semantics: passing both flags skips the prompt. --- .changeset/remove-app-release-force-flag.md | 5 ++ .../interfaces/app-release.interface.ts | 6 -- .../app/src/cli/commands/app/release.test.ts | 81 ------------------- packages/app/src/cli/commands/app/release.ts | 37 +++------ packages/cli/README.md | 2 - packages/cli/oclif.manifest.json | 9 --- 6 files changed, 14 insertions(+), 126 deletions(-) create mode 100644 .changeset/remove-app-release-force-flag.md delete mode 100644 packages/app/src/cli/commands/app/release.test.ts diff --git a/.changeset/remove-app-release-force-flag.md b/.changeset/remove-app-release-force-flag.md new file mode 100644 index 00000000000..ec9ef5ba8bb --- /dev/null +++ b/.changeset/remove-app-release-force-flag.md @@ -0,0 +1,5 @@ +--- +'@shopify/app': major +--- + +Remove the deprecated `--force` (`-f`) flag and `SHOPIFY_FLAG_FORCE` environment variable from `shopify app release`. Use `--allow-updates` for CI/CD environments, or `--allow-updates --allow-deletes` if you also want to allow removals. Passing both `--allow-updates` and `--allow-deletes` skips the confirmation prompt (matching the previous `--force` behavior). diff --git a/docs-shopify.dev/commands/interfaces/app-release.interface.ts b/docs-shopify.dev/commands/interfaces/app-release.interface.ts index eb392e1515d..ea024422430 100644 --- a/docs-shopify.dev/commands/interfaces/app-release.interface.ts +++ b/docs-shopify.dev/commands/interfaces/app-release.interface.ts @@ -28,12 +28,6 @@ export interface apprelease { */ '-c, --config '?: string - /** - * [Deprecated] Release without asking for confirmation. Equivalent to --allow-updates --allow-deletes. Use --allow-updates for CI/CD environments instead. - * @environment SHOPIFY_FLAG_FORCE - */ - '-f, --force'?: '' - /** * Disable color output. * @environment SHOPIFY_FLAG_NO_COLOR diff --git a/packages/app/src/cli/commands/app/release.test.ts b/packages/app/src/cli/commands/app/release.test.ts deleted file mode 100644 index e79bb466186..00000000000 --- a/packages/app/src/cli/commands/app/release.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import Release from './release.js' -import {testAppLinked, testDeveloperPlatformClient, testOrganizationApp} from '../../models/app/app.test-data.js' -import {OrganizationSource} from '../../models/organization.js' -import {describe, expect, test, vi, beforeEach} from 'vitest' -import {renderWarning} from '@shopify/cli-kit/node/ui' - -vi.mock('../../services/release.js') -vi.mock('../../services/app-context.js') -vi.mock('@shopify/cli-kit/node/metadata', async (importOriginal) => { - const actual = await importOriginal() - return {...actual, addPublicMetadata: vi.fn()} -}) -vi.mock('@shopify/cli-kit/node/ui', async (importOriginal) => { - const actual = await importOriginal() - return {...actual, renderWarning: vi.fn()} -}) - -describe('app release --force deprecation warning', () => { - beforeEach(async () => { - const {linkedAppContext} = await import('../../services/app-context.js') - const {release} = await import('../../services/release.js') - vi.mocked(linkedAppContext).mockResolvedValue({ - app: testAppLinked(), - remoteApp: testOrganizationApp(), - developerPlatformClient: testDeveloperPlatformClient(), - organization: { - id: '1', - businessName: 'test', - source: OrganizationSource.Partners, - }, - specifications: [], - project: {} as any, - activeConfig: {} as any, - }) - vi.mocked(release).mockResolvedValue(undefined) - }) - - test('shows deprecation warning when --force is passed', async () => { - await Release.run(['--version', 'v1.0.0', '--force']) - - expect(renderWarning).toHaveBeenCalledWith( - expect.objectContaining({ - headline: expect.arrayContaining(['The']), - body: expect.arrayContaining(['Use']), - }), - ) - const call = vi.mocked(renderWarning).mock.calls[0]![0] - expect(JSON.stringify(call)).toContain('--force') - expect(JSON.stringify(call)).toContain('next major release') - }) - - test('shows deprecation warning when SHOPIFY_FLAG_FORCE env var is set', async () => { - vi.stubEnv('SHOPIFY_FLAG_FORCE', '1') - - await Release.run(['--version', 'v1.0.0']) - - expect(renderWarning).toHaveBeenCalled() - const call = vi.mocked(renderWarning).mock.calls[0]![0] - expect(JSON.stringify(call)).toContain('--force') - - vi.unstubAllEnvs() - }) - - test('does not show deprecation warning when only --allow-updates is passed', async () => { - await Release.run(['--version', 'v1.0.0', '--allow-updates']) - - expect(renderWarning).not.toHaveBeenCalled() - }) - - test('does not show deprecation warning when --allow-updates and --allow-deletes are passed', async () => { - await Release.run(['--version', 'v1.0.0', '--allow-updates', '--allow-deletes']) - - expect(renderWarning).not.toHaveBeenCalled() - }) - - test('does not show deprecation warning when only --allow-deletes is passed', async () => { - await Release.run(['--version', 'v1.0.0', '--allow-deletes']) - - expect(renderWarning).not.toHaveBeenCalled() - }) -}) diff --git a/packages/app/src/cli/commands/app/release.ts b/packages/app/src/cli/commands/app/release.ts index c2da906e0f1..79d82322225 100644 --- a/packages/app/src/cli/commands/app/release.ts +++ b/packages/app/src/cli/commands/app/release.ts @@ -5,7 +5,6 @@ import {linkedAppContext} from '../../services/app-context.js' import {Flags} from '@oclif/core' import {globalFlags} from '@shopify/cli-kit/node/cli' import {addPublicMetadata} from '@shopify/cli-kit/node/metadata' -import {renderWarning} from '@shopify/cli-kit/node/ui' export default class Release extends AppLinkedCommand { static summary = 'Release an app version.' @@ -19,13 +18,6 @@ export default class Release extends AppLinkedCommand { static flags = { ...globalFlags, ...appFlags, - force: Flags.boolean({ - hidden: false, - description: - '[Deprecated] Release without asking for confirmation. Equivalent to --allow-updates --allow-deletes. Use --allow-updates for CI/CD environments instead.', - env: 'SHOPIFY_FLAG_FORCE', - char: 'f', - }), 'allow-updates': Flags.boolean({ hidden: false, description: @@ -50,27 +42,19 @@ export default class Release extends AppLinkedCommand { const {flags} = await this.parse(Release) const clientId = flags['client-id'] - if (flags.force) { - renderWarning({ - headline: ['The', {command: '--force'}, 'flag is deprecated and will be removed in the next major release.'], - body: [ - 'Use', - {command: '--allow-updates'}, - 'for CI/CD environments, or', - {command: '--allow-updates --allow-deletes'}, - 'if you also want to allow removals.', - ], - }) - } - await addPublicMetadata(() => ({ cmd_app_reset_used: flags.reset, })) - // We require --force or --allow-updates or --allow-deletes for non-TTY. + const allowUpdates = flags['allow-updates'] + const allowDeletes = flags['allow-deletes'] + // `force` (skip confirmation prompt) is implied when both --allow-updates + // and --allow-deletes are set. + const force = Boolean(allowUpdates && allowDeletes) + + // We require --allow-updates or --allow-deletes for non-TTY. const requiredNonTTYFlags: string[] = [] - const hasAnyForceFlags = flags.force || flags['allow-updates'] || flags['allow-deletes'] - if (!hasAnyForceFlags) { + if (!allowUpdates && !allowDeletes) { requiredNonTTYFlags.push('allow-updates') } this.failMissingNonTTYFlags(flags, requiredNonTTYFlags) @@ -82,14 +66,11 @@ export default class Release extends AppLinkedCommand { userProvidedConfigName: flags.config, }) - const allowUpdates = flags.force || flags['allow-updates'] - const allowDeletes = flags.force || flags['allow-deletes'] - await release({ app, remoteApp, developerPlatformClient, - force: flags.force, + force, allowUpdates, allowDeletes, version: flags.version, diff --git a/packages/cli/README.md b/packages/cli/README.md index 8bfb0312129..9fb91ca240e 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -928,8 +928,6 @@ USAGE FLAGS -c, --config= [env: SHOPIFY_FLAG_APP_CONFIG] The name of the app configuration. - -f, --force [env: SHOPIFY_FLAG_FORCE] [Deprecated] Release without asking for confirmation. Equivalent to - --allow-updates --allow-deletes. Use --allow-updates for CI/CD environments instead. --allow-deletes [env: SHOPIFY_FLAG_ALLOW_DELETES] Allows removing extensions and configuration without requiring user confirmation. For CI/CD environments, the recommended flag is --allow-updates. --allow-updates [env: SHOPIFY_FLAG_ALLOW_UPDATES] Allows adding and updating extensions and configuration diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index a1b7db2dccd..91429ecdf48 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -2863,15 +2863,6 @@ "name": "config", "type": "option" }, - "force": { - "allowNo": false, - "char": "f", - "description": "[Deprecated] Release without asking for confirmation. Equivalent to --allow-updates --allow-deletes. Use --allow-updates for CI/CD environments instead.", - "env": "SHOPIFY_FLAG_FORCE", - "hidden": false, - "name": "force", - "type": "boolean" - }, "no-color": { "allowNo": false, "description": "Disable color output.",