ข้ามไปยังเนื้อหาหลัก

Migrate from GitLab CI

GitLab is the first place teams move their CI from when they come to Loha. This guide covers both halves of that move:

  1. Connecting your self-hosted GitLab to Loha: network, TLS, tokens, and how push events reach a build.
  2. Converting .gitlab-ci.yml to .loha.yml: the vocabulary maps cleanly; this is mostly find-and-replace.

Do part 1 once per GitLab instance. Do part 2 once per repository.

Before you start​

You will need:

  • A self-hosted GitLab instance (GitLab.com works too: the steps are identical).
  • Maintainer on the GitLab project: needed to add the incoming webhook yourself if Loha cannot (see below) and, on most instances, to create access tokens. This is the role of the person doing setup; the token itself needs less (see token role).
  • The ability to create a Personal Access Token on that GitLab instance.
  • Admin or Owner on the Loha workspace you are connecting into.

Network reachability​

Loha and your GitLab talk in both directions. Both paths must be open:

DirectionWhat flowsRequirement
Loha → GitLabReads .loha.yml, lists projects and branches, resolves commitsThe Loha dashboard must be able to make outbound HTTPS calls to your GitLab API
GitLab → LohaPush and merge-request webhook eventsYour GitLab must be able to make outbound HTTPS calls to the Loha dashboard URL

Your GitLab API URL must resolve to a routable, public HTTPS endpoint. Loha rejects API URLs that point at private address space: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, link-local, and CGNAT ranges are all blocked, as are cloud metadata endpoints. This is SSRF protection and it is not configurable.

If your GitLab currently lives on a private network, expose it through a public hostname (a reverse proxy or ingress with a real DNS name) before connecting it to Loha.

TLS / certificates​

Loha calls your GitLab API over standard HTTPS and validates the certificate against the public system CA store. There is no option to pin a custom CA or to skip certificate verification.

