Skip to content

Release HarperDB

Release HarperDB #6

Workflow file for this run

name: 'Release HarperDB'
on:
push:
tags:
- release_*
workflow_dispatch:
inputs:
forcedVersion:
type: string
description: 'Force a release with this version (x.y.z[-<alpha|beta|rc|custom>.w]). Please make sure you know what you are doing. This process will not tag latest. Defaults to `no_forced`'
default: 'no_forced'
required: false
dryrun:
type: boolean
description: "dryrun: Do not publish"
default: true
jobs:
########
# Meta
########
meta:
runs-on: ubuntu-latest
outputs:
harperdbVersion: ${{ steps.get-values.outputs.harperdbVersion }}
version: ${{ steps.get-values.outputs.version }}
full_version: ${{ steps.get-values.outputs.full_version }}
version_major: ${{ steps.get-values.outputs.version_major }}
version_minor: ${{ steps.get-values.outputs.version_minor }}
version_patch: ${{ steps.get-values.outputs.version_patch }}
release_type: ${{ steps.get-values.outputs.release_type }}
prerelease_version: ${{ steps.get-values.outputs.prerelease_version }}
npm_extra_tags: ${{ steps.get-values.outputs.npm_extra_tags }}
dockerhub_tag_latest: ${{ steps.get-values.outputs.dockerhub_tag_latest }}
dryrun: ${{ steps.get-values.outputs.dryrun }}
push: ${{ steps.get-values.outputs.push }}
steps:
- uses: actions/checkout@v4
- name: 'Get values'
id: get-values
env:
docker_repo_url: 'https://hub.docker.com/v2/namespaces/harperdb/repositories/harperdb/tags?page_size=100'
inputs_dryrun: ${{ inputs.dryrun }}
tag: ${{ github.ref }}
forcedVersion: ${{ inputs.forcedVersion }}
run: |
#!/usr/bin/env bash
# set default
dryrun='true'
push='false'
# make sure we know what the value of dryrun is
if [[ "${inputs_dryrun}" == "false" ]]; then
dryrun='false'
push='true'
fi
# get the version from package.json
harperdbVersion=$(jq -r '.version' package.json)
# check for forcedVersion. override tag and harperdbVersion to match
if [[ "${forcedVersion}" != "no_forced" && -n "${forcedVersion}" ]]; then
tag="refs/tags/release_${forcedVersion}"
harperdbVersion="${forcedVersion}"
cat <<EOF >> "${GITHUB_STEP_SUMMARY}"
> [!CAUTION]
> Forced Version
> version == "${harperdbVersion}"
EOF
dryrun="${inputs_dryrun:-false}"
[[ "${dryrun}" == 'false' ]] && push='true'
# this workflow needs a tag to function properly, in the case of being on a pushed tag, lets just check the tag
elif [[ "${tag}" =~ ^refs/tags/release_.*$ ]]; then
dryrun="${inputs_dryrun:-false}"
[[ "${dryrun}" == 'false' ]] && push='true'
else
cat <<EOF >> "${GITHUB_STEP_SUMMARY}"
> [!CAUTION]
> Exiting as I am unable to determine the version:
> tag == "${tag}"
> harperdbVersion == "${harperdbVersion}"
> dryrun == "${dryrun}"
> push == "${push}"
EOF
exit 1
fi
cat <<EOF >> "${GITHUB_STEP_SUMMARY}"
dryrun=${dryrun}
push=${push}
harperdbVersion=${harperdbVersion}
EOF
full_version=${tag#refs/*/release_}
release_type='stable'
prerelease_version=''
if [[ "${full_version}" == *"-"* ]]; then
prerelease_version_string="${full_version##*-}"
prerelease_version="${prerelease_version_string##*.}"
release_type="${prerelease_version_string%%.*}"
fi
version="${full_version%%-*}"
version="${version#[vV]}"
version_major="${version%%.*}"
version_minor="${version#*.}"
version_minor="${version_minor%.*}"
version_patch="${version##*.}"
npm_extra_tags=''
dockerhub_tag_latest=''
if [[ "${release_type}" == @(alpha|beta|rc|custom) ]]; then
npm_extra_tags='next'
[[ "${forcedVersion}" != "no_forced" ]] && npm_extra_tags=''
dockerhub_tag_latest=false
else
# before we set to latest, we need to make sure that this isn't just a new stable release on an older release train
repo_data_json=$(curl -sL "${docker_repo_url}")
current_latest_image_digest=$(echo "${repo_data_json}" | jq -r '.results[] | select(.name == "latest").digest')
# Extract the highest semantic version tag that shares the same digest as 'latest'
# This filters out commit hash tags and ensures we only compare actual version numbers
# To verify locally:
# docker_repo_url='https://hub.docker.com/v2/namespaces/harperdb/repositories/harperdb/tags?page_size=100'
# repo_data_json=$(curl -sL "${docker_repo_url}")
# current_latest_image_digest=$(echo "${repo_data_json}" | jq -r '.results[] | select(.name == "latest").digest')
# echo "${repo_data_json}" | jq -r ".results[] | select(.digest == \"${current_latest_image_digest}\" and .name != \"latest\") | .name" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1
current_latest_image_tag=$(echo "${repo_data_json}" | jq -r ".results[] | select(.digest == \"${current_latest_image_digest}\" and .name != \"latest\") | .name" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)
current_latest_image_tag_major="${current_latest_image_tag%%\.*}"
current_latest_image_tag_minor="${current_latest_image_tag#*.}"
current_latest_image_tag_minor="${current_latest_image_tag_minor%.*}"
current_latest_image_tag_patch="${current_latest_image_tag##*.}"
if [ "${version_major}" -gt "${current_latest_image_tag_major}" ]; then
npm_extra_tags='latest+stable'
dockerhub_tag_latest=true
elif [ "${version_major}" -eq "${current_latest_image_tag_major}" ] \
&& [ "${version_minor}" -gt "${current_latest_image_tag_minor}" ]; then
npm_extra_tags='latest+stable'
dockerhub_tag_latest=true
elif [ "${version_major}" -eq "${current_latest_image_tag_major}" ] \
&& [ "${version_minor}" -eq "${current_latest_image_tag_minor}" ] \
&& [ "${version_patch}" -ge "${current_latest_image_tag_patch}" ]; then
npm_extra_tags='latest+stable'
dockerhub_tag_latest=true
else
npm_extra_tags='none'
dockerhub_tag_latest=false
fi
fi
cat <<EOF >> "${GITHUB_OUTPUT}"
harperdbVersion=${harperdbVersion}
version=${version}
full_version=${full_version}
version_major=${version_major}
version_minor=${version_minor}
version_patch=${version_patch}
release_type=${release_type}
prerelease_version=${prerelease_version}
npm_extra_tags=${npm_extra_tags}
dockerhub_tag_latest=${dockerhub_tag_latest}
dryrun=${dryrun}
push=${push}
EOF
cat <<EOF >> "${GITHUB_STEP_SUMMARY}"
## Release ${version} ##
harperdbVersion: ${harperdbVersion}
version from tag: ${full_version}
> release type: ${release_type}
> npm tag: ${npm_extra_tags}
> dockerhub latest tag: ${dockerhub_tag_latest}
---
major: ${version_major}
minor: ${version_minor}
patch: ${version_patch}
EOF
[[ "${release_type}" != "stable" ]] && echo "pre-release version: ${release_type}.${prerelease_version:-}" >> "${GITHUB_STEP_SUMMARY}"
cat <<EOF
harperdbVersion: ${harperdbVersion}
version: ${version}
full_version: ${full_version}
version_major: ${version_major}
version_minor: ${version_minor}
version_patch: ${version_patch}
release_type: ${release_type}
prerelease_version: ${prerelease_version}
npm_extra_tags: ${npm_extra_tags}
dockerhub_tag_latest: ${dockerhub_tag_latest}
dryrun: ${dryrun}
push: ${push}
inputs_dryrun: ${inputs_dryrun}
EOF
if [[ "${dryrun}" != "false" ]]; then
cat <<EOF >> "${GITHUB_STEP_SUMMARY}"
> [!WARNING]
> dryrun == "${dryrun}"
EOF
fi
if [[ "${full_version}" != "${harperdbVersion}" ]]; then
cat <<EOF >> "${GITHUB_STEP_SUMMARY}"
> [!CAUTION]
> Terminating job as the version from the tag (${tag}, derived version ${full_version}) does not match harperdbVersion (${harperdbVersion})
EOF
exit 1
fi
############################################
# Send Slack Message for start of Release
############################################
sendSlackMessageRealeaseStarted:
if: ${{ needs.meta.outputs.dryrun == 'false' }}
uses: ./.github/workflows/post-to-slack-channel.yaml
needs:
- meta
secrets: inherit
with:
channel_id: C05PLHLQFQA
payload: |
{
"text": "${{ github.run_id }} Release ${{ needs.meta.outputs.full_version }} In Progress.",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "HarperDB Release ${{ needs.meta.outputs.full_version }}"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "*Status*"
},
{
"type": "mrkdwn",
"text": "In Progress"
},
{
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|run ${{ github.run_id }}>"
}
]
}
]
}
####################
# Build HarperDB
####################
build:
needs:
- meta
uses: ./.github/workflows/build.yaml
with:
artifactVersionOverride: ${{ needs.meta.outputs.harperdbVersion }}
# # These values should only be used to debug the workflow
# preferDownload: true
# dummyLicense: true
#############################################################
# Build offline installer to
# https://products-harperdb-io.s3.us-east-2.amazonaws.com/
#############################################################
build-offline-installer:
runs-on: ubuntu-latest
if: ${{ inputs.forcedVersion != 'no_forced' }}
needs:
- meta
- build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Build artifact
run: |
#!/bin/bash
./utility/devops/build/all-in-one-build.sh
- name: 'Upload Artifact'
uses: actions/upload-artifact@v4
with:
name: offline-harperdb-${{ needs.meta.outputs.harperdbVersion }}.tgz
path: harperdb-${{ needs.build.outputs.harperdbVersion }}.tgz
retention-days: 1
##############################################
# Release to Private NPM harperdb/@harperdb
##############################################
npmPrivateRelease:
continue-on-error: true
needs:
- meta
- build
runs-on: ubuntu-latest
timeout-minutes: 8
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: harperdb-${{ needs.meta.outputs.harperdbVersion }}.tgz
- uses: actions/setup-node@v4
with:
node-version: '${{needs.build.outputs.nodeVersion}}'
registry-url: 'https://registry.npmjs.org'
- name: 'npm Publish'
env:
NODE_AUTH_TOKEN: '${{ secrets.NPM_ACCESS_KEY }}'
HARPERDB_VERSION: '${{ needs.meta.outputs.harperdbVersion }}'
PUBLISH_DESTINATION: 'private'
EXTRA_TAGS: "${{ needs.meta.outputs.npm_extra_tags }}"
DRYRUN: "${{ needs.meta.outputs.dryrun }}"
run: ./.github/scripts/npm-publish.sh
#########################################
# Release to dockerhub harperdb/private
#########################################
dockerhubPrivateRelease:
needs:
- meta
- build
strategy:
matrix:
nodeVersion: [16, 20, 22, 24, 25]
dockerFile: ['utility/Docker/Dockerfile']
include:
- nodeVersion: 16
dockerFile: 'utility/Docker/Dockerfile-node16'
timeout-minutes: 30
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: checkout repo
uses: actions/checkout@v4
- name: docker-build
uses: ./.github/docker-build
with:
harperdbVersion: ${{ needs.meta.outputs.harperdbVersion }}
nodeVersion: ${{ matrix.nodeVersion }}
dockerFile: ${{ matrix.dockerFile }}
containerImage: 'harperdb/private'
tagLatest: ${{ needs.meta.outputs.dockerhub_tag_latest == 'true' && true || false }}
buildPlatforms: 'linux/amd64,linux/arm64'
push: ${{ needs.meta.outputs.push == 'true' && 'true' || 'false' }}
dockerUsername: ${{ secrets.DOCKER_USERNAME }}
dockerPassword: ${{ secrets.DOCKER_PASSWORD }}
############################################
# Send Slack Message on 'Ready to Deploy'
############################################
sendSlackMessageReadyToDeploy:
if: ${{ needs.meta.outputs.release_type == 'stable' && needs.meta.outputs.dryrun == 'false' }}
uses: ./.github/workflows/post-to-slack-channel.yaml
secrets: inherit
needs:
- meta
- npmPrivateRelease
- dockerhubPrivateRelease
- sendSlackMessageRealeaseStarted
with:
channel_id: C05PLHLQFQA
update_ts: ${{ needs.sendSlackMessageRealeaseStarted.outputs.ts }}
payload: |
{
"text": "${{ github.run_id }} Release ${{ needs.meta.outputs.full_version }} requires review.",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "HarperDB Release ${{ needs.meta.outputs.full_version }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Review(s) Required*"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Review Release"
},
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"style": "primary"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "*Status*"
},
{
"type": "mrkdwn",
"text": "In progress"
},
{
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|run ${{ github.run_id }}>"
}
]
}
]
}
##############################################################
#
# ██████ ██ ██ ██████ ██ ██ ██████
# ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██████ ██ ██ ██████ ██ ██ ██
# ██ ██ ██ ██ ██ ██ ██ ██
# ██ ██████ ██████ ███████ ██ ██████
#
#
# ██████ ███████ ██ ███████ █████ ███████ ███████
# ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██████ █████ ██ █████ ███████ ███████ █████
# ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██ ██ ███████ ███████ ███████ ██ ██ ███████ ███████
#
##############################################################
###################################
# Release to Public NPM harperdb
###################################
publicReleaseNPM:
environment: ${{ needs.meta.outputs.release_type == 'stable' && 'release-npm' || 'pre-release-npm' }}
continue-on-error: true
needs:
- build
- meta
- npmPrivateRelease
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
# setup for publishing to npm
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: harperdb-${{ needs.meta.outputs.harperdbVersion }}.tgz
# setup and publishing to npm
- uses: actions/setup-node@v4
with:
node-version: '${{needs.build.outputs.nodeVersion}}'
registry-url: 'https://registry.npmjs.org'
- name: 'npm Publish'
env:
NODE_AUTH_TOKEN: '${{ secrets.NPM_ACCESS_KEY }}'
HARPERDB_VERSION: '${{ needs.meta.outputs.harperdbVersion }}'
PUBLISH_DESTINATION: 'public'
EXTRA_TAGS: "${{ needs.meta.outputs.npm_extra_tags }}"
DRYRUN: ${{ needs.meta.outputs.dryrun }}
run: ./.github/scripts/npm-publish.sh
###################################
# Release to Public Dockerhub
# harpersystems/harper
# harpersystems/harper-openshift
# harpersystems/harper-chrome (amd64 only)
# Publish to Redhat Connect
###################################
publicReleaseDocker:
environment: ${{ needs.meta.outputs.release_type == 'stable' && 'release-docker' || 'pre-release-docker' }}
needs:
- build
- meta
- dockerhubPrivateRelease
runs-on: ubuntu-latest
continue-on-error: true
timeout-minutes: 30
strategy:
matrix:
container:
- 'harpersystems/harper'
- 'harpersystems/harper-openshift'
nodeVersion: [ 16, 20, 22, 24, 25 ]
include:
- container: 'harpersystems/harper'
dockerFile: 'utility/Docker/Dockerfile'
buildPlatforms: 'linux/amd64,linux/arm64'
- container: 'harpersystems/harper-openshift'
dockerFile: 'utility/Docker/Dockerfile-openshift'
buildPlatforms: 'linux/amd64,linux/arm64'
- container: 'harpersystems/harper-chrome'
dockerFile: 'utility/Docker/Dockerfile-chrome'
buildPlatforms: 'linux/amd64'
nodeVersion: 22
- nodeVersion: 16
dockerFile: 'utility/Docker/Dockerfile-node16'
steps:
- name: checkout repo
uses: actions/checkout@v4
- name: docker-build
uses: ./.github/docker-build
with:
harperdbVersion: ${{ needs.meta.outputs.harperdbVersion }}
nodeVersion: ${{ matrix.nodeVersion }}
dockerFile: ${{ matrix.dockerFile }}
containerImage: ${{ matrix.container }}
tagLatest: ${{ needs.meta.outputs.dockerhub_tag_latest == 'true' && true || false }}
buildPlatforms: 'linux/amd64,linux/arm64'
push: ${{ needs.meta.outputs.push == 'true' && 'true' || 'false' }}
pushRedhatConnect: ${{ matrix.nodeVersion == 22 && 'true' || 'false' }} # only checking node version here, docker-build action will check if harpersystems/harper-openshift
dockerUsername: ${{ secrets.DOCKER_USERNAME }}
dockerPassword: ${{ secrets.DOCKER_PASSWORD }}
publishToRedHatConnect:
needs:
- build
- publicReleaseDocker
uses: ./.github/workflows/publish-redhat-connect-images.yaml
with:
container_tag: ${{ needs.build.outputs.harperdbVersion }}
secrets:
REDHAT_CONNECT_DOCKERCONFIG_JSON: ${{ secrets.REDHAT_CONNECT_DOCKERCONFIG_JSON }}
REDHAT_PYXIS_KEY: ${{ secrets.REDHAT_PYXIS_KEY }}
#############################################################
# Release offline installer to
# https://products-harperdb-io.s3.us-east-2.amazonaws.com/
#############################################################
publicReleaseOfflineInstaller:
if: ${{ inputs.forcedVersion != 'no_forced' }}
environment: ${{ needs.meta.outputs.release_type == 'stable' && 'release-offline-installer' || 'pre-release-offline-installer' }}
runs-on: ubuntu-latest
needs:
- meta
- build-offline-installer
steps:
- name: 'Download Artifact'
uses: actions/download-artifact@v4
with:
name: offline-harperdb-${{ needs.meta.outputs.harperdbVersion }}.tgz
- name: Publish to s3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1
updateLatest: ${{ needs.meta.outputs.dockerhub_tag_latest }}
dryrun: ${{ needs.meta.outputs.dryrun }}
run: |
#!/bin/bash
set -e
# if dryrun, just echo out the commands
aws_cmd='echo aws'
[ "${dryrun}" == "false" ] && aws_cmd='aws'
${aws_cmd} s3 cp ./harperdb-${{ needs.meta.outputs.harperdbVersion }}.tgz s3://products-harperdb-io
if [[ ${updateLatest} == "true" ]]
then
${aws_cmd} s3 rm --recursive s3://products-harperdb-io/latest
${aws_cmd} s3 cp ./harperdb-${{ needs.meta.outputs.harperdbVersion }}.tgz s3://products-harperdb-io/latest/
fi
#################################
# Send Slack Message on success
#################################
sendSlackMessageParty:
uses: ./.github/workflows/post-to-slack-channel.yaml
if: ${{ !cancelled() && !failure() && needs.publicReleaseNPM.result == 'success' && needs.publicReleaseDocker.result == 'success' && needs.publicReleaseOfflineInstaller.result == 'success' && needs.meta.outputs.dryrun == 'false' }}
secrets: inherit
needs:
- meta
- publicReleaseNPM
- publicReleaseDocker
- publicReleaseOfflineInstaller
- sendSlackMessageRealeaseStarted
- sendSlackMessageReadyToDeploy
with:
channel_id: C05PLHLQFQA
update_ts: ${{ needs.meta.outputs.release_type == 'stable' && needs.sendSlackMessageReadyToDeploy.outputs.ts || needs.sendSlackMessageRealeaseStarted.outputs.ts }}
payload: |
{
"text": "${{ github.run_id }} Release ${{ needs.meta.outputs.full_version }} Complete.",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "HarperDB Release ${{ needs.meta.outputs.full_version }}"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "*Status*"
},
{
"type": "mrkdwn",
"text": ":white_check_mark:"
},
{
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|run ${{ github.run_id }}>"
},
{
"type": "mrkdwn",
"text": "Completed"
}
]
}
]
}
#################################
# Send Slack Message on failure
#################################
sendSlackMessageFailure:
if: ${{ failure() && !cancelled() && needs.meta.outputs.dryrun == 'false' }}
uses: ./.github/workflows/post-to-slack-channel.yaml
secrets: inherit
needs:
- meta
- build
- npmPrivateRelease
- dockerhubPrivateRelease
- publicReleaseNPM
- publicReleaseDocker
- publicReleaseOfflineInstaller
- sendSlackMessageRealeaseStarted
- sendSlackMessageReadyToDeploy
- sendSlackMessageParty
with:
channel_id: C05PLHLQFQA
update_ts: ${{ needs.sendSlackMessageRealeaseStarted.outputs.ts }}
payload: |
{
"text": "${{ github.run_id }} Release ${{ needs.meta.outputs.full_version }} Failed.",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "HarperDB Release ${{ needs.meta.outputs.full_version }} Failed"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "*Status*"
},
{
"type": "mrkdwn",
"text": "Failed"
},
{
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|run ${{ github.run_id }}>"
},
{
"type": "mrkdwn",
"text": ":x:"
}
]
}
]
}