Interestingly, it is possible to post from any branch, just not from any job.
To do this, I needed to make two changes:
- I need to know the current state of the published data
- I need to change directory based on the current branch
GitLab has the ability to cache folders. Generally this is used to speed up builds by caching downloaded drivers. There is no reasons I could not use this to store the public folder. This way when I make changes to staging, I will remember the state of the root application:
cache:
paths:
- public
The next trick would be to publish pages to the appropriate folder, depending on current branch being built. To do this, we can look to GitLab CI/CD Environment Variables; in particular:
CI_COMMIT_REF_SLUG: The current branchCI_DEFAULT_BRANCH: the default branch (master)
Knowing these two values, we can do a bit of bash to determine the correct place to write the content to.
pages:
stage: deploy
script:
- dir="$CI_COMMIT_REF_SLUG"
- if [ "$CI_COMMIT_REF_SLUG" == "$CI_DEFAULT_BRANCH" ]; then dir=""; fi;
- dir="public/$dir"
- echo "Deploying to $dir"
- mkdir -p
dir
artifacts:
paths:
- public
only:
- staging
- master
Don't forget to limit pages to only staging and master.
WARNING
I'm not satisfied with this.
I think it would be better to maintain the cache somewhere completely different and copy them in at a later stage, but completely re-writing the public folder each time.
The current solution will build cruft over time, but the basic idea is sound.
Answer from Jefferey Cave on Stack OverflowInterestingly, it is possible to post from any branch, just not from any job.
To do this, I needed to make two changes:
- I need to know the current state of the published data
- I need to change directory based on the current branch
GitLab has the ability to cache folders. Generally this is used to speed up builds by caching downloaded drivers. There is no reasons I could not use this to store the public folder. This way when I make changes to staging, I will remember the state of the root application:
cache:
paths:
- public
The next trick would be to publish pages to the appropriate folder, depending on current branch being built. To do this, we can look to GitLab CI/CD Environment Variables; in particular:
CI_COMMIT_REF_SLUG: The current branchCI_DEFAULT_BRANCH: the default branch (master)
Knowing these two values, we can do a bit of bash to determine the correct place to write the content to.
pages:
stage: deploy
script:
- dir="$CI_COMMIT_REF_SLUG"
- if [ "$CI_COMMIT_REF_SLUG" == "$CI_DEFAULT_BRANCH" ]; then dir=""; fi;
- dir="public/$dir"
- echo "Deploying to $dir"
- mkdir -p
dir
artifacts:
paths:
- public
only:
- staging
- master
Don't forget to limit pages to only staging and master.
WARNING
I'm not satisfied with this.
I think it would be better to maintain the cache somewhere completely different and copy them in at a later stage, but completely re-writing the public folder each time.
The current solution will build cruft over time, but the basic idea is sound.
You can only publish changes to GitLab pages through your master branch, just as you describe. The only thing that GitLab pages does though, is to put files in the public folder in the job called pages. These files can be whatever files that you want though, as long as you manage to get them to this folder through the GitLab job.
You could try something like this:
pages:
...
script:
- mkdir -p public
- cp -r www public
- git checkout origin/staging
- mkdir -p public/staging
- cp -r www public/staging
I haven't tested this, so please let me know if it doesn't work!
If you run a GitLab job, it usually has all of the git history of your repo. There are settings that changes this though, both in git and in GitLab, so you have to make sure that you always get all of your git history to the pages job. If you have a folder that hasn't been added to git, like public, git should not change anything in it when you checkout another branch.
I think that you should also be able to set up the GitLab pages job with a schedule, so that the pages job is run even if only the staging branch has been updated, but not the master branch.
git - Deploying GitLab pages for different branches - Stack Overflow
GitLab one page per branch - Stack Overflow
Pages for branches
git - Deploying GitLab pages for different branches using Hugo framework? - Stack Overflow
Hey, so I haven't actually been a HPC systems engineer in years so apologies if this is quite a basic question... :)
Does anyone have experience setting up GitLab Pages to deploy per branch? The use case would be preview builds for merge requests. I have Pages working in a GitLab CI script for a master branch in a test repo, and I have preview builds linked from GitLab MRs ('View app' button) deploying to an S3 bucket with Jenkins totally separately. It would be ideal if we could just use GitLab CI for everything.
Any help appreciated; apologies if I haven't been clear anywhere and happy to try to clarify if needed.
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.
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
Using cache is the right direction, but the solution needs some side features to be fully satisfying.
With below solution, from GitLab Pages per branch : the no-compromise hack to serve preview pages , you can have these features :
maincontent is exposed on$CI_PAGES_URLand the path is configurable with$CURRENT_CONTENT_PATH- 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_PATHvariable 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
pagesfolder - If
maincontent 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:rulescan be deleted if you already have your own, or updated to match your flow - The job must be named
pagesand the artifact must be apublicfolder 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/$CI_DEFAULT_BRANCH ]; 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: preview/$CI_COMMIT_BRANCH # an indirection to allow arbirtraty 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
Try prepending the following to your .gitlab-ci.yml:
cache:
key: "$CI_JOB_NAME"
paths:
- public
Adding a cache step will tell GitLab to keep the specified paths across builds—each new build will see what the previous ones have produced. The key element causes GitLab to keep the cache across branches, rather than keep a separate branch for each cache.
Of course this has the side effect that your pipelines will no longer start off with a fresh public dir—rather, it will contain whatever the last pipeline left in there. Everything any of your pipelines ever places in public will stay there until explicitly deleted or overwritten—you will need to handle that in some way if the set of artifacs changes between builds.
(Haven’t tried this myself yet, but this is how I understand it works.)