Skip to content

Deploy on GitLab Pages

Render this repository's CI job file for GitLab, include it from your root .gitlab-ci.yml, then add the pages job GitLab itself requires to publish the built site — ocx-catalog ci renders the build/verify half only; the pages job is yours to write.

Render the CI job file

Add a ci block to catalog.config.json:

{
  "ci": { "forge": "gitlab" }
}

Then run:

npx ocx-catalog ci

This writes .gitlab-ci/catalog.yml — a job file, not a root .gitlab-ci.yml. Add it to your own root file's include: block, exactly as the rendered file's own header comment says:

include:
  - local: ".gitlab-ci/catalog.yml"

With packageManager defaulted to npm and verifyCi defaulted to true, a first render (no committed pins to carry forward — GitLab jobs pin by image:, not uses:) produces:

# generated by ocx-catalog ci v1 — do not edit
#
# Rendered by `ocx-catalog ci` from this repository's own catalog.config.json
# `ci` block. Not a root .gitlab-ci.yml — this repository's own root file
# includes it:
#
#     include:
#       - local: ".gitlab-ci/catalog.yml"
#
# Do not hand-edit this file — change the config instead and re-render:
#
#     npx ocx-catalog ci
#
# `ci.verifyCi: false` in catalog.config.json omits the catalog:verify job
# below (`ocx-catalog ci --check`) and stops CI from catching a hand-edit —
# the repository then owns this file by hand instead.
catalog:build:
  image: node:22-alpine
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - npm ci
    - npx ocx-catalog build
  artifacts:
    paths:
      - dist

catalog:verify:
  image: node:22-alpine
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - npm ci
    - npx ocx-catalog ci --check

Both jobs run on a merge request pipeline and on the default branch; catalog:build keeps dist as an artifact, catalog:verify re-renders the job file in memory and fails if it has drifted from catalog.config.json (see CI rendering). Set "verifyCi": false in the ci block to omit catalog:verify.

Set "packageManager": "bun" and the image and script lines change instead:

image: node:22-alpine
script:
  - npm ci
  - npx ocx-catalog build
image: oven/bun:1
script:
  - bun install --frozen-lockfile
  - bun x ocx-catalog build

Add the pages job

GitLab Pages has its own contract, independent of this package: the publishing job must be literally named pages, and its artifacts must include the directory it publishes — conventionally public. Neither requirement comes from ocx-catalog; add the job yourself in your root .gitlab-ci.yml, alongside the include: block above:

pages:
  image: node:22-alpine
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - npm ci
    - npx ocx-catalog build --out public
  artifacts:
    paths:
      - public

--out public builds straight into the directory GitLab publishes, so no separate move step is needed. The rules: entry limits the job to the default branch — GitLab publishes whatever the most recent successful pages job on that branch produced.

The default project Pages URL is a subpath

A generated catalog site cannot be served from a URL subpath: the build never emits a VitePress base:, and the theme's data fetches (/data/catalog/catalog.json, /p/<ns>/<pkg>.json, and the OCI image index fetches) are literal root-relative strings. The default GitLab Pages URL — https://<group>.gitlab.io/<project>/ — is a subpath, so every asset and every one of those fetches 404s once deployed there. This is GitLab's own project-Pages URL topology, not a missing option in this package: enable GitLab Pages' unique domain for the project (a dedicated root URL GitLab assigns per project) or attach a custom domain at the domain root instead.

The mirrored /p/* tree also ships an inert _headers file — GitLab Pages never reads it. See Hosting and headers for what that means and what to do about it.