Skip to content

Commit cd294d4

Browse files
authored
add publish actions for wit and component (#70)
Signed-off-by: markfisher <mark@modulewise.com>
1 parent 779aeed commit cd294d4

5 files changed

Lines changed: 266 additions & 0 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Publish Component
2+
3+
on:
4+
push:
5+
tags:
6+
- 'component-*-v[0-9]+.[0-9]+.[0-9]+-?**'
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
id-token: write
13+
packages: write
14+
contents: read
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v6
18+
19+
- name: Parse tag
20+
id: parse
21+
run: |
22+
tag="${GITHUB_REF#refs/tags/}"
23+
rest="${tag#component-}"
24+
name="${rest%-v*}"
25+
version="${rest##*-v}"
26+
echo "name=$name" >> "$GITHUB_OUTPUT"
27+
echo "version=$version" >> "$GITHUB_OUTPUT"
28+
29+
- name: Read component metadata
30+
id: meta
31+
working-directory: components
32+
run: |
33+
name="${{ steps.parse.outputs.name }}"
34+
version="${{ steps.parse.outputs.version }}"
35+
json=$(cargo metadata --no-deps --format-version 1 \
36+
| jq --arg n "$name" '.packages[] | select(.name == $n)')
37+
if [ -z "$json" ]; then
38+
echo "Component '$name' not found in cargo workspace" >&2
39+
exit 1
40+
fi
41+
manifest_version=$(echo "$json" | jq -r '.version')
42+
if [ "$manifest_version" != "$version" ]; then
43+
echo "Tag version ($version) does not match Cargo.toml version ($manifest_version) for $name" >&2
44+
exit 1
45+
fi
46+
description=$(echo "$json" | jq -r '.description // ""')
47+
homepage=$(echo "$json" | jq -r '.homepage // ""')
48+
repository=$(echo "$json" | jq -r '.repository // ""')
49+
license=$(echo "$json" | jq -r '.license // ""')
50+
for field in description homepage repository license; do
51+
if [ -z "${!field}" ]; then
52+
echo "Component '$name' is missing '$field' in Cargo.toml" >&2
53+
exit 1
54+
fi
55+
done
56+
echo "description=$description" >> "$GITHUB_OUTPUT"
57+
echo "homepage=$homepage" >> "$GITHUB_OUTPUT"
58+
echo "source=$repository" >> "$GITHUB_OUTPUT"
59+
echo "license=$license" >> "$GITHUB_OUTPUT"
60+
61+
- name: Add wasm32-unknown-unknown target
62+
run: rustup target add wasm32-unknown-unknown
63+
64+
- name: Install cargo-binstall
65+
uses: cargo-bins/cargo-binstall@v1
66+
67+
- name: Install build tools
68+
run: |
69+
cargo binstall cargo-auditable --no-confirm
70+
cargo binstall auditable2cdx --no-confirm
71+
cargo binstall wasm-tools --no-confirm
72+
73+
- name: Build core wasm with cargo-auditable
74+
working-directory: components
75+
run: cargo auditable build -p ${{ steps.parse.outputs.name }} --target wasm32-unknown-unknown --release
76+
77+
- name: Wrap as wasm component
78+
working-directory: components
79+
run: |
80+
name="${{ steps.parse.outputs.name }}"
81+
cargo_name="${name//-/_}"
82+
wasm-tools component new \
83+
"target/wasm32-unknown-unknown/release/${cargo_name}.wasm" \
84+
-o "/tmp/${name}.wasm"
85+
86+
- name: Extract SBOM
87+
run: |
88+
name="${{ steps.parse.outputs.name }}"
89+
auditable2cdx "/tmp/${name}.wasm" > "/tmp/${name}.cdx.json"
90+
91+
- name: Login to GitHub Container Registry
92+
uses: docker/login-action@v4
93+
with:
94+
registry: ghcr.io
95+
username: ${{ github.actor }}
96+
password: ${{ secrets.GITHUB_TOKEN }}
97+
98+
- name: Publish component to GitHub Container Registry
99+
id: publish
100+
uses: bytecodealliance/wkg-github-action@v5
101+
with:
102+
file: /tmp/${{ steps.parse.outputs.name }}.wasm
103+
oci-reference-without-tag: ghcr.io/modulewise/component/${{ steps.parse.outputs.name }}
104+
version: ${{ steps.parse.outputs.version }}
105+
description: ${{ steps.meta.outputs.description }}
106+
source: ${{ steps.meta.outputs.source }}
107+
homepage: ${{ steps.meta.outputs.homepage }}
108+
licenses: ${{ steps.meta.outputs.license }}
109+
110+
- name: Install cosign
111+
uses: sigstore/cosign-installer@v4
112+
113+
- name: Sign component
114+
run: |
115+
cosign sign --yes \
116+
ghcr.io/modulewise/component/${{ steps.parse.outputs.name }}@${{ steps.publish.outputs.digest }}
117+
118+
- name: Attest SBOM
119+
run: |
120+
name="${{ steps.parse.outputs.name }}"
121+
cosign attest --yes \
122+
--type cyclonedx \
123+
--predicate "/tmp/${name}.cdx.json" \
124+
ghcr.io/modulewise/component/${name}@${{ steps.publish.outputs.digest }}

.github/workflows/publish-wit.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: Publish WIT package
2+
3+
on:
4+
push:
5+
tags:
6+
- 'wit-*-v[0-9]+.[0-9]+.[0-9]+-?**'
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
id-token: write
13+
packages: write
14+
contents: read
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v6
18+
19+
- name: Parse tag
20+
id: parse
21+
run: |
22+
tag="${GITHUB_REF#refs/tags/}"
23+
rest="${tag#wit-}"
24+
name="${rest%-v*}"
25+
version="${rest##*-v}"
26+
wit_dir="components/${name}/wit"
27+
wkg_toml="components/${name}/wkg.toml"
28+
package_wit="${wit_dir}/package.wit"
29+
for f in "$wit_dir" "$wkg_toml" "$package_wit"; do
30+
if [ ! -e "$f" ]; then
31+
echo "Required path not found: $f" >&2
32+
exit 1
33+
fi
34+
done
35+
# Namespace from `package <ns>:<name>@<version>;` declaration
36+
ns=$(sed -nE 's/^package ([a-z][a-z0-9-]*):.*$/\1/p' "$package_wit" | head -1)
37+
if [ -z "$ns" ]; then
38+
echo "Failed to extract namespace from $package_wit" >&2
39+
exit 1
40+
fi
41+
echo "name=$name" >> "$GITHUB_OUTPUT"
42+
echo "version=$version" >> "$GITHUB_OUTPUT"
43+
echo "namespace=$ns" >> "$GITHUB_OUTPUT"
44+
echo "wkg_toml=$wkg_toml" >> "$GITHUB_OUTPUT"
45+
46+
- name: Read package metadata
47+
id: meta
48+
run: |
49+
file="${{ steps.parse.outputs.wkg_toml }}"
50+
description=$(yq -p toml -o yaml -r '.metadata.description // ""' "$file")
51+
homepage=$(yq -p toml -o yaml -r '.metadata.homepage // ""' "$file")
52+
repository=$(yq -p toml -o yaml -r '.metadata.repository // ""' "$file")
53+
license=$(yq -p toml -o yaml -r '.metadata.license // ""' "$file")
54+
for field in description homepage repository license; do
55+
if [ -z "${!field}" ]; then
56+
echo "wkg.toml is missing '$field' under [metadata]" >&2
57+
exit 1
58+
fi
59+
done
60+
echo "description=$description" >> "$GITHUB_OUTPUT"
61+
echo "homepage=$homepage" >> "$GITHUB_OUTPUT"
62+
echo "source=$repository" >> "$GITHUB_OUTPUT"
63+
echo "license=$license" >> "$GITHUB_OUTPUT"
64+
65+
- name: Install cargo-binstall
66+
uses: cargo-bins/cargo-binstall@v1
67+
68+
- name: Install wkg
69+
run: cargo binstall wkg --no-confirm
70+
71+
- name: Build WIT package
72+
working-directory: components/${{ steps.parse.outputs.name }}
73+
run: wkg wit build --output /tmp/wit-package.wasm
74+
75+
- name: Login to GitHub Container Registry
76+
uses: docker/login-action@v4
77+
with:
78+
registry: ghcr.io
79+
username: ${{ github.actor }}
80+
password: ${{ secrets.GITHUB_TOKEN }}
81+
82+
- name: Publish WIT to GitHub Container Registry
83+
id: publish
84+
uses: bytecodealliance/wkg-github-action@v5
85+
with:
86+
file: /tmp/wit-package.wasm
87+
oci-reference-without-tag: ghcr.io/modulewise/wit/${{ steps.parse.outputs.namespace }}/${{ steps.parse.outputs.name }}
88+
version: ${{ steps.parse.outputs.version }}
89+
description: ${{ steps.meta.outputs.description }}
90+
source: ${{ steps.meta.outputs.source }}
91+
homepage: ${{ steps.meta.outputs.homepage }}
92+
licenses: ${{ steps.meta.outputs.license }}
93+
94+
- name: Install cosign
95+
uses: sigstore/cosign-installer@v4
96+
97+
- name: Sign WIT package
98+
run: |
99+
cosign sign --yes \
100+
ghcr.io/modulewise/wit/${{ steps.parse.outputs.namespace }}/${{ steps.parse.outputs.name }}@${{ steps.publish.outputs.digest }}

components/http/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# composable:http
2+
3+
HTTP client support, including a WIT definition and a Wasm Component.
4+
5+
(for hosting Wasm Components behind an HTTP server, see [`crates/http-server`](../../crates/http-server))
6+
7+
## The `composable:http/client` Interface
8+
9+
Request functions (each returns `result<http-response, string>`):
10+
11+
- `request(method, url, headers, body, options)`
12+
- `get(url, headers, options)`
13+
- `post(url, headers, body, options)`
14+
- `put(url, headers, body, options)`
15+
- `delete(url, headers, options)`
16+
- `patch(url, headers, body, options)`
17+
- `head(url, headers, options)`
18+
- `options(url, headers, options)`
19+
20+
The `options` arg includes per-request timeouts and response-size limits. If not provided, default timeouts will be used, and there will be no cap on the response size.
21+
22+
See [`wit/package.wit`](wit/package.wit) for the full type definitions.
23+
24+
## The `http-client` World
25+
26+
- exports `composable:http/client`
27+
- imports `wasi:http/outgoing-handler`
28+
29+
That import can be satisfied by the `wasi:http` Capability which is available in the core runtime.
30+
31+
## The `http-client` Component
32+
33+
Implementation of the `http-client` world, with source code in the [client](client/) sub-directory.

components/http/client/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
name = "http-client"
33
version = "0.1.0"
44
edition = "2024"
5+
description = "HTTP client component implementing the composable:http/client interface"
6+
homepage = "https://github.com/modulewise/composable-runtime/tree/main/components/http"
7+
license = "Apache-2.0"
8+
repository = "https://github.com/modulewise/composable-runtime"
59

610
[lib]
711
crate-type = ["cdylib"]

components/http/wkg.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[metadata]
2+
description = "WIT package defining the composable:http interfaces"
3+
license = "Apache-2.0"
4+
homepage = "https://github.com/modulewise/composable-runtime/tree/main/components/http"
5+
repository = "https://github.com/modulewise/composable-runtime"

0 commit comments

Comments
 (0)