44 push :
55 branches :
66 - ' release/v11**'
7- - ' v11'
87 release :
9- types : [ released ]
8+ types : [published ]
109
1110jobs :
1211 setup_deployment :
@@ -61,109 +60,34 @@ jobs:
6160 echo "event_processor_tag=${{ vars.TW_EVENT_PROCESSOR_VERSION_DEV }}" >> $GITHUB_OUTPUT
6261
6362 # =====================================================================
64- # RC — push to v11
63+ # PRODUCTION — release.published (prerelease or not)
6564 #
66- # Tag derivation walks two CMs to handle both the normal flow and
67- # the hotfix flow without divergent code paths:
68- #
69- # 1. CM DEV gives us the candidate BASE (strip `-dev.N`).
70- # 2. CM PROD gives us the latest version already in production.
71- # 3. If BASE > PROD → use BASE. Normal flow.
72- # If BASE <= PROD → BASE is already released (hotfix scenario)
73- # so bump the patch of PROD instead. Avoids overwriting an
74- # already-shipped tag and matches the roll-forward policy.
75- # =====================================================================
76- elif ${{ github.event_name == 'push' && github.ref == 'refs/heads/v11' }}; then
77- ENVIRONMENT="rc"
78- CM_URL="https://cm.utmstack.com"
79- CM_DEV_URL="https://cm.dev.utmstack.com"
80- echo "Environment: $ENVIRONMENT"
81- echo "CM URL (target): $CM_URL"
82- echo "CM URL (source for base): $CM_DEV_URL"
83-
84- DEV_RESPONSE=$(curl -s "${CM_DEV_URL}/api/v1/versions/latest")
85- LATEST_DEV_VERSION=$(echo "$DEV_RESPONSE" | jq -r '.version // empty')
86- echo "Latest dev version from CM DEV: $LATEST_DEV_VERSION"
87-
88- if [ -z "$LATEST_DEV_VERSION" ]; then
89- echo "❌ No dev version found in CM DEV — cannot derive RC base. Push a release/v11.x.x branch first."
90- exit 1
91- fi
92-
93- BASE=$(echo "$LATEST_DEV_VERSION" | sed -E 's/-dev\.[0-9]+$//')
94- echo "BASE derived from CM DEV: $BASE"
95-
96- if [[ ! "$BASE" =~ ^v11\. ]]; then
97- echo "❌ Derived BASE '$BASE' is not a v11 release."
98- exit 1
99- fi
100-
101- # Double-check against production to detect the hotfix scenario.
102- PROD_RESPONSE=$(curl -s "${CM_URL}/api/v1/versions/latest")
103- PROD_LATEST=$(echo "$PROD_RESPONSE" | jq -r '.version // empty')
104- echo "Latest production version from CM PROD: ${PROD_LATEST:-<none>}"
105-
106- if [ -n "$PROD_LATEST" ]; then
107- # sort -V puts the higher semver last.
108- HIGHER=$(printf '%s\n%s\n' "$BASE" "$PROD_LATEST" | sort -V | tail -1)
109- if [ "$HIGHER" = "$BASE" ] && [ "$BASE" != "$PROD_LATEST" ]; then
110- # BASE is strictly newer than PROD — use it as-is.
111- TAG="$BASE"
112- echo "BASE ($BASE) > PROD ($PROD_LATEST) — using BASE as RC tag."
113- else
114- # PROD is >= BASE → BASE was already released (hotfix case).
115- # Bump the patch of PROD.
116- MAJOR_MINOR=$(echo "$PROD_LATEST" | sed -E 's/^(v[0-9]+\.[0-9]+)\.[0-9]+.*$/\1/')
117- PATCH=$(echo "$PROD_LATEST" | sed -E 's/^v[0-9]+\.[0-9]+\.([0-9]+).*$/\1/')
118- NEW_PATCH=$((PATCH + 1))
119- TAG="${MAJOR_MINOR}.${NEW_PATCH}"
120- echo "BASE ($BASE) <= PROD ($PROD_LATEST) — hotfix scenario. Bumping patch: $TAG"
121- fi
122- else
123- # CM PROD has no versions yet — use BASE.
124- TAG="$BASE"
125- echo "CM PROD is empty — using BASE as RC tag."
126- fi
127-
128- echo "RC tag: $TAG"
129-
130- echo "tag=$TAG" >> $GITHUB_OUTPUT
131- echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT
132- echo "cm_url=$CM_URL" >> $GITHUB_OUTPUT
133- echo "event_processor_tag=${{ vars.TW_EVENT_PROCESSOR_VERSION_PROD }}" >> $GITHUB_OUTPUT
134-
135- # =====================================================================
136- # PRODUCTION — release.released (non-prerelease publish)
137- #
138- # Production does NOT rebuild anything. The images, installer, and
139- # changelog are all artifacts of the RC run. The production trigger
140- # only needs to tell CM "this version is now available to community
141- # instances" via a promote endpoint (TODO: define and wire up).
65+ # The tag is taken directly from the release event — no CM queries.
66+ # Always runs a full build and deploys to our production instances.
14267 # =====================================================================
143- elif ${{ github.event_name == 'release' && github.event.action == 'released' }}; then
144- ENVIRONMENT="production"
68+ elif ${{ github.event_name == 'release' }}; then
14569 CM_URL="https://cm.utmstack.com"
146- echo "Environment: $ENVIRONMENT"
147- echo "CM URL: $CM_URL"
148-
14970 TAG="${{ github.event.release.tag_name }}"
150- echo "Tag from release: $TAG"
71+ echo "Event: release, tag= $TAG"
15172
15273 if [[ ! "$TAG" =~ ^v11\. ]]; then
15374 echo "⏭️ Skipping: tag '$TAG' is not a v11 release."
15475 exit 0
15576 fi
15677
78+ echo "Environment: production (our instances)"
79+ echo "CM URL: $CM_URL"
80+
15781 echo "tag=$TAG" >> $GITHUB_OUTPUT
158- echo "environment=$ENVIRONMENT " >> $GITHUB_OUTPUT
82+ echo "environment=production " >> $GITHUB_OUTPUT
15983 echo "cm_url=$CM_URL" >> $GITHUB_OUTPUT
16084 echo "event_processor_tag=${{ vars.TW_EVENT_PROCESSOR_VERSION_PROD }}" >> $GITHUB_OUTPUT
16185 fi
16286
16387 build_agent :
16488 name : Build Agent Binaries
16589 needs : [setup_deployment]
166- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
90+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
16791 runs-on : ubuntu-24.04
16892 steps :
16993 - name : Check out code into the right branch
@@ -263,7 +187,7 @@ jobs:
263187 sign_agent_windows :
264188 name : Sign Windows Agent Binaries
265189 needs : [build_agent, setup_deployment]
266- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
190+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
267191 uses : ./.github/workflows/reusable-sign-agent.yml
268192 with :
269193 os : windows
@@ -283,7 +207,7 @@ jobs:
283207 sign_agent_macos :
284208 name : Sign macOS Agent Binaries
285209 needs : [build_agent, setup_deployment]
286- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
210+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
287211 uses : ./.github/workflows/reusable-sign-agent.yml
288212 with :
289213 os : macos
@@ -297,7 +221,7 @@ jobs:
297221 build_utmstack_collector :
298222 name : Build UTMStack Collector
299223 needs : [setup_deployment]
300- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
224+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
301225 runs-on : ubuntu-24.04
302226 steps :
303227 - name : Check out code into the right branch
@@ -331,7 +255,7 @@ jobs:
331255 build_agent_manager :
332256 name : Build Agent Manager Microservice
333257 needs : [sign_agent_windows, sign_agent_macos, build_utmstack_collector, setup_deployment]
334- if : ${{ always() && needs.sign_agent_windows.result == 'success' && needs.sign_agent_macos.result == 'success' && needs.build_utmstack_collector.result == 'success' && needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
258+ if : ${{ always() && needs.sign_agent_windows.result == 'success' && needs.sign_agent_macos.result == 'success' && needs.build_utmstack_collector.result == 'success' && needs.setup_deployment.outputs.tag != '' }}
335259 runs-on : ubuntu-24.04
336260 steps :
337261 - name : Check out code into the right branch
@@ -425,7 +349,7 @@ jobs:
425349 build_event_processor :
426350 name : Build Event Processor Microservice
427351 needs : [setup_deployment]
428- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
352+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
429353 runs-on : ubuntu-24.04
430354 steps :
431355 - name : Check out code into the right branch
@@ -483,7 +407,7 @@ jobs:
483407 build_backend :
484408 name : Build Backend Microservice
485409 needs : [setup_deployment]
486- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
410+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
487411 uses : ./.github/workflows/reusable-java.yml
488412 with :
489413 image_name : backend
@@ -496,7 +420,7 @@ jobs:
496420 build_frontend :
497421 name : Build Frontend Microservice
498422 needs : [setup_deployment]
499- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
423+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
500424 uses : ./.github/workflows/reusable-node.yml
501425 with :
502426 image_name : frontend
@@ -505,7 +429,7 @@ jobs:
505429 build_user_auditor :
506430 name : Build User-Auditor Microservice
507431 needs : [setup_deployment]
508- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
432+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
509433 uses : ./.github/workflows/reusable-java.yml
510434 with :
511435 image_name : user-auditor
@@ -517,7 +441,7 @@ jobs:
517441 build_web_pdf :
518442 name : Build Web-PDF Microservice
519443 needs : [setup_deployment]
520- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
444+ if : ${{ needs.setup_deployment.outputs.tag != '' }}
521445 uses : ./.github/workflows/reusable-java.yml
522446 with :
523447 image_name : web-pdf
@@ -541,33 +465,28 @@ jobs:
541465 steps :
542466 - run : echo "✅ All builds completed successfully."
543467
544- # AI changelog runs only on RC. Production reuses the same release notes
545- # because the GitHub Release was already created during RC.
546468 generate_changelog :
547469 name : Generate Changelog
548470 needs : [all_builds_complete, setup_deployment]
549- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment == 'rc ' }}
471+ if : ${{ needs.setup_deployment.outputs.tag != '' && github.event_name == 'release ' }}
550472 uses : ./.github/workflows/generate-changelog.yml
551473 with :
552474 current_tag : ${{ needs.setup_deployment.outputs.tag }}
553475 secrets :
554476 THREATWINDS_API_KEY : ${{ secrets.THREATWINDS_API_KEY }}
555477 THREATWINDS_API_SECRET : ${{ secrets.THREATWINDS_API_SECRET }}
556478
557- # Installer build runs only on RC. The resulting binary is uploaded to the
558- # GitHub Release as a prerelease asset. When the release is later promoted
559- # to non-prerelease (which triggers production), the installer is already
560- # there — no rebuild needed.
479+ # Installer build runs on any GitHub release event (prerelease or not).
480+ # The binary is uploaded to the release that triggered this pipeline.
561481 build_installer_release :
562482 name : Build & Upload Installer
563483 needs : [generate_changelog, setup_deployment]
564- if : ${{ always() && needs.setup_deployment.result == 'success' && needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment == 'rc ' && needs.generate_changelog.result != 'cancelled' }}
484+ if : ${{ always() && needs.setup_deployment.result == 'success' && needs.setup_deployment.outputs.tag != '' && github.event_name == 'release ' && needs.generate_changelog.result != 'cancelled' }}
565485 uses : ./.github/workflows/installer-release.yml
566486 with :
567487 version : ${{ needs.setup_deployment.outputs.tag }}
568488 version_major : v11
569- environment : rc
570- prerelease : true
489+ environment : ${{ needs.setup_deployment.outputs.environment }}
571490 changelog : ${{ needs.generate_changelog.outputs.changelog }}
572491 secrets :
573492 API_SECRET : ${{ secrets.API_SECRET }}
@@ -588,12 +507,10 @@ jobs:
588507 CM_ENCRYPT_SALT : ${{ secrets.CM_ENCRYPT_SALT }}
589508 CM_SIGN_PUBLIC_KEY : ${{ secrets.CM_SIGN_PUBLIC_KEY }}
590509
591- # Publish a new version to CM. Runs for dev and rc — production does NOT
592- # re-publish because the rc run already registered v11.x.x in CM PROD.
593510 publish_new_version :
594511 name : Publish New Version to Customer Manager
595512 needs : [all_builds_complete, generate_changelog, setup_deployment]
596- if : ${{ always() && needs.all_builds_complete.result == 'success' && needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
513+ if : ${{ always() && needs.all_builds_complete.result == 'success' && needs.setup_deployment.outputs.tag != '' }}
597514 runs-on : ubuntu-24.04
598515 steps :
599516 - name : Check out code
@@ -606,7 +523,7 @@ jobs:
606523 TAG : ${{ needs.setup_deployment.outputs.tag }}
607524 CM_URL : ${{ needs.setup_deployment.outputs.cm_url }}
608525 run : |
609- # Use AI changelog for rc / production, generic for dev.
526+ # Use AI changelog for production releases , generic for dev.
610527 if [ "$ENVIRONMENT" != "dev" ] && [ -n "$CHANGELOG_CONTENT" ]; then
611528 changelog="$CHANGELOG_CONTENT"
612529 else
@@ -641,13 +558,10 @@ jobs:
641558
642559 echo "Response: $response"
643560
644- # Schedule the freshly-published version. For dev, this targets the dev
645- # instance list; for rc, the RC instance list. Production does NOT use this
646- # job — promotion to community lives in `promote_to_community` below.
647561 schedule :
648562 name : Schedule release to our instances
649563 needs : [publish_new_version, setup_deployment]
650- if : ${{ always() && needs.publish_new_version.result == 'success' && needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment != 'production' }}
564+ if : ${{ always() && needs.publish_new_version.result == 'success' && needs.setup_deployment.outputs.tag != '' }}
651565 runs-on : ubuntu-24.04
652566 env :
653567 ENVIRONMENT : ${{ needs.setup_deployment.outputs.environment }}
@@ -665,7 +579,6 @@ jobs:
665579 instance_ids="${{ vars.SCHEDULE_INSTANCES_DEV }}"
666580 auth_json='${{ secrets.CM_SERVICE_ACCOUNT_DEV }}'
667581 else
668- # rc uses prod variables
669582 instance_ids="${{ vars.SCHEDULE_INSTANCES_PROD }}"
670583 auth_json='${{ secrets.CM_SERVICE_ACCOUNT_PROD }}'
671584 fi
@@ -703,45 +616,5 @@ jobs:
703616
704617 echo "✅ Scheduled release for all instances with version $TAG"
705618
706- promote_to_community :
707- name : Promote to Community
708- needs : [setup_deployment]
709- if : ${{ needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment == 'production' }}
710- runs-on : ubuntu-24.04
711- env :
712- TAG : ${{ needs.setup_deployment.outputs.tag }}
713- CM_URL : ${{ needs.setup_deployment.outputs.cm_url }}
714- steps :
715- - name : Schedule update for all community instances
716- run : |
717- echo "🚀 Promoting $TAG to community"
718- echo " CM URL: $CM_URL"
719-
720- cmAuth=$(echo '${{ secrets.CM_SERVICE_ACCOUNT_PROD }}' | jq -r '.')
721- auth_id=$(echo "$cmAuth" | jq -r '.id')
722- auth_key=$(echo "$cmAuth" | jq -r '.key')
723-
724- body=$(jq -n \
725- --arg version "$TAG" \
726- '{version: $version, edition: "community"}')
727-
728- response=$(curl -sS -w "\n%{http_code}" -X POST "${CM_URL}/api/v1/updates" \
729- -H "Content-Type: application/json" \
730- -H "id: $auth_id" \
731- -H "key: $auth_key" \
732- -d "$body")
733-
734- http_code=$(echo "$response" | tail -n1)
735- payload=$(echo "$response" | sed '$d')
736-
737- echo "HTTP $http_code"
738- echo "Response: $payload"
739-
740- if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
741- echo "✅ Community broadcast scheduled for $TAG"
742- else
743- echo "❌ Community broadcast failed (HTTP $http_code)"
744- exit 1
745- fi
746619
747620
0 commit comments