# This file is a template, and might need editing before it works on your project.
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/python:3.10

variables:
  BEC_CORE_BRANCH:
    description: bec branch
    value: main
  CHILD_PIPELINE_BRANCH: $CI_DEFAULT_BRANCH
  CHECK_PKG_VERSIONS:
    description: Whether to run additional tests against min/max/random selection of dependencies. Set to 1 for running. 
    value: 0


workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
    - if: $CI_PIPELINE_SOURCE == "web"
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_PIPELINE_SOURCE == "pipeline"
    - if: $CI_PIPELINE_SOURCE == "parent_pipeline"
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never
    - if: $CI_COMMIT_BRANCH
  auto_cancel:
    on_new_commit: interruptible

include:
  - template: Security/Secret-Detection.gitlab-ci.yml
  - project: "bec/awi_utils"
    file: "/templates/check-packages-job.yml"
    inputs:
      stage: test
      path: "."
      pytest_args: "-v,--random-order,tests/"
      ignore_dep_group: ""
  - project: "bec/awi_utils"
    file: "templates/device-list-job.yml"
    inputs:
      target: $CI_PROJECT_NAME
      token: $CI_UPDATES
  - project: "bec/awi_utils"
    file: "/templates/trigger-beamline-repos.yml"
    inputs: 
      bec_core_branch: $BEC_CORE_BRANCH
      ophyd_devices_branch: $CHILD_PIPELINE_BRANCH

#commands to run in the Docker container before starting each job.
before_script:
  - if [[ "$CI_PROJECT_PATH" != "bec/ophyd_devices" ]]; then
    echo -e "\033[35;1m Using branch $CHILD_PIPELINE_BRANCH of Ophyd Devices \033[0;m";
    test -d ophyd_devices || git clone --branch $CHILD_PIPELINE_BRANCH https://gitlab.psi.ch/bec/ophyd_devices.git; cd ophyd_devices;
    fi
  - pip install -e .[dev]
  - git clone --branch $BEC_CORE_BRANCH https://gitlab.psi.ch/bec/bec.git
  - pip install -e ./bec/bec_lib[dev]
  - pip install -e ./bec/bec_server[dev]

# different stages in the pipeline
stages:
  - Formatter
  - test # must be called test for security/secret-detection to work
  - AdditionalTests
  - Deploy

formatter:
  stage: Formatter
  before_script:
    - ""
  script:
    - pip install black isort
    - pip install -e .[dev]
    - isort --check --diff ./
    - black --check --diff --color ./
  rules:
    - if: '$CI_PIPELINE_SOURCE == "parent_pipeline"'
      when: never
    - if: $CI_PROJECT_PATH == "bec/ophyd_devices"
  interruptible: true

