Skip to content

ZXF: Deploy Production Release #6496

ZXF: Deploy Production Release

ZXF: Deploy Production Release #6496

# SPDX-License-Identifier: Apache-2.0
name: "ZXF: Deploy Production Release"
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+-?*"
workflow_dispatch:
inputs:
ref:
required: true
description: "Git Reference (branch, tag, or commit SHA)"
ref-name:
required: true
description: "Git Reference Name (branch name or tag name)"
dry-run-enabled:
required: false
default: false
type: boolean
description: "Specify that this is a test run which will skip certain steps."
defaults:
run:
shell: bash
permissions:
id-token: write
contents: write
actions: read
jobs:
prepare-tag-release:
name: Prepare Release [Tag]
runs-on: hl-cn-default-lin-sm
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
outputs:
version: ${{ steps.tag.outputs.version }}
prerelease: ${{ steps.tag.outputs.prerelease }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Install Semantic Version Tools
run: |
echo "::group::Download SemVer Binary"
sudo curl -L -o /usr/local/bin/semver https://raw.githubusercontent.com/fsaintjacques/semver-tool/master/src/semver
echo "::endgroup::"
echo "::group::Change SemVer Binary Permissions"
sudo chmod -v +x /usr/local/bin/semver
echo "::endgroup::"
echo "::group::Show SemVer Binary Version Info"
semver --version
echo "::endgroup::"
- name: Extract Tag Version
id: tag
run: |
RELEASE_VERSION="$(semver get release "${{ github.ref_name }}")"
PRERELEASE_VERSION="$(semver get prerel "${{ github.ref_name }}")"
FINAL_VERSION="${RELEASE_VERSION}"
PRERELEASE_FLAG="false"
[[ -n "${PRERELEASE_VERSION}" ]] && FINAL_VERSION="${RELEASE_VERSION}-${PRERELEASE_VERSION}"
[[ -n "${PRERELEASE_VERSION}" ]] && PRERELEASE_FLAG="true"
echo "version=${FINAL_VERSION}" >>"${GITHUB_OUTPUT}"
echo "prerelease=${PRERELEASE_FLAG}" >>"${GITHUB_OUTPUT}"
release-tag:
name: Release [Tag]
uses: ./.github/workflows/node-zxc-build-release-artifact.yaml
needs:
- prepare-tag-release
with:
version-policy: specified
new-version: ${{ needs.prepare-tag-release.outputs.version }}
trigger-env-deploy: none
release-profile: ${{ needs.prepare-tag-release.outputs.prerelease == 'true' && 'PrereleaseChannel' || 'MavenCentral' }}
ref: ${{ github.ref }}
ref-name: ${{ github.ref_name }}
secrets:
access-token: ${{ secrets.GITHUB_TOKEN }}
bucket-name: ${{ secrets.RELEASE_ARTIFACT_BUCKET_NAME }}
cdn-bucket-name: ${{ secrets.CDN_ARTIFACT_BUCKET_NAME }}
central-publishing-username: ${{ secrets.CENTRAL_PUBLISHING_USERNAME }}
central-publishing-password: ${{ secrets.CENTRAL_PUBLISHING_PASSWORD }}
svcs-gpg-key-contents: ${{ secrets.SVCS_GPG_KEY_CONTENTS }}
svcs-gpg-key-passphrase: ${{ secrets.SVCS_GPG_KEY_PASSPHRASE }}
sdk-ossrh-username: ${{ secrets.PLATFORM_OSSRH_USERNAME }}
sdk-ossrh-password: ${{ secrets.PLATFORM_OSSRH_PASSWORD }}
sdk-gpg-key-contents: ${{ secrets.PLATFORM_GPG_KEY_CONTENTS }}
sdk-gpg-key-passphrase: ${{ secrets.PLATFORM_GPG_KEY_PASSPHRASE }}
slack-webhook-url: ${{ secrets.SLACK_CITR_BUILD_PROMOTION_WEBHOOK }}
jf-url: ${{ vars.JF_URL }}
jf-docker-registry: ${{ vars.JF_DOCKER_REGISTRY }}
jf-user-name: ${{ vars.JF_USER_NAME }}
jf-access-token: ${{ secrets.JF_ACCESS_TOKEN }}
update-hedera-protobufs:
name: Update Hedera Protobufs
runs-on: hl-cn-default-lin-sm
needs:
- prepare-tag-release
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Checkout Hiero Consensus Node Code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ secrets.GH_ACCESS_TOKEN }}
ref: ${{ inputs.ref }}
fetch-depth: "0"
- name: Checkout Hedera Protobufs Code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ secrets.PROTOBUFS_GH_ACCESS_TOKEN }}
fetch-depth: "0"
repository: hashgraph/hedera-protobufs
path: hedera-protobufs
- name: Install rsync
run: sudo apt update && sudo apt -y install rsync
- name: Update the folders owned by Services
working-directory: hedera-protobufs
run: |
git push --delete origin v${{ needs.prepare-tag-release.outputs.version }} || true
git tag --delete v${{ needs.prepare-tag-release.outputs.version }} || true
rsync -a --delete ../hapi/hedera-protobuf-java-api/src/main/proto/services/ services/
- name: Import GPG key for commit signoff
id: gpg_import
uses: step-security/ghaction-import-gpg@c86c374c0659a6c2d1284bccf8af889e73ce8fe0 # v6.3.0
with:
gpg_private_key: ${{ secrets.PROTOBUFS_GPG_KEY_CONTENTS }}
passphrase: ${{ secrets.PROTOBUFS_GPG_KEY_PASSPHRASE }}
git_user_signingkey: true
git_commit_gpgsign: true
git_tag_gpgsign: true
- name: Add & Commit
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
with:
cwd: "hedera-protobufs"
author_name: swirlds-eng-automation
author_email: ${{ secrets.PROTOBUFS_GPG_USER_EMAIL }}
commit: --signoff
message: "ci: Copied recent protobuf changes from hedera-services"
new_branch: "update-recent-protobuf-changes-${{ github.run_number }}"
tag: 'v${{ needs.prepare-tag-release.outputs.version }} -s -m "Hedera Protobufs v${{ needs.prepare-tag-release.outputs.version }}" -u "${{ steps.gpg_import.outputs.keyid }}"'
release-branch:
name: Release [Branch]
uses: ./.github/workflows/node-zxc-build-release-artifact.yaml
if: ${{ github.event_name == 'workflow_dispatch' }}
with:
version-policy: branch-commit
trigger-env-deploy: integration
release-profile: DevelopCommit
ref: ${{ inputs.ref }}
ref-name: ${{ inputs.ref-name }}
dry-run-enabled: ${{ inputs.dry-run-enabled }}
secrets:
access-token: ${{ secrets.GITHUB_TOKEN }}
bucket-name: ${{ secrets.RELEASE_ARTIFACT_BUCKET_NAME }}
cdn-bucket-name: ${{ secrets.CDN_ARTIFACT_BUCKET_NAME }}
central-publishing-username: ${{ secrets.CENTRAL_PUBLISHING_USERNAME }}
central-publishing-password: ${{ secrets.CENTRAL_PUBLISHING_PASSWORD }}
svcs-gpg-key-contents: ${{ secrets.SVCS_GPG_KEY_CONTENTS }}
svcs-gpg-key-passphrase: ${{ secrets.SVCS_GPG_KEY_PASSPHRASE }}
sdk-ossrh-username: ${{ secrets.PLATFORM_OSSRH_USERNAME }}
sdk-ossrh-password: ${{ secrets.PLATFORM_OSSRH_PASSWORD }}
sdk-gpg-key-contents: ${{ secrets.PLATFORM_GPG_KEY_CONTENTS }}
sdk-gpg-key-passphrase: ${{ secrets.PLATFORM_GPG_KEY_PASSPHRASE }}
slack-webhook-url: ${{ secrets.SLACK_CITR_BUILD_PROMOTION_WEBHOOK }}
jf-url: ${{ vars.JF_URL }}
jf-docker-registry: ${{ vars.JF_DOCKER_REGISTRY }}
jf-user-name: ${{ vars.JF_USER_NAME }}
jf-access-token: ${{ secrets.JF_ACCESS_TOKEN }}
deploy-xts-ci-trigger:
name: Trigger XTS CI Flow
runs-on: hl-cn-default-lin-sm
needs:
- release-branch
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Checkout Code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: "0"
ref: ${{ inputs.ref }}
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Check Prep XTS Job State
id: check-xts-job
if: ${{ needs.release-branch.result == 'success' }}
env:
GH_TOKEN: ${{ github.token }}
run: |
JOB_ENABLED="true"
JOB_STATE=$(gh workflow list --all --json name,state | jq -r '.[]|select(.name=="ZXF: Prepare Extended Test Suite")|.state')
[[ "${JOB_STATE}" == "disabled_manually" ]] && JOB_ENABLED="false"
echo "enabled=${JOB_ENABLED}" >> "${GITHUB_OUTPUT}"
- name: Trigger ZXF Prepare Extended Test Suite
if: ${{ needs.release-branch.result == 'success' && steps.check-xts-job.outputs.enabled == 'true' }}
uses: step-security/workflow-dispatch@b4c1dc0afa074d0b4f0e653d3b80d4b2798599aa # v1.2.7
with:
workflow: .github/workflows/zxf-prepare-extended-test-suite.yaml
repo: hiero-ledger/hiero-consensus-node # ensure we are executing in the hiero-ledger org
ref: main # ensure we are always using the workflow definition from the main branch
token: ${{ secrets.GH_ACCESS_TOKEN }}
inputs: '{
"ref": "${{ inputs.ref }}",
"dry-run-enabled": "${{ inputs.dry-run-enabled }}"
}'
deploy-integration-ci-trigger:
name: Trigger Deploy Integration Flow
runs-on: hl-cn-default-lin-sm
needs:
- release-branch
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Checkout Code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: "0"
ref: ${{ inputs.ref }}
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Check Integration Job State
id: check-integration-job
if: ${{ needs.release-branch.result == 'success' && !cancelled() }}
env:
GH_TOKEN: ${{ github.token }}
run: |
JOB_ENABLED="true"
JOB_STATE=$(gh workflow list --all --json name,state --limit 200 | jq -r '.[]|select(.name=="ZXF: [Node] Deploy Integration Network Release")|.state')
[[ "${JOB_STATE}" == "disabled_manually" ]] && JOB_ENABLED="false"
echo "enabled=${JOB_ENABLED}" >> "${GITHUB_OUTPUT}"
- name: Trigger ZXF Deploy Integration
if: >-
${{
inputs.dry-run-enabled == false &&
needs.release-branch.result == 'success' &&
steps.check-integration-job.outputs.enabled == 'true' &&
!cancelled()
}}
uses: step-security/workflow-dispatch@b4c1dc0afa074d0b4f0e653d3b80d4b2798599aa # v1.2.7
with:
workflow: .github/workflows/node-zxf-deploy-integration.yaml
repo: hiero-ledger/hiero-consensus-node # ensure we are executing in the hiero-ledger org
ref: main # ensure we are always using the workflow definition from the main branch
token: ${{ secrets.GH_ACCESS_TOKEN }}
report-failures:
name: Report Production Release Failures
runs-on: hl-cn-default-lin-sm
needs:
- release-branch
- deploy-xts-ci-trigger
if:
${{ github.event_name == 'workflow_dispatch' && inputs.dry-run-enabled == false &&
(needs.release-branch.result != 'success' || needs.deploy-xts-ci-trigger.result != 'success') &&
!cancelled() }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Checkout Code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: "0"
ref: ${{ inputs.ref }}
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Collect run logs in a log file
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
run: |
for job_id in $(gh run view ${{ github.run_id }} --json jobs --jq '.jobs | map(.databaseId) | .[0:-1] | .[]'); do
echo "Fetching logs for job $job_id..."
current_job_name=$(gh run view ${{ github.run_id }} --json jobs | jq --argjson job_id "$job_id" -r '.jobs[] | select(.databaseId == $job_id) | .name')
echo "Logs for job $current_job_name :" >> run.log
gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/hiero-ledger/hiero-consensus-node/actions/jobs/$job_id/logs >> run.log
done
- name: Upload log as artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
continue-on-error: true
with:
path: run.log
- name: Get Commit Information
id: fetch-commit-info
continue-on-error: true
run: |
echo "commit-hash=${{ inputs.ref }}" >> "${GITHUB_OUTPUT}"
echo "commit-author=$(git show -s --format='%an <%ae>' ${{ inputs.ref }})" >> "${GITHUB_OUTPUT}"
echo "commit-email=$(git show -s --format='%ae' ${{ inputs.ref }})" >> "${GITHUB_OUTPUT}"
- name: Find Commit Author in Slack
id: find-commit-author-slack
continue-on-error: true
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_CITR_BOT_TOKEN }}
EMAIL: ${{ steps.fetch-commit-info.outputs.commit-email }}
run: |
SLACK_USER_ID=$(curl -s -X GET "https://slack.com/api/users.list" \
-H "Authorization: Bearer ${SLACK_BOT_TOKEN}" | jq -r --arg email "${EMAIL}" \
'.members[] | select((.profile.email // "" | ascii_downcase) == ($email | ascii_downcase)) | .name')
if [[ -z "${SLACK_USER_ID}" || "${SLACK_USER_ID}" == "null" ]]; then
echo "No Slack user found for email: ${EMAIL}"
SLACK_USER_ID="No matching slack user found"
else
echo "Found slack user for email: ${EMAIL}"
SLACK_USER_ID="<@${SLACK_USER_ID}>"
fi
echo "slack-user-id=${SLACK_USER_ID}" >> "${GITHUB_OUTPUT}"
- name: Build Rootly Summary
id: rootly-summary
run: |
title="Deploy Production Release job failed (${{ inputs.ref-name }} - ${{ inputs.ref }})."
echo "title=${title}" >> "${GITHUB_OUTPUT}"
{
echo 'summary<<EOF'
echo "------------------------------------"
echo "Status of each jobs:"
echo "- Release [Branch]: ${{ needs.release-branch.result }}"
echo "- Deploy XTS CI Trigger: ${{ needs.deploy-xts-ci-trigger.result }}"
echo "------------------------------------"
echo "Commit information:"
echo "- Author: ${{ steps.fetch-commit-info.outputs.commit-author }}"
echo "- Commit: <${{ github.server_url }}/${{ github.repository }}/commit/${{ steps.fetch-commit-info.outputs.commit-hash }}>"
echo "- Workflow: <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>"
echo EOF
} >> "${GITHUB_OUTPUT}"
- name: Log Rootly Summary
run: |
echo "## Rootly Summary:"
echo "### Title: ${{ steps.rootly-summary.outputs.title }}" >> "${GITHUB_STEP_SUMMARY}"
echo "${{ steps.rootly-summary.outputs.summary }}" >> "${GITHUB_STEP_SUMMARY}"
echo "### Rootly Service" >> "${GITHUB_STEP_SUMMARY}"
echo "${{ steps.set-rootly-service.outputs.service }}" >> "${GITHUB_STEP_SUMMARY}"
- name: Create Rootly Alert
id: create-rootly-alert
uses: pandaswhocode/rootly-alert-action@fdae1529e5aed62040016accf719a0ceb7dae57f # v1.0.0
continue-on-error: true
with:
api_key: ${{ secrets.ROOTLY_API_TOKEN }}
summary: "${{ steps.rootly-summary.outputs.title }}"
details: "${{ steps.rootly-summary.outputs.summary }}"
notification_target_type: "Service"
notification_target: "CI/CD Workflows"
set_as_noise: "false"
alert_urgency: "High"
external_id: ${{ github.run_id }}
external_url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
environments: "CITR"
- name: Build Slack Payload Message
id: payload
run: |
cat <<EOF > slack_payload.json
{
"attachments": [
{
"color": "#FF0000",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": ":exclamation: Hiero Consensus Node - Deploy Production Release Failure Report",
"emoji": true
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Deploy Production Release job resulted in failure on `main`. See status below.*"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Release [Branch]*: ${{ needs.release-branch.result }}"
},
{
"type": "mrkdwn",
"text": "*Deploy XTS CI Trigger*: ${{ needs.deploy-xts-ci-trigger.result }}"
}
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Workflow and Commit Information*"
},
"fields": [
{
"type": "mrkdwn",
"text": "*Source Commit*:"
},
{
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/commit/${{ steps.fetch-commit-info.outputs.commit-hash }}>"
},
{
"type": "mrkdwn",
"text": "*Commit author*:"
},
{
"type": "mrkdwn",
"text": "${{ steps.fetch-commit-info.outputs.commit-author }}"
},
{
"type": "mrkdwn",
"text": "*Slack user*:"
},
{
"type": "mrkdwn",
"text": "${{ steps.find-commit-author-slack.outputs.slack-user-id }}"
},
{
"type": "mrkdwn",
"text": "*Workflow run ID*:"
},
{
"type": "mrkdwn",
"text": " ${{ github.run_id }}"
},
{
"type": "mrkdwn",
"text": "*Workflow run URL*:"
},
{
"type": "mrkdwn",
"text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>"
}
]
}
]
}
]
}
EOF
- name: Report failure (slack citr-failures)
uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0
with:
webhook: ${{ secrets.SLACK_CITR_FAILURES_WEBHOOK }}
webhook-type: incoming-webhook
payload-templated: true
payload-file-path: slack_payload.json