name: Publish to Launchpad PPA

on:
  push:
    tags: ["v*"]
  workflow_dispatch:
    inputs:
      version:
        description: "Version to publish (e.g. 0.1.6). Defaults to the tag."
        required: false
      series:
        description: "Comma-separated Ubuntu series (e.g. resolute,noble,jammy). Default: resolute,noble,jammy"
        required: false
        default: "resolute,noble,jammy"

jobs:
  resolve:
    runs-on: ubuntu-24.04
    outputs:
      version: ${{ steps.v.outputs.version }}
      series_matrix: ${{ steps.v.outputs.series_matrix }}
    env:
      INPUT_VERSION: ${{ inputs.version }}
      INPUT_SERIES: ${{ inputs.series }}
      REF_NAME: ${{ github.ref_name }}
    steps:
      - id: v
        run: |
          set -eu
          if [ -n "${INPUT_VERSION:-}" ]; then
            VERSION="$INPUT_VERSION"
          else
            VERSION="${REF_NAME#v}"
          fi
          # Strict version sanity check: digits and dots only, plus optional suffix.
          if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+(\.[0-9]+){0,3}([a-zA-Z0-9.+~-]*)$'; then
            echo "Refusing unsafe version string: $VERSION" >&2
            exit 1
          fi
          SERIES="${INPUT_SERIES:-resolute,noble,jammy}"
          # Whitelist series names (alnum only) before passing them downstream.
          MATRIX=$(printf '%s' "$SERIES" \
            | tr ',' '\n' \
            | sed 's/^ *//; s/ *$//' \
            | grep -E '^[a-z][a-z0-9]+$' \
            | jq -R . | jq -sc .)
          if [ "$MATRIX" = "[]" ]; then
            echo "No valid series after whitelist filter: $SERIES" >&2
            exit 1
          fi
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
          echo "series_matrix=$MATRIX" >> "$GITHUB_OUTPUT"
          echo "Publishing version=$VERSION to series=$MATRIX"

  publish:
    needs: resolve
    runs-on: ubuntu-24.04
    strategy:
      fail-fast: false
      matrix:
        series: ${{ fromJSON(needs.resolve.outputs.series_matrix) }}
    env:
      VERSION: ${{ needs.resolve.outputs.version }}
      SERIES: ${{ matrix.series }}
      DEBEMAIL: spiderpower02@gmail.com
      DEBFULLNAME: Chang Ning Tsai
      PPA: ppa:crazyguitar/rdmatop
      KEY_ID: ${{ secrets.LAUNCHPAD_GPG_KEY_ID }}
    steps:
      - uses: actions/checkout@v7

      - name: Install build tooling
        run: |
          sudo apt-get update -qq
          sudo apt-get install -y -qq --no-install-recommends \
            devscripts debhelper dput build-essential cargo-1.91 rustc-1.91 gnupg jq
          /usr/lib/rust-1.91/bin/cargo --version
          /usr/lib/rust-1.91/bin/rustc --version

      - name: Import GPG signing key
        env:
          LAUNCHPAD_GPG_PRIVATE_KEY: ${{ secrets.LAUNCHPAD_GPG_PRIVATE_KEY }}
        run: |
          set -eu
          mkdir -p ~/.gnupg && chmod 700 ~/.gnupg
          printf '%s' "$LAUNCHPAD_GPG_PRIVATE_KEY" | gpg --batch --import
          gpg --list-secret-keys --keyid-format=long
          # Trust the imported key so debuild can use it non-interactively.
          printf '5\ny\n' | gpg --batch --command-fd 0 --expert --edit-key "$KEY_ID" trust quit || true

      - name: Vendor Rust dependencies
        env:
          PATH: /usr/lib/rust-1.91/bin:/usr/bin:/bin
        run: |
          set -eu
          rm -f Cargo.lock
          cargo vendor vendor/ > /dev/null
          mkdir -p .cargo
          cat > .cargo/config.toml <<'EOF'
          [source.crates-io]
          replace-with = "vendored-sources"

          [source.vendored-sources]
          directory = "vendor"
          EOF
          head -2 Cargo.lock

      - name: Regenerate debian/changelog
        run: |
          set -eu
          rm -f debian/changelog
          dch --create --package rdmatop \
              --newversion "${VERSION}-1ppa1~${SERIES}1" \
              --distribution "$SERIES" \
              "Release ${VERSION} for ${SERIES}."
          head -n 5 debian/changelog

      - name: Create upstream orig tarball
        run: |
          set -eu
          # 3.0 (quilt) source format requires ../rdmatop_<upstream>.orig.tar.gz.
          # The orig tarball is shared across all matrix series uploads, so it
          # MUST be byte-identical regardless of which job builds it first.
          # Launchpad rejects re-uploads of the same orig name with different
          # SHA. We synthesise it deterministically: stable mtime, sorted
          # entries, fixed owner, gzip with no embedded timestamp.
          PKG_DIR="rdmatop-${VERSION}"
          mkdir -p "/tmp/${PKG_DIR}"
          rsync -a \
            --exclude='/.git' \
            --exclude='/.github' \
            --exclude='/debian' \
            --exclude='/.cargo-home' \
            --exclude='/target' \
            --exclude='/out' \
            ./ "/tmp/${PKG_DIR}/"
          # Pin every file's mtime to the tag's commit time so the tarball
          # contents are reproducible across matrix jobs.
          SOURCE_DATE_EPOCH="$(git -C ./ log -1 --pretty=%ct 2>/dev/null || echo 0)"
          export SOURCE_DATE_EPOCH
          find "/tmp/${PKG_DIR}" -exec touch -h -d "@${SOURCE_DATE_EPOCH}" {} +
          tar --sort=name \
              --mtime="@${SOURCE_DATE_EPOCH}" \
              --owner=0 --group=0 --numeric-owner \
              --format=gnu \
              -cf - -C /tmp "${PKG_DIR}" \
            | gzip -n -9 > "../rdmatop_${VERSION}.orig.tar.gz"
          rm -rf "/tmp/${PKG_DIR}"
          sha256sum "../rdmatop_${VERSION}.orig.tar.gz"

      - name: Strip CI metadata from working tree
        run: |
          set -eu
          # The orig tarball excludes .github/, so the working tree must too,
          # otherwise dpkg-source -b reports "unexpected upstream changes".
          # .git/ is auto-ignored by dpkg-source so it can stay.
          rm -rf .github

      - name: Build source package
        run: |
          set -eu
          dpkg-buildpackage --build=source -sa -us -uc -ui
          ls -la ../

      - name: Sign source package
        run: |
          set -eu
          debsign -k"$KEY_ID" \
            "../rdmatop_${VERSION}-1ppa1~${SERIES}1_source.changes"

      - name: Stage source artifacts
        if: always()
        run: |
          set -eu
          mkdir -p out
          cp -v ../rdmatop_*.dsc       out/ 2>/dev/null || true
          cp -v ../rdmatop_*.tar.*     out/ 2>/dev/null || true
          cp -v ../rdmatop_*.changes   out/ 2>/dev/null || true
          cp -v ../rdmatop_*.buildinfo out/ 2>/dev/null || true
          ls -la out/

      - name: Upload to Launchpad PPA
        run: |
          set -eu
          dput "$PPA" "out/rdmatop_${VERSION}-1ppa1~${SERIES}1_source.changes"

      - name: Stash source artifacts
        if: always()
        uses: actions/upload-artifact@v7
        with:
          name: ppa-source-${{ matrix.series }}
          path: out/
          if-no-files-found: warn