Your GitLab API URL must present a valid, publicly-trusted certificate chain. A self-signed certificate or a certificate from an internal-only CA will fail the connection. Use a certificate from a public CA (Let's Encrypt is fine).

GitLab API version compatibility​

Loha targets the GitLab REST API v4. The API URL you give Loha must end with /api/v4:

https://gitlab.example.com/api/v4

Do not include a group or project path: just the instance host plus /api/v4. Authentication uses GitLab's PRIVATE-TOKEN header.

Any GitLab version that serves the v4 API works. v4 has been the stable API since GitLab 9, so any currently-supported GitLab release is compatible.

Create a Personal Access Token​

In GitLab: User Settings → Access Tokens → Add new token.

  • Scopes (required):
    • api: Loha uses the GitLab REST API to list your projects, read .loha.yml, and report build status back to merge requests.
    • read_repository: lets the build clone your repository over HTTPS.
  • Add write_repository only if you want Loha to commit a generated .loha.yml back to your repo from the pipeline editor.
  • Expiry: set one you can rotate. Loha stores the token encrypted at rest and supports rotating it later.

read_repository alone is not enough. It covers Git-over-HTTP and the Repository Files API, but listing projects and reporting commit status go through the wider REST API, which needs api. A token missing api typically shows up as an empty repository list or a connection marked invalid.

Token role​

Scopes say what kind of access the token has; the token's role on the project says how much. Two tiers matter:

  • Reporter: the minimum. Lets Loha read .loha.yml, list projects, and clone the repo, so builds run. A Reporter token is enough to get green.
  • Developer (recommended) enables deeper integration: GitLab only accepts commit/merge-request build status updates from Developer or above. With a Reporter token, builds still run; Loha just can't post the pass/fail status back onto your merge requests.

Use a group or project access token with the Developer role for shared/team setups, or a Personal Access Token from a user who has Developer on the project.

Copy the token now: GitLab only shows it once.

If Loha reports "Invalid access token" the token is wrong or expired. "Insufficient permissions" means the token is valid but missing api / read_repository (or lacks access to the project).

Add the SCM connection in Loha​

An SCM connection stores your GitLab API URL and token at the workspace level, so every repository in the workspace can share it.

In the dashboard: Workspace settings → Integrations → Add connection.

FieldValue
NameA label, e.g. Company GitLab
ProviderGitLab
API URLhttps://gitlab.example.com/api/v4
Access tokenThe Personal Access Token you just created

On save, Loha test-validates the token by making a probe API call. A green Valid status means the connection works. If validation fails, the dashboard tells you whether it was the URL, the network, the certificate, or the token.

Connect a repository and route webhooks​

With the connection in place, add a repository and pick it from your GitLab project list. Loha reads .loha.yml from the default branch (or you can author one in the pipeline editor).

For pushes to start builds, your GitLab project needs a webhook pointing at Loha. Creating the trigger is usually all it takes:

  1. In Loha, create a webhook trigger for the repository. If your access token has the api scope, Loha adds the webhook to the GitLab project for you and you are done. Skip to step 3. If it only has read_api, the trigger shows a webhook URL and a secret token to add yourself; copy both.
  2. In GitLab: Project → Settings → Webhooks → Add new webhook.
    • URL: the webhook URL from the Loha trigger page.
    • Secret token: the secret from the Loha trigger page.
    • Trigger events: enable Push events (and Merge request events if your .loha.yml listens for pull_request).
  3. Use GitLab's Test → Push events button to send a sample delivery and confirm Loha receives it.

The scope is what decides this: read_api is enough for Loha to read your project and run builds, but registering a webhook needs api. Granting it is optional. See the scopes table.

Verify your first build​

Push a commit to a branch your .loha.yml listens for. Within a few seconds the build should appear in the dashboard with streaming logs. If it doesn't, check the webhook's recent deliveries in GitLab: a non-2xx response there points at a URL or secret mismatch.

See Your first pipeline for a full walkthrough of the build view.


Converting .gitlab-ci.yml to .loha.yml

.gitlab-ci.yml and .loha.yml solve the same problem with different vocabulary. This section walks the mapping. A .gitlab-ci.yml importer is in flight in the pipeline editor; for now, plan on a manual port.

Vocabulary mapping​

GitLab CILoha
stages:implicit; expressed via needs: between jobs
<job>: stage: build + stage: testjobs.build and jobs.test with test.needs: [build]
script:steps[].run
image:wrapped automatically by loha/run-command or via the resource class
artifacts: paths:artifacts.paths on the job
cache:loha/cache@v1
rules: / only: / except:on: (top-level triggers) + if: (per-job/step)
before_script: / after_script:inline as steps
variables:env: (global, job, or step level)
parallel: matrix: (one set)strategy.matrix

Triggers​

.gitlab-ci.yml
workflow:
rules:
- if: $CI_COMMIT_BRANCH == 'main'
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'

Becomes:

.loha.yml
on:
push:
branches: [main]
pull_request:
branches: [main]

Stages → DAG​

GitLab CI's stages are linear. .loha.yml uses an explicit DAG via needs:. To keep behavior, set needs to the previous stage's jobs.

.gitlab-ci.yml
stages: [build, test, deploy]
build_app: { stage: build, script: ['make build'] }
unit: { stage: test, script: ['make unit'] }
deploy: { stage: deploy, script: ['make deploy'] }

Becomes:

.loha.yml
version: '1'
on:
push:
branches: [main]
jobs:
build_app:
steps:
- name: Build
run: make build
unit:
needs: [build_app]
steps:
- name: Unit tests
run: make unit
deploy:
needs: [unit]
steps:
- name: Deploy
run: make deploy

GitLab's stages: become needs: edges: there is no separate stage list. Every step needs a name:.

Artifacts between jobs​

GitLab's artifacts: paths: carries over to Loha's artifacts.paths on the job, and the translator does it for you:

.gitlab-ci.yml
build:
stage: build
script: ['make build']
artifacts:
paths: [dist/]

test:
stage: test
script: ['make test'] # receives dist/ from build

Becomes:

.loha.yml
jobs:
build:
steps:
- name: Build
run: make build
artifacts:
paths: [dist/]
test:
needs: [build]
steps:
- name: Test
run: make test

Only paths carries over. The rest of the artifacts: block is dropped, each key with a warning of its own:

GitLab keyIn Loha
expire_inNot supported. Job archives are kept for the deployment-wide retention window (14 days by default)
reportsNot supported
whenNot supported. Files are saved only when the job succeeds
name, untracked, exclude, expose_as, publicNot supported
dependencies: on a jobNot supported. A job receives the files of every job in its needs: chain
needs: - job: x, artifacts: falseNot supported. The job still receives the files of every job in its needs:

Two behaviours differ from GitLab. A declared path that matches nothing fails the job instead of being silently skipped. And a job receives artifacts only from the jobs in its needs: chain. The translator builds needs: from the previous stage, so a job receives that stage's files and, through them, earlier ones; when you port by hand, give every consuming job a needs:. Paths that Loha cannot express (absolute, containing .., or more than 20 entries) are dropped with a warning.

What doesn't port over (yet)​

  • include: / extends: for reusable templates is planned for v0.7.0.
  • parallel: matrix: ports when it has one variable set. The translator turns each variable into a strategy.matrix axis (1–5 axes, at most 20 values per axis and 20 combinations, values are strings), keeps it readable under its own name (env: { NODE: ${{ matrix.NODE }} }, so $NODE in a script still works), and rewrites $NODE in image: to ${{ matrix.NODE }}. GitLab's parallel: matrix: is a list of variable sets, while a Loha job has one cross-product: a list with more than one set is dropped with a warning, and you rewrite it by hand as one Loha job per set. Plain parallel: N has no equivalent; the job runs once.
  • Manual jobs / when: manual map to workflow_dispatch today; per-job approval gates are v0.7.0.
  • Dynamic child pipelines are not on the near-term roadmap.

See the roadmap for parity targets.