stages:
  - build
  - build docker image
  - test
  - upload artifacts

variables:
  # Make GitLab CI go fast:
  GIT_SUBMODULE_STRATEGY: recursive
  FF_USE_FASTZIP: 1
  CACHE_COMPRESSION_LEVEL: fastest

# --------------------------------------------------------------------- #
#  Create and publish docker image                                      #
# --------------------------------------------------------------------- #

.docker-shared-settings:
  stage: "build docker image"
  needs: []
  tags: [ "docker" ]
  variables:
    # Docker in Docker:
    DOCKER_BUILDKIT: 1
  image:
    name: docker.io/docker
  services:
    - name: docker.io/docker:dind
      alias: docker
  script:
    - apk add openssh-client
    - eval $(ssh-agent -s)
    - mkdir -p ~/.ssh && chmod 700 ~/.ssh
    - printf "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config
    - sh .gitlab/setup-buildx-remote-builders.sh
    # Authorize against this project's own image registry:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
    # Build multiplatform image and push to temporary tag:
    - >
      docker buildx build 
      --platform "linux/arm/v7,linux/arm64,linux/amd64"
      --pull
      --tag "$CI_REGISTRY_IMAGE/temporary-ci-images:$CI_JOB_ID"
      --push
      --provenance=false
      --file "Dockerfile" .
    # Build multiplatform image to deb stage and extract their .deb files:
    - >
      docker buildx build 
      --platform "linux/arm/v7,linux/arm64,linux/amd64"
      --target "packager-result"
      --output="type=local,dest=/tmp/build-output"
      --provenance=false
      --file "Dockerfile" .
    # Build multiplatform image to binary stage and extract their binaries:
    - >
      docker buildx build 
      --platform "linux/arm/v7,linux/arm64,linux/amd64"
      --target "builder-result"
      --output="type=local,dest=/tmp/build-output"
      --provenance=false
      --file "Dockerfile" .
    # Copy to GitLab container registry:
    - >
      docker buildx imagetools create
      --tag "$CI_REGISTRY_IMAGE/$TAG"
      --tag "$CI_REGISTRY_IMAGE/$TAG-bullseye"
      --tag "$CI_REGISTRY_IMAGE/$TAG-commit-$CI_COMMIT_SHORT_SHA"
      "$CI_REGISTRY_IMAGE/temporary-ci-images:$CI_JOB_ID"
    # if DockerHub credentials exist, also copy to dockerhub:
    - if [ -n "${DOCKER_HUB}" ]; then docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_PASSWORD" "$DOCKER_HUB"; fi
    - >
      if [ -n "${DOCKER_HUB}" ]; then 
      docker buildx imagetools create
      --tag "$DOCKER_HUB_IMAGE/$TAG"
      --tag "$DOCKER_HUB_IMAGE/$TAG-bullseye"
      --tag "$DOCKER_HUB_IMAGE/$TAG-commit-$CI_COMMIT_SHORT_SHA"
      "$CI_REGISTRY_IMAGE/temporary-ci-images:$CI_JOB_ID"
      ; fi
    - mv /tmp/build-output ./
  artifacts:
    paths:
      - "./build-output/" 

docker:next:
  extends: .docker-shared-settings
  rules:
    - if: '$BUILD_SERVER_SSH_PRIVATE_KEY && $CI_COMMIT_BRANCH == "next"'
  variables:
    TAG: "matrix-conduit:next"

docker:master:
  extends: .docker-shared-settings
  rules:
    - if: '$BUILD_SERVER_SSH_PRIVATE_KEY && $CI_COMMIT_BRANCH == "master"'
  variables:
    TAG: "matrix-conduit:latest"

docker:tags:
  extends: .docker-shared-settings
  rules:
    - if: "$BUILD_SERVER_SSH_PRIVATE_KEY && $CI_COMMIT_TAG"
  variables:
    TAG: "matrix-conduit:$CI_COMMIT_TAG"


docker build debugging:
  extends: .docker-shared-settings
  rules:
    - if: "$CI_MERGE_REQUEST_TITLE =~ /.*[Dd]ocker.*/"
  variables:
    TAG: "matrix-conduit-docker-tests:latest"

# --------------------------------------------------------------------- #
#  Run tests                                                            #
# --------------------------------------------------------------------- #

cargo check:
  stage: test
  image: docker.io/rust:1.74.1-bullseye
  needs: []
  interruptible: true
  before_script:
    - "rustup show && rustc --version && cargo --version" # Print version info for debugging
    - apt-get update && apt-get -y --no-install-recommends install libclang-dev # dependency for rocksdb
  script:
    - cargo check


