# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
# SPDX-License-Identifier: Apache-2.0

variables:
  GIT_SUBMODULE_STRATEGY: recursive
  GIT_DEPTH: 0
  DISTRO: fedora # Standard distribution
  DOCKER_FILE: packaging/docker/Dockerfile.${DISTRO}
  DOCKER_TAG: ${CI_COMMIT_REF_NAME}
  DOCKER_IMAGE: registry.git.rwth-aachen.de/acs/public/villas/node
  DOCKER_IMAGE_DEV: ${DOCKER_IMAGE}/dev-${DISTRO}
  DOCKER_CLI_EXPERIMENTAL: enabled
  CMAKE_BUILD_OPTS: "--parallel 16"
  CMAKE_EXTRA_OPTS: "-DCMAKE_BUILD_TYPE=Release -DVILLAS_COMPILE_WARNING_AS_ERROR=ON"

stages:
  - prepare
  - build
  - test
  - packaging
  - deploy
  - latest

# Stage: prepare

# Build docker image which is used to build & test VILLASnode
prepare:docker:
  stage: prepare
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker build ${DOCKER_OPTS}
      --file ${DOCKER_FILE}
      --tag ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
      --target ${TARGET} .
    - docker push ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
  variables:
    TARGET: dev
  parallel:
    matrix:
      - DISTRO: [ubuntu, debian, rocky]
      - DISTRO: fedora
        DOCKER_OPTS: --tag ${DOCKER_IMAGE}/dev:${DOCKER_TAG}
      - DISTRO: fedora
        DOCKER_FILE: packaging/docker/Dockerfile.fedora-minimal
        DOCKER_IMAGE_DEV: ${DOCKER_IMAGE}/dev-fedora-minimal
      - DISTRO: fedora
        TARGET: dev-vscode
        DOCKER_IMAGE_DEV: ${DOCKER_IMAGE}/dev-vscode
  tags:
    - docker-image-builder

# Stage: build

build:source:
  stage: build
  needs: ["prepare:docker"]
  image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
  script:
    - cmake -S . -B build ${CMAKE_OPTS} ${CMAKE_EXTRA_OPTS}
    - cmake --build build ${CMAKE_BUILD_OPTS}
  artifacts:
    expire_in: 1 week
    paths:
      - build/
  variables:
    CMAKE: cmake
  parallel:
    matrix:
      - DISTRO: [fedora, fedora-minimal, debian, rocky, ubuntu]
      - DISTRO: fedora-minimal
        CMAKE_EXTRA_OPTS: -DVILLAS_COMPILE_WARNING_AS_ERROR=ON
          -DWITH_API=OFF
          -DWITH_CLIENTS=OFF
          -DWITH_CONFIG=OFF
          -DWITH_DOC=OFF
          -DWITH_FPGA=OFF
          -DWITH_GRAPHVIZ=OFF
          -DWITH_HOOKS=OFF
          -DWITH_LUA=OFF
          -DWITH_OPENMP=OFF
          -DWITH_PLUGINS=OFF
          -DWITH_SRC=OFF
          -DWITH_TESTS=OFF
          -DWITH_TOOLS=OFF
          -DWITH_WEB=OFF
          -DCMAKE_MODULE_PATH=/usr/local/lib64/cmake
          -DCMAKE_PREFIX_PATH=/usr/local

build:nix:
  stage: build
  image: nixos/nix
  needs: []

  artifacts:
    when: on_success
    access: all
    expire_in: "1 year"
    paths:
      - artifacts/villas

  before_script:
    - | # Create artifacts directory
      mkdir artifacts

    - | # Configure Nix
      echo "experimental-features = flakes nix-command" >> /etc/nix/nix.conf

    # - | # Configure Attic
    #   nix shell nixpgks#attic-client

    #   attic login nulll https://cache.0l.de ${ATTIC_TOKEN}
    #   attic use "nulll:villas"

  script:
    - nix build --print-build-logs .

    - | # Build ARX bundle
      nix bundle --out-link villas .
      cp -L villas artifacts/villas

    - | # Build Docker image
      nix run nixpkgs#skopeo -- login \
        --username ${CI_REGISTRY_USER} \
        --password ${CI_REGISTRY_PASSWORD} \
        ${CI_REGISTRY}

      nix build '.#dockerImage'

      nix run nixpkgs#skopeo -- copy --insecure-policy \
        "docker-archive:./result" \
        "docker://${DOCKER_IMAGE}:${DOCKER_TAG}-nix"

  # after_script:
  #   - | # Push whole store to cache
  #     nix shell nixpgks#attic-client

  #     attic push /nix/store/*

# Stage: test

test:python:
  stage: test
  script:
    - cd python
    - /venv/bin/pytest --verbose .
    - /venv/bin/black --extend-exclude=".*(\\.pyi|_pb2.py)$" --check .
    - /venv/bin/flake8 --extend-exclude="*.pyi,*_pb2.py" .
    - /venv/bin/mypy .
  image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
  needs:
    - job: "build:source: [fedora]"

test:cppcheck:
  stage: test
  script:
    - ./tools/run-cppcheck.sh | tee cppcheck.log
  image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
  needs:
    - job: "build:source: [fedora]"
  artifacts:
    when: on_failure
    paths:
      - cppcheck.log
    expose_as: cppcheck

