Skip to content

Commit 98cd60b

Browse files
committed
feat: add release CI
1 parent 17ea67a commit 98cd60b

File tree

12 files changed

+712
-11
lines changed

12 files changed

+712
-11
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM node:slim
2+
3+
COPY . /action
4+
WORKDIR /action
5+
6+
RUN npm install --production
7+
8+
ENTRYPOINT ["node", "/action/main.js"]
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# github-release
2+
3+
Copy-pasted from
4+
https://github.com/rust-lang/rust-analyzer/tree/2df30e1e07eafc1de0359566423f471920693a34/.github/actions/github-release
5+
6+
An action used to publish GitHub releases for `wasmtime`.
7+
8+
As of the time of this writing there's a few actions floating around which
9+
perform github releases but they all tend to have their set of drawbacks.
10+
Additionally nothing handles deleting releases which we need for our rolling
11+
`dev` release.
12+
13+
To handle all this, this action rolls its own implementation using the
14+
actions/toolkit repository and packages published there. These run in a Docker
15+
container and take various inputs to orchestrate the release from the build.
16+
17+
More comments can be found in `main.js`.
18+
19+
Testing this is really hard. If you want to try though run `npm install` and
20+
then `node main.js`. You'll have to configure a bunch of env vars though to get
21+
anything reasonably working.
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: 'wasmtime github releases'
2+
description: 'wasmtime github releases'
3+
inputs:
4+
token:
5+
description: ''
6+
required: true
7+
name:
8+
description: ''
9+
required: true
10+
files:
11+
description: ''
12+
required: true
13+
runs:
14+
using: 'docker'
15+
image: 'Dockerfile'
+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
const core = require('@actions/core');
2+
const path = require("path");
3+
const fs = require("fs");
4+
const github = require('@actions/github');
5+
const glob = require('glob');
6+
7+
function sleep(milliseconds) {
8+
return new Promise(resolve => setTimeout(resolve, milliseconds));
9+
}
10+
11+
async function runOnce() {
12+
// Load all our inputs and env vars. Note that `getInput` reads from `INPUT_*`
13+
const files = core.getInput('files');
14+
const name = core.getInput('name');
15+
const token = core.getInput('token');
16+
const slug = process.env.GITHUB_REPOSITORY;
17+
const owner = slug.split('/')[0];
18+
const repo = slug.split('/')[1];
19+
const sha = process.env.HEAD_SHA;
20+
21+
core.info(`files: ${files}`);
22+
core.info(`name: ${name}`);
23+
24+
const options = {
25+
request: {
26+
timeout: 30000,
27+
}
28+
};
29+
const octokit = github.getOctokit(token, options);
30+
31+
// Delete the previous release since we can't overwrite one. This may happen
32+
// due to retrying an upload or it may happen because we're doing the dev
33+
// release.
34+
const releases = await octokit.paginate("GET /repos/:owner/:repo/releases", { owner, repo });
35+
for (const release of releases) {
36+
if (release.tag_name !== name) {
37+
continue;
38+
}
39+
const release_id = release.id;
40+
core.info(`deleting release ${release_id}`);
41+
await octokit.rest.repos.deleteRelease({ owner, repo, release_id });
42+
}
43+
44+
// We also need to update the `dev` tag while we're at it on the `dev` branch.
45+
if (name == 'nightly') {
46+
try {
47+
core.info(`updating nightly tag`);
48+
await octokit.rest.git.updateRef({
49+
owner,
50+
repo,
51+
ref: 'tags/nightly',
52+
sha,
53+
force: true,
54+
});
55+
} catch (e) {
56+
core.error(e);
57+
core.info(`creating nightly tag`);
58+
await octokit.rest.git.createTag({
59+
owner,
60+
repo,
61+
tag: 'nightly',
62+
message: 'nightly release',
63+
object: sha,
64+
type: 'commit',
65+
});
66+
}
67+
}
68+
69+
// Creates an official GitHub release for this `tag`, and if this is `dev`
70+
// then we know that from the previous block this should be a fresh release.
71+
core.info(`creating a release`);
72+
const release = await octokit.rest.repos.createRelease({
73+
owner,
74+
repo,
75+
name,
76+
tag_name: name,
77+
target_commitish: sha,
78+
prerelease: name === 'nightly',
79+
});
80+
const release_id = release.data.id;
81+
82+
// Upload all the relevant assets for this release as just general blobs.
83+
for (const file of glob.sync(files)) {
84+
const size = fs.statSync(file).size;
85+
const name = path.basename(file);
86+
87+
await runWithRetry(async function() {
88+
// We can't overwrite assets, so remove existing ones from a previous try.
89+
let assets = await octokit.rest.repos.listReleaseAssets({
90+
owner,
91+
repo,
92+
release_id
93+
});
94+
for (const asset of assets.data) {
95+
if (asset.name === name) {
96+
core.info(`delete asset ${name}`);
97+
const asset_id = asset.id;
98+
await octokit.rest.repos.deleteReleaseAsset({ owner, repo, asset_id });
99+
}
100+
}
101+
102+
core.info(`upload ${file}`);
103+
const headers = { 'content-length': size, 'content-type': 'application/octet-stream' };
104+
const data = fs.createReadStream(file);
105+
await octokit.rest.repos.uploadReleaseAsset({
106+
data,
107+
headers,
108+
name,
109+
url: release.data.upload_url,
110+
});
111+
});
112+
}
113+
}
114+
115+
async function runWithRetry(f) {
116+
const retries = 10;
117+
const maxDelay = 4000;
118+
let delay = 1000;
119+
120+
for (let i = 0; i < retries; i++) {
121+
try {
122+
await f();
123+
break;
124+
} catch (e) {
125+
if (i === retries - 1)
126+
throw e;
127+
128+
core.error(e);
129+
const currentDelay = Math.round(Math.random() * delay);
130+
core.info(`sleeping ${currentDelay} ms`);
131+
await sleep(currentDelay);
132+
delay = Math.min(delay * 2, maxDelay);
133+
}
134+
}
135+
}
136+
137+
async function run() {
138+
await runWithRetry(runOnce);
139+
}
140+
141+
run().catch(err => {
142+
core.error(err);
143+
core.setFailed(err.message);
144+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "wasmtime-github-release",
3+
"version": "0.0.0",
4+
"main": "main.js",
5+
"dependencies": {
6+
"@actions/core": "^1.6",
7+
"@actions/github": "^5.0",
8+
"glob": "^7.1.5"
9+
}
10+
}

.github/workflows/release.yml

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
name: release
2+
on:
3+
workflow_dispatch:
4+
5+
push:
6+
branches:
7+
- 'release/**'
8+
9+
env:
10+
CARGO_INCREMENTAL: 0
11+
CARGO_NET_RETRY: 10
12+
RUSTFLAGS: "-D warnings -W unreachable-pub"
13+
RUSTUP_MAX_RETRIES: 10
14+
FETCH_DEPTH: 0 # pull in the tags for the version string
15+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
16+
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
17+
18+
jobs:
19+
dist:
20+
strategy:
21+
matrix:
22+
include:
23+
- os: windows-latest
24+
target: x86_64-pc-windows-msvc
25+
code-target: win32-x64
26+
- os: windows-latest
27+
target: i686-pc-windows-msvc
28+
code-target: win32-ia32
29+
- os: windows-latest
30+
target: aarch64-pc-windows-msvc
31+
code-target: win32-arm64
32+
- os: ubuntu-22.04
33+
target: x86_64-unknown-linux-gnu
34+
code-target: linux-x64
35+
- os: ubuntu-22.04
36+
target: aarch64-unknown-linux-gnu
37+
code-target: linux-arm64
38+
- os: ubuntu-22.04
39+
target: arm-unknown-linux-gnueabihf
40+
code-target: linux-armhf
41+
- os: macos-12
42+
target: x86_64-apple-darwin
43+
code-target: darwin-x64
44+
- os: macos-12
45+
target: aarch64-apple-darwin
46+
code-target: darwin-arm64
47+
48+
env:
49+
LSP_AI_TARGET: ${{ matrix.target }}
50+
51+
name: dist (${{ matrix.target }})
52+
runs-on: ${{ matrix.os }}
53+
container: ${{ matrix.container }}
54+
55+
steps:
56+
- name: Checkout repository
57+
uses: actions/checkout@v4
58+
with:
59+
fetch-depth: ${{ env.FETCH_DEPTH }}
60+
61+
- name: Install Rust toolchain
62+
run: |
63+
rustup update --no-self-update stable
64+
rustup target add ${{ matrix.target }}
65+
rustup component add rust-src
66+
67+
- name: Update apt repositories
68+
if: contains(matrix.os, 'ubuntu')
69+
run: sudo apt-get update -y
70+
71+
- name: Install AArch64 target toolchain
72+
if: matrix.target == 'aarch64-unknown-linux-gnu'
73+
run: sudo apt-get install gcc-aarch64-linux-gnu libc6-dev-arm64-cross g++-aarch64-linux-gnu
74+
75+
- name: Install ARM target toolchain
76+
if: matrix.target == 'arm-unknown-linux-gnueabihf'
77+
run: sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
78+
79+
- name: Dist
80+
run: cargo xtask dist
81+
82+
- name: Upload artifacts
83+
uses: actions/upload-artifact@v4
84+
with:
85+
name: dist-${{ matrix.target }}
86+
path: ./dist
87+
88+
dist-x86_64-unknown-linux-musl:
89+
name: dist (x86_64-unknown-linux-musl)
90+
runs-on: ubuntu-latest
91+
env:
92+
LLM_LS_TARGET: x86_64-unknown-linux-musl
93+
# For some reason `-crt-static` is not working for clang without lld
94+
RUSTFLAGS: "-C link-arg=-fuse-ld=lld -C target-feature=-crt-static"
95+
container:
96+
image: rust:alpine
97+
volumes:
98+
- /usr/local/cargo/registry:/usr/local/cargo/registry
99+
100+
steps:
101+
- name: Install dependencies
102+
run: apk add --no-cache git clang lld musl-dev nodejs npm openssl-dev pkgconfig g++
103+
104+
- name: Checkout repository
105+
uses: actions/checkout@v4
106+
with:
107+
fetch-depth: ${{ env.FETCH_DEPTH }}
108+
109+
- name: Dist
110+
run: cargo xtask dist
111+
112+
- name: Upload artifacts
113+
uses: actions/upload-artifact@v4
114+
with:
115+
name: dist-x86_64-unknown-linux-musl
116+
path: ./dist
117+
118+
publish:
119+
name: publish
120+
runs-on: ubuntu-latest
121+
needs: ["dist", "dist-x86_64-unknown-linux-musl"]
122+
steps:
123+
- name: Checkout repository
124+
uses: actions/checkout@v4
125+
with:
126+
fetch-depth: ${{ env.FETCH_DEPTH }}
127+
128+
- run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
129+
- run: 'echo "HEAD_SHA: $HEAD_SHA"'
130+
131+
- name: Split branch name
132+
env:
133+
BRANCH: ${{ github.ref_name }}
134+
id: split
135+
run: echo "tag=${BRANCH##*/}" >> $GITHUB_OUTPUT
136+
137+
- uses: actions/download-artifact@v4
138+
with:
139+
name: dist-aarch64-apple-darwin
140+
path: dist
141+
- uses: actions/download-artifact@v4
142+
with:
143+
name: dist-x86_64-apple-darwin
144+
path: dist
145+
- uses: actions/download-artifact@v4
146+
with:
147+
name: dist-x86_64-unknown-linux-gnu
148+
path: dist
149+
- uses: actions/download-artifact@v4
150+
with:
151+
name: dist-x86_64-unknown-linux-musl
152+
path: dist
153+
- uses: actions/download-artifact@v4
154+
with:
155+
name: dist-aarch64-unknown-linux-gnu
156+
path: dist
157+
- uses: actions/download-artifact@v4
158+
with:
159+
name: dist-arm-unknown-linux-gnueabihf
160+
path: dist
161+
- uses: actions/download-artifact@v4
162+
with:
163+
name: dist-x86_64-pc-windows-msvc
164+
path: dist
165+
- uses: actions/download-artifact@v4
166+
with:
167+
name: dist-i686-pc-windows-msvc
168+
path: dist
169+
- uses: actions/download-artifact@v4
170+
with:
171+
name: dist-aarch64-pc-windows-msvc
172+
path: dist
173+
- run: ls -al ./dist
174+
175+
- name: Publish Release
176+
uses: ./.github/actions/github-release
177+
with:
178+
files: "dist/*"
179+
name: ${{ steps.split.outputs.tag }}
180+
token: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)