kugiri 0.2.1

Marker-based block editing CLI
Documentation
name: Release

on:
  push:
    branches:
      - main  # Only run on main branch pushes (PR merges)
  pull_request:
    types: [ opened, synchronize, reopened, labeled, unlabeled ]

permissions: {}

jobs:
  release-pr:
    permissions:
      contents: write
      pull-requests: write
      issues: write
      statuses: write
    runs-on: ubuntu-latest
    outputs:
      release_pr: ${{ toJSON(steps.release-pr.outputs) }}
    steps:
      - name: Harden the runner (Audit all outbound calls)
        uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
        with:
          egress-policy: audit

      - name: Checkout
        uses: actions/checkout@v5
      - uses: actionutils/trusted-tag-verifier@68bef2f18f8ceb5c4dccc5542cadcfbc82ed4656 # v0.5.0
        with:
          verify: 'actionutils/create-release-pr'
      - uses: actionutils/create-release-pr@v0
        id: release-pr

  update-release-pr:
    needs: [release-pr]
    if: fromJSON(needs.release-pr.outputs.release_pr).state == 'release_pr_open'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      id-token: write # Required for OIDC token exchange
    steps:
      - name: Harden the runner (Audit all outbound calls)
        uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
        with:
          egress-policy: audit
      - uses: actions/checkout@v5
        with:
          ref: ${{ fromJSON(needs.release-pr.outputs.release_pr).pr_branch }}
      - uses: actions/dependency-review-action@v4
        id: dependency-review
        with:
          base-ref: ${{ fromJSON(needs.release-pr.outputs.release_pr).current_tag }}
          head-ref: main
      - uses: chainguard-dev/actions/setup-gitsign@0cda751b114eb55c388e88f7479292668165602a # v1.0.2

      - name: Update README installation section
        if: fromJSON(needs.release-pr.outputs.release_pr).next_tag != ''
        env:
          VERSION: ${{ fromJSON(needs.release-pr.outputs.release_pr).next_tag }}
        run: |
          # Build kugiri first
          cargo build --release
          # Update README with new version
          ./scripts/update-installation.sh "$VERSION"
          # Commit if there are changes
          git add README.md
          if git diff --cached --exit-code; then
            echo "No changes to README.md"
          else
            git commit -m "docs: Update README installation section to $VERSION"
            git push
          fi

      - name: Update CHANGELOG with release notes
        if: fromJSON(needs.release-pr.outputs.release_pr).next_tag != ''
        env:
          VERSION: ${{ fromJSON(needs.release-pr.outputs.release_pr).next_tag }}
          RELEASE_NOTES: ${{ fromJSON(needs.release-pr.outputs.release_pr).release_notes }}
        run: |
          # Create auto-generated title
          TITLE="# [$VERSION](https://github.com/${{ github.repository }}/releases/tag/$VERSION) - $(date +%Y-%m-%d)"

          # Check if {version} section exists
          if ./target/release/kugiri extract CHANGELOG.md --id "$VERSION" > /tmp/existing_section.md 2>/dev/null; then
            echo "Found existing $VERSION section, updating auto-generated parts directly in CHANGELOG"

            # Update the auto-generated-title directly in CHANGELOG
            echo "$TITLE" | ./target/release/kugiri update CHANGELOG.md \
              --id "auto-generated-title" \
              --write

            # Update the auto-generated-notes directly in CHANGELOG
            echo "$RELEASE_NOTES" | ./target/release/kugiri update CHANGELOG.md \
              --id "auto-generated-notes" \
              --write
          else
            echo "No existing $VERSION section found, creating new structure"

            # Create new section with wrapped auto-generated parts
            echo "$TITLE" | ./target/release/kugiri wrap --id "auto-generated-title" > /tmp/complete_entry.md
            echo "" >> /tmp/complete_entry.md
            # Create note with auto-generated-notes inside
            echo "$RELEASE_NOTES" | ./target/release/kugiri wrap --id "auto-generated-notes" | \
              ./target/release/kugiri wrap --id "note-$VERSION" >> /tmp/complete_entry.md

            # Insert new section after changelog marker
            ./target/release/kugiri insert CHANGELOG.md \
              --id "$VERSION" \
              --body-file /tmp/complete_entry.md \
              --after "changelog" \
              --write
          fi

          # Commit if there are changes
          git add CHANGELOG.md
          if git diff --cached --exit-code; then
            echo "No changes to CHANGELOG.md"
          else
            git commit -m "docs: Update CHANGELOG for $VERSION"
            git push
          fi

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Cache cargo dependencies
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

      - name: Install cargo-edit
        if: fromJSON(needs.release-pr.outputs.release_pr).next_tag != ''
        uses: taiki-e/cache-cargo-install-action@b33c63d3b3c85540f4eba8a4f71a5cc0ce030855 # v2.3.0
        with:
          tool: cargo-edit
      - name: Bump version in Cargo.{toml,lock}
        if: fromJSON(needs.release-pr.outputs.release_pr).next_tag != ''
        env:
          VERSION: ${{ fromJSON(needs.release-pr.outputs.release_pr).next_tag }}
        run: |
          # Remove 'v' prefix if present for cargo set-version
          VERSION=${VERSION#v}
          cargo set-version "$VERSION"
          git add Cargo.{toml,lock}
          if git diff --cached --exit-code; then
            echo "No changes to commit"
          else
            git commit -m "Bump version to $VERSION"
            git push
          fi

      - uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f # v2.0.5
      - uses: sigstore/cosign-installer@v3.9.2 # installs cosign
      - uses: anchore/sbom-action/download-syft@v0.20.5 # installs syft
      # Pre-install cargo-zigbuild before exposing MinGW to avoid Linux linker hijack
      - name: Preinstall cargo-zigbuild
        uses: taiki-e/cache-cargo-install-action@b33c63d3b3c85540f4eba8a4f71a5cc0ce030855 # v2.3.0
        with:
          tool: cargo-zigbuild
          locked: true
      # Provide dlltool for windows-gnu without hijacking cc/c++
      - name: Install mingw binutils (dlltool)
        uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # v1.5.3
        with:
          packages: binutils-mingw-w64
          version: 1.0
      - uses: goreleaser/goreleaser-action@v6
        id: goreleaser
        with:
          version: '~> v2'
          args: release --clean --draft --snapshot --skip publish
      - uses: actions/upload-artifact@v4
        with:
          name: snapshot
          path: ./dist

  verify-releaser:
    needs: [release-pr]
    if: fromJSON(needs.release-pr.outputs.release_pr).state == 'release_required'
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Harden the runner (Audit all outbound calls)
        uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
        with:
          egress-policy: audit
      - name: Verify Trusted Tag Releaser
        uses: actionutils/trusted-tag-verifier@68bef2f18f8ceb5c4dccc5542cadcfbc82ed4656 # v0.5.0
        with:
          verify: 'actionutils/trusted-go-releaser@v1'

  release:
    needs: [verify-releaser, release-pr]
    if: fromJSON(needs.release-pr.outputs.release_pr).state == 'release_required'
    concurrency:
      group: "release"
      cancel-in-progress: false
    permissions:
      id-token: write    # Required for signed tags
      contents: write    # Required for release and tag creation
      pull-requests: write # Required for bumpr commenting
      attestations: write # Required for build provenance attestation
      actions: read      # Required for SLSA generator
    uses: actionutils/trusted-go-releaser/.github/workflows/trusted-release-workflow.yml@v1
    with:
      environment: release
      setup-zig: true
      install-mingw-dlltool: true
    secrets:
      github-token: ${{ secrets.GITHUB_TOKEN }}

  publish:
    needs: [release-pr, release]
    if: fromJSON(needs.release-pr.outputs.release_pr).state == 'release_required'
    runs-on: ubuntu-latest
    permissions:
      id-token: write # Required for OIDC token exchange
    steps:
    - uses: actions/checkout@v5
    - uses: rust-lang/crates-io-auth-action@v1
      id: auth
    - run: cargo publish
      env:
        CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}