release-cut: Update process to use unstable's current public snapshots

This alters the release cut scripts to use unstable's snapshots as the basis for
creating the release cut. It also migrates a bunch of functions into the library
so we can better manage the code, and makes the cut idempotent, doing a "publish
switch" instead of a "publish snapshot" in case the releases have already been
published.

Change-Id: Ib4754cb70f6fce37f265b6dfc075025dc69fde99
diff --git a/cicd/pipelines/tasks/task_release_cut.jenkins b/cicd/pipelines/tasks/task_release_cut.jenkins
index 43f7ee3..d338ca3 100644
--- a/cicd/pipelines/tasks/task_release_cut.jenkins
+++ b/cicd/pipelines/tasks/task_release_cut.jenkins
@@ -1,24 +1,6 @@
 #!/usr/bin/env groovy
 
-String getLatestSnapshot(repository_stem) {
-    def script = """
-        aptly snapshot list --sort=time --raw \
-            | grep -E '^${repository_stem}-' \
-            | tail -n1
-    """
-
-    return sh(returnStdout: true, script: script).trim()
-}
-
-def installGpgKeyring() {
-    sh """
-       install -d -m 700 -o root -g root /var/lib/aptly/.gnupg
-       tar -C /var/lib/aptly/.gnupg -zxf /var/lib/aptly/keyring/release-keyring.tar.gz
-       chown -R root:root /var/lib/aptly/.gnupg
-       find /var/lib/aptly/.gnupg -type d -exec chmod 700 '{}' ';'
-       find /var/lib/aptly/.gnupg -type f -exec chmod 600 '{}' ';'
-       """
-}
+library 'functions'
 
 def workspacePath = "/home/jenkins/workspace"
 def buildLabel = "task.publish.unstable-${UUID.randomUUID().toString()}"
@@ -41,8 +23,7 @@
     node(buildLabel) {
         dir(sourcePath) {
             container('debian') {
-                def date = new Date()
-                String stamp = date.format("yyyyMMdd-HHmmss")
+                String stamp = functions.generateDateStamp()
                 def releaseName = params.release
                 def boards = params.boards.split(' ')
 
@@ -53,25 +34,31 @@
                 sh "cp /etc/aptly.conf ~/.aptly.conf"
 
                 withEnv(['GNUPGHOME=/var/lib/aptly/.gnupg']) {
-                    installGpgKeyring()
+                    functions.installGpgKeyring()
 
                     for (board in boards) {
-                        def unstableBspSnapshotName = getLatestSnapshot("unstable-bsp-${board}")
+                        def unstablePublishedBspSnapshotName = functions.getSnapshotFromPublish("unstable-bsp-${board}", 'main')
                         def releasedBspSnapshotName = "${releaseName}-bsp-${board}-${stamp}"
 
-                        sh """
-                           aptly snapshot merge ${releasedBspSnapshotName} ${unstableBspSnapshotName}
-                           aptly publish snapshot --batch --force-overwrite --passphrase-file=/var/lib/aptly/keyring/passphrase.txt --architectures=source,amd64,arm64,armhf --distribution=${releaseName} ${releasedBspSnapshotName} filesystem:public:${releaseName}-bsp-${board}
-                           """
+                        sh "aptly snapshot merge ${releasedBspSnapshotName} ${unstableBspSnapshotName}"
+
+                        if (functions.bspIsPublished(releaseName, board)) {
+                           sh "aptly publish switch --batch --force-overwrite --passphrase-file=/var/lib/aptly/keyring/passphrase.txt ${releaseName} filesystem:public:${releaseName}-bsp-${board} ${releasedBspSnapshotName}"
+                        } else {
+                           sh "aptly publish snapshot --batch --force-overwrite --passphrase-file=/var/lib/aptly/keyring/passphrase.txt --architectures=source,amd64,arm64,armhf --distribution=${releaseName} ${releasedBspSnapshotName} filesystem:public:${releaseName}-bsp-${board}"
+                        }
                     }
 
-                    def unstableCoreSnapshotName = getLatestSnapshot('core-full-unstable')
+                    def unstablePublishedCoreSnapshotName = functions.getSnapshotFromPublish('unstable', 'main')
                     def releasedCoreSnapshotName = "core-full-${releaseName}-${stamp}"
 
-                    sh """
-                       aptly snapshot merge ${releasedCoreSnapshotName} ${unstableCoreSnapshotName}
-                       aptly publish snapshot --batch --force-overwrite --passphrase-file=/var/lib/aptly/keyring/passphrase.txt --architectures=source,amd64,arm64,armhf --distribution=${releaseName} ${releasedCoreSnapshotName} filesystem:public:${releaseName}
-                       """
+                    sh "aptly snapshot merge ${releasedCoreSnapshotName} ${unstableCoreSnapshotName}"
+
+                    if (functions.coreIsPublished(releaseName)) {
+                        sh "aptly publish switch --batch --force-overwrite --passphrase-file=/var/lib/aptly/keyring/passphrase.txt ${releaseName} filesystem:public:${releaseName} ${releasedCoreSnapshotName}"
+                    } else {
+                        sh "aptly publish snapshot --batch --force-overwrite --passphrase-file=/var/lib/aptly/keyring/passphrase.txt --architectures=source,amd64,arm64,armhf --distribution=${releaseName} ${releasedCoreSnapshotName} filesystem:public:${releaseName}"
+                    }
                 }
             }
         }
diff --git a/vars/functions.groovy b/vars/functions.groovy
index 043213c..da99632 100644
--- a/vars/functions.groovy
+++ b/vars/functions.groovy
@@ -22,6 +22,17 @@
     return sh(returnStdout: true, script: script).trim()
 }
 
+String getSnapshotFromPublish(publishName, component) {
+    def script = """
+        aptly publish show filesystem:public:${release} \
+            | grep -E '^  ${component}: .*' \
+            | grep 'snapshot'
+            | awk '{ print $2 }'
+    """
+
+    return sh(returnStdout: true, script: script).trim()
+}
+
 def installGpgKeyring() {
     sh """
        install -d -m 700 -o root -g root /var/lib/aptly/.gnupg