test(helper): keep the malformed-frame behaviours alive after writing #3
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*.*.*' | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| verify-tag: | |
| name: Verify release tag | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Check tag matches the CLI version | |
| id: version | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| tag_version="${GITHUB_REF_NAME#v}" | |
| crate_version="$(cargo metadata --no-deps --format-version 1 | python3 -c 'import json, sys; print(next(package["version"] for package in json.load(sys.stdin)["packages"] if package["name"] == "codehelion"))')" | |
| if [ "$tag_version" != "$crate_version" ]; then | |
| echo "tag $GITHUB_REF_NAME does not match codehelion version $crate_version" >&2 | |
| exit 1 | |
| fi | |
| echo "version=$tag_version" >> "$GITHUB_OUTPUT" | |
| build-helpers: | |
| name: Build helpers (${{ matrix.name }}) | |
| needs: verify-tag | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: linux-x86_64 | |
| runner: ubuntu-latest | |
| extension: '' | |
| archive: tar.gz | |
| # Pinned rather than macos-latest so the helpers and the CLI they are | |
| # loaded by are built against the same macOS version. | |
| - name: macos-aarch64 | |
| runner: macos-15 | |
| extension: '' | |
| archive: tar.gz | |
| - name: windows-x86_64 | |
| runner: windows-latest | |
| extension: .exe | |
| archive: zip | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Build helper executables | |
| run: cargo build --release -p codehelion-backend-rust -p codehelion-backend-clang | |
| - name: Stage Unix helper archive | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| directory="codehelion-helpers-${{ needs.verify-tag.outputs.version }}-${{ matrix.name }}" | |
| mkdir "$directory" | |
| cp "target/release/codehelion-backend-rust${{ matrix.extension }}" "$directory/" | |
| cp "target/release/codehelion-backend-clang${{ matrix.extension }}" "$directory/" | |
| cp LICENSE "$directory/" | |
| cp README.md "$directory/" | |
| tar -czf "$directory.tar.gz" "$directory" | |
| - name: Stage Windows helper archive | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $directory = "codehelion-helpers-${{ needs.verify-tag.outputs.version }}-${{ matrix.name }}" | |
| New-Item -ItemType Directory -Path $directory | Out-Null | |
| Copy-Item "target/release/codehelion-backend-rust${{ matrix.extension }}" $directory | |
| Copy-Item "target/release/codehelion-backend-clang${{ matrix.extension }}" $directory | |
| Copy-Item LICENSE $directory | |
| Copy-Item README.md $directory | |
| Compress-Archive -Path $directory -DestinationPath "$directory.zip" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: helpers-${{ matrix.name }} | |
| path: codehelion-helpers-${{ needs.verify-tag.outputs.version }}-${{ matrix.name }}.${{ matrix.archive }} | |
| if-no-files-found: error | |
| retention-days: 14 | |
| build-cli: | |
| name: Build CLI (${{ matrix.name }}) | |
| needs: verify-tag | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: linux-x86_64 | |
| runner: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| extension: '' | |
| archive: tar.gz | |
| cross_linker: '' | |
| - name: linux-aarch64 | |
| runner: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| extension: '' | |
| archive: tar.gz | |
| cross_linker: aarch64-linux-gnu-gcc | |
| - name: macos-x86_64 | |
| runner: macos-15-intel | |
| target: x86_64-apple-darwin | |
| extension: '' | |
| archive: tar.gz | |
| cross_linker: '' | |
| - name: macos-aarch64 | |
| runner: macos-15 | |
| target: aarch64-apple-darwin | |
| extension: '' | |
| archive: tar.gz | |
| cross_linker: '' | |
| - name: windows-x86_64 | |
| runner: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| extension: .exe | |
| archive: zip | |
| cross_linker: '' | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| # The cross compiler alone is not enough: dependencies that carry C sources | |
| # compile them for the target, and that needs the target's libc headers. | |
| - name: Install Linux cross toolchain | |
| if: matrix.cross_linker != '' | |
| shell: bash | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| gcc-aarch64-linux-gnu libc6-dev-arm64-cross | |
| - name: Build CLI | |
| shell: bash | |
| run: cargo build --locked --release -p codehelion --target ${{ matrix.target }} | |
| env: | |
| CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.cross_linker }} | |
| - name: Stage Unix CLI archive | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| directory="codehelion-${{ needs.verify-tag.outputs.version }}-${{ matrix.name }}" | |
| mkdir "$directory" | |
| cp "target/${{ matrix.target }}/release/codehelion${{ matrix.extension }}" "$directory/" | |
| cp LICENSE "$directory/" | |
| cp README.md "$directory/" | |
| tar -czf "$directory.tar.gz" "$directory" | |
| - name: Stage Windows CLI archive | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $directory = "codehelion-${{ needs.verify-tag.outputs.version }}-${{ matrix.name }}" | |
| New-Item -ItemType Directory -Path $directory | Out-Null | |
| Copy-Item "target/${{ matrix.target }}/release/codehelion${{ matrix.extension }}" $directory | |
| Copy-Item LICENSE $directory | |
| Copy-Item README.md $directory | |
| Compress-Archive -Path $directory -DestinationPath "$directory.zip" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: cli-${{ matrix.name }} | |
| path: codehelion-${{ needs.verify-tag.outputs.version }}-${{ matrix.name }}.${{ matrix.archive }} | |
| if-no-files-found: error | |
| retention-days: 14 | |
| publish-release: | |
| name: Publish GitHub Release assets | |
| needs: [verify-tag, build-helpers, build-cli] | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Every artifact this run produced, with no pattern to narrow it: the | |
| # release carries both the CLI and the helpers, and a pattern that names | |
| # one of them drops the other from the release without failing. | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Generate archive checksums | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cd dist | |
| sha256sum codehelion-* > SHA256SUMS | |
| - name: Create or update GitHub Release | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| name: Release ${{ github.ref_name }} | |
| tag_name: ${{ github.ref_name }} | |
| generate_release_notes: true | |
| files: | | |
| dist/codehelion-* | |
| dist/SHA256SUMS | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Last, because it is the one step nothing undoes: a published version stays | |
| # published, and yanking it does not free the number. Everything that can | |
| # still be corrected — the builds, the archives, the release notes — has | |
| # already succeeded by the time this runs. | |
| publish-crates: | |
| name: Publish to crates.io | |
| needs: [verify-tag, publish-release] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| # Trusted publishing: crates.io exchanges this run's identity for a token | |
| # that expires with the job, so no registry credential is stored here. | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| # Asked before publishing rather than discovered during it. A run that | |
| # already published cannot publish again, so re-running a tag has to be | |
| # able to end in nothing happening — and a run that got partway through | |
| # has to say so rather than fail halfway a second time. | |
| - name: Ask the registry what is already published | |
| id: registry | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version="${{ needs.verify-tag.outputs.version }}" | |
| crates="$(cargo metadata --no-deps --format-version 1 | python3 -c \ | |
| 'import json, sys; print("\n".join(package["name"] for package in json.load(sys.stdin)["packages"] if package["publish"] is None))')" | |
| present=() | |
| absent=() | |
| for crate in $crates; do | |
| status="$(curl -sS -o /dev/null -w '%{http_code}' \ | |
| -H 'User-Agent: codehelion release workflow (https://github.com/libraz/codehelion)' \ | |
| "https://crates.io/api/v1/crates/$crate/$version")" | |
| case "$status" in | |
| 200) present+=("$crate") ;; | |
| 404) absent+=("$crate") ;; | |
| *) echo "crates.io answered $status for $crate $version" >&2; exit 1 ;; | |
| esac | |
| done | |
| if [ "${#absent[@]}" -eq 0 ]; then | |
| echo "every crate is already at $version; nothing to publish" | |
| echo "publish=false" >> "$GITHUB_OUTPUT" | |
| elif [ "${#present[@]}" -eq 0 ]; then | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "version $version is published for ${present[*]} but not for ${absent[*]}." >&2 | |
| echo "cargo cannot resume a partial release; finish the remaining crates by hand." >&2 | |
| exit 1 | |
| fi | |
| # No token is configured for a crate that does not exist yet, so the | |
| # first release of a new name is made from a workstation and this job | |
| # finds nothing left to do. From the second release on it is the whole | |
| # of the publish. | |
| - name: Authenticate with crates.io | |
| if: steps.registry.outputs.publish == 'true' | |
| id: auth | |
| uses: rust-lang/crates-io-auth-action@v1 | |
| # Verification builds each crate from its own package before uploading | |
| # it, and cargo waits for each upload to be resolvable before publishing | |
| # what depends on it. | |
| - name: Publish | |
| if: steps.registry.outputs.publish == 'true' | |
| run: cargo publish --workspace --locked | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} |