name: CI and Artifacts

on:
    pull_request:
    push:
        branches:
            - main
            - dev

    # Allows you to run this workflow manually from the Actions tab
    workflow_dispatch:

env:
    # Required to make some things output color
    TERM: ansi
    # Publishing to my nix binary cache
    ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN }}
    # Just in case incremental is still being set to true, speeds up CI
    CARGO_INCREMENTAL: 0
    # Custom nix binary cache if fork is being used
    ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
    ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}

permissions:
    packages: write
    contents: read

jobs:
    tests:
        name: CI and Tests
        runs-on: ubuntu-latest

        steps:
            - name: Sync repository
              uses: actions/checkout@v4

            - name: Install Nix (with flakes and nix-command enabled)
              uses: cachix/install-nix-action@v26
              with:
                  nix_path: nixpkgs=channel:nixos-unstable

                  # Add `nix-community`, Crane, upstream Conduit, and conduwuit binary caches
                  extra_nix_config: |
                      experimental-features = nix-command flakes
                      extra-substituters = https://nix-community.cachix.org
                      extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=
                      extra-substituters = https://crane.cachix.org
                      extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=
                      extra-substituters = https://nix.computer.surgery/conduit
                      extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=
                      extra-substituters = https://attic.kennel.juneis.dog/conduit
                      extra-trusted-public-keys = conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=
                      extra-substituters = https://attic.kennel.juneis.dog/conduwuit
                      extra-trusted-public-keys = conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=

            - name: Add alternative Nix binary caches if specified
              if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
              run: |
                  echo "extra-substituters = ${{ env.ATTIC_ENDPOINT }}" >> /etc/nix/nix.conf
                  echo "extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}" >> /etc/nix/nix.conf

            - name: Pop/push Magic Nix Cache
              uses: DeterminateSystems/magic-nix-cache-action@main

            - name: Configure `nix-direnv`
              run: |
                  echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"

            - name: Install `direnv` and `nix-direnv`
              run: nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv

            - name: Pop/push downloaded crate cache
              uses: actions/cache@v4
              with:
                  key: downloaded-crates
                  path: ~/.cargo

            - name: Pop/push compiled crate cache
              uses: actions/cache@v4
              with:
                  key: compiled-crates-${{runner.os}}
                  path: target

            # Do this to shorten the logs for the real CI step
            - name: Populate `/nix/store`
              run: nix develop --command true

            - name: Allow direnv
              run: direnv allow

            - name: Cache x86_64 inputs for devShell
              run: |
                  ./bin/nix-build-and-cache .#devShells.x86_64-linux.default.inputDerivation

            - name: Perform continuous integration
              run: direnv exec . engage

    build-static:
        name: Static Artifacts
        runs-on: ubuntu-latest
        needs: tests
        strategy:
            max-parallel: 8
            matrix:
                target:
                    [
                        "x86_64-unknown-linux-musl",
                        "x86_64-unknown-linux-musl-jemalloc",
                        "aarch64-unknown-linux-musl",
                        "aarch64-unknown-linux-musl-jemalloc",
                    ]

        steps:
            - name: Sync repository
              uses: actions/checkout@v4

            - name: Install Nix (with flakes and nix-command enabled)
              uses: cachix/install-nix-action@v26
              with:
                  nix_path: nixpkgs=channel:nixos-unstable

                  # Add `nix-community`, Crane, upstream Conduit, and conduwuit binary caches
                  extra_nix_config: |
                      experimental-features = nix-command flakes
                      extra-substituters = https://nix-community.cachix.org
                      extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=
                      extra-substituters = https://crane.cachix.org
                      extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=
                      extra-substituters = https://nix.computer.surgery/conduit
                      extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=
                      extra-substituters = https://attic.kennel.juneis.dog/conduit
                      extra-trusted-public-keys = conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=
                      extra-substituters = https://attic.kennel.juneis.dog/conduwuit
                      extra-trusted-public-keys = conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=

            - name: Add alternative Nix binary caches if specified
              if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
              run: |
                  echo "extra-substituters = ${{ env.ATTIC_ENDPOINT }}" >> /etc/nix/nix.conf
                  echo "extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}" >> /etc/nix/nix.conf

            - name: Pop/push Magic Nix Cache
              uses: DeterminateSystems/magic-nix-cache-action@main

            - name: Configure `nix-direnv`
              run: |
                  echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"

            - name: Install `direnv` and `nix-direnv`
              run: nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv

            - name: Pop/push downloaded crate cache
              uses: actions/cache@v4
              with:
                  key: downloaded-crates
                  path: ~/.cargo

            - name: Pop/push compiled crate cache
              uses: actions/cache@v4
              with:
                  key: compiled-crates-${{runner.os}}
                  path: target

            # Do this to shorten the logs for the real CI step
            - name: Populate `/nix/store`
              run: nix develop --command true

            - name: Allow direnv
              run: direnv allow

            - name: Cache x86_64 inputs for devShell
              run: |
                  ./bin/nix-build-and-cache .#devShells.x86_64-linux.default.inputDerivation

            - name: Build static artifacts
              run: |
                  ./bin/nix-build-and-cache .#static-${{ matrix.target }}
                  mkdir -p target/release
                  cp -v -f result/bin/conduit target/release
                  direnv exec . cargo deb --no-build --no-strip --output target/debian/${{ matrix.target }}.deb

            - name: Upload static artifacts
              uses: actions/upload-artifact@v4
              with:
                  name: static-${{ matrix.target }}
                  path: result/bin/conduit
                  if-no-files-found: error

            - name: Upload static deb artifacts
              uses: actions/upload-artifact@v4
              with:
                  name: ${{ matrix.target }}.deb
                  path: target/debian/${{ matrix.target }}.deb
                  if-no-files-found: error

    build-oci:
        name: OCI Artifacts and Publish
        runs-on: ubuntu-latest
        needs: tests
        strategy:
            max-parallel: 8
            matrix:
                oci-target:
                    [
                        "x86_64-unknown-linux-musl",
                        "x86_64-unknown-linux-musl-jemalloc",
                        "aarch64-unknown-linux-musl",
                        "aarch64-unknown-linux-musl-jemalloc",
                    ]

        steps:
            - name: Sync repository
              uses: actions/checkout@v4

            - name: Install Nix (with flakes and nix-command enabled)
              uses: cachix/install-nix-action@v26
              with:
                  nix_path: nixpkgs=channel:nixos-unstable

                  # Add `nix-community`, Crane, upstream Conduit, and conduwuit binary caches
                  extra_nix_config: |
                      experimental-features = nix-command flakes
                      extra-substituters = https://nix-community.cachix.org
                      extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=
                      extra-substituters = https://crane.cachix.org
                      extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=
                      extra-substituters = https://nix.computer.surgery/conduit
                      extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=
                      extra-substituters = https://attic.kennel.juneis.dog/conduit
                      extra-trusted-public-keys = conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=
                      extra-substituters = https://attic.kennel.juneis.dog/conduwuit
                      extra-trusted-public-keys = conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=

            - name: Add alternative Nix binary caches if specified
              if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
              run: |
                  echo "extra-substituters = ${{ env.ATTIC_ENDPOINT }}" >> /etc/nix/nix.conf
                  echo "extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}" >> /etc/nix/nix.conf

            - name: Pop/push Magic Nix Cache
              uses: DeterminateSystems/magic-nix-cache-action@main

            - name: Configure `nix-direnv`
              run: |
                  echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"

            - name: Install `direnv` and `nix-direnv`
              run: nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv

            - name: Pop/push downloaded crate cache
              uses: actions/cache@v4
              with:
                  key: downloaded-crates
                  path: ~/.cargo

            - name: Pop/push compiled crate cache
              uses: actions/cache@v4
              with:
                  key: compiled-crates-${{runner.os}}
                  path: target

            # Do this to shorten the logs for the real CI step
            - name: Populate `/nix/store`
              run: nix develop --command true

            - name: Allow direnv
              run: direnv allow

            - name: Cache x86_64 inputs for devShell
              run: |
                  ./bin/nix-build-and-cache .#devShells.x86_64-linux.default.inputDerivation

            - name: Build OCI images
              run: |
                  ./bin/nix-build-and-cache .#oci-image-${{ matrix.oci-target }}
                  cp -v -f result oci-image-${{ matrix.oci-target }}.tar.gz

            - name: Upload OCI image artifacts
              uses: actions/upload-artifact@v4
              with:
                  name: oci-image-${{ matrix.oci-target }}
                  path: oci-image-${{ matrix.oci-target }}.tar.gz
                  if-no-files-found: error
                  # don't compress again
                  compression-level: 0

    create-and-push-manifest:
        name: Create and Push Docker Manifest
        runs-on: ubuntu-latest
        needs: build-oci
        if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
        steps:
            - name: Download OCI Images Artifacts
              uses: actions/download-artifact@v4
              with:
                  merge-multiple: true

            - name: Load OCI Images and tag
              run: |
                  ID_AARCH64=$(docker load -i oci-image-aarch64-unknown-linux-musl-jemalloc.tar.gz | sed -n 's/Loaded image: \(.*\)/\1/p')
                  docker tag $ID_AARCH64 conduwuit:${{ github.sha }}-aarch64-jemalloc
                  ID_X86_64=$(docker load -i oci-image-x86_64-unknown-linux-musl-jemalloc.tar.gz | sed -n 's/Loaded image: \(.*\)/\1/p')
                  docker tag $ID_X86_64 conduwuit:${{ github.sha }}-x86_64-jemalloc

            - name: Login to GitHub Container Registry
              uses: docker/login-action@v3
              with:
                  registry: ghcr.io
                  username: ${{ github.actor }}
                  password: ${{ secrets.GITHUB_TOKEN }}

            - name: Create and push Manifest to GitHub
              run: |
                  REPO_GHCR="ghcr.io/${{ github.repository }}"
                  SHA_TAG="${{ github.ref_name }}-${{ github.sha }}"
                  BRANCH_TAG="${{ github.ref_name }}"
                  if [ "$BRANCH_TAG" == "main" ]; then
                    BRANCH_TAG="latest"
                  fi

                  docker manifest create $REPO_GHCR:$SHA_TAG \
                    --amend conduwuit:${{ github.sha }}-x86_64-jemalloc \
                    --amend conduwuit:${{ github.sha }}-aarch64-jemalloc
                  docker manifest push $REPO_GHCR:$SHA_TAG

                  docker manifest create $REPO_GHCR:$BRANCH_TAG \
                    --amend conduwuit:${{ github.sha }}-x86_64-jemalloc \
                    --amend conduwuit:${{ github.sha }}-aarch64-jemalloc
                  docker manifest push $REPO_GHCR:$BRANCH_TAG

            - name: Login to Docker Hub
              uses: docker/login-action@v3
              with:
                  username: ${{ secrets.DOCKER_USERNAME }}
                  password: ${{ secrets.DOCKERHUB_TOKEN }}

            - name: Create and push manifest to Docker Hub
              run: |
                  REPO_DOCKER="docker.io/${{ github.repository }}"
                  SHA_TAG="${{ github.ref_name }}-${{ github.sha }}"
                  BRANCH_TAG="${{ github.ref_name }}"
                  if [ "$BRANCH_TAG" == "main" ]; then
                    BRANCH_TAG="latest"
                  fi

                  docker manifest create $REPO_DOCKER:$SHA_TAG \
                    --amend conduwuit:${{ github.sha }}-x86_64-jemalloc \
                    --amend conduwuit:${{ github.sha }}-aarch64-jemalloc
                  docker manifest push $REPO_DOCKER:$SHA_TAG

                  docker manifest create $REPO_DOCKER:$BRANCH_TAG \
                    --amend conduwuit:${{ github.sha }}-x86_64-jemalloc \
                    --amend conduwuit:${{ github.sha }}-aarch64-jemalloc
                  docker manifest push $REPO_DOCKER:$BRANCH_TAG
