# 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:
  workflow_dispatch:
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-24.04, ubuntu-24.04-arm]
        go-version: [1.24.x, 1.25.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.24.x
          - criu: criu-dev
            rootless: rootless
          # Do race detection only on latest Go.
          - race: -race
            go-version: 1.24.x

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

    steps:
    - name: checkout
      uses: actions/checkout@v6

    - 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: 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 (${{ matrix.criu }})
      if: ${{ matrix.criu != '' }}
      run: |
        sudo apt -qy install \
          libcap-dev libnet1-dev libnl-3-dev uuid-dev \
          libprotobuf-c-dev libprotobuf-dev protobuf-c-compiler protobuf-compiler
        git clone --depth 1 --branch ${{ matrix.criu }} --single-branch \
          https://github.com/checkpoint-restore/criu.git ~/criu
        (cd ~/criu && sudo make -j $(nproc) install-criu)
        rm -rf ~/criu
        criu --version

    - name: install go ${{ matrix.go-version }}
      uses: actions/setup-go@v6
      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.1
      with:
        bats-version: 1.12.0 # Known as BATS_VERSION in other places.
        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: startsWith(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: |
        ./script/setup_rootless.sh
        sudo chmod a+X $HOME # for Ubuntu 22.04 and later

    - name: integration test (fs driver)
      continue-on-error: ${{ matrix.criu != '' }} # Don't let criu-dev errors fail CI.
      run: sudo -E PATH="$PATH" script -e -c 'make local${{ matrix.rootless }}integration'

    - name: integration test (systemd driver)
      continue-on-error: ${{ matrix.criu != '' }} # Don't let criu-dev errors fail CI.
      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@v6

    - name: install deps
      run: |
        sudo dpkg --add-architecture i386
        # Add criu repo. The web server returns gateway timeout, thus the retry.
        sudo add-apt-repository -y ppa:criu/ppa || 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@v6
      with:
        go-version: 1.x # Latest stable
        check-latest: true

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

  fedora:
    timeout-minutes: 30
    runs-on: ubuntu-24.04
    steps:
    - uses: actions/checkout@v6

    - uses: lima-vm/lima-actions/setup@v1
      id: lima-actions-setup

    - uses: actions/cache@v5
      with:
        path: ~/.cache/lima
        key: lima-${{ steps.lima-actions-setup.outputs.version }}

    - name: "Start VM"
      # --plain is set to disable file sharing, port forwarding, built-in containerd, etc. for faster start up
      #
      # CPUs: min(4, host CPU cores)
      # RAM:  min(4 GiB, half of host memory)
      # Disk: 100 GiB
      run: limactl start --plain --name=default template://fedora

    - name: "Initialize VM"
      run: |
        set -eux -o pipefail
        limactl cp -r . default:/tmp/runc
        lima sudo /tmp/runc/script/setup_host_fedora.sh

    - name: "Show guest info"
      run: |
        set -eux -o pipefail
        lima uname -a
        lima systemctl --version
        lima df -T
        lima cat /etc/os-release
        lima go version
        lima sestatus
        lima rpm -q container-selinux

    - name: "Check config"
      run: lima /tmp/runc/script/check-config.sh

    # NOTE the execution environment lacks a terminal, needed for
    # some integration tests. So we use `ssh -tt` command to fake a terminal.
    - name: "Run unit tests"
      run: ssh -tt lima-default sudo -i make -C /tmp/runc localunittest

    - name: "Run integration tests (systemd driver)"
      run: ssh -tt lima-default sudo -i make -C /tmp/runc localintegration RUNC_USE_SYSTEMD=yes

    - name: "Run integration tests (fs driver)"
      run: ssh -tt lima-default sudo -i make -C /tmp/runc localintegration

    - name: "Run integration tests (systemd driver, rootless)"
      run: ssh -tt lima-default sudo -i make -C /tmp/runc localrootlessintegration RUNC_USE_SYSTEMD=yes

    - name: "Run integration tests (fs driver, rootless)"
      run: ssh -tt lima-default sudo -i make -C /tmp/runc localrootlessintegration

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