glslint 0.6.0

A luma.gl/deck.gl-aware GLSL checker and language server
name: release

# release-please maintains a Release PR on each push to main; merging it bumps
# the versions (Cargo.toml + the extension's package.json), updates the
# changelog, and creates the tag + GitHub Release. That same run then builds the
# per-platform binaries and uploads them to the release — one workflow, so there's
# no cross-workflow trigger to fight. CI runs first (workflow_call into ci.yml)
# and gates release-please, so a release can never cut from a red main.
#
# Give every PR a conventional-commit title (`feat:`, `fix:`, `ci:`). Squash is
# the merge method here, and it takes its subject from the PR title whenever the
# branch holds more than one commit, so a plain title lands on main with no type
# and release-please skips the release entirely.

on:
  push:
    branches: [main]

permissions:
  contents: write
  pull-requests: write

concurrency:
  group: release
  cancel-in-progress: false

jobs:
  ci:
    permissions:
      contents: read
    uses: ./.github/workflows/ci.yml

  release-please:
    needs: ci
    runs-on: ubuntu-latest
    timeout-minutes: 10
    outputs:
      release_created: ${{ steps.release.outputs.release_created }}
      tag_name: ${{ steps.release.outputs.tag_name }}
    steps:
      - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
        id: release

  build:
    needs: release-please
    if: ${{ needs.release-please.outputs.release_created == 'true' }}
    runs-on: ${{ matrix.os }}
    timeout-minutes: 45
    strategy:
      fail-fast: false
      matrix:
        include:
          - { os: macos-latest, target: aarch64-apple-darwin }
          - { os: macos-latest, target: x86_64-apple-darwin }
          - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
          - { os: windows-latest, target: x86_64-pc-windows-msvc }
    permissions:
      contents: write
    env:
      RUSTC_WRAPPER: sccache
      SCCACHE_GHA_ENABLED: "true"
      CARGO_INCREMENTAL: "0"
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0
        with:
          target: ${{ matrix.target }}
          cache: "false"
      - uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          cache-targets: "false"
      - name: Build
        run: cargo build --release --locked --target ${{ matrix.target }}
      - name: Stage asset (unix)
        if: runner.os != 'Windows'
        run: cp "target/${{ matrix.target }}/release/glslint" "glslint-${{ matrix.target }}"
      - name: Stage asset (windows)
        if: runner.os == 'Windows'
        run: copy "target\${{ matrix.target }}\release\glslint.exe" "glslint-${{ matrix.target }}.exe"
      - name: Upload to release (unix)
        if: runner.os != 'Windows'
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh release upload "${{ needs.release-please.outputs.tag_name }}" "glslint-${{ matrix.target }}" --clobber
      - name: Upload to release (windows)
        if: runner.os == 'Windows'
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh release upload "${{ needs.release-please.outputs.tag_name }}" "glslint-${{ matrix.target }}.exe" --clobber
      - name: sccache stats
        if: ${{ always() }}
        run: sccache --show-stats

  crates:
    # Publish the crate to crates.io over OIDC trusted publishing: the auth
    # action exchanges the workflow's OIDC token for a short-lived crates.io
    # token (auto-revoked when the job ends), so no CARGO_REGISTRY_TOKEN secret
    # is stored. The trusted publisher registered on crates.io pins this workflow
    # by filename, same as npm, so renaming release.yml breaks it until updated.
    needs: release-please
    if: ${{ needs.release-please.outputs.release_created == 'true' }}
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      id-token: write # the OIDC credential the auth action exchanges
    env:
      RUSTC_WRAPPER: sccache
      SCCACHE_GHA_ENABLED: "true"
      CARGO_INCREMENTAL: "0"
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0
        with:
          cache: "false"
      - uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          cache-targets: "false"
      # glslint shells out to glslangValidator, so cargo publish's verify build
      # (a full `cargo build`) needs it on PATH just as CI does.
      - name: Install glslang (provides glslangValidator)
        run: sudo apt-get update && sudo apt-get install -y glslang-tools
      - name: Authenticate with crates.io
        id: auth
        uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
      - name: Publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
        run: cargo publish --locked
      - name: sccache stats
        if: ${{ always() }}
        run: sccache --show-stats

  npm:
    # The npm channel: an `@glslint/cli` wrapper whose optionalDependencies are
    # per-platform packages holding the prebuilt binaries, so a deck.gl project
    # installs one binary and needs no Rust toolchain. Needs `build`, since it
    # republishes exactly the binaries that job attached to the release.
    #
    # Publishes over OIDC trusted publishing: no stored token, and npm mints the
    # provenance attestation itself, so `--provenance` must NOT be passed. The
    # trusted publisher registered on npmjs.com pins this workflow by filename,
    # so renaming release.yml breaks publishing until that config is updated.
    needs: [release-please, build]
    if: ${{ needs.release-please.outputs.release_created == 'true' }}
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      id-token: write # the OIDC credential npm authenticates with
    env:
      TAG: ${{ needs.release-please.outputs.tag_name }}
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
        with:
          node-version: 'lts/*'
          registry-url: 'https://registry.npmjs.org'
      # Trusted publishing wants npm >= 11.5.1, ahead of the npm bundled with
      # the Node LTS images.
      - name: Update npm
        run: npm install -g npm@latest
      - name: Download the release binaries
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh release download "$TAG" --dir dist --pattern 'glslint-*'
      - name: Lay out the packages
        run: node npm/prepare.mjs --version "$TAG" --binaries dist
      # Platform packages go first: the wrapper pins their exact versions, so
      # they must exist before it becomes installable. Paths keep their `./`
      # prefix — npm reads a bare `npm/glslint` as the GitHub shorthand for that
      # org/repo, not as a directory.
      - name: Publish
        run: |
          for dir in ./npm/@glslint/*/; do
            npm publish "$dir" --access public
          done
          npm publish ./npm/glslint --access public

  homebrew:
    # The Homebrew channel: regenerate the tap's formula from the release
    # binaries and push it to johncarmack1984/homebrew-tap, so `brew upgrade`
    # picks up each new version. Needs `build`, since it checksums exactly the
    # binaries that job attached to the release.
    #
    # Pushing to another repo is beyond GITHUB_TOKEN's scope, so this uses a
    # dedicated PAT (HOMEBREW_TAP_TOKEN, contents:write on the tap). Every step
    # is skipped — not failed — when the secret is unset, so the release never
    # breaks before the tap is wired up, exactly like publish-extension below.
    needs: [release-please, build]
    if: ${{ needs.release-please.outputs.release_created == 'true' }}
    runs-on: ubuntu-latest
    timeout-minutes: 10
    env:
      TAG: ${{ needs.release-please.outputs.tag_name }}
      HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
        if: ${{ env.HOMEBREW_TAP_TOKEN != '' }}
        with:
          node-version: 'lts/*'
      - name: Download the release binaries
        if: ${{ env.HOMEBREW_TAP_TOKEN != '' }}
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh release download "$TAG" --dir dist --pattern 'glslint-*'
      - name: Generate the formula
        if: ${{ env.HOMEBREW_TAP_TOKEN != '' }}
        run: node homebrew/prepare.mjs --version "$TAG" --binaries dist --out glslint.rb
      - name: Push it to the tap
        if: ${{ env.HOMEBREW_TAP_TOKEN != '' }}
        run: |
          git clone --depth 1 \
            "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/johncarmack1984/homebrew-tap.git" tap
          mkdir -p tap/Formula
          cp glslint.rb tap/Formula/glslint.rb
          cd tap
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add Formula/glslint.rb
          # Nothing to do if a re-run regenerates an identical formula.
          git diff --cached --quiet || git commit -m "glslint ${TAG#v}"
          git push

  publish-extension:
    needs: release-please
    if: ${{ needs.release-please.outputs.release_created == 'true' }}
    runs-on: ubuntu-latest
    timeout-minutes: 10
    env:
      OVSX_PAT: ${{ secrets.OVSX_PAT }}
      VSCE_PAT: ${{ secrets.VSCE_PAT }}
    defaults:
      run:
        working-directory: editors/vscode
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
        with:
          node-version: 'lts/*'
      - run: npm ci
      - name: Package
        run: npx @vscode/vsce package
      # Each publish is skipped (not failed) when its token secret is unset, so
      # the release never breaks before the registries are configured.
      - name: Publish to Open VSX
        if: ${{ env.OVSX_PAT != '' }}
        run: npx ovsx publish *.vsix -p "$OVSX_PAT"
      - name: Publish to VS Code Marketplace
        if: ${{ env.VSCE_PAT != '' }}
        run: npx @vscode/vsce publish --packagePath $(ls *.vsix) -p "$VSCE_PAT"