test:unit:
  stage: test
  image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
  script:
    - cmake -S . -B build ${CMAKE_OPTS}
    - cmake --build build ${CMAKE_BUILD_OPTS} --target run-unit-tests run-unit-tests-common
  needs:
    - job: "build:source: [fedora]"
      artifacts: true

test:integration:
  stage: test
  image: ${DOCKER_IMAGE_DEV}:${DOCKER_TAG}
  script:
    - cmake -S . -B build ${CMAKE_OPTS}
    - cmake --build build ${CMAKE_BUILD_OPTS} --target run-integration-tests
  artifacts:
    name: ${CI_PROJECT_NAME}-integration-tests-${CI_BUILD_REF}
    when: always
    paths:
      - build/tests/integration/
  services:
    - name: eclipse-mosquitto:2.0
      alias: mosquitto
      command: [mosquitto, -c, /mosquitto-no-auth.conf]
    - name: rwthacs/rabbitmq
      alias: rabbitmq
    - name: redis:6.2
      alias: redis
  needs:
    - job: "build:source: [fedora]"
      artifacts: true

test:reuse:
  stage: test
  needs: []
  image:
    name: fsfe/reuse:latest
    entrypoint: [""]
  script:
    - reuse lint

# Stage: packaging

pkg:docker:
  stage: packaging
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker build ${DOCKER_OPTS}
      --pull
      --target ${TARGET}
      --build-arg ARCH=${ARCH}
      --build-arg TRIPLET=${TRIPLET}
      --build-arg CMAKE_OPTS="${CMAKE_OPTS} ${CMAKE_EXTRA_OPTS}"
      --platform ${PLATFORM}
      --file ${DOCKER_FILE}
      --tag ${DOCKER_IMAGE}:${DOCKER_TAG}-${ARCH} .
    - docker push ${DOCKER_IMAGE}:${DOCKER_TAG}-${ARCH}
  tags:
    - $TAG
  variables:
    TARGET: app
  parallel:
    matrix:
      - DISTRO: debian
        PLATFORM: linux/amd64
        ARCH: x86_64
        TRIPLET: x86_64-linux-gnu
        TAG: docker
      - DISTRO: debian
        PLATFORM: linux/arm64/v8
        ARCH: arm64
        TRIPLET: aarch64-linux-gnu
        TAG: pi5
  needs: []

# Stage: deploy

deploy:docker:
  stage: deploy
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker manifest rm ${DOCKER_IMAGE}:${DOCKER_TAG} || true
    - docker manifest create ${DOCKER_IMAGE}:${DOCKER_TAG}
      ${DOCKER_IMAGE}:${DOCKER_TAG}-x86_64
      ${DOCKER_IMAGE}:${DOCKER_TAG}-arm64
    - docker manifest push   ${DOCKER_IMAGE}:${DOCKER_TAG}
  tags:
    - docker
  needs:
    - job: "pkg:docker: [debian, linux/amd64, x86_64, x86_64-linux-gnu, docker]"
    - job: "pkg:docker: [debian, linux/arm64/v8, arm64, aarch64-linux-gnu, pi5]"

deploy:docker-dev:
  stage: deploy
  variables:
    DOCKER_CLI_EXPERIMENTAL: enabled
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker push ${DOCKER_IMAGE}/dev:${DOCKER_TAG}
  tags:
    - docker
  needs:
    - job: "prepare:docker: [fedora, --tag ${DOCKER_IMAGE}/dev:${DOCKER_TAG}]"

deploy:docker-dev-vscode:
  stage: deploy
  variables:
    DOCKER_CLI_EXPERIMENTAL: enabled
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker push ${DOCKER_IMAGE}/dev-vscode:${DOCKER_TAG}
  tags:
    - docker
  needs:
    - job: "prepare:docker: [fedora, dev-vscode, ${DOCKER_IMAGE}/dev-vscode]"

# Stage: latest

.latest:docker:latest: &deploy_latest_docker
  stage: latest
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker manifest rm ${DOCKER_IMAGE}:latest || true
    - docker manifest create ${DOCKER_IMAGE}:latest
      ${DOCKER_IMAGE}:${DOCKER_TAG}-x86_64
      ${DOCKER_IMAGE}:${DOCKER_TAG}-arm64
    - docker manifest push   ${DOCKER_IMAGE}:latest
  tags:
    - docker
  needs:
    - job: deploy:docker

.latest:docker-dev:latest: &deploy_latest_docker_dev
  stage: latest
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker tag  ${DOCKER_IMAGE}/dev:${DOCKER_TAG} ${DOCKER_IMAGE}/dev:latest
    - docker push ${DOCKER_IMAGE}/dev:latest
  tags:
    - docker
  needs:
    - job: deploy:docker-dev

latest:docker:
  <<: *deploy_latest_docker
  only:
    - "/^v\\d+(\\.\\d+)+$/" # Only on version tags

latest:docker-dev:
  <<: *deploy_latest_docker_dev
  only:
    - "/^v\\d+(\\.\\d+)+$/" # Only on version tags

latest:docker:manual:
  <<: *deploy_latest_docker
  when: manual
  except:
    - "/^v\\d+(\\.\\d+)+$/" # Only on version tags

latest:docker-dev:manual:
  <<: *deploy_latest_docker_dev
  when: manual
  except:
    - "/^v\\d+(\\.\\d+)+$/" # Only on version tags
