Skip to content

GitLab CI

On a privileged GitLab runner, OmniPackage works the same way as on your machine. The job container has podman. OmniPackage starts one container per distro inside it with podman run --mount .... You do not need a special runtime or a Docker-in-Docker service.

Requirements

  • Privileged jobs. On a self-hosted runner, set privileged = true under [runners.docker] (or [runners.kubernetes]) in config.toml. GitLab.com hosted Linux runners already allow privileged jobs, because they support Docker-in-Docker.
  • A job image with podman. The examples use quay.io/podman/stable. It has curl, and it stores containers in the /var/lib/containers volume, so nested containers work.

With the shell executor on a host that has podman or docker, you need neither. Remove image: and run the same commands.

Pipeline

flowchart LR
    G["generate"] -->|release.yml| R["release child pipeline<br/>one job per distro"]
    G -->|prime.yml| P["prime child pipeline<br/>one job per distro"]
    P -->|push| REG[("GitLab Container Registry")]
    REG -.->|pull| R
    R -->|upload| S3[("S3 repository")]

The generate job reads builds: with omnipackage info --list-distros. It writes a child pipeline with one job per distro. This works like the GitHub Actions matrix: when you add a build target to config.yml, you get a new job.

.gitlab-ci.yml:

include:
  - local: .gitlab/omnipackage.yml

variables:
  OMNIPACKAGE_REPOSITORY: My packages
  OMNIPACKAGE_IMAGE_CACHE: ""

workflow:
  rules:
    - if: $CI_COMMIT_TAG
    - if: $CI_PIPELINE_SOURCE == "schedule"
    - if: $CI_PIPELINE_SOURCE == "web"

stages: [generate, run]

generate:
  extends: .omnipackage
  stage: generate
  script:
    - |
      for kind in release prime; do
        echo "include: [{local: .gitlab/omnipackage.yml}]" > "$kind.yml"
        for distro in $(omnipackage info . --list-distros); do
          printf '%s:%s:\n  extends: .omnipackage-%s\n  variables:\n    DISTRO: "%s"\n' "$kind" "$distro" "$kind" "$distro" >> "$kind.yml"
        done
      done
  artifacts:
    paths: [release.yml, prime.yml]

release:
  stage: run
  needs: [generate]
  trigger:
    include:
      - artifact: release.yml
        job: generate
    strategy: depend
  rules:
    - if: $CI_PIPELINE_SOURCE != "schedule"

prime:
  stage: run
  needs: [generate]
  trigger:
    include:
      - artifact: prime.yml
        job: generate
    strategy: depend
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
    - if: $CI_PIPELINE_SOURCE == "web"
      when: manual
      allow_failure: true

.gitlab/omnipackage.yml has the job templates for both child pipelines:

.omnipackage:
  image: quay.io/podman/stable
  before_script:
    - curl -fsSL https://repositories.omnipackage.org/omnipackage-rs/stable/install.sh | sh -s -- -y

.omnipackage-release:
  extends: .omnipackage
  resource_group: omnipackage-$DISTRO
  script:
    - omnipackage release . --repository "$OMNIPACKAGE_REPOSITORY" --distros "$DISTRO" ${OMNIPACKAGE_IMAGE_CACHE:+--image-cache "$OMNIPACKAGE_IMAGE_CACHE"}

.omnipackage-prime:
  extends: .omnipackage
  script:
    - omnipackage prime . --image-cache gitlab --distros "$DISTRO"

For fedora_42, the release child pipeline gets this job:

release:fedora_42:
  extends: .omnipackage-release
  variables:
    DISTRO: "fedora_42"

Notes:

  • Triggers. Tags and manual runs ("Run pipeline") release packages. Schedules only prime the image cache. Change workflow:rules if you need other triggers. For example, add $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH and a separate repositories: entry to publish every push to the default branch, like the GitHub setup.
  • resource_group runs only one release job per distro at a time, even across pipelines. This is like the GitHub concurrency group. Two jobs that write to the same repository at the same time break its metadata.
  • Variables. GitLab passes top-level variables: to child pipelines. Set OMNIPACKAGE_REPOSITORY and OMNIPACKAGE_IMAGE_CACHE once, in .gitlab-ci.yml.
  • Architecture. Packages are built for the runner's CPU. For aarch64, use runner tags (for example saas-linux-small-arm64), the stable-aarch64 install URL, and a separate repositories: entry. Arch and Manjaro are x86_64 only.
  • Storage errors. If podman fails with overlay mount errors, add STORAGE_DRIVER: vfs to .omnipackage. It is slower, but it works on any filesystem.

Image cache on GitLab Container Registry

image_caches:
  - name: gitlab
    provider: registry
    image_tag: omnipackage-cache
    registry:
      url: "${CI_REGISTRY}"
      namespace: "${CI_PROJECT_PATH}"
      username: "${CI_REGISTRY_USER}"
      password: "${CI_REGISTRY_PASSWORD}"

The images are $CI_REGISTRY_IMAGE/<distro_id>:omnipackage-cache. GitLab sets all four variables in every job. The job token can push to and pull from the project's own registry. The namespace must be lowercase. If your project path has capital letters, write the namespace by hand.

Steps:

  1. Run the pipeline once with an empty OMNIPACKAGE_IMAGE_CACHE. Release jobs start from plain distro images and run setup themselves.
  2. Run prime. Start a pipeline manually and run the prime job, or add a schedule. Once a month is enough. Also prime again when build_dependencies or before_build_script change. See prime.
  3. Set OMNIPACKAGE_IMAGE_CACHE: gitlab. Release jobs now pull the cached images and skip setup.

Secrets

Store secrets as masked CI/CD variables in Settings → CI/CD → Variables. You need the GPG key and the S3 credentials. Create the key with omnipackage gpg generate --format base64: the output is one line, so GitLab can mask it. ${...} in config.yml reads environment variables first, so you do not need a .env file. Child pipelines get project variables automatically. Mark the variables as protected only if you release from protected branches or tags.