From d605abc322c402b64604b70d6c100996763be9ef Mon Sep 17 00:00:00 2001 From: Mendy Berger <12537668+MendyBerger@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:20:18 -0400 Subject: [PATCH] feat(ci): setup ci oci releases --- .github/workflows/main.yml | 16 +- .github/workflows/prepare-release.yml | 55 +++++ .github/workflows/publish-release.yml | 95 ++++++++ generate/src/main.rs | 24 ++- imports.md | 299 ++++++++------------------ test/ensure-parses/src/main.rs | 4 +- wit/imports.wit | 2 +- wit/webgpu.wit | 2 +- 8 files changed, 274 insertions(+), 223 deletions(-) create mode 100644 .github/workflows/prepare-release.yml create mode 100644 .github/workflows/publish-release.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 760faa7..45c2f4b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,9 +6,21 @@ on: branches: [main] jobs: + validate: + name: Validate WIT + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Install wasm-tools + uses: bytecodealliance/actions/wasm-tools/setup@v1 + + - name: Validate WIT + run: wasm-tools component wit wit/ + abi-up-to-date: name: Check ABI files are up-to-date runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: WebAssembly/wit-abi-up-to-date@v25 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: WebAssembly/wit-abi-up-to-date@v25 diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 0000000..43189ec --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,55 @@ +name: Prepare Release + +on: + workflow_dispatch: + +jobs: + prepare: + name: Create Release PR + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Determine next RC version + id: version + run: | + # Find highest RC number from existing tags + HIGHEST_RC=0 + for tag in $(git tag -l 'v0.2.0-rc.*'); do + if [[ "$tag" =~ ^v0\.2\.0-rc\.([0-9]+)$ ]]; then + RC_NUM="${BASH_REMATCH[1]}" + if [ "$RC_NUM" -gt "$HIGHEST_RC" ]; then + HIGHEST_RC="$RC_NUM" + fi + fi + done + + NEXT_RC=$((HIGHEST_RC + 1)) + VERSION="0.2.0-rc.${NEXT_RC}" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Preparing version: $VERSION" + + - name: Bump package version + run: | + # Update package version (matches wasi:webgpu@ANY_VERSION) + find wit -type f -name "*.wit" -exec sed -i \ + "s/\(wasi:webgpu@\)[^;]*/\1${{ steps.version.outputs.version }}/g" {} + + + - name: Create Pull Request + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 + with: + commit-message: "chore: bump version to ${{ steps.version.outputs.version }}" + branch: release/${{ steps.version.outputs.version }} + title: "Release ${{ steps.version.outputs.version }}" + body: | + Bumps package version to `${{ steps.version.outputs.version }}`. + + Once merged, manually run the **Publish Release** workflow to: + - Create git tag `v${{ steps.version.outputs.version }}` + - Create GitHub release + - Publish to `ghcr.io/webassembly/wasi/webgpu:${{ steps.version.outputs.version }}` diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000..1a3d33a --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,95 @@ +name: Publish Release + +on: + workflow_dispatch: + +jobs: + publish: + name: Publish Release + runs-on: ubuntu-latest + permissions: + id-token: write + packages: write + contents: write + attestations: write + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Extract version from package + id: version + run: | + # Extract version from wit/imports.wit + VERSION=$(grep -oP 'package wasi:webgpu@\K[^;]+' wit/imports.wit) + if [[ ! "$VERSION" =~ ^0\.2\.0-rc\.[0-9]+$ ]]; then + echo "Error: Version '$VERSION' is not a valid RC version" + exit 1 + fi + TAG="v${VERSION}" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "Publishing version: $VERSION" + + - name: Check tag doesn't exist + run: | + if git tag -l "${{ steps.version.outputs.tag }}" | grep -q .; then + echo "Error: Tag ${{ steps.version.outputs.tag }} already exists" + exit 1 + fi + + - name: Create git tag + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.version }}" + git push origin "${{ steps.version.outputs.tag }}" + + - name: Create GitHub release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "${{ steps.version.outputs.tag }}" \ + --title "${{ steps.version.outputs.version }}" \ + --prerelease \ + --generate-notes + + - name: Install cargo-binstall + uses: cargo-bins/cargo-binstall@80aaafe04903087c333980fa2686259ddd34b2d9 # v1.16.6 + + - name: Install wkg + run: cargo binstall -y "wkg@0.13.0" + + - name: Login to GitHub Container Registry + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build WIT package + run: wkg wit build -o wasi-webgpu.wasm + + - name: Publish to GitHub Container Registry + id: publish + uses: bytecodealliance/wkg-github-action@10b3b04b9059ba46208cd7daf7d352af14bded0f # v5 + with: + oci-reference-without-tag: ghcr.io/webassembly/wasi/webgpu + file: wasi-webgpu.wasm + description: 'WASI WebGPU API' + source: https://github.com/WebAssembly/wasi-webgpu + homepage: https://github.com/WebAssembly/wasi-webgpu + version: ${{ steps.version.outputs.version }} + licenses: Apache-2.0 WITH LLVM-exception + + - name: Attest build provenance + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3.1.0 + with: + subject-name: ghcr.io/webassembly/wasi/webgpu + subject-digest: ${{ steps.publish.outputs.digest }} + push-to-registry: true + + - name: Upload wasm to release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release upload "${{ steps.version.outputs.tag }}" wasi-webgpu.wasm diff --git a/generate/src/main.rs b/generate/src/main.rs index 44bc1fe..2b952e4 100644 --- a/generate/src/main.rs +++ b/generate/src/main.rs @@ -26,17 +26,7 @@ fn main() { "OffscreenCanvas".into(), ], resource_inheritance: ResourceInheritance::DuplicateMethods, - package_name: webidl2wit::PackageName::new( - "wasi", - "webgpu", - Some(semver::Version { - major: 0, - minor: 0, - patch: 1, - pre: Default::default(), - build: Default::default(), - }), - ), + package_name: webidl2wit::PackageName::new("wasi", "webgpu", Some(package_version())), interface_name: "webgpu".into(), ..Default::default() }, @@ -73,3 +63,15 @@ fn main() { fs::write("../wit/webgpu.wit", output).unwrap(); } + +fn package_version() -> semver::Version { + let version = fs::read_to_string("../wit/imports.wit") + .ok() + .and_then(|wit| { + wit.lines() + .find_map(|line| line.trim().strip_prefix("package wasi:webgpu@")) + .map(|rest| rest.trim_end_matches(';').trim().to_string()) + }) + .unwrap(); + semver::Version::parse(&version).unwrap() +} diff --git a/imports.md b/imports.md index e62ed4b..e578b3c 100644 --- a/imports.md +++ b/imports.md @@ -2,11 +2,11 @@
wasi:webgpu/webgpu@0.0.1wasi:webgpu/webgpu@0.2.0-rc.2variant create-query-set-error-kindpendingmappedresource gpu-buffer-usagetype gpu-buffer-usage-flagsu32
-#### `enum gpu-canvas-alpha-mode` +
flags gpu-buffer-usagemap-read: map-write: copy-src: copy-dst: index: vertex: uniform: storage: indirect: query-resolve: enum gpu-canvas-alpha-moderesource gpu-color-writetype gpu-color-write-flagsu32
-#### `resource gpu-command-buffer` +
flags gpu-color-writeresource gpu-command-bufferrecord gpu-command-buffer-descriptortype gpu-flags-constantu32
enum gpu-front-faceresource gpu-map-modetype gpu-map-mode-flagsu32
-#### `enum gpu-mipmap-filter-mode` +
flags gpu-map-modeenum gpu-mipmap-filter-modenearestcompilation-hints: option<list<gpu-shader-module-compilation-hint>>label: option<string>resource gpu-shader-stagetype gpu-shader-stage-flagsu32
-#### `type gpu-signed-offset32` -`s32` +
flags gpu-shader-stagetype gpu-signed-offset32s32
#### `type gpu-size32` `u32` @@ -495,7 +512,7 @@
size: gpu-size64usage: gpu-buffer-usage-flagsusage: gpu-buffer-usagemapped-at-creation: option<bool>label: option<string>format: gpu-texture-formatblend: option<gpu-blend-state>write-mask: option<gpu-color-write-flags>write-mask: option<gpu-color-write>record gpu-depth-stencil-statesintuintresource gpu-texture-usagetype gpu-texture-usage-flagsu32
-#### `resource gpu-texture-view` +
flags gpu-texture-usagecopy-src: copy-dst: texture-binding: storage-binding: render-attachment: transient-attachment: resource gpu-texture-viewvariant gpu-binding-resourcebinding: gpu-index32visibility: gpu-shader-stage-flagsvisibility: gpu-shader-stagebuffer: option<gpu-buffer-binding-layout>sampler: option<gpu-sampler-binding-layout>texture: option<gpu-texture-binding-layout>sample-count: option<gpu-size32>dimension: option<gpu-texture-dimension>format: gpu-texture-formatusage: gpu-texture-usage-flagsusage: gpu-texture-usageview-formats: option<list<gpu-texture-format>>texture-binding-view-dimension: option<gpu-texture-view-dimension>label: option<string>format: option<gpu-texture-format>dimension: option<gpu-texture-view-dimension>usage: option<gpu-texture-usage-flags>usage: option<gpu-texture-usage>aspect: option<gpu-texture-aspect>base-mip-level: option<gpu-integer-coordinate>mip-level-count: option<gpu-integer-coordinate>device: borrow<gpu-device>format: gpu-texture-formatusage: option<gpu-texture-usage-flags>usage: option<gpu-texture-usage>view-formats: option<list<gpu-texture-format>>color-space: option<predefined-color-space>tone-mapping: option<gpu-canvas-tone-mapping>device: own<gpu-device>format: gpu-texture-formatusage: option<gpu-texture-usage-flags>usage: option<gpu-texture-usage>view-formats: option<list<gpu-texture-format>>color-space: option<predefined-color-space>tone-mapping: option<gpu-canvas-tone-mapping>[method]gpu.request-adapter: func[async method]gpu.request-adapter: funcself: borrow<gpu>options: option<gpu-request-adapter-options>self: borrow<gpu>options: option<gpu-request-adapter-options>gpu-adapter>>>gpu-adapter>>[method]gpu.get-preferred-canvas-format: funcgpu-adapter-info>[method]gpu-adapter.request-device: func[async method]gpu-adapter.request-device: funcself: borrow<gpu-adapter>descriptor: option<gpu-device-descriptor>self: borrow<gpu-adapter>descriptor: option<gpu-device-descriptor>gpu-device>, request-device-error>>gpu-device>, request-device-error>[method]gpu-adapter-info.vendor: func[method]gpu-buffer.map-state: func[method]gpu-buffer.map-async: func[async method]gpu-buffer.map-async: funcself: borrow<gpu-buffer>mode: gpu-map-mode-flagsoffset: option<gpu-size64>size: option<gpu-size64>self: borrow<gpu-buffer>mode: gpu-map-modeoffset: option<gpu-size64>size: option<gpu-size64>map-async-error>map-async-error>[method]gpu-buffer.get-mapped-range-get-with-copy: funcget-mapped-range-error>[static]gpu-buffer-usage.MAP-READ: func[static]gpu-buffer-usage.MAP-WRITE: func[static]gpu-buffer-usage.COPY-SRC: func[static]gpu-buffer-usage.COPY-DST: func[static]gpu-buffer-usage.INDEX: func[static]gpu-buffer-usage.VERTEX: func[static]gpu-buffer-usage.UNIFORM: func[static]gpu-buffer-usage.STORAGE: func[static]gpu-buffer-usage.INDIRECT: func[static]gpu-buffer-usage.QUERY-RESOLVE: func[method]gpu-canvas-context.configure: funcgpu-texture>[static]gpu-color-write.RED: func[static]gpu-color-write.GREEN: func[static]gpu-color-write.BLUE: func[static]gpu-color-write.ALPHA: func[static]gpu-color-write.ALL: func[method]gpu-command-buffer.label: funcgpu-render-pipeline>[method]gpu-device.create-compute-pipeline-async: func[async method]gpu-device.create-compute-pipeline-async: funcself: borrow<gpu-device>descriptor: gpu-compute-pipeline-descriptorself: borrow<gpu-device>descriptor: gpu-compute-pipeline-descriptorgpu-compute-pipeline>, create-pipeline-error>>gpu-compute-pipeline>, create-pipeline-error>[method]gpu-device.create-render-pipeline-async: func[async method]gpu-device.create-render-pipeline-async: funcself: borrow<gpu-device>descriptor: gpu-render-pipeline-descriptorself: borrow<gpu-device>descriptor: gpu-render-pipeline-descriptorgpu-render-pipeline>, create-pipeline-error>>gpu-render-pipeline>, create-pipeline-error>[method]gpu-device.create-command-encoder: funcself: borrow<gpu-device>filter: gpu-error-filter[method]gpu-device.pop-error-scope: func[async method]gpu-device.pop-error-scope: funcself: borrow<gpu-device>self: borrow<gpu-device>gpu-error>>, pop-error-scope-error>>gpu-error>>, pop-error-scope-error>[method]gpu-device.on-uncaptured-error: func[static]gpu-map-mode.READ: func[static]gpu-map-mode.WRITE: func[method]gpu-pipeline-layout.label: funcself: borrow<gpu-queue>command-buffers: list<borrow<gpu-command-buffer>>[method]gpu-queue.on-submitted-work-done: func[async method]gpu-queue.on-submitted-work-done: funcself: borrow<gpu-queue>self: borrow<gpu-queue>[method]gpu-queue.write-buffer-with-copy: funcself: borrow<gpu-sampler>label: string[method]gpu-shader-module.get-compilation-info: func[async method]gpu-shader-module.get-compilation-info: funcself: borrow<gpu-shader-module>self: borrow<gpu-shader-module>gpu-compilation-info>>gpu-compilation-info>[method]gpu-shader-module.label: funcself: borrow<gpu-shader-module>label: string[static]gpu-shader-stage.VERTEX: func[static]gpu-shader-stage.FRAGMENT: func[static]gpu-shader-stage.COMPUTE: func[method]gpu-supported-features.has: func[method]gpu-texture.texture-binding-view-dimension: funcself: borrow<gpu-texture>label: string[static]gpu-texture-usage.COPY-SRC: func[static]gpu-texture-usage.COPY-DST: func[static]gpu-texture-usage.TEXTURE-BINDING: func[static]gpu-texture-usage.STORAGE-BINDING: func[static]gpu-texture-usage.RENDER-ATTACHMENT: func[static]gpu-texture-usage.TRANSIENT-ATTACHMENT: func[method]gpu-texture-view.label: func