workflow:
  rules:
    # Don't create a pipeline if commit is on a branch with open merge requests
    - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never
    - when: always

variables:
  TEST_HBA_DATASET_NAME: "test_data.tar.gz"
  TEST_LBA_DATASET_NAME: "test_data_lba.tar.gz"
  CALIBRATOR_HBA_RESULTS_NAME: "results_calibrator.tar.gz"
  CALIBRATOR_LBA_RESULTS_NAME: 'results_calibrator_lba.tar.gz'
  TARGET_HBA_RESULTS_NAME: "results_target.tar.gz"
  TARGET_LBA_RESULTS_NAME: "results_target_lba.tar.gz"
  TARGET_HBA_SELFCAL_RESULTS_NAME: "results_target_selfcal.tar.gz"
  TARGET_HBA_VLBI_RESULTS_NAME: "results_target_VLBI.tar.gz"
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
  BUILD_DOCKER_IMAGE: "0"
  GIT_STRATEGY: clone  # ensure every job starts with a pristine working copy
  GIT_DEPTH: 0  # do not do shallow clones, needed for `git describe --tags`

stages:
  - initialize
  - versioning
  - build
  - install
  - prepare_tests
  - run_tests
  - docs
  - deploy
  - finalize

.release:
  before_script:
    # Make sure release branch is checked out when building a release
    - export RELEASE=$(echo -n $CI_COMMIT_BRANCH | sed -n 's,^releases/,,p')
    - |
      if test -n "$RELEASE"
      then
        echo "Checking out branch '$CI_COMMIT_BRANCH'"
        git checkout $CI_COMMIT_BRANCH
      fi