pylint:
  stage: Formatter
  script:
    - pip install pylint pylint-exit anybadge
    - mkdir ./pylint
    - pylint ./ophyd_devices --output-format=text --output=./pylint/pylint.log | tee ./pylint/pylint.log || pylint-exit $?
    - PYLINT_SCORE=$(sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' ./pylint/pylint.log)
    - anybadge --label=Pylint --file=pylint/pylint.svg --value=$PYLINT_SCORE 2=red 4=orange 8=yellow 10=green
    - echo "Pylint score is $PYLINT_SCORE"
  artifacts:
    paths:
      - ./pylint/
    expire_in: 1 week
  rules:
    - if: '$CI_PIPELINE_SOURCE == "parent_pipeline"'
      when: never
    - if: $CI_PROJECT_PATH == "bec/ophyd_devices"
  interruptible: true

pylint-check:
  stage: Formatter
  needs: []
  allow_failure: true
  before_script:
    - pip install pylint pylint-exit anybadge
    - apt-get update
    - apt-get install -y bc
  script:
    # Identify changed Python files
    - if [ "$CI_PIPELINE_SOURCE" == "merge_request_event" ]; then
      TARGET_BRANCH_COMMIT_SHA=$(git rev-parse $CI_MERGE_REQUEST_TARGET_BRANCH_NAME);
      CHANGED_FILES=$(git diff --name-only $SOURCE_BRANCH_COMMIT_SHA $TARGET_BRANCH_COMMIT_SHA | grep '\.py$' || true);
      else
      CHANGED_FILES=$(git diff --name-only $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | grep '\.py$' || true);
      fi
    - if [ -z "$CHANGED_FILES" ]; then echo "No Python files changed."; exit 0; fi

    # Run pylint only on changed files
    - mkdir ./pylint
    - pylint $CHANGED_FILES --output-format=text . | tee ./pylint/pylint_changed_files.log || pylint-exit $?
    - PYLINT_SCORE=$(sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' ./pylint/pylint_changed_files.log)
    - echo "Pylint score is $PYLINT_SCORE"

    # Fail the job if the pylint score is below 9
    - if [ "$(echo "$PYLINT_SCORE < 9" | bc)" -eq 1 ]; then echo "Your pylint score is below the acceptable threshold (9)."; exit 1; fi
  artifacts:
    paths:
      - ./pylint/
    expire_in: 1 week
  rules:
    - if: '$CI_PIPELINE_SOURCE == "parent_pipeline"'
      when: never
    - if: $CI_PROJECT_PATH == "bec/ophyd_devices"
  interruptible: true

secret_detection:
  before_script:
    - ""
  rules:
    - if: $CI_PROJECT_PATH == "bec/ophyd_devices"
  interruptible: true

pytest:
  stage: test
  script:
    - pip install coverage
    - coverage run --source=./ophyd_devices --omit=*/ophyd_devices/tests/* -m pytest -v --junitxml=report.xml --random-order --full-trace ./tests
    - coverage report
    - coverage xml
  coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
  artifacts:
    reports:
      junit: report.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
  interruptible: true

config_test:
  stage: test
  script:
    - ophyd_test --config ./ophyd_devices/configs/ --output ./config_tests
  artifacts:
    paths:
      - ./config_tests
    when: on_failure
    expire_in: "30 days"
  interruptible: true

tests-3.11:
  stage: AdditionalTests
  needs:
    - job: "formatter"
      optional: true
    - job: "pylint"
      optional: true
  image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/python:3.11
  script:
    - pytest -v --random-order ./tests
  allow_failure: true
  interruptible: true

tests-3.12:
  extends: "tests-3.11"
  stage: AdditionalTests
  image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/python:3.12
  allow_failure: true

trigger_bec:
  needs: []
  trigger:
    strategy: depend
    include:
      - project: bec/bec
        ref: $BEC_CORE_BRANCH
        file: /.gitlab-ci.yml
  variables:
    OPHYD_DEVICES_BRANCH: $CI_COMMIT_REF_NAME
    CHILD_PIPELINE_BRANCH: $BEC_CORE_BRANCH
  rules:
    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main" && $CI_PROJECT_PATH == "bec/ophyd_devices"'
  interruptible: true

semver:
  stage: Deploy
  needs: ["pytest"]
  script:
    - git config --global user.name "ci_update_bot"
    - git config --global user.email "ci_update_bot@bec.ch"
    - git checkout "$CI_COMMIT_REF_NAME"
    - git reset --hard origin/"$CI_COMMIT_REF_NAME"

    # delete all local tags
    - git tag -l | xargs git tag -d
    - git fetch --tags
    - git tag

    # build and publish package
    - pip install python-semantic-release==9.* wheel build twine
    - export GL_TOKEN=$CI_UPDATES
    - semantic-release -vv version

    # check if any artifacts were created
    - if [ ! -d dist ]; then echo No release will be made; exit 0; fi
    - twine upload dist/* -u __token__ -p $CI_PYPI_TOKEN --skip-existing
    - semantic-release publish

  allow_failure: false
  rules:
    - if: '$CI_COMMIT_REF_NAME == "main" && $CI_PROJECT_PATH == "bec/ophyd_devices"'
  interruptible: true
