# NOTE Github Actions execution environments lack a terminal, needed for
# some integration tests. So we use `script` command to fake a terminal.

name: ci
on:
  push:
    tags:
      - v*
    branches:
      - main
      - release-*
  pull_request:
permissions:
  contents: read

env:
  # Don't ignore C warnings. Note that the output of "go env CGO_CFLAGS" by default is "-g -O2", so we keep them.
  CGO_CFLAGS: -g -O2 -Werror

jobs:
  test:
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-20.04, ubuntu-24.04, actuated-arm64-6cpu-8gb]
        go-version: [1.22.x, 1.23.x]
        rootless: ["rootless", ""]
        race: ["-race", ""]
        criu: ["", "criu-dev"]
        exclude:
          # Disable most of criu-dev jobs, as they are expensive
          # (need to compile criu) and don't add much value/coverage.
          - criu: criu-dev
            go-version: 1.22.x
          - criu: criu-dev
            rootless: rootless
          - criu: criu-dev
            race: -race
          - go-version: 1.22.x
            os: actuated-arm64-6cpu-8gb
          - race: "-race"
            os: actuated-arm64-6cpu-8gb
          - criu: criu-dev
            os: actuated-arm64-6cpu-8gb

    runs-on: ${{ matrix.os }}

    steps:
# https://gist.github.com/alexellis/1f33e581c75e11e161fe613c46180771#file-metering-gha-md
# vmmeter start
    - name: Prepare arkade
      uses: alexellis/arkade-get@master
      if: matrix.os == 'actuated-arm64-6cpu-8gb'
      with:
        crane: latest
        print-summary: false

    - name: Install vmmeter
      if: matrix.os == 'actuated-arm64-6cpu-8gb'
      run: |
        crane export --platform linux/arm64 ghcr.io/openfaasltd/vmmeter:latest | sudo tar -xvf - -C /usr/local/bin

    - name: Run vmmeter
      uses: self-actuated/vmmeter-action@master
      if: matrix.os == 'actuated-arm64-6cpu-8gb'
