mirror of https://github.com/WebAssembly/wasi-sdk
ci: document and script the release process (#295)
Previously, the release process for wasi-sdk was undocumented and manual. The `RELEASING.md` document in this commit describes the steps necessary to publish a new version of `wasi-sdk` as a GitHub release and provides helpful scripts to automate some of the steps. With this in place, a future change could add a workflow trigger that allows running the steps automatically in GitHub actions. Keeping the steps as scripts in the repository, however, allows developers to re-run steps themselves in the (likely) case that something goes awry.pull/307/head
parent
7ef7e948fa
commit
22b1d89e64
@ -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"
|
@ -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-...`)
|
||||||
|
# -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"
|
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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
|
||||||
|
>&2 echo "===== Completed ====="
|
||||||
|
>&2 echo "This created a draft release, do not forget to manually publish it at:"
|
||||||
|
echo "${HTML_URL}"
|
@ -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"
|
Loading…
Reference in new issue