Skip to content

Commit

Permalink
(SIMP-10264) GHA: Add rpm_release workflow (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
op-ct authored Jul 14, 2021
1 parent ff9926c commit a9e5c9e
Show file tree
Hide file tree
Showing 2 changed files with 301 additions and 16 deletions.
272 changes: 272 additions & 0 deletions .github/workflows/release_rpms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# Manual action to build, sign, and attach a release's RPMs
# ------------------------------------------------------------------------------
#
# NOTICE: **This file is maintained with puppetsync**
#
# This file is updated automatically as part of a puppet module baseline.
#
# The next baseline sync will overwrite any local changes to this file!
#
# ==============================================================================
# This pipeline uses the following GitHub Action Secrets:
#
# GitHub Secret variable Notes
# ------------------------------- ---------------------------------------
# SIMP_CORE_REF_FOR_BUILDING_RPMS simp-core ref (tag) to use to build
# RPMs with `rake pkg:single`
# SIMP_DEV_GPG_SIGNING_KEY GPG signing key's secret key
# SIMP_DEV_GPG_SIGNING_KEY_ID User ID (name) of signing key
# SIMP_DEV_GPG_SIGNING_KEY_PASSPHRASE Passphrase to use GPG signing key
#
# ------------------------------------------------------------------------------
#
# * This is a workflow_dispatch action, which can be triggered manually or from
# other workflows/API.
#
# * If triggered by another workflow, it will be necessary to provide a GitHub
# access token via the the `target_repo_token` parameter
#
---
name: 'RELENG: Build + attach RPMs to GitHub Release'

on:
workflow_dispatch:
inputs:
release_tag:
description: "Release tag"
required: true
clobber:
description: "Clobber identical assets?"
required: false
default: 'yes'
clean:
description: "Wipe all release assets first?"
required: false
default: 'no'
autocreate_release:
# A GitHub release is needed to upload artifacts to, and some repos
# (e.g., forked mirrors) only have tags.
description: "Create release if missing? (tag must exist)"
required: false
default: 'yes'
build_container_os:
description: "Build container OS"
required: true
default: 'centos8'
target_repo:
description: "Target repo (instead of this one)"
required: false
# WARNING: To avoid exposing secrets in the log, only use this token with
# action/script's `github-token` parameter, NEVER in `env:` vars
target_repo_token:
description: "API token for uploading to target repo"
required: false
dry_run:
description: "Dry run (Test-build RPMs)"
required: false
default: 'no'

env:
TARGET_REPO: ${{ (github.event.inputs.target_repo != null && format('{0}/{1}', github.repository_owner, github.event.inputs.target_repo)) || github.repository }}
RELEASE_TAG: ${{ github.event.inputs.release_tag }}

jobs:
create-and-attach-rpms-to-github-release:
name: Build and attach RPMs to Release
runs-on: ubuntu-20.04
steps:
- name: "Validate inputs"
run: |
if ! [[ "$TARGET_REPO" =~ ^[a-z0-9][a-z0-9-]+/[a-z0-9][a-z0-9_-]+$ ]]; then
printf '::error ::Target repository name has invalid format: %s\n' "$TARGET_REPO"
exit 88
fi
if ! [[ "$RELEASE_TAG" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+(-(rc|alpha|beta|pre)?([0-9]+)?)?$ ]]; then
printf '::error ::Release Tag format is not SemVer or SemVer-ish RPM: %s\n' "$RELEASE_TAG"
exit 88
fi
- name: >
Query info for ${{ env.TARGET_REPO }}
release ${{ github.event.inputs.release_tag }}
(autocreate_release = '${{ github.event.inputs.autocreate_release }}')
id: release-api
env:
AUTOCREATE_RELEASE: ${{ github.event.inputs.autocreate_release }}
uses: actions/github-script@v4
with:
github-token: ${{ github.event.inputs.target_repo_token || secrets.GITHUB_TOKEN }}
script: |
const [owner, repo] = process.env.TARGET_REPO.split('/')
const tag = process.env.RELEASE_TAG
const autocreate_release = (process.env.AUTOCREATE_RELEASE == 'yes')
var release_id
const owner_data = { owner: owner, repo: repo }
const release_data = Object.assign( {tag: tag}, owner_data )
const create_release_data = Object.assign( {tag_name: tag}, owner_data )
const tag_data = Object.assign( {ref: `tags/${tag}`}, owner_data )
function id_from_release(data) {
console.log( `>> Release for ${owner}/${repo}, tag ${tag}` )
console.log( `>>>> release_id: ${data.id}` )
return(data.id)
}
function throw_error_unless_should_autocreate_release(err){
if (!( err.name == 'HttpError' && err.status == 404 && autocreate_release )){
core.error(`Error finding release for tag ${tag}: ${err.name}`)
throw err
}
}
async function autocreate_release_if_appropriate(err){
throw_error_unless_should_autocreate_release(err)
core.warning(`Can't find release for tag ${tag} and tag exists, auto-creating release`)
// Must already have a tag
github.request( 'GET /repos/{owner}/{repo}/git/matching-refs/{ref}', tag_data ).then (
result => {
if (result.data.length == 0) { throw `Can't find tag ${tag} in repo ${owner}/${repo}` }
}
).then(
result => {
github.request( 'POST /repos/{owner}/{repo}/releases', create_release_data).then(
result=>{
release_id = id_from_release(result.data)
},
post_err =>{
core.error('Error auto-creating release')
throw post_err
}
)
}
).finally(()=>{
return(release_id)
})
}
github.request('GET /repos/{owner}/{repo}/releases/tags/{tag}', release_data ).then(
result => { release_id = id_from_release(result.data) },
err => { release_id = autocreate_release_if_appropriate(err) }
).catch( e => { throw e } ).then(
result => {
if (!release_id){
throw `Could not get release for ${tag} for repo ${owner}:${repo}`
}
core.setOutput('id', release_id)
}
)
- name: Checkout code
uses: actions/checkout@v2
with:
repository: ${{ env.TARGET_REPO }}
ref: ${{ env.RELEASE_TAG }}
clean: true
fetch-depth: 0

- name: 'Build & Sign RPMs for ${{ github.event.inputs.release_tag }} Release'
uses: simp/github-action-build-and-sign-pkg-single-rpm@v2
id: build-and-sign-rpm
with:
gpg_signing_key: ${{ secrets.SIMP_DEV_GPG_SIGNING_KEY }}
gpg_signing_key_id: ${{ secrets.SIMP_DEV_GPG_SIGNING_KEY_ID }}
gpg_signing_key_passphrase: ${{ secrets.SIMP_DEV_GPG_SIGNING_KEY_PASSPHRASE }}
simp_core_ref_for_building_rpms: ${{ secrets.SIMP_CORE_REF_FOR_BUILDING_RPMS }}
simp_builder_docker_image: 'docker.io/simpproject/simp_build_${{ github.event.inputs.build_container_os }}:latest'

- name: "Wipe all previous assets from GitHub Release (when clean == 'yes')"
if: ${{ github.event.inputs.clean == 'yes' && github.event.inputs.dry_run != 'yes' }}
uses: actions/github-script@v4
env:
release_id: ${{ steps.release-api.outputs.id }}
with:
github-token: ${{ github.event.inputs.target_repo_token || secrets.GITHUB_TOKEN }}
script: |
const release_id = process.env.release_id
const [owner, repo] = process.env.TARGET_REPO.split('/')
const existingAssets = await github.repos.listReleaseAssets({ owner, repo, release_id })
console.log( ` !! !! Wiping ALL uploaded assets for ${owner}/${repo} release (id: ${release_id})`)
existingAssets.data.forEach(async function(asset){
asset_id = asset.id
console.log( ` !! !! !! Wiping existing asset for ${asset.name} (id: ${asset_id})`)
await github.repos.deleteReleaseAsset({ owner, repo, asset_id })
})
- name: 'Upload RPM file(s) to GitHub Release (github-script)'
if: ${{ github.event.inputs.dry_run != 'yes' }}
uses: actions/github-script@v4
env:
rpm_file_paths: ${{ steps.build-and-sign-rpm.outputs.rpm_file_paths }}
rpm_gpg_file: ${{ steps.build-and-sign-rpm.outputs.rpm_gpg_file }}
release_id: ${{ steps.release-api.outputs.id }}
clobber: ${{ github.event.inputs.clobber }}
clean: ${{ github.event.inputs.clean }}
dry_run: ${{ github.event.inputs.dry_run }}
with:
github-token: ${{ github.event.inputs.target_repo_token || secrets.GITHUB_TOKEN }}
script: |
const path = require('path')
const fs = require('fs')
async function clobberAsset (name, owner, repo, release_id ){
console.log( ` -- clobber asset ${name}: owner: ${owner} repo: ${repo} release_id: ${release_id}` )
const existingAssets = await github.repos.listReleaseAssets({ owner, repo, release_id })
const matchingAssets = existingAssets.data.filter(item => item.name == name);
if ( matchingAssets.length > 0 ){
asset_id = matchingAssets[0].id
console.log( ` !! !! Clobbering existing asset for ${name} (id: ${asset_id})`)
await github.repos.deleteReleaseAsset({ owner, repo, asset_id })
return(true)
}
return(false)
}
async function uploadAsset(owner, repo, release_id, file, assetContentType ){
console.log( `\n\n -- uploadAsset: owner: ${owner} repo: ${repo} release_id: ${release_id}, file: ${file}\n` )
const name = path.basename(file)
const data = fs.readFileSync(file)
const contentLength = fs.statSync(file).size
const headers = {
'content-type': assetContentType,
'content-length': contentLength
};
console.log( ` == Uploading asset ${name}: ${assetContentType}` )
const uploadAssetResponse = await github.repos.uploadReleaseAsset({
owner, repo, release_id, data, name, headers,
})
return( uploadAssetResponse );
}
console.log('== start');
const release_id = process.env.release_id
const [owner, repo] = process.env.TARGET_REPO.split('/')
const clobber = process.env.clobber == 'yes';
const rpm_files = process.env.rpm_file_paths.split(/[\r\n]+/);
const rpm_gpg_file = process.env.rpm_gpg_file;
let uploaded_files = rpm_files.concat(rpm_gpg_file).map(function(file){
const name = path.basename(file)
var content_type = 'application/pgp-keys'
if( name.match(/\.rpm$/) ){
content_type = 'application/octet-stream'
}
let conditionalClobber = new Promise((resolve,reject) => {
if ( clobber ){
resolve(clobberAsset( name, owner, repo, release_id ))
return
}
resolve( false )
})
conditionalClobber.then((clobbered)=> {
uploadAsset(owner, repo, release_id, file, content_type )
}).then(result => result )
})
console.log('== done')
45 changes: 29 additions & 16 deletions .github/workflows/tag_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,6 @@ jobs:
clean: true
fetch-depth: 0

- name: Build Release RPM
uses: simp/github-action-build-and-sign-pkg-single-rpm@v1
id: build-and-sign-rpm
with:
gpg_signing_key: ${{ secrets.SIMP_DEV_GPG_SIGNING_KEY }}
gpg_signing_key_id: ${{ secrets.SIMP_DEV_GPG_SIGNING_KEY_ID }}
gpg_signing_key_passphrase: ${{ secrets.SIMP_DEV_GPG_SIGNING_KEY_PASSPHRASE }}
simp_core_ref_for_building_rpms: ${{ secrets.SIMP_CORE_REF_FOR_BUILDING_RPMS }}

- name: Get tag & annotation info (${{github.ref}})
id: tag-check
run: |
Expand Down Expand Up @@ -115,15 +106,37 @@ jobs:
draft: false
prerelease: false

- name: Upload RPM file to Release
uses: actions/upload-release-asset@v1
build-and-attach-rpms:
name: Trigger RPM release
needs: [ create-github-release ]
if: github.repository_owner == 'simp'
runs-on: ubuntu-18.04
env:
TARGET_REPO: ${{ github.repository }}
steps:
- name: Get tag & annotation info (${{github.ref}})
id: tag-check
run: echo "::set-output name=tag::${GITHUB_REF/refs\/tags\//}"
- name: Trigger RPM release workflow
uses: actions/github-script@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
TARGET_TAG: ${{ steps.tag-check.outputs.tag }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.build-and-sign-rpm.outputs.rpm_file_path }}
asset_name: ${{ steps.build-and-sign-rpm.outputs.rpm_file_basename }}
asset_content_type: application/octet-stream
github-token: ${{ secrets.SIMP_AUTO_GITHUB_TOKEN__REPO_SCOPE }}
script: |
const [owner, repo] = process.env.TARGET_REPO.split('/')
await github.request('POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches', {
owner: owner,
repo: repo,
workflow_id: 'release_rpms.yml',
ref: process.env.DEFAULT_BRANCH,
inputs: {
release_tag: process.env.TARGET_TAG
}
}).then( (result) => {
console.log( `== Submitted workflow dispatch: status ${result.status}` )
})
deploy-to-puppet-forge:
name: Deploy PuppetForge Release
Expand Down

0 comments on commit a9e5c9e

Please sign in to comment.