name: Release
on:
  push:
    tags:
      - "v*"
  workflow_dispatch:
jobs:
  build:
    name: Build release artifacts
    runs-on: self-hosted
    container:
      image: archlinux:latest
    env:
      CARGO_TERM_COLOR: always
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Initialise pacman
        run: |
          pacman-key --init
          pacman-key --populate archlinux
          pacman -Syu --noconfirm
      - name: Install build dependencies
        run: |
          pacman -S --noconfirm --needed base-devel rust pkgconf openssl git pacman-contrib fzf cmake jq restic unzip python3
      - name: Rust cache
        uses: Swatinem/rust-cache@v2
      - name: Create build user
        run: |
          useradd -m builder
          chown -R builder:builder "$PWD"
      - name: Determine target triple
        id: target
        run: |
          target=$(rustc -vV | awk '/^host:/ {print $2}')
          if [[ -z "$target" ]]; then
            echo "Failed to determine host target triple" >&2
            exit 1
          fi
          echo "triple=$target" >> "$GITHUB_OUTPUT"
      - name: Extract version
        id: vars
        run: |
          version=$(awk -F '"' '/^version =/ {print $2; exit}' Cargo.toml)
          if [[ -z "$version" ]]; then
            echo "Failed to determine version from Cargo.toml" >&2
            exit 1
          fi
          tag_version="${GITHUB_REF_NAME#v}"
          if [[ -n "$tag_version" && "$tag_version" != "$version" ]]; then
            echo "Tag version $tag_version does not match Cargo.toml version $version" >&2
            exit 1
          fi
          echo "version=$version" >> "$GITHUB_OUTPUT"
      - name: Run tests
        run: |
          WORKSPACE="$PWD"
          su builder -c "cd '$WORKSPACE' && CARGO_TERM_COLOR=always cargo test --locked"
      - name: Build release binary
        run: |
          WORKSPACE="$PWD"
          su builder -c "cd '$WORKSPACE' && CARGO_TERM_COLOR=always cargo build --release --locked"
      - name: Strip binary
        run: |
          WORKSPACE="$PWD"
          su builder -c "cd '$WORKSPACE' && strip target/release/ins"
      - name: Collect artifacts
        env:
          VERSION: ${{ steps.vars.outputs.version }}
          TARGET: ${{ steps.target.outputs.triple }}
        run: |
          mkdir -p artifacts
          pkg_dir="ins-${TARGET}-v${VERSION}"
          tmpdir=$(mktemp -d)
          mkdir -p "$tmpdir/$pkg_dir"
          install -Dm0755 "target/release/ins" "$tmpdir/$pkg_dir/ins"
          tar -czf "artifacts/${pkg_dir}.tgz" -C "$tmpdir" "$pkg_dir"
          sha256sum "artifacts/${pkg_dir}.tgz" > "artifacts/${pkg_dir}.tgz.sha256"
          rm -rf "$tmpdir"
      - name: Build AppImage
        env:
          VERSION: ${{ steps.vars.outputs.version }}
        run: |
          mkdir -p artifacts
          WORKSPACE="$PWD"
          su builder -c "cd '$WORKSPACE' && chmod +x utils/build_appimage.sh && ./utils/build_appimage.sh"
          install -Dm0644 "target/appimage/InstantCLI-${VERSION}-x86_64.AppImage" "artifacts/ins-${VERSION}-x86_64.AppImage"
          sha256sum "artifacts/ins-${VERSION}-x86_64.AppImage" > "artifacts/ins-${VERSION}-x86_64.AppImage.sha256"
      - name: Upload release package
        uses: actions/upload-artifact@v4
        with:
          name: ins-${{ steps.vars.outputs.version }}-${{ steps.target.outputs.triple }}
          path: |
            artifacts/ins-${{ steps.target.outputs.triple }}-v${{ steps.vars.outputs.version }}.tgz
            artifacts/ins-${{ steps.target.outputs.triple }}-v${{ steps.vars.outputs.version }}.tgz.sha256
            artifacts/ins-${{ steps.vars.outputs.version }}-x86_64.AppImage
            artifacts/ins-${{ steps.vars.outputs.version }}-x86_64.AppImage.sha256
  cross-compile:
    name: Cross-compile for ARM and musl targets
    runs-on: self-hosted
    container:
      image: ubuntu:24.04
    env:
      CARGO_TERM_COLOR: always
      DEBIAN_FRONTEND: noninteractive
      CARGO_HOME: /opt/cargo
      RUSTUP_HOME: /opt/rustup
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Install dependencies
        run: |
          apt-get update
          apt-get install -y --no-install-recommends \
            build-essential curl ca-certificates pkg-config \
            gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
            gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
            musl-tools \
            unzip
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
          echo "$CARGO_HOME/bin" >> "$GITHUB_PATH"
      - name: Add Rust cross-compilation targets
        run: |
          rustup target add \
            aarch64-unknown-linux-gnu \
            armv7-unknown-linux-gnueabihf \
            x86_64-unknown-linux-musl \
            aarch64-unknown-linux-musl \
            aarch64-linux-android
      - name: Install aarch64-linux-musl cross toolchain
        run: |
          curl -fsSL https://musl.cc/aarch64-linux-musl-cross.tgz | tar xz -C /opt
          echo "/opt/aarch64-linux-musl-cross/bin" >> "$GITHUB_PATH"
      - name: Install Android NDK
        run: |
          NDK_VERSION=r27c
          curl -fsSL "https://dl.google.com/android/repository/android-ndk-${NDK_VERSION}-linux.zip" -o /tmp/ndk.zip
          unzip -q /tmp/ndk.zip -d /opt
          rm /tmp/ndk.zip
          echo "ANDROID_NDK_HOME=/opt/android-ndk-${NDK_VERSION}" >> "$GITHUB_ENV"
          echo "ANDROID_NDK_ROOT=/opt/android-ndk-${NDK_VERSION}" >> "$GITHUB_ENV"
      - name: Extract version
        id: vars
        run: |
          version=$(awk -F '"' '/^version =/ {print $2; exit}' Cargo.toml)
          if [[ -z "$version" ]]; then
            echo "Failed to determine version from Cargo.toml" >&2
            exit 1
          fi
          echo "version=$version" >> "$GITHUB_OUTPUT"
      - name: Rust cache
        uses: Swatinem/rust-cache@v2
        with:
          key: cross-compile
      - name: Cross compile for ARM64 Linux
        env:
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
        run: cargo build --locked --release --features cross-compile --target aarch64-unknown-linux-gnu
      - name: Cross compile for ARMv7 Linux
        env:
          CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
        run: cargo build --locked --release --features cross-compile --target armv7-unknown-linux-gnueabihf
      - name: Cross compile for x86_64 musl
        env:
          CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
        run: cargo build --locked --release --features cross-compile --target x86_64-unknown-linux-musl
      - name: Cross compile for aarch64 musl
        env:
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: aarch64-linux-musl-gcc
        run: cargo build --locked --release --features cross-compile --target aarch64-unknown-linux-musl
      - name: Strip cross-compiled binaries
        run: |
          aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/ins
          arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/ins
          strip target/x86_64-unknown-linux-musl/release/ins
          aarch64-linux-musl-strip target/aarch64-unknown-linux-musl/release/ins
      - name: Install cargo-ndk
        run: cargo install cargo-ndk
      - name: Build for Android
        run: cargo ndk -t aarch64-linux-android build --release --locked --features cross-compile
      - name: Collect cross-compile artifacts
        env:
          VERSION: ${{ steps.vars.outputs.version }}
        run: |
          mkdir -p artifacts

          # ARM64 Linux
          pkg_dir_aarch64="ins-aarch64-unknown-linux-gnu-v${VERSION}"
          tmpdir_aarch64=$(mktemp -d)
          mkdir -p "$tmpdir_aarch64/$pkg_dir_aarch64"
          install -Dm0755 "target/aarch64-unknown-linux-gnu/release/ins" "$tmpdir_aarch64/$pkg_dir_aarch64/ins"
          tar -czf "artifacts/${pkg_dir_aarch64}.tgz" -C "$tmpdir_aarch64" "$pkg_dir_aarch64"
          sha256sum "artifacts/${pkg_dir_aarch64}.tgz" > "artifacts/${pkg_dir_aarch64}.tgz.sha256"
          rm -rf "$tmpdir_aarch64"

          # ARMv7 Linux
          pkg_dir_armv7="ins-armv7-unknown-linux-gnueabihf-v${VERSION}"
          tmpdir_armv7=$(mktemp -d)
          mkdir -p "$tmpdir_armv7/$pkg_dir_armv7"
          install -Dm0755 "target/armv7-unknown-linux-gnueabihf/release/ins" "$tmpdir_armv7/$pkg_dir_armv7/ins"
          tar -czf "artifacts/${pkg_dir_armv7}.tgz" -C "$tmpdir_armv7" "$pkg_dir_armv7"
          sha256sum "artifacts/${pkg_dir_armv7}.tgz" > "artifacts/${pkg_dir_armv7}.tgz.sha256"
          rm -rf "$tmpdir_armv7"

          # x86_64 musl (static)
          pkg_dir_musl_x86="ins-x86_64-unknown-linux-musl-v${VERSION}"
          tmpdir_musl_x86=$(mktemp -d)
          mkdir -p "$tmpdir_musl_x86/$pkg_dir_musl_x86"
          install -Dm0755 "target/x86_64-unknown-linux-musl/release/ins" "$tmpdir_musl_x86/$pkg_dir_musl_x86/ins"
          tar -czf "artifacts/${pkg_dir_musl_x86}.tgz" -C "$tmpdir_musl_x86" "$pkg_dir_musl_x86"
          sha256sum "artifacts/${pkg_dir_musl_x86}.tgz" > "artifacts/${pkg_dir_musl_x86}.tgz.sha256"
          rm -rf "$tmpdir_musl_x86"

          # aarch64 musl (static)
          pkg_dir_musl_arm="ins-aarch64-unknown-linux-musl-v${VERSION}"
          tmpdir_musl_arm=$(mktemp -d)
          mkdir -p "$tmpdir_musl_arm/$pkg_dir_musl_arm"
          install -Dm0755 "target/aarch64-unknown-linux-musl/release/ins" "$tmpdir_musl_arm/$pkg_dir_musl_arm/ins"
          tar -czf "artifacts/${pkg_dir_musl_arm}.tgz" -C "$tmpdir_musl_arm" "$pkg_dir_musl_arm"
          sha256sum "artifacts/${pkg_dir_musl_arm}.tgz" > "artifacts/${pkg_dir_musl_arm}.tgz.sha256"
          rm -rf "$tmpdir_musl_arm"

          # Android ARM64 (Termux)
          pkg_dir_termux="ins-aarch64-termux-v${VERSION}"
          tmpdir_termux=$(mktemp -d)
          mkdir -p "$tmpdir_termux/$pkg_dir_termux"
          install -Dm0755 "target/aarch64-linux-android/release/ins" "$tmpdir_termux/$pkg_dir_termux/ins"
          tar -czf "artifacts/${pkg_dir_termux}.tgz" -C "$tmpdir_termux" "$pkg_dir_termux"
          sha256sum "artifacts/${pkg_dir_termux}.tgz" > "artifacts/${pkg_dir_termux}.tgz.sha256"
          rm -rf "$tmpdir_termux"
      - name: Upload cross-compile artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ins-${{ steps.vars.outputs.version }}-cross-binaries
          path: |
            artifacts/ins-aarch64-unknown-linux-gnu-v${{ steps.vars.outputs.version }}.tgz
            artifacts/ins-aarch64-unknown-linux-gnu-v${{ steps.vars.outputs.version }}.tgz.sha256
            artifacts/ins-armv7-unknown-linux-gnueabihf-v${{ steps.vars.outputs.version }}.tgz
            artifacts/ins-armv7-unknown-linux-gnueabihf-v${{ steps.vars.outputs.version }}.tgz.sha256
            artifacts/ins-x86_64-unknown-linux-musl-v${{ steps.vars.outputs.version }}.tgz
            artifacts/ins-x86_64-unknown-linux-musl-v${{ steps.vars.outputs.version }}.tgz.sha256
            artifacts/ins-aarch64-unknown-linux-musl-v${{ steps.vars.outputs.version }}.tgz
            artifacts/ins-aarch64-unknown-linux-musl-v${{ steps.vars.outputs.version }}.tgz.sha256
            artifacts/ins-aarch64-termux-v${{ steps.vars.outputs.version }}.tgz
            artifacts/ins-aarch64-termux-v${{ steps.vars.outputs.version }}.tgz.sha256
      - name: Clean up toolchains
        if: always()
        run: rm -rf /opt/cargo /opt/rustup /opt/android-ndk-* /opt/aarch64-linux-musl-cross
  pkgbuild:
    name: Build Arch package
    runs-on: self-hosted
    container:
      image: archlinux:latest
    needs: build
    steps:
      - name: Initialise pacman
        run: |
          pacman-key --init
          pacman-key --populate archlinux
          pacman -Syu --noconfirm
      - name: Install build dependencies
        run: |
          pacman -S --noconfirm --needed base-devel rust pkgconf openssl git pacman-contrib cmake fzf sqlite sudo
      - name: Checkout
        uses: actions/checkout@v4
      - name: Extract version
        id: vars
        run: |
          version=$(awk -F '"' '/^version =/ {print $2; exit}' Cargo.toml)
          if [[ -z "$version" ]]; then
            echo "Failed to determine version from Cargo.toml" >&2
            exit 1
          fi
          echo "version=$version" >> "$GITHUB_OUTPUT"
      - name: Sync PKGBUILD version
        env:
          VERSION: ${{ steps.vars.outputs.version }}
        run: |
          sed -i "s/^pkgver=.*/pkgver=$VERSION/" pkgbuild/ins/PKGBUILD
          sed -i "s/^pkgrel=.*/pkgrel=1/" pkgbuild/ins/PKGBUILD
      - name: Create build user
        run: |
          useradd -m builder
          echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
          chown -R builder:builder "$PWD"
      - name: Build package
        run: |
          cd pkgbuild/ins
          su builder -c 'makepkg -s --noconfirm'
      - name: Collect package artifacts
        env:
          VERSION: ${{ steps.vars.outputs.version }}
        run: |
          mkdir -p artifacts
          shopt -s nullglob
          for pkg in pkgbuild/ins/*.pkg.tar.zst; do mv "$pkg" artifacts/; done
          for src in pkgbuild/ins/*.src.tar.zst; do mv "$src" artifacts/; done
      - name: Upload Arch package
        uses: actions/upload-artifact@v4
        with:
          name: ins-${{ steps.vars.outputs.version }}-arch-packages
          path: artifacts/
  deb:
    name: Build Debian package
    runs-on: self-hosted
    container:
      image: ubuntu:24.04
    needs: build
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install dependencies
        run: apt-get update && apt-get install -y dpkg-dev
      - name: Extract version
        id: vars
        run: |
          version=$(awk -F '"' '/^version =/ {print $2; exit}' Cargo.toml)
          if [[ -z "$version" ]]; then
            echo "Failed to determine version from Cargo.toml" >&2
            exit 1
          fi
          echo "version=$version" >> "$GITHUB_OUTPUT"
      - name: Download native build artifact
        uses: actions/download-artifact@v4
        with:
          name: ins-${{ steps.vars.outputs.version }}-x86_64-unknown-linux-gnu
          path: dist
      - name: Extract binary from tarball
        env:
          VERSION: ${{ steps.vars.outputs.version }}
        run: |
          tar -xzf "dist/ins-x86_64-unknown-linux-gnu-v${VERSION}.tgz" -C dist
          install -Dm0755 "dist/ins-x86_64-unknown-linux-gnu-v${VERSION}/ins" target/release/ins
      - name: Build .deb package
        run: |
          chmod +x utils/build_deb.sh
          ./utils/build_deb.sh
      - name: Collect deb artifacts
        env:
          VERSION: ${{ steps.vars.outputs.version }}
        run: |
          mkdir -p artifacts
          mv target/deb/ins_${VERSION}_amd64.deb artifacts/
          cd artifacts && sha256sum ins_${VERSION}_amd64.deb > ins_${VERSION}_amd64.deb.sha256
      - name: Upload deb package
        uses: actions/upload-artifact@v4
        with:
          name: ins-${{ steps.vars.outputs.version }}-deb
          path: artifacts/
  release:
    name: Publish GitHub release
    runs-on: self-hosted
    container:
      image: ubuntu:24.04
    needs:
      - build
      - cross-compile
      - pkgbuild
      - deb
    permissions:
      contents: write
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          path: dist
          merge-multiple: true
      - name: Publish release
        uses: softprops/action-gh-release@v2
        with:
          files: dist/**/*
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
