Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ jobs:
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false

- name: Lint Codebase
id: super-linter
uses: super-linter/super-linter/slim@12150456a73e248bdc94d0794898f94e23127c88 # v7.4.0
uses: super-linter/super-linter/slim@61abc07d755095a68f4987d1c2c3d1d64408f1f9 # v8.5.0
env:
DEFAULT_BRANCH: main
FILTER_REGEX_EXCLUDE: dist/**/*
Expand Down
29 changes: 10 additions & 19 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
fetch-depth: 0
Expand All @@ -30,24 +30,24 @@ jobs:
- name: Setup wash CLI
uses: ./setup-wash-action
with:
wash-version: wash-v1.0.0-beta.8
wash-version: wash-v2.0.0-rc.7

# create a test working directory
# create a test working directory with a Rust project
- name: Create test directory
run: |
mkdir test-dir
cd test-dir

- name: Create test Rust project
working-directory: test-dir
run: |
cargo init --lib --name test-component
printf '\n[lib]\ncrate-type = ["cdylib"]\n' >> Cargo.toml

- name: Setup cargo-auditable
uses: ./setup-wash-cargo-auditable
with:
working-directory: test-dir

- name: Create test Rust project
working-directory: test-dir
run: |
cargo init --name test-component

- name: Test wash-build action
id: build
uses: ./wash-build
Expand All @@ -66,17 +66,8 @@ jobs:
env:
STEPS_BUILD_OUTPUTS_COMPONENT_PATH: ${{ steps.build.outputs.component_path }}

- name: Verify wash config updated with cargo-auditable
shell: bash
working-directory: test-dir
run: |
jq . .wash/config.json
if ! jq -e '.build.rust.custom_command | arrays | contains(["auditable"])' .wash/config.json; then
echo "Error: .wash/config.json does not contain 'auditable' in custom_command"
exit 1
fi

- name: Test wash-oci-publish action (multiple tags)
if: github.event_name == 'push'
uses: ./wash-oci-publish
with:
component_path: ${{ steps.build.outputs.component_path }}
Expand Down
2 changes: 1 addition & 1 deletion setup-wash-action
40 changes: 15 additions & 25 deletions setup-wash-cargo-auditable/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ description: |
wash CLI must be installed and available in PATH. We recommend using the
wasmcloud/setup-wash-action prior to this action to install wash.

A Cargo project (Cargo.toml) must already exist in the working directory
before calling this action, as it reads the package name to determine
the component output path.

inputs:
working-directory:
description: "Directory containing the WebAssembly project"
Expand All @@ -15,36 +19,22 @@ inputs:
runs:
using: "composite"
steps:
- uses: taiki-e/install-action@4575ae687efd0e2c78240087f26013fb2484987f # v2.62.6
- uses: taiki-e/install-action@1cf3de8de323df92fe08c793e53eaef58799aec4 # v2.68.3
with:
tool: cargo-auditable,cargo-audit

- name: Set auditable as custom build command in .wash/config.json
- name: Set auditable as custom build command in .wash/config.yaml
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
set -euo pipefail

# Derive the wasm artifact path from the Cargo package name
PACKAGE_NAME=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].name' | tr '-' '_')
COMPONENT_PATH="target/wasm32-wasip2/release/${PACKAGE_NAME}.wasm"

mkdir -p .wash

