Skip to content

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 (but public 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:

  1. 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 classic artifacts property if they want to publish artifacts from that repo too, but it would be ignored by the pages server)

  2. 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.

Edited by Janis Altherr