Skip to content

Instantly share code, notes, and snippets.

@mgoellnitz
Last active March 8, 2026 17:15
Show Gist options
  • Select an option

  • Save mgoellnitz/45f4cbe6fa4d757a1e9803094361b846 to your computer and use it in GitHub Desktop.

Select an option

Save mgoellnitz/45f4cbe6fa4d757a1e9803094361b846 to your computer and use it in GitHub Desktop.
Clean old GitLab CI build job's results
#!/bin/bash
#
# Copyright 2017-2026 Martin Goellnitz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
if [ `which jq|wc -l` = 0 ] ; then
echo "To use this tool, jq must be installed."
exit
fi
if [ "$#" = "0" ]; then
echo "GitLab Project Pipeline Old Build Cleanup 'clean-old-gitlab-pipelines.sh'"
echo ""
echo "$0 [-s] [-b branch] [-u instance url] [-d date] [-t token] project"
echo ""
echo " -s - simulate - don't actually delete"
echo ""
echo " -b branch - no default - any branch is used"
echo ""
echo " -u instance base url - default https://gitlab.com"
echo ""
echo " -t gitlab api token - default \$GITLAB_COM_TOKEN"
echo ""
echo " -d yyyy-mm-dd - date to clean builds before"
exit 1
fi
# defaults
GITLAB="https://gitlab.com"
TOKEN=$GITLAB_COM_TOKEN
# Default: save builds for 30 days
REF=$[ $(date +%s) - 2592000 ]
while getopts "d:hst:u:" opt ; do
case "${opt}" in
d)
REF="$(date -d $OPTARG +%s)"
;;
h)
usage
exit
;;
s)
SIMULATE="sim"
;;
t)
TOKEN=$OPTARG
;;
u)
GITLAB=$OPTARG
;;
*)
usage
exit 1
;;
esac
done
shift $((OPTIND-1))
PROJECT=$1
HEADER="PRIVATE-TOKEN: $TOKEN"
REFDATE=$(date -d @$REF +%Y-%m-%d)
echo "project: $PROJECT gitlab: $GITLAB for reference date $REFDATE"
if [ ! -z "$PROJECT" ] ; then
N=$(echo $PROJECT|sed -e 's/\(.*\)\/\(.*\)/\2/g')
PID=$(curl -k -H "$HEADER" ${GITLAB}'/api/v4/projects?per_page=100&search='$N 2> /dev/null|jq '.[]|select(.path_with_namespace=="'$PROJECT'")|.id')
echo "project ID for $PROJECT is $PID"
if [ -z "$PID" ] ; then
echo "Unknown project $PROJECT on $GITLAB ($TOKEN)"
exit 1
fi
URLADD="/projects/$PID"
else
echo "Cannot work without the context of a project. Sorry..."
exit 1
fi
PAGES=$(curl -k -D - -X "HEAD" -H "$HEADER" "${GITLAB}/api/v4$URLADD/pipelines?per_page=100&updated_before=$REFDATE" 2> /dev/null \
|grep '^x-total-pages'|sed -e 's/^.*[a-z]:.\([0-9][0-9]*\).*$/\1/g')
PAGE="0"
while [ "$PAGE" -lt "$PAGES" ] ; do
PAGE=$[ $PAGE + 1 ]
echo "Chunk $PAGE of $PAGES"
for PIPELINE in `curl -k -H "$HEADER" "${GITLAB}/api/v4$URLADD/pipelines?per_page=100&updated_before=$REFDATE&page=$PAGE" 2> /dev/null|jq '.[]|.id'` ; do
echo -n "Pipeline $PIPELINE: "
eval $(curl -k -H "$HEADER" ${GITLAB}/api/v4$URLADD/pipelines/$PIPELINE 2> /dev/null| \
jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")
DATE=$(echo $finished_at|sed -e 's/^\(.*\)T.*/\1/g'|sed -e 's/null/1970-01-01/g')
# echo " date: $DATE"
if [ -z "$BRANCH" ] || [ "$BRANCH" = "$ref" ] || [ "$status" != "success" ] ; then
if [ "$tag" = "false" ] || [ "$status" != "success" ]; then
D=$(date -d $DATE +%s)
# echo "$D vs $REF"
if [ "$D" -lt "$REF" ] ; then
if [ -z "$SIMULATE" ] ; then
echo "Deleting pipeline $PIPELINE of $DATE ($ref)"
curl -k -H "$HEADER" -X "DELETE" ${GITLAB}/api/v4$URLADD/pipelines/$PIPELINE 2> /dev/null
else
echo "Would delete pipeline $PIPELINE of $DATE ($ref / $status)"
fi
fi
else
echo "Saving $ref ($status / $tag / $DATE)"
# for JOB in `curl -k -H "$HEADER" ${GITLAB}/api/v4$URLADD/pipelines/$PIPELINE/jobs 2> /dev/null|jq '.[]|.id'`; do
# echo -n " Erasing Job $JOB "
# curl -X POST -k -H "$HEADER" ${GITLAB}/api/v4$URLADD/jobs/${JOB}/erase 2> /dev/null|jq '.|.name'
# echo ""
# done
fi
else
echo "($ref)"
fi
done
done
# Deleting Jobs:
#for p in `curl -k -H "$HEADER" ${GITLAB}/api/v4$URLADD/pipelines?per_page=100 2> /dev/null|jq '.[]|(.id,.status)'` ; do
# if [ -z "$PING" ] ; then
# PING="pong"
# PIPELINE=$p
# else
# PING=
# echo "Checking Pipeline $PIPELINE ($D)."
# DATE=$(curl -k -H "$HEADER" ${GITLAB}/api/v4$URLADD/pipelines/$PIPELINE 2> /dev/null|jq '.finished_at'|sed -e 's/^"\(.*\)T.*/\1/g')
# # echo date: $DATE
# if [ "$DATE" != "null" ] ; then
# D=$(date -d $DATE +%s)
# # echo "$D vs $REF"
# if [ "$D" -lt "$REF" ] ; then
# echo "Working on Pipeline $PIPELINE"
# for JOB in `curl -k -H "$HEADER" ${GITLAB}/api/v4$URLADD/pipelines/$PIPELINE/jobs 2> /dev/null|jq '.[]|.id'`; do
# echo -n " Erasing Job $JOB "
# curl -X POST -k -H "$HEADER" ${GITLAB}/api/v4$URLADD/jobs/${JOB}/erase 2> /dev/null|jq '.|.name'
# done
# fi
# fi
# fi
#done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment