stages:
  - install
  - build
  - lint
  - format
  - setup
  - test
  - teardown
  - release

# Fallback variables

variables:
  PLAYWRIGHT_VERSION: $CI_JOB_IMAGE
  TEST_ID: $CI_JOB_ID
  CC_TEST_REPORTER_ID: $CC_TEST_REPORTER_ID
  GCP_ZONE: us-central1-c
  CONTAINER_NAME: gitlab-${CI_PIPELINE_ID}

# Cache Config

.cache:install:modules: &cacheinstallmodules
  key:
    files:
      - yarn.lock
  paths:
    - node_modules

.cache:install:cc: &cachecodeclimate
  key: codeclimate-0.11.1
  paths:
    - tmp/codeclimate

.cache:install:yarn: &cacheinstallyarn
  key: yarn-$CI_JOB_IMAGE
  paths:
    - .yarn

.cache:nx: &cachenx
  key:
    files:
      - yarn.lock
  paths:
    - .nx/cache

#Link and Install all required dependancies

install:
  image: node:20-alpine
  stage: install
  variables:
    NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
  before_script:
    - apk add --no-cache libc6-compat jq
    - echo "PLAYWRIGHT_VERSION=$(yarn info @playwright/test -A --json | jq ".children.Version")" >> install.env
  script:
    - yarn
  cache:
    - <<: *cacheinstallmodules
      policy: pull-push
    - <<: *cacheinstallyarn
      policy: pull-push
  artifacts:
    reports:
      dotenv: install.env

build:
  stage: build
  image: node:20-alpine
  needs:
    - install
  variables:
    NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
  cache:
    - <<: *cacheinstallmodules
      policy: pull
    - <<: *cacheinstallyarn
      policy: pull
    - <<: *cachenx
      policy: pull-push
  before_script:
    - apk add --no-cache libc6-compat git
  script:
    - yarn build
  artifacts:
    paths:
      - packages/core/dist/
      - packages/cli/dist/
      - packages/rest/dist/
      - packages/requester-utils/dist/
    expire_in: 2d

# Lint all code, tests and supporting documentation (README, CHANGELOG etc)

lint:
  stage: lint
  image: node:20-alpine
  needs:
    - install
  variables:
    NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
  cache:
    - <<: *cacheinstallmodules
      policy: pull
    - <<: *cacheinstallyarn
      policy: pull
    - <<: *cachenx
      policy: pull-push
  before_script:
    - apk add --no-cache libc6-compat
  script: yarn lint

format:
  stage: format
  image: node:20-alpine
  needs:
    - install
  variables:
    NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
  cache:
    - <<: *cacheinstallmodules
      policy: pull
    - <<: *cacheinstallyarn
      policy: pull
  before_script:
    - apk add --no-cache libc6-compat
  script: yarn format

## Test Setup

test:unit:setup:
  stage: setup
  image: alpine/curl
  needs: []
  script:
    - mkdir -p tmp/codeclimate
    - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./tmp/codeclimate/cc-test-reporter
    - ls tmp/codeclimate/
    - chmod +x ./tmp/codeclimate/cc-test-reporter
  cache:
    - <<: *cachecodeclimate
      policy: pull-push

test:live:setup:
  stage: setup
  image: gcr.io/google.com/cloudsdktool/cloud-sdk:alpine
  needs: []
  timeout: 15m
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: on_success
      allow_failure: false
    - if: $CI_COMMIT_BRANCH == "next" || $CI_COMMIT_BRANCH =~ /^pr-[0-9]+$/
      when: manual
      allow_failure: true
  variables:
    GITLAB_PERSONAL_ACCESS_TOKEN: gitbeaker
    GITLAB_VERSION: 16.11.3-ce.0
    GITLAB_ROOT_PASSWORD: gitbeaker
  before_script:
    - apk add jq curl envsubst
    - gcloud components install beta -q
  script:
    - echo $GCLOUD_SERVICE_KEY | base64 -d | gcloud auth activate-service-account --key-file=-
    - gcloud --quiet config set project ${GOOGLE_PROJECT_ID} --no-user-output-enabled
    - envsubst < scripts/startup.sh > scripts/startup_filled.sh
    - |
      GITLAB_HOST="$(gcloud beta compute instances create $CONTAINER_NAME \
        --project gitbeaker \
        --machine-type=e2-highmem-2 \
        --boot-disk-size=25GB \
        --zone=$GCP_ZONE \
        --tags http-server \
        --metadata-from-file startup-script=scripts/startup_filled.sh \
        --image-family=debian-12 \
        --image-project=debian-cloud \
        --format='get(networkInterfaces[0].accessConfigs[0].natIP)'
        2>/dev/null \
        )"
    - GITLAB_URL="http://${GITLAB_HOST}"
    - echo "GITLAB_URL=${GITLAB_URL}" >> intergration.env
    - echo "GITLAB_PERSONAL_ACCESS_TOKEN=${GITLAB_PERSONAL_ACCESS_TOKEN}" >> intergration.env
    - echo "Waiting for service to start"
    - sleep 240
    - attempt=1
    - |
      while [[ "$(curl --fail --silent -X GET "$GITLAB_URL/-/readiness?all=1" --insecure | jq -r '.master_check[0].status')" != "ok" ]]; do
        echo "Polling Attempt: $attempt - Gitlab service is not alive yet";
        sleep 10;
        ((attempt++));
      done
    - echo "Service is up and running"
  artifacts:
    reports:
      dotenv: intergration.env

# Tests Templates

.test:base:
  stage: test
  image: node:20-alpine
  needs:
    - install
  variables:
    NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
  cache:
    - <<: *cacheinstallmodules
      policy: pull
    - <<: *cacheinstallyarn
      policy: pull
    - <<: *cachenx
      policy: pull-push
  before_script:
    - apk add --no-cache libc6-compat

.test:unit:base:
  extends: .test:base
  needs:
    - install
    - test:unit:setup
  cache:
    - <<: *cacheinstallmodules
      policy: pull
    - <<: *cacheinstallyarn
      policy: pull
    - <<: *cachecodeclimate
      policy: pull
    - <<: *cachenx
      policy: pull-push
  coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
  before_script:
    - apk add --no-cache libc6-compat git
  after_script:
    - cd $PKG_PATH
    - ../../tmp/codeclimate/cc-test-reporter format-coverage -t lcov -o coverage/cc-coverage.json coverage/lcov.info
  artifacts:
    when: always
    paths:
      - $PKG_PATH/coverage/cc-coverage.json
    reports:
      junit: $PKG_PATH/reports/*junit.xml
      coverage_report:
        coverage_format: cobertura
        path: $PKG_PATH/coverage/cobertura-coverage.xml

.test:types:base:
  extends: .test:base
  needs:
    - build

.test:integration:base:
  extends: .test:base
  needs:
    - build

.test:e2e:base:
  extends: .test:base
  only:
    refs:
      - /^pr-[0-9]+$/
      - main
      - next
  needs:
    - build
    - test:live:setup

  when: on_success

## Unit Tests

test:unit:utils:
  extends: .test:unit:base
  script: yarn test:unit --projects=@gitbeaker/requester-utils
  variables:
    PKG_PATH: 'packages/requester-utils'

test:unit:core:
  extends: .test:unit:base
  script: yarn test:unit --projects=@gitbeaker/core
  variables:
    PKG_PATH: 'packages/core'

test:unit:rest:
  extends: .test:unit:base
  script: yarn test:unit --projects=@gitbeaker/rest
  variables:
    PKG_PATH: 'packages/rest'

test:unit:cli:
  extends: .test:unit:base
  script: yarn test:unit --projects=@gitbeaker/cli
  variables:
    PKG_PATH: 'packages/cli'

## Types Tests

test:types:utils:
  extends: .test:types:base
  variables:
    PKG_PATH: 'packages/requester-utils'
  script: yarn test:types --projects=@gitbeaker/requester-utils

test:types:core:
  extends: .test:types:base
  variables:
    PKG_PATH: 'packages/core'
  script: yarn test:types --projects=@gitbeaker/core

test:types:rest:
  extends: .test:types:base
  variables:
    PKG_PATH: 'packages/rest'
  script: yarn test:types --projects=@gitbeaker/rest

## Integration Tests

test:integration:rest:
  extends: .test:integration:base
  image: mcr.microsoft.com/playwright:v1.45.1-focal
  before_script:
    # reinstall swc due to missing binding for ubuntu image
    - yarn add @swc/core
  script: yarn test:integration --projects=@gitbeaker/rest

test:integration:core:
  extends: .test:integration:base
  script: yarn test:integration --projects=@gitbeaker/core

## End-to-End Tests

test:e2e:rest:
  extends: .test:e2e:base
  image: mcr.microsoft.com/playwright:v1.45.1-focal
  allow_failure: true
  before_script:
    # reinstall swc due to missing binding for ubuntu image
    - yarn add @swc/core
  script: yarn test:e2e --projects=@gitbeaker/rest

test:e2e:cli:
  extends: .test:e2e:base
  allow_failure: true
  script: yarn test:e2e --projects=@gitbeaker/cli

# Teardown

test:unit:teardown:
  stage: teardown
  image: alpine/curl
  except:
    refs:
      - /^pr-[0-9]+$/
  needs:
    - test:unit:utils
    - test:unit:core
    - test:unit:rest
    - test:unit:cli
  when: always
  cache:
    - <<: *cachecodeclimate
      policy: pull
  before_script:
    - mkdir coverage
    - ls tmp/codeclimate
  script:
    - ./tmp/codeclimate/cc-test-reporter sum-coverage packages/*/coverage/cc-coverage.json -p 4 -o ./coverage/codeclimate.json
    - ./tmp/codeclimate/cc-test-reporter upload-coverage -i ./coverage/codeclimate.json

test:live:teardown:
  stage: teardown
  image: gcr.io/google.com/cloudsdktool/cloud-sdk:alpine
  needs:
    - test:e2e:rest
    - test:e2e:cli
  only:
    refs:
      - /^pr-[0-9]+$/
      - main
      - next
  variables:
    GIT_STRATEGY: none
  script:
    - echo $GCLOUD_SERVICE_KEY | base64 -d | gcloud auth activate-service-account --key-file=-
    - gcloud --quiet config set project ${GOOGLE_PROJECT_ID}
    - gcloud compute instances delete $CONTAINER_NAME --zone=$GCP_ZONE

# Releases

.release:base:
  stage: release
  image: node:20-alpine
  variables:
    GIT_STRATEGY: clone
    GIT_DEPTH: 0
    NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
  needs:
    - build # CHECKME: Needed for the artifacts
    - test:unit:utils
    - test:unit:core
    - test:unit:cli
    - test:unit:rest
    - test:types:utils
    - test:types:core
    - test:types:rest
    - test:integration:rest
    - test:integration:core
    - test:e2e:cli
    - test:e2e:rest
  cache:
    - <<: *cacheinstallmodules
      policy: pull
    - <<: *cacheinstallyarn
      policy: pull
  before_script:
    - apk add --no-cache libc6-compat git openssh
    - npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN
    - git config --global user.name "Autobot"
    - git config --global user.email "ci@gitlab.com"
    - git remote set-url origin https://jdalrymple:${GITHUB_TOKEN}@github.com/jdalrymple/gitbeaker.git
    - git branch $CI_COMMIT_BRANCH HEAD
    - git checkout $CI_COMMIT_BRANCH
    - echo 'Ensure build files are present'
    - ls -A packages/requester-utils/dist
    - ls -A packages/core/dist
    - ls -A packages/rest/dist
    - ls -A packages/cli/dist

## Canary or RC Special Releases

release:special:
  extends: .release:base
  only:
    refs:
      - /^pr-[0-9]+$/
  script:
    - echo "Checking for special release..."
    - |
      if [[ $CI_COMMIT_BRANCH =~ ^pr-[0-9]*$ ]]; then
        pr_id="${CI_COMMIT_BRANCH//[!0-9]/}"

        export CI_MERGE_REQUEST_ID=$pr_id
        export CI_MERGE_REQUEST_SOURCE_BRANCH_NAME=$CI_COMMIT_BRANCH

        labels=$(yarn auto label --pr $pr_id)

        if [[ "$labels" =~ "release:canary" ]]; then
          yarn release:canary
        fi
      fi

## Production

release:
  extends: .release:base
  only:
    refs:
      - main
      - next
  script: yarn release
