Commit 6a12e394 authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

feat: multi S3 providers support

BREAKING CHANGE:
- S3_ROOT_PATH replaced with S3_PREFIX
- S3_REGION is now required to enable bucket creation
parent 88eeaa54
Loading
Loading
Loading
Loading
+29 −11
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@ This project implements a generic GitLab CI template for any [S3](https://en.wik

This is a basic and very cheap solution to host static pages websites as well as [progressive web applications](https://en.wikipedia.org/wiki/Progressive_web_application).

The template uses [s3cmd](https://github.com/s3tools/s3cmd) to control the S3 API endpoint and uploading objects.

## Overview

This template implements continuous delivery/continuous deployment for projects hosted on S3 platforms.
@@ -63,13 +65,29 @@ The S3 template uses some global configuration used throughout all jobs.
| Name                   | description                                   | default value     |
| ---------------------- | --------------------------------------------- | ----------------- |
| `S3_CMD_IMAGE`         | The Docker image used to run [s3cmd](https://s3tools.org/usage) commands | `d3fk/s3cmd:latest` |
| `S3_ENDPOINT_HOST`     | Default S3 endpoint hostname (and port)       | **has to be defined** |
| `S3_ENDPOINT_HOST`     | Default S3 endpoint hostname (with port)      | `s3.amazonaws.com` (AWS) |
| `S3_HOST_BUCKET`       | Default DNS-style bucket+hostname:port template for accessing a bucket | `%(bucket)s.$S3_ENDPOINT_HOST` |
| `S3_REGION`            | Default region to create the buckets in (if not defined, the template won't create any) | _none_ |
| :lock: `S3_ACCESS_KEY` | Default S3 service Access Key                 | **has to be defined** |
| :lock: `S3_SECRET_KEY` | Default S3 service Secret Key                 | **has to be defined** |
| `S3_BASE_BUCKET_NAME`  | Base bucket name                              | `$CI_PROJECT_NAME` ([see GitLab doc](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)) |
| `S3_PREFIX`            | Default S3 prefix to use as a root destination to upload objects in the S3 bucket | _none_ |

#### Using other S3-compatible systems than AWS

The template might be used with other storage systems provided they are implementing a compatible API. 
In that case, you'll have to override the default `$S3_ENDPOINT_HOST` and `$S3_WEBSITE_ENDPOINT`variables.

Google Cloud Platform supports [S3 interoperability](https://cloud.google.com/storage/docs/interoperability). The corresponding configuration is:

| Name                   | Value for GCP     |
| ---------------------- | ----------------- |
| `S3_ENDPOINT_HOST`     | `storage.googleapis.com` |
| `S3_WEBSITE_ENDPOINT`  | _website hosting in GCP not supported by `s3cmd`_ |

S3 interoperability support from Microsoft Azure requires using [Minio](https://min.io/).
Read [this article](https://cloudblogs.microsoft.com/opensource/2017/11/09/s3cmd-amazon-s3-compatible-apps-azure-storage/) for further information.

### Secrets management

Here are some advices about your **secrets** (variables marked with a :lock:): 
@@ -110,12 +128,11 @@ Here are variables supported to configure review environments:
| Name                     | description                            | default value     |
| ------------------------ | -------------------------------------- | ----------------- |
| `S3_REVIEW_DISABLED`     | Set to `true` to disable `review` environments         | _none_ (enabled) |
| `S3_REVIEW_ENDPOINT_HOST`| S3 endpoint hostname (and port) for `review` env  _(only define if different from default)_ | `$S3_ENDPOINT_HOST` |
| `S3_REVIEW_ENDPOINT_HOST`| S3 endpoint hostname (with port) for `review` env  _(only define if different from default)_ | `$S3_ENDPOINT_HOST` |
| `S3_REVIEW_REGION`       | Region to create the `review` buckets in (if not defined, the template won't create any) | `$S3_REGION` |
| :lock: `S3_REVIEW_ACCESS_KEY` | S3 service Access Key for `review` env  _(only define if different from default)_    | `$S3_ACCESS_KEY` |
| :lock: `S3_REVIEW_SECRET_KEY` | S3 service Secret Key for `review` env  _(only define if different from default)_    | `$S3_SECRET_KEY` |
| `S3_REVIEW_BUCKET_NAME`  | Bucket name for `review` env      | `"${S3_BASE_BUCKET_NAME}-${CI_ENVIRONMENT_SLUG}"` (ex: `myproject-review-fix-bug-12`) |
| `S3_REVIEW_ENVIRONMENT_SCHEME` | The review environment protocol scheme | `https` |
| `S3_REVIEW_ENVIRONMENT_DOMAIN` | The review environment domain. | _none_ |
| `S3_REVIEW_PREFIX`  |  S3 prefix to use for `review` env _(only define if different from default)_ | `S3_PREFIX` |

Note: By default, review `environment.url` will be built as `${S3_REVIEW_ENVIRONMENT_SCHEME}://${$CI_PROJECT_NAME}-${CI_ENVIRONMENT_SLUG}.${S3_REVIEW_ENVIRONMENT_DOMAIN}`
@@ -131,11 +148,11 @@ Here are variables supported to configure the integration environment:
| Name                     | description                            | default value     |
| ------------------------ | -------------------------------------- | ----------------- |
| `S3_INTEG_DISABLED`      | Set to `true` to disable the `integration` environment | _none_ (enabled) |
| `S3_INTEG_ENDPOINT_HOST` | S3 endpoint hostname (and port) for `integration` env  _(only define if different from default)_    | `$S3_ENDPOINT_HOST` |
| `S3_INTEG_ENDPOINT_HOST` | S3 endpoint hostname (with port) for `integration` env  _(only define if different from default)_    | `$S3_ENDPOINT_HOST` |
| `S3_INTEG_REGION`        | Region to create the `integration` bucket in | `$S3_REGION` |
| :lock: `S3_INTEG_ACCESS_KEY` | S3 service Access Key for `integration` env  _(only define if different from default)_    | `$S3_ACCESS_KEY` |
| :lock: `S3_INTEG_SECRET_KEY` | S3 service Secret Key for `integration` env  _(only define if different from default)_    | `$S3_SECRET_KEY` |
| `S3_INTEG_BUCKET_NAME`   | Bucket name for `integration` env | `${S3_BASE_BUCKET_NAME}-integration` |
| `S3_INTEG_ENVIRONMENT_URL` | The integration environment url **including scheme** (ex: `https://my-project-integration.s3-website.nonpublic.domain.com`). Do not use variable inside variable definition as it will result in a two level cascade variable and gitlab does not allow that. | _none_ |
| `S3_INTEG_PREFIX`  |  S3 prefix to use for `integration` env _(only define if different from default)_ | `S3_PREFIX` |

#### Staging environment
@@ -150,11 +167,11 @@ Here are variables supported to configure the staging environment:
| Name                     | description                            | default value     |
| ------------------------ | -------------------------------------- | ----------------- |
| `S3_STAGING_DISABLED`    | Set to `true` to disable the `staging` environment     | _none_ (enabled) |
| `S3_STAGING_ENDPOINT_HOST`| S3 endpoint hostname (and port) for `staging` env  _(only define if different from default)_   | `$S3_ENDPOINT_HOST` |
| `S3_STAGING_ENDPOINT_HOST`| S3 endpoint hostname (with port) for `staging` env  _(only define if different from default)_   | `$S3_ENDPOINT_HOST` |
| `S3_STAGING_REGION`      | Region to create the `staging` bucket in | `$S3_REGION` |
| :lock: `S3_STAGING_ACCESS_KEY` | S3 service Access Key for `staging` env  _(only define if different from default)_    | `$S3_ACCESS_KEY` |
| :lock: `S3_STAGING_SECRET_KEY` | S3 service Secret Key for `staging` env  _(only define if different from default)_    | `$S3_SECRET_KEY` |
| `S3_STAGING_BUCKET_NAME` | Bucket name for `staging` env     | `${S3_BASE_BUCKET_NAME}-staging` |
| `S3_STAGING_ENVIRONMENT_URL` | The staging environment url **including scheme** (ex: `https://my-project-staging.s3-website.nonpublic.domain`). Do not use variable inside variable definition as it will result in a two level cascade variable and gitlab does not allow that. | _none_ |
| `S3_STAGING_PREFIX`  |  S3 prefix to use for `staging` env _(only define if different from default)_ | `S3_PREFIX` |

#### Production environment
@@ -168,11 +185,11 @@ Here are variables supported to configure the production environment:
| Name                     | description                            | default value     |
| ------------------------ | -------------------------------------- | ----------------- |
| `S3_PROD_DISABLED`       | Set to `true` to disable the `production` environment  | _none_ (enabled) |
| `S3_PROD_ENDPOINT_HOST`  | S3 endpoint hostname (and port) for `production` env  _(only define if different from default)_| `$S3_ENDPOINT_HOST` |
| `S3_PROD_ENDPOINT_HOST`  | S3 endpoint hostname (with port) for `production` env  _(only define if different from default)_| `$S3_ENDPOINT_HOST` |
| `S3_PROD_REGION`         | Region to create the `production` bucket in | `$S3_REGION` |
| :lock: `S3_PROD_ACCESS_KEY` | S3 service Access Key for `production` env  _(only define if different from default)_    | `$S3_ACCESS_KEY` |
| :lock: `S3_PROD_SECRET_KEY` | S3 service Secret Key for `production` env  _(only define if different from default)_    | `$S3_SECRET_KEY` |
| `S3_PROD_BUCKET_NAME`    | Bucket name for `production` env  | `$S3_BASE_BUCKET_NAME` |
| `S3_PROD_ENVIRONMENT_URL`| The production environment url **including scheme** (ex: `https://my-project.s3-website.public.domain.com`) Do not use variable inside variable definition as it will result in a two level cascade variable and gitlab does not allow that. | _none_ |
| `AUTODEPLOY_TO_PROD`     | Set this variable to auto-deploy to production. If not set deployment to production will be `manual` (default behaviour). | _none_ (disabled) |
| `S3_PROD_PREFIX`  |  S3 prefix to use for `production` env _(only define if different from default)_ | `S3_PREFIX` |

@@ -234,6 +251,7 @@ It uses the following variables:
| `S3_DEPLOY_FILES`      | Pattern(s) of files to deploy to the S3 bucket| `public/` _(all files from `public` directory)_ |
| `S3_WEBSITE_DISABLED`  | Set to `true` to disable WebSite hosting by your S3 bucket    | _none_ (enabled by default) |
| `S3_WEBSITE_ARGS`      | [s3cmd](https://s3tools.org/usage) command and options to enable WebSite hosting on the bucket | `ws-create --ws-index=index.html` |
| `S3_WEBSITE_ENDPOINT`  | Default WebSite endpoint url pattern (supports `%(bucket)s` and `%(location)s` placeholders).<br/>_only required when website hosting is not disabled_ | `http://%(bucket)s.s3-website.%(location)s.amazonaws.com` |

If need be you could add your own hook script `s3-pre-deploy.sh` that will be triggered right before deploying files to
the S3 bucket.
+31 −34
Original line number Diff line number Diff line
@@ -12,7 +12,8 @@
    },
    {
      "name": "S3_ENDPOINT_HOST",
      "description": "Default S3 endpoint hostname (and port)",
      "description": "Default S3 endpoint hostname (with port)",
      "default": "s3.amazonaws.com",
      "mandatory": true
    },
    {
@@ -21,6 +22,10 @@
      "default": "%(bucket)s.$S3_ENDPOINT_HOST",
      "mandatory": true
    },
    {
      "name": "S3_REGION",
      "description": "Default region to create the buckets in (if not defined, the template won't create any)"
    },
    {
      "name": "S3_ACCESS_KEY",
      "description": "Default S3 service Access Key",
@@ -55,6 +60,11 @@
      "description": "Disables WebSite hosting by your S3 bucket",
      "type": "boolean"
    },
    {
      "name": "S3_WEBSITE_ENDPOINT",
      "description": "Default WebSite endpoint url pattern (supports `%(bucket)s` and `%(location)s` placeholders)",
      "default": "http://%(bucket)s.s3-website.%(location)s.amazonaws.com"
    },
    {
      "name": "S3_WEBSITE_ARGS",
      "description": "[s3cmd](https://s3tools.org/usage) command and options to enable WebSite hosting on the bucket",
@@ -75,7 +85,11 @@
      "variables": [
        {
          "name": "S3_REVIEW_ENDPOINT_HOST",
          "description": "S3 endpoint hostname (and port) for `review` env  _(only define if different from default)_"
          "description": "S3 endpoint hostname (with port) for `review` env  _(only define if different from default)_"
        },
        {
          "name": "S3_REVIEW_REGION",
          "description": "Region to create the `review` buckets in (if not defined, the template won't create any)"
        },
        {
          "name": "S3_REVIEW_ACCESS_KEY",
@@ -93,17 +107,6 @@
          "default": "${S3_BASE_BUCKET_NAME}-${CI_ENVIRONMENT_SLUG}",
          "advanced": true
        },
        {
          "name": "S3_REVIEW_ENVIRONMENT_SCHEME",
          "description": "The review environment protocol scheme",
          "default": "https",
          "mandatory": true
        },
        {
          "name": "S3_REVIEW_ENVIRONMENT_DOMAIN",
          "description": "The review environment domain (ex: `s3-website.nonpublic.domain`).\n\nBy default review `environment.url` will be built as `${S3_REVIEW_ENVIRONMENT_SCHEME}://${$CI_PROJECT_NAME}-${CI_ENVIRONMENT_SLUG}.${S3_REVIEW_ENVIRONMENT_DOMAIN}`",
          "mandatory": true
        },
        {
          "name": "CLEANUP_ALL_REVIEW",
          "description": "Enables a **manual** job to cleanup all review envs at once.\n\nYou may also use it to [schedule](https://docs.gitlab.com/ee/ci/pipelines/schedules.html) cloud resources cleanup. See documentation.",
@@ -124,7 +127,11 @@
      "variables": [
        {
          "name": "S3_INTEG_ENDPOINT_HOST",
          "description": "S3 endpoint hostname (and port) for `integration` env  _(only define if different from default)_"
          "description": "S3 endpoint hostname (with port) for `integration` env  _(only define if different from default)_"
        },
        {
          "name": "S3_INTEG_REGION",
          "description": "Region to create the `integration` bucket in"
        },
        {
          "name": "S3_INTEG_ACCESS_KEY",
@@ -142,12 +149,6 @@
          "default": "${S3_BASE_BUCKET_NAME}-integration",
          "advanced": true
        },
        {
          "name": "S3_INTEG_ENVIRONMENT_URL",
          "type": "url",
          "description": "The integration environment url including scheme (ex: `https://my-project-integration.s3-website.nonpublic.domain`).\n\nDo not use variable inside variable definition as it will result in a two level cascade variable and gitlab does not allow that.",
          "mandatory": true
        },
        {
          "name": "S3_INTEG_PREFIX",
          "description": "S3 prefix to use for `integration` env _(only define if different from default)_",
@@ -163,7 +164,11 @@
      "variables": [
        {
          "name": "S3_STAGING_ENDPOINT_HOST",
          "description": "S3 endpoint hostname (and port) for `staging` env  _(only define if different from default)_"
          "description": "S3 endpoint hostname (with port) for `staging` env  _(only define if different from default)_"
        },
        {
          "name": "S3_STAGING_REGION",
          "description": "Region to create the `staging` bucket in"
        },
        {
          "name": "S3_STAGING_ACCESS_KEY",
@@ -181,12 +186,6 @@
          "default": "${S3_BASE_BUCKET_NAME}-staging",
          "advanced": true
        },
        {
          "name": "S3_STAGING_ENVIRONMENT_URL",
          "type": "url",
          "description": "The staging environment url including scheme (ex: `https://my-project-staging.s3-website.nonpublic.domain`).\n\nDo not use variable inside variable definition as it will result in a two level cascade variable and gitlab does not allow that.",
          "mandatory": true
        },
        {
          "name": "S3_STAGING_PREFIX",
          "description": "S3 prefix to use for `staging` env _(only define if different from default)_",
@@ -202,7 +201,11 @@
      "variables": [
        {
          "name": "S3_PROD_ENDPOINT_HOST",
          "description": "S3 endpoint hostname (and port) for `production` env  _(only define if different from default)_"
          "description": "S3 endpoint hostname (with port) for `production` env  _(only define if different from default)_"
        },
        {
          "name": "S3_PROD_REGION",
          "description": "Region to create the `production` bucket in"
        },
        {
          "name": "S3_PROD_ACCESS_KEY",
@@ -220,12 +223,6 @@
          "default": "${S3_BASE_BUCKET_NAME}",
          "advanced": true
        },
        {
          "name": "S3_PROD_ENVIRONMENT_URL",
          "type": "url",
          "description": "The production environment url including scheme (ex: `https://my-project.s3-website.public.domain.com`).\n\nDo not use variable inside variable definition as it will result in a two level cascade variable and gitlab does not allow that.",
          "mandatory": true
        },
        {
          "name": "AUTODEPLOY_TO_PROD",
          "type": "boolean",
+38 −26
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ variables:
  TBC_TRACKING_IMAGE: "$CI_REGISTRY/to-be-continuous/tools/tracking:master"

  S3_CMD_IMAGE: "d3fk/s3cmd:latest"
  # defaults to AWS
  S3_ENDPOINT_HOST: "s3.amazonaws.com"
  S3_HOST_BUCKET: "%(bucket)s.$S3_ENDPOINT_HOST"
  S3_BASE_BUCKET_NAME: "$CI_PROJECT_NAME"
  S3_SCRIPTS_DIR: "."
@@ -34,13 +36,8 @@ variables:
  S3_DEPLOY_ARGS: "put --acl-public --no-mime-magic --guess-mime-type --recursive"
  S3_DEPLOY_FILES: "public/"
  S3_WEBSITE_ARGS: "ws-create --ws-index=index.html"

  # for backwards compatibility
  S3_PREFIX: "$S3_ROOT_PATH"
  S3_REVIEW_PREFIX: "$S3_REVIEW_ROOT_PATH"
  S3_INTEG_PREFIX: "$S3_INTEG_ROOT_PATH"
  S3_STAGING_PREFIX: "$S3_STAGING_ROOT_PATH"
  S3_PROD_PREFIX: "$S3_PROD_ROOT_PATH"
  # defaults to AWS
  S3_WEBSITE_ENDPOINT: "http://%(bucket)s.s3-website.%(location)s.amazonaws.com"

  # default production ref name (pattern)
  PROD_REF: '/^(master|main)$/'
@@ -271,7 +268,7 @@ stages:
    access_key=$3
    secret_key=$4

    echo -e "[default]\\nhost_base = $endpoint_host\\nhost_bucket = $host_bucket\\naccess_key = $access_key\\nsecret_key = $secret_key" > ~/.s3cfg
    echo -e "[default]\\nhost_base = $endpoint_host\\nhost_bucket = $host_bucket\\nwebsite_endpoint = $S3_WEBSITE_ENDPOINT\\naccess_key = $access_key\\nsecret_key = $secret_key" > ~/.s3cfg

    # shellcheck disable=SC2154
    proxy_ic=${https_proxy:-$HTTPS_PROXY}
@@ -284,9 +281,9 @@ stages:

    if s3cmd ls >/dev/null
    then
      log_info "... Login to S3 \\e[33;1msuccedeed\\e[0m"
      log_info "... Login to API endpoint \\e[33;1msuccedeed\\e[0m"
    else
      log_error "... Login to S3 \\e[33;1mfailed\\e[0m"
      log_error "... Login to API endpoint \\e[33;1mfailed\\e[0m"
      exit 1
    fi
  }
@@ -296,11 +293,12 @@ stages:
    export env=$1
    bucket=$2
    prefix=$3
    # normalize prefix with trailing slash
    # normalize prefix with leading slash
    if [[ "$prefix" ]] && [[ "${prefix:0:1}" != "/" ]]
    then
      prefix="/$prefix"
    fi
    region=$4

    # extract hostname from $CI_ENVIRONMENT_URL
    hostname=$(echo "$CI_ENVIRONMENT_URL" | awk -F[/:] '{print $4}')
@@ -308,6 +306,7 @@ stages:

    log_info "--- \\e[32mdeploy\\e[0m (env: \\e[33;1m${env}\\e[0m)"
    log_info "--- bucket: \\e[33;1m${bucket}\\e[0m"
    log_info "--- region: \\e[33;1m${region}\\e[0m"
    log_info "--- prefix: \\e[33;1m${prefix:-(none)}\\e[0m"
    log_info "--- env: \\e[33;1m${env}\\e[0m"
    log_info "--- hostname: \\e[33;1m${hostname}\\e[0m"
@@ -315,11 +314,16 @@ stages:
    # maybe create bucket
    if s3cmd info "s3://${bucket}" >/dev/null 2>&1
    then
      log_info "... S3 bucket \\e[33;1m${bucket}\\e[0m found: continue"
    else
      log_info "... S3 bucket \\e[33;1m${bucket}\\e[0m not found: create"
      log_info "... bucket \\e[33;1m${bucket}\\e[0m found: continue"
    elif [[ "$region" ]]
    then
      log_info "... bucket \\e[33;1m${bucket}\\e[0m not found: create (in \\e[33;1m${region}\\e[0m)"
      # shellcheck disable=SC2086
      s3cmd ${TRACE+-v} mb "s3://${bucket}"
      s3cmd ${TRACE+-v} mb --region "$region" "s3://${bucket}"
    else
      log_error "... bucket \\e[33;1m${bucket}\\e[0m not found and region not specified: fail"
      log_error "... either create it or set the region to enable auto-create (see doc)"
      exit 1
    fi

    # maybe execute pre deploy script
@@ -344,11 +348,16 @@ stages:
      log_info "... create website"
      # shellcheck disable=SC2086
      s3cmd ${TRACE+-v} $S3_WEBSITE_ARGS "s3://${bucket}"
    fi

      # obtain website url
      website_url=$(s3cmd ws-info "s3://${bucket}" | grep -i "website endpoint" | cut -d':' -f2- | tr -d '[:space:]')
      # remote trailing slash
      website_url=${website_url%/}
      # build environment url (with prefix)
      environment_url="$website_url$prefix"
      # finally persist environment url
    echo "$CI_ENVIRONMENT_URL" > environment_url.txt
    echo -e "environment_type=$env\\nenvironment_name=$bucket\\nenvironment_url=$CI_ENVIRONMENT_URL" > s3.env
      echo "$environment_url" > environment_url.txt
      echo -e "environment_type=$env\\nenvironment_name=$bucket\\nenvironment_url=$environment_url" > s3.env
    fi
  }

  # delete application
@@ -356,7 +365,7 @@ stages:
    export env=$1
    bucket=$2
    prefix=$3
    # normalize prefix with trailing slash
    # normalize prefix with leading slash
    if [[ "$prefix" ]] && [[ "${prefix:0:1}" != "/" ]]
    then
      prefix="/$prefix"
@@ -370,7 +379,7 @@ stages:
    # check if bucket exists
    if ! s3cmd info "s3://${bucket}" >/dev/null
    then
      log_info "... S3 bucket \\e[33;1m${bucket}\\e[0m not found: skip"
      log_info "... bucket \\e[33;1m${bucket}\\e[0m not found: skip"
      return
    fi

@@ -439,6 +448,7 @@ stages:
#
# @arg ENV_TYPE         : environment type
# @arg ENV_BUCKET_NAME  : env-specific S3 bucket name
# @arg ENV_REGION       : env-specific region
# @arg ENV_PREFIX       : env-specific S3 prefix
# @arg ENV_APP_SUFFIX   : env-specific application suffix
# @arg ENV_ENDPOINT_HOST: env-specific S3 endpoint host
@@ -458,13 +468,15 @@ stages:
    - assert_defined "${ENV_SECRET_KEY:-$S3_SECRET_KEY}" 'Missing required S3 secret key'
    - login "${ENV_ENDPOINT_HOST:-$S3_ENDPOINT_HOST}" "${S3_HOST_BUCKET}" "${ENV_ACCESS_KEY:-$S3_ACCESS_KEY}" "${ENV_SECRET_KEY:-$S3_SECRET_KEY}"
  script:
    - deploy "$ENV_TYPE" "${ENV_BUCKET_NAME:-${S3_BASE_BUCKET_NAME}${ENV_APP_SUFFIX}}" "${ENV_PREFIX:-${S3_PREFIX}}"
    - deploy "$ENV_TYPE" "${ENV_BUCKET_NAME:-${S3_BASE_BUCKET_NAME}${ENV_APP_SUFFIX}}" "${ENV_PREFIX:-${S3_PREFIX}}" "${ENV_REGION:-${S3_REGION}}"
  artifacts:
    name: "$ENV_TYPE env url for $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
    paths:
      - environment_url.txt
    reports:
      dotenv: s3.env
  environment:
    url: "$environment_url" # can be either static or dynamic

# Cleanup job prototype
# Can be extended for each deletable environment
@@ -504,13 +516,13 @@ s3-review:
  variables:
    ENV_TYPE: review
    ENV_BUCKET_NAME: "$S3_REVIEW_BUCKET_NAME"
    ENV_REGION: "$S3_REVIEW_REGION"
    ENV_PREFIX: "$S3_REVIEW_PREFIX"
    ENV_ENDPOINT_HOST: "$S3_REVIEW_ENDPOINT_HOST"
    ENV_ACCESS_KEY: "$S3_REVIEW_ACCESS_KEY"
    ENV_SECRET_KEY: "$S3_REVIEW_SECRET_KEY"
  environment:
    name: review/$CI_COMMIT_REF_NAME
    url: "${S3_REVIEW_ENVIRONMENT_SCHEME}://${CI_PROJECT_NAME}-${CI_ENVIRONMENT_SLUG}.${S3_REVIEW_ENVIRONMENT_DOMAIN}"
    on_stop: s3-cleanup-review
  resource_group: review/$CI_COMMIT_REF_NAME
  rules:
@@ -596,13 +608,13 @@ s3-integration:
  variables:
    ENV_TYPE: integration
    ENV_BUCKET_NAME: "$S3_INTEG_BUCKET_NAME"
    ENV_REGION: "$S3_INTEG_REGION"
    ENV_PREFIX: "$S3_INTEG_PREFIX"
    ENV_ENDPOINT_HOST: "$S3_INTEG_ENDPOINT_HOST"
    ENV_ACCESS_KEY: "$S3_INTEG_ACCESS_KEY"
    ENV_SECRET_KEY: "$S3_INTEG_SECRET_KEY"
  environment:
    name: integration
    url: "${S3_INTEG_ENVIRONMENT_URL}"
  resource_group: integration
  rules:
    # exclude if $S3_INTEG_DISABLED set
@@ -618,13 +630,13 @@ s3-staging:
  variables:
    ENV_TYPE: staging
    ENV_BUCKET_NAME: "$S3_STAGING_BUCKET_NAME"
    ENV_REGION: "$S3_STAGING_REGION"
    ENV_PREFIX: "$S3_STAGING_PREFIX"
    ENV_ENDPOINT_HOST: "$S3_STAGING_ENDPOINT_HOST"
    ENV_ACCESS_KEY: "$S3_STAGING_ACCESS_KEY"
    ENV_SECRET_KEY: "$S3_STAGING_SECRET_KEY"
  environment:
    name: staging
    url: "${S3_STAGING_ENVIRONMENT_URL}"
  resource_group: staging
  rules:
    # exclude if $S3_STAGING_DISABLED set
@@ -642,13 +654,13 @@ s3-production:
    ENV_TYPE: production
    ENV_APP_SUFFIX: "" # no suffix for prod
    ENV_BUCKET_NAME: "$S3_PROD_BUCKET_NAME"
    ENV_REGION: "$S3_PROD_REGION"
    ENV_PREFIX: "$S3_PROD_PREFIX"
    ENV_ENDPOINT_HOST: "$S3_PROD_ENDPOINT_HOST"
    ENV_ACCESS_KEY: "$S3_PROD_ACCESS_KEY"
    ENV_SECRET_KEY: "$S3_PROD_SECRET_KEY"
  environment:
    name: production
    url: "${S3_PROD_ENVIRONMENT_URL}"
  resource_group: production
  rules:
    # exclude non-production branches