Skip to content

Commit c172c81

Browse files
committed
[Build] Unify clean-up jobs as Jenkins pipelines and inline scripts
Migrate all clean-up jobs to pure Jenkinsfile based pipeline jobs. In this process the following enhancements are applied additionally: - Unify the daily clean-up builds into one job that handles Eclipse and Equinox builds in parallel. - Implement the logic for all release/stable and I-build drop removals upon releases into the job for the cleanup of release artifacts, so that it doesn't need any input parameters. - Continuously clean-up Y-build drops, with the policy to keep the last nine builds (corresponds to three consecutive weeks on the regular build schedule). - Introduce the ability to manually mark a as kept-indfinitivly in order to prevent the clean-up jobs from deleting it. - Move the update of the eclipse/updates/index.html to redirect to the update-site of the latest release into the (release) publication job.
1 parent 5407792 commit c172c81

13 files changed

+383
-396
lines changed

JenkinsJobs/Builds/FOLDER.groovy

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@ pipelineJob('Builds/Build-Docker-images'){
5757

5858
pipelineJob('Builds/mark-build'){
5959
displayName("Mark build")
60-
description("Mark a build as stable or unstable.")
60+
description("Mark a build as stable/unstable or to (not to) be kept indefinitely.")
6161
parameters {
6262
stringParam('buildId', null, "ID of the build to be marked.")
6363
choiceParam('markAs', [
6464
'STABLE',
6565
'UNSTABLE',
66+
'RETAINED_INDEFINITELY',
67+
'NOT_RETAINED',
6668
], 'The kind of marker to apply to (respectively remove from) the specified build.')
6769
stringParam('issueURL', null, 'URL of the causing Github issue or PR (<em>only relevant if the build is marked as unstable<em>).')
6870
}

JenkinsJobs/Builds/markBuild.jenkinsfile

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,26 @@ pipeline {
4343
echo "<p>This build is marked unstable due to <a href='${issueURL}'>${label}</a>.</p>" > buildUnstable
4444
scp buildUnstable [email protected]:${EP_BUILD_DROP}/buildUnstable
4545
;;
46+
RETAINED_INDEFINITELY)
47+
#Add keep tag
48+
ssh [email protected] touch ${EP_BUILD_DROP}/buildKeep
49+
;;
50+
NOT_RETAINED)
51+
#Remove keep tag
52+
ssh [email protected] rm -f ${EP_BUILD_DROP}/buildKeep
53+
;;
4654
esac
4755
'''
4856
}
4957
build job: 'Releng/updateIndex', wait: false
50-
build job: 'Releng/modifyP2CompositeRepository', wait: true, propagate: true, parameters: [
51-
string(name: 'repositoryPath', value: "eclipse/updates/${RELEASE_VER}-I-builds"),
52-
string(name: "${params.markAs == 'STABLE' ? 'add' : 'remove'}", value: "${buildId}")
53-
]
58+
script {
59+
if (params.markAs == 'STABLE' || params.markAs == 'UNSTABLE') {
60+
build job: 'Releng/modifyP2CompositeRepository', wait: true, propagate: true, parameters: [
61+
string(name: 'repositoryPath', value: "eclipse/updates/${RELEASE_VER}-I-builds"),
62+
string(name: "${params.markAs == 'STABLE' ? 'add' : 'remove'}", value: "${buildId}")
63+
]
64+
}
65+
}
5466
}
5567
}
5668
}

JenkinsJobs/Cleanup/FOLDER.groovy

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,43 @@
11
folder('Cleanup') {
22
description('Cleanup Scripts.')
33
}
4+
5+
pipelineJob('Cleanup/cleanupBuilds'){
6+
displayName('Daily Cleanup of old Builds')
7+
description('Remove old builds from the downloads servers.')
8+
properties {
9+
pipelineTriggers {
10+
triggers {
11+
cron {
12+
spec('''TZ=America/Toronto
13+
0 4 * * *
14+
0 16 * * *
15+
''')
16+
}
17+
}
18+
}
19+
}
20+
definition {
21+
cpsScm {
22+
lightweight(true)
23+
scm {
24+
github('eclipse-platform/eclipse.platform.releng.aggregator', 'master')
25+
}
26+
scriptPath('JenkinsJobs/Cleanup/cleanupBuilds.jenkinsfile')
27+
}
28+
}
29+
}
30+
31+
pipelineJob('Cleanup/cleanupReleaseArtifacts'){
32+
displayName('Cleanup Release Artifacts')
33+
description('Cleanup major artifacts from previous releases at the beginning of a new release.')
34+
definition {
35+
cpsScm {
36+
lightweight(true)
37+
scm {
38+
github('eclipse-platform/eclipse.platform.releng.aggregator', 'master')
39+
}
40+
scriptPath('JenkinsJobs/Cleanup/cleanupReleaseArtifacts.jenkinsfile')
41+
}
42+
}
43+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
2+
def int EQUINOX_RETENTION_COUNT = 3 // Keep the three most recent builds
3+
def int ECLIPSE_I_BUILD_RETENTION_DAYS = 5 // Minimal number of days for which a drop should be retained
4+
def int ECLIPSE_I_BUILD_RETENTION_COUNT = 4 // Minimal number of drops to retain regardless of their age
5+
def int ECLIPSE_Y_BUILD_RETENTION_COUNT = 9 // Number of drops retained for Y-builds (corresponds to builds of three consecutive weeks)
6+
7+
pipeline {
8+
options {
9+
skipDefaultCheckout()
10+
timestamps()
11+
timeout(time: 15, unit: 'MINUTES')
12+
buildDiscarder(logRotator(numToKeepStr:'5'))
13+
}
14+
agent {
15+
label 'basic'
16+
}
17+
environment {
18+
// Download Server locations (would very seldom change)
19+
EP_ROOT = '/home/data/httpd/download.eclipse.org'
20+
EP_ECLIPSE_DROPS = "${EP_ROOT}/eclipse/downloads/drops4"
21+
EP_EQUINOX_DROPS = "${EP_ROOT}/equinox/drops"
22+
23+
SSH = 'ssh [email protected]'
24+
}
25+
stages {
26+
stage('Checkout SCM') {
27+
steps {
28+
checkout scmGit(userRemoteConfigs: [[url: "${scm.userRemoteConfigs[0].url}"]], branches: [[name: "${scm.branches[0].name}"]],
29+
extensions: [cloneOption(depth: 1, shallow: true, noTags: true), sparseCheckout([
30+
[path: 'JenkinsJobs/shared/utilities.groovy'],
31+
[path: 'cje-production/buildproperties.txt'],
32+
])])
33+
script {
34+
utilities = load "JenkinsJobs/shared/utilities.groovy"
35+
utilities.setDryRun(false)
36+
def buildProperties = readProperties(file: 'cje-production/buildproperties.txt')
37+
devVersionMajor = buildProperties.STREAMMajor.replace('"','').toInteger() // Remove surrounding quotes
38+
devVersionMinor = buildProperties.STREAMMinor.replace('"','').toInteger() // Remove surrounding quotes
39+
devVersionService = buildProperties.STREAMService.replace('"','').toInteger() // Remove surrounding quotes
40+
}
41+
}
42+
}
43+
stage('Clean up builds') {
44+
parallel {
45+
stage('Equinox') {
46+
steps {
47+
sshagent (['projects-storage.eclipse.org-bot-ssh']) {
48+
removeSurplusBuildDrops("${EP_EQUINOX_DROPS}", 'I', EQUINOX_RETENTION_COUNT)
49+
removeSurplusBuildDrops("${EP_EQUINOX_DROPS}", 'Y', EQUINOX_RETENTION_COUNT)
50+
}
51+
}
52+
}
53+
stage('Eclipse I-builds') {
54+
steps {
55+
sshagent (['projects-storage.eclipse.org-bot-ssh']) {
56+
removeOldBuildDropsOfCurrentStream("${EP_ECLIPSE_DROPS}", 'I', ECLIPSE_I_BUILD_RETENTION_DAYS, ECLIPSE_I_BUILD_RETENTION_COUNT)
57+
}
58+
}
59+
}
60+
stage('Eclipse Y-builds') {
61+
steps {
62+
sshagent (['projects-storage.eclipse.org-bot-ssh']) {
63+
removeSurplusBuildDrops("${EP_ECLIPSE_DROPS}", 'Y', ECLIPSE_Y_BUILD_RETENTION_COUNT)
64+
}
65+
}
66+
}
67+
}
68+
post {
69+
success {
70+
build job: 'Releng/updateIndex', wait: false
71+
}
72+
}
73+
}
74+
}
75+
}
76+
77+
@groovy.transform.Field
78+
def utilities = null
79+
@groovy.transform.Field
80+
def int devVersionMajor = null
81+
@groovy.transform.Field
82+
def int devVersionMinor = null
83+
@groovy.transform.Field
84+
def int devVersionService = null
85+
86+
def removeSurplusBuildDrops(String remoteDirectory, String buildType, int retentionCount) {
87+
def drops = utilities.listBuildDropDirectoriesOnRemote(remoteDirectory, "${buildType}*") // sorted in ascending order
88+
if (retentionCount < drops.size()) {
89+
utilities.removeDropsOnRemote(remoteDirectory, drops.subList(0, drops.size() - retentionCount))
90+
} else {
91+
echo "Nothing to clean in ${remoteDirectory} with pattern '${buildType}*'. Found only ${drops}, not exceeding the threshold of ${retentionCount}."
92+
}
93+
}
94+
95+
def removeOldBuildDropsOfCurrentStream(String remoteDirectory, String buildType, int retentionDays, int retentionCount) {
96+
// Among all builds older than minimal retention time, keep only the first stable build of each week (starting Monday) and delete all others.
97+
// But keep at least the minimal retention count of builds regardless of their age and all of those younger than the minimal retention time.
98+
def retentionThresholdDate = java.time.LocalDate.now().minusDays(retentionDays)
99+
def allBuilds = utilities.listBuildDropDirectoriesOnRemote(remoteDirectory, "${buildType}*",
100+
devVersionMajor, devVersionMinor, devVersionService).sort() // sort ascending to start with Mondays
101+
echo "Number of ${buildType}-builds before cleaning: ${allBuilds.size()}"
102+
def oldBuilds = allBuilds.findAll{ b -> parseDate(b) < retentionThresholdDate} // sorted ascendingly to start with Mondays
103+
// Keep the specified count of latest builds
104+
def toRetain = allBuilds.subList(Math.max(allBuilds.size() - retentionCount, 0), allBuilds.size()).toSet()
105+
echo "Old ${buildType}-Builds: ${oldBuilds}"
106+
echo "Minimally retained ${buildType}-Builds: ${toRetain}"
107+
def Set<Integer> retainedWeeks = []
108+
def toRemove = []
109+
for (oldBuild in oldBuilds) {
110+
if (toRetain.contains(oldBuild)) {
111+
echo "Not removed (since one of the ${retentionCount} newest builds, even though old): ${oldBuild}"
112+
continue;
113+
}
114+
def weekOfYear = parseDate(oldBuild).get(java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR)
115+
if (!retainedWeeks.contains(weekOfYear)) {
116+
def isUnstable = sh(script: """$SSH "if [ -f '${remoteDirectory}/${oldBuild}/buildUnstable' ]; then echo 'true'; fi" """, returnStdout: true).toBoolean()
117+
if (!isUnstable) {
118+
retainedWeeks.add(weekOfYear) // Retain the first stable build of a week
119+
echo "Retain stable build ${oldBuild} for week ${weekOfYear}"
120+
continue;
121+
} else {
122+
echo "Ignore unstable build ${oldBuild} to be retained for week ${weekOfYear}"
123+
}
124+
}
125+
toRemove.add(oldBuild)
126+
}
127+
if (!toRemove.isEmpty()) {
128+
utilities.removeDropsOnRemote(remoteDirectory, toRemove)
129+
} else {
130+
echo "Nothing to clean in ${remoteDirectory} with pattern '${buildType}*' and retention of at least ${retentionDays} days and ${retentionCount} builds."
131+
}
132+
}
133+
134+
@NonCPS
135+
def parseDate(String iBuildId) {
136+
def idMatcher = iBuildId =~ /I(?<date>\d{8})-(?<time>\d{4})/
137+
if (!idMatcher.matches()) {
138+
error "buildID: ${iBuildId}, does not match the expected pattern."
139+
}
140+
def date = idMatcher.group('date')
141+
return java.time.LocalDate.of(date.substring(0, 4).toInteger(), date.substring(4, 6).toInteger(), date.substring(6, 8).toInteger())
142+
}

JenkinsJobs/Cleanup/cleanupDLsite.groovy

Lines changed: 0 additions & 33 deletions
This file was deleted.

JenkinsJobs/Cleanup/cleanupReleaseArtifacts.groovy

Lines changed: 0 additions & 102 deletions
This file was deleted.

0 commit comments

Comments
 (0)