This article explains how to turn .gitlab-ci.yml into an AngusGit pipeline with Import GitLab CI, and what to change before you save. After reading it, you can use the unmapped list and the notes to handle artifacts, rules, and when that did not convert, add the branch limit back, and keep deploy from running when nobody has clicked. You create any credentials yourself.
Pipelines are available on Team and Enterprise. Community has no CI/CD menu. If you have not written an AngusGit pipeline yet, read the YAML getting-started article linked at the end first. A job is one group of steps in a pipeline. A runner is the program on a build machine that picks up a job and runs the commands.
1. A .gitlab-ci.yml used for the conversion
The file below is only for showing the conversion. It is not a file from a live repository. It has three stages: build, then test, then a deploy on main that a person starts by hand.
stages:
- build
- test
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2"
build:
stage: build
image: maven:3.9-eclipse-temurin-21
tags: [docker]
script:
- mvn -B -DskipTests package
artifacts:
paths: [target/*.jar]
test:
stage: test
image: maven:3.9-eclipse-temurin-21
tags: [docker]
script:
- mvn -B test
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
deploy:
stage: deploy
tags: [deploy]
script:
- ./deploy.sh $CI_COMMIT_SHORT_SHA
when: manual
only: [main]
2. Import in the console and take the draft
- Open the import dialog
Open CI/CD → Pipelines and click New Pipeline. The menu has Blank pipeline, Generate from repository, Import GitHub Actions, and Import GitLab CI. Under the last two, the hint is “Some syntax is converted automatically — review after import”. Choose Import GitLab CI.
- Paste the file and convert
The dialog title is Import GitLab CI. The description is “Maps a subset to AngusCICD YAML. include, full rules, and child pipelines are not expanded — check the unmapped list.” The default tab is Paste YAML (the other tab is Scan repository). The field label is GitLab CI, and the placeholder is “Paste .gitlab-ci.yml”. Paste the whole YAML from section 1 and click Convert.
- Read the two lists
The result sits under the input, in this order: Unmapped (needs a manual edit), Notes, Pipeline name, then the YAML editor. The pipeline name is filled in as imported-from-gitlab (the placeholder is “Enter pipeline name”).
- Create only after the lists are checked
Opened from the pipeline list, the button at the lower right is Create Pipeline. Opened from the blank create page, the same place is Apply to editor. If the list items are still unfixed, click Cancel. The repository keeps no pipeline.
After convert, the middle of the dialog scrolls to the two lists. The paste box above may scroll out of view. The title, the description, and the lower-right Cancel and Create Pipeline stay in place. The four unmapped lines are:
jobs.build.artifactsjobs.test rules/only/except (used only to infer the trigger; the full condition was not mapped)jobs.deploy rules/only/except (used only to infer the trigger; the full condition was not mapped)jobs.deploy.when
That line means the rules were used only to guess the trigger, and the full condition was not mapped.
The three notes are:
jobs.deploy has no image, mapped to NATIVECI_COMMIT_SHORT_SHA → commitSha (full SHA, not the short SHA)GitLab CI_* predefined variables were rewritten approximately; other CI_* variables are not injected at runtime
Those notes mean: deploy has no image and is mapped to NATIVE; CI_COMMIT_SHORT_SHA becomes commitSha, which is the full SHA, not a short SHA; GitLab CI_* variables were rewritten approximately, and any remaining CI_* names are not injected at run time.
NATIVE in the first note, and DOCKER in the draft below, are the two execution modes. DOCKER runs the commands in a container started from the image you name. NATIVE runs them directly on the machine where the runner process lives. Items that converted cleanly are not listed one by one. The draft in the editor is:
version: '1'
name: imported-from-gitlab
triggers:
- 'on': push
- 'on': pull_request
env:
MAVEN_OPTS: -Dmaven.repo.local=.m2
pipeline:
build:
runner:
mode: DOCKER
image: maven:3.9-eclipse-temurin-21
labels:
- docker
steps:
- name: script
run: mvn -B -DskipTests package
test:
runner:
mode: DOCKER
image: maven:3.9-eclipse-temurin-21
labels:
- docker
steps:
- name: script
run: mvn -B test
needs:
- build
deploy:
runner:
mode: NATIVE
labels:
- deploy
steps:
- name: script
run: ./deploy.sh ${{ commitSha }}
needs:
- test
'on' is quoted because YAML treats an unquoted on as a boolean. The exporter adds the quotes on purpose. This is the converted draft. The unmapped items still need a decision before you save.
3. What converted, and what did not
Start with the parts that convert. The important one is stages. GitLab CI uses stages for order. AngusGit has no stages. The later job writes needs to name the job it waits for.
Import converts these on its own:
| GitLab CI | How it looks in the draft | Notes |
|---|---|---|
script |
Step name script, command in run
|
A one-line script gets the step name script
|
image |
runner.mode: DOCKER plus runner.image
|
The image name is kept as maven:3.9-eclipse-temurin-21
|
No image
|
runner.mode: NATIVE |
For example deploy. The note is jobs.deploy has no image, mapped to NATIVE
|
tags |
runner.labels |
docker and deploy become labels as written |
variables |
Pipeline-level env
|
MAVEN_OPTS is written in the top-level env
|
stages |
The job in the next stage writes needs
|
test depends on build, and deploy depends on test. stages itself is not kept |
A merge request inside rules
|
The triggers gain a pull_request entry |
The full if is not written into the job |
allow_failure |
continue-on-error |
This sample does not use it |
environment |
Only the environment name is kept | This sample does not use it. When it is present, the note is keeps only the name, without GitLab environment protection, meaning only the name is kept and GitLab environment protection rules are not |
Next are the parts that did not convert. The table lists them, and the bullets after the table spell each one out.
This sample did not convert these. Handle them before you save:
| What the list shows | What the draft actually did | What to change before you save |
|---|---|---|
jobs.build.artifacts |
target/*.jar is not passed to later jobs |
You can leave this sample as it is for now |
rules / only on jobs.test and jobs.deploy
|
Used only to guess the trigger. main did not become branches
|
Add branches: [main] on the triggers |
jobs.deploy.when |
when: manual was dropped. deploy is still there with needs: [test]
|
Remove deploy and create another pipeline, or add approval
|
include, retry, timeout, dependencies, trigger
|
Absent from this sample. When they appear, they go into the unmapped list | See the notes after the table |
-
artifacts(build outputs): In this sample,testrunsmvn testagain and does not read that jar, so the two jobs can stay as they are. If a later script must read a file from the previous job, put those steps in the same job, or upload the file to an artifact repository and download it later. -
rules/only: In the draft, bothpushandpull_requesthave nobranches: [main]. Other branches trigger this pipeline, anddeployruns with them. -
when: Afterwhen: manualis dropped, deploy runs on every trigger. To keep deploy from starting on its own, removedeployfrom this pipeline and create another pipeline triggered byworkflow_dispatch(manual run only), or addapprovalon this job. -
Other fields: If the file uses
include, merge the referenced pieces into one YAML before you import. Rewrite a job-levelretryortimeoutonto the step, in seconds. Split a downstream pipeline started bytriggerand handle it on its own.
A schedule can be imported, and you fill in the cron yourself. A cron expression describes when a schedule fires. GitLab stores the schedule on the page, not in the YAML. If the YAML has only: schedules, or a rules condition that mentions schedule, import adds on: schedule and writes this unmapped line: schedule cron was not mapped (GitLab schedules live in the UI, not in the YAML). That means the cron is not mapped, because the GitLab schedule lives in the UI, not in the YAML. You then add the cron yourself, in five fields, for example 0 2 * * *. Section 1 has no schedule, so the draft has no on: schedule.
4. Check CI_* variables one by one
GitLab injects a set of predefined variables whose names start with CI_. AngusGit does not inject them. Import rewrites only the names in the table below into ${{ }} expressions. It does not turn them into the environment variable GIT_COMMIT_SHA:
| GitLab variable | After import | In this sample |
|---|---|---|
$CI_COMMIT_SHA |
${{ commitSha }} |
The sample script does not use it |
$CI_COMMIT_SHORT_SHA |
Also ${{ commitSha }}, the full SHA |
The deploy command becomes ./deploy.sh ${{ commitSha }}
|
$CI_COMMIT_BRANCH |
${{ branch }} |
Only inside the test rules. That if is not written into the job |
$CI_PIPELINE_SOURCE |
${{ event }} |
Used only to infer pull_request
|
$CI_COMMIT_TAG |
${{ ref }} |
Absent here. When it appears, the note is CI_COMMIT_TAG → ref (approximate), an approximate mapping |
$CI_COMMIT_SHORT_SHA is the easy one to get wrong. The note is CI_COMMIT_SHORT_SHA → commitSha (full SHA, not the short SHA): after the rewrite you get the full commit id. For an 8-character short SHA, cut $GIT_COMMIT_SHA in the shell yourself. ${{ commitSha }} is the full value.
Other CI_ variables outside the table, such as CI_PROJECT_DIR, CI_PIPELINE_ID, and CI_REGISTRY, are not injected at run time. When the original file had a variable rewritten and still contains a CI_ variable, the notes include this sentence: GitLab CI_* predefined variables were rewritten approximately; other CI_* variables are not injected at runtime. A script you write can use the platform environment variables GIT_REPO_URL, GIT_BRANCH, and GIT_COMMIT_SHA.
One more group of names needs a look. When a name ends with TOKEN, SECRET, PASSWORD, PASSWD, PRIVATE_KEY, ACCESS_KEY, or API_KEY, import writes that name into the job secrets list and adds this note: $NAME → env.NAME, and added to the job secrets list. NAME is the variable name. The note means the name is rewritten to env.<name> and added to the job secrets list. AngusGit then needs a credential with the same name. Otherwise the job fails with Credential not visible or expired. You create the credential yourself under CI/CD → Settings → Credentials. Section 1 has no such variable, so the draft has no secrets.
5. A shape worth saving
This draft is not ready to save. Two places say the opposite of the original file. only: [main] did not become branches: [main], so other branches trigger it. when: manual is gone, so deploy still runs automatically after test.
Split it into two pipelines. The first keeps build and test, and adds the branch limit. Keep the converted image for now. If the private network cannot pull Docker Hub, replace maven:3.9-eclipse-temurin-21 with the platform image repo.anguskit.com/docker-public/angus-ci-maven:21. The YAML below is the hand-edited result, not the draft the importer produced:
version: "1"
name: imported-from-gitlab
triggers:
- "on": push
branches: [main]
- "on": pull_request
branches: [main]
env:
MAVEN_OPTS: "-Dmaven.repo.local=.m2"
pipeline:
build:
runner:
mode: DOCKER
image: maven:3.9-eclipse-temurin-21
labels: [docker]
steps:
- name: script
run: mvn -B -DskipTests package
test:
needs: [build]
runner:
mode: DOCKER
image: maven:3.9-eclipse-temurin-21
labels: [docker]
steps:
- name: script
run: mvn -B test
needs only sets the order. It does not pass target/*.jar to test. These can stay as two jobs because test runs mvn test itself and does not need that jar. If the deploy script must read the jar, write package and deploy as two steps in the same job.
The second pipeline holds only deploy, and it can be started only by hand. ${{ commitSha }} in the script argument is still the full SHA. A workable shape is:
version: "1"
name: deploy
triggers:
- "on": workflow_dispatch
pipeline:
deploy:
runner:
mode: NATIVE
labels: [deploy]
steps:
- name: script
run: ./deploy.sh "${{ commitSha }}"
If deploy should still follow the build, and a named person must approve it before it continues, add approval on that job. The dropped when: manual does not do that. The approval article linked at the end shows how.
6. Check once before you really save
After the edits, create the pipeline and check it in three steps:
- Pass the pre-run check
If the dialog was opened from the list, click Create Pipeline. If it was opened from the blank create page, click Apply to editor, then save on the create page. Before save, the platform runs a pre-run check. When it passes, the page shows “All checks passed. Ready to save.” When it shows “Fix errors first”, change the YAML as the message says. The usual cause is a GitLab field such as stages or variables still left in the YAML.
- Run it once by hand
On the pipeline detail page, click Trigger, fill Branch / tag with a test branch, and confirm that a runner with the docker label picks up the job. If deploy is still in the same pipeline, this run really executes the deploy command.
- Then check the automatic trigger
After the manual run passes, open a pull request. Confirm that pull_request triggers the pipeline and that the result is written back to the status check on the pull request page (the row that shows whether the pipeline passed).
During the move, .gitlab-ci.yml can stay in the repository. AngusGit does not run it. Delete it after the new pipeline has been running steadily.
Common questions
These are the situations people hit most often after the move:
| What you see | Why | What to do |
|---|---|---|
| The job stays queued | No online runner matches both mode and labels
|
The runner needs the docker label and must be able to run that execution mode |
| The job is not in a container and runs on the host | The original job had no image, so it became NATIVE
|
Add runner.mode: DOCKER and runner.image
|
A later job cannot find target/*.jar
|
artifacts is unmapped, and needs does not pass files |
Put the steps in one job, or pass the file through an artifact repository |
| Deploy runs when nobody clicked |
when: manual is unmapped, and the job is still in the pipeline |
Remove that job, or switch it to workflow_dispatch / approval
|
A branch other than main also deploys |
only: [main] was not written as branches
|
Add branches: [main] on push and pull_request
|
| A path or an id in the script is empty | The script uses a CI_* variable that is not injected |
Replace it as in section 4. Cut a short SHA from $GIT_COMMIT_SHA yourself |
There is no automatic build after the merge into main
|
Merging on the pull request page does not fire on: push. Only git push does |
Use pull_request to run build and test before the merge |
Can I import several files at once?
No. The dialog handles one YAML at a time. If the project uses include (one YAML pulls in other YAML pieces), merge the referenced files into one file before you import. Otherwise the unmapped list says include (remote or local snippets are not fetched; expand them before import), meaning remote and local fragments are not fetched and must be expanded before import.
Can I use the conversion result as it is?
The draft of this sample is not ready. deploy runs on its own, and the branch limit is gone. Fix those two places as in section 5, then save.
Summary
Paste .gitlab-ci.yml into Import GitLab CI and click Convert. You get a draft that still needs a person to check it. In this example the name is imported-from-gitlab. script, image, tags, variables, and stages become steps, DOCKER mode, labels, env, and needs. For section 1, the unmapped items are jobs.build.artifacts, two rules/only/except lines, and jobs.deploy.when. The notes say deploy became NATIVE, and $CI_COMMIT_SHORT_SHA became the full ${{ commitSha }}. The important part: when: manual does not become a manual job, and only: [main] does not become a branch limit. Left unchanged, deploy runs automatically on other branches. Check each item, edit, then create the pipeline. Create the credentials you need yourself.
Conclusion: Import GitLab CI gives you only the converted draft. Before you save, handle the unmapped
artifactsandwhen, turn the droppedonly: [main]intobranches: [main]on the triggers, and remember that${{ commitSha }}is the full SHA.
Further reading
- AngusGit manual: Pipeline basics
- AngusGit manual: Advanced pipelines
- After the repository accepts pushes: how to write the built-in pipeline YAML
- Move from GitLab to AngusGit: checklist, dual-run, and rollback
- The pipeline stops at an approval: who can let it continue
- The runner is not taking jobs: labels, online status, or concurrency do not match

Top comments (0)