Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic release when pushing to develop branch #502

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions .github/actions/github-release/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test.sh
package-lock.json
node_modules/
8 changes: 8 additions & 0 deletions .github/actions/github-release/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM node:slim

COPY . /action
WORKDIR /action

RUN npm install --production

ENTRYPOINT ["node", "/action/main.js"]
21 changes: 21 additions & 0 deletions .github/actions/github-release/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# github-release

Copy-pasted from
https://github.com/rust-lang/rust-analyzer/tree/master/.github/actions/github-release

An action used to publish GitHub releases for `wasmtime`.

As of the time of this writing there's a few actions floating around which
perform github releases but they all tend to have their set of drawbacks.
Additionally nothing handles deleting releases which we need for our rolling
`dev` release.

To handle all this, this action rolls its own implementation using the
actions/toolkit repository and packages published there. These run in a Docker
container and take various inputs to orchestrate the release from the build.

More comments can be found in `main.js`.

Testing this is really hard. If you want to try though run `npm install` and
then `node main.js`. You'll have to configure a bunch of env vars though to get
anything reasonably working.
15 changes: 15 additions & 0 deletions .github/actions/github-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: 'develop github releases'
description: 'develop github releases'
inputs:
token:
description: ''
required: true
name:
description: ''
required: true
files:
description: ''
required: true
runs:
using: 'docker'
image: 'Dockerfile'
146 changes: 146 additions & 0 deletions .github/actions/github-release/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
const core = require('@actions/core');
const path = require("path");
const fs = require("fs");
const github = require('@actions/github');
const glob = require('glob');

function sleep(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}

async function runOnce() {
// Load all our inputs and env vars. Note that `getInput` reads from `INPUT_*`
const files = core.getInput('files');
const name = core.getInput('name');
const token = core.getInput('token');
const slug = process.env.GITHUB_REPOSITORY;
const owner = slug.split('/')[0];
const repo = slug.split('/')[1];
const sha = process.env.HEAD_SHA;

core.info(`files: ${files}`);
core.info(`name: ${name}`);

const options = {
request: {
timeout: 30000,
}
};
const octokit = github.getOctokit(token, options);

// Delete the previous release since we can't overwrite one. This may happen
// due to retrying an upload or it may happen because we're doing the dev
// release.
const releases = await octokit.paginate("GET /repos/:owner/:repo/releases", { owner, repo });
for (const release of releases) {
if (release.tag_name !== name) {
continue;
}
const release_id = release.id;
core.info(`deleting release ${release_id}`);
await octokit.rest.repos.deleteRelease({ owner, repo, release_id });
}

// We also need to update the `dev` tag while we're at it on the `dev` branch.
if (name == 'nightly') {
try {
core.info(`updating nightly tag`);
await octokit.rest.git.updateRef({
owner,
repo,
ref: 'tags/nightly',
sha,
force: true,
});
} catch (e) {
core.error(e);
core.info(`creating nightly tag`);
await octokit.rest.git.createTag({
owner,
repo,
tag: 'nightly',
message: 'nightly release',
object: sha,
type: 'commit',
});
}
}

// Creates an official GitHub release for this `tag`, and if this is `dev`
// then we know that from the previous block this should be a fresh release.
core.info(`creating a release`);
const release = await octokit.rest.repos.createRelease({
owner,
repo,
name,
tag_name: name,
target_commitish: sha,
prerelease: name === 'nightly',
// TODO: just for testing
draft: true
});
const release_id = release.data.id;

// Upload all the relevant assets for this release as just general blobs.
for (const file of glob.sync(files)) {
const size = fs.statSync(file).size;
const name = path.basename(file);

await runWithRetry(async function () {
// We can't overwrite assets, so remove existing ones from a previous try.
let assets = await octokit.rest.repos.listReleaseAssets({
owner,
repo,
release_id
});
for (const asset of assets.data) {
if (asset.name === name) {
core.info(`delete asset ${name}`);
const asset_id = asset.id;
await octokit.rest.repos.deleteReleaseAsset({ owner, repo, asset_id });
}
}

core.info(`upload ${file}`);
const headers = { 'content-length': size, 'content-type': 'application/octet-stream' };
const data = fs.createReadStream(file);
await octokit.rest.repos.uploadReleaseAsset({
data,
headers,
name,
url: release.data.upload_url,
});
});
}
}

async function runWithRetry(f) {
const retries = 10;
const maxDelay = 4000;
let delay = 1000;

for (let i = 0; i < retries; i++) {
try {
await f();
break;
} catch (e) {
if (i === retries - 1)
throw e;

core.error(e);
const currentDelay = Math.round(Math.random() * delay);
core.info(`sleeping ${currentDelay} ms`);
await sleep(currentDelay);
delay = Math.min(delay * 2, maxDelay);
}
}
}

