Skip to content

Instantly share code, notes, and snippets.

@jcfr
Created October 31, 2025 20:35
Show Gist options
  • Select an option

  • Save jcfr/d048b3e32b6d774e6fb9c18c8be8302d to your computer and use it in GitHub Desktop.

Select an option

Save jcfr/d048b3e32b6d774e6fb9c18c8be8302d to your computer and use it in GitHub Desktop.
Lists private, owned GitLab projects that are NOT forks, includes only projects where YOU are the sole Owner
#!/usr/bin/env bash
set -euo pipefail
GITLAB_URL="https://gitlab.kitware.com"
MODE="only-me" # default owner mode
FORK_MODE="non-forks" # default fork mode
# Parse flags (note: use "$@" not "${@:-}")
while (($#)); do
case "$1" in
--any-owners) MODE="any-owners"; shift ;;
--only-forks) FORK_MODE="only-forks"; shift ;;
-h|--help)
cat <<'EOF'
Usage: list_owned_private_projects.sh [--any-owners] [--only-forks]
Default behavior:
- Lists private, owned projects that are NOT forks
- Includes only projects where YOU are the sole Owner
Options:
--any-owners Include projects regardless of how many Owners there are
--only-forks List forked projects instead of non-forks
-h, --help Show this help
EOF
exit 0
;;
--) shift; break ;; # end of options
-*) echo "Unknown option: $1" >&2; exit 2 ;;
*) break ;; # first positional (none expected)
esac
done
auth_hdr=("PRIVATE-TOKEN: ${PRIVATE_TOKEN}")
# Your GitLab user id
MY_ID="$(
curl -sS --header "${auth_hdr[@]}" "${GITLAB_URL}/api/v4/user" | jq -r '.id'
)"
# Choose project filter based on fork mode
if [[ "$FORK_MODE" == "only-forks" ]]; then
jq_filter='.[] | select(.forked_from_project)'
else
jq_filter='.[] | select(.forked_from_project | not)'
fi
page=1
while :; do
projects_json="$(
curl -sS --header "${auth_hdr[@]}" \
"${GITLAB_URL}/api/v4/projects?visibility=private&owned=true&simple=true&per_page=100&page=${page}"
)"
[[ "$(jq 'length' <<<"$projects_json")" -eq 0 ]] && break
jq -r "${jq_filter} | \"\(.id)\t\(.path_with_namespace)\"" \
<<<"$projects_json" \
| while IFS=$'\t' read -r pid path; do
if [[ "$MODE" == "any-owners" ]]; then
echo "$path"
continue
fi
owners_json="$(
curl -sS --header "${auth_hdr[@]}" \
"${GITLAB_URL}/api/v4/projects/${pid}/members/all?per_page=100"
)"
owners_count="$(jq '[.[] | select(.access_level == 50)] | length' <<<"$owners_json")"
if [[ "$owners_count" -eq 1 ]]; then
only_owner_id="$(jq -r '[.[] | select(.access_level == 50)][0].id' <<<"$owners_json")"
[[ "$only_owner_id" == "$MY_ID" ]] && echo "$path"
fi
done
page=$((page+1))
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment