rust-switcher 1.0.13

Windows keyboard layout switcher and text conversion utility
Documentation
name: Release

on:
  push:
    branches:
      - dev
  workflow_dispatch:
    inputs:
      version:
        description: Optional explicit release version (defaults to the next patch version)
        required: false
        type: string

permissions:
  contents: write
  id-token: write
  pull-requests: read

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

jobs:
  release:
    runs-on: windows-latest
    timeout-minutes: 90
    environment: release
    env:
      CARGO_TERM_PROGRESS_WHEN: never
      GH_TOKEN: ${{ github.token }}

    steps:
      - name: Checkout dev
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
        with:
          fetch-depth: 0
          persist-credentials: false
          ref: dev

      - name: Detect release-worthy dependency merge
        id: release_context
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        with:
          github-token: ${{ github.token }}
          script: |

            if (context.eventName === 'workflow_dispatch') {
              core.setOutput('should_release', 'true');
              core.setOutput('reason', 'manual');
              return;
            }

            const message = context.payload.head_commit?.message ?? '';
            const subject = message.split('\n', 1)[0] ?? '';
            const match = subject.match(/\(#(\d+)\)$/);
            if (!match) {
              core.setOutput('should_release', 'false');
              core.setOutput('reason', 'no-pr-number-in-subject');
              return;
            }

            const prNumber = Number(match[1]);
            const { data: pr } = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: prNumber,
            });

            if (
              !pr.merged ||
              pr.base.ref !== 'dev' ||
              pr.user.login !== 'dependabot[bot]' ||
              !pr.head.ref.startsWith('dependabot/cargo/')
            ) {
              core.setOutput('should_release', 'false');
              core.setOutput('reason', 'no-matching-pr');
              return;
            }

            core.setOutput('should_release', 'true');
            core.setOutput('reason', `dependabot-pr-${prNumber}`);

      - name: Stop when this push is not a cargo Dependabot merge
        if: steps.release_context.outputs.should_release != 'true'
        shell: bash
        run: |

          set -euo pipefail
          echo "Skipping release: ${{ steps.release_context.outputs.reason }}"

      - name: Configure authenticated origin
        if: steps.release_context.outputs.should_release == 'true'
        shell: bash
        run: |

          set -euo pipefail
          git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"

      - name: Reattach local dev branch
        if: steps.release_context.outputs.should_release == 'true'
        shell: bash
        run: |

          set -euo pipefail
          git checkout -B dev origin/dev

      - name: Configure git author
        if: steps.release_context.outputs.should_release == 'true'
        shell: bash
        run: |

          set -euo pipefail
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Install Rust toolchain (nightly)
        if: steps.release_context.outputs.should_release == 'true'
        uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
        with:
          toolchain: nightly
          components: rustfmt, clippy

      - name: Rust cache
        if: steps.release_context.outputs.should_release == 'true'
        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

      - name: Authenticate to crates.io with trusted publishing
        if: steps.release_context.outputs.should_release == 'true'
        id: crates_auth
        continue-on-error: true
        uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe # v1.0.4

      - name: Verify crates.io token source
        if: steps.release_context.outputs.should_release == 'true'
        shell: pwsh
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.crates_auth.outputs.token || secrets.CARGO_REGISTRY_TOKEN }}
        run: |

          $ErrorActionPreference = 'Stop'

          if (-not $env:CARGO_REGISTRY_TOKEN) {
            throw 'No crates.io token source is available. Configure trusted publishing for this crate or add the CARGO_REGISTRY_TOKEN secret to the release environment.'
          }

      - name: Run release automation
        if: steps.release_context.outputs.should_release == 'true'
        shell: pwsh
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.crates_auth.outputs.token || secrets.CARGO_REGISTRY_TOKEN }}
          RELEASE_VERSION: ${{ inputs.version }}
        run: |

          $ErrorActionPreference = 'Stop'

          $version = ($env:RELEASE_VERSION ?? '').Trim()
          if ($version) {
            & pwsh -ExecutionPolicy Bypass -NoLogo -NoProfile -File .\scripts\release\release.ps1 $version
          } else {
            & pwsh -ExecutionPolicy Bypass -NoLogo -NoProfile -File .\scripts\release\release.ps1
          }