Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions git-fix-orphans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ Declared via `zz-use` at the top of `run.sh` and resolved on demand
(installed if and only if missing) — see `run.sh` for the exact list. Needs
the `gh` CLI, authenticated (`GH_TOKEN`).

## GitLab CI

Inside GitLab CI (`GITLAB_CI=true`) the merge requests are listed through the
GitLab REST API (`curl`, `jq`, up to 1000) instead of `gh`, and with `-p` the
remote is switched to `https://oauth2:$GITLAB_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git`
to push the renames. Set `GITLAB_TOKEN` to a project access token with the
`api` and `write_repository` scopes. If the API cannot be read, nothing is renamed.

## Tests

```sh
Expand Down
29 changes: 25 additions & 4 deletions git-fix-orphans/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,31 @@ allowed_names="$allowed_names $default"

# Branches that ever had a pull request (open, merged or closed) are associated.
# Without a reliable answer, rename nothing.
with_pr=$(gh pr list --state all --limit 1000 --json headRefName --jq '.[].headRefName') || {
zz-log e "Unable to list pull requests with gh, aborting"
exit 1
}
# Inside GitLab CI the merge requests are listed through the REST API instead
# (GITLAB_TOKEN: a project access token with the api and write_repository scopes).
if [ "${GITLAB_CI:-}" = "true" ]; then
with_pr=$(
page=1
while [ "$page" -le 10 ]; do
json=$(curl -fsS --header "PRIVATE-TOKEN: ${GITLAB_TOKEN:-}" \
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests?state=all&per_page=100&page=$page") || exit 1
names=$(printf '%s' "$json" | jq -r '.[].source_branch') || exit 1
[ -n "$names" ] || break
printf '%s\n' "$names"
page=$((page + 1))
done
) || {
zz-log e "Unable to list merge requests with the GitLab API, aborting"
exit 1
}
# The CI checkout token is read-only: rename with the access token.
[ -z "$push" ] || git remote set-url "$remote" "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
else
with_pr=$(gh pr list --state all --limit 1000 --json headRefName --jq '.[].headRefName') || {
zz-log e "Unable to list pull requests with gh, aborting"
exit 1
}
fi

is_compliant() {
for n in $allowed_names; do
Expand Down
32 changes: 32 additions & 0 deletions git-fix-orphans/test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,35 @@ remote_branches() { git -C "$ORIGIN" for-each-ref --format='%(refname:short)' re
[ "$status" -eq 0 ]
[[ "$(remote_branches)" == *"stale/wip-nopr"* ]]
}

# GitLab: merge requests come from the REST API (curl), gh is not used.
# curl stub: page 1 lists the source branches in $MR_BRANCHES, later pages are empty.
gitlab_env() {
stub_script curl <<'STUB'
#!/bin/sh
[ -n "$CURL_FAIL" ] && exit 1
case "$*" in
*"page=1") printf '[%s]' "$MR_JSON" ;;
*) printf '[]' ;;
esac
STUB
export GITLAB_CI=true CI_API_V4_URL=https://gitlab.test/api/v4 CI_PROJECT_ID=7 CI_PROJECT_PATH=g/p CI_SERVER_HOST=gitlab.test GITLAB_TOKEN=tok
# the push goes to the tokenized project URL: point it back at the local bare repo
git config url."$ORIGIN".insteadOf "https://oauth2:tok@gitlab.test/g/p.git"
}

@test "git-fix-orphans on GitLab keeps branches with a merge request" {
gitlab_env
MR_JSON='{"source_branch":"has-pr"}' run git-fix-orphans -p
[ "$status" -eq 0 ]
[[ "$(remote_branches)" == *"orphan/wip-nopr"* ]]
[[ "$(remote_branches)" == *"has-pr"* ]]
[[ "$(remote_branches)" != *"orphan/has-pr"* ]]
}

@test "git-fix-orphans on GitLab renames nothing when the API fails" {
gitlab_env
CURL_FAIL=1 run git-fix-orphans -p
[ "$status" -eq 1 ]
[[ "$(remote_branches)" == *"wip-nopr"* ]]
}
Loading