glslint 0.3.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
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
      # Real toolchain step: reads the channel from rust-toolchain.toml and
      # installs the cross target, instead of leaning on the runner default.
      - uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0
        with:
          target: ${{ matrix.target }}
      - 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

  npm:
    # The npm channel: a `glslint` wrapper package whose optionalDependencies are
    # per-platform packages holding the prebuilt binaries, so `npm i -D glslint`
    # works with no Rust toolchain. Needs `build`, since it republishes exactly
    # the binaries that job attached to the release.
    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 # npm provenance attestation
    env:
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
      TAG: ${{ needs.release-please.outputs.tag_name }}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
        with:
          node-version: 'lts/*'
          registry-url: 'https://registry.npmjs.org'
      - 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
      # Both publishes are skipped (not failed) when NPM_TOKEN is unset, matching
      # the extension job: the release never breaks before npm is configured.
      # Paths keep their `./` prefix — npm reads a bare `npm/glslint` as the
      # GitHub shorthand for that org/repo, not as a directory.
      - name: Publish the platform packages
        if: ${{ env.NPM_TOKEN != '' }}
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
        # Platform packages go first: the wrapper pins their exact versions, so
        # they must exist before it becomes installable.
        run: |
          for dir in ./npm/@glslint/*/; do
            npm publish "$dir" --access public --provenance
          done
      - name: Publish the wrapper package
        if: ${{ env.NPM_TOKEN != '' }}
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: npm publish ./npm/glslint --access public --provenance

  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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
      - 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"