elint 0.1.2

Erlang code linter
Documentation
name: Create GitHub Release and Publish to crates.io

on:
  push:
    tags: ['v*']

jobs:
  validate:
    name: Validate release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - run: rustup update stable
      - run: rustup default stable
      - run: rustup component add rustfmt clippy
      # Guard against a `v*` tag whose name does not match Cargo.toml.
      # Without this, a tag like `v0.1.2` pushed against a crate
      # still on `version = "0.1.1"` would silently publish 0.1.1 to
      # crates.io while the GitHub Release advertised v0.1.2.
      # `cargo metadata + jq` is preferred over `cargo pkgid | sed` so
      # a future change to cargo's pkgid output format cannot silently
      # break the check. `jq` is preinstalled on ubuntu-latest.
      - name: Tag matches Cargo.toml version
        run: |
          set -euo pipefail
          tag_version="${GITHUB_REF_NAME#v}"
          cargo_version="$(cargo metadata --format-version=1 --no-deps \
            | jq -r '.packages[] | select(.name == "elint") | .version')"
          if [ "$tag_version" != "$cargo_version" ]; then
            echo "tag version ($tag_version) does not match Cargo.toml version ($cargo_version)"
            exit 1
          fi
      # The `validate` job re-runs the CI suite so a broken commit
      # that somehow received a `v*` tag never publishes, even if the
      # push-time CI on that commit failed or was skipped.
      - run: cargo test --all
      - run: cargo test --doc
      - run: cargo fmt --all -- --check
      - run: cargo clippy --all -- -D warnings
      - env:
          RUSTDOCFLAGS: -D warnings
        run: cargo doc --no-deps
      # Packaging smoke test: exercises manifest metadata, include /
      # exclude, readme path, and the verify pass in a clean copy.
      - run: cargo publish --dry-run

  # publish first: crates.io publish is non-reversible (unpublish is
  # restricted to 72 hours and only removes the version from the index,
  # not from downstream Cargo.lock files), so make it the guarding step.
  # If it fails, no orphan github-release with a version that does not
  # exist on crates.io is left behind. A github-release failure after
  # publish succeeds is recoverable by re-running `gh release create`
  # against the existing tag.
  publish:
    needs: validate
    runs-on: ubuntu-latest
    # environment: release  # Optional: for enhanced security
    permissions:
      id-token: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
        id: auth
      - run: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

  github-release:
    needs: publish
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - id: create-release
        run: gh release create ${{ github.ref_name }} --title "${{ github.ref_name }}" --generate-notes
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # The release must already exist before any asset upload, so every binary
  # job depends on `github-release`. The three jobs run in parallel and
  # attach statically linked or SDK-pinned binaries to the same tag.
  # Asset names strip the leading `v` from the tag, e.g. `elint-0.0.3.xxx`.

  linux-x86_64-binary:
    name: Upload Binary for Linux x86_64
    needs: github-release
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      # musl-tools provides the musl linker for a fully static binary.
      - run: sudo apt install -y musl-tools
      - run: rustup update stable
      - run: rustup target add x86_64-unknown-linux-musl
      - run: cargo build --release --target=x86_64-unknown-linux-musl
      - name: Upload Release Asset
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mv target/x86_64-unknown-linux-musl/release/elint \
            elint-${GITHUB_REF_NAME#v}.x86_64-unknown-linux-musl
          gh release upload ${{ github.ref_name }} \
            elint-${GITHUB_REF_NAME#v}.x86_64-unknown-linux-musl

  linux-arm64-binary:
    name: Upload Binary for Linux arm64
    needs: github-release
    # arm64 native runner, so aarch64-unknown-linux-musl needs no cross
    # toolchain: apt musl-tools on this image is the aarch64 one.
    runs-on: ubuntu-24.04-arm
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - run: sudo apt install -y musl-tools
      - run: rustup update stable
      - run: rustup target add aarch64-unknown-linux-musl
      - run: cargo build --release --target=aarch64-unknown-linux-musl
      - name: Upload Release Asset
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mv target/aarch64-unknown-linux-musl/release/elint \
            elint-${GITHUB_REF_NAME#v}.aarch64-unknown-linux-musl
          gh release upload ${{ github.ref_name }} \
            elint-${GITHUB_REF_NAME#v}.aarch64-unknown-linux-musl

  macos-binary:
    name: Upload Binary for macOS
    needs: github-release
    # macos-15 (not macos-latest): the older SDK keeps the minimum macOS
    # version of the produced binaries low and the image stable.
    runs-on: macos-15
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - run: rustup update stable
      - run: rustup target add aarch64-apple-darwin
      - run: cargo build --release --target=aarch64-apple-darwin
      - name: Upload Release Asset
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mv target/aarch64-apple-darwin/release/elint \
            elint-${GITHUB_REF_NAME#v}.aarch64-apple-darwin
          gh release upload ${{ github.ref_name }} \
            elint-${GITHUB_REF_NAME#v}.aarch64-apple-darwin