Skip to content

Commit ab3ea53

Browse files
authored
Merge pull request #6463 from Shopify/jc.windows-on-arm-for-functions
Add support for Windows on ARM for Functions binaries
2 parents edcd06f + 835dd39 commit ab3ea53

File tree

3 files changed

+70
-37
lines changed

3 files changed

+70
-37
lines changed

.changeset/silly-impalas-unite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/app': patch
3+
---
4+
5+
Add support for Windows on ARM cpus for Functions binaries

packages/app/src/cli/services/function/binaries.test.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const javy = javyBinary()
1919
const javyPlugin = javyPluginBinary()
2020
const functionRunner = functionRunnerBinary()
2121

22+
const oldJavy = javyBinary('6.0.0')
23+
2224
vi.mock('@shopify/cli-kit/node/http', async () => {
2325
const actualImports = await vi.importActual('@shopify/cli-kit/node/http')
2426
return {
@@ -30,7 +32,7 @@ vi.mock('@shopify/cli-kit/node/http', async () => {
3032
describe('javy', () => {
3133
test('properties are set correctly', () => {
3234
expect(javy.name).toBe('javy')
33-
expect(javy.version).match(/^v\d\.\d\.\d$/)
35+
expect(javy.version).match(/^\d\.\d\.\d$/)
3436
if (process.platform === 'win32') {
3537
expect(javy.path).toContain(`javy-${PREFERRED_JAVY_VERSION}.exe`)
3638
} else {
@@ -117,13 +119,23 @@ describe('javy', () => {
117119
expect(() => javy.downloadUrl('darwin', arch)).toThrowError('Unsupported architecture ppc')
118120
})
119121

120-
test('Unsupported combination throws an error', () => {
122+
test('Supported combination on preferred Javy succeeds', () => {
123+
// When
124+
const url = javy.downloadUrl('win32', 'arm')
125+
126+
// Then
127+
expect(url).toMatch(
128+
/https:\/\/github.com\/bytecodealliance\/javy\/releases\/download\/v\d\.\d\.\d\/javy-arm-windows-v\d\.\d\.\d\.gz/,
129+
)
130+
})
131+
132+
test('Unsupported combination on old Javy throws an error', () => {
121133
// When
122134
const arch = 'arm'
123135
const platform = 'win32'
124136

125137
// Then
126-
expect(() => javy.downloadUrl(platform, arch)).toThrowError(
138+
expect(() => oldJavy.downloadUrl(platform, arch)).toThrowError(
127139
'Unsupported platform/architecture combination win32/arm',
128140
)
129141
})
@@ -167,7 +179,7 @@ describe('javy', () => {
167179
describe('javy-plugin', () => {
168180
test('properties are set correctly', () => {
169181
expect(javyPlugin.name).toBe('shopify_functions_javy_v3')
170-
expect(javyPlugin.version).match(/^v\d+$/)
182+
expect(javyPlugin.version).match(/^\d+$/)
171183
expect(javyPlugin.path).toMatch(/(\/|\\)shopify_functions_javy_v3.wasm$/)
172184
})
173185

@@ -199,7 +211,7 @@ describe('javy-plugin', () => {
199211
describe('functionRunner', () => {
200212
test('properties are set correctly', () => {
201213
expect(functionRunner.name).toBe('function-runner')
202-
expect(functionRunner.version).match(/^v\d\.\d\.\d$/)
214+
expect(functionRunner.version).match(/^\d\.\d\.\d$/)
203215
if (process.platform === 'win32') {
204216
expect(functionRunner.path).toContain(`function-runner-${PREFERRED_FUNCTION_RUNNER_VERSION}.exe`)
205217
} else {
@@ -285,17 +297,6 @@ describe('functionRunner', () => {
285297
// Then
286298
expect(() => functionRunner.downloadUrl('darwin', arch)).toThrowError('Unsupported architecture ppc')
287299
})
288-
289-
test('Unsupported combination throws an error', () => {
290-
// When
291-
const arch = 'arm'
292-
const platform = 'win32'
293-
294-
// Then
295-
expect(() => functionRunner.downloadUrl(platform, arch)).toThrowError(
296-
'Unsupported platform/architecture combination win32/arm',
297-
)
298-
})
299300
})
300301

301302
test('downloads function-runner', async () => {
@@ -351,7 +352,7 @@ describe('trampoline', () => {
351352

352353
test('v1 properties are set correctly', () => {
353354
expect(v1Trampoline.name).toBe('shopify-function-trampoline')
354-
expect(v1Trampoline.version).match(/v1.\d.\d$/)
355+
expect(v1Trampoline.version).match(/1.\d.\d$/)
355356
if (process.platform === 'win32') {
356357
expect(v1Trampoline.path).toContain(`shopify-function-trampoline-${V1_TRAMPOLINE_VERSION}.exe`)
357358
} else {
@@ -361,7 +362,7 @@ describe('trampoline', () => {
361362

362363
test('v2 properties are set correctly', () => {
363364
expect(v2Trampoline.name).toBe('shopify-function-trampoline')
364-
expect(v2Trampoline.version).match(/v2.\d.\d$/)
365+
expect(v2Trampoline.version).match(/2.\d.\d$/)
365366
if (process.platform === 'win32') {
366367
expect(v2Trampoline.path).toContain(`shopify-function-trampoline-${V2_TRAMPOLINE_VERSION}.exe`)
367368
} else {
@@ -375,7 +376,7 @@ describe('trampoline', () => {
375376

376377
// Then
377378
const expectedUrlRegex = new RegExp(
378-
`https://github.com/Shopify/shopify-function-wasm-api/releases/download/shopify_function_trampoline/${v2Trampoline.version}/shopify-function-trampoline-x86_64-macos-${v2Trampoline.version}.gz`,
379+
`https://github.com/Shopify/shopify-function-wasm-api/releases/download/shopify_function_trampoline/v${v2Trampoline.version}/shopify-function-trampoline-x86_64-macos-v${v2Trampoline.version}.gz`,
379380
)
380381
expect(url).toMatch(expectedUrlRegex)
381382
})

packages/app/src/cli/services/function/binaries.ts

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,26 @@ import {chmod, createFileWriteStream, fileExists, inTemporaryDirectory, mkdir, m
33
import {outputDebug} from '@shopify/cli-kit/node/output'
44
import {performActionWithRetryAfterRecovery} from '@shopify/cli-kit/common/retry'
55
import {fetch} from '@shopify/cli-kit/node/http'
6+
import {versionSatisfies} from '@shopify/cli-kit/node/node-package-manager'
67
import {PipelineSource} from 'stream'
78
import {pipeline} from 'stream/promises'
89
import stream from 'node:stream/promises'
910
import fs from 'node:fs'
1011
import * as gzip from 'node:zlib'
1112
import {fileURLToPath} from 'node:url'
1213

13-
export const PREFERRED_FUNCTION_RUNNER_VERSION = 'v9.1.0'
14+
export const PREFERRED_FUNCTION_RUNNER_VERSION = '9.1.1'
1415

1516
// Javy dependencies.
16-
export const PREFERRED_JAVY_VERSION = 'v6.0.0'
17+
export const PREFERRED_JAVY_VERSION = '7.0.0'
1718
// The Javy plugin version should match the plugin version used in the
1819
// function-runner version specified above.
19-
export const PREFERRED_JAVY_PLUGIN_VERSION = 'v3'
20+
export const PREFERRED_JAVY_PLUGIN_VERSION = '3'
2021

2122
const BINARYEN_VERSION = '123.0.0'
2223

23-
export const V1_TRAMPOLINE_VERSION = 'v1.0.2'
24-
export const V2_TRAMPOLINE_VERSION = 'v2.0.0'
24+
export const V1_TRAMPOLINE_VERSION = '1.0.2'
25+
export const V2_TRAMPOLINE_VERSION = '2.0.1'
2526

2627
interface DownloadableBinary {
2728
path: string
@@ -43,9 +44,9 @@ export interface BinaryDependencies {
4344
export function deriveJavaScriptBinaryDependencies(version: string): BinaryDependencies | null {
4445
if (version === '0' || version === '1') {
4546
return {
46-
functionRunner: 'v7.0.1',
47-
javy: 'v4.0.0',
48-
javyPlugin: 'v1',
47+
functionRunner: '7.0.1',
48+
javy: '4.0.0',
49+
javyPlugin: '1',
4950
}
5051
} else if (version === '2') {
5152
return {
@@ -67,11 +68,19 @@ class Executable implements DownloadableBinary {
6768
readonly path: string
6869
readonly release: string
6970
private readonly gitHubRepo: string
70-
71-
constructor(name: string, version: string, gitHubRepo: string, release = version) {
71+
private readonly supportsWindowsOnArm: boolean
72+
73+
constructor(
74+
name: string,
75+
version: string,
76+
gitHubRepo: string,
77+
supportsWindowsOnArm: boolean,
78+
release = `v${version}`,
79+
) {
7280
this.name = name
7381
this.version = version
7482
this.release = release
83+
this.supportsWindowsOnArm = supportsWindowsOnArm
7584

7685
let filename: string
7786
// add version to the filename
@@ -115,13 +124,15 @@ class Executable implements DownloadableBinary {
115124
}
116125

117126
const archPlatform = `${arch}-${platform}`
118-
// These are currently the same between both binaries _coincidentally_.
119127
const supportedTargets = ['arm-linux', 'arm-macos', 'x86_64-macos', 'x86_64-windows', 'x86_64-linux']
128+
if (this.supportsWindowsOnArm) {
129+
supportedTargets.push('arm-windows')
130+
}
120131
if (!supportedTargets.includes(archPlatform)) {
121132
throw Error(`Unsupported platform/architecture combination ${processPlatform}/${processArch}`)
122133
}
123134

124-
return `https://github.com/${this.gitHubRepo}/releases/download/${this.release}/${this.name}-${archPlatform}-${this.version}.gz`
135+
return `https://github.com/${this.gitHubRepo}/releases/download/${this.release}/${this.name}-${archPlatform}-v${this.version}.gz`
125136
}
126137

127138
async processResponse(responseStream: PipelineSource<unknown>, outputStream: fs.WriteStream): Promise<void> {
@@ -135,13 +146,18 @@ class JavyPlugin implements DownloadableBinary {
135146
readonly path: string
136147

137148
constructor(version: string) {
138-
this.name = `shopify_functions_javy_${version}`
149+
this.name = `shopify_functions_javy_v${version}`
139150
this.version = version
140-
this.path = joinPath(dirname(fileURLToPath(import.meta.url)), '..', 'bin', `shopify_functions_javy_${version}.wasm`)
151+
this.path = joinPath(
152+
dirname(fileURLToPath(import.meta.url)),
153+
'..',
154+
'bin',
155+
`shopify_functions_javy_v${version}.wasm`,
156+
)
141157
}
142158

143159
downloadUrl(_processPlatform: string, _processArch: string) {
144-
return `https://cdn.shopify.com/shopifycloud/shopify-functions-javy-plugin/shopify_functions_javy_${this.version}.wasm`
160+
return `https://cdn.shopify.com/shopifycloud/shopify-functions-javy-plugin/shopify_functions_javy_v${this.version}.wasm`
145161
}
146162

147163
async processResponse(responseStream: PipelineSource<unknown>, outputStream: fs.WriteStream): Promise<void> {
@@ -172,15 +188,25 @@ class WasmOptExecutable implements DownloadableBinary {
172188
let _wasmOpt: DownloadableBinary
173189

174190
export function javyBinary(version: string = PREFERRED_JAVY_VERSION) {
175-
return new Executable('javy', version, 'bytecodealliance/javy') as DownloadableBinary
191+
return new Executable(
192+
'javy',
193+
version,
194+
'bytecodealliance/javy',
195+
versionSatisfies(version, '>=7.0.0'),
196+
) as DownloadableBinary
176197
}
177198

178199
export function javyPluginBinary(version: string = PREFERRED_JAVY_PLUGIN_VERSION) {
179200
return new JavyPlugin(version) as DownloadableBinary
180201
}
181202

182203
export function functionRunnerBinary(version: string = PREFERRED_FUNCTION_RUNNER_VERSION) {
183-
return new Executable('function-runner', version, 'Shopify/function-runner') as DownloadableBinary
204+
return new Executable(
205+
'function-runner',
206+
version,
207+
'Shopify/function-runner',
208+
versionSatisfies(version, '>=9.1.1'),
209+
) as DownloadableBinary
184210
}
185211

186212
export function wasmOptBinary() {
@@ -196,7 +222,8 @@ export function trampolineBinary(version: string) {
196222
'shopify-function-trampoline',
197223
version,
198224
'Shopify/shopify-function-wasm-api',
199-
`shopify_function_trampoline/${version}`,
225+
versionSatisfies(version, '>=2.0.1'),
226+
`shopify_function_trampoline/v${version}`,
200227
)
201228
}
202229

0 commit comments

Comments
 (0)