Make Prebuilds #215
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Release procedure: | |
# 1. On the master branch, update the changelog and stage it in git. | |
# 2. Run `yarn version --[major|minor|patch]`. (This tags the commit.) | |
# 3. Push both the commit and tags to GitHub (`git push --tags Automattic` and | |
# `git push Automattic HEAD:master`, where "Automattic" is the remote name | |
# for [email protected]:Automattic/node-canvas.git). | |
# 4. Create a draft release manually using the GitHub UI for the new tag. | |
# 5. Switch to the prebuilds branch. | |
# 6. If necessary, update `jobs.*.strategy.matrix.node` to change the node | |
# version used to build | |
# 7. Set the `jobs.*.strategy.matrix.canvas_tag` arrays to the set of Canvas | |
# tags to build. (Usually this is a single tag, but can be an array when a | |
# new version of Node.js is released and older versions of Canvas need to be | |
# built.) | |
# 8. Commit this file and push. | |
# 9. In the Actions tab, navigate to the "Make Prebuilds" workflow and click | |
# "Run workflow". Select the `prebuilds` branch so that that branch's | |
# prebuild.yaml file is used. | |
# 10.Once the builds succeed, promote the draft release to a full release and | |
# run npm publish. | |
# Note that the files in the prebuild/ directory will be used from the commit | |
# that was used to trigger the prebuild workflow. They will not be from the | |
# commit/tag that you are building. Because of differences in the provided | |
# versions of git, this is achieved in a different way on Linux than on Mac and | |
# Win. | |
# | |
# Pay particular attention to changes to binding.gyp on master. That file is not | |
# used! The ones in prebuild/**/ are. | |
# Tip: If uploads are inexplicably failing, open the release in GitHub, click | |
# "edit" and delete any assets that are in purgatory (have a /!\ next to them). | |
name: Make Prebuilds | |
on: workflow_dispatch | |
# UPLOAD_TO can be specified to upload the release assets under a different tag | |
# name (e.g. for testing). If omitted, the assets are published under the same | |
# release tag as the canvas version being built. | |
# env: | |
# UPLOAD_TO: "v0.0.1" | |
# Node 19 requires a recent node-gyp | |
# Node 10, 11 require 8 | |
# Node 8, 9 require 6.1 | |
# Manually set this file depending on what you're building!! | |
jobs: | |
Linux: | |
strategy: | |
fail-fast: false | |
matrix: | |
node: [21] | |
canvas_tag: ["v3.0.0"] # e.g. "v2.6.1" | |
name: ${{ matrix.canvas_tag }}, Node.js ${{ matrix.node }}, Linux | |
runs-on: ubuntu-latest | |
container: | |
image: ${{ matrix.node < 18 && 'chearon/canvas-prebuilt:9' || 'zbbjornson/canvas-prebuilt:11' }} | |
env: | |
CANVAS_VERSION_TO_BUILD: ${{ matrix.canvas_tag }} | |
steps: | |
- uses: actions/checkout@v2 | |
with: | |
ref: ${{ matrix.canvas_tag }} | |
# Use the files in the prebuild/ directory from the commit that was used | |
# to trigger the prebuild workflow. The version of git that's installed on | |
# this Linux here doesn't support the method used on Mac and Win, so we | |
# checkout the prebuild branch and copy the files from the prebuild/ | |
# directory in the Build step. | |
- uses: actions/checkout@v2 | |
with: | |
ref: ${{ env.GITHUB_SHA }} | |
path: prebuild-branch | |
- uses: actions/setup-node@v1 | |
with: | |
node-version: ${{ matrix.node }} | |
- name: Build | |
run: | | |
set -ex | |
mkdir -p prebuild | |
cp -rfv ./prebuild-branch/prebuild/* ./prebuild/ | |
npm install -g node-gyp | |
npm install --ignore-scripts | |
cp prebuild/Linux/binding.gyp binding.gyp | |
node-gyp rebuild -j 2 | |
. prebuild/Linux/bundle.sh | |
- name: Test binary | |
continue-on-error: true | |
run: | | |
set -ex | |
cd /root/harfbuzz-* && make uninstall | |
cd /root/cairo-* && make uninstall | |
cd /root/pango-* && cd _build && ninja uninstall | |
cd /root/libpng-* && make uninstall | |
cd /root/libjpeg-* && cd b && make uninstall | |
cd /root/giflib-* && make uninstall | |
cd $GITHUB_WORKSPACE | |
ls build/Release | |
ldd build/Release/canvas.node | |
npx mocha test/*.test.js | |
- name: Make bundle | |
id: make_bundle | |
run: . prebuild/tarball.sh | |
- name: Upload | |
uses: actions/github-script@v2 | |
with: | |
script: | | |
const fs = require("fs"); | |
const assetName = "${{ steps.make_bundle.outputs.asset_name }}"; | |
const tagName = process.env.UPLOAD_TO || process.env.CANVAS_VERSION_TO_BUILD; | |
const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); | |
const releases = await github.repos.listReleases({owner, repo}); | |
const release = releases.data.find(r => r.tag_name === tagName); | |
if (!release) | |
throw new Error(`Tag ${tagName} not found. Did you make the GitHub release?`); | |
const oldAsset = release.assets.find(a => a.name === assetName); | |
if (oldAsset) | |
await github.repos.deleteReleaseAsset({owner, repo, asset_id: oldAsset.id}); | |
// (This is equivalent to actions/upload-release-asset. We're | |
// already in a script, so might as well do it here.) | |
const r = await github.repos.uploadReleaseAsset({ | |
url: release.upload_url, | |
headers: { | |
"content-type": "application/x-gzip", | |
"content-length": `${fs.statSync(assetName).size}` | |
}, | |
name: assetName, | |
data: fs.readFileSync(assetName) | |
}); | |
macOS: | |
strategy: | |
fail-fast: false | |
matrix: | |
node: [21] | |
canvas_tag: ["v3.0.0"] # e.g. "v2.6.1" | |
os: | |
- runner: macos-15-large | |
- runner: macos-15 | |
name: ${{ matrix.canvas_tag}}, Node.js ${{ matrix.node }}, macOS ${{ matrix.os.arch }} | |
runs-on: ${{ matrix.os.runner }} | |
env: | |
CANVAS_VERSION_TO_BUILD: ${{ matrix.canvas_tag }} | |
steps: | |
- uses: actions/checkout@v2 | |
with: | |
ref: ${{ matrix.canvas_tag }} | |
# Fetch all commits/all branches so we can checkout the prebuild | |
# branch's files | |
fetch-depth: 0 | |
- uses: actions/setup-node@v1 | |
with: | |
node-version: ${{ matrix.node }} | |
- name: Build | |
run: | | |
set -Eeuxo pipefail | |
git checkout ${{ matrix.canvas_tag }} | |
git checkout $GITHUB_SHA -- prebuild/ | |
npm install -g node-gyp | |
npm install --ignore-scripts | |
. prebuild/macOS/preinstall.sh | |
cp prebuild/macOS/binding.gyp binding.gyp | |
node-gyp rebuild -j 2 | |
. prebuild/macOS/bundle.sh | |
- name: Test binary | |
run: | | |
brew uninstall --force --ignore-dependencies cairo pango librsvg giflib harfbuzz | |
npm test | |
- name: Sign the executable | |
run: | | |
for file in $(ls build/Release); | |
do codesign --deep --force --sign - build/Release/$file; | |
done; | |
- name: Make bundle | |
id: make_bundle | |
run: . prebuild/tarball.sh | |
- name: Upload | |
uses: actions/github-script@v2 | |
with: | |
script: | | |
const fs = require("fs"); | |
const assetName = "${{ steps.make_bundle.outputs.asset_name }}"; | |
const tagName = process.env.UPLOAD_TO || process.env.CANVAS_VERSION_TO_BUILD; | |
const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); | |
const releases = await github.repos.listReleases({owner, repo}); | |
const release = releases.data.find(r => r.tag_name === tagName); | |
if (!release) | |
throw new Error(`Tag ${tagName} not found. Did you make the GitHub release?`); | |
const oldAsset = release.assets.find(a => a.name === assetName); | |
if (oldAsset) | |
await github.repos.deleteReleaseAsset({owner, repo, asset_id: oldAsset.id}); | |
// (This is equivalent to actions/upload-release-asset. We're | |
// already in a script, so might as well do it here.) | |
const r = await github.repos.uploadReleaseAsset({ | |
url: release.upload_url, | |
headers: { | |
"content-type": "application/x-gzip", | |
"content-length": `${fs.statSync(assetName).size}` | |
}, | |
name: assetName, | |
data: fs.readFileSync(assetName) | |
}); | |
Win: | |
strategy: | |
fail-fast: false | |
matrix: | |
node: [21] | |
canvas_tag: ["v3.0.0"] # e.g. "v2.6.1" | |
name: ${{ matrix.canvas_tag}}, Node.js ${{ matrix.node }}, Windows | |
runs-on: windows-2019 | |
env: | |
CANVAS_VERSION_TO_BUILD: ${{ matrix.canvas_tag }} | |
steps: | |
# GitHub runners now have msys2 installed, but msys is not on the path and | |
# is apparently slow to start. | |
# https://github.com/msys2/setup-msys2#setup-msys2 | |
# https://github.com/actions/virtual-environments/pull/632 | |
- uses: msys2/setup-msys2@v2 | |
with: | |
msystem: UCRT64 | |
update: true | |
path-type: inherit | |
- uses: actions/setup-node@v3 | |
with: | |
node-version: ${{ matrix.node }} | |
- uses: actions/checkout@v3 | |
with: | |
ref: ${{ matrix.canvas_tag }} | |
# Fetch all commits/all branches so we can checkout the prebuild | |
# branch's files | |
fetch-depth: 0 | |
- name: Build | |
run: | | |
git checkout ${{ matrix.canvas_tag }} | |
git checkout $env:GITHUB_SHA -- prebuild/ | |
npm install -g node-gyp | |
npm prefix -g | % {npm config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"} | |
npm install --ignore-scripts | |
msys2 -c ". prebuild/Windows/preinstall.sh" | |
msys2 -c "cp prebuild/Windows/binding.gyp binding.gyp" | |
npm install --build-from-source | |
- name: Install Depends | |
run: | | |
Invoke-WebRequest "http://www.dependencywalker.com/depends22_x64.zip" -OutFile depends22_x64.zip | |
7z e depends22_x64.zip | |
- name: Bundle pt 2 | |
shell: msys2 {0} | |
run: | | |
# removes object files and debug | |
. prebuild/Windows/bundle.sh | |
./depends.exe -c -oc depends.csv build\\Release\\canvas.node || true | |
[ -f depends.csv ] || { echo "error invoking depends.exe"; exit 1; } | |
copies=$(comm -12 \ | |
<(cat depends.csv | cut -d ',' -f2 | sed 's/"//g' | tr '[:upper:]' '[:lower:]' | sort) \ | |
<(find /ucrt64/bin -name '*.dll' -printf "%f\n" | tr '[:upper:]' '[:lower:]' | sort) \ | |
) | |
for dll in $copies; do | |
cp /ucrt64/bin/$dll build/Release | |
done; | |
- name: Test binary | |
# By not running in msys2, this doesn't have access to the msys2 libs | |
run: npm test | |
- name: Make asset | |
id: make_bundle | |
# I can't figure out why this isn't an env var already. It shows up with `env`. | |
run: msys2 -c "UPLOAD_TO=${{ env.UPLOAD_TO }} CANVAS_VERSION_TO_BUILD=${{ env.CANVAS_VERSION_TO_BUILD}} . prebuild/tarball.sh" | |
- name: Upload | |
uses: actions/github-script@v2 | |
with: | |
script: | | |
const fs = require("fs"); | |
const assetName = "${{ steps.make_bundle.outputs.asset_name }}"; | |
const tagName = process.env.UPLOAD_TO || process.env.CANVAS_VERSION_TO_BUILD; | |
const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); | |
const releases = await github.repos.listReleases({owner, repo}); | |
const release = releases.data.find(r => r.tag_name === tagName); | |
if (!release) | |
throw new Error(`Tag ${tagName} not found. Did you make the GitHub release?`); | |
const oldAsset = release.assets.find(a => a.name === assetName); | |
if (oldAsset) | |
await github.repos.deleteReleaseAsset({owner, repo, asset_id: oldAsset.id}); | |
// (This is equivalent to actions/upload-release-asset. We're | |
// already in a script, so might as well do it here.) | |
const r = await github.repos.uploadReleaseAsset({ | |
url: release.upload_url, | |
headers: { | |
"content-type": "application/x-gzip", | |
"content-length": `${fs.statSync(assetName).size}` | |
}, | |
name: assetName, | |
data: fs.readFileSync(assetName) | |
}); |