-
Notifications
You must be signed in to change notification settings - Fork 192
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
ci: document and script the release process #295
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Release Process | ||
|
||
To publish a new version of `wasi-sdk` as a GitHub release: | ||
|
||
1. Tag a commit with an annotated tag. Note that this must be an annotated tag, | ||
not a lightweight tag, so that `version.sh` can use it for calculating the | ||
package version (use `git show wasi-sdk-...` to show other tag messages). | ||
|
||
```shell script | ||
TAG=wasi-sdk-1 | ||
git tag -a $TAG | ||
git push origin $TAG | ||
``` | ||
|
||
2. Find a successful workflow that CI has run for the tag. That successful | ||
workflow run will have build artifacts that need to be attached to the | ||
release. One could search around in the GitHub [actions], but the following | ||
script will list completed workflows for a tag (get a token [here][tokens]): | ||
|
||
```shell script | ||
ci/get-workflows-for-tag.sh $TAG $GITHUB_TOKEN | ||
``` | ||
|
||
[actions]: https://github.com/WebAssembly/wasi-sdk/actions | ||
[tokens]: https://github.com/settings/tokens | ||
|
||
3. Download and unzip the workflow artifacts. Note that artifacts with `+m` or | ||
`.m` suffixes indicate that the Git tree was modified. Expect some duplicates | ||
since some of the same artifacts are built on multiple CI runners (e.g., | ||
Windows, MacOS, Linux). The following script does all of this automatically: | ||
|
||
```shell script | ||
ci/download-workflow-artifacts.sh $TAG $WORKFLOW_RUN_ID $GITHUB_TOKEN | ||
``` | ||
|
||
4. Draft a new release. This could be done [manually][releases] but the | ||
following script simplifies the uploading of all the files and auto-generates | ||
the release description: | ||
|
||
```shell script | ||
ci/draft-release.sh $TAG $ARTIFACTS_DIR $GITHUB_TOKEN | ||
``` | ||
|
||
[releases]: https://github.com/WebAssembly/wasi-sdk/releases | ||
|
||
5. Publish the release; the previous step only creates a draft. Follow the link | ||
in the previous step or navigate to the GitHub [releases] to review the | ||
description, commit, tag, and assets before clicking "Publish" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
#!/usr/bin/env bash | ||
set -e | ||
|
||
# This script downloads and unzips the artifacts produced in a workflow run. It | ||
# also checks that the workflow commit corresponds to the tag commit that these | ||
# artifacts will be released under. The script has several pre-requisites: | ||
# - some standard Bash tools (curl, unzip) and one slightly more rare one (jq) | ||
# - an already-created tag in the repository (this marks the code to release) | ||
# - the ID of a workflow run that has run successfully--this is where we | ||
# retrieve the artifacts from | ||
# - a GitHub access token, see https://github.com/settings/tokens | ||
# | ||
# Usage: download-workflow-artifacts.sh <release tag> <workflow run ID> <token> | ||
|
||
TAG=$1 | ||
WORKFLOW_RUN_ID=$2 | ||
GITHUB_TOKEN=$3 | ||
GITHUB_API_VERSION=2022-11-28 | ||
GITHUB_API_URL=https://api.github.com/repos/WebAssembly/wasi-sdk | ||
TMP_DIR=$(mktemp -d -t wasi-sdk-artifacts.XXXXXXX) | ||
|
||
if [ -z "${TAG}" ] || [ -z "${WORKFLOW_RUN_ID}" ] || [ -z "${GITHUB_TOKEN}" ]; then | ||
>&2 echo "Missing parameter; exiting..." | ||
>&2 echo "Usage: download-worfklow-artifacts.sh <release tag> <workflow run ID> <token>" | ||
exit 1 | ||
fi | ||
|
||
# Get the commit SHA for the passed tag. | ||
# See https://docs.github.com/en/rest/commits/commits#get-a-commit | ||
MATCHING_COMMIT=$(curl \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
"${GITHUB_API_URL}/commits/${TAG}") | ||
COMMIT=$(echo $MATCHING_COMMIT | jq -r '.sha') | ||
>&2 echo "===== Found commit for tag ${TAG}: ${COMMIT} =====" | ||
|
||
# Check that the commit of the workflow run matches the tag commit and that the | ||
# workflow was successful. | ||
# See https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run | ||
WORKFLOW_RUN=$(curl \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
"${GITHUB_API_URL}/actions/runs/${WORKFLOW_RUN_ID}") | ||
WORKFLOW_COMMIT=$(echo $WORKFLOW_RUN | jq -r '.head_sha') | ||
WORKFLOW_STATUS=$(echo $WORKFLOW_RUN | jq -r '.status') | ||
>&2 echo "===== Found commit for workflow ${WORKFLOW_RUN_ID}: ${WORKFLOW_COMMIT} =====" | ||
if [ "${COMMIT}" != "${WORKFLOW_COMMIT}" ]; then | ||
>&2 echo "Commit at tag ${TAG} did not match the commit for workflow ${WORKFLOW_RUN_ID}, exiting...:" | ||
>&2 echo " ${COMMIT} != ${WORKFLOW_COMMIT}" | ||
exit 1 | ||
fi | ||
if [ "${WORKFLOW_STATUS}" != "completed" ]; then | ||
>&2 echo "Workflow ${WORKFLOW_RUN_ID} did not end successfully, exiting...:" | ||
>&2 echo " status = ${WORKFLOW_STATUS}" | ||
exit 1 | ||
fi | ||
|
||
# List out the artifacts in the given workflow run. | ||
# See https://docs.github.com/en/rest/actions/artifacts#list-workflow-run-artifacts | ||
ARTIFACTS=$(curl \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
"${GITHUB_API_URL}/actions/runs/${WORKFLOW_RUN_ID}/artifacts" \ | ||
| jq -r '.artifacts[] | [(.id|tostring), .name, .archive_download_url] | join(",")') | ||
|
||
for A in $ARTIFACTS; do | ||
ID=$(echo $A | cut -d ',' -f 1) | ||
NAME=$(echo $A | cut -d ',' -f 2) | ||
URL=$(echo $A | cut -d ',' -f 3) | ||
TO=$TMP_DIR/$NAME.zip | ||
>&2 echo "===== Downloading: ${TO} =====" | ||
|
||
# Download the artifacts to the temporary directory. | ||
# See https://docs.github.com/en/rest/actions/artifacts#download-an-artifact | ||
curl \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
--location --output "${TO}" \ | ||
"${GITHUB_API_URL}/actions/artifacts/${ID}/zip" | ||
done | ||
|
||
# Unzip the workflow artifacts into a `release` directory. | ||
pushd $TMP_DIR > /dev/null | ||
mkdir release | ||
ls -1 *.zip | xargs -n1 unzip -q -o -d release | ||
# Some explanation: | ||
# -1 prints each file on a separate line | ||
# -n1 runs the command once for each item | ||
# -q means quietly | ||
# -o allows unzip to overwrite existing files (e.g., multiple copies of `libclang_rt.builtins-wasm32-wasi-...`) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for linux, assets from the docker job should be used. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I understand why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. assets from ubuntu job (dist-ubuntu-latest) requires newer glibc. |
||
# -d tells unzip which directory to place things in | ||
>&2 echo "===== Files to release: ${TMP_DIR}/release =====" | ||
>&2 ls -1 release | ||
popd > /dev/null | ||
|
||
>&2 echo | ||
>&2 echo "Ensure the above artifacts look correct, then run \`draft-release.sh\` with the following directory:" | ||
echo "${TMP_DIR}/release" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
#!/usr/bin/env bash | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you write all these new shell scripts from scratch? Do you expect these scripts to run during ci? If not perhaps they should just live under There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I wrote them as I fought through issues with CI/Git/GitHub while trying to publish the wasi-sdk-20+threads pre-release (it was a fight: not every CI run has the right tag applied, e.g.). I had to do things enough times that this work already paid for itself, but in the back of my head it seemed like a good idea to record how one might publish new wasi-sdk releases. I was hoping at some point to integrate these in CI with a GitHub workflow trigger but that could be done later. I don't mind moving them to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I talked to @sunfishcode a bit more about this kind of PR and I think I'm going to go ahead and merge soon; @sbc100, any strong preference on where to put these scripts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really no. |
||
set -e | ||
|
||
# This script creates a draft pre-release with the artifacts produced in a | ||
# workflow run (see `download-workflow-artifacts.sh`). Note that the pre-release | ||
# is not published publicly--this is kept as a manual step as a safeguard. The | ||
# script has several pre-requisites: | ||
# - some standard Bash tools (curl, unzip) and one slightly more rare one (jq) | ||
# - an already-created tag in the repository (this marks the code to release) | ||
# - a directory containing the artifacts to attach to the release. | ||
# - a GitHub access token, see https://github.com/settings/tokens | ||
# | ||
# Usage: draft-release.sh <release tag> <artifacts dir> <token> | ||
|
||
TAG=$1 | ||
ARTIFACTS_DIR=$2 | ||
GITHUB_TOKEN=$3 | ||
GITHUB_API_VERSION=2022-11-28 | ||
GITHUB_API_URL=https://api.github.com/repos/WebAssembly/wasi-sdk | ||
TMP_DIR=$(mktemp -d -t release.sh.XXXXXXX) | ||
|
||
if [ -z "${TAG}" ] || [ -z "${ARTIFACTS_DIR}" ] || [ -z "${GITHUB_TOKEN}" ]; then | ||
>&2 echo "Missing parameter; exiting..." | ||
>&2 echo "Usage: draft-release.sh <release tag> <artifacts dir> <token>" | ||
exit 1 | ||
fi | ||
|
||
# Get the commit SHA for the passed tag. | ||
# See https://docs.github.com/en/rest/commits/commits#get-a-commit | ||
MATCHING_COMMIT=$(curl \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
"${GITHUB_API_URL}/commits/${TAG}") | ||
COMMIT=$(echo $MATCHING_COMMIT | jq -r '.sha') | ||
>&2 echo "===== Found commit for tag ${TAG}: ${COMMIT} =====" | ||
|
||
# Create a draft pre-release for this commit. | ||
# See https://docs.github.com/en/rest/releases/releases#create-a-release | ||
RELEASE_JSON=$(curl \ | ||
-X POST \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}"\ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
"${GITHUB_API_URL}/releases" \ | ||
-d '{"tag_name":"'${TAG}'","target_commitish":"'${COMMIT}'","name":"'${TAG}'","draft":true,"prerelease":true,"generate_release_notes":true}') | ||
UPLOAD_URL=$(echo $RELEASE_JSON | jq -r '.upload_url') | ||
# Remove the "helpful" but invalid URL suffix that GitHub adds: | ||
UPLOAD_URL=${UPLOAD_URL/\{?name,label\}} | ||
HTML_URL=$(echo $RELEASE_JSON | jq -r '.html_url') | ||
>&2 echo "===== Created draft release: ${HTML_URL} =====" | ||
|
||
# Upload the unzipped artifact files to the release. | ||
# See https://docs.github.com/en/rest/releases/assets#upload-a-release-asset | ||
for FILE in $(ls "${ARTIFACTS_DIR}"); do | ||
FROM=$ARTIFACTS_DIR/$FILE | ||
>&2 echo "===== Uploading: ${FROM} =====" | ||
UPLOADED=$(curl \ | ||
-X POST \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}"\ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
-H "Content-Type: application/octet-stream" \ | ||
"${UPLOAD_URL}?name=${FILE}" \ | ||
--data-binary "@${FROM}") | ||
done | ||
|
||
>&2 echo | ||
abrown marked this conversation as resolved.
Show resolved
Hide resolved
|
||
>&2 echo "===== Completed =====" | ||
>&2 echo "This created a draft release, do not forget to manually publish it at:" | ||
echo "${HTML_URL}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#!/usr/bin/env bash | ||
set -e | ||
|
||
# This script retrieves a list of GitHub workflow runs that have successfully completed for a given | ||
# Git tag. The list is printed to stdout (all other output to stderr). It has several | ||
# pre-requisites: | ||
# - some standard Bash tools (curl) and one slightly more rare one (jq) | ||
# - an already-created tag in the repository (this marks the code to release) | ||
# - a GitHub access token, see https://github.com/settings/tokens | ||
# | ||
# Usage: get-workflows-for-tag.sh <tag> <token> | ||
|
||
TAG=$1 | ||
GITHUB_TOKEN=$2 | ||
GITHUB_API_VERSION=2022-11-28 | ||
GITHUB_API_URL=https://api.github.com/repos/WebAssembly/wasi-sdk | ||
|
||
if [ -z "${TAG}" ] || [ -z "${GITHUB_TOKEN}" ]; then | ||
>&2 echo "Missing parameter; exiting..." | ||
>&2 echo "Usage: get-workflows-for-tag.sh <tag> <token>" | ||
exit 1 | ||
fi | ||
|
||
# Get the commit SHA for the passed tag. | ||
# See https://docs.github.com/en/rest/commits/commits#get-a-commit | ||
MATCHING_COMMIT=$(curl \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
"${GITHUB_API_URL}/commits/${TAG}") | ||
COMMIT=$(echo $MATCHING_COMMIT | jq -r '.sha') | ||
>&2 echo "===== Found commit for tag ${TAG}: ${COMMIT} =====" | ||
|
||
# Find the workflow runs matching the tag commit. | ||
# See https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-repository | ||
MATCHING_WORKFLOWS=$(curl \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \ | ||
-H "X-GitHub-Api-Version: ${GITHUB_API_VERSION}" \ | ||
"${GITHUB_API_URL}/actions/runs?head_sha=${COMMIT}&status=success") | ||
WORKFLOW_RUN_IDS=$(echo $MATCHING_WORKFLOWS | jq -r '.workflow_runs[].id') | ||
>&2 echo "===== Matching workflow run IDs: =====" | ||
echo "$WORKFLOW_RUN_IDS" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you prefer to have a low-level script here while there are many handy commands/scripts (eg.
hub
) and even github actions available?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not everyone has all of that installed; I went with an environment I thought @sunfishcode would like 😁. (The idea was that these scripts would be runnable both from CI or from a maintainer's shell if something goes wrong).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i suspect it's easier to install one of them than maintaining this script by ourselves. but i don't insist.