Allow customizing the artifacts path ("public") in GitLab Pages
Reasoning
Currently GitLab Pages will only publish built files from a folder named public
inside the project root.
Except for a few minor Frameworks this requires changing the default behaviour of the framework the user is using:
For example, Next.js uses .next
, Nuxt and Astro use dist
, Eleventy uses _site
, etc... — what all of them have in common is the fact, that the public
folder (sometimes in the root directory, sometimes not) often has a slightly different use: It stores static public files that don't need to go through the build process.
This means that in order to deploy a site with pages not only does the user need to configure the build path, they also often need to change where files from the original public
folders live.
So the default behaviour of Pages is almost never what the user needs. It almost always is in conflict with the default behaviour of the frameworks. This causes confusion because it's not clearly documented (gitlab#340682).
The most user-centric way to approach this is to refactor GitLab Pages so that it obtains the artifacts folder from the .gitlab-ci.yml
file and uses whatever has been specified there.
Proposal
The initial idea
The easiest way would be to just drop the requirement entirely and do the following:
- If there's only one folder in artifacts, use that as the pages root
- If there's multiple folders, do some educated guessing which one to use (quoted from the original MR):
In the case where
artifacts.paths
contains more than one folder, it will check for the presence of any of the following folders. These are the output folders generated by the most popular frontend frameworks, ordered by the popularity of the related framework (butpublic
first, because backwards compatibility):-
public
(previous GitLab behaviour, Hugo, Gatsby, Svelte) -
build
(React) -
dist
(Vue, Nuxt.js, Angular, Astro, Vite) -
out
(Next.js) -
_site
(Eleventy, Jekyll)
-
The problem with that approach is that it would change GitLab Pages behavior for existing pages. If someone had previously uploaded files inside the pages
job that's not in a public
directory, they would start to be exposed at the time the change came into effect.
Although it's rather unlikely that users uploaded artifacts in the pages job they did not want to be exposed, this case cannot be excluded, so following the principle of least surprise, we should require the user to opt in to a behaviour like this.
The better idea
Let's introduce a new property to the pipeline. One that's specific to the pages
job. Say, publish
pages:
script: ...
publish: some/dir
So this new publish
property has a semantic meaning: "This is the folder I want to publish".
In the background this does two things:
-
It behaves as
artifacts.paths
with a single entry. (Pages needs a single root directory anyway, so it doesn't make much sense to allow more than one dir, at least for Pages. The user is allowed to add a classicartifacts
property if they want to publish artifacts from that repo too, but it would be ignored by the pages server) -
It causes the GitLab Pages server to treat a folder of that name as the pages root. I guess the best implementation to do this would be via the API, but I'm open to suggestions.
Now, if there's no publish
property in the pages job definition, we just keep the legacy behaviour: Without a publish
property, Pages will only ever expose the public
folder. No surprises for anyone. Users would have to explicitly enable the new behaviour.