Skip to content

Add post-deploy message for extensions migration to Dev Dash #6104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
16 changes: 14 additions & 2 deletions packages/app/src/cli/services/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ export async function ensureThemeExtensionDevContext(
return registration
}

interface EnsureDeployContextResult {
identifiers: Identifiers
didMigrateExtensionsToDevDash: boolean
}

/**
* Make sure there is a valid context to execute `deploy`
* That means we have a valid session, organization and app.
Expand All @@ -140,7 +145,7 @@ export async function ensureThemeExtensionDevContext(
* @param developerPlatformClient - The client to access the platform API
* @returns The selected org, app and dev store
*/
export async function ensureDeployContext(options: DeployOptions): Promise<Identifiers> {
export async function ensureDeployContext(options: DeployOptions): Promise<EnsureDeployContextResult> {
const {reset, force, noRelease, app, remoteApp, developerPlatformClient, organization} = options
const activeAppVersion = await developerPlatformClient.activeAppVersion(remoteApp)

Expand All @@ -160,7 +165,14 @@ export async function ensureDeployContext(options: DeployOptions): Promise<Ident

await updateAppIdentifiers({app, identifiers, command: 'deploy', developerPlatformClient})

return identifiers
// if the current active app version is missing user_identifiers in some app module, then we are migrating to dev dash
let didMigrateExtensionsToDevDash = false
if (developerPlatformClient.supportsAtomicDeployments && activeAppVersion) {
const missingUids = activeAppVersion.appModuleVersions.some((version) => !version.registrationUuid)
didMigrateExtensionsToDevDash = missingUids
}

return {identifiers, didMigrateExtensionsToDevDash}
}

interface ShouldOrPromptIncludeConfigDeployOptions {
Expand Down
51 changes: 50 additions & 1 deletion packages/app/src/cli/services/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {AppInterface, AppLinkedInterface} from '../models/app/app.js'
import {OrganizationApp} from '../models/organization.js'
import {DeveloperPlatformClient} from '../utilities/developer-platform-client.js'
import {PosSpecIdentifier} from '../models/extensions/specifications/app_config_point_of_sale.js'
import {getTomls} from '../utilities/app/config/getTomls.js'
import {beforeEach, describe, expect, vi, test} from 'vitest'
import {renderInfo, renderSuccess, renderTasks, renderTextPrompt, Task} from '@shopify/cli-kit/node/ui'
import {formatPackageManagerCommand} from '@shopify/cli-kit/node/output'
Expand All @@ -44,6 +45,7 @@ vi.mock('@shopify/cli-kit/node/ui')
vi.mock('@shopify/cli-kit/node/crypto')
vi.mock('../validators/extensions.js')
vi.mock('./context/prompts')
vi.mock('../utilities/app/config/getTomls.js')

beforeEach(() => {
// this is needed because using importActual to mock the ui module
Expand Down Expand Up @@ -400,6 +402,7 @@ describe('deploy', () => {
// Then
expect(renderSuccess).toHaveBeenCalledWith({
headline: 'New version released to users.',
customSections: [],
body: [
{
link: {
Expand Down Expand Up @@ -433,6 +436,7 @@ describe('deploy', () => {
// Then
expect(renderInfo).toHaveBeenCalledWith({
headline: 'New version created, but not released.',
customSections: [],
body: [
{
link: {
Expand Down Expand Up @@ -466,6 +470,7 @@ describe('deploy', () => {
// Then
expect(renderSuccess).toHaveBeenCalledWith({
headline: 'New version created.',
customSections: [],
body: [
{
link: {
Expand All @@ -484,6 +489,45 @@ describe('deploy', () => {
],
})
})

test('shows a custom section when migrating extensions to dev dash', async () => {
// Given
const app = testAppLinked()

vi.mocked(getTomls).mockResolvedValue({
'111': 'shopify.app.prod.toml',
'222': 'shopify.app.stg.toml',
})

// When
await testDeployBundle({app, remoteApp, developerPlatformClient, didMigrateExtensionsToDevDash: true})

// Then
expect(renderSuccess).toHaveBeenCalledWith({
headline: 'New version released to users.',
body: [
{
link: {
label: 'unique-version-tag',
url: 'https://partners.shopify.com/0/apps/0/versions/1',
},
},
'',
],
customSections: [
{
title: 'Next steps',
body: [
'• Map extension IDs to other copies of your app by running',
{command: formatPackageManagerCommand(app.packageManager, 'shopify app deploy')},
'for: ',
{list: {items: ['shopify.app.prod.toml', 'shopify.app.stg.toml']}},
"• Commit to source control to ensure your extension IDs aren't regenerated on the next deploy.",
],
},
],
})
})
})

interface TestDeployBundleInput {
Expand All @@ -499,6 +543,7 @@ interface TestDeployBundleInput {
commitReference?: string
appToDeploy?: AppInterface
developerPlatformClient: DeveloperPlatformClient
didMigrateExtensionsToDevDash?: boolean
}

async function testDeployBundle({
Expand All @@ -509,6 +554,7 @@ async function testDeployBundle({
commitReference,
appToDeploy,
developerPlatformClient,
didMigrateExtensionsToDevDash = false,
}: TestDeployBundleInput) {
// Given
const extensionsPayload: {[key: string]: string} = {}
Expand All @@ -526,7 +572,10 @@ async function testDeployBundle({
extensionsNonUuidManaged: extensionsNonUuidPayload,
}

vi.mocked(ensureDeployContext).mockResolvedValue(identifiers)
vi.mocked(ensureDeployContext).mockResolvedValue({
identifiers,
didMigrateExtensionsToDevDash,
})

vi.mocked(uploadExtensionsBundle).mockResolvedValue({
validationErrors: [],
Expand Down
37 changes: 35 additions & 2 deletions packages/app/src/cli/services/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import {AppLinkedInterface} from '../models/app/app.js'
import {updateAppIdentifiers} from '../models/app/identifiers.js'
import {DeveloperPlatformClient} from '../utilities/developer-platform-client.js'
import {Organization, OrganizationApp} from '../models/organization.js'
import {getTomls} from '../utilities/app/config/getTomls.js'
import {renderInfo, renderSuccess, renderTasks} from '@shopify/cli-kit/node/ui'
import {mkdir} from '@shopify/cli-kit/node/fs'
import {joinPath, dirname} from '@shopify/cli-kit/node/path'
import {outputNewline, outputInfo, formatPackageManagerCommand} from '@shopify/cli-kit/node/output'
import {getArrayRejectingUndefined} from '@shopify/cli-kit/common/array'
import type {Task} from '@shopify/cli-kit/node/ui'
import type {AlertCustomSection, Task} from '@shopify/cli-kit/node/ui'

export interface DeployOptions {
/** The app to be built and uploaded */
Expand Down Expand Up @@ -53,7 +54,7 @@ interface TasksContext {
export async function deploy(options: DeployOptions) {
const {app, remoteApp, developerPlatformClient, noRelease} = options

const identifiers = await ensureDeployContext({...options, developerPlatformClient})
const {identifiers, didMigrateExtensionsToDevDash} = await ensureDeployContext({...options, developerPlatformClient})
const release = !noRelease
const apiKey = remoteApp.apiKey

Expand Down Expand Up @@ -128,6 +129,7 @@ export async function deploy(options: DeployOptions) {
app,
release,
uploadExtensionsBundleResult,
didMigrateExtensionsToDevDash,
})

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -147,30 +149,61 @@ async function outputCompletionMessage({
app,
release,
uploadExtensionsBundleResult,
didMigrateExtensionsToDevDash,
}: {
app: AppLinkedInterface
release: boolean
uploadExtensionsBundleResult: UploadExtensionsBundleOutput
didMigrateExtensionsToDevDash: boolean
}) {
const linkAndMessage = [
{link: {label: uploadExtensionsBundleResult.versionTag ?? 'version', url: uploadExtensionsBundleResult.location}},
uploadExtensionsBundleResult.message ? `\n${uploadExtensionsBundleResult.message}` : '',
]

let customSections: AlertCustomSection[] = []
if (didMigrateExtensionsToDevDash) {
const tomls = await getTomls(app.directory)
const tomlsWithoutCurrent = Object.values(tomls).filter((toml) => toml !== tomls[app.configuration.client_id])

customSections = [
{
title: 'Next steps',
body: [
'• Map extension IDs to other copies of your app by running',
{
command: formatPackageManagerCommand(app.packageManager, 'shopify app deploy'),
},
'for: ',
{
list: {
items: tomlsWithoutCurrent,
},
},
"• Commit to source control to ensure your extension IDs aren't regenerated on the next deploy.",
],
},
]
}

if (release) {
return uploadExtensionsBundleResult.deployError
? renderInfo({
headline: 'New version created, but not released.',
body: [...linkAndMessage, `\n\n${uploadExtensionsBundleResult.deployError}`],
customSections,
})
: renderSuccess({
headline: 'New version released to users.',
body: linkAndMessage,
customSections,
})
}

return renderSuccess({
headline: 'New version created.',
body: linkAndMessage,
customSections,
nextSteps: [
[
'Run',
Expand Down