Skip to content

release hmy

release hmy #2

Workflow file for this run

name: release hmy
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag value to create the release, for example v2026.1.0'
required: true
type: string
permissions: {}
env:
GOPATH: ${{ github.workspace }}
GOBIN: ${{ github.workspace }}/bin
defaults:
run:
shell: bash
jobs:
check:
name: Check release tag
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
outputs:
tag: ${{ steps.resolve-tag.outputs.tag }}
steps:
- name: Resolve release tag
id: resolve-tag
env:
INPUT_TAG: ${{ github.event.inputs.tag || '' }}
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
TAG="${INPUT_TAG}"
else
TAG="${GITHUB_REF_NAME}"
fi
if [[ ! "${TAG}" =~ ^v[0-9]{4}\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
echo "::error::invalid release tag: ${TAG}"
echo "::error::expected format: v2026.0.0"
exit 1
fi
git check-ref-format "refs/tags/${TAG}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- name: Checkout hmy code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0
with:
path: go-sdk
ref: refs/tags/${{ steps.resolve-tag.outputs.tag }}
fetch-depth: 1
persist-credentials: false
- name: Check tag is annotated
env:
RELEASE_TAG: ${{ steps.resolve-tag.outputs.tag }}
run: |
set -euo pipefail
git fetch --force --depth=1 origin "refs/tags/${RELEASE_TAG}:refs/tags/${RELEASE_TAG}"
if ! git rev-parse -q --verify "${RELEASE_TAG}^{tag}" >/dev/null; then
echo "::error::release tag '${RELEASE_TAG}' is not annotated"
exit 1
fi
working-directory: go-sdk
build:
name: Build hmy binary (${{ matrix.arch }})
needs: check
runs-on: ${{ matrix.os }}
timeout-minutes: 60
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
arch: amd64
- os: ubuntu-24.04-arm
arch: arm64
steps:
- name: Checkout hmy code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0
with:
path: go-sdk
ref: refs/tags/${{ needs.check.outputs.tag }}
fetch-depth: 1
persist-credentials: false
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 #v6.5.0
with:
go-version-file: go-sdk/go.mod
cache: false
- name: Checkout mcl
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0
with:
repository: harmony-one/mcl
path: src/github.com/harmony-one/mcl
ref: master
fetch-depth: 1
persist-credentials: false
- name: Checkout bls
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0
with:
repository: harmony-one/bls
path: src/github.com/harmony-one/bls
ref: master
fetch-depth: 1
persist-credentials: false
- name: Checkout harmony BLS build flags helper
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0
with:
repository: harmony-one/harmony
path: src/github.com/harmony-one/harmony
ref: main
fetch-depth: 1
persist-credentials: false
sparse-checkout: |
scripts/setup_bls_build_flags.sh
sparse-checkout-cone-mode: false
- name: Build hmy binary for Linux
id: build-hmy
run: |
set -euo pipefail
make static
ARCH="$(dpkg --print-architecture)"
BINARY_NAME="hmy-${ARCH}"
mkdir -p release
if [[ -f dist/hmy ]]; then
cp dist/hmy "release/${BINARY_NAME}"
elif [[ -f hmy ]]; then
cp hmy "release/${BINARY_NAME}"
elif [[ -f bin/hmy ]]; then
cp bin/hmy "release/${BINARY_NAME}"
else
echo "::error::could not find built hmy binary"
find . -maxdepth 3 -type f -name 'hmy*' -print
exit 1
fi
chmod +x "release/${BINARY_NAME}"
test -x "release/${BINARY_NAME}"
echo "binary_name=${BINARY_NAME}" >> "$GITHUB_OUTPUT"
working-directory: go-sdk
- name: Upload hmy binary artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
with:
name: ${{ steps.build-hmy.outputs.binary_name }}
path: go-sdk/release/${{ steps.build-hmy.outputs.binary_name }}
retention-days: 1
if-no-files-found: error
archive: false
release-page:
name: Sign binaries and create draft release
needs: [check, build]
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: write
steps:
- name: Checkout hmy code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0
with:
path: go-sdk
ref: refs/tags/${{ needs.check.outputs.tag }}
fetch-depth: 1
persist-credentials: false
- name: Get release metadata
env:
RELEASE_TAG: ${{ needs.check.outputs.tag }}
run: |
set -euo pipefail
git fetch --force --depth=1 origin "refs/tags/${RELEASE_TAG}:refs/tags/${RELEASE_TAG}"
VERSION="${RELEASE_TAG#v}"
COMMIT_SHA="$(git rev-parse --short=12 HEAD)"
VERSION_LONG="${RELEASE_TAG}-${COMMIT_SHA}"
echo "build_version=${VERSION}" >> "$GITHUB_ENV"
echo "build_version_long=${VERSION_LONG}" >> "$GITHUB_ENV"
working-directory: go-sdk
- name: Download hmy binary artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
with:
pattern: hmy-*
path: release-assets
merge-multiple: true
- name: Verify downloaded artifacts
run: |
set -euo pipefail
test -s release-assets/hmy-amd64
test -s release-assets/hmy-arm64
ls -lah release-assets
- name: Import GPG private key and export public key
id: gpg-key
env:
GPG_PRIVATE_KEY: ${{ secrets.HMY_GPG_PRIVATE_KEY }}
run: |
set -euo pipefail
if [[ -z "${GPG_PRIVATE_KEY}" ]]; then
echo "::error::HMY_GPG_PRIVATE_KEY secret is empty or unavailable"
exit 1
fi
install -m 700 -d ~/.gnupg
mkdir -p release-assets
printf '%s' "${GPG_PRIVATE_KEY}" | gpg --batch --import
KEY_FINGERPRINT="$(
gpg --batch --with-colons --list-secret-keys \
| awk -F: '/^fpr:/ { print $10; exit }'
)"
if [[ -z "${KEY_FINGERPRINT}" ]]; then
echo "::error::GPG private key was not imported"
exit 1
fi
gpg --batch --armor --export "${KEY_FINGERPRINT}" \
> release-assets/HMY_RELEASE_SIGNING_KEY.asc
test -s release-assets/HMY_RELEASE_SIGNING_KEY.asc
echo "fingerprint=${KEY_FINGERPRINT}" >> "$GITHUB_OUTPUT"
echo "[INFO] GPG public key exported"
echo "[INFO] GPG signing key fingerprint: ${KEY_FINGERPRINT}"
- name: Sign binaries and generate checksums
env:
GPG_PRIVATE_KEY_PASS: ${{ secrets.HMY_GPG_PRIVATE_KEY_PASS }}
GPG_KEY_FINGERPRINT: ${{ steps.gpg-key.outputs.fingerprint }}
run: |
set -euo pipefail
if [[ -z "${GPG_PRIVATE_KEY_PASS}" ]]; then
echo "::error::HMY_GPG_PRIVATE_KEY_PASS secret is empty or unavailable"
exit 1
fi
cd release-assets
for binary in hmy-amd64 hmy-arm64; do
gpg --batch --yes --pinentry-mode loopback \
--local-user "${GPG_KEY_FINGERPRINT}" \
--passphrase-fd 3 \
--output "${binary}.sig" \
--detach-sign "${binary}" 3<<<"${GPG_PRIVATE_KEY_PASS}"
sha256sum "${binary}" > "${binary}.sha256"
done
- name: Add legacy hmy asset name
run: |
set -euo pipefail
cp release-assets/hmy-amd64 release-assets/hmy
- name: Generate release notes from tag message
env:
RELEASE_TAG: ${{ needs.check.outputs.tag }}
GPG_KEY_FINGERPRINT: ${{ steps.gpg-key.outputs.fingerprint }}
run: |
set -euo pipefail
TAG_MESSAGE="$(
git for-each-ref "refs/tags/${RELEASE_TAG}" \
--format='%(contents:subject)%0a%0a%(contents:body)'
)"
if [[ -z "${TAG_MESSAGE}" ]]; then
echo "::error::could not read annotated tag message for ${RELEASE_TAG}"
exit 1
fi
{
echo "${TAG_MESSAGE}"
echo
echo "The released version: ${build_version_long}"
echo
echo "## Verify release binaries"
echo
echo "GPG signing key fingerprint:"
echo
echo '```text'
echo "${GPG_KEY_FINGERPRINT}"
echo '```'
echo
echo '```bash'
echo "gpg --import HMY_RELEASE_SIGNING_KEY.asc"
echo
echo "gpg --verify hmy-amd64.sig hmy-amd64"
echo "gpg --verify hmy-arm64.sig hmy-arm64"
echo
echo "sha256sum -c hmy-amd64.sha256"
echo "sha256sum -c hmy-arm64.sha256"
echo '```'
} > tag_message.md
working-directory: go-sdk
- name: Recreate draft release and upload assets
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.check.outputs.tag }}
run: |
set -euo pipefail
if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
echo "[INFO] Release ${RELEASE_TAG} already exists, deleting it"
gh release delete "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--yes
fi
gh release create "${RELEASE_TAG}" release-assets/* \
--repo "${GITHUB_REPOSITORY}" \
--draft \
--verify-tag \
--title "Mainnet Release ${build_version}" \
--notes-file ./go-sdk/tag_message.md
- name: Cleanup GPG key
if: always()
run: |
set -euo pipefail
gpgconf --kill all || true
rm -rf ~/.gnupg