async function run() {
await runWithRetry(runOnce);
}

run().catch(err => {
core.error(err);
core.setFailed(err.message);
});
10 changes: 10 additions & 0 deletions .github/actions/github-release/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "wasmtime-github-release",
"version": "0.0.0",
"main": "main.js",
"dependencies": {
"@actions/core": "^1.6",
"@actions/github": "^5.0",
"glob": "^7.1.5"
}
}
85 changes: 51 additions & 34 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
on:
# separate push conditions so they are combined with 'or', not 'and'
push:
branches:
- develop
# TODO: only for testing
- felix/automatic-release
push:
# Sequence of patterns matched against refs/tags
tags:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that multiple push values are always combined with AND, so the workflow is only excecuted if the push is on branch develop AND it is a tag. So far I havent found a way to make an OR condition, other than executing unconditionally and then checking manually if it should continue or abort.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The if statement could be a job that execute first and the other one depends on it?
But your statement is not true as per docs here: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-your-workflow-only-when-a-push-to-specific-branches-occurs

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also this option, where we it runs if a PR got merged: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-your-pull_request_target-workflow-when-a-pull-request-merges

Since we can't just push to the develop branch without a PR, this could also be an option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would still run on the PR branch so its probably not what we want. I will try running the workflow always on main and develop branches, then checking manually for tags on main.

Expand Down Expand Up @@ -67,24 +73,13 @@ jobs:
- run: ci/set_rust_version.bash stable ${{ matrix.target }}
- run: ci/build.bash cross ${{ matrix.target }} RELEASE
- run: tar -czvf ${{ env.BIN }}.tar.gz --directory=target/${{ matrix.target }}/release ${{ env.BIN }}
- uses: XAMPPRocky/create-release@v1.0.2
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: false
prerelease: false
- uses: actions/upload-release-asset@v1
id: upload-release-asset
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release artifacts for publishing
uses: actions/upload-artifact@v3
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ env.BIN }}.tar.gz
asset_name: ${{ env.BIN }}-${{ matrix.target }}.tar.gz
asset_content_type: application/gzip
name: dist-without-markdown
path: |
${{ env.BIN }}.tar.gz
${{ env.BIN }}-${{ matrix.target }}.tar.gz

linux:
runs-on: ubuntu-latest
Expand All @@ -95,6 +90,8 @@ jobs:
- aarch64-unknown-linux-gnu
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
- aarch64-unknown-linux-
Nutomic marked this conversation as resolved.
Show resolved Hide resolved
- armv7-unknown-linux-gnueabihf
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.9.1
Expand All @@ -117,22 +114,42 @@ jobs:
- run: ci/set_rust_version.bash stable ${{ matrix.target }}
- run: ci/build.bash /tmp/cross ${{ matrix.target }} RELEASE
- run: tar -czvf ${{ env.BIN }}.tar.gz --directory=target/${{ matrix.target }}/release ${{ env.BIN }}
- uses: XAMPPRocky/create-release@v1.0.2
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release artifacts for publishing
uses: actions/upload-artifact@v3
with:
name: dist-without-markdown
path: |
${{ env.BIN }}.tar.gz
${{ env.BIN }}-${{ matrix.target }}.tar.gz

publish:
name: publish
runs-on: ubuntu-latest
needs: ["macos", "linux"]
steps:
- name: Install Nodejs
uses: actions/setup-node@v3
with:
node-version: 18
- name: Download artifacts from builders
uses: actions/download-artifact@v1
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: false
prerelease: false
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: dist-aarch64-pc-windows-msvc
shekohex marked this conversation as resolved.
Show resolved Hide resolved
path: |
webb-relayer-aarch64-apple-darwin.tar.gz
webb-relayer-aarch64-unknown-linux-gnu.tar.gz
webb-relayer-x86_64-apple-darwin.tar.gz
webb-relayer-x86_64-unknown-linux-gnu.tar.gz
webb-relayer-x86_64-unknown-linux-musl.tar.gz
shekohex marked this conversation as resolved.
Show resolved Hide resolved
- run: ls -al ./dist
- run: echo ${{github.action}}
- name: Initialize variable `release_name`
env:
# TODO: change to develop branch
release_name: ${{ (github.branch == 'felix/automatic-release') && 'nightly' || github.ref}}
- name: Publish Release
uses: ./.github/actions/github-release
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ env.BIN }}.tar.gz
asset_name: ${{ env.BIN }}-${{ matrix.target }}.tar.gz
asset_content_type: application/gzip
files: "dist/*"
name: ${{ release_name }}
token: ${{ secrets.GITHUB_TOKEN }}