From 5ae5d8d01bdced0996656bd3ae803565d3508b07 Mon Sep 17 00:00:00 2001 From: Bailey Hayes Date: Mon, 1 Dec 2025 20:41:20 -0500 Subject: [PATCH] ci: add release workflow Works for both v0.2.+ and v0.3 rc's Signed-off-by: Bailey Hayes --- .github/RELEASE.md | 42 ++++ .github/actions/install-tools/action.yml | 4 +- .github/dependabot.yml | 10 + .github/scripts/release.sh | 210 ++++++++++++++++++ .github/workflows/ci.yml | 2 +- .github/workflows/lint-gh.yml | 2 +- .github/workflows/publish.yml | 271 +++++++++++++++++++++++ .github/workflows/release.yaml | 43 ---- .github/workflows/release.yml | 211 ++++++++++++++++++ 9 files changed, 748 insertions(+), 47 deletions(-) create mode 100644 .github/RELEASE.md create mode 100644 .github/dependabot.yml create mode 100755 .github/scripts/release.sh create mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/release.yml diff --git a/.github/RELEASE.md b/.github/RELEASE.md new file mode 100644 index 000000000..4c2be4208 --- /dev/null +++ b/.github/RELEASE.md @@ -0,0 +1,42 @@ +# WASI Release Process + + ┌─────────────────┐ ┌──────────────┐ ┌─────────────┐ + │ release.sh │────►│ release.yml │────►│ Creates PR │ + │ (manual trigger)│ │ (workflow) │ │ for review │ + └─────────────────┘ └──────────────┘ └──────┬──────┘ + │ + ┌──────────────┐ │ merge + │ publish.yml │◄───────────┘ + │ (on release) │ + └──────┬───────┘ + │ + ┌──────────────────────┼──────────────────────┐ + ▼ ▼ ▼ + ┌─────────┐ ┌─────────┐ ┌─────────┐ + │ wasi:io │ │wasi:cli │ ... │wasi:http│ + │ → GHCR │ │ → GHCR │ │ → GHCR │ + └─────────┘ └─────────┘ └─────────┘ + +## Usage + +The unified `release.sh` script in `.github/scripts/` handles both patch and RC releases: + +```bash +# Patch release (0.2.x stable) +.github/scripts/release.sh --type patch --prev 0.2.8 --next 0.2.9 + +# RC release (0.3.0-rc-YYYY-MM-DD) +.github/scripts/release.sh --type rc --prev-rc-date 2025-09-16 +.github/scripts/release.sh --type rc # First RC, no previous date +``` + +## What the Script Does + +The script automates the entire release process: + +1. Triggers `release.yml` to bump version numbers and create a PR +2. Waits for the PR to be created and CI to pass +3. Awaits manual review and merge of the PR +4. Creates a GitHub release (with `--prerelease` flag for RC) +5. Waits for `publish.yml` to publish packages to GHCR +6. Validates all packages were published successfully diff --git a/.github/actions/install-tools/action.yml b/.github/actions/install-tools/action.yml index 8a7490ecb..f94703fe0 100644 --- a/.github/actions/install-tools/action.yml +++ b/.github/actions/install-tools/action.yml @@ -15,13 +15,13 @@ runs: using: 'composite' steps: - name: Setup wasm-tools - uses: bytecodealliance/actions/wasm-tools/setup@v1 + uses: bytecodealliance/actions/wasm-tools/setup@d742827944dcb656569399571a8a45261b5089f6 # v1.1.0 with: version: ${{ inputs.wasm-tools-version }} - name: Cache wit-deps id: cache-wit-deps - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ~/.local/bin/wit-deps key: wit-deps-${{ inputs.wit-deps-version }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..2390d8c80 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + groups: + github-actions: + patterns: + - "*" diff --git a/.github/scripts/release.sh b/.github/scripts/release.sh new file mode 100755 index 000000000..edbca9994 --- /dev/null +++ b/.github/scripts/release.sh @@ -0,0 +1,210 @@ +#!/bin/bash + +# Unified release script for WASI releases +# +# This script automates the release process for both patch (0.2.x) and RC (0.3.0-rc) releases: +# 1. Triggers the release.yml workflow to update versions and create PR +# 2. Waits for PR to be filed and CI to pass +# 3. Waits for manual PR review and merge +# 4. Creates a GitHub release to trigger publishing +# 5. Waits for publish workflow to complete (validates packages in CI) +# +# Usage: +# Patch release: ./release.sh --type patch --prev 0.2.8 --next 0.2.9 +# RC release: ./release.sh --type rc [--prev-rc-date 2025-09-16] + +set -e +set -x + +# Parse arguments +RELEASE_TYPE="" +PREV_VERSION="" +NEXT_VERSION="" +PREV_RC_DATE="" + +while [[ $# -gt 0 ]]; do + case $1 in + --type) RELEASE_TYPE="$2"; shift 2 ;; + --prev) PREV_VERSION="$2"; shift 2 ;; + --next) NEXT_VERSION="$2"; shift 2 ;; + --prev-rc-date) PREV_RC_DATE="$2"; shift 2 ;; + -h|--help) + echo "Usage:" + echo " Patch release: $0 --type patch --prev --next " + echo " RC release: $0 --type rc [--prev-rc-date ]" + echo "" + echo "Examples:" + echo " $0 --type patch --prev 0.2.8 --next 0.2.9" + echo " $0 --type rc --prev-rc-date 2025-09-16" + echo " $0 --type rc # First RC, no previous date" + exit 0 + ;; + *) + echo "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done + +# Configuration +DATE="$(date +'%Y-%m-%d')" +REPO="WebAssembly/WASI" + +# Configure based on release type +if [ "$RELEASE_TYPE" == "patch" ]; then + if [ -z "$PREV_VERSION" ] || [ -z "$NEXT_VERSION" ]; then + echo "Error: Patch release requires --prev and --next" + echo "Example: $0 --type patch --prev 0.2.8 --next 0.2.9" + exit 1 + fi + TAG="v$NEXT_VERSION" + PRERELEASE_FLAG="" + RELEASE_LABEL="Patch" +elif [ "$RELEASE_TYPE" == "rc" ]; then + NEXT_VERSION="0.3.0-rc-$DATE" + TAG="v$NEXT_VERSION" + PRERELEASE_FLAG="--prerelease" + RELEASE_LABEL="RC" +else + echo "Error: --type must be 'patch' or 'rc'" + echo "Use --help for usage information" + exit 1 +fi + +echo "============================================" +echo "WASI $RELEASE_LABEL Release" +echo "============================================" +if [ "$RELEASE_TYPE" == "patch" ]; then + echo "Previous version: $PREV_VERSION" +else + echo "Previous RC date: ${PREV_RC_DATE:-'(none/first RC)'}" +fi +echo "Next version: $NEXT_VERSION" +echo "Tag: $TAG" +echo "Repository: $REPO" +echo "============================================" + +# Ensure we're operating on the correct repo +gh repo set-default "$REPO" + +# Check if release already exists +if gh release view "$TAG" &>/dev/null; then + echo "Error: Release $TAG already exists!" + echo "If you need to re-run, delete the release first:" + echo " gh release delete $TAG --yes" + exit 1 +fi + +# Step 1: Trigger the release workflow +echo "" +echo "Step 1: Triggering release.yml workflow..." + +if [ "$RELEASE_TYPE" == "patch" ]; then + gh workflow run "release.yml" \ + -f release_type="patch" \ + -f prev_version="$PREV_VERSION" \ + -f next_version="$NEXT_VERSION" +else + if [ -n "$PREV_RC_DATE" ]; then + gh workflow run "release.yml" \ + -f release_type="rc" \ + -f prev_rc_date="$PREV_RC_DATE" + else + gh workflow run "release.yml" \ + -f release_type="rc" + fi +fi + +# Wait for workflow to start +echo "Waiting for workflow to start..." +sleep 10 + +# Get the run ID +RUN_ID="$(gh run list --workflow "release.yml" --created "$DATE" --json databaseId --limit 1 | jq -r '.[0].databaseId')" +if [ -z "$RUN_ID" ] || [ "$RUN_ID" == "null" ]; then + echo "Error: Could not find workflow run" + exit 1 +fi + +echo "Workflow run ID: $RUN_ID" +echo "Waiting for workflow to complete..." +gh run watch "$RUN_ID" --exit-status || { + echo "Error: Workflow failed!" + gh run view "$RUN_ID" --log-failed + exit 1 +} + +# Step 2: Wait for PR and CI +echo "" +echo "Step 2: Waiting for PR..." +sleep 5 + +PR_NUMBER="$(gh pr list --head "release-v$NEXT_VERSION" --json number --limit 1 | jq -r '.[0].number')" +if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" == "null" ]; then + echo "Error: Could not find PR for release-v$NEXT_VERSION" + exit 1 +fi + +echo "PR #$PR_NUMBER created" + +# Close and reopen to trigger CI (workaround for some CI configurations) +echo "Retriggering CI..." +gh pr close "$PR_NUMBER" +gh pr reopen "$PR_NUMBER" + +echo "Waiting for CI checks to pass..." +sleep 10 +gh pr checks "$PR_NUMBER" --watch || { + echo "Warning: Some checks may have failed. Continuing anyway..." +} + +# Step 3: Wait for manual PR review and merge +echo "" +echo "Step 3: PR ready for review" +echo "============================================" +echo "PR #$PR_NUMBER: https://github.com/$REPO/pull/$PR_NUMBER" +echo "============================================" +echo "" +read -r -p "Press Enter after the PR has been reviewed and merged..." + +# Verify PR was actually merged +STATE="$(gh pr view "$PR_NUMBER" --json state --jq '.state')" +if [ "$STATE" != "MERGED" ]; then + echo "Error: PR #$PR_NUMBER is not merged (state: $STATE)" + exit 1 +fi + +# Step 4: Create GitHub release +echo "" +echo "Step 4: Creating GitHub release $TAG..." +sleep 5 + +gh release create "$TAG" --generate-notes $PRERELEASE_FLAG +gh release view "$TAG" + +# Step 5: Wait for publish workflow +echo "" +echo "Step 5: Waiting for publish workflow to complete..." +sleep 10 + +PUBLISH_RUN_ID="$(gh run list --workflow "publish.yml" --created "$DATE" --json databaseId --limit 1 | jq -r '.[0].databaseId')" +if [ -z "$PUBLISH_RUN_ID" ] || [ "$PUBLISH_RUN_ID" == "null" ]; then + echo "Warning: Could not find publish workflow run. It may not have started yet." + sleep 30 + PUBLISH_RUN_ID="$(gh run list --workflow "publish.yml" --created "$DATE" --json databaseId --limit 1 | jq -r '.[0].databaseId')" +fi + +if [ -n "$PUBLISH_RUN_ID" ] && [ "$PUBLISH_RUN_ID" != "null" ]; then + echo "Publish workflow run ID: $PUBLISH_RUN_ID" + gh run watch "$PUBLISH_RUN_ID" --exit-status || { + echo "Error: Publish workflow failed!" + gh run view "$PUBLISH_RUN_ID" --log-failed + exit 1 + } +fi + +echo "" +echo "============================================" +echo "✓ Release $NEXT_VERSION ($RELEASE_LABEL) completed successfully!" +echo "============================================" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3077a3703..36eedad61 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: name: Validate WIT runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: changes diff --git a/.github/workflows/lint-gh.yml b/.github/workflows/lint-gh.yml index 3320d1667..cfd493330 100644 --- a/.github/workflows/lint-gh.yml +++ b/.github/workflows/lint-gh.yml @@ -17,5 +17,5 @@ jobs: name: Lint GitHub Actions runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - uses: rhysd/actionlint@a443f344ff32813837fa49f7aa6cbc478d770e62 # v1.7.9 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..cd815a8c4 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,271 @@ +name: Publish + +# This workflow publishes all WASI proposals to GitHub Container Registry +# when a release is created + +on: + release: + types: [published] + + workflow_dispatch: + inputs: + version: + description: 'Version to publish (without the v)' + required: true + type: string + wit_dir: + description: 'WIT directory to publish from (wit or wit-0.3.0-draft)' + required: true + type: choice + options: + - wit + - wit-0.3.0-draft + default: wit + +jobs: + # Determine version and configuration + setup: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + wit_dir: ${{ steps.config.outputs.wit_dir }} + is_prerelease: ${{ steps.config.outputs.is_prerelease }} + steps: + - name: Get version from release tag + id: version + run: | + if [ "${{ github.event_name }}" == "release" ]; then + # Extract version from tag (remove 'v' prefix) + VERSION="${{ github.event.release.tag_name }}" + VERSION="${VERSION#v}" + else + VERSION="${{ inputs.version }}" + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Publishing version: $VERSION" + + - name: Determine configuration + id: config + run: | + VERSION="${{ steps.version.outputs.version }}" + + # Determine configuration based on version: + # - RC versions always use wit-0.3.0-draft + # - Stable versions use wit (or workflow_dispatch input if provided) + if [[ "$VERSION" == *"-rc-"* ]]; then + IS_PRERELEASE="true" + WIT_DIR="wit-0.3.0-draft" + elif [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ -n "${{ inputs.wit_dir }}" ]; then + IS_PRERELEASE="false" + WIT_DIR="${{ inputs.wit_dir }}" + else + IS_PRERELEASE="false" + WIT_DIR="wit" + fi + + { + echo "is_prerelease=$IS_PRERELEASE" + echo "wit_dir=$WIT_DIR" + } >> "$GITHUB_OUTPUT" + + # Publish each proposal + publish: + needs: setup + runs-on: ubuntu-latest + permissions: + id-token: write + packages: write + contents: write + attestations: write + + strategy: + fail-fast: false + matrix: + proposal: + - name: io + description: "WASI I/O interfaces for streams and poll" + exclude_for_p3: true + - name: random + description: "WASI random number generation interfaces" + - name: clocks + description: "WASI clock interfaces for monotonic and wall clocks" + - name: filesystem + description: "WASI filesystem interfaces" + - name: sockets + description: "WASI socket interfaces for TCP and UDP" + - name: cli + description: "WASI CLI interfaces for command-line programs" + - name: http + description: "WASI HTTP interfaces for HTTP client and server" + + steps: + # Skip proposals marked exclude_for_p3 when publishing prereleases (0.3.0-rc) + - name: Check if should skip + id: skip_check + run: | + if [ "${{ matrix.proposal.exclude_for_p3 }}" == "true" ] && [ "${{ needs.setup.outputs.is_prerelease }}" == "true" ]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "Skipping ${{ matrix.proposal.name }} for P3 prerelease" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout repository + if: steps.skip_check.outputs.skip != 'true' + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + + - name: Install cargo-binstall + if: steps.skip_check.outputs.skip != 'true' + uses: cargo-bins/cargo-binstall@3fc81674af4165a753833a94cae9f91d8849049f # v1.16.2 + + - name: Install wkg + if: steps.skip_check.outputs.skip != 'true' + shell: bash + run: cargo binstall -y wkg + + - name: Login to GitHub Container Registry + if: steps.skip_check.outputs.skip != 'true' + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.ORG_PAT }} + + - name: Build WIT package + if: steps.skip_check.outputs.skip != 'true' + shell: bash + working-directory: proposals/${{ matrix.proposal.name }}/${{ needs.setup.outputs.wit_dir }} + run: | + echo "Building from: $(pwd)" + wkg wit build -o "$GITHUB_WORKSPACE/wasi-${{ matrix.proposal.name }}.wasm" + + - name: Publish to GitHub Container Registry + if: steps.skip_check.outputs.skip != 'true' + id: publish + uses: bytecodealliance/wkg-github-action@10b3b04b9059ba46208cd7daf7d352af14bded0f # v5 + with: + oci-reference-without-tag: 'ghcr.io/webassembly/wasi/${{ matrix.proposal.name }}' + file: 'wasi-${{ matrix.proposal.name }}.wasm' + description: ${{ matrix.proposal.description }} + source: 'https://github.com/webassembly/wasi' + homepage: 'https://wasi.dev' + version: ${{ needs.setup.outputs.version }} + licenses: 'Apache-2.0 WITH LLVM-exception' + + - name: Attest build provenance + if: steps.skip_check.outputs.skip != 'true' + uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 + with: + subject-name: ghcr.io/webassembly/wasi/${{ matrix.proposal.name }} + subject-digest: ${{ steps.publish.outputs.digest }} + push-to-registry: true + github-token: ${{ secrets.ORG_PAT }} + + # Validate all packages were published successfully + validate: + needs: [setup, publish] + runs-on: ubuntu-latest + steps: + - name: Install oras + uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4 + + - name: Validate published packages + run: | + VERSION="${{ needs.setup.outputs.version }}" + IS_PRERELEASE="${{ needs.setup.outputs.is_prerelease }}" + + # io is excluded from RC releases + if [ "$IS_PRERELEASE" == "true" ]; then + PROPOSALS="random clocks filesystem sockets cli http" + else + PROPOSALS="io random clocks filesystem sockets cli http" + fi + + echo "Validating packages for version $VERSION..." + FAILED="" + + for proposal in $PROPOSALS; do + echo "Checking ghcr.io/webassembly/wasi/$proposal:$VERSION..." + if oras manifest fetch "ghcr.io/webassembly/wasi/$proposal:$VERSION" > /dev/null 2>&1; then + echo " ✓ $proposal published successfully" + else + echo " ✗ $proposal NOT FOUND" + FAILED="$FAILED $proposal" + fi + done + + if [ -n "$FAILED" ]; then + echo "" + echo "ERROR: Failed to validate packages:$FAILED" + exit 1 + fi + + echo "" + echo "✓ All packages validated successfully!" + + # Create specification entry after all publishes complete + create-specification: + needs: [setup, publish, validate] + runs-on: ubuntu-latest + if: needs.setup.outputs.is_prerelease == 'false' + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + + - name: Create specification directory + run: | + VERSION="${{ needs.setup.outputs.version }}" + SPEC_DIR="specifications/wasi-$VERSION" + mkdir -p "$SPEC_DIR" + + cat > "$SPEC_DIR/Overview.md" << EOF + # WASI Specification v${VERSION} + + This is version ${VERSION} of the WASI specification, based on the [WebAssembly + Component Model][cm]. + + ## Proposals + + The [WebAssembly Interface Type (WIT)][wit] definitions for the proposals included in this + version of the specification are pushed to OCI based on the [Wasm OCI Artifact + layout][wasm-oci]: + + - [wasi:io@${VERSION}](https://github.com/WebAssembly/WASI/pkgs/container/wasi%2Fio?tag=${VERSION}) + - [wasi:random@${VERSION}](https://github.com/WebAssembly/WASI/pkgs/container/wasi%2Frandom?tag=${VERSION}) + - [wasi:clocks@${VERSION}](https://github.com/WebAssembly/WASI/pkgs/container/wasi%2Fclocks?tag=${VERSION}) + - [wasi:sockets@${VERSION}](https://github.com/WebAssembly/WASI/pkgs/container/wasi%2Fsockets?tag=${VERSION}) + - [wasi:filesystem@${VERSION}](https://github.com/WebAssembly/WASI/pkgs/container/wasi%2Ffilesystem?tag=${VERSION}) + - [wasi:cli@${VERSION}](https://github.com/WebAssembly/WASI/pkgs/container/wasi%2Fcli?tag=${VERSION}) + - [wasi:http@${VERSION}](https://github.com/WebAssembly/WASI/pkgs/container/wasi%2Fhttp?tag=${VERSION}) + + [cm]: https://github.com/WebAssembly/component-model + [wit]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md + [wasm-oci]: https://tag-runtime.cncf.io/wgs/wasm/deliverables/wasm-oci-artifact + EOF + + # Remove leading whitespace from heredoc + sed -i 's/^ //' "$SPEC_DIR/Overview.md" + + - name: Create Pull Request for specification + uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9 + with: + commit-message: "Add specification for v${{ needs.setup.outputs.version }}" + branch: spec-v${{ needs.setup.outputs.version }} + title: "Add specification for WASI v${{ needs.setup.outputs.version }}" + body: | + This PR adds the specification entry for WASI v${{ needs.setup.outputs.version }}. + + The following packages have been published to GHCR: + - `ghcr.io/webassembly/wasi/io:${{ needs.setup.outputs.version }}` + - `ghcr.io/webassembly/wasi/random:${{ needs.setup.outputs.version }}` + - `ghcr.io/webassembly/wasi/clocks:${{ needs.setup.outputs.version }}` + - `ghcr.io/webassembly/wasi/sockets:${{ needs.setup.outputs.version }}` + - `ghcr.io/webassembly/wasi/filesystem:${{ needs.setup.outputs.version }}` + - `ghcr.io/webassembly/wasi/cli:${{ needs.setup.outputs.version }}` + - `ghcr.io/webassembly/wasi/http:${{ needs.setup.outputs.version }}` + base: main + delete-branch: true diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index 3f1c3678f..000000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: WASI release - -on: - workflow_dispatch: - inputs: - wasi_version: - description: 'WASI version (without the v)' - required: true - default: '0.2.5' - -permissions: - contents: write - pull-requests: write - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Needed for PRs and full history - - name: Install cargo-binstall - uses: cargo-bins/cargo-binstall@v1.12.5 - - name: Install wit-bindgen - shell: bash - run: cargo binstall -y wit-bindgen-cli wit-deps-cli - - - - name: Run WASI deps script - run: ./scripts/wasi-deps.sh "${{ github.event.inputs.wasi_version }}" - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 - with: - commit-message: "Release WASI v${{ github.event.inputs.wasi_version }}" - branch: release-v${{ github.event.inputs.wasi_version }} - title: "Release WASI v${{ github.event.inputs.wasi_version }}" - body: | - Release v${{ inputs.wasi_version }}. - base: main - delete-branch: true - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..15bc1b209 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,211 @@ +name: Release WASI + +# Main workflow for creating WASI releases +# This workflow: +# 1. Updates version strings across all proposals +# 2. Creates a PR for review +# 3. After merge, a release can be created to trigger publish + +on: + workflow_dispatch: + inputs: + release_type: + description: 'Type of release' + required: true + type: choice + options: + - patch # 0.2.x patch release + - rc # 0.3.0-rc-YYYY-MM-DD release candidate + prev_version: + description: 'Previous version (for patch releases, e.g., 0.2.8)' + required: false + type: string + next_version: + description: 'Next version (for patch releases, e.g., 0.2.9). For RC, leave empty to auto-generate date.' + required: false + type: string + prev_rc_date: + description: 'Previous RC date (for RC releases, e.g., 2025-09-16). Leave empty for first RC.' + required: false + type: string + +permissions: + contents: write + pull-requests: write + +jobs: + prepare-release: + runs-on: ubuntu-latest + outputs: + prev_version: ${{ steps.versions.outputs.prev_version }} + next_version: ${{ steps.versions.outputs.next_version }} + wit_dir: ${{ steps.versions.outputs.wit_dir }} + tag: ${{ steps.versions.outputs.tag }} + is_prerelease: ${{ steps.versions.outputs.is_prerelease }} + + steps: + - name: Checkout repository + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + + - name: Determine versions + id: versions + run: | + DATE="$(date +'%Y-%m-%d')" + + if [ "${{ inputs.release_type }}" == "patch" ]; then + # Patch release (0.2.x) + PREV_VERSION="${{ inputs.prev_version }}" + NEXT_VERSION="${{ inputs.next_version }}" + WIT_DIR="wit" + TAG="v$NEXT_VERSION" + IS_PRERELEASE="false" + + if [ -z "$PREV_VERSION" ] || [ -z "$NEXT_VERSION" ]; then + echo "Error: prev_version and next_version are required for patch releases" + exit 1 + fi + else + # RC release (0.3.0-rc-YYYY-MM-DD) + if [ -n "${{ inputs.prev_rc_date }}" ]; then + PREV_VERSION="0.3.0-rc-${{ inputs.prev_rc_date }}" + else + # First RC or unknown previous + PREV_VERSION="0.3.0-draft" + fi + NEXT_VERSION="0.3.0-rc-$DATE" + WIT_DIR="wit-0.3.0-draft" + TAG="v$NEXT_VERSION" + IS_PRERELEASE="true" + fi + + { + echo "prev_version=$PREV_VERSION" + echo "next_version=$NEXT_VERSION" + echo "wit_dir=$WIT_DIR" + echo "tag=$TAG" + echo "is_prerelease=$IS_PRERELEASE" + } >> "$GITHUB_OUTPUT" + + echo "Release configuration:" + echo " Previous version: $PREV_VERSION" + echo " Next version: $NEXT_VERSION" + echo " WIT directory: $WIT_DIR" + echo " Tag: $TAG" + echo " Is prerelease: $IS_PRERELEASE" + + update-versions: + needs: prepare-release + runs-on: ubuntu-latest + env: + # io is excluded for 0.3.0-rc (no wit-0.3.0-draft directory) + PROPOSALS: ${{ inputs.release_type == 'patch' && 'io random clocks filesystem sockets cli http' || 'random clocks filesystem sockets cli http' }} + steps: + - name: Checkout repository + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + + - name: Install tools + uses: ./.github/actions/install-tools + + - name: Setup wit-bindgen + uses: bytecodealliance/actions/wit-bindgen/setup@d742827944dcb656569399571a8a45261b5089f6 # v1.1.0 + with: + version: '0.48.1' + + - name: Update WIT versions + run: | + WIT_DIR="${{ needs.prepare-release.outputs.wit_dir }}" + PREV="${{ needs.prepare-release.outputs.prev_version }}" + NEXT="${{ needs.prepare-release.outputs.next_version }}" + + echo "Updating from $PREV to $NEXT in $WIT_DIR" + + for proposal in $PROPOSALS; do + wit_path="proposals/$proposal/$WIT_DIR" + if [ -d "$wit_path" ]; then + echo "Updating $proposal..." + + # Update package declarations and use/import statements + find "$wit_path" -type f -name "*.wit" -exec sed -i \ + "s/\(wasi:[a-zA-Z0-9_\/-]*@\)$PREV/\1$NEXT/g" {} + || true + + # Only update @since annotations if they contain an RC version (-rc-). + # Stable @since versions (e.g., @since(version = 0.2.0)) mark when a feature + # was released and must not be changed. + find "$wit_path" -type f -name "*.wit" -exec sed -i \ + "s/\(@since(version = \)\([0-9.]*-rc-[^)]*\))/\1$NEXT)/g" {} + || true + fi + done + + - name: Update wit-deps + run: | + WIT_DIR="${{ needs.prepare-release.outputs.wit_dir }}" + + for proposal in $PROPOSALS; do + wit_path="proposals/$proposal/$WIT_DIR" + deps_file="$wit_path/deps.toml" + + if [ -f "$deps_file" ]; then + echo "Updating dependencies for $proposal..." + wit-deps -m "$deps_file" -l "$wit_path/deps.lock" -d "$wit_path/deps" update + fi + done + + # Generate markdown documentation (only for 0.2.x patch releases) + - name: Generate markdown documentation + if: inputs.release_type == 'patch' + run: | + WIT_DIR="${{ needs.prepare-release.outputs.wit_dir }}" + + for proposal in $PROPOSALS; do + wit_path="proposals/$proposal/$WIT_DIR" + if [ -d "$wit_path" ]; then + echo "Generating docs for $proposal..." + + worlds=$(find "$wit_path" -maxdepth 1 -type f -name '*.wit' -exec grep -hE '^world[[:space:]]+' {} + 2>/dev/null \ + | sed -E 's/^world[[:space:]]+([^[:space:]]+).*$/\1/' \ + | tr '\n' ' ' || echo "") + + for world in $worlds; do + echo " Generating markdown for world: $world" + wit-bindgen markdown "$wit_path" -w "$world" --html-in-md --all-features + done + fi + done + + - name: Create Pull Request + uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9 + with: + commit-message: "Release WASI v${{ needs.prepare-release.outputs.next_version }}" + branch: release-v${{ needs.prepare-release.outputs.next_version }} + title: "Release WASI v${{ needs.prepare-release.outputs.next_version }}" + body: | + ## Release v${{ needs.prepare-release.outputs.next_version }} + + This PR updates all WASI proposals to version `${{ needs.prepare-release.outputs.next_version }}`. + + ### Release Type + ${{ inputs.release_type == 'patch' && '**Patch Release** (stable 0.2.x)' || '**Release Candidate** (0.3.0-rc)' }} + + ### Version Changes + - Previous: `${{ needs.prepare-release.outputs.prev_version }}` + - Next: `${{ needs.prepare-release.outputs.next_version }}` + - WIT directory: `${{ needs.prepare-release.outputs.wit_dir }}` + + ### Proposals Updated + ${{ inputs.release_type == 'patch' && '- io' || '' }} + - random + - clocks + - filesystem + - sockets + - cli + - http + + ### After Merging + + After this PR is merged, create a GitHub release with tag `${{ needs.prepare-release.outputs.tag }}` to trigger publishing to GHCR. + + ```bash + gh release create "${{ needs.prepare-release.outputs.tag }}" --generate-notes ${{ needs.prepare-release.outputs.is_prerelease == 'true' && '--prerelease' || '' }} + ``` + base: main + delete-branch: true