Skip to content
Draft
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
53 changes: 52 additions & 1 deletion packages/app/src/cli/services/build/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {beforeEach, describe, expect, test, vi} from 'vitest'
import {exec} from '@shopify/cli-kit/node/system'
import lockfile from 'proper-lockfile'
import {AbortError} from '@shopify/cli-kit/node/error'
import {fileExistsSync} from '@shopify/cli-kit/node/fs'
import {fileExistsSync, readFile} from '@shopify/cli-kit/node/fs'
import * as outputModule from '@shopify/cli-kit/node/output'

vi.mock('@shopify/cli-kit/node/system')
vi.mock('../function/build.js')
Expand Down Expand Up @@ -416,4 +417,54 @@ describe('buildFunctionExtension', () => {
expect(releaseLock).toHaveBeenCalled()
expect(runWasmOpt).toHaveBeenCalled()
})

describe('schema version mismatch warning', () => {
function mockSchemaFile(content: string) {
vi.mocked(readFile).mockResolvedValueOnce(content)
}

function mockSchemaNotFound() {
const err = new Error('ENOENT') as NodeJS.ErrnoException
err.code = 'ENOENT'
vi.mocked(readFile).mockRejectedValueOnce(err)
}

test('warns when schema version does not match toml version', async () => {
const warnSpy = vi.spyOn(outputModule, 'outputWarn').mockImplementation(() => {})
mockSchemaFile('schema @apiVersion(version: "2025-01") {\n query: QueryRoot\n}')

await buildFunctionExtension(extension, {stdout, stderr, signal, app, environment: 'production'})

expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining('schema.graphql was generated for API version 2025-01 but your extension targets 2022-07'),
)
})

test('does not warn when schema version matches toml version', async () => {
const warnSpy = vi.spyOn(outputModule, 'outputWarn').mockImplementation(() => {})
mockSchemaFile('schema @apiVersion(version: "2022-07") {\n query: QueryRoot\n}')

await buildFunctionExtension(extension, {stdout, stderr, signal, app, environment: 'production'})

expect(warnSpy).not.toHaveBeenCalled()
})

test('does not warn when schema has no apiVersion directive', async () => {
const warnSpy = vi.spyOn(outputModule, 'outputWarn').mockImplementation(() => {})
mockSchemaFile('type Query { shop: Shop }')

await buildFunctionExtension(extension, {stdout, stderr, signal, app, environment: 'production'})

expect(warnSpy).not.toHaveBeenCalled()
})

test('does not warn when schema file does not exist', async () => {
const warnSpy = vi.spyOn(outputModule, 'outputWarn').mockImplementation(() => {})
mockSchemaNotFound()

await buildFunctionExtension(extension, {stdout, stderr, signal, app, environment: 'production'})

expect(warnSpy).not.toHaveBeenCalled()
})
})
})
28 changes: 27 additions & 1 deletion packages/app/src/cli/services/build/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {AbortSignal} from '@shopify/cli-kit/node/abort'
import {AbortError, AbortSilentError} from '@shopify/cli-kit/node/error'
import lockfile from 'proper-lockfile'
import {dirname, joinPath} from '@shopify/cli-kit/node/path'
import {outputDebug} from '@shopify/cli-kit/node/output'
import {outputDebug, outputWarn} from '@shopify/cli-kit/node/output'
import {readFile, touchFile, writeFile, fileExistsSync} from '@shopify/cli-kit/node/fs'
import {Writable} from 'stream'

Expand Down Expand Up @@ -135,6 +135,8 @@ export async function buildFunctionExtension(
extension: ExtensionInstance,
options: BuildFunctionExtensionOptions,
): Promise<void> {
await warnIfSchemaMismatch(extension as ExtensionInstance<FunctionConfigType>)

const lockfilePath = joinPath(extension.directory, '.build-lock')
let releaseLock
try {
Expand Down Expand Up @@ -232,6 +234,30 @@ async function buildOtherFunction(extension: ExtensionInstance, options: BuildFu
return runCommand(extension.buildCommand, extension, options)
}

const API_VERSION_DIRECTIVE_RE = /@apiVersion\(version:\s*"([^"]+)"\)/

async function warnIfSchemaMismatch(extension: ExtensionInstance<FunctionConfigType>) {
const schemaPath = joinPath(extension.directory, 'schema.graphql')
let content: string
try {
content = await readFile(schemaPath)
} catch (error) {
if (error instanceof Error && 'code' in error) return
throw error
}

const match = API_VERSION_DIRECTIVE_RE.exec(content)
if (!match) return

const schemaVersion = match[1]!
const tomlVersion = extension.configuration.api_version
if (schemaVersion !== tomlVersion) {
outputWarn(
`schema.graphql was generated for API version ${schemaVersion} but your extension targets ${tomlVersion}. Run \`shopify app function schema\` to update.`,
)
}
}

async function runCommand(buildCommand: string, extension: ExtensionInstance, options: BuildFunctionExtensionOptions) {
const buildCommandComponents = buildCommand.split(' ')
options.stdout.write(`Building function ${extension.localIdentifier}...`)
Expand Down
Loading