# vmmeter end

    - name: checkout
      uses: actions/checkout@v4

    - name: Show host info
      run: |
        set -x
        # Sync `set -x` outputs with command ouputs
        exec 2>&1
        # Version
        uname -a
        cat /etc/os-release
        # Hardware
        cat /proc/cpuinfo
        free -mt
        # cgroup
        ls -F /sys/fs/cgroup
        cat /proc/self/cgroup
        if [ -e /sys/fs/cgroup/cgroup.controllers ]; then
          cat /sys/fs/cgroup/cgroup.controllers
          cat /sys/fs/cgroup/cgroup.subtree_control
          ls -F /sys/fs/cgroup$(grep -oP '0::\K.*' /proc/self/cgroup)
        fi
        # kernel config
        script/check-config.sh

    - name: start sshd (used for testing rootless with systemd user session)
      if: ${{ matrix.os == 'actuated-arm64-6cpu-8gb' && matrix.rootless == 'rootless' }}
      run: |
        # Generate new keys to fix "sshd: no hostkeys available -- exiting."
        sudo ssh-keygen -A
        if ! sudo systemctl start ssh.service; then
          sudo journalctl -xeu ssh.service
          exit 1
        fi
        ps auxw | grep sshd

    - name: install deps
      run: |
        sudo apt update
        sudo apt -y install libseccomp-dev sshfs uidmap

    - name: install CRIU
      if: ${{ matrix.criu == '' }}
      env:
        PREFIX: https://download.opensuse.org/repositories/devel:/tools:/criu/xUbuntu
      run: |
        REPO=${PREFIX}_$(. /etc/os-release && echo $VERSION_ID)
        curl -fSsLl $REPO/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/devel_tools_criu.gpg > /dev/null
        echo "deb $REPO/ /" | sudo tee /etc/apt/sources.list.d/criu.list
        sudo apt update
        sudo apt -y install criu

    - name: install CRIU (criu ${{ matrix.criu }})
      if: ${{ matrix.criu != '' }}
      run: |
        sudo apt -qy install \
          libcap-dev libnet1-dev libnl-3-dev \
          libprotobuf-c-dev libprotobuf-dev protobuf-c-compiler protobuf-compiler
        git clone https://github.com/checkpoint-restore/criu.git ~/criu
        (cd ~/criu && git checkout ${{ matrix.criu }} && sudo make install-criu)
        rm -rf ~/criu

    - name: install go ${{ matrix.go-version }}
      uses: actions/setup-go@v5
      with:
        go-version: ${{ matrix.go-version }}
        check-latest: true

    - name: build
      run: sudo -E PATH="$PATH" make EXTRA_FLAGS="${{ matrix.race }}" all

    - name: Setup Bats and bats libs
      uses: bats-core/bats-action@3.0.0
      with:
        bats-version: 1.9.0
        support-install: false
        assert-install: false
        detik-install: false
        file-install: false

    - name: Allow userns for runc
      # https://discourse.ubuntu.com/t/ubuntu-24-04-lts-noble-numbat-release-notes/39890#unprivileged-user-namespace-restrictions-15
      if: matrix.os == 'ubuntu-24.04'
      run: |
        sed "s;^profile runc /usr/sbin/;profile runc-test $PWD/;" < /etc/apparmor.d/runc | sudo apparmor_parser

    - name: unit test
      if: matrix.rootless != 'rootless'
      run: sudo -E PATH="$PATH" -- make TESTFLAGS="${{ matrix.race }}" localunittest

    - name: add rootless user
      if: matrix.rootless == 'rootless'
      run: |
        sudo useradd -u2000 -m -d/home/rootless -s/bin/bash rootless
        # Allow root and rootless itself to execute `ssh rootless@localhost` in tests/rootless.sh
        ssh-keygen -t ecdsa -N "" -f $HOME/rootless.key
        sudo mkdir -m 0700 -p /home/rootless/.ssh
        sudo cp $HOME/rootless.key /home/rootless/.ssh/id_ecdsa
        sudo cp $HOME/rootless.key.pub /home/rootless/.ssh/authorized_keys
        sudo chown -R rootless.rootless /home/rootless
        sudo chmod a+X $HOME # for Ubuntu 22.04 and later

    - name: integration test (fs driver)
      run: sudo -E PATH="$PATH" script -e -c 'make local${{ matrix.rootless }}integration'

    - name: integration test (systemd driver)
      # Skip rootless+systemd for ubuntu 20.04 because of cgroup v1.
      if: ${{ !(matrix.os == 'ubuntu-20.04' && matrix.rootless == 'rootless') }}
      run: |
        # Delegate all cgroup v2 controllers to rootless user via --systemd-cgroup.
        # The default (since systemd v252) is "pids memory cpu".
        sudo mkdir -p /etc/systemd/system/user@.service.d
        printf "[Service]\nDelegate=yes\n" | sudo tee /etc/systemd/system/user@.service.d/delegate.conf
        sudo systemctl daemon-reload
        # Run the tests.
        sudo -E PATH="$PATH" script -e -c 'make RUNC_USE_SYSTEMD=yes local${{ matrix.rootless }}integration'

  # We need to continue support for 32-bit ARM.
  # However, we do not have 32-bit ARM CI, so we use i386 for testing 32bit stuff.
  # We are not interested in providing official support for i386.
  cross-i386:
    timeout-minutes: 15
    strategy:
      fail-fast: false
    runs-on: ubuntu-22.04

    steps:

    - name: checkout
      uses: actions/checkout@v4

    - name: install deps
      run: |
        sudo dpkg --add-architecture i386
        # add criu repo
        sudo add-apt-repository -y ppa:criu/ppa
        # apt-add-repository runs apt update so we don't have to.

        sudo apt -qy install libseccomp-dev libseccomp-dev:i386 gcc-multilib libgcc-s1:i386 criu

    - name: install go
      uses: actions/setup-go@v5
      with:
        go-version: 1.x # Latest stable
        check-latest: true

    - name: unit test
      run: sudo -E PATH="$PATH" -- make GOARCH=386 localunittest

  all-done:
    needs:
    - test
    - cross-i386
    runs-on: ubuntu-24.04
    steps:
    - run: echo "All jobs completed"