Skip to content

Commit 5b9b844

Browse files
authored
Feature: compile for Windows ARM (#1542)
1 parent 9dcdec9 commit 5b9b844

File tree

4 files changed

+130
-126
lines changed

4 files changed

+130
-126
lines changed

.github/actionlint.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
paths:
2+
.github/workflows/**/*.{yml,yaml}:
3+
ignore:
4+
# Don't try to detect if a GitHub runner isn't available 😡 actions will fail it's not
5+
- 'available labels are'

.github/workflows/node-compile.yml

Lines changed: 119 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -48,129 +48,147 @@ jobs:
4848
- path-filter
4949
if: ${{ needs.path-filter.outputs.changes == 'true' || github.event_name != 'pull_request' }}
5050
runs-on: ${{ matrix.os }}
51-
name: node-compile (${{ matrix.os }} ${{ matrix.arch }})
51+
name: node-compile (${{ matrix.os }} ${{ matrix.docker_arch || matrix.go_arch }})
5252
strategy:
5353
fail-fast: false
5454
matrix:
5555
include:
5656
# docker manifest inspect node:<version> | jq '.manifests[].platform'
5757
- os: ubuntu-latest
58-
arch: amd64
59-
build: |
60-
set -x
61-
NPM_PKG_VERSION=$(npm pkg get version | sed 's/"//g')
62-
DOCKER_ARCH=amd64
63-
BIN_OUTPUT="igir-${NPM_PKG_VERSION}-${RUNNER_OS}-${DOCKER_ARCH/\//}.tar.gz"
64-
echo "BIN_OUTPUT=${BIN_OUTPUT}" >> "${GITHUB_ENV}"
65-
docker run --rm --platform "linux/${DOCKER_ARCH}" --volume "$(pwd):/app" --workdir "/app" \
66-
"node:$(jq --raw-output '.volta.node' package.json)" sh -c \
67-
"(cd node_modules/zstd-napi && ../.bin/prebuild-install --runtime napi || (npm install node-gyp node-addon-api && node_modules/.bin/node-gyp rebuild)) && npm run package -- . igir && ./igir --help"
68-
tar cvf - igir | gzip --best > "${BIN_OUTPUT}"
69-
{
70-
echo '```text'
71-
ls -alh igir "${BIN_OUTPUT}"
72-
echo '```'
73-
} >> "${GITHUB_STEP_SUMMARY}"
58+
docker_arch: linux/amd64
7459
- os: ubuntu-latest
75-
arch: arm/v7
76-
build: |
77-
set -x
78-
NPM_PKG_VERSION=$(npm pkg get version | sed 's/"//g')
79-
DOCKER_ARCH=arm/v7
80-
BIN_OUTPUT="igir-${NPM_PKG_VERSION}-${RUNNER_OS}-${DOCKER_ARCH/\//}.tar.gz"
81-
echo "BIN_OUTPUT=${BIN_OUTPUT}" >> "${GITHUB_ENV}"
82-
docker run --rm --platform "linux/${DOCKER_ARCH}" --volume "$(pwd):/app" --workdir "/app" \
83-
"node:$(jq --raw-output '.volta.node' package.json)" sh -c \
84-
"(cd node_modules/zstd-napi && ../.bin/prebuild-install --runtime napi || (npm install node-gyp node-addon-api && node_modules/.bin/node-gyp rebuild)) && npm run package -- . igir && ./igir --help"
85-
tar cvf - igir | gzip --best > "${BIN_OUTPUT}"
86-
{
87-
echo '```text'
88-
ls -alh igir "${BIN_OUTPUT}"
89-
echo '```'
90-
} >> "${GITHUB_STEP_SUMMARY}"
60+
docker_arch: linux/arm/v7
9161
- os: ubuntu-latest
92-
arch: arm64/v8
93-
build: |
94-
set -x
95-
NPM_PKG_VERSION=$(npm pkg get version | sed 's/"//g')
96-
DOCKER_ARCH=arm64/v8
97-
BIN_OUTPUT="igir-${NPM_PKG_VERSION}-${RUNNER_OS}-${DOCKER_ARCH/\//}.tar.gz"
98-
echo "BIN_OUTPUT=${BIN_OUTPUT}" >> "${GITHUB_ENV}"
99-
docker run --rm --platform "linux/${DOCKER_ARCH}" --volume "$(pwd):/app" --workdir "/app" \
100-
"node:$(jq --raw-output '.volta.node' package.json)" sh -c \
101-
"(cd node_modules/zstd-napi && ../.bin/prebuild-install --runtime napi || (npm install node-gyp node-addon-api && node_modules/.bin/node-gyp rebuild)) && npm run package -- . igir && ./igir --help"
102-
tar cvf - igir | gzip --best > "${BIN_OUTPUT}"
103-
{
104-
echo '```text'
105-
ls -alh igir "${BIN_OUTPUT}"
106-
echo '```'
107-
} >> "${GITHUB_STEP_SUMMARY}"
62+
docker_arch: linux/arm64/v8
63+
10864
- os: macos-13
109-
build: |
110-
set -x
111-
NPM_PKG_VERSION=$(npm pkg get version | sed 's/"//g')
112-
NODE_ARCH=$(node --print 'process.arch')
113-
BIN_OUTPUT="igir-${NPM_PKG_VERSION}-${RUNNER_OS}-${NODE_ARCH}.tar.gz"
114-
echo "BIN_OUTPUT=${BIN_OUTPUT}" >> "${GITHUB_ENV}"
115-
(cd node_modules/zstd-napi && ../.bin/prebuild-install --runtime napi || (npm install node-gyp node-addon-api && node_modules/.bin/node-gyp rebuild))
116-
npm run package -- . igir
117-
./igir --help
118-
tar cvf - igir | gzip --best > "${BIN_OUTPUT}"
119-
{
120-
echo '```text'
121-
ls -alh igir "${BIN_OUTPUT}"
122-
echo '```'
123-
} >> "${GITHUB_STEP_SUMMARY}"
12465
- os: macos-15
125-
build: |
126-
set -x
127-
NPM_PKG_VERSION=$(npm pkg get version | sed 's/"//g')
128-
NODE_ARCH=$(node --print 'process.arch')
129-
BIN_OUTPUT="igir-${NPM_PKG_VERSION}-${RUNNER_OS}-${NODE_ARCH}.tar.gz"
130-
echo "BIN_OUTPUT=${BIN_OUTPUT}" >> "${GITHUB_ENV}"
131-
(cd node_modules/zstd-napi && ../.bin/prebuild-install --runtime napi || (npm install node-gyp node-addon-api && node_modules/.bin/node-gyp rebuild))
132-
npm run package -- . igir
133-
./igir --help
134-
tar cvf - igir | gzip --best > "${BIN_OUTPUT}"
135-
{
136-
echo '```text'
137-
ls -alh igir "${BIN_OUTPUT}"
138-
echo '```'
139-
} >> "${GITHUB_STEP_SUMMARY}"
66+
14067
- os: windows-latest
141-
build: |
142-
$NPM_PKG_VERSION=$(npm pkg get version).replace('"','')
143-
$NODE_ARCH=$(node --print 'process.arch')
144-
$BIN_OUTPUT="igir-$NPM_PKG_VERSION-${env:RUNNER_OS}-${NODE_ARCH}.zip"
145-
echo "BIN_OUTPUT=${BIN_OUTPUT}" | Out-File -FilePath $env:GITHUB_ENV -Append # no need for -Encoding utf8
146-
npm run package -- . igir.exe
147-
Compress-Archive -Path igir.exe -DestinationPath "${BIN_OUTPUT}" -CompressionLevel Optimal -Force
148-
echo "``````text" >> "${env:GITHUB_STEP_SUMMARY}"
149-
Get-ChildItem igir.exe >> "${env:GITHUB_STEP_SUMMARY}"
150-
Get-ChildItem "${BIN_OUTPUT}" >> "${env:GITHUB_STEP_SUMMARY}"
151-
echo "``````" >> "${env:GITHUB_STEP_SUMMARY}"
152-
echo "" >> "${env:GITHUB_STEP_SUMMARY}"
153-
echo "``````text" >> "${env:GITHUB_STEP_SUMMARY}"
154-
.\igir.exe --help >> "${env:GITHUB_STEP_SUMMARY}"
155-
echo "``````" >> "${env:GITHUB_STEP_SUMMARY}"
68+
go_arch: amd64
69+
- os: windows-11-arm
70+
go_arch: arm64
71+
15672
steps:
15773
# Setup and install
15874
- uses: actions/checkout@v4
15975
with:
16076
ref: ${{ env.ref }}
161-
- if: matrix.os == 'ubuntu-latest'
162-
uses: docker/setup-qemu-action@v3
16377
- uses: volta-cli/action@v4
164-
- run: npm ci
165-
- run: npm run build
78+
16679
# Compile and upload
167-
- run: ${{ matrix.build }}
168-
- if: ${{ github.event_name != 'pull_request' }}
169-
uses: actions/upload-artifact@v4
80+
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
81+
uses: docker/setup-qemu-action@v3
82+
- id: linux-vars
83+
if: ${{ startsWith(matrix.os, 'ubuntu') }}
84+
run: |
85+
echo "VOLTA_NODE=$(jq --raw-output '.volta.node' package.json)" >> "${GITHUB_OUTPUT}"
86+
87+
DOCKER_ARCH=${{ matrix.docker_arch }}
88+
DOCKER_ARCH=${DOCKER_ARCH/linux\//}
89+
DOCKER_ARCH=${DOCKER_ARCH//\//}
90+
echo "DOCKER_ARCH=${DOCKER_ARCH}" >> "${GITHUB_OUTPUT}"
91+
92+
NPM_PKG_VERSION=$(npm pkg get version | sed 's/"//g')
93+
BIN_OUTPUT="igir-${NPM_PKG_VERSION}-${{ runner.os }}-${DOCKER_ARCH}.tar.gz"
94+
echo "BIN_OUTPUT=${BIN_OUTPUT}" >> "${GITHUB_ENV}"
95+
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
96+
uses: addnab/docker-run-action@v3
97+
with:
98+
image: node:${{ steps.linux-vars.outputs.VOLTA_NODE }}
99+
shell: bash
100+
options: |
101+
--platform ${{ matrix.docker_arch }}
102+
--volume ${{ github.workspace }}:/build
103+
--workdir /build
104+
run: |
105+
# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference
106+
set -eo pipefail
107+
108+
set -x
109+
npm ci
110+
npm run package -- . igir
111+
./igir --help
112+
tar cvf - igir | gzip --best > "${{ env.BIN_OUTPUT }}"
113+
114+
{
115+
echo '```text'
116+
ls -alh igir "${{ env.BIN_OUTPUT }}"
117+
echo '```'
118+
} >> "${GITHUB_STEP_SUMMARY:-/dev/null}"
119+
120+
- if: ${{ startsWith(matrix.os, 'macos') }}
121+
run: |
122+
set -x
123+
NPM_PKG_VERSION=$(npm pkg get version | sed 's/"//g')
124+
NODE_ARCH=$(node --print 'process.arch')
125+
BIN_OUTPUT="igir-${NPM_PKG_VERSION}-${{ runner.os }}-${NODE_ARCH}.tar.gz"
126+
echo "BIN_OUTPUT=${BIN_OUTPUT}" >> "${GITHUB_ENV}"
127+
128+
npm ci
129+
npm run package -- . igir
130+
./igir --help
131+
tar cvf - igir | gzip --best > "${BIN_OUTPUT}"
132+
133+
{
134+
echo '```text'
135+
ls -alh igir "${BIN_OUTPUT}"
136+
echo '```'
137+
} >> "${GITHUB_STEP_SUMMARY}"
138+
139+
- if: ${{ startsWith(matrix.os, 'windows') }}
140+
shell: pwsh
141+
run: |
142+
Set-PSDebug -Trace 1
143+
$NPM_PKG_VERSION=$(npm pkg get version).replace('"','')
144+
$NODE_ARCH=$(node --print 'process.arch')
145+
$BIN_OUTPUT="igir-$NPM_PKG_VERSION-${{ runner.os }}-${NODE_ARCH}.zip"
146+
echo "BIN_OUTPUT=${BIN_OUTPUT}" | Out-File -FilePath $env:GITHUB_ENV -Append # no need for -Encoding utf8
147+
148+
# Install dependencies...
149+
if ($NODE_ARCH -in @("x64")) {
150+
npm ci
151+
} else {
152+
# ...but fix zstd-napi not compiling Zstandard correctly out of the box
153+
npm ci --ignore-scripts
154+
Push-Location
155+
cd node_modules\zstd-napi
156+
Set-Content -Path deps\zstd.gyp -Value (Get-Content -Path deps\zstd.gyp | Select-String -Pattern 'huf_decompress_amd64' -NotMatch)
157+
npm install node-gyp
158+
node_modules\.bin\node-gyp rebuild
159+
Pop-Location
160+
}
161+
if ($NODE_ARCH -notin @("ia32", "x64")) {
162+
# ...and use a version of 7zip-bin that includes this arch
163+
npm install 7zip-bin@">=5.2.0"
164+
}
165+
if ($NODE_ARCH -notin @("x64")) {
166+
# ...and build the missing caxa stub
167+
Push-Location
168+
cd node_modules\caxa
169+
npm install
170+
node_modules\.bin\cross-env CGO_ENABLED=0 GOOS=windows "GOARCH=${{ matrix.go_arch }}" go build -o "stubs\stub--win32--${NODE_ARCH}" stubs\stub.go
171+
$stubSeparatorBytes = [System.Text.Encoding]::ASCII.GetBytes("`nCAXACAXACAXA`n")
172+
$stubFileStream = [System.IO.File]::Open([System.IO.Path]::GetFullPath("stubs\stub--win32--${NODE_ARCH}", $PWD), 'Append', 'Write')
173+
$stubFileStream.Write($stubSeparatorBytes, 0, $stubSeparatorBytes.Length)
174+
$stubFileStream.Close()
175+
Pop-Location
176+
}
177+
178+
npm run package -- . igir.exe
179+
Compress-Archive -Path igir.exe -DestinationPath "${BIN_OUTPUT}" -CompressionLevel Optimal -Force
180+
181+
echo "``````text" >> "${env:GITHUB_STEP_SUMMARY}"
182+
Get-ChildItem igir.exe >> "${env:GITHUB_STEP_SUMMARY}"
183+
Get-ChildItem "${BIN_OUTPUT}" >> "${env:GITHUB_STEP_SUMMARY}"
184+
echo "``````" >> "${env:GITHUB_STEP_SUMMARY}"
185+
186+
- uses: actions/upload-artifact@v4
170187
with:
171188
name: ${{ env.BIN_OUTPUT }}
172189
path: ${{ env.BIN_OUTPUT }}
173190
if-no-files-found: error
191+
retention-days: 7
174192

175193
release-update:
176194
if: ${{ github.event_name != 'pull_request' }}

.github/workflows/node-test.yml

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -117,24 +117,6 @@ jobs:
117117
- run: npm run build
118118
- run: ./test/endToEndTest.sh
119119

120-
node-build:
121-
needs:
122-
- path-filter
123-
if: ${{ needs.path-filter.outputs.changes == 'true' }}
124-
runs-on: ${{ matrix.os }}
125-
strategy:
126-
matrix:
127-
os: [ ubuntu-24.04, ubuntu-24.04-arm, macos-15, windows-latest ]
128-
steps:
129-
# Setup and install
130-
- uses: actions/checkout@v4
131-
with:
132-
ref: ${{ env.ref }}
133-
- uses: volta-cli/action@v4
134-
- run: npm ci
135-
# Test building
136-
- run: npm run build
137-
138120
node-package:
139121
needs:
140122
- path-filter
@@ -171,7 +153,6 @@ jobs:
171153
- node-lint
172154
- node-unit
173155
- node-e2e
174-
- node-build
175156
- node-package
176157
runs-on: ubuntu-latest
177158
steps:

package.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@ const fileFilter = (filters: FileFilter[]): string[] => {
2222
let results: string[] = [];
2323
filters.forEach((filter) => {
2424
if (filter.include) {
25-
const include = fg
26-
.globSync(filter.include.replaceAll('\\', '/'), filter)
27-
.map((file) => path.resolve(file));
25+
const includeNormalized = filter.include.replaceAll('\\', '/');
26+
const include = fg.globSync(includeNormalized, filter).map((file) => path.resolve(file));
2827
if (include.length === 0) {
29-
throw new ExpectedError(`glob pattern '${filter.include}' returned no paths`);
28+
throw new ExpectedError(`glob pattern '${includeNormalized}' returned no paths`);
3029
}
3130
results = [...results, ...include];
3231
}
3332
if (filter.exclude) {
33+
const excludeNormalized = filter.exclude.replaceAll('\\', '/');
3434
const exclude = new Set(
35-
fg.globSync(filter.exclude.replaceAll('\\', '/'), filter).map((file) => path.resolve(file)),
35+
fg.globSync(excludeNormalized, filter).map((file) => path.resolve(file)),
3636
);
3737
if (exclude.size === 0) {
38-
throw new ExpectedError(`glob pattern '${filter.exclude}' returned no paths`);
38+
throw new ExpectedError(`glob pattern '${excludeNormalized}' returned no paths`);
3939
}
4040
results = results.filter((result) => !exclude.has(result));
4141
}

0 commit comments

Comments
 (0)