Skip to content

GitLab component: commit what the run wrote, and open the first merge request - #13

Merged
pavel-te merged 1 commit into
mainfrom
fix/gitlab-component-scope-and-first-run
Aug 27, 2026
Merged

GitLab component: commit what the run wrote, and open the first merge request#13
pavel-te merged 1 commit into
mainfrom
fix/gitlab-component-scope-and-first-run

Conversation

@pavel-te

Copy link
Copy Markdown
Collaborator

Two defects in templates/translate/template.yml, both there since it was written. The GitHub half lost the second one in #11; this one had kept both.

1. The first run opened no merge request

The guard was ! git diff --quiet, evaluated before staging. git diff looks only at tracked files, and on a first run every translation is a new file:

first run, translations are NEW files:
  git diff --quiet          -> exit 0   ("nothing changed")
  git add -A
  git diff --cached --quiet -> exit 1   (the index knows better)

So the block was skipped, nothing was pushed, and the job went green having produced nothing. The second run worked — which is why it survived this long. Same defect the CLI fixed upstream in ptc-cli #15.

2. git add -A committed the whole working directory

Anything the caller's job dirtied before us shipped inside a merge request titled "Update translations from PTC".

What replaces them

The working tree is recorded before the CLI runs and compared afterwards; only what differs is staged. By content, not timestamps — the CLI unpacks from a ZIP, and unzip restores mtimes from the archive, so a translation written moments ago can be dated before the run began.

A run that wrote nothing commits nothing and pushes nothing, instead of pushing an empty branch. Paths are staged one at a time under GIT_LITERAL_PATHSPECS, because git add -- <path> takes a pathspec: a translation written to messages[1].json would otherwise stage the caller's messages1.json and exit 0.

Where the shell lives

lib/gitlab-scope.sh — that is where it is tested. A component ships YAML only and cannot vendor a sibling file, so the template inlines it, and one of the tests fails if the two drift apart.

POSIX throughout: the component runs on alpine:3.22, i.e. busybox ash until before_script installs bash.

Verification

tests/gitlab-scope.test.sh — 8 cases: first run, a workspace dirtied beforehand, nothing written, a rewritten file next to an untouched one, ignored files, a path with a space, a glob metacharacter, and the template/library drift check.

Green under bash, dash and busybox ash (the last in a real alpine:3.22 container). CI runs bash and dash.

Mutation-checked: restoring the old git diff --quiet guard fails 5 of 8; restoring git add -A fails 6 of 8.

Unchanged and re-run: 19 unit cases, 5 act jobs, shellcheck clean, the component template still parses.

… request

Two defects, both since the template was written.

The first run opened no merge request. The guard was `! git diff --quiet`
evaluated BEFORE staging, and `git diff` looks only at tracked files - on a
first run every translation is a new file, so it reported "nothing changed",
the push was skipped, and the job went green having produced nothing. The
second run worked, which is why this survived.

`git add -A` committed the whole working directory. Anything the caller's job
dirtied before us - an install rewriting a lockfile, a build, a codegen -
shipped inside a merge request titled "Update translations from PTC". The
GitHub half lost that behaviour; this one had kept it.

The working tree is now recorded before the CLI runs and compared afterwards,
and only what differs is staged - by content, because the CLI unpacks from a
ZIP and unzip restores mtimes from the archive, so a fresh translation can
carry a timestamp from before the run began. Nothing written means no commit
and no push, rather than an empty branch.

Paths are staged one at a time under GIT_LITERAL_PATHSPECS: `git add -- <path>`
takes a pathspec, so a translation written to messages[1].json would otherwise
stage the caller's messages1.json instead, and exit 0 doing it.

The shell lives in lib/gitlab-scope.sh, which is where it is tested, and the
template inlines it - a component ships YAML only and cannot vendor a sibling
file. A test fails if the two drift apart.

POSIX throughout: the component runs on alpine, which is busybox ash until
before_script installs bash. The suite runs under bash, dash and ash; CI runs
the first two.
@pavel-te
pavel-te merged commit e5bc32d into main Aug 27, 2026
1 check passed
@pavel-te
pavel-te deleted the fix/gitlab-component-scope-and-first-run branch August 27, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants