image: tcitworld/mobilizon-ci

stages:
  - install
  - check
  - build-js
  - test
  - docker
  - package
  - upload
  - deploy

variables:
  MIX_ENV: "test"
  YARN_CACHE_FOLDER: "js/.yarn"
  # DB Variables for Postgres / Postgis
  POSTGRES_DB: mobilizon_test
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: postgres
  POSTGRES_HOST: postgres
  # DB Variables for Mobilizon
  MOBILIZON_DATABASE_USERNAME: $POSTGRES_USER
  MOBILIZON_DATABASE_PASSWORD: $POSTGRES_PASSWORD
  MOBILIZON_DATABASE_DBNAME: $POSTGRES_DB
  MOBILIZON_DATABASE_HOST: $POSTGRES_HOST
  GEOLITE_CITIES_PATH: "/usr/share/GeoIP/GeoLite2-City.mmdb"
  MOBILIZON_INSTANCE_REGISTRATIONS_OPEN: "true"
  # Release elements
  PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${CI_PROJECT_NAME}"
  ARCH: "amd64"
  EXPORT_FORMATS: "csv,ods,pdf"
  APP_VERSION: "${CI_COMMIT_REF_NAME}"
  APP_ASSET: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_${ARCH}.tar.gz"
  CYPRESS_INSTALL_BINARY: 0

cache:
  key: "${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}"
  paths:
    - ~/.cache/Cypress
    - cache/Cypress
    - deps/
    - _build/
    - js/node_modules
    - js/.yarn

# Installed dependencies are cached across the pipeline
# So there is no need to reinstall them all the time
# It saves minutes during a pipeline build time
install:
  stage: install
  script:
    - yarn --cwd "js" install --frozen-lockfile
    - mix deps.get
    - mix compile

lint-elixir:
  stage: check
  before_script:
    - mix deps.get
  script:
    - export EXITVALUE=0
    - git fetch origin ${CI_DEFAULT_BRANCH}
    - TARGET_SHA1=$(git show-ref -s ${CI_DEFAULT_BRANCH})
    - echo "$TARGET_SHA1"
    - mix format --check-formatted --dry-run || export EXITVALUE=1
    - mix credo diff --from-git-merge-base $TARGET_SHA1 --strict -a || export EXITVALUE=1
    - mix sobelow --config || export EXITVALUE=1
    - exit $EXITVALUE

lint-front:
  image: node:16
  stage: check
  before_script:
    - export EXITVALUE=0
    - yarn --cwd "js" install --frozen-lockfile
  script:
    - yarn --cwd "js" run lint || export EXITVALUE=1
    - yarn --cwd "js" run prettier -c . || export EXITVALUE=1
    - exit $EXITVALUE

build-frontend:
  stage: build-js
  image: node:16
  before_script:
    - apt update
    - apt install -y --no-install-recommends python build-essential webp imagemagick gifsicle jpegoptim optipng pngquant
  script:
    - yarn --cwd "js" install --frozen-lockfile
    - yarn --cwd "js" run build
  artifacts:
    expire_in: 5 days
    paths:
      - priv/static
  needs:
    - lint-front

deps:
  stage: check
  before_script:
    - mix deps.get
  script:
    - export EXITVALUE=0
    - mix hex.outdated || export EXITVALUE=1
    - yarn --cwd "js" outdated || export EXITVALUE=1
    - exit $EXITVALUE
  allow_failure: true
  needs:
    - install

exunit:
  stage: test
  services:
    - name: postgis/postgis:13-3.1
      alias: postgres
  variables:
    MIX_ENV: test
  before_script:
    - mix deps.get && mix tz_world.update
    - mix ecto.create
    - mix ecto.migrate
  script:
    - mix coveralls
  artifacts:
    when: always
    reports:
      junit:
        - test-junit-report.xml
    expire_in: 30 days

jest:
  stage: test
  needs:
    - lint-front
  before_script:
    - yarn --cwd "js" install --frozen-lockfile
  script:
    - yarn --cwd "js" run test:unit --no-color --ci --reporters=default --reporters=jest-junit
  artifacts:
    when: always
    paths:
      - js/coverage
    reports:
      junit:
        - js/junit.xml
    expire_in: 30 days

# cypress:
#   stage: test
#   services:
#     - name: postgis/postgis:13.3
#       alias: postgres
#   variables:
#     MIX_ENV=e2e
#   script:
#     - mix ecto.create
#     - mix ecto.migrate
#     - mix run priv/repo/e2e.seed.exs
#     - mix phx.server &
#     - cd js
#     - npx wait-on http://localhost:4000
#     - if [ -z "$CYPRESS_KEY" ]; then npx cypress run; else npx cypress run --record --parallel --key $CYPRESS_KEY; fi
#   artifacts:
#     expire_in: 2 day
#     paths:
#       - js/tests/e2e/screenshots/**/*.png
#       - js/tests/e2e/videos/**/*.mp4

pages:
  stage: deploy
  script:
    - mkdir public
    - mix deps.get
    - mix docs
    - mv doc public/backend
  #     #- yarn run --cwd "js" styleguide:build
  #     #- mv js/styleguide public/frontend
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
  artifacts:
    expire_in: 1 hour
    paths:
      - public

.docker: &docker
  stage: docker
  image: docker:20.10.12
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
    DOCKER_HOST: tcp://docker:2376
    DOCKER_TLS_VERIFY: 1
    DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
    DOCKER_DRIVER: overlay2
  services:
    - docker:20.10.12-dind
  cache: {}
  before_script:
    # Install buildx
    - wget https://github.com/docker/buildx/releases/download/v0.8.1/buildx-v0.8.1.linux-amd64
    - mkdir -p ~/.docker/cli-plugins/
    - mv buildx-v0.8.1.linux-amd64 ~/.docker/cli-plugins/docker-buildx
    - chmod a+x ~/.docker/cli-plugins/docker-buildx
    # Create env
    - docker context create tls-environment
    - docker buildx create --use tls-environment
    # Install qemu/binfmt
    - docker pull tonistiigi/binfmt:latest
    - docker run --rm --privileged tonistiigi/binfmt:latest --install all
    # Login to DockerHub
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$CI_REGISTRY_AUTH\",\"email\":\"$CI_REGISTRY_EMAIL\"}}}" > ~/.docker/config.json
  tags:
    - "privileged"

build-docker-main:
  <<: *docker
  rules:
    - if: '$CI_PROJECT_NAMESPACE != "framasoft"'
      when: never
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
  script:
    - docker buildx build --push --platform linux/amd64 -t framasoft/mobilizon:main -f docker/production/Dockerfile .

build-docker-tag:
  <<: *docker
  rules: &tag-rules
    - if: '$CI_PROJECT_NAMESPACE != "framasoft"'
      when: never
    - if: $CI_COMMIT_TAG
  timeout: 3 hours
  script:
    - >
      docker buildx build
      --push
      --platform linux/amd64,linux/arm64,linux/arm
      -t framasoft/mobilizon:$CI_COMMIT_TAG
      -t framasoft/mobilizon:latest
      -f docker/production/Dockerfile .

# Packaging app for amd64
package-app:
  stage: package
  variables: &release-variables
    MIX_ENV: "prod"
  script: &release-script
    - mix local.hex --force
    - mix local.rebar --force
    - mix deps.get --only-prod
    - mix compile
    - mix phx.digest.clean --all && \
    - mix release --path release/mobilizon
    - cd release/mobilizon && ln -s lib/mobilizon-*/priv priv && cd ../../
    - du -sh release/
    - 'echo "Artifact: ${APP_ASSET}"'
    - tar czf ${APP_ASSET} -C release mobilizon
    - du -sh ${APP_ASSET}
  only:
    - tags@framasoft/mobilizon
  artifacts:
    expire_in: 2 days
    paths:
      - ${APP_ASSET}

package-app-dev:
  stage: package
  variables: *release-variables
  script: *release-script
  except:
    - tags@framasoft/mobilizon
  artifacts:
    expire_in: 2 days
    paths:
      - ${APP_ASSET}

# Packaging app for multi-arch
multi-arch-release:
  stage: package
  image: docker:20.10.12
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
    DOCKER_HOST: tcp://docker:2376
    DOCKER_TLS_VERIFY: 1
    DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
    DOCKER_DRIVER: overlay2
    APP_ASSET: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_${ARCH}.tar.gz"
  services:
    - docker:20.10.12-dind
  cache: {}
  before_script:
    # Install buildx
    - wget https://github.com/docker/buildx/releases/download/v0.8.1/buildx-v0.8.1.linux-amd64
    - mkdir -p ~/.docker/cli-plugins/
    - mv buildx-v0.8.1.linux-amd64 ~/.docker/cli-plugins/docker-buildx
    - chmod a+x ~/.docker/cli-plugins/docker-buildx
    # Create env
    - docker context create tls-environment
    - docker buildx create --use tls-environment
    # Install qemu/binfmt
    - docker pull tonistiigi/binfmt:latest
    - docker run --rm --privileged tonistiigi/binfmt:latest --install all
  script:
    - docker buildx build --platform linux/${ARCH} --output type=local,dest=releases --build-arg APP_ASSET=${APP_ASSET} -f docker/multiarch/Dockerfile .
    - ls -alh releases/mobilizon/
    - du -sh releases/mobilizon/${APP_ASSET}
    - mv releases/mobilizon/${APP_ASSET} .
  tags:
    - "privileged"
  artifacts:
    expire_in: 2 days
    paths:
      - ${APP_ASSET}
  parallel:
    matrix:
      - ARCH: ["arm", "arm64"]
  rules:
    - if: '$CI_PROJECT_NAMESPACE != "framasoft"'
      when: never
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
    - if: $CI_COMMIT_TAG
  timeout: 3h

# Release
release-upload:
  stage: upload
  image: framasoft/upload-packages:latest
  variables:
    APP_ASSET: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_${ARCH}.tar.gz"
  rules: *tag-rules
  script:
    - eval `ssh-agent -s`
    - ssh-add <(echo "${DEPLOYEMENT_KEY}" | base64 --decode -i)
    - echo "put -r ${APP_ASSET}" | sftp -o "VerifyHostKeyDNS yes" ${DEPLOYEMENT_USER}@${DEPLOYEMENT_HOST}:public/
  artifacts:
    expire_in: 1 day
    when: on_success
    paths:
      - mobilizon_*.tar.gz
  parallel:
    matrix:
      - ARCH: ["amd64", "arm", "arm64"]

release-create:
  stage: deploy
  image: registry.gitlab.com/gitlab-org/release-cli:latest
  rules: *tag-rules
  variables:
    APP_ASSET_AMD64: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_amd64.tar.gz"
    APP_ASSET_ARM: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_arm.tar.gz"
    APP_ASSET_ARM64: "${CI_PROJECT_NAME}_${CI_COMMIT_REF_NAME}_arm64.tar.gz"
  before_script:
    - apk --no-cache add gawk sed grep
  script: |
    CHANGELOG=$(awk -v version="$APP_VERSION" '/^## / { printit = $2 == version }; printit' CHANGELOG.md | grep -v "## $APP_VERSION" | sed '1{/^$/d}')
    ENDPOINT="https://packages.joinmobilizon.org"

    release-cli create  --name "$CI_COMMIT_TAG" \
                        --description "$CHANGELOG" \
                        --tag-name "$CI_COMMIT_TAG" \
                        --assets-link "{\"name\":\"${APP_ASSET_AMD64}\",\"url\":\"${ENDPOINT}/${APP_ASSET_AMD64}\"}" \
                        --assets-link "{\"name\":\"${APP_ASSET_ARM}\",\"url\":\"${ENDPOINT}/${APP_ASSET_ARM}\"}" \
                        --assets-link "{\"name\":\"${APP_ASSET_ARM64}\",\"url\":\"${ENDPOINT}/${APP_ASSET_ARM64}\"}"