# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: 2025 The Gleam contributors name: "Build Gleam" description: "Build Gleam Release" inputs: version: description: "Build Version" required: true toolchain: description: "Cargo Toolchain" required: true target: description: "Cargo Installation Target" required: true cargo-tool: description: "Cargo Tool used for Build (for example, `cross`)" required: true expected-binary-architecture: description: "Expected Binary Architecture" required: false default: "" generate-gleam-licences-html: description: "Generate gleam-licences.html" required: false default: "false" azure-tenant-id: description: "Azure Tenant ID for Windows Code Signing" required: false azure-subscription-id: description: "Azure Subscription ID for Windows Code Signing" required: false azure-client-id: description: "Azure Client ID for Windows Code Signing" required: false azure-trusted-signing-account-name: description: "Azure Trusted Signing Account Name for Windows Code Signing" required: false azure-certificate-profile-name: description: "Azure Certificate Profile Name for Windows Code Signing" required: false outputs: files: description: "Path to all files" value: | ${{ steps.build.outputs.archive }} ${{ steps.build.outputs.archive }}.sha256 ${{ steps.build.outputs.archive }}.sha512 ${{ steps.build.outputs.archive }}.sigstore ${{ steps.build.outputs.archive }}.sbom.spdx.json ${{ steps.build.outputs.archive }}.sbom.cyclonedx.json ${{ inputs.generate-gleam-licences-html == 'true' && 'gleam-licences.html' || '' }} runs: using: "composite" steps: - name: Install Rust toolchain uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4 with: toolchain: ${{ inputs.toolchain }} target: ${{ inputs.target }} cache-key: v1-${{ inputs.target }} - name: Install Cargo SBoM shell: bash # The `cargo-sbom` version is specified in the next line. Change it to # keep it up-to-date. run: cargo install cargo-sbom@~0.9.1 - name: Build WASM release binary if: ${{ inputs.target != 'wasm32-unknown-unknown' }} uses: clechasseur/rs-cargo@319e211e9459c5e531afb6d157296765b441572e # v3.0.3 with: command: build args: --release --target ${{ inputs.target }} tool: ${{ inputs.cargo-tool }} - name: Install wasm-pack if: ${{ inputs.target == 'wasm32-unknown-unknown' }} shell: bash run: curl -sSL https://rustwasm.github.io/wasm-pack/installer/init.sh | sh - name: Build WASM release binary if: ${{ inputs.target == 'wasm32-unknown-unknown' }} shell: bash run: wasm-pack build --release --target web compiler-wasm - name: Verify binary architecture if: ${{ inputs.expected-binary-architecture }} shell: bash run: | BINARY_PATH="target/${INPUTS_TARGET}/release/gleam" if [[ "${INPUTS_TARGET}" == *"windows"* ]]; then BINARY_PATH="${BINARY_PATH}.exe" fi if ! file -b "$BINARY_PATH" | grep -iq "${INPUTS_EXPECTED_BINARY_ARCHITECTURE}"; then echo "error: Architecture mismatch" echo "Expected architecture: '${INPUTS_EXPECTED_BINARY_ARCHITECTURE}'" echo "Found binary type: '$(file -b "$BINARY_PATH")'" exit 1 fi echo "ok: Architecture match" env: INPUTS_TARGET: ${{ inputs.target }} INPUTS_EXPECTED_BINARY_ARCHITECTURE: ${{ inputs.expected-binary-architecture }} # We use Azure Trusted Signing to sign the Windows binaries. # This is done to ensure that the binaries are trusted and can be # verified by users and systems that require signed code. # # Why is this helpful? # * It provides a way to verify the authenticity and integrity of the # binaries distributed by Gleam. # * It helps prevent tampering with the binaries, ensuring that users # can trust the code they are running. # # For more information, see: # * https://github.com/Azure/trusted-signing-action # * https://azure.microsoft.com/en-us/products/trusted-signing - name: Log in to Azure if: ${{ contains(inputs.target, '-windows-') && inputs.azure-tenant-id && inputs.azure-subscription-id && inputs.azure-client-id && inputs.azure-trusted-signing-account-name && inputs.azure-certificate-profile-name }} uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 with: client-id: ${{ inputs.azure-client-id }} tenant-id: ${{ inputs.azure-tenant-id }} subscription-id: ${{ inputs.azure-subscription-id }} - name: Windows Code Signing if: ${{ contains(inputs.target, '-windows-') && inputs.azure-tenant-id && inputs.azure-subscription-id && inputs.azure-client-id && inputs.azure-trusted-signing-account-name && inputs.azure-certificate-profile-name }} uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0 with: endpoint: https://eus.codesigning.azure.net/ signing-account-name: ${{ inputs.azure-trusted-signing-account-name }} certificate-profile-name: ${{ inputs.azure-certificate-profile-name }} files: ${{ github.workspace }}\target\${{ inputs.target }}\release\gleam.exe file-digest: SHA256 timestamp-rfc3161: http://timestamp.acs.microsoft.com timestamp-digest: SHA256 # https://github.com/Azure/artifact-signing-action/issues/146 cache-dependencies: false - name: Build archive id: build shell: bash run: | case "$TARGET" in *windows*) ARCHIVE="gleam-$VERSION-$TARGET.zip" cp "target/$TARGET/release/gleam.exe" "gleam.exe" 7z a "$ARCHIVE" "gleam.exe" rm gleam.exe ;; wasm*) ARCHIVE="gleam-$VERSION-browser.tar.gz" tar -C compiler-wasm/pkg/ -czvf $ARCHIVE . rm -rf compiler-wasm/pkg/ ;; *) ARCHIVE="gleam-$VERSION-$TARGET.tar.gz" cp "target/$TARGET/release/gleam" "gleam" tar -czvf "$ARCHIVE" "gleam" rm gleam ;; esac echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT env: TARGET: "${{ inputs.target }}" VERSION: "${{ inputs.version }}" - name: Ensure binary successfully boots if: ${{ inputs.expected-binary-architecture }} shell: bash run: | case "$TARGET" in *windows*) 7z x "$ARCHIVE" ./gleam.exe --version ;; aarch64*) echo "We cannot test an ARM binary on a AMD64 runner" ;; *) tar -xvzf "$ARCHIVE" ./gleam --version ;; esac env: TARGET: "${{ inputs.target }}" ARCHIVE: "${{ steps.build.outputs.archive }}" - name: Install Erlang if: ${{ inputs.generate-gleam-licences-html == 'true' }} uses: erlef/setup-beam@fc68ffb90438ef2936bbb3251622353b3dcb2f93 # v1.24.0 with: otp-version: "29" rebar3-version: "3" - name: Generate licences HTML if: ${{ inputs.generate-gleam-licences-html == 'true' }} shell: bash working-directory: licence-bundler run: | case "$TARGET" in wasm*) echo "error: cannot generate gleam-licences.html from a wasm build" exit 1 ;; *windows*) ../target/"$TARGET"/release/gleam.exe run ;; *) ../target/"$TARGET"/release/gleam run ;; esac env: TARGET: "${{ inputs.target }}" # By using `cargo-sbom``, we create two formats of Build SBoMs # (SPDX and CycloneDX) for the gleam build. # We store those files alongside the build artifacts on the GitHub Release # page and also use them to create Container SBoMs for Docker images. # # Why is this helpful? # * It gives us and our users complete visibility into which dependencies # and which versions are present in the build / container image. # * The SBoM can be fed into vulnerability scanners so that anyone can check # if any dependencies have known security issues. - name: Generate Build SBoM shell: bash run: | cargo-sbom \ --output-format spdx_json_2_3 \ > "$ARCHIVE.sbom.spdx.json" cargo-sbom \ --output-format cyclone_dx_json_1_4 \ > "$ARCHIVE.sbom.cyclonedx.json" env: ARCHIVE: "${{ steps.build.outputs.archive }}" - name: Hash Build Archive shell: bash run: | openssl dgst -r -sha256 -out "$ARCHIVE".sha256 "$ARCHIVE" openssl dgst -r -sha512 -out "$ARCHIVE".sha512 "$ARCHIVE" env: ARCHIVE: "${{ steps.build.outputs.archive }}" # We provide SLSA Provenance for the distribution build. This attests to # where, when, and how the asset or image was built. # # Why is this helpful? # * It provides a record of the exact Git commit (git sha) and GitHub # Actions workflow used to produce a release. # * Users or automated systems can verify that the artifact you’re # downloading was indeed built from the official Gleam repo, on a # particular date, using the correct pipeline and not tampered with later. # * The attestation is published to a transparency log for extra # verification: https://github.com/gleam-lang/gleam/attestations/ # # For more information, see: # * https://github.com/actions/attest # * https://github.com/actions/attest-sbom - name: Attest Distribution Assets with SBoM id: attest-sbom uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 with: subject-path: | ${{ steps.build.outputs.archive }} ${{ steps.build.outputs.archive }}.sbom.spdx.json ${{ steps.build.outputs.archive }}.sbom.cyclonedx.json sbom-path: "${{ steps.build.outputs.archive }}.sbom.spdx.json" # The provenanve information is stored alongside the built artifact with # the `.sigstore` file extension. - name: "Copy SBoM provenance" id: sbom-provenance shell: bash run: | cp "$ATTESTATION" "$ARCHIVE.sigstore" env: ARCHIVE: "${{ steps.build.outputs.archive }}" ATTESTATION: "${{ steps.attest-sbom.outputs.bundle-path }}" - name: Upload artifact uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: release-${{ matrix.target }} path: | ${{ steps.build.outputs.archive }} ${{ steps.build.outputs.archive }}.sha256 ${{ steps.build.outputs.archive }}.sha512 ${{ steps.build.outputs.archive }}.sigstore ${{ steps.build.outputs.archive }}.sbom.spdx.json ${{ steps.build.outputs.archive }}.sbom.cyclonedx.json ${{ inputs.generate-gleam-licences-html == 'true' && 'gleam-licences.html' || '' }} overwrite: true