.versioning:
  image: python
  before_script:
    - !reference [.release, before_script]
    - pip install setuptools_scm
  script:
    # Determine the LINC version, and the docker image tag. If RELEASE is set,
    # check if it adheres to the PEP-440 naming conventions, and if so, set the LINC
    # version string.  If RELEASE is not set, then determine the LINC version string
    # using `setuptools_scm`. For the docker image tag, replace all the characters
    # in the LINC version string that are not allowed in a tag string with a dash.
    - |
      if test -n "$RELEASE"
      then
        LINC_VERSION=$(python -c 'import os; from packaging.version import Version; print(Version(os.getenv("RELEASE")))')
      else
        LINC_VERSION=$(python -m setuptools_scm)
      fi
    - LINC_TAG=${LINC_VERSION//[^[:alnum:]_.-]/-}
    - echo "LINC_VERSION=$LINC_VERSION" >> versions.env
    - echo "LINC_TAG=$LINC_TAG" >> versions.env
  artifacts:
    reports:
      dotenv: versions.env

.setup_git:
  image: bitnami/git
  before_script:
    # Use a token that has git push permissions. Token must be created in the
    # GitLab GUI; a project variable must be created containing the token.
    # See file `DEVELOPER.md` for details.
    - !reference [.release, before_script]
    - git config user.email $GITLAB_USER_EMAIL
    - git config user.name $GITLAB_USER_NAME
    - git remote set-url origin https://oauth2:$GIT_PUSH_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git

.setup_docker:
  stage: build
  image: docker
  tags:
    - dind
  before_script:
    - !reference [.release, before_script]
    - echo "Logging in as $CI_REGISTRY_USER @ $CI_REGISTRY"
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY

.run_tests:
  stage: run_tests
  image: $INTEGRATION_IMAGE
  before_script:
    - !reference [.release, before_script]
    - mkdir -p workdir
    - mkdir -p logs

.deploy:
  stage: deploy
  extends: .setup_docker
  tags:
    - dind
  before_script:
    - !reference [.setup_docker, before_script]
    - echo "Logging in as $DH_REGISTRY_USER @ DockerHub"
    - echo $DH_REGISTRY_PASSWORD | docker login -u $DH_REGISTRY_USER --password-stdin

.docs:
  stage: docs
  image: $INTEGRATION_IMAGE

.finalize:
  stage: finalize
  extends: .setup_git


### Stage: initialize

prepare_release:
  stage: initialize
  image: python
  extends: .setup_git
  rules:
    # Run this job when current branch is a release branch
    - if: '$CI_COMMIT_BRANCH =~ /^releases//'
      when: always
  artifacts:
    # Create a marker file if release tag already exists. The presence of this
    # file is used in the `rollback_release` job, to decide that the existing
    # tag should not be removed.
    paths:
      - .tag.exists
    untracked: true
    when: on_failure
  before_script:
    - !reference [.versioning, before_script]
    # When building a release, bail out if release tag already exists
    - |
      if git ls-remote --tags --exit-code origin $RELEASE > /dev/null
      then
        echo "*** Release '$RELEASE' already exists. Bailing out! ***"
        touch .tag.exists
        exit 1
      fi
    - !reference [.setup_git, before_script]
  script:
    # Use .versioning:script to get LINC_TAG
    - !reference [.versioning, script]
    - echo "Updating dockerPull URI to use image tag '$LINC_TAG'"
    - sed -ri "/dockerPull/s,['\"]?(astronrd/linc).*,\1:$LINC_TAG," steps/*.cwl
    - git add -u steps/*.cwl
    # Only commit if there are changes
    - |
      if test -n "$(git status -uno --porcelain)"
      then
        git commit -m "Tag $RELEASE added to dockerPull URI in CWL steps (by GitLab CI)"
      fi
    # Tag current revision
    - git tag -a $RELEASE -m "Git tag $RELEASE created (by GitLab CI)"
    # Skip CI on this push
    - git push --follow-tags -o ci.skip


### Stage: versioning

versioning:
  stage: versioning
  extends: .versioning
  script:
    - !reference [.versioning, script]
    - ./Docker/fetch_commit_hashes.sh | tee commits.txt >> versions.env
    # Use hash of commits to determine version of base image (and rebuild if necessary)
    - echo INTEGRATION_BASE_IMAGE=$CI_REGISTRY_IMAGE/integration_base:$(sha256sum commits.txt | cut -d " " -f 1) >> versions.env
    - echo INTEGRATION_IMAGE=$CI_REGISTRY_IMAGE/integration_full:$(git log -n 1 --pretty=format:%H) >> versions.env
    - cat versions.env


### Stage: build

build_base:
  stage: build
  extends: .setup_docker
  script:
    - |
      if ! docker manifest inspect $INTEGRATION_BASE_IMAGE > /dev/null || [ "$BUILD_DOCKER_IMAGE" = "1" ]; then
        if [ "$BUILD_DOCKER_IMAGE" = "1" ] ; then
          DOCKER_CACHE_PARAMETERS="--no-cache"
        else
          DOCKER_CACHE_PARAMETERS=""
        fi
        docker build \
          $DOCKER_CACHE_PARAMETERS \
          --build-arg CASACORE_COMMIT \
          --build-arg LOFARSTMAN_COMMIT \
          --build-arg IDG_COMMIT \
          --build-arg AOFLAGGER_COMMIT \
          --build-arg EVERYBEAM_COMMIT \
          --build-arg SAGECAL_COMMIT \
          --build-arg DP3_COMMIT \
          --build-arg WSCLEAN_COMMIT \
          --build-arg PYTHONCASACORE_COMMIT \
          --build-arg LINC_VERSION \
          --file Docker/Dockerfile-base \
          --tag $INTEGRATION_BASE_IMAGE \
          .
        docker push $INTEGRATION_BASE_IMAGE
      fi


### Stage: install

install_linc:
  stage: install
  extends: .setup_docker
  script:
    - |
      if [ "$BUILD_DOCKER_IMAGE" = "1" ] ; then
        DOCKER_CACHE_PARAMETERS="--no-cache"
      else
        DOCKER_CACHE_PARAMETERS=""
      fi
      docker build \
        $DOCKER_CACHE_PARAMETERS \
        --build-arg BASE_IMAGE=$INTEGRATION_BASE_IMAGE \
        --build-arg LINC_VERSION=$LINC_VERSION \
        --file Docker/Dockerfile-linc \
        --tag $INTEGRATION_IMAGE \
        .
    - docker push $INTEGRATION_IMAGE


### Stage: prepare_tests

download_data:
  image: $INTEGRATION_IMAGE
  stage: prepare_tests
  script:
    - mkdir data && cd data
    - wget -nv https://support.astron.nl/software/ci_data/linc/$TEST_HBA_DATASET_NAME -O $TEST_HBA_DATASET_NAME && tar xfz $TEST_HBA_DATASET_NAME && rm -f $TEST_HBA_DATASET_NAME
    - wget -nv https://support.astron.nl/software/ci_data/linc/$TEST_LBA_DATASET_NAME -O $TEST_LBA_DATASET_NAME && tar xfz $TEST_LBA_DATASET_NAME && rm -f $TEST_LBA_DATASET_NAME
    - wget -nv https://support.astron.nl/software/ci_data/linc/$CALIBRATOR_HBA_RESULTS_NAME -O $CALIBRATOR_HBA_RESULTS_NAME && tar xfz $CALIBRATOR_HBA_RESULTS_NAME && rm -f $CALIBRATOR_HBA_RESULTS_NAME
    - wget -nv https://support.astron.nl/software/ci_data/linc/$CALIBRATOR_LBA_RESULTS_NAME -O $CALIBRATOR_LBA_RESULTS_NAME && tar xfz $CALIBRATOR_LBA_RESULTS_NAME && rm -f $CALIBRATOR_LBA_RESULTS_NAME
    - wget -nv https://support.astron.nl/software/ci_data/linc/$TARGET_HBA_RESULTS_NAME -O $TARGET_HBA_RESULTS_NAME && tar xfz $TARGET_HBA_RESULTS_NAME && rm -f $TARGET_HBA_RESULTS_NAME
    - wget -nv https://support.astron.nl/software/ci_data/linc/$TARGET_LBA_RESULTS_NAME -O $TARGET_LBA_RESULTS_NAME && tar xfz $TARGET_LBA_RESULTS_NAME && rm -f $TARGET_LBA_RESULTS_NAME
    - wget -nv https://support.astron.nl/software/ci_data/linc/$TARGET_HBA_SELFCAL_RESULTS_NAME -O $TARGET_HBA_SELFCAL_RESULTS_NAME && tar xfz $TARGET_HBA_SELFCAL_RESULTS_NAME && rm -f $TARGET_HBA_SELFCAL_RESULTS_NAME
    - wget -nv https://support.astron.nl/software/ci_data/linc/$TARGET_HBA_VLBI_RESULTS_NAME -O $TARGET_HBA_VLBI_RESULTS_NAME && tar xfz $TARGET_HBA_VLBI_RESULTS_NAME && rm -f $TARGET_HBA_VLBI_RESULTS_NAME
  artifacts:
    paths:
      - data


### Stage: run_tests

validate_scripts:
  extends: .run_tests
  script:
    - |
      errors=0
      for file in $(find steps -name "*.cwl")
      do
        cwltool --validate $file || ((errors++))
      done
      ((errors == 0))

blsmooth:
  extends: .run_tests
  script:
    - cwltool --no-container steps/blsmooth.cwl test_jobs/blsmooth.json

find_skymodel_cal:
  extends: .run_tests
  script:
    - cwltool --no-container --preserve-environment PYTHONPATH steps/find_skymodel_cal.cwl test_jobs/find_skymodel_cal.json

check_ateam_separation:
  extends: .run_tests
  script:
    - cwltool --no-container --preserve-environment PATH --preserve-environment PYTHONPATH steps/check_ateam_separation.cwl test_jobs/check_ateam_separation.json

run_hba_calibrator:
  extends: .run_tests
  script:
    - cwltool --no-container --preserve-environment PATH --preserve-environment LINC_DATA_ROOT --preserve-environment PYTHONPATH --outdir results --leave-tmpdir --tmpdir-prefix /tmp/run_hba_calibrator/ workflows/HBA_calibrator.cwl test_jobs/HBA_calibrator.json
    - test_jobs/check_workflow_results.py results $CI_PROJECT_DIR/data/results_calibrator
  after_script:
    - find /tmp/run_hba_calibrator -name "*.log" -print0 | tar czf hba_calibrator_logs.tar.gz --null -T -
  artifacts:
    paths:
      - hba_calibrator_logs.tar.gz
    when: on_failure

run_hba_target:
  extends: .run_tests
  script:
    - cwltool --no-container --preserve-environment PATH --preserve-environment LINC_DATA_ROOT --preserve-environment PYTHONPATH --outdir results --leave-tmpdir --tmpdir-prefix /tmp/run_hba_target/ workflows/HBA_target.cwl test_jobs/HBA_target.json
    - test_jobs/check_workflow_results.py results $CI_PROJECT_DIR/data/results_target
  after_script:
    - find /tmp/run_hba_target -name "*.log" -print0 | tar czf hba_target_logs.tar.gz --null -T -
    - find /tmp/run_hba_target -name "*.png" -print0 | tar czf inspection.tar.gz --null -T -
    - find $CI_PROJECT_DIR/results/ -name "cal_solutions.h5" -print0 | tar czf cal_solutions.tar.gz --null -T -
  artifacts:
    paths:
      - hba_target_logs.tar.gz
      - inspection.tar.gz
      - cal_solutions.tar.gz
    when: on_failure

run_hba_target_selfcal:
  extends: .run_tests
  script:
    - cwltool --no-container --preserve-environment PATH --preserve-environment LINC_DATA_ROOT --preserve-environment PYTHONPATH --outdir results --leave-tmpdir --tmpdir-prefix /tmp/run_hba_target/ workflows/HBA_target.cwl test_jobs/HBA_target_selfcal.json
    - test_jobs/check_workflow_results.py --skip_soltabs TGSSphase_final results $CI_PROJECT_DIR/data/results_target_selfcal
  after_script:
    - find /tmp/run_hba_target -name "*.log" -print0 | tar czf hba_target_selfcal_logs.tar.gz --null -T -
    - find /tmp/run_hba_target -name "*.png" -print0 | tar czf inspection.tar.gz --null -T -
    - find $CI_PROJECT_DIR/results/ -name "cal_solutions.h5" -print0 | tar czf cal_solutions.tar.gz --null -T -
  artifacts:
    paths:
      - hba_target_selfcal_logs.tar.gz
      - inspection.tar.gz
      - cal_solutions.tar.gz
    when: on_failure

run_hba_target_vlbi:
  extends: .run_tests
  script:
    - cwltool --no-container --preserve-environment PATH --preserve-environment LINC_DATA_ROOT --preserve-environment PYTHONPATH --outdir results --leave-tmpdir --tmpdir-prefix /tmp/run_hba_target/ workflows/HBA_target_VLBI.cwl test_jobs/HBA_target.json
    - test_jobs/check_workflow_results.py results $CI_PROJECT_DIR/data/results_target_VLBI
  after_script:
    - find /tmp/run_hba_target -name "*.log" -print0 | tar czf hba_target_vlbi_logs.tar.gz --null -T -
    - find /tmp/run_hba_target -name "*.png" -print0 | tar czf inspection.tar.gz --null -T -
    - find $CI_PROJECT_DIR/results/ -name "cal_solutions.h5" -print0 | tar czf cal_solutions.tar.gz --null -T -
  artifacts:
    paths:
      - hba_target_vlbi_logs.tar.gz
      - inspection.tar.gz
      - cal_solutions.tar.gz
    when: on_failure

run_lba_calibrator:
  extends: .run_tests
  script:
    - cwltool --no-container --preserve-environment PATH --preserve-environment LINC_DATA_ROOT --preserve-environment PYTHONPATH --outdir results --leave-tmpdir --tmpdir-prefix /tmp/run_lba_calibrator workflows/LBA_calibrator.cwl test_jobs/LBA_calibrator.json
    - test_jobs/check_workflow_results.py results $CI_PROJECT_DIR/data/results_calibrator_lba
  after_script:
    - find /tmp/run_lba_calibrator -name "*.log" -print0 | tar czf lba_calibrator_logs.tar.gz --null -T -
  artifacts:
    paths:
      - lba_calibrator_logs.tar.gz
    when: on_failure

run_lba_target:
  extends: .run_tests
  script:
    - cwltool --no-container --preserve-environment PATH --preserve-environment LINC_DATA_ROOT --preserve-environment PYTHONPATH --outdir results --leave-tmpdir --tmpdir-prefix /tmp/run_lba_target/ workflows/LBA_target.cwl test_jobs/LBA_target.json
    - test_jobs/check_workflow_results.py --skip_soltabs GSMtec_final results $CI_PROJECT_DIR/data/results_target_lba
  after_script:
    - find /tmp/run_lba_target -name "*.log" -print0 | tar czf lba_target_logs.tar.gz --null -T -
    - find /tmp/run_lba_target -name "*.png" -print0 | tar czf inspection.tar.gz --null -T -
    - find $CI_PROJECT_DIR/results/ -name "cal_solutions.h5" -print0 | tar czf cal_solutions.tar.gz --null -T -
  artifacts:
    paths:
      - lba_target_logs.tar.gz
      - inspection.tar.gz
      - cal_solutions.tar.gz
    when: on_failure


### Stage: docs

build_doc:
  extends: .docs
  before_script:
    - apt-get update
    - apt-get install -y make
    - pip install sphinx
  script:
    - cd docs
    - make html
  artifacts:
    paths:
      - docs/build/html
  rules:
    # Only add job for commits to the default branch or on merge request events
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "schedule"'
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - changes:
        - docs/**/*


### Stage: deploy

deploy_docker:
  stage: deploy
  extends: .deploy
  script:
    - echo "Deploying to DockerHub, using tag '$LINC_TAG'"
    - docker pull $INTEGRATION_IMAGE
    - docker tag $INTEGRATION_IMAGE astronrd/linc:$LINC_TAG
    - docker tag $INTEGRATION_IMAGE astronrd/linc:latest
    - docker push astronrd/linc:$LINC_TAG
    - docker push astronrd/linc:latest
  rules:
    # Run on the default branch or on a release branch
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
    - if: '$CI_COMMIT_BRANCH =~ /^releases//'
      when: on_success

deploy_docker_tag_stable:
  stage: deploy
  extends: .deploy
  when: manual
  script:
    - docker pull $INTEGRATION_IMAGE
    - docker tag $INTEGRATION_IMAGE astronrd/linc:stable
    - docker push astronrd/linc:stable


### Stage: finalize

rollback_release:
  stage: finalize
  extends: .finalize
  rules:
    # Run this job if the pipeline fails, to undo changes made in prepare_release.
    # We only care about removing the tag; other changes can remain.
    - if: '$CI_COMMIT_BRANCH =~ /^releases//'
      when: on_failure
  script:
    - echo "Rolling back release $RELEASE"
    - |
      if test -f .tag.exists
      then
        echo "*** Not removing existing tag '$RELEASE'! ***"
      else
        git push origin -d $RELEASE
      fi

finalize_release:
  stage: finalize
  extends: .finalize
  rules:
    # Run this job if the pipeline succeeds, to create a versioned release.
    # A versioned release is a release whose branch/tag name matches a string
    # that looks like "v<major-version>[.<minor_version>][...]".
    # We want to ensure that the release tag also exists on the default branch,
    # so that `git describe --tags` yields the desired output. Hence, we must
    # undo the changes made in the `prepare_release` job, before merging to
    # the default branch.
    - if: '$CI_COMMIT_BRANCH =~ /^releases\/v[0-9]+(\.[0-9]+)*/'
      when: on_success
  script:
    - echo "Finalizing release $RELEASE"
    # Update dockerPull image URI in CWL steps by removing version tag
    - sed -ri "/dockerPull/s,(astronrd/linc).*,\1," steps/*.cwl
    - git add -u steps/*.cwl
    # Only commit if there are changes
    - |
      if test -n "$(git status -uno --porcelain)"
      then
        git commit -m "Tag removed from dockerPull URI in CWL steps (by GitLab CI)"
      fi
    # Skip CI on this push
    - git push -o ci.skip
    # Next switch to the default branch and make sure it's up to date
    - git checkout $CI_DEFAULT_BRANCH
    - git pull
    - echo "Merging changes from $CI_COMMIT_BRANCH into $CI_DEFAULT_BRANCH"
    # Merge release branch into the default branch
    - git merge $CI_COMMIT_BRANCH -m"Merged release branch into default branch (by Gitlab CI)"
    # Skip CI on this push
    - git push -o ci.skip
