Deploying GitLab pages for different branches
Asked Answered
L

6

52

I am deploying my React app using GitLab Pages, and it works well.

Here is my gitlab-ci.yml:

# Using the node alpine image to build the React app
image: node:alpine

# Announce the URL as per CRA docs
# https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#advanced-configuration
variables:
  PUBLIC_URL: /
# Cache node modules - speeds up future builds
cache:
  paths:
  - client/node_modules

# Name the stages involved in the pipeline
stages:
  - deploy

# Job name for gitlab to recognise this results in assets for Gitlab Pages
# https://docs.gitlab.com/ee/user/project/pages/introduction.html#gitlab-pages-requirements
pages:
  stage: deploy
  script:
    - cd client
    - npm install # Install all dependencies
    - npm run build --prod # Build for prod
    - cp public/index.html public/404.html # Not necessary, but helps with https://medium.com/@pshrmn/demystifying-single-page-applications-3068d0555d46
    - mv public _public # CRA and gitlab pages both use the public folder. Only do this in a build pipeline.
    - mv build ../public # Move build files to public dir for Gitlab Pages
  artifacts:
    paths:
    - public # The built files for Gitlab Pages to serve
  only:
    - master # Only run on master branch

Now, I just created a dev version, based on my branch develop

I would like to have 2 versions of my React app with 2 different URLs. How can I do that?

For example right now, I have:

my-react-app.com linked to master branch

How should I have

dev.my-react-app.com or even my-react-app.gitlab.io linked to develop branch?<

Luminary answered 9/4, 2019 at 15:54 Comment(1)
did you achieve custom domain like dev.my-react-app.com to the develop branch?Ashcroft
K
44

I've had success using the browsable artifacts for this purpose. In your example, you would create a job for your develop branch and set the PUBLIC_URL to the path on gitlab.io where the job's artifacts are published:

develop:
    artifacts:
        paths:
          - public

    environment:
        name: Develop
        url: "https://$CI_PROJECT_NAMESPACE.gitlab.io/-/$CI_PROJECT_NAME/-/jobs/$CI_JOB_ID/artifacts/public/index.html"

    script: |
        # whatever

    stage: deploy

    variables:
        PUBLIC_URL: "/-/$CI_PROJECT_NAME/-/jobs/$CI_JOB_ID/artifacts/public"

Setting the environment as indicated produces a »Review app« link in relevant merge requests, allowing you to get to the artifacts with a single click.

Note: if your repository is in a subgroup, you need to insert the subgroup name in two places above above between /-/ and $CI_PROJECT_NAME for the resulting URLs to work.

