Bug 568936: Move Jenkinsfile and used scripts into source control
The Jenkinsfile used to be entered in the job config
and the scripts were stored in
https://download.eclipse.org/tools/orbit/commonFiles
The Jenkinsfile has been split into the pod definition
and a deployment script as well.
NOTE: This commit is not tested, it exists to have a comparison
point ahead of doing the main work of Bug 568936
Change-Id: Iead717d30b0b227ad8d86f1068261dcd8a15a99b
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..2ab286f
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,62 @@
+pipeline {
+ options {
+ timestamps()
+ disableConcurrentBuilds()
+ timeout(time: 180, unit: 'MINUTES')
+ }
+ parameters {
+ booleanParam(defaultValue: true, description: 'Whether the latest-X repository should reference this build, if it succeeds. Enabled by default. Generally this is disabled when building for a service release.', name: 'UPDATE_LATEST_X')
+ string(defaultValue: 'master', description: 'The branch of the Git repositories that will be build. Committers should not need to touch this field.', name: 'BUILD_BRANCH')
+ string(defaultValue: '', description: 'If a value is set, this will create a composite p2 repository pointing to this resulting build (if successful), at the given name under the downloads page.<br/><br/>This is only ever set to the release name (eg. '2019-06') for a milestone (S) build. The purpose is to give consumers a static location that tracks the release.', name: 'SIMREL_NAME')
+ choice(choices: ['I', 'S', 'M', 'R'], description: 'Valid options : I, S, M, or R. Most committers should be using I. S, M, or R should be done by a project lead, or someone tasked with putting together the release.', name: 'BUILD_LABEL')
+ }
+ agent {
+ kubernetes {
+ defaultContainer 'jnlp'
+ yamlFile 'pod.yaml'
+ }
+ }
+ environment {
+ MAVEN_OPTS = "-Xmx2G"
+ scriptDir= "./releng/scripts"
+ repoDir = "releng/repository/target/repository"
+ }
+ stages {
+ stage('Prepare') {
+ steps {
+ container('container') {
+ git branch: "${BUILD_BRANCH}", url: 'https://git.eclipse.org/r/orbit/orbit-recipes'
+ }
+ }
+ }
+ stage('Build') {
+ steps {
+ container('container') {
+ sh 'mvn -V -B -e clean install -Declipse-sign=true -Dartifact-comparator=true'
+ sh 'mvn -V -B -e clean install -Declipse-sign=true -Dartifact-comparator=true -f releng/aggregationfeature/pom.xml'
+ }
+ }
+ }
+ stage('Generate-Repositories') {
+ steps {
+ container('container') {
+ sh 'mvn -V -B -e clean verify -Declipse-sign=true -Dartifact-comparator=true -DbuildType=${BUILD_LABEL} -f releng/pom.xml'
+ }
+ }
+ }
+ stage ('Deploy') {
+ steps {
+ container('container') {
+ sshagent ( ['projects-storage.eclipse.org-bot-ssh']) {
+ sh '${scriptDir}/deploy.sh'
+ }
+ }
+ }
+ post {
+ always {
+ archiveArtifacts artifacts: 'releng/repository/target/repository/**'
+ }
+ }
+ }
+ }
+}
diff --git a/pod.yml b/pod.yml
new file mode 100644
index 0000000..4e851f6
--- /dev/null
+++ b/pod.yml
@@ -0,0 +1,46 @@
+apiVersion: v1
+kind: Pod
+spec:
+ containers:
+ - name: container
+ image: rgrunber/orbit-recipes-build@sha256:b962fe745e73c66000f958916849c76cc1739d4fbac582e9756db1b2e73440b0
+ tty: true
+ command: [ "uid_entrypoint", "cat" ]
+ resources:
+ requests:
+ memory: "2Gi"
+ cpu: "1"
+ limits:
+ memory: "2Gi"
+ cpu: "1"
+ volumeMounts:
+ - name: volume-known-hosts
+ mountPath: /home/vnc/.ssh
+ - name: jenkins-home
+ mountPath: /home/jenkins
+ readOnly: false
+ - name: jnlp
+ image: 'eclipsecbi/jenkins-jnlp-agent'
+ volumeMounts:
+ - name: volume-known-hosts
+ mountPath: /home/jenkins/.ssh
+ - name: settings-xml
+ mountPath: /home/jenkins/.m2/settings.xml
+ subPath: settings.xml
+ readOnly: true
+ - name: m2-repo
+ mountPath: /home/jenkins/.m2/repository
+ volumes:
+ - name: volume-known-hosts
+ configMap:
+ name: known-hosts
+ - name: settings-xml
+ secret:
+ secretName: m2-secret-dir
+ items:
+ - key: settings.xml
+ path: settings.xml
+ - name: m2-repo
+ emptyDir: {}
+ - name: jenkins-home
+ emptyDir: {}
diff --git a/promote-to-downloads.Jenkinsfile b/promote-to-downloads.Jenkinsfile
new file mode 100644
index 0000000..bc4586a
--- /dev/null
+++ b/promote-to-downloads.Jenkinsfile
@@ -0,0 +1,34 @@
+pipeline {
+ options {
+ timestamps()
+ disableConcurrentBuilds()
+ timeout(time: 180, unit: 'MINUTES')
+ }
+ parameters {
+ string(defaultValue: '', description: 'The location of the composite repository build folder relative to the root of the orbit folder. (eg. S-builds/S20161021172207/ ).', name: 'SRC_LOCATION')
+ string(defaultValue: 'downloads/drops', description: 'The destination for the composite repository.', name: 'DST_LOCATION')
+ string(defaultValue: '', description: 'Optional parameter to specify the name for the destination directory (eg. latest-N). If empty, the name of the destination directory will be the same as the source location.', name: 'NEW_NAME')
+ }
+ agent {
+ kubernetes {
+ defaultContainer 'jnlp'
+ yamlFile 'pod.yaml'
+ }
+ }
+ environment {
+ MAVEN_OPTS = "-Xmx2G"
+ scriptDir= "releng/scripts"
+ repoDir = "releng/repository/target/repository"
+ }
+ stages {
+ stage ('Promote') {
+ steps {
+ container('container') {
+ sshagent ( ['projects-storage.eclipse.org-bot-ssh']) {
+ sh '${scriptDir}/promote-to-downloads.sh'
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/releng/scripts/add_iplog.xsl b/releng/scripts/add_iplog.xsl
new file mode 100755
index 0000000..39f54cd
--- /dev/null
+++ b/releng/scripts/add_iplog.xsl
@@ -0,0 +1,53 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+ <xsl:output omit-xml-declaration="yes" indent="yes"/>
+
+ <!--
+ Find the <unit> element whose id, version attributes match the ones passed
+ in as parameters, and add the following elements under that unit's
+ <properties> :
+
+ <property name='iplog.bug_id' value='${bug_id}'>
+ <property name='iplog.contact.name' value='${name}'>
+ <property name='iplog.contact.email' value='${email}'>
+ -->
+
+ <xsl:param name="id"/>
+ <xsl:param name="version"/>
+ <xsl:param name="bug_id"/>
+ <xsl:param name="name"/>
+ <xsl:param name="email"/>
+
+ <xsl:template match="@* | node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+<!-- Can't reference variables/parameters inside match pattern with XSLT 1.0.
+ We split it up using template match and a conditional which does support
+ variables/parameters -->
+
+ <!-- Match against '<property name='maven-version>' so we can insert our
+ our iplog metadata below it -->
+ <xsl:template match="/repository/units/unit[properties/property/@name='maven-artifactId' and properties/property/@name='maven-version']/properties/property[@name='maven-version']">
+
+ <xsl:copy-of select="."/>
+ <!-- Confirm ancestor 'unit' for the 'properties' matches -->
+ <xsl:if test="ancestor::unit[@id=$id and starts-with(@version,$version)]">
+ <xsl:text>
+</xsl:text>
+ <xsl:text> </xsl:text>
+ <property name="iplog.bug_id" value="{$bug_id}"/>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> </xsl:text>
+ <property name="iplog.contact.name" value="{$name}"/>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> </xsl:text>
+ <property name="iplog.contact.email" value="{$email}"/>
+ </xsl:if>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/releng/scripts/cpp2c-jiro.sh b/releng/scripts/cpp2c-jiro.sh
new file mode 100755
index 0000000..6a0d631
--- /dev/null
+++ b/releng/scripts/cpp2c-jiro.sh
@@ -0,0 +1,122 @@
+#! /bin/bash
+
+function cpp2c () {
+
+src=$1
+target=$2
+newname=$3
+suffix=repository
+
+if [ ! -e "${src}" ]; then
+ echo "${src} does not exist."
+ exit 1
+fi
+
+if [ ! -e "${target}" ]; then
+ echo "${target} does not exist."
+ exit 1
+fi
+
+build=${target}
+
+if [ -z "${newname}" ]; then
+ build=${target}/`basename ${src}`
+ srcisrepo=0
+else
+ build=${target}/${newname}
+ srcisrepo=1
+fi
+
+# Sanity check (operate on one or fewer repositories)
+if [ -e "${build}" ]; then
+ num=`find ${build} -name compositeContent.xml | wc -l`
+ if [ ${num} -gt 1 ]; then
+ echo "Too many composite repositories under ${build}. Aborting."
+ exit 1
+ fi
+fi
+
+mkdir -p ${build}
+cp -rp ${src}/* ${build}
+
+for repo in compositeContent.xml compositeArtifacts.xml ; do
+ children=$(xmllint -xpath '/repository/children/child/@location' `find ${src} -name ${repo}`)
+ for child in ${children} ; do
+ childLoc=`echo ${child} | cut -d'"' -f2`
+ if [ ${srcisrepo} -eq 1 ]; then
+ newChildLoc=$(realpath -m --relative-to=${build} ${src}/${childLoc})
+ else
+ newChildLoc=$(realpath -m --relative-to=`find ${build} -type d -name ${suffix}` `find ${src} -type d -name ${suffix}`/${childLoc})
+ fi
+ sed -i "s|${childLoc}|${newChildLoc}|g" `find ${build} -name ${repo}`
+ done
+done
+
+# Add 'files.count' to ensure visibility on downloads page (if promotion)
+if [ -z "${newname}" ]; then
+ echo 1 > ${build}/files.count
+fi
+
+# Take into account fixed paths in index.html that must change
+# Take into account relative paths to images that must change
+if [ -e ${build}/index.html ]; then
+ sed -i "/For HTTP access/ s|[NISR]-builds|downloads/drops|g" ${build}/index.html
+ sed -i "s|\.\./\.\./commonFiles|\.\./\.\./\.\./commonFiles|g" ${build}/index.html
+fi
+
+
+
+}
+
+function mkp2c () {
+
+name=$1
+src=$2
+target=$3
+
+timestamp=$(date -u +"v%Y%m%d%H%M%S")
+
+if [ ! -e "${src}" ]; then
+ echo "${src} does not exist."
+ exit 1
+fi
+
+if [ ! -e "${target}" ]; then
+ echo "${target} does not exist."
+ exit 1
+fi
+
+mkdir -p ${target}/${name}
+loc=$(realpath -m --relative-to=${target}/${name} ${src}/repository)
+
+pushd ${target}/${name}
+
+cat << EOF > compositeArtifacts.xml
+<?compositeArtifactRepository version='1.0.0'?>
+<repository name='Eclipse Orbit Composite Site ${name}'
+ type='org.eclipse.equinox.internal.p2.artifact.repository.CompositeArtifactRepository' version='1.0.0'>
+ <properties size='1'>
+ <property name='p2.timestamp' value='${timestamp}'/>
+ </properties>
+ <children size='1'>
+ <child location='${loc}'/>
+ </children>
+</repository>
+EOF
+
+cat << EOF > compositeContent.xml
+<?compositeMetadataRepository version='1.0.0'?>
+<repository name='Eclipse Orbit Composite Site ${name}'
+ type='org.eclipse.equinox.internal.p2.metadata.repository.CompositeMetadataRepository' version='1.0.0'>
+ <properties size='1'>
+ <property name='p2.timestamp' value='${timestamp}'/>
+ </properties>
+ <children size='1'>
+ <child location='${loc}'/>
+ </children>
+</repository>
+EOF
+
+popd
+
+}
diff --git a/releng/scripts/create-composite.py b/releng/scripts/create-composite.py
index 21c8ed8..95f2fb8 100755
--- a/releng/scripts/create-composite.py
+++ b/releng/scripts/create-composite.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
# -----------------------------------------------------------------
# Script creating an Orbit composite p2 repository containing both the
# old style and new style recipe-based p2 repositories
@@ -20,7 +20,7 @@
def makeDirs(dir):
try:
os.makedirs(dir)
- except OSError, e:
+ except OSError as e:
if e.errno != errno.EEXIST:
raise
@@ -30,7 +30,6 @@
file.close()
def main(argv):
- ROOT_DIR = '/home/data/httpd/download.eclipse.org/tools/orbit/'
ARTIFACT_TEMPLATE = """<?xml version='1.0' encoding='UTF-8'?>
<?compositeArtifactRepository version='1.0.0'?>
<repository name='Eclipse Orbit Composite Site {build}'
@@ -76,7 +75,7 @@
buildTimestamp = getVariable('BUILD_TIME')
build = buildLabel + buildTimestamp
- destination = ROOT_DIR + buildLabel + '-builds/'+ build + '/repository'
+ destination = buildLabel + '-builds/'+ build + '/repository'
makeDirs(destination)
writeFile(destination + '/compositeArtifacts.xml',
@@ -86,7 +85,7 @@
METADATA_TEMPLATE.format(build = build, timestamp = buildTimestamp,
orbitOldLocation = orbitOldLocation, orbitNewLocation = orbitNewLocation))
writeFile(destination + '/p2.index', P2_INDEX)
- print 'Created composite p2 repository in {0}'.format(destination)
+ print('Created composite p2 repository in {0}'.format(destination))
if __name__ == "__main__":
main(sys.argv[1:])
diff --git a/releng/scripts/deploy.sh b/releng/scripts/deploy.sh
new file mode 100644
index 0000000..8087424
--- /dev/null
+++ b/releng/scripts/deploy.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+
+# The default seems to be 0022
+# We need user (genie.orbit) and group (tools.orbit) to match
+umask 0002
+
+contentFile=${repoDir}/content.xml
+
+# Ensure repository generated by maven has proper permissions
+# Remove this line when Hudson has global umask of 0002
+chmod -R g+w ${repoDir}
+
+unzip -d ${repoDir} ${repoDir}/content.jar
+
+set -e
+for f in `find . -name ip_log.xml`; do
+
+ # Check if IP Information is from IPZilla or ClearlyDefined
+ set +e
+ test_ipzilla=`xmllint --xpath '/ip_log/project/legal/ipzilla/@bug_id' ${f}`
+ set -e
+ if [ -n "${test_ipzilla}" ]; then
+ bug_id_xpath='/ip_log/project/legal/ipzilla/@bug_id'
+ else
+ bug_id_xpath='/ip_log/project/legal/clearlydefined/@url'
+ fi
+
+ xsltproc \
+ --stringparam id \
+ `xmllint --xpath '/ip_log/project/@id' ${f} | cut -d'"' -f2` \
+ --stringparam version \
+ `xmllint --xpath '/ip_log/project/@version' ${f} | cut -d'"' -f2` \
+ --stringparam name \
+ "`xmllint --xpath '/ip_log/project/contact/name/text()' ${f}`" \
+ --stringparam email \
+ `xmllint --xpath '/ip_log/project/contact/email/text()' ${f}` \
+ --stringparam bug_id \
+ `xmllint --xpath "${bug_id_xpath}" ${f} | head -1 | cut -d'"' -f2` \
+ ${scriptDir}/add_iplog.xsl ${contentFile} > ${repoDir}/new-content.xml
+
+ mv ${repoDir}/new-content.xml ${contentFile}
+done
+set +e
+
+xsltproc ${scriptDir}/props_size.xsl ${contentFile} > ${repoDir}/new-content.xml
+mv ${repoDir}/new-content.xml ${contentFile}
+
+zip -j ${repoDir}/content.jar ${contentFile}
+rm ${contentFile}
+
+export BUILD_TIME=$(echo $(basename $(find releng/repository -name "orbit-*-repo.zip")) | grep -o "[0-9]*")
+OLD_BUILD_LABEL=`echo ${ORBIT_OLD_LOCATION} | rev | cut -d/ -f2 | rev`
+NEW_BUILD_LABEL=${BUILD_LABEL}${BUILD_TIME}
+
+# Promote orbit-recipes build to drops2 location
+ORBIT_DOWNLOAD_LOC=/home/data/httpd/download.eclipse.org/tools/orbit/downloads
+ssh genie.orbit@projects-storage.eclipse.org mkdir -p ${ORBIT_DOWNLOAD_LOC}/drops2/${NEW_BUILD_LABEL}
+scp -r ${repoDir} genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}/drops2/${NEW_BUILD_LABEL}
+
+# Create the composite repository
+export ORBIT_NEW_LOCATION=../../../downloads/drops2/${NEW_BUILD_LABEL}/repository
+pushd $HOME
+${scriptDir}/create-composite.py
+scp -r ${BUILD_LABEL}-builds/${NEW_BUILD_LABEL} genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}/../${BUILD_LABEL}-builds/
+ssh genie.orbit@projects-storage.eclipse.org chmod g+s ${ORBIT_DOWNLOAD_LOC}/../${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}
+popd
+
+# Copy the aggregated repository archive
+buildRepoZipPath=`find releng/repository-all/target/ -name "orbit-buildrepo-*.zip"`
+chmod g+w ${buildRepoZipPath}
+buildRepoZipName=`basename ${buildRepoZipPath}`
+buildRepoZipDir=`dirname ${buildRepoZipPath}`
+zipFileSize='('`ls -sh ${buildRepoZipPath} | cut -d' ' -f1`')'
+mkdir checksum
+pushd releng/repository-all/target/
+md5sum ${buildRepoZipName} > ../../../checksum/${buildRepoZipName}.md5
+sha1sum ${buildRepoZipName} > ../../../checksum/${buildRepoZipName}.sha1
+popd
+scp -r ${buildRepoZipPath} checksum genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}/../${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/
+
+# Copy Repository Report
+chmod -R g+w releng/repository-report/target/reporeports/
+scp -r releng/repository-report/target/reporeports/ genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}/../${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}
+
+# Generate IPLog HTML Page
+mkdir -p bug506001/{${OLD_BUILD_LABEL},${NEW_BUILD_LABEL}}
+pushd bug506001
+curl -L -o ${OLD_BUILD_LABEL}/content.jar https://download.eclipse.org/tools/orbit/downloads/drops/${OLD_BUILD_LABEL}/repository/content.jar
+curl -L -o ${NEW_BUILD_LABEL}/content.jar https://download.eclipse.org/tools/orbit/downloads/drops2/${NEW_BUILD_LABEL}/repository/content.jar
+unzip -d ${OLD_BUILD_LABEL} ${OLD_BUILD_LABEL}/content.jar
+unzip -d ${NEW_BUILD_LABEL} ${NEW_BUILD_LABEL}/content.jar
+popd
+scp -r bug506001 genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}/../bug506001
+
+curl -L -O https://download.eclipse.org/tools/orbit/${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/repository/compositeContent.xml
+cat compositeContent.xml | curl -v -o index.html -d @- "http://www.eclipse.org/orbit/scripts/iplog.php?repoPath=tools/orbit/${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/repository&buildURL=${BUILD_URL}&zipFileSize=${zipFileSize}"
+scp index.html genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}/../${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/
+
+ssh genie.orbit@projects-storage.eclipse.org rm -r ${ORBIT_DOWNLOAD_LOC}/../bug506001
+
+. ${scriptDir}/cpp2c-jiro.sh
+pushd $HOME
+
+# Update latest-X repository with this build
+if [ "${UPDATE_LATEST_X}" = "true" ]; then
+ mkdir -p ${BUILD_LABEL}-builds/${NEW_BUILD_LABEL} downloads/latest-${BUILD_LABEL}
+ scp -r genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}/../${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/repository ${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}
+ cpp2c ${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/repository/ downloads/ latest-${BUILD_LABEL}
+ scp -r downloads/latest-${BUILD_LABEL} genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}
+fi
+
+# Update static release repo with this build
+if [ -n "${SIMREL_NAME}" ]; then
+ mkdir -p downloads
+ mkp2c "${SIMREL_NAME}" ${BUILD_LABEL}-builds/${NEW_BUILD_LABEL} downloads
+ scp -r downloads/${SIMREL_NAME} genie.orbit@projects-storage.eclipse.org:${ORBIT_DOWNLOAD_LOC}
+fi
+
+popd
+
+set +x
+echo "####################################################################################################"
+echo "####################################################################################################"
+echo "####################################################################################################"
+echo "### Build Page : https://download.eclipse.org/tools/orbit/${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/ ###"
+echo "### p2 Repository : https://download.eclipse.org/tools/orbit/${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/repository ###"
+echo "####################################################################################################"
+echo "####################################################################################################"
+echo "####################################################################################################"
+
+if [ "${BUILD_LABEL}" = "I" ]; then
+ export SRC_LOCATION=${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/
+ export DST_LOCATION=downloads/drops
+ ${scriptDir}/promote-to-downloads.sh
+ curl -s "${JENKINS_URL}job/promote-to-downloads/buildWithParameters?token=ef2561f5adf2c26628129367452e3583&SRC_LOCATION=${BUILD_LABEL}-builds/${NEW_BUILD_LABEL}/"
+fi
+
+set -x
diff --git a/releng/scripts/promote-to-downloads.sh b/releng/scripts/promote-to-downloads.sh
new file mode 100644
index 0000000..a9b37e8
--- /dev/null
+++ b/releng/scripts/promote-to-downloads.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# The default seems to be 0022
+# We need user (genie.orbit) and group (tools.orbit) to match
+umask 0002
+
+. ${scriptDir}/cpp2c-jiro.sh
+
+ORBIT_ROOT=/home/data/httpd/download.eclipse.org/tools/orbit
+
+mkdir -p ${SRC_LOCATION} ${DST_LOCATION}
+pushd ${SRC_LOCATION}
+scp -r genie.orbit@projects-storage.eclipse.org:${ORBIT_ROOT}/${SRC_LOCATION}/* .
+popd
+cpp2c ${SRC_LOCATION} ${DST_LOCATION} ${NEW_NAME}
+scp -r ${DST_LOCATION}/* genie.orbit@projects-storage.eclipse.org:${ORBIT_ROOT}/${DST_LOCATION}
diff --git a/releng/scripts/props_size.xsl b/releng/scripts/props_size.xsl
new file mode 100755
index 0000000..3dc0ae5
--- /dev/null
+++ b/releng/scripts/props_size.xsl
@@ -0,0 +1,29 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+ <xsl:output omit-xml-declaration="yes" indent="yes"/>
+
+ <!-- Increment the size attribute (under <properties> ) by 3 for any
+ <unit> containing a property named 'iplog.bug_id' -->
+
+ <xsl:template match="@* | node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="/repository/units/unit/properties/@size">
+ <xsl:choose>
+ <xsl:when test="../property[@name='iplog.bug_id']">
+ <xsl:attribute name="size">
+ <xsl:value-of select="../@size+3"/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="size">
+ <xsl:value-of select="../@size"/>
+ </xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>