diff --git a/git-fix-orphans/README.md b/git-fix-orphans/README.md index 554630d..0955370 100644 --- a/git-fix-orphans/README.md +++ b/git-fix-orphans/README.md @@ -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 diff --git a/git-fix-orphans/run.sh b/git-fix-orphans/run.sh index 9e47bbc..8872322 100755 --- a/git-fix-orphans/run.sh +++ b/git-fix-orphans/run.sh @@ -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 diff --git a/git-fix-orphans/test.bats b/git-fix-orphans/test.bats index c0da992..60f7de5 100644 --- a/git-fix-orphans/test.bats +++ b/git-fix-orphans/test.bats @@ -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"* ]] +}