.test-shared-settings:
  stage: "test"
  needs: []
  image: "registry.gitlab.com/jfowl/conduit-containers/rust-with-tools:latest"
  tags: ["docker"]
  variables:
    CARGO_INCREMENTAL: "false" # https://matklad.github.io/2021/09/04/fast-rust-builds.html#ci-workflow
  interruptible: true

test:cargo:
  extends: .test-shared-settings
  before_script:
    - apt-get update && apt-get -y --no-install-recommends install libclang-dev # dependency for rocksdb
  script:
    - rustc --version && cargo --version # Print version info for debugging
    - "cargo test --color always --workspace --verbose --locked --no-fail-fast"

test:clippy:
  extends: .test-shared-settings
  before_script:
    - rustup component add clippy
    - apt-get update && apt-get -y --no-install-recommends install libclang-dev # dependency for rocksdb
  script:
    - rustc --version && cargo --version # Print version info for debugging
    - "cargo clippy --color always --verbose --message-format=json | gitlab-report -p clippy > $CI_PROJECT_DIR/gl-code-quality-report.json"
  artifacts:
    when: always
    reports:
      codequality: gl-code-quality-report.json

test:format:
  extends: .test-shared-settings
  before_script:
    - rustup component add rustfmt
  script:
    - cargo fmt --all -- --check

test:audit:
  extends: .test-shared-settings
  script:
    - cargo audit --color always || true
    - cargo audit --stale --json | gitlab-report -p audit > gl-sast-report.json
  artifacts:
    when: always
    reports:
      sast: gl-sast-report.json

test:dockerlint:
  stage: "test"
  needs: []
  image: "ghcr.io/hadolint/hadolint@sha256:6c4b7c23f96339489dd35f21a711996d7ce63047467a9a562287748a03ad5242" # 2.8.0-alpine
  interruptible: true
  script:
    - hadolint --version
    # First pass: Print for CI log:
    - >
      hadolint
      --no-fail --verbose
      ./Dockerfile
    # Then output the results into a json for GitLab to pretty-print this in the MR:
    - >
      hadolint
      --format gitlab_codeclimate
      --failure-threshold error
      ./Dockerfile > dockerlint.json
  artifacts:
    when: always
    reports:
      codequality: dockerlint.json
    paths:
      - dockerlint.json
  rules:
    - if: '$CI_COMMIT_REF_NAME != "master"'
      changes:
        - docker/*Dockerfile
        - Dockerfile
        - .gitlab-ci.yml
    - if: '$CI_COMMIT_REF_NAME == "master"'
    - if: '$CI_COMMIT_REF_NAME == "next"'

# --------------------------------------------------------------------- #
#  Store binaries as package so they have download urls                 #
# --------------------------------------------------------------------- #

# DISABLED FOR NOW, NEEDS TO BE FIXED AT A LATER TIME:

#publish:package:
#  stage: "upload artifacts"
#  needs:
#    - "docker:tags"
#  rules:
#    - if: "$CI_COMMIT_TAG"
#  image: curlimages/curl:latest
#  tags: ["docker"]
#  variables:
#    GIT_STRATEGY: "none" # Don't need a clean copy of the code, we just operate on artifacts
#  script:
#    - 'BASE_URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/conduit-${CI_COMMIT_REF_SLUG}/build-${CI_PIPELINE_ID}"'
#    - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_amd64/conduit "${BASE_URL}/conduit-x86_64-unknown-linux-gnu"'
#    - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_arm_v7/conduit "${BASE_URL}/conduit-armv7-unknown-linux-gnu"'
#    - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_arm64/conduit "${BASE_URL}/conduit-aarch64-unknown-linux-gnu"'
#    - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_amd64/conduit.deb "${BASE_URL}/conduit-x86_64-unknown-linux-gnu.deb"'
#    - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_arm_v7/conduit.deb "${BASE_URL}/conduit-armv7-unknown-linux-gnu.deb"'
#    - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build-output/linux_arm64/conduit.deb "${BASE_URL}/conduit-aarch64-unknown-linux-gnu.deb"'

# Avoid duplicate pipelines
# See: https://docs.gitlab.com/ee/ci/yaml/workflow.html#switch-between-branch-pipelines-and-merge-request-pipelines
workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
      when: never
    - if: "$CI_COMMIT_BRANCH"
    - if: "$CI_COMMIT_TAG"