# Create or update config with jq, merging with existing config
if [ -f .wash/config.json ]; then
# Merge with existing config, ensuring nested objects exist
jq '(.build // {}) as $build |
(.build.rust // {}) as $rust |
.build = ($build | .rust = ($rust |
.custom_command = ["cargo", "auditable", "build", "--release", "--target", "wasm32-wasip2", "--message-format", "json"] |
.target = "wasm32-wasip2" |
.release = true
))' .wash/config.json > .wash/config.json.tmp
mv .wash/config.json.tmp .wash/config.json
else
# Create new config
jq -n '{
"build": {
"rust": {
"target": "wasm32-wasip2",
"release": true,
"custom_command": ["cargo", "auditable", "build", "--release", "--target", "wasm32-wasip2", "--message-format", "json"]
}
}
}' > .wash/config.json
fi
# Write .wash/config.yaml in wash v2.0 format
printf 'build:\n command: cargo auditable build --release --target wasm32-wasip2\n component_path: "%s"\n' \
"$COMPONENT_PATH" > .wash/config.yaml
6 changes: 4 additions & 2 deletions wash-build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ runs:
- name: Verify Wasm binary exists
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
COMPONENT_PATH: ${{ steps.build.outputs.component_path }}
run: |
if [ ! -f "${{ steps.build.outputs.component_path }}" ]; then
echo "Error: ${{ steps.build.outputs.component_path }} not found!" >&2
if [ ! -f "$COMPONENT_PATH" ]; then
echo "Error: $COMPONENT_PATH not found!" >&2
exit 1
fi
71 changes: 43 additions & 28 deletions wash-oci-publish/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,40 +37,45 @@ runs:
using: "composite"
steps:
- name: Login to container registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ${{ inputs.registry }}
username: ${{ github.actor }}
password: ${{ inputs.token }}

- name: Set IMAGE_NAME env as lower-case repository name
- name: Set image name and SBOM filename
id: image-info
shell: bash
run: |
echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
# Create a sanitized filename for SBOM (replace / with -)
echo "SBOM_FILENAME=$(echo "${GITHUB_REPOSITORY,,}" | tr '/' '-').spdx.json" >> $GITHUB_ENV
echo "image_name=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT
echo "sbom_filename=$(echo "${GITHUB_REPOSITORY,,}" | tr '/' '-').spdx.json" >> $GITHUB_OUTPUT

- name: Push Wasm component to container registry
id: push
shell: bash
env:
IMAGE_TAGS: ${{ inputs.image_tags }}
REGISTRY: ${{ inputs.registry }}
IMAGE_NAME: ${{ steps.image-info.outputs.image_name }}
COMPONENT_PATH: ${{ inputs.component_path }}
run: |
# Split comma-separated tags and push each one
IFS=',' read -ra TAGS <<< "${{ inputs.image_tags }}"
IFS=',' read -ra TAGS <<< "$IMAGE_TAGS"

digests=()
for tag in "${TAGS[@]}"; do
# Trim whitespace
tag=$(echo "$tag" | xargs)
echo "Pushing ${{ inputs.registry }}/${{ env.IMAGE_NAME }}:$tag"
push_output=$(wash oci push --output json "${{ inputs.registry }}/${{ env.IMAGE_NAME }}:$tag" "${{ inputs.component_path }}")
echo "Pushing $REGISTRY/$IMAGE_NAME:$tag"

push_output=$(wash oci push --output json "$REGISTRY/$IMAGE_NAME:$tag" "$COMPONENT_PATH")
digest=$(echo "$push_output" | jq -r .data.digest)

if [ -z "$digest" ] || [ "$digest" = "null" ]; then
echo "Failed to determine pushed component digest for tag $tag: $push_output" >&2
exit 1
fi

echo "Component pushed with digest: $digest for tag: $tag"
digests+=("$digest")
done
Expand All @@ -81,20 +86,23 @@ runs:

- name: Generate artifact attestation
if: ${{ inputs.attestation == 'true' }}
uses: actions/attest-build-provenance@v3
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
with:
subject-name: ${{ inputs.registry }}/${{ env.IMAGE_NAME }}
subject-name: ${{ inputs.registry }}/${{ steps.image-info.outputs.image_name }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

- uses: taiki-e/install-action@4575ae687efd0e2c78240087f26013fb2484987f # v2.62.6
- uses: taiki-e/install-action@1cf3de8de323df92fe08c793e53eaef58799aec4 # v2.68.3
if: ${{ inputs.attestation == 'true' }}
with:
tool: auditable2cdx

- name: Install CycloneDX CLI
id: install-cyclonedx
if: ${{ inputs.attestation == 'true' }}
shell: bash
env:
CYCLONEDX_VERSION: ${{ inputs.cyclonedx-version }}
run: |
# Detect OS and architecture for cyclonedx-cli binary
case "$(uname -s)" in
Expand Down Expand Up @@ -124,41 +132,48 @@ runs:
*) echo "Unsupported OS: $(uname -s)"; exit 1 ;;
esac

