name: "CI"

on:
  pull_request:
  merge_group:
  push:
  workflow_dispatch:
    inputs:
      dogfood:
        description: 'Use dogfood Nix build'
        required: false
        default: true
        type: boolean

permissions: read-all

jobs:
  eval:
    runs-on: ubuntu-24.04
    steps:
    - uses: actions/checkout@v5
      with:
        fetch-depth: 0
    - uses: ./.github/actions/install-nix-action
      with:
        dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
        extra_nix_config:
          experimental-features = nix-command flakes
        github_token: ${{ secrets.GITHUB_TOKEN }}
    - run: nix flake show --all-systems --json

  pre-commit-checks:
    name: pre-commit checks
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v5
      - uses: ./.github/actions/install-nix-action
        with:
          dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
          extra_nix_config: experimental-features = nix-command flakes
          github_token: ${{ secrets.GITHUB_TOKEN }}
      - uses: DeterminateSystems/magic-nix-cache-action@main
      - run: ./ci/gha/tests/pre-commit-checks

  basic-checks:
    name: aggregate basic checks
    if: ${{ always() }}
    runs-on: ubuntu-24.04
    needs: [pre-commit-checks, eval]
    steps:
      - name: Exit with any errors
        if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
        run: |
          exit 1

  tests:
    needs: basic-checks
    strategy:
      fail-fast: false
      matrix:
        include:
          - scenario: on ubuntu
            runs-on: ubuntu-24.04
            os: linux
            instrumented: false
            primary: true
            stdenv: stdenv
          - scenario: on macos
            runs-on: macos-14
            os: darwin
            instrumented: false
            primary: true
            stdenv: stdenv
          - scenario: on ubuntu (with sanitizers / coverage)
            runs-on: ubuntu-24.04
            os: linux
            instrumented: true
            primary: false
            stdenv: clangStdenv
    name: tests ${{ matrix.scenario }}
    runs-on: ${{ matrix.runs-on }}
    timeout-minutes: 60
    steps:
    - uses: actions/checkout@v5
      with:
        fetch-depth: 0
    - uses: ./.github/actions/install-nix-action
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
        # The sandbox would otherwise be disabled by default on Darwin
        extra_nix_config: "sandbox = true"
    - uses: DeterminateSystems/magic-nix-cache-action@main
    # Since ubuntu 22.30, unprivileged usernamespaces are no longer allowed to map to the root user:
    # https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
    - run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
      if: matrix.os == 'linux'
    - name: Run component tests
      run: |
        nix build --file ci/gha/tests/wrapper.nix componentTests -L \
          --arg withInstrumentation ${{ matrix.instrumented }} \
          --argstr stdenv "${{ matrix.stdenv }}"
    - name: Run flake checks and prepare the installer tarball
      run: |
        ci/gha/tests/build-checks
        ci/gha/tests/prepare-installer-for-github-actions
      if: ${{ matrix.primary }}
    - name: Collect code coverage
      run: |
        nix build --file ci/gha/tests/wrapper.nix codeCoverage.coverageReports -L \
          --arg withInstrumentation ${{ matrix.instrumented }} \
          --argstr stdenv "${{ matrix.stdenv }}" \
          --out-link coverage-reports
        cat coverage-reports/index.txt >> $GITHUB_STEP_SUMMARY
      if: ${{ matrix.instrumented }}
    - name: Upload coverage reports
      uses: actions/upload-artifact@v4
      with:
        name: coverage-reports
        path: coverage-reports/
      if: ${{ matrix.instrumented }}
    - name: Upload installer tarball
      uses: actions/upload-artifact@v4
      with:
        name: installer-${{matrix.os}}
        path: out/*
      if: ${{ matrix.primary }}

  installer_test:
    needs: [tests]
    strategy:
      fail-fast: false
      matrix:
        include:
          - scenario: on ubuntu
            runs-on: ubuntu-24.04
            os: linux
          - scenario: on macos
            runs-on: macos-14
            os: darwin
    name: installer test ${{ matrix.scenario }}
    runs-on: ${{ matrix.runs-on }}
    steps:
    - uses: actions/checkout@v5
    - name: Download installer tarball
      uses: actions/download-artifact@v5
      with:
        name: installer-${{matrix.os}}
        path: out
    - name: Looking up the installer tarball URL
      id: installer-tarball-url
      run: echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
    - uses: cachix/install-nix-action@v31
      with:
        install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
        install_options: ${{ format('--tarball-url-prefix {0}', steps.installer-tarball-url.outputs.installer-url) }}
    - run: sudo apt install fish zsh
      if: matrix.os == 'linux'
    - run: brew install fish
      if: matrix.os == 'darwin'
    - run: exec bash -c "nix-instantiate -E 'builtins.currentTime' --eval"
    - run: exec sh -c "nix-instantiate -E 'builtins.currentTime' --eval"
    - run: exec zsh -c "nix-instantiate -E 'builtins.currentTime' --eval"
    - run: exec fish -c "nix-instantiate -E 'builtins.currentTime' --eval"
    - run: exec bash -c "nix-channel --add https://releases.nixos.org/nixos/unstable/nixos-23.05pre466020.60c1d71f2ba nixpkgs"
    - run: exec bash -c "nix-channel --update && nix-env -iA nixpkgs.hello && hello"

  # Steps to test CI automation in your own fork.
  # 1. Sign-up for https://hub.docker.com/
  # 2. Store your dockerhub username as DOCKERHUB_USERNAME in "Repository secrets" of your fork repository settings (https://github.com/$githubuser/nix/settings/secrets/actions)
  # 3. Create an access token in https://hub.docker.com/settings/security and store it as DOCKERHUB_TOKEN in "Repository secrets" of your fork
  check_secrets:
    permissions:
      contents: none
    name: Check presence of secrets
    runs-on: ubuntu-24.04
    outputs:
      docker: ${{ steps.secret.outputs.docker }}
    steps:
      - name: Check for DockerHub secrets
        id: secret
        env:
          _DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }}
        run: |
          echo "docker=${{ env._DOCKER_SECRETS != '' }}" >> $GITHUB_OUTPUT

  docker_push_image:
    needs: [tests, vm_tests, check_secrets]
    permissions:
      contents: read
      packages: write
    if: >-
      needs.check_secrets.outputs.docker == 'true' &&
      github.event_name == 'push' &&
      github.ref_name == 'master'
    runs-on: ubuntu-24.04
    steps:
    - uses: actions/checkout@v5
      with:
        fetch-depth: 0
    - uses: cachix/install-nix-action@v31
      with:
        install_url: https://releases.nixos.org/nix/nix-2.20.3/install
    - uses: DeterminateSystems/magic-nix-cache-action@main
    - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#nix.version | tr -d \")" >> $GITHUB_ENV
    - run: nix --experimental-features 'nix-command flakes' build .#dockerImage -L
    - run: docker load -i ./result/image.tar.gz
    - run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION
    - run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:master
    # We'll deploy the newly built image to both Docker Hub and Github Container Registry.
    #
    # Push to Docker Hub first
    - name: Login to Docker Hub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}
    - run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION
    - run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/nix:master
    # Push to GitHub Container Registry as well
    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    - name: Push image
      run: |
        IMAGE_ID=ghcr.io/${{ github.repository_owner }}/nix
        # Change all uppercase to lowercase
        IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')

        docker tag nix:$NIX_VERSION $IMAGE_ID:$NIX_VERSION
        docker tag nix:$NIX_VERSION $IMAGE_ID:latest
        docker push $IMAGE_ID:$NIX_VERSION
        docker push $IMAGE_ID:latest
        # deprecated 2024-02-24
        docker tag nix:$NIX_VERSION $IMAGE_ID:master
        docker push $IMAGE_ID:master

  vm_tests:
    needs: basic-checks
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v5
      - uses: ./.github/actions/install-nix-action
        with:
          dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
          extra_nix_config:
            experimental-features = nix-command flakes
          github_token: ${{ secrets.GITHUB_TOKEN }}
      - uses: DeterminateSystems/magic-nix-cache-action@main
      - run: |
          nix build -L \
            .#hydraJobs.tests.functional_user \
            .#hydraJobs.tests.githubFlakes \
            .#hydraJobs.tests.nix-docker \
            .#hydraJobs.tests.tarballFlakes \
            ;

  flake_regressions:
    needs: vm_tests
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout nix
        uses: actions/checkout@v5
      - name: Checkout flake-regressions
        uses: actions/checkout@v5
        with:
          repository: NixOS/flake-regressions
          path: flake-regressions
      - name: Checkout flake-regressions-data
        uses: actions/checkout@v5
        with:
          repository: NixOS/flake-regressions-data
          path: flake-regressions/tests
      - uses: ./.github/actions/install-nix-action
        with:
          dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
          extra_nix_config:
            experimental-features = nix-command flakes
          github_token: ${{ secrets.GITHUB_TOKEN }}
      - uses: DeterminateSystems/magic-nix-cache-action@main
      - run: nix build -L --out-link ./new-nix && PATH=$(pwd)/new-nix/bin:$PATH MAX_FLAKES=25 flake-regressions/eval-all.sh

  profile_build:
    needs: tests
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    if: >-
      github.event_name == 'push' &&
      github.ref_name == 'master'
    steps:
    - uses: actions/checkout@v5
      with:
        fetch-depth: 0
    - uses: ./.github/actions/install-nix-action
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
        extra_nix_config: |
          experimental-features = flakes nix-command ca-derivations impure-derivations
          max-jobs = 1
    - uses: DeterminateSystems/magic-nix-cache-action@main
    - run: |
        nix build -L --file ./ci/gha/profile-build buildTimeReport --out-link build-time-report.md
        cat build-time-report.md >> $GITHUB_STEP_SUMMARY
