How can we configure gitlab to keep only the last 10 CI jobs/builds and keep deleting the rest?
For example , in Jenkins , we can configure the job to keep only last X builds.
How can we configure gitlab to keep only the last 10 CI jobs/builds and keep deleting the rest?
For example , in Jenkins , we can configure the job to keep only last X builds.
As of Gitlab Release 12.6, deleting a pipeline is now an option in the GUI for users in role Owner:
As of Gitlab Release 11.6, deleting a pipeline is now an option, for users in Owner role only, via the API.
You need:
id
of the projectpipeline_id
of the pipeline you wish to remove.Example using curl from the docs for project id: 1
and pipeline_id: 4
:
curl --header "PRIVATE-TOKEN: <your_access_token>" --request "DELETE" "https://gitlab.example.com/api/v4/projects/1/pipelines/46"
Documentation is here
Mass deletion script fixed for the lazy, delete X pipelines from the oldest (Linux systems).
Note: need jq.
#!/bin/bash
set -e
TOKEN="__SET_YOUR_PERSONAL_ACCESS_TOKEN__"
# Visible under name of project page.
PROJECT_ID="__SET_NUMERIC_PROJECT_ID__"
# Set your gitlab instance url.
GITLAB_INSTANCE="https://gitlab.com/api/v4/projects"
# How many to delete from the oldest, 100 is the maximum, above will just remove 100.
PER_PAGE=100
for PIPELINE in $(curl --header "PRIVATE-TOKEN: $TOKEN" "$GITLAB_INSTANCE/$PROJECT_ID/pipelines?per_page=$PER_PAGE&sort=asc" | jq '.[].id') ; do
echo "Deleting pipeline $PIPELINE"
curl --header "PRIVATE-TOKEN: $TOKEN" --request "DELETE" "$GITLAB_INSTANCE/$PROJECT_ID/pipelines/$PIPELINE"
done
Based on previous answers, modified the script to retrieve several projects, and for each one, delete pipelines older than the configured date.
#!/bin/bash
set -e
TOKEN=""
# How many to delete from the oldest.
PER_PAGE=100
UPDATED_BEFORE=2021-02-01T00:00:00Z
GITLAB_URL=
while : ; do
COUNTER=0
for PROJECT in $(curl -s --header "PRIVATE-TOKEN: $TOKEN" "$GITLAB_URL/api/v4/projects?per_page=$PER_PAGE" | jq '.[].id') ; do
echo "Deleting in project $PROJECT"
for PIPELINE in $(curl -s --header "PRIVATE-TOKEN: $TOKEN" "$GITLAB_URL/api/v4/projects/$PROJECT/pipelines?per_page=$PER_PAGE&sort=asc&updated_before=$UPDATED_BEFORE" | jq '.[].id') ; do
echo "Deleting pipeline $PIPELINE"
curl --header "PRIVATE-TOKEN: $TOKEN" --request "DELETE" "$GITLAB_URL/api/v4/projects/$PROJECT/pipelines/$PIPELINE"
(( COUNTER++ )) || true
done
done
echo $COUNTER
if [[ "$COUNTER" -le 0 ]]; then
break;
fi
done
I think Gitlab doesn't support this feature. But you can create this functionality on your own using Gitlab API and webhooks.
When you push to repo (and pipeline started) it will trigger webhook which can read your CI history via API => you can delete whatever you want.
Here is docs for pipeline events
Here is docs for job API
FYI I use similar solution. I have deployed server for each branch (every brach has MR). When is MR closed it delete deployed server. It is very reliable.
To delete all pipelines in a project using python the following code can be utilized. You can head over to Jupyter to try it online
import requests
def confirmDeletion():
confirmDelete = input('Do you want to delete all pipelines in the project Y/N ?')
if confirmDelete == 'Y' or confirmDelete == 'y' :
return True
else:
return False
projectId = input('Provide the Gitlab Project ID')
token = input('Provide the Gitlab Access Token')
proceed = bool()
pipelinesQueryUrl = f'https://gitlab.com/api/v4/projects/{projectId}/pipelines'
if confirmDeletion():
print('Deleting pipelines')
# The Gitlab API does
while True:
response = requests.get(pipelinesQueryUrl, headers = {"PRIVATE-TOKEN": token})
if response.ok:
pipelines = response.json()
if len(pipelines) == 0:
print('No more pipelines found, exiting')
break
else:
for pipeline in pipelines:
pipelineId = pipeline.get('id')
url = f'https://gitlab.com/api/v4/projects/{projectId}/pipelines/{pipelineId}'
response = requests.delete(url, headers = {"PRIVATE-TOKEN": token})
if response.ok:
print(f'Pipeline {pipelineId} succesfully deleted')
else:
print (f'Deleting pipeline {pipelineId} on path failed: {response.url} : ({response.status_code}) {response.reason}')
if response.status_code == 429:
# Watch out for rate specific limits https://docs.gitlab.com/ee/user/gitlab_com/index.html#gitlabcom-specific-rate-limits
print ('Rate Limits have been reached, wait and try again later')
break
else:
print (f'Querying for pipelines failed: {response.url}: ({response.status_code}) {response.reason}')
if response.status_code == 429:
# Watch out for rate specific limits https://docs.gitlab.com/ee/user/gitlab_com/index.html#gitlabcom-specific-rate-limits
print ('Rate Limits have been reached, wait and try again later')
break
else:
print('No pipelines deleted')
_gitlab_pipeline_cleanup() {
echo GITLAB PIPELINE CLEANUP TOOL
echo DELETING ALL EXCEPT RUNNING AND THE MOST RECENT PIPELINE
if [ -z $GITLABURL ]; then echo GITLAB_URL:; read GITLABURL;fi
if [ -z $GITLAB_TOKEN ]; then echo TOKEN:; read GITLAB_TOKEN;fi
if [ -z $PROJECT ]; then echo PROJECT_NUM:; read PROJECT;fi
list=$(curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$GITLABURL/api/v4/projects/$PROJECT/pipelines?per_page=100" |jq -c '.[] | select( .status != "running" )| .id ' |tail -n+2 |grep -v ^$)
echo removing from $GITLABURL Project number $PROJECT
while echo $(echo -n "$list" |wc -c ) |grep -v ^0$; do
list=$(curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$GITLABURL/api/v4/projects/$PROJECT/pipelines?per_page=100" |jq -c '.[] | select( .status != "running" )| .id ' |tail -n+2 |grep -v ^$)
for item in $list ;do
echo -n "| -p $item |"
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" --request "DELETE" "$GITLABURL/api/v4/projects/$PROJECT/pipelines/$item"
done
done
echo ; } ;
Then you could do
for PROJECT in 12345 23476234 2138734876238746 ;do _gitlab_pipeline_cleanup ;done
As a small extension to Mog's answer since my gitlab on the one hand provides a maximum of 100 entries per page and on the other hand I wanted to delete depending on the status of the pipeline:
#!/bin/bash
set -e
TOKEN="---your token here--"
PROJECT="PROJECT-ID"
BASEURL="https://gitlab.com/api/v4/projects" # Base Url, if you host your
# Status values: created, waiting_for_resource, preparing, pending, running, success, failed, canceled, skipped, manual, scheduled
STATUS=(failed canceled skipped) # separate status by space
# How many to delete from the oldest.
DELETE_CNT=500
# -----
CNT=0
declare -A STATUS_RESULT
for CUR_STATUS in "${STATUS[@]}";
do
TEMP_CNT=$CNT
DOLOOP=true
while [ "$DOLOOP" = true ]
do
DOLOOP=false
for PIPELINE in $(curl --header "PRIVATE-TOKEN: $TOKEN" "$BASEURL/$PROJECT/pipelines?per_page=$DELETE_CNT&sort=asc&status=$CUR_STATUS" | jq '.[].id') ; do
if [[ "$CNT" -lt "$DELETE_CNT" ]]; then
echo "#$CNT Deleting pipeline $PIPELINE with status $CUR_STATUS"
curl --header "PRIVATE-TOKEN: $TOKEN" --request "DELETE" "$BASEURL/$PROJECT/pipelines/$PIPELINE"
let "CNT+=1"
DOLOOP=true
fi
done
if [ "$DOLOOP" = true ] ; then
if [[ "$CNT" -ge "$DELETE_CNT" ]]; then
DOLOOP=false
fi
fi
done
TEMP_CNT=$((CNT-TEMP_CNT))
STATUS_RESULT[$CUR_STATUS]=$TEMP_CNT
echo "Removed $TEMP_CNT pipelines with status $CUR_STATUS"
done
echo "===================================================="
echo "= Summary of removed pipelines (max: $DELETE_CNT)"
echo "= Total: $CNT"
echo "="
for key in "${!STATUS_RESULT[@]}"; do
CNT=${STATUS_RESULT[$key]}
echo "= $key: $CNT"
done
For the lazy, to expand on https://stackoverflow.com/a/55815040/1041691
Get your PROJECT
and TOKEN
and run this until all the pipelines are deleted
for PIPELINE in $(curl --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT/jobs?per_page=100" | jq '.[].pipeline.id') ; do
echo "deleting $PIPELINE"
curl --header "PRIVATE-TOKEN: $TOKEN" --request "DELETE" "https://gitlab.com/api/v4/projects/$PROJECT/pipelines/$PIPELINE"
done
This deletes all pipelines for a project, but I'm sure you can figure out the Perl stuff to skip the first 10
curl -s --header "PRIVATE-TOKEN: ******" --request "GET" "https://gitlab.com/api/v4/projects/********/pipelines?per_page=32767" \
| perl -MJSON -le '$d = decode_json(<>); map { print $_->{"id"} } @$d' - | while read i; do
curl -s --header "PRIVATE-TOKEN: ********" --request "DELETE" "https://gitlab.com/api/v4/projects/******/pipelines/$i"
done
The project ID is written under the project name (project page) and you get access tokens from "Edit Profile"."Access Tokens" and just check the "API" checkbox.
Install JSON module in Perl on Linux via:
sudo apt -y install libjson-perl
My 2 cents. I had issues connecting to the API with curl and I discovered that the R httr library did work fine, so I developed a small R script to have the work done.
library(rjson)
library(httr)
library(optparse)
library(data.table)
#########################
## Define the arguments
option_list = list(
make_option(c("-n", "--projectName"),
type="character", default=NULL,
help="Project name (e.g. cronadmin)",
metavar="character"),
make_option(c("-d", "--days"),
type="integer", default=NULL,
help="Remove ci/cd pieplines older than this number of days (cannot be used together with the -p/--id option)",
metavar="integer"),
make_option(c("-p", "--pid"),
type="integer", default=NULL,
help="Identifier of the pipeline that should be removed (cannot be used together with the --days/-d option)",
metavar="integer"),
make_option(c("-u", "--gitlabUrl"),
type="character",
help="gitlab URL",
default=NULL,
metavar="character"),
make_option(c("-g", "--gitlabToken"),
type="character",
help="gitlab token",
default=NULL,
metavar="character")
);
errors <- vector();
opt_parser = OptionParser(option_list=option_list);
opt = parse_args(opt_parser);
projectName <- opt$projectName
days <- opt$days
pid <- opt$pid
gitlabUrl <- opt$gitlabUrl
gitlabToken <- opt$gitlabToken
if (!is.null(days) && !is.null(pid)) {
stop(" -p/--pid option cannot be used together with the --days/-d option")
}
if (is.null(days) && is.null(pid)) {
stop("-p/--pid or --days/-d option must be used")
}
daysVal <- as.numeric(days)
pidVal <- as.numeric(pid)
if (!is.null(days) && (is.na(daysVal) || daysVal != floor(daysVal))) {
stop(paste("Number of days must be an integer"))
}
if (!is.null(pid) && (is.na(pidVal) || pidVal != floor(pidVal))) {
stop(paste("Pipelineid id must be an integer"))
}
if (is.null(projectName)) {
stop("Missing project name (-n)")
}
if (is.null(gitlabUrl)) {
stop("Missing gitlaburl (-u)")
}
if (is.null(gitlabToken)) {
stop("Missing gitlabToken (-g)")
}
## GET THE PROJECT ID IN WHICH THE pipes you want to remove are defined
apiUrl <- paste0(gitlabUrl, "/api/v4/")
# GET PROJECTS
projectResults <- GET(
paste0(apiUrl,"/projects"),
add_headers(c(
"PRIVATE-TOKEN" = gitlabToken
))
)
projectsList <- content(projectResults)
projectId <-NA
for (i in 1:length(projectsList)) {
project <- projectsList[[i]]
projectNamei <- project$name
if (projectNamei == projectName) {
projectId <- project$id
}
}
if (is.na(projectId)) {
stop(paste("Project with name",projectName,"does not exist"))
} else {
print(paste("Project", projectName, "corresponds to id", projectId))
}
pipelinesToDelete <- vector()
if (!is.null(pid)) {
# removal by pipeline
print(paste("Trying to remove pipeline with id", pidVal))
# check pidVal does exist
pipelineExistsResults <- GET(
paste0(apiUrl,"/projects/",projectId,"/pipelines/",pid),
add_headers(c(
"PRIVATE-TOKEN" = gitlabToken
))
)
message <- content(pipelineExistsResults)$message
if (!is.null(message) && message == '404 Not found') {
stop(paste("Pipeline",pid, "not found"))
} else {
pipelinesToDelete <- append(pipelinesToDelete, pid)
}
} else {
# removal by date
allPipelinesResults <- GET(
paste0(apiUrl,"/projects/",projectId,"/pipelines?per_page=100"),
add_headers(c(
"PRIVATE-TOKEN" = gitlabToken
))
)
allPipelinesDT <- rbindlist(content(allPipelinesResults))
allPipelinesDT$date <- as.Date(allPipelinesDT$updated_at)
allPipelinesDT$daynb <- as.Date(Sys.time()) - allPipelinesDT$date
pipelinesToDelete <- as.vector(allPipelinesDT[daynb > daysVal][["id"]])
}
for (pipelinesToDeletei in pipelinesToDelete) {
contentDeleteResult <- DELETE(
url = paste0(apiUrl,"/projects/",projectId,"/pipelines/",pipelinesToDeletei),
add_headers(c(
"PRIVATE-TOKEN" = gitlabToken
))
)
print(contentDeleteResult)
}