Skip to content

Truly Dynamic Environment URLs

Description

Current Implementation

create_env:
  stage: review
  when: manual
  script: echo "Create platform environment and create a dynamic variable called FANCY_SLUG"
  environment:
    name: env/$CI_BUILD_REF_SLUG
    url: https://$CI_BUILD_REF_SLUG-$FANCY_SLUG-$APPS_DOMAIN
    on_stop: delete_env
  • $APPS_DOMAIN is a YAML-defined variable (not job-level)
  • $FANCY_SLUG is a variable that, ideally, could be set during the script: portion of the job

Limitation

  • Environment urls in .gitlab-ci.yml definitions only expand CI-* variables

Example image

Using a popular hosting platform (https://platform.sh/) I can create environments pretty easily by installing their CLI and using authentication tokens. But the entire integration requires that I have some dynamic variable ability in setting the environment URL. Their URL has a token that is unique per branch, something that requires the script step of the review stage.

Functional Work Around

Let me upfront tell you that I'm probably the worst dev ops to tell you how or what you should do. The below works for me and I see it as a stop-gap measure and provide no warranty whatsoever.

<job>:
  stage: review
  when: manual
  script:
    - export ENVURL='http://replacethiswithanAPIgeneratedURL.com/'
    - export PRIVATE_TOKEN='I recommend you set a project variable called PRIVATE_TOKEN and drop your private token in there.'
    - export GITLAB_URL='https://gitlab.websites-r-us.com'

    # Using the API to bypass the limitation 
    # @see https://docs.gitlab.com/ce/api/enviroments.html#environments
    # I clearly don't know how to script in this environment
    # See this issue for something better hopefully: 
    - export ENVIRONMENT_REQ_HEADER="PRIVATE-TOKEN:"" "$PRIVATE_TOKEN
    - export ENV_ID_REQ_URL=$GITLAB_URL"/api/v3/projects/$CI_PROJECT_ID/environments"
    - export JSON=$(curl --header "$ENVIRONMENT_REQ_HEADER" "$ENV_ID_REQ_URL")
    - export PARSE_JSON='$r = json_decode(fgets(STDIN)); foreach ($r as $e) if ($e->name == "'$CI_ENVIRONMENT_NAME'") { echo $e->id; break;}'
    - export CI_ENVIRONMENT_ID=$(echo $JSON | php -r "$PARSE_JSON")
    - export ENV_SET_URL=$GITLAB_URL"/api/v3/projects/$CI_PROJECT_ID/environments/$CI_ENVIRONMENT_ID"
    - curl --request PUT --data "external_url=$ENVURL" --header "$ENVIRONMENT_REQ_HEADER" "$ENV_SET_URL"
  environment:
    name: $CI_BUILD_REF_SLUG
    on_stop: delete_env
  only:
    - branches
  except:
    - master
  • Caveat: This will not work if your CI_ENVIRONMENT_NAME is not unique. Though, I can't imagine how that would happen
  • OMG! You used something that is stupid. ... I would really like the above snippet to either get better or for us to not have to revert to using the API to make a URL dynamic.
  • I've not tested on a new environment yet, but I expect it to not work.

Proposal

I propose that all variables from the stage in which an environment is created be available during the URL definition. I've not written Ruby before, but I did read through the code that expands this url variable. I think supporting some way to pass at least one variable from a contextually related script: declaration ought to be possible.

If you did support a script-created variable for URLs, I could do something like this :heart::

image