diff --git a/.github/workflows/release-wasi-demo-app.yml b/.github/workflows/release-wasi-demo-app.yml new file mode 100644 index 000000000..04487e6d3 --- /dev/null +++ b/.github/workflows/release-wasi-demo-app.yml @@ -0,0 +1,139 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-action.json + +name: Release wasi-demo-app +run-name: wasi-demo-app@${{ inputs.version }} + +on: + workflow_dispatch: + inputs: + version: + description: "The version of the image to release. (e.g., 1.2.3)" + type: string + required: true + dry_run: + description: "Run the release without actually releasing bits" + type: boolean + default: true + +env: + CARGO_TERM_COLOR: always + +jobs: + release-wasi-demo: + permissions: + packages: write + runs-on: ubuntu-latest + outputs: + wasi_demo_app_digest: ${{ steps.get_digests.outputs.wasi_demo_app_digest }} + wasi_demo_oci_digest: ${{ steps.get_digests.outputs.wasi_demo_oci_digest }} + wasi_demo_oci_artifact_digest: ${{ steps.get_digests.outputs.wasi_demo_oci_artifact_digest }} + steps: + - uses: actions/checkout@v4 + - name: Setup build env + run: ./scripts/setup-linux.sh + - name: Install Rust and wasm32-wasi target + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: wasm32-wasi + override: true + - name: Build oci tarballs + run: | + make load + make load/oci + sudo ctr image ls + - name: Tagging images + run: | + sudo ctr image tag ghcr.io/containerd/runwasi/wasi-demo-app:latest ghcr.io/${{ github.repository }}/wasi-demo-app:${{ inputs.version }} + sudo ctr image tag ghcr.io/containerd/runwasi/wasi-demo-oci:latest ghcr.io/${{ github.repository }}/wasi-demo-app:latest + + sudo ctr image tag ghcr.io/containerd/runwasi/wasi-demo-oci:latest ghcr.io/${{ github.repository }}/wasi-demo-oci:${{ inputs.version }} + sudo ctr image tag ghcr.io/containerd/runwasi/wasi-demo-oci:latest ghcr.io/${{ github.repository }}/wasi-demo-oci:latest + + sudo ctr image tag ghcr.io/containerd/runwasi/wasi-demo-oci-artifact:latest ghcr.io/${{ github.repository }}/wasi-demo-oci-artifact:${{ inputs.version }} + sudo ctr image tag ghcr.io/containerd/runwasi/wasi-demo-oci-artifact:latest ghcr.io/${{ github.repository }}/wasi-demo-oci-artifact:latest + + - name: Push images to ghcr.io + if: ${{ inputs.dry_run == false }} + run: | + sudo ctr images push --user ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} ghcr.io/${{ github.repository }}/wasi-demo-app:${{ inputs.version }} + sudo ctr images push --user ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} ghcr.io/${{ github.repository }}/wasi-demo-app:latest + + sudo ctr images push --user ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} ghcr.io/${{ github.repository }}/wasi-demo-oci:${{ inputs.version }} + sudo ctr images push --user ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} ghcr.io/${{ github.repository }}/wasi-demo-oci:latest + + sudo ctr images push --user ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} ghcr.io/${{ github.repository }}/wasi-demo-oci-artifact:${{ inputs.version }} + sudo ctr images push --user ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} ghcr.io/${{ github.repository }}/wasi-demo-oci-artifact:latest + + - name: Display image digests + id: get_digests + run: | + digest_wasi_demo_app=$(sudo ctr images ls | grep "ghcr.io/${{ github.repository }}/wasi-demo-app:${{ inputs.version }}" | awk '{print $3}') + echo "Digest for wasi-demo-app: $digest_wasi_demo_app" + echo "wasi_demo_app_digest=$digest_wasi_demo_app" >> $GITHUB_OUTPUT + + digest_wasi_demo_oci=$(sudo ctr images ls | grep "ghcr.io/${{ github.repository }}/wasi-demo-oci:${{ inputs.version }}" | awk '{print $3}') + echo "Digest for wasi-demo-oci: $digest_wasi_demo_oci" + echo "wasi_demo_oci_digest=$digest_wasi_demo_oci" >> $GITHUB_OUTPUT + + digest_wasi_demo_oci_artifact=$(sudo ctr images ls | grep "ghcr.io/${{ github.repository }}/wasi-demo-oci-artifact:${{ inputs.version }}" | awk '{print $3}') + echo "Digest for wasi-demo-oci-artifact: $digest_wasi_demo_oci_artifact" + echo "wasi_demo_oci_artifact_digest=$digest_wasi_demo_oci_artifact" >> $GITHUB_OUTPUT + + sign-wasi-demo-app: + if: ${{ inputs.dry_run == false }} + needs: + - release-wasi-demo + uses: ./.github/workflows/sign.yml + with: + image-name: "wasi-demo-app" + image-digest: ${{ needs.release-wasi-demo.outputs.wasi_demo_app_digest }} + + sbom-wasi-demo-app: + if: ${{ inputs.dry_run == false }} + needs: + - release-wasi-demo + uses: ./.github/workflows/sbom.yml + with: + image-name: "wasi-demo-app" + image-digest: ${{ needs.release-wasi-demo.outputs.wasi_demo_app_digest }} + + + sign-wasi-demo-oci: + if: ${{ inputs.dry_run == false }} + needs: + - release-wasi-demo + uses: ./.github/workflows/sign.yml + with: + image-name: "wasi-demo-oci" + image-digest: ${{ needs.release-wasi-demo.outputs.wasi_demo_oci_digest }} + + + sbom-wasi-demo-oci: + if: ${{ inputs.dry_run == false }} + needs: + - release-wasi-demo + uses: ./.github/workflows/sbom.yml + with: + image-name: "wasi-demo-oci" + image-digest: ${{ needs.release-wasi-demo.outputs.wasi_demo_oci_digest }} + + + sign-wasi-demo-oci-artifact: + if: ${{ inputs.dry_run == false }} + needs: + - release-wasi-demo + uses: ./.github/workflows/sign.yml + with: + image-name: "wasi-demo-oci-artifact" + image-digest: ${{ needs.release-wasi-demo.outputs.wasi_demo_oci_artifact_digest }} + + + sbom-wasi-demo-oci-artifact: + if: ${{ inputs.dry_run == false }} + needs: + - release-wasi-demo + uses: ./.github/workflows/sbom.yml + with: + image-name: "wasi-demo-oci-artifact" + image-digest: ${{ needs.release-wasi-demo.outputs.wasi_demo_oci_artifact_digest }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 589eeb5a6..0c16b426c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -182,3 +182,4 @@ jobs: env: GH_TOKEN: ${{ github.token }} RELEASE_NAME: ${{ matrix.crate }}/v${{ matrix.version }} + diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml new file mode 100644 index 000000000..da72d3e1f --- /dev/null +++ b/.github/workflows/sbom.yml @@ -0,0 +1,83 @@ +name: Generate SBOMs + +on: + workflow_call: + inputs: + image-name: + type: string + required: true + image-digest: + type: string + required: true + +jobs: + sbom: + name: Generate SBOM, sign and attach them to OCI image + permissions: + packages: write + id-token: write + + runs-on: ubuntu-latest + steps: + - name: Install cosign + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 + + - name: Install syft + uses: anchore/sbom-action/download-syft@e8d2a6937ecead383dfe75190d104edd1f9c5751 # v0.16.0 + + - name: Install crane + uses: IAreKyleW00t/crane-installer@66858ae469ebd32ce731051d9c2bae9b811537e9 # v1.3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Find platform digest + shell: bash + run: | + set -e + IMG_REPOSITORY_NAME=$( echo ${{ github.repository }} | awk '{print tolower($0)}' ) + echo IMG_REPOSITORY_NAME=${IMG_REPOSITORY_NAME} >> $GITHUB_ENV + DIGEST=$(crane digest \ + ghcr.io/${IMG_REPOSITORY_NAME}/${{ inputs.image-name }}@${{ inputs.image-digest }}) + echo "PLATFORM_DIGEST=${DIGEST}" >> "$GITHUB_ENV" + + - name: Create SBOM file + shell: bash + run: | + SYFT=$(which syft) + sudo $SYFT \ + -o spdx-json \ + --file ${{ inputs.image-name }}-sbom.spdx \ + ghcr.io/${{ env.IMG_REPOSITORY_NAME }}/${{ inputs.image-name }}@${{ env.PLATFORM_DIGEST }} + + - name: Sign SBOM file + run: | + cosign sign-blob --yes \ + --output-certificate ${{ inputs.image-name }}-sbom.spdx.cert \ + --output-signature ${{ inputs.image-name }}-sbom.spdx.sig \ + ${{ inputs.image-name }}-sbom.spdx + + - name: Attach SBOM to container image + shell: bash + run: | + cosign attach \ + sbom --sbom ${{ inputs.image-name }}-sbom.spdx \ + ghcr.io/${{ env.IMG_REPOSITORY_NAME }}/${{ inputs.image-name }}@${{ env.PLATFORM_DIGEST }} + + - name: Sign SBOM file pushed to OCI registry + shell: bash + run: | + set -e + SBOM_TAG="$(echo ${{ env.PLATFORM_DIGEST }} | sed -e 's/:/-/g').sbom" + cosign sign --yes \ + ghcr.io/${{ env.IMG_REPOSITORY_NAME }}/${{ inputs.image-name }}:${SBOM_TAG} + + - name: Upload SBOMs as artifacts + uses: actions/upload-artifact@v4 + with: + name: sbom + path: ${{ inputs.image-name }}-sbom-* \ No newline at end of file diff --git a/.github/workflows/sign.yml b/.github/workflows/sign.yml new file mode 100644 index 000000000..3d8ae9bdc --- /dev/null +++ b/.github/workflows/sign.yml @@ -0,0 +1,36 @@ +name: Sign image + +on: + workflow_call: + inputs: + image-name: + type: string + required: true + image-digest: + type: string + required: true + +jobs: + sign: + name: Sign image + permissions: + packages: write + id-token: write + + runs-on: ubuntu-latest + steps: + - name: Install cosign + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 + + - name: Login to GitHub Container Registry + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + with: + registry: ghcr.io + username: ${{ github.repository }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Sign container image + run: | + IMG_REPOSITORY_NAME=$( echo ${{ github.repository }} | awk '{print tolower($0)}' ) + cosign sign --yes \ + ghcr.io/${IMG_REPOSITORY_NAME}/${{ inputs.image-name }}@${{ inputs.image-digest }} \ No newline at end of file