# Pin to known good version 0.29.1
# Pin to known good version
echo "Downloading cyclonedx-cli binary: $BINARY_NAME"
curl -LO "https://github.com/CycloneDX/cyclonedx-cli/releases/download/v${{ inputs.cyclonedx-version }}/$BINARY_NAME"
chmod +x "$BINARY_NAME"
curl -LO "https://github.com/CycloneDX/cyclonedx-cli/releases/download/v${CYCLONEDX_VERSION}/${BINARY_NAME}"

# Create a directory for the binary and add to PATH
mkdir -p "$HOME/.local/bin"
mv "$BINARY_NAME" "$HOME/.local/bin/cyclonedx"
echo "$HOME/.local/bin" >> $GITHUB_PATH
chmod +x "$HOME/.local/bin/cyclonedx"

echo "cyclonedx_bin=$HOME/.local/bin/cyclonedx" >> $GITHUB_OUTPUT

- name: Extract SBOM from component
id: extract-sbom
if: ${{ inputs.attestation == 'true' }}
shell: bash
env:
COMPONENT_PATH: ${{ inputs.component_path }}
run: |
# Create absolute path for CycloneDX SBOM file
CYCLONEDX_PATH="$(pwd)/$(echo "${GITHUB_REPOSITORY,,}" | tr '/' '-').cyclonedx.json"

# Extract CycloneDX SBOM
auditable2cdx ${{ inputs.component_path }} > "$CYCLONEDX_PATH"
auditable2cdx "$COMPONENT_PATH" > "$CYCLONEDX_PATH"
echo "CycloneDX SBOM created at: $CYCLONEDX_PATH"

# Store path for next step
echo "CYCLONEDX_ABS_PATH=$CYCLONEDX_PATH" >> $GITHUB_ENV
echo "cyclonedx_path=$CYCLONEDX_PATH" >> $GITHUB_OUTPUT

- name: Convert SBOM to SPDX
id: convert-sbom
if: ${{ inputs.attestation == 'true' }}
shell: bash
env:
SBOM_FILENAME: ${{ steps.image-info.outputs.sbom_filename }}
CYCLONEDX_ABS_PATH: ${{ steps.extract-sbom.outputs.cyclonedx_path }}
CYCLONEDX_BIN: ${{ steps.install-cyclonedx.outputs.cyclonedx_bin }}
run: |
# Create absolute path for SPDX SBOM file
SBOM_PATH="$(pwd)/${{ env.SBOM_FILENAME }}"
SBOM_PATH="$(pwd)/$SBOM_FILENAME"

# Convert CycloneDX to SPDX format for GitHub attestation
cyclonedx convert --input-file "${{ env.CYCLONEDX_ABS_PATH }}" --output-file "$SBOM_PATH" --output-format spdxjson
"$CYCLONEDX_BIN" convert --input-file "$CYCLONEDX_ABS_PATH" --output-file "$SBOM_PATH" --output-format spdxjson

echo "SBOM_ABS_PATH=$SBOM_PATH" >> $GITHUB_ENV
echo "sbom_abs_path=$SBOM_PATH" >> $GITHUB_OUTPUT

# Debug: Print SBOM file info and contents
echo "SPDX SBOM file created at: $SBOM_PATH"
Expand All @@ -168,9 +183,9 @@ runs:
echo "SPDX SBOM file format detection:"
file "$SBOM_PATH"

- uses: actions/attest-sbom@v3
- uses: actions/attest-sbom@4651f806c01d8637787e274ac3bdf724ef169f34 # v3.0.0
if: ${{ inputs.attestation == 'true' }}
with:
subject-name: ${{ inputs.registry }}/${{ env.IMAGE_NAME }}
subject-name: ${{ inputs.registry }}/${{ steps.image-info.outputs.image_name }}
subject-digest: ${{ steps.push.outputs.digest }}
sbom-path: ${{ env.SBOM_ABS_PATH }}
sbom-path: ${{ steps.convert-sbom.outputs.sbom_abs_path }}