lab-ops 0.1.23

Personal utility tools for my homelab
Documentation
name: Release

on:
  workflow_call:
    inputs:
      binary-name:
        description: 'Binary name (e.g. tomo, lab-ops)'
        required: false
        type: string
        default: ''
      platforms:
        description: 'Build targets: multi (all 4) or linux (x64 only)'
        required: false
        type: string
        default: 'multi'
      build-binaries:
        description: 'Whether to build and release binaries'
        required: false
        type: boolean
        default: true
      publish-crates:
        description: 'Whether to publish to crates.io'
        required: false
        type: boolean
        default: false
    outputs:
      tag:
        description: 'The release tag that was created'
        value: ${{ jobs.publish-changelog.outputs.tag }}
    secrets:
      PRIVATE_TOKEN:
        required: true
      DISCORD_RELEASE_WEBHOOK_URL:
        required: false
      CARGO_REGISTRY_TOKEN:
        required: false

jobs:
  changelog:
    name: Generate Changelog
    runs-on: ubuntu-latest
    outputs:
      tag: ${{ steps.changelog.outputs.tag }}
      skipped: ${{ steps.changelog.outputs.skipped }}
      changelog: ${{ steps.changelog.outputs.changelog }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - name: Fetch All Tags
        run: git fetch --tags --force origin '+refs/tags/*:refs/tags/*'

      - name: Set Up version.json
        run: |
          LATEST_TAG=$(git tag -l 'v*' --sort=-version:refname | head -1)
          echo "{\"version\":\"${LATEST_TAG:-v0.0.0}\"}" > version.json

      - name: Install changelog dependencies
        run: |
          npm install conventional-changelog-conventionalcommits@7.0.2
          rm -f package.json package-lock.json

      - name: Generate Changelog (dry run)
        id: changelog
        uses: TriPSs/conventional-changelog-action@v6
        with:
          skip-commit: true
          skip-tag: true
          github-token: ${{ secrets.PRIVATE_TOKEN }}
          config-file-path: ./.github/.config.js
          version-file: ./version.json
          preset: conventionalcommits
          skip-version-file: true
          skip-git-pull: true
          skip-on-empty: false

  patch-version:
    name: Patch Version
    needs: changelog
    if: ${{ needs.changelog.outputs.skipped != 'true' }}
    uses: ./.github/workflows/patch-version.yml
    with:
      version: ${{ needs.changelog.outputs.tag }}

  build:
    name: Build Binaries
    needs: [changelog, patch-version]
    if: ${{ needs.changelog.outputs.skipped != 'true' && inputs.build-binaries }}
    uses: ./.github/workflows/build.yml
    with:
      cargo-build-args: '--release --all-features --all-targets'
      upload-binary: true
      release: true
      require-patched-cargo: true

  publish-crates:
    name: Publish to crates.io
    needs: [changelog, patch-version, build]
    if: |
      always() && 
      needs.changelog.outputs.skipped != 'true' && 
      inputs.publish-crates && 
      needs.changelog.result == 'success' &&
      (needs.build.result == 'success' || needs.build.result == 'skipped')
    runs-on: ubuntu-latest
    environment:
      name: crates-io-production
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Download patched Cargo.toml
        uses: actions/download-artifact@v4
        with:
          name: patched-cargo-toml
          path: .

      - name: Install Rust Toolchain
        uses: dtolnay/rust-toolchain@stable
      
      - name: Rust Cache
        uses: Swatinem/rust-cache@v2

      - name: Publish to crates.io
        uses: katyo/publish-crates@v2
        with:
          registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
          ignore-unpublished-changes: true
          args: --allow-dirty

  publish-changelog:
    name: Publish Changelog Commit
    needs: [changelog, build, publish-crates]
    if: |
      always() && 
      needs.changelog.outputs.skipped != 'true' && 
      needs.changelog.result == 'success' &&
      (needs.build.result == 'success' || needs.build.result == 'skipped') &&
      (needs.publish-crates.result == 'success' || needs.publish-crates.result == 'skipped')
    outputs:
      tag: ${{ steps.commit.outputs.tag }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - name: Fetch All Tags
        run: git fetch --tags --force origin '+refs/tags/*:refs/tags/*'

      - name: Set Up version.json
        run: |
          LATEST_TAG=$(git tag -l 'v*' --sort=-version:refname | head -1)
          echo "{\"version\":\"${LATEST_TAG:-v0.0.0}\"}" > version.json

      - name: Download patched Cargo.toml
        uses: actions/download-artifact@v4
        with:
          name: patched-cargo-toml
          path: .

      - name: Stage patched files
        run: |
          git add Cargo.toml
          git add crates/*/Cargo.toml 2>/dev/null || true

      - name: Install changelog dependencies
        run: |
          npm install conventional-changelog-conventionalcommits@7.0.2
          rm -f package.json package-lock.json

      - name: Commit, Tag, Push
        id: commit
        uses: TriPSs/conventional-changelog-action@v6
        with:
          github-token: ${{ secrets.PRIVATE_TOKEN }}
          git-user-name: "faz-bot"
          git-user-email: "mail@fazuh.com"
          config-file-path: ./.github/.config.js
          version-file: ./version.json
          preset: conventionalcommits
          skip-version-file: true
          skip-git-pull: true
          skip-on-empty: false

      - name: Sync version bump to main
        run: |
          git config user.name "faz-bot"
          git config user.email "mail@fazuh.com"
          git remote set-url origin "https://x-access-token:${{ secrets.PRIVATE_TOKEN }}@github.com/${{ github.repository }}"
          git fetch origin main
          git checkout -B main origin/main
          git merge origin/release --no-edit -m "chore(release): sync version bump to main [skip ci]"
          git push origin main

  release-github:
    name: Release to GitHub
    needs: [changelog, build, publish-changelog, publish-crates]
    if: |
      always() && 
      needs.changelog.outputs.skipped != 'true' && 
      needs.changelog.result == 'success' &&
      (needs.build.result == 'success' || needs.build.result == 'skipped') &&
      (needs.publish-crates.result == 'success' || needs.publish-crates.result == 'skipped') &&
      needs.publish-changelog.result == 'success'
    runs-on: ubuntu-latest
    steps:
      - name: Download Artifacts
        if: ${{ inputs.build-binaries }}
        uses: actions/download-artifact@v4
        with:
          path: artifacts
          pattern: ${{ inputs.binary-name }}-*

      - name: Prepare Release Files
        if: ${{ inputs.build-binaries }}
        run: |
          mkdir -p release
          cp artifacts/${{ inputs.binary-name }}-linux-x64/${{ inputs.binary-name }}-linux-x64 release/
          if [[ "${{ inputs.platforms }}" != "linux" ]]; then
            cp artifacts/${{ inputs.binary-name }}-windows-x64.exe/${{ inputs.binary-name }}-windows-x64.exe release/
            cp artifacts/${{ inputs.binary-name }}-macos-x64/${{ inputs.binary-name }}-macos-x64 release/
            cp artifacts/${{ inputs.binary-name }}-macos-arm64/${{ inputs.binary-name }}-macos-arm64 release/
          fi

      - name: Create Release
        id: release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.publish-changelog.outputs.tag }}
          body: ${{ needs.changelog.outputs.changelog }}
          files: release/*

      - name: Post Release on Discord
        uses: tsickert/discord-webhook@v6.0.0
        with:
          webhook-url: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
          embed-color: "9498256"
          embed-title: "${{ inputs.binary-name }} ${{ needs.publish-changelog.outputs.tag }}"
          embed-description: ${{ needs.changelog.outputs.changelog }}
          embed-url: ${{ steps.release.outputs.url }}
          embed-timestamp: ${{ github.event.head_commit.timestamp }}