Publish Beta Release #101
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Publish Beta Release | |
on: | |
push: | |
tags: | |
- "*_beta" | |
workflow_dispatch: | |
inputs: | |
version: | |
description: "Version to release" | |
required: true | |
default: "1.0.0" | |
remove_old_beta: | |
description: "Remove old beta releases" | |
required: false | |
type: boolean | |
default: true | |
jobs: | |
check-version-change: | |
outputs: | |
changed: ${{ steps.check-version.outputs.result }} | |
runs-on: ubuntu-latest | |
permissions: | |
contents: read | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Check if version has changed | |
id: check-version | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
// Get the version from the workflow input | |
let version = '${{ github.event.inputs.version }}'; | |
if (!version) { | |
fs = require('fs'); | |
let v = ''; | |
try { | |
v = fs.readFileSync('./VERSION', 'utf8'); | |
console.log(`Version found: ${v}`); | |
} | |
catch (err) { | |
return false; | |
} | |
if (!v) { | |
return false; | |
} else { | |
version = v; | |
} | |
} | |
let majorMinorVersion = version.split('.').slice(0, 2).join('.'); | |
version = `${majorMinorVersion}.${{github.run_id}}`; | |
console.log(`Version: ${version}`) | |
// Find a release for that version | |
const release = await github.rest.repos.getReleaseByTag({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
tag: `v${version}-beta`, | |
}).catch(() => null); | |
// If the release exists, the version has not changed | |
if (release) { | |
console.log(`Version ${version} has an existing release`); | |
console.log(release.data.html_url); | |
core.summary.addLink(`Release v${version}`, release.data.html_url); | |
await core.summary.write(); | |
return "false"; | |
} | |
console.log(`Version ${version} does not have a release`); | |
return true; | |
beta-release: | |
name: Release Beta version | |
needs: | |
- check-version-change | |
if: ${{ needs.check-version-change.outputs.changed == 'true' }} | |
runs-on: ubuntu-latest | |
permissions: | |
contents: write | |
packages: read | |
env: | |
EXT_VERSION: "" # will be set in the workflow | |
MAJOR_VERSION: "" # will be set in the workflow | |
UPLOAD_URL: "" # will be set in the workflow | |
outputs: | |
version: ${{ env.EXT_VERSION }} | |
majorVersion: ${{ env.MAJOR_VERSION }} | |
upload_url: ${{ env.UPLOAD_URL }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set new version | |
run: | | |
VERSION=${{ github.event.inputs.version }} | |
MAJOR_VERSION=${VERSION%.*} | |
NEW_VERSION=${VERSION%.*}.${{ github.run_id }} | |
echo "Beta Version: $NEW_VERSION" | |
echo "EXT_VERSION=${NEW_VERSION}" >> "$GITHUB_ENV" | |
echo "MAJOR_VERSION=${MAJOR_VERSION}" >> "$GITHUB_ENV" | |
- name: Generate release notes | |
env: | |
GH_TOKEN: ${{ secrets.PARALLELS_WORKFLOW_PAT }} | |
run: | | |
./.github/workflow_scripts/get-latest-beta-changelog.sh --repo ${{ github.repository }} --output-to-file --version "${EXT_VERSION}" | |
cat release_notes.md | |
- name: Create release and upload release asset | |
uses: actions/github-script@v7 | |
id: create_release | |
with: | |
script: | | |
const fs = require("fs"); | |
const release = await github.rest.repos.createRelease({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: fs.readFileSync("release_notes.md", "utf8"), | |
tag_name: "v${{ env.EXT_VERSION }}-beta", | |
name: "v${{ env.EXT_VERSION }}-beta", | |
draft: false, | |
prerelease: true | |
}); | |
core.exportVariable('UPLOAD_URL', release.data.upload_url); | |
beta-releases-matrix: | |
needs: | |
- check-version-change | |
- beta-release | |
name: Release Go Binary (Windows, Linux) | |
runs-on: ubuntu-latest | |
env: | |
EXT_VERSION: ${{ needs.beta-release.outputs.version }} | |
AmplitudeApiKey: ${{ secrets.AMPLITUDE_API_KEY }} | |
strategy: | |
fail-fast: false | |
matrix: | |
# build and publish in parallel: linux/386, linux/amd64, linux/arm64, windows/386, windows/amd64, darwin/amd64, darwin/arm64 | |
goos: [linux, windows] | |
goarch: ["386", amd64, arm64] | |
exclude: | |
- goarch: "386" | |
goos: darwin | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Setup Go 1.21.x | |
uses: actions/setup-go@v4 | |
with: | |
go-version: "1.21.x" | |
cache-dependency-path: ${{ github.workspace }}/src/go.sum | |
- name: Add Inbuilt Variables | |
run: | | |
sed -i "/@version/c\//\t@version\t\t$EXT_VERSION" ./src/main.go | |
go install github.com/swaggo/swag/cmd/swag@latest | |
cd src | |
go mod tidy | |
swag fmt | |
swag init -g main.go | |
cd .. | |
- uses: wangyoucao577/go-release-action@v1 | |
timeout-minutes: 10 | |
with: | |
github_token: ${{ secrets.GITHUB_TOKEN }} | |
goos: ${{ matrix.goos }} | |
goarch: ${{ matrix.goarch }} | |
goversion: "https://dl.google.com/go/go1.21.1.linux-amd64.tar.gz" | |
project_path: "./src" | |
binary_name: "prldevops" | |
release_name: "v${{ env.EXT_VERSION }}-beta" | |
ldflags: "-s -w -X main.ver=${{ env.EXT_VERSION }} -X 'github.com/Parallels/prl-devops-service/telemetry.AmplitudeApiKey=${{ env.AmplitudeApiKey }}'" | |
beta-releases-macos: | |
needs: | |
- check-version-change | |
- beta-release | |
runs-on: macos-latest | |
name: Release Go Binary (macOS) | |
env: | |
EXT_VERSION: ${{ needs.beta-release.outputs.version }} | |
AMPLITUDE_API_KEY: ${{ secrets.AMPLITUDE_API_KEY }} | |
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
APPLE_CERT_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }} | |
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
APPLE_API_KEY_ISSUER: ${{ secrets.APPLE_API_KEY_ISSUER }} | |
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} | |
APPLE_DEVELOPER_IDENTITY: ${{ secrets.APPLE_DEVELOPER_IDENTITY }} | |
strategy: | |
fail-fast: false | |
matrix: | |
# build and publish in parallel: darwin/amd64, darwin/arm64 | |
goos: [darwin] | |
goarch: [amd64, arm64] | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Setup Go 1.21.x | |
uses: actions/setup-go@v4 | |
with: | |
go-version: "1.21.x" | |
cache-dependency-path: ${{ github.workspace }}/src/go.sum | |
- name: Add Inbuilt Variables | |
run: | | |
brew install gnu-sed | |
gsed -i "/@version/c\//\t@version\t\t$EXT_VERSION" ./src/main.go | |
go install github.com/swaggo/swag/cmd/swag@latest | |
cd src | |
go mod tidy | |
swag fmt | |
swag init -g main.go | |
cd .. | |
- name: Build | |
run: | | |
cd src && go build -ldflags="-s -w -X main.ver=$EXT_VERSION -X 'github.com/Parallels/prl-devops-service/constants.AmplitudeApiKey=$AMPLITUDE_API_KEY'" -o prldevops | |
- name: Create and Unlock Temporary Keychain | |
run: | | |
security create-keychain -p "github" temp.keychain | |
security unlock-keychain -p "github" temp.keychain | |
security set-keychain-settings -lut 3600 temp.keychain | |
security list-keychains -s temp.keychain | |
- name: Import sign certificate | |
run: | | |
echo "${{ secrets.APPLE_CERTIFICATE }}" | base64 --decode > apple_developer_identity.p12 | |
security import apple_developer_identity.p12 -k temp.keychain -P ${{ secrets.APPLE_CERT_PASSWORD }} -T /usr/bin/codesign | |
rm apple_developer_identity.p12 | |
security set-key-partition-list -S apple-tool:,apple: -s -k "github" temp.keychain | |
security list-keychains | |
security find-identity -v -p codesigning temp.keychain | |
- name: Import notary credentials | |
run: | | |
echo "${{ secrets.APPLE_API_KEY }}" | base64 --decode > apple_api_key.p8 | |
xcrun notarytool store-credentials "notary-credentials" \ | |
--key apple_api_key.p8 \ | |
--key-id ${{ secrets.APPLE_API_KEY_ID }} \ | |
--issuer ${{ secrets.APPLE_API_KEY_ISSUER }} | |
- name: Sign binary | |
run: | | |
cd src | |
codesign --force --deep --strict --verbose --options=runtime,library --sign "${{ secrets.APPLE_DEVELOPER_IDENTITY }}" prldevops | |
ditto -c -k --sequesterRsrc prldevops prldevops.zip | |
xcrun notarytool submit prldevops.zip --keychain-profile "notary-credentials" --wait | |
- name: Verify signed binary | |
run: | | |
cd src | |
codesign --verify --verbose prldevops | |
spctl -t open --context context:primary-signature -a -vvv prldevops | |
- name: Compress asset to tar.gz | |
run: | | |
cd src | |
tar -czf prldevops--${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz prldevops | |
md5 prldevops--${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz | awk '{print $4}' > prldevops--${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz.md5 | |
- name: Upload release asset | |
uses: actions/[email protected] | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
with: | |
upload_url: ${{ needs.beta-release.outputs.upload_url }} | |
asset_path: src/prldevops--${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz | |
asset_name: prldevops--${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz | |
asset_content_type: application/octet-stream | |
- name: Upload release asset checksum | |
uses: actions/[email protected] | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
with: | |
upload_url: ${{ needs.beta-release.outputs.upload_url }} | |
asset_path: src/prldevops--${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz.md5 | |
asset_name: prldevops--${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz.md5 | |
asset_content_type: application/octet-stream | |
- name: Clean Up Keychain | |
if: always() | |
run: | | |
security delete-keychain temp.keychain | |
build-containers: | |
needs: | |
- check-version-change | |
- beta-release | |
env: | |
EXT_VERSION: ${{ needs.beta-release.outputs.version }} | |
AmplitudeApiKey: ${{ secrets.AMPLITUDE_API_KEY }} | |
name: Build Docker Images | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: docker/setup-buildx-action@v3 | |
- uses: docker/login-action@v3 | |
with: | |
username: ${{ secrets.DOCKER_USERNAME }} | |
password: ${{ secrets.DOCKER_PASSWORD }} | |
- uses: docker/build-push-action@v6 | |
with: | |
context: . | |
file: ./Dockerfile | |
platforms: linux/amd64,linux/arm64 | |
push: true | |
build-args: | | |
VERSION=${{ env.EXT_VERSION }} | |
secrets: | | |
amplitude_api_key=${{ secrets.AMPLITUDE_API_KEY }} | |
tags: | | |
${{ secrets.DOCKER_USERNAME }}/prl-devops-service:latest-beta | |
${{ secrets.DOCKER_USERNAME }}/prl-devops-service:${{ env.EXT_VERSION }}-beta | |
remove-old-beta-release: | |
if: ${{ github.event.inputs.remove_old_beta == true }} | |
name: Remove old beta release | |
needs: | |
- check-version-change | |
- beta-release | |
- beta-releases-matrix | |
- beta-releases-macos | |
- build-containers | |
runs-on: ubuntu-latest | |
permissions: | |
contents: write | |
packages: read | |
env: | |
EXT_VERSION: ${{ needs.beta-release.outputs.version }} | |
MAJOR_VERSION: ${{ needs.beta-release.outputs.version }} | |
steps: | |
- name: Remove old beta release | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const fs = require("fs"); | |
let version ='${{ github.event.inputs.version }}'.trim().split('.').slice(0, 2).join('.'); | |
let currentVersion = `${version}.${{github.run_id}}-beta`; | |
console.log(`Current Version: ${currentVersion}`); | |
const releases = await github.rest.repos.listReleases({ | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
for(const idx in releases.data) { | |
const release = releases.data[idx]; | |
if (release.tag_name.includes("-beta") && release.tag_name !== `v${currentVersion}`) { | |
for(const assetIdx in release.assets) { | |
const asset = release.assets[assetIdx]; | |
console.log(`Deleting asset: ${asset.name}`); | |
await github.rest.repos.deleteReleaseAsset({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
asset_id: asset.id | |
}); | |
} | |
console.log(`Deleting release: ${release.tag_name}`); | |
await github.rest.repos.deleteRelease({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
release_id: release.id | |
}); | |
console.log(`Deleting tag: tags/${release.tag_name}`); | |
await github.rest.git.deleteRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: `tags/${release.tag_name}` | |
}); | |
} | |
} |