Kelikeligot answered 15/10, 2019 at 21:22 Comment(13)
I'm not sure that this works in all cases, particularly when the repo is in a subgroup, which changes the pattern of the $CI_ENVIRONMENT_URL. See this question for more detail.Cathar
can you share a public repo containing this?Ashcroft
@KarlAnthonyBaluyot this minimal repo by a previous commenter uses the technique, albeit slightly modified for a project in a subgroup. Does this help you?Kelikeligot
Yes thanks @joki. Have you tried using custom domain for the develop branch gitlab page?Ashcroft
@KarlAnthonyBaluyot I haven't tried it, but it should be fairly straightforward by replacing gitlab.io by your custom domain in environment.url and configuring your page generator for your custom domain.Kelikeligot
The documentation for this feature is at docs.gitlab.com/ee/ci/yaml/#environmenturlCoulson
@Kelikeligot that was an excellent suggestion - that repo is great - thanks!Botticelli
You should be able to use ${CI_SERVER_PROTOCOL}://${CI_PROJECT_ROOT_NAMESPACE}.${CI_PAGES_DOMAIN}/-/${CI_PROJECT_PATH#"${CI_PROJECT_ROOT_NAMESPACE}/"}/-/jobs/$CI_JOB_ID/artifacts/public/index.html to make the URL independant from your Pages Domain and if the project is in a subgroup or not.Indiscretion
@Indiscretion I cannot make this working in the environment.url of my gitlab-ci.yml since there is no variable substitution happening here (I think).Paquin
@Paquin Yea you might be correct, that variable substitution does not happen makes sense. I am using this somewhere in the script section and actually did not test it on the environment.url field. Mhmm, I looked closely through the predefined gitlab varibles and unfortunatly did not found anything which yields the same result as ${CI_PROJECT_PATH#"${CI_PROJECT_ROOT_NAMESPACE}/"}.Indiscretion
Is there a way to avoid specifying the index.html at the end of the URL?Ainsley
indeed, it's kinda broken without specifying the index.html at the end of the URL. In case of work in mkdocs, for instanceMosira
This should now be possible using the dynamic url approach shown here: docs.gitlab.com/ee/ci/environments/…Varia
I
32

It is possible to keep several pages published for different pipelines/branches.

To do that you need to copy your pages content (basically test report, or whatever needs to be published) to specific unique directory in public folder. For example, the name of the directory can be the id of pipeline (CI_PIPELINE_ID). So the path to pages sources would be like public/$CI_PIPELINE_ID/.

Then the whole public folder should be defined as artifacts with specific unique name (here again "$CI_PIPELINE_ID" can be used).

Unique name for artifacts is needed to not override the artifacts with the next pipeline execution (if name is not specified, the default name will be taken https://docs.gitlab.com/ee/ci/yaml/#artifactsname).

Then you can access the published report via the link:

https://yourGitlab/yourNamespace/yourProjectName/{CI_PIPELINE_ID}/index.html

, that means you can access all your saved reports by changing the pipeline id.

My example:

stages:
  - publish

cache:
  # Required to keep artifacts from old builds, e.g. from master
  paths:
    - public

pages:
  stage: publish
  script:
    - mkdir -p public/$CI_PIPELINE_ID
    - cp target/site/allure-maven-plugin/* public/$CI_PIPELINE_ID/ -R
  artifacts:
    name: "$CI_PIPELINE_ID"
    paths:
      - public
    expire_in: 5 days
  when: always
Inalienable answered 18/11, 2019 at 13:17 Comment(9)
Can you post a link to a GitLab repo where this actually works? It does not seem to work for me: gitlab.com/ostrokach/deletemeSkeptical
Hi, I cannot post a link to a repo, because it is private. Can you try exactly the same as I proposed (I mean copy the folders structure to public)? Because I see that you do it in other wayInalienable
Hi VladyslavPyrozhok, thanks for your contribution! I tried your example, based on @Skeptical code, but didn't give expected result. Each time I'll run the pipeline, my previously deployed file won't be present anymore. Have a look : gitlab.com/alvaro.costa/deleteme/blob/master/.gitlab-ci.ymlHarrell
Sorry guys, I just missed one small piece of code in pipeline file: cache: paths: - public So, please, add it and I hope it will work. I tested in my repo here https://gitlab.com/pyrozhok.vladyslav/pages-test , so you can check. The results are report_1, report_2 . I updated the response.Inalienable
Also make sure, that you run several pipelines with the same gitlab-ci fileInalienable
Did you have a chance to check? @alvaro-costaInalienable
Sorry Vladyslav, I didn't see you answered until you tag me. :-(Harrell
About your solution, it indeed works by making a clever use of GitLab cache... but it's a dirty solution since it isn't based on what's already available on GitLab page. If your cache is cleaned or expires (enterprises usually put an expiry time), you lose everything.. I'd suggest looking at joki's solution instead. :)Harrell
@alvaro-costa If your project is public and you can publish your results to the world then it will work. Otherwise if you cannot open your web-page to the internet (like in my case) so it is not a handy solution. Anyway, choose whatever fits you better :)Inalienable
L
15

Every GitLab project can have at most one Pages site. I can't find an explicit reference for this, but the documentation for .gitlab-ci.yml says:

Be aware that Pages are by default branch/tag agnostic and their deployment relies solely on what you specify in .gitlab-ci.yml. If you don’t limit the pages job with the only parameter, whenever a new commit is pushed to whatever branch or tag, the Pages will be overwritten.

Without the only parameter, updates to any branch publish to the same Pages site, overwriting whatever is there. With the only parameter, only the provided branch will trigger a Pages build.

Lithophyte answered 9/4, 2019 at 16:38 Comment(1)
This is really important. I just ran into the issue where the production version from the master branch was replaced by a job on a branch. I'm not sure what changed; I used to be able to access the master branch results on https://[…]/ and the other branch results on https://[…]/branch-name with a single job deploying to the same environment.Khanna
E
3

Other answers are not fully satisfying :

  • relying on artifacts make the address complex and changing
  • caching in folders based in pipeline ID has the same problems plus never ending size increase (no cleaning)

Using cache is the right direction, but the solution needs more side features.

With below solution, from GitLab Pages per branch : the no-compromise hack to serve preview pages, you can have these features :

  • main (or more precisely your $CI_DEFAULT_BRANCH) content is exposed on $CI_PAGES_URL and the path is configurable with $CURRENT_CONTENT_PATH from the rules section
  • Per-branch preview content is exposed on $CI_PAGES_URL/preview, with a homepage to easily navigate to branches content
  • Path to root preview folder is configurable with $EPHEMERAL_BRANCHES_PATH variable to hide preview content by obfuscation
  • Generated pages are associated with environments to take advantage of auto-cleaning on branch deletion
  • To avoid disturbing already existing environments, pages environment are placed under a pages folder
  • If main content has not been generated in current cache, or if the cache has been deleted, an error is triggered, to avoid accidental deletion
  • Deletion job can be triggered manually with any cache path as input, to clean outdated data
  • Code can safely be added to an existing project pipeline without causing trouble with already existing jobs
  • The workflow:rules can be deleted if you already have your own, or updated to match your flow
  • The job must be named pages and the artifact must be a public folder to be deployed to GitLab Pages (or you can use the pages:publish keyword)
workflow:
  rules: # disable tag pipelines and duplicate MR pipelines
    - if: $CI_COMMIT_BRANCH

variables:
  EPHEMERAL_BRANCHES_PATH: preview # subpath to ephemeral branches content for preview, anything will work

pages:
  stage: build
  image: alpine:3.18
  cache:
    key: gitlab-pages
    paths: [public]
  before_script:
    # default available 'tree' app in alpine image does not work as intended
    - apk add tree
    # CURRENT_CONTENT_PATH is defined in rules, different between main branch and ephemeral branches
    - mkdir -p public/$CURRENT_CONTENT_PATH && ls public/$CURRENT_CONTENT_PATH/..
    - | # avoid deleting main branch content when cache has been erased
      if [ "$CI_COMMIT_BRANCH" != "$CI_DEFAULT_BRANCH" ] && [ ! -d public/index.html ]; then
        echo -e "💥\e[91;1m Unable to retrieve $CI_DEFAULT_BRANCH generated files from cache ; please regenerate $CI_DEFAULT_BRANCH files first\e[0m"
        exit 1
      fi
    - rm -rf public/$CURRENT_CONTENT_PATH || true # remove last version of current branch
  script:
    - ./generate-my-html.sh --output build-docs || true # insert here your code that generates documentation
    - mv --verbose build-docs public/$CURRENT_CONTENT_PATH
    - cd public/$EPHEMERAL_BRANCHES_PATH
    - tree -d -H '.' -L 1 --noreport --charset utf-8 -T "Versions" -o index.html # generate a root HTML listing all previews for easier access
  environment:
    name: pages/$CI_COMMIT_BRANCH
    action: start
    url: $CI_PAGES_URL/$CURRENT_CONTENT_PATH
    on_stop: pages-clean-preview
  rules:
    # 'main branch' is exposed at GitLab Pages root
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      variables:
        CURRENT_CONTENT_PATH: "."
    # other (short-lived) branches generation are exposed in 'EPHEMERAL_BRANCHES_PATH/branch-name-sanitized' sub path
    - variables:
        CURRENT_CONTENT_PATH: $EPHEMERAL_BRANCHES_PATH/$CI_COMMIT_REF_SLUG
  artifacts:
    paths: [public]
    expire_in: 1h

pages-clean-preview:
  stage: build
  image: alpine:3.18
  cache:
    key: gitlab-pages
    paths: [public]
  variables:
    GIT_STRATEGY: none # git files not available after branch deletion
    FOLDER_TO_DELETE: $EPHEMERAL_BRANCHES_PATH/$CI_COMMIT_REF_SLUG # an indirection to allow arbitrary deletion when launching this job
  script:
    - rm -rf public/$FOLDER_TO_DELETE
  environment:
    name: pages/$CI_COMMIT_BRANCH
    action: stop
  rules:
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
      when: manual
      allow_failure: true
Evincive answered 4/8, 2023 at 19:24 Comment(4)
While adapting, I've found proposed code never creates the directory public/$CI_DEFAULT_BRANCHPeskoff
If you mean that there is no subfolder for the $CI_DEFAULT_BRANCH, this is intended, you can put any folder path in the line CURRENT_CONTENT_PATH: "." if you want to change the behavior.Evincive
I meant for the ` [ ! -d public/$CI_DEFAULT_BRANCH ]` test which seem uselessPeskoff
Totally right, answer fixed, replaced with [ ! -d public/index.html ]. It was a bad copy paste from a version where every branches content where in subfolders.Evincive
L
1

Since version 16.7, GitLab offers a new feature for multiple pages deployments with the keyword pages.path_prefix.

Official example for per branch deployment:

pages:
  stage: deploy
  script:
    - echo "Pages accessible through ${CI_PAGES_URL}/${CI_COMMIT_BRANCH}"
  pages:
    path_prefix: "$CI_COMMIT_BRANCH"
  artifacts:
    paths:
    - public

But as of 04.04.2024 it has experimental status and is only available for self-managed instances.

Leonie answered 4/4 at 16:47 Comment(2)
As of 25.06.2024 this feature is already available on GitLab.com, Self-managed, GitLab Dedicated, but only for Premium and Ultimate tiers.Wane
As of 20240717: "On GitLab.com and GitLab Dedicated, this feature is not available. This feature is not ready for production use."Escent
A
0

I faced this problem as well and first found the gitlab-versioned-pages python package. This works for some time until the pages size and amount of files increases above the GitLab Pages size limit. Sure you can increase that limit, but that does not scale or is sufficient. Same for copying new content into a specific folder, like proposed above with

cp target/site/allure-maven-plugin/* public/$CI_PIPELINE_ID/ -R

I therefore created a the lightweight-versioned-gitlab-pages package, mirrored to Github. Instead of copying or downloading the artifacts and archiving it again until infinity, the script generates a index page with "just" the URLs to the (already) archived artifacts. No dublicated content, lightweight, scalable and you can serve whatever content you want, from any job that ran on any branch. You can use it on selfhosted GitLab instances and of course on GitLab.

The docs for that package are provided via GitLab pages using the package itself. The root page is not beautiful, but it's just a template that can be customized as required.

The only limit or requirement is to have this job running on a tag. Just try it out, report issues or ask for further details

Auberge answered 18/2, 2023 at 7:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.