fix(installer,ci): fix OpenSearch memory starvation, dev CM misroutin… #627
Workflow file for this run
This file contains hidden or 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: "v11 - Build & Deploy Pipeline" | |
| on: | |
| push: | |
| branches: | |
| - 'release/v11**' | |
| release: | |
| types: [published] | |
| jobs: | |
| setup_deployment: | |
| name: Setup Deployment | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| tag: ${{ steps.set-env.outputs.tag }} | |
| environment: ${{ steps.set-env.outputs.environment }} | |
| cm_url: ${{ steps.set-env.outputs.cm_url }} | |
| event_processor_tag: ${{ steps.set-env.outputs.event_processor_tag }} | |
| gh_environment: ${{ steps.set-env.outputs.gh_environment }} | |
| steps: | |
| - name: Determine Build Environment | |
| id: set-env | |
| run: | | |
| # ===================================================================== | |
| # DEV — push to release/v11** | |
| # Version = <branch-base>-dev.N, auto-incremented via CM. | |
| # ===================================================================== | |
| if ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/heads/release/v11') }}; then | |
| ENVIRONMENT="dev" | |
| CM_URL="https://cm.dev.utmstack.com" | |
| echo "Environment: $ENVIRONMENT" | |
| echo "CM URL: $CM_URL" | |
| # Extract version from branch name (e.g. release/v11.2.1 → v11.2.1) | |
| BRANCH_VERSION=$(echo "${{ github.ref }}" | sed 's|refs/heads/release/||') | |
| echo "Branch version: $BRANCH_VERSION" | |
| RESPONSE=$(curl -s "${CM_URL}/api/v1/versions/latest?product=utmstack-v11-dev") | |
| LATEST_VERSION=$(echo "$RESPONSE" | jq -r '.version // empty') | |
| echo "Latest version from CM: $LATEST_VERSION" | |
| if [ -n "$LATEST_VERSION" ]; then | |
| LATEST_BASE=$(echo "$LATEST_VERSION" | sed 's/-dev\.[0-9]*$//') | |
| if [ "$BRANCH_VERSION" = "$LATEST_BASE" ]; then | |
| DEV_NUM=$(echo "$LATEST_VERSION" | grep -oP '(?<=-dev\.)\d+') | |
| NEW_DEV_NUM=$((DEV_NUM + 1)) | |
| TAG="${BRANCH_VERSION}-dev.${NEW_DEV_NUM}" | |
| echo "Versions match, incrementing: $TAG" | |
| else | |
| TAG="${BRANCH_VERSION}-dev.1" | |
| echo "Versions don't match, starting fresh: $TAG" | |
| fi | |
| else | |
| TAG="${BRANCH_VERSION}-dev.1" | |
| echo "No previous version found, starting fresh: $TAG" | |
| fi | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT | |
| echo "cm_url=$CM_URL" >> $GITHUB_OUTPUT | |
| echo "event_processor_tag=${{ vars.TW_EVENT_PROCESSOR_VERSION_DEV }}" >> $GITHUB_OUTPUT | |
| echo "gh_environment=v11-dev" >> $GITHUB_OUTPUT | |
| # ===================================================================== | |
| # PRODUCTION — release.published (prerelease or not) | |
| # | |
| # The tag is taken directly from the release event — no CM queries. | |
| # Always runs a full build and deploys to our production instances. | |
| # ===================================================================== | |
| elif ${{ github.event_name == 'release' }}; then | |
| CM_URL="https://cm.utmstack.com" | |
| TAG="${{ github.event.release.tag_name }}" | |
| echo "Event: release, tag=$TAG" | |
| if [[ ! "$TAG" =~ ^v11\. ]]; then | |
| echo "⏭️ Skipping: tag '$TAG' is not a v11 release." | |
| exit 0 | |
| fi | |
| echo "Environment: production (our instances)" | |
| echo "CM URL: $CM_URL" | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| echo "environment=production" >> $GITHUB_OUTPUT | |
| echo "cm_url=$CM_URL" >> $GITHUB_OUTPUT | |
| echo "event_processor_tag=${{ vars.TW_EVENT_PROCESSOR_VERSION_PROD }}" >> $GITHUB_OUTPUT | |
| echo "gh_environment=v11-prod" >> $GITHUB_OUTPUT | |
| fi | |
| build_agent: | |
| name: Build Agent Binaries | |
| needs: [setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Check out code into the right branch | |
| uses: actions/checkout@v4 | |
| - name: Build Linux Binaries (amd64) | |
| env: | |
| GOOS: linux | |
| GOARCH: amd64 | |
| CGO_ENABLED: 0 | |
| run: | | |
| cd ${{ github.workspace }}/agent | |
| go build -o utmstack_agent_service_linux_amd64 -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . | |
| cd ${{ github.workspace }}/agent/updater | |
| go build -o utmstack_updater_service_linux_amd64 . | |
| - name: Build Linux Binaries (arm64) | |
| env: | |
| GOOS: linux | |
| GOARCH: arm64 | |
| CGO_ENABLED: 0 | |
| run: | | |
| cd ${{ github.workspace }}/agent | |
| go build -o utmstack_agent_service_linux_arm64 -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . | |
| cd ${{ github.workspace }}/agent/updater | |
| go build -o utmstack_updater_service_linux_arm64 . | |
| - name: Build Windows Binaries (amd64) | |
| env: | |
| GOOS: windows | |
| GOARCH: amd64 | |
| CGO_ENABLED: 0 | |
| run: | | |
| cd ${{ github.workspace }}/agent | |
| go build -o utmstack_agent_service_windows_amd64.exe -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . | |
| cd ${{ github.workspace }}/agent/updater | |
| go build -o utmstack_updater_service_windows_amd64.exe . | |
| - name: Build Windows Binaries (arm64) | |
| env: | |
| GOOS: windows | |
| GOARCH: arm64 | |
| CGO_ENABLED: 0 | |
| run: | | |
| cd ${{ github.workspace }}/agent | |
| go build -o utmstack_agent_service_windows_arm64.exe -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . | |
| cd ${{ github.workspace }}/agent/updater | |
| go build -o utmstack_updater_service_windows_arm64.exe . | |
| - name: Build macOS Binaries (arm64) | |
| env: | |
| GOOS: darwin | |
| GOARCH: arm64 | |
| CGO_ENABLED: 0 | |
| run: | | |
| cd ${{ github.workspace }}/agent | |
| go build -o utmstack_agent_service_darwin_arm64 -v -ldflags "-X 'github.com/utmstack/UTMStack/agent/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . | |
| cd ${{ github.workspace }}/agent/updater | |
| go build -o utmstack_updater_service_darwin_arm64 . | |
| - name: Upload Linux binaries as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: agents-linux | |
| path: | | |
| agent/utmstack_agent_service_linux_amd64 | |
| agent/utmstack_agent_service_linux_arm64 | |
| agent/updater/utmstack_updater_service_linux_amd64 | |
| agent/updater/utmstack_updater_service_linux_arm64 | |
| retention-days: 1 | |
| - name: Upload unsigned Windows binaries as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: agents-windows-unsigned | |
| path: | | |
| agent/utmstack_agent_service_windows_amd64.exe | |
| agent/utmstack_agent_service_windows_arm64.exe | |
| agent/updater/utmstack_updater_service_windows_amd64.exe | |
| agent/updater/utmstack_updater_service_windows_arm64.exe | |
| retention-days: 1 | |
| - name: Upload unsigned macOS binaries as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: agents-darwin-unsigned | |
| path: | | |
| agent/utmstack_agent_service_darwin_arm64 | |
| agent/updater/utmstack_updater_service_darwin_arm64 | |
| retention-days: 1 | |
| sign_agent_windows: | |
| name: Sign Windows Agent Binaries | |
| needs: [build_agent, setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| uses: ./.github/workflows/reusable-sign-agent.yml | |
| with: | |
| os: windows | |
| artifact_name: agents-windows-unsigned | |
| signed_artifact_name: agents-windows-signed | |
| gcp_project_id: ${{ vars.GCP_PROJECT_PROD }} | |
| kms_location: ${{ vars.KMS_KEYRING_LOCATION }} | |
| kms_keyring: ${{ vars.KMS_KEYRING_NAME }} | |
| kms_key: ${{ vars.KMS_KEY_NAME }} | |
| binaries: | | |
| utmstack_agent_service_windows_amd64.exe | |
| utmstack_agent_service_windows_arm64.exe | |
| updater/utmstack_updater_service_windows_amd64.exe | |
| updater/utmstack_updater_service_windows_arm64.exe | |
| secrets: inherit | |
| sign_agent_macos: | |
| name: Sign macOS Agent Binaries | |
| needs: [build_agent, setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| uses: ./.github/workflows/reusable-sign-agent.yml | |
| with: | |
| os: macos | |
| artifact_name: agents-darwin-unsigned | |
| signed_artifact_name: agents-darwin-signed | |
| binaries: | | |
| utmstack_agent_service_darwin_arm64 | |
| updater/utmstack_updater_service_darwin_arm64 | |
| secrets: inherit | |
| build_utmstack_collector: | |
| name: Build UTMStack Collector | |
| needs: [setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Check out code into the right branch | |
| uses: actions/checkout@v4 | |
| - name: Build UTMStack Collectors | |
| run: | | |
| echo "Building UTMStack Collector..." | |
| cd ${{ github.workspace }}/utmstack-collector | |
| GOOS=linux GOARCH=amd64 go build -o utmstack_collector -v -ldflags "-X 'github.com/utmstack/UTMStack/utmstack-collector/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . | |
| echo "Building UTMStack AS400 Collector..." | |
| cd ${{ github.workspace }}/as400 | |
| GOOS=linux GOARCH=amd64 go build -o utmstack_as400_collector_service -v -ldflags "-X 'github.com/utmstack/UTMStack/as400/config.REPLACE_KEY=${{ secrets.AGENT_SECRET_PREFIX }}'" . | |
| cd ${{ github.workspace }}/as400/updater | |
| GOOS=linux GOARCH=amd64 go build -o utmstack_as400_updater_service -v . | |
| - name: Upload collector binary as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: utmstack-collectors | |
| path: | | |
| ${{ github.workspace }}/utmstack-collector/utmstack_collector | |
| ${{ github.workspace }}/as400/utmstack_as400_collector_service | |
| ${{ github.workspace }}/as400/updater/utmstack_as400_updater_service | |
| retention-days: 1 | |
| build_agent_manager: | |
| name: Build Agent Manager Microservice | |
| needs: [sign_agent_windows, sign_agent_macos, build_utmstack_collector, setup_deployment] | |
| 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 != '' }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Check out code into the right branch | |
| uses: actions/checkout@v4 | |
| - name: Download Linux agents from artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: agents-linux | |
| path: ${{ github.workspace }}/agent | |
| - name: Download signed Windows agents from artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: agents-windows-signed | |
| path: ${{ github.workspace }}/agent | |
| - name: Download UTMStack Collectors from artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: utmstack-collectors | |
| path: ${{ github.workspace }}/utmstack-collector | |
| - name: Download signed macOS agents from artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: agents-darwin-signed | |
| path: ${{ github.workspace }}/agent-darwin | |
| - name: Prepare dependencies for Agent Manager Image | |
| run: | | |
| cd ${{ github.workspace }}/agent-manager | |
| GOOS=linux GOARCH=amd64 go build -o agent-manager -v . | |
| mkdir -p ./dependencies/collector | |
| cp "${{ github.workspace }}/utmstack-collector/utmstack-collector/utmstack_collector" ./dependencies/collector/ | |
| cp "${{ github.workspace }}/utmstack-collector/version.json" ./dependencies/collector/ | |
| mkdir -p ./dependencies/collector/as400 | |
| curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/collector/as400-collector.jar" -o ./dependencies/collector/as400/as400-collector.jar | |
| cp "${{ github.workspace }}/as400/version.json" ./dependencies/collector/as400/ | |
| cp "${{ github.workspace }}/utmstack-collector/as400/utmstack_as400_collector_service" ./dependencies/collector/as400/ | |
| cp "${{ github.workspace }}/utmstack-collector/as400/updater/utmstack_as400_updater_service" ./dependencies/collector/as400/ | |
| mkdir -p ./dependencies/agent/ | |
| # Linux agents | |
| cp "${{ github.workspace }}/agent/utmstack_agent_service_linux_amd64" ./dependencies/agent/ | |
| cp "${{ github.workspace }}/agent/utmstack_agent_service_linux_arm64" ./dependencies/agent/ | |
| cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_linux_amd64" ./dependencies/agent/ | |
| cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_linux_arm64" ./dependencies/agent/ | |
| # Windows agents | |
| cp "${{ github.workspace }}/agent/utmstack_agent_service_windows_amd64.exe" ./dependencies/agent/ | |
| cp "${{ github.workspace }}/agent/utmstack_agent_service_windows_arm64.exe" ./dependencies/agent/ | |
| cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_amd64.exe" ./dependencies/agent/ | |
| cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_arm64.exe" ./dependencies/agent/ | |
| # macOS agents (signed and notarized) | |
| cp "${{ github.workspace }}/agent-darwin/utmstack_agent_service_darwin_arm64" ./dependencies/agent/ | |
| cp "${{ github.workspace }}/agent-darwin/updater/utmstack_updater_service_darwin_arm64" ./dependencies/agent/ | |
| curl -sSL "https://storage.googleapis.com/utmstack-updates/agent_updates/release/macos-agent/latest/utmstack-collector-mac" -o ./dependencies/agent/utmstack-collector-mac | |
| # TODO: Remove legacy binary names after all agents have migrated to new naming convention | |
| # Legacy names for backwards compatibility with existing agents | |
| cp "${{ github.workspace }}/agent/utmstack_agent_service_linux_amd64" ./dependencies/agent/utmstack_agent_service | |
| cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_linux_amd64" ./dependencies/agent/utmstack_updater_service | |
| cp "${{ github.workspace }}/agent/utmstack_agent_service_windows_amd64.exe" ./dependencies/agent/utmstack_agent_service.exe | |
| cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_amd64.exe" ./dependencies/agent/utmstack_updater_service.exe | |
| cp "${{ github.workspace }}/agent/utmstack_agent_service_windows_arm64.exe" ./dependencies/agent/utmstack_agent_service_arm64.exe | |
| cp "${{ github.workspace }}/agent/updater/utmstack_updater_service_windows_arm64.exe" ./dependencies/agent/utmstack_updater_service_arm64.exe | |
| cp "${{ github.workspace }}/agent/version.json" ./dependencies/agent/ | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: utmstack | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and Push the Agent Manager Image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: ./agent-manager | |
| push: true | |
| provenance: false | |
| tags: ghcr.io/utmstack/utmstack/agent-manager:${{ needs.setup_deployment.outputs.tag }} | |
| build_event_processor: | |
| name: Build Event Processor Microservice | |
| needs: [setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Check out code into the right branch | |
| uses: actions/checkout@v4 | |
| - name: Build Plugins | |
| env: | |
| GOOS: linux | |
| GOARCH: amd64 | |
| run: | | |
| cd ${{ github.workspace }}/plugins/alerts; go build -o com.utmstack.alerts.plugin -v . | |
| cd ${{ github.workspace }}/plugins/aws; go build -o com.utmstack.aws.plugin -v . | |
| cd ${{ github.workspace }}/plugins/azure; go build -o com.utmstack.azure.plugin -v . | |
| cd ${{ github.workspace }}/plugins/bitdefender; go build -o com.utmstack.bitdefender.plugin -v . | |
| cd ${{ github.workspace }}/plugins/config; go build -o com.utmstack.config.plugin -v . | |
| cd ${{ github.workspace }}/plugins/events; go build -o com.utmstack.events.plugin -v . | |
| cd ${{ github.workspace }}/plugins/gcp; go build -o com.utmstack.gcp.plugin -v . | |
| cd ${{ github.workspace }}/plugins/geolocation; go build -o com.utmstack.geolocation.plugin -v . | |
| cd ${{ github.workspace }}/plugins/inputs; go build -o com.utmstack.inputs.plugin -v . | |
| cd ${{ github.workspace }}/plugins/o365; go build -o com.utmstack.o365.plugin -v . | |
| cd ${{ github.workspace }}/plugins/sophos; go build -o com.utmstack.sophos.plugin -v . | |
| cd ${{ github.workspace }}/plugins/stats; go build -o com.utmstack.stats.plugin -v . | |
| cd ${{ github.workspace }}/plugins/soc-ai; go build -o com.utmstack.soc-ai.plugin -v . | |
| cd ${{ github.workspace }}/plugins/modules-config; go build -o com.utmstack.modules-config.plugin -v . | |
| cd ${{ github.workspace }}/plugins/crowdstrike; go build -o com.utmstack.crowdstrike.plugin -v . | |
| cd ${{ github.workspace }}/plugins/feeds; go build -o com.utmstack.feeds.plugin -v . | |
| cd ${{ github.workspace }}/plugins/rule-flood-guard; go build -o com.utmstack.rule-flood-guard.plugin -v . | |
| - name: Prepare Dependencies for Event Processor Image | |
| run: | | |
| mkdir -p ./geolocation | |
| curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/geolocation/asn-blocks-v4.csv" -o ./geolocation/asn-blocks-v4.csv | |
| curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/geolocation/asn-blocks-v6.csv" -o ./geolocation/asn-blocks-v6.csv | |
| curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/geolocation/blocks-v4.csv" -o ./geolocation/blocks-v4.csv | |
| curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/geolocation/blocks-v6.csv" -o ./geolocation/blocks-v6.csv | |
| curl -sSL "https://storage.googleapis.com/utmstack-updates/dependencies/geolocation/locations-en.csv" -o ./geolocation/locations-en.csv | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: utmstack | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and Push the Event Processor Image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./event_processor.Dockerfile | |
| push: true | |
| provenance: false | |
| tags: ghcr.io/utmstack/utmstack/eventprocessor:${{ needs.setup_deployment.outputs.tag }} | |
| build-args: | | |
| BASE_IMAGE=ghcr.io/threatwinds/eventprocessor/base:${{ needs.setup_deployment.outputs.event_processor_tag }} | |
| build_backend: | |
| name: Build Backend Microservice | |
| needs: [setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| uses: ./.github/workflows/reusable-java.yml | |
| with: | |
| image_name: backend | |
| tag: ${{ needs.setup_deployment.outputs.tag }} | |
| java_version: '17' | |
| use_tag_as_version: true | |
| maven_profile: 'prod' | |
| maven_goals: 'clean package' | |
| copy_filters_and_rules: true | |
| build_frontend: | |
| name: Build Frontend Microservice | |
| needs: [setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| uses: ./.github/workflows/reusable-node.yml | |
| with: | |
| image_name: frontend | |
| tag: ${{ needs.setup_deployment.outputs.tag }} | |
| build_user_auditor: | |
| name: Build User-Auditor Microservice | |
| needs: [setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| uses: ./.github/workflows/reusable-java.yml | |
| with: | |
| image_name: user-auditor | |
| tag: ${{ needs.setup_deployment.outputs.tag }} | |
| java_version: '11' | |
| use_version_file: false | |
| maven_goals: 'clean install -U' | |
| build_web_pdf: | |
| name: Build Web-PDF Microservice | |
| needs: [setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' }} | |
| uses: ./.github/workflows/reusable-java.yml | |
| with: | |
| image_name: web-pdf | |
| tag: ${{ needs.setup_deployment.outputs.tag }} | |
| java_version: '11' | |
| use_version_file: false | |
| maven_goals: 'clean install -U' | |
| all_builds_complete: | |
| name: All Builds Complete | |
| needs: [ | |
| build_agent_manager, | |
| build_event_processor, | |
| build_backend, | |
| build_frontend, | |
| build_user_auditor, | |
| build_web_pdf | |
| ] | |
| if: ${{ always() && needs.build_agent_manager.result == 'success' && needs.build_event_processor.result == 'success' && needs.build_backend.result == 'success' && needs.build_frontend.result == 'success' && needs.build_user_auditor.result == 'success' && needs.build_web_pdf.result == 'success' }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - run: echo "✅ All builds completed successfully." | |
| generate_changelog: | |
| name: Generate Changelog | |
| needs: [all_builds_complete, setup_deployment] | |
| if: ${{ needs.setup_deployment.outputs.tag != '' && github.event_name == 'release' }} | |
| uses: ./.github/workflows/generate-changelog.yml | |
| with: | |
| current_tag: ${{ needs.setup_deployment.outputs.tag }} | |
| secrets: | |
| THREATWINDS_API_KEY: ${{ secrets.THREATWINDS_API_KEY }} | |
| THREATWINDS_API_SECRET: ${{ secrets.THREATWINDS_API_SECRET }} | |
| # Installer build runs on any GitHub release event (prerelease or not). | |
| # The binary is uploaded to the release that triggered this pipeline. | |
| build_installer_release: | |
| name: Build & Upload Installer | |
| needs: [generate_changelog, setup_deployment] | |
| if: ${{ always() && needs.setup_deployment.result == 'success' && needs.setup_deployment.outputs.tag != '' && github.event_name == 'release' && needs.generate_changelog.result != 'cancelled' }} | |
| uses: ./.github/workflows/installer-release.yml | |
| with: | |
| version: ${{ needs.setup_deployment.outputs.tag }} | |
| version_major: v11 | |
| environment: ${{ needs.setup_deployment.outputs.environment }} | |
| changelog: ${{ needs.generate_changelog.outputs.changelog }} | |
| secrets: | |
| API_SECRET: ${{ secrets.API_SECRET }} | |
| CM_ENCRYPT_SALT: ${{ secrets.CM_ENCRYPT_SALT }} | |
| CM_SIGN_PUBLIC_KEY: ${{ secrets.CM_SIGN_PUBLIC_KEY }} | |
| deploy_installer_dev: | |
| name: Deploy Installer (Dev) | |
| needs: [all_builds_complete, setup_deployment] | |
| if: ${{ always() && needs.all_builds_complete.result == 'success' && needs.setup_deployment.outputs.tag != '' && needs.setup_deployment.outputs.environment == 'dev' }} | |
| uses: ./.github/workflows/installer-release.yml | |
| with: | |
| version: ${{ needs.setup_deployment.outputs.tag }} | |
| version_major: v11 | |
| environment: dev | |
| secrets: | |
| API_SECRET: ${{ secrets.API_SECRET }} | |
| CM_ENCRYPT_SALT: ${{ secrets.CM_ENCRYPT_SALT }} | |
| CM_SIGN_PUBLIC_KEY: ${{ secrets.CM_SIGN_PUBLIC_KEY }} | |
| publish_new_version: | |
| name: Publish New Version to Customer Manager | |
| needs: [all_builds_complete, generate_changelog, setup_deployment] | |
| if: ${{ always() && needs.all_builds_complete.result == 'success' && needs.setup_deployment.outputs.tag != '' }} | |
| environment: ${{ needs.setup_deployment.outputs.gh_environment }} | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v4 | |
| - name: Publish version | |
| env: | |
| CHANGELOG_CONTENT: ${{ needs.generate_changelog.outputs.changelog }} | |
| ENVIRONMENT: ${{ needs.setup_deployment.outputs.environment }} | |
| TAG: ${{ needs.setup_deployment.outputs.tag }} | |
| CM_URL: ${{ needs.setup_deployment.outputs.cm_url }} | |
| SCHEDULE_INSTANCES: ${{ vars.SCHEDULE_INSTANCES }} | |
| run: | | |
| # Use AI changelog for production releases, generic for dev. | |
| if [ "$ENVIRONMENT" != "dev" ] && [ -n "$CHANGELOG_CONTENT" ]; then | |
| changelog="$CHANGELOG_CONTENT" | |
| else | |
| changelog="Development build $TAG - Internal testing release" | |
| fi | |
| echo "Environment: $ENVIRONMENT" | |
| echo "CM URL: $CM_URL" | |
| echo "Tag: $TAG" | |
| if [ "$ENVIRONMENT" = "dev" ]; then | |
| cmAuth='${{ secrets.CM_SERVICE_ACCOUNT_DEV }}' | |
| else | |
| cmAuth='${{ secrets.CM_SERVICE_ACCOUNT_PROD }}' | |
| fi | |
| id=$(echo "$cmAuth" | jq -r '.id') | |
| key=$(echo "$cmAuth" | jq -r '.key') | |
| # CM-dev already has the products schema (same as v12). CM-prod | |
| # doesn't yet, so production keeps registering one version with no | |
| # product_id, exactly as before -- only dev goes through the | |
| # per-product loop. | |
| if [ "$ENVIRONMENT" = "dev" ]; then | |
| if ! echo "$SCHEDULE_INSTANCES" | jq empty > /dev/null 2>&1; then | |
| echo "❌ SCHEDULE_INSTANCES is not valid JSON: $SCHEDULE_INSTANCES" | |
| exit 1 | |
| fi | |
| for product_id in $(echo "$SCHEDULE_INSTANCES" | jq -r 'keys[]'); do | |
| echo "Registering $TAG for product: $product_id" | |
| body=$(jq -n \ | |
| --arg version "$TAG" \ | |
| --arg changelog "$changelog" \ | |
| --arg product_id "$product_id" \ | |
| '{version: $version, changelog: $changelog, product_id: $product_id}' | |
| ) | |
| response=$(curl -s -X POST "${CM_URL}/api/v1/versions/register" \ | |
| -H "Content-Type: application/json" \ | |
| -H "id: $id" \ | |
| -H "key: $key" \ | |
| -d "$body") | |
| echo "Response: $response" | |
| done | |
| else | |
| body=$(jq -n \ | |
| --arg version "$TAG" \ | |
| --arg changelog "$changelog" \ | |
| '{version: $version, changelog: $changelog}' | |
| ) | |
| response=$(curl -s -X POST "${CM_URL}/api/v1/versions/register" \ | |
| -H "Content-Type: application/json" \ | |
| -H "id: $id" \ | |
| -H "key: $key" \ | |
| -d "$body") | |
| echo "Response: $response" | |
| fi | |
| schedule: | |
| name: Schedule release to our instances | |
| needs: [publish_new_version, setup_deployment] | |
| if: ${{ always() && needs.publish_new_version.result == 'success' && needs.setup_deployment.outputs.tag != '' }} | |
| environment: ${{ needs.setup_deployment.outputs.gh_environment }} | |
| runs-on: ubuntu-24.04 | |
| env: | |
| ENVIRONMENT: ${{ needs.setup_deployment.outputs.environment }} | |
| TAG: ${{ needs.setup_deployment.outputs.tag }} | |
| CM_URL: ${{ needs.setup_deployment.outputs.cm_url }} | |
| SCHEDULE_INSTANCES: ${{ vars.SCHEDULE_INSTANCES }} | |
| CM_AUTH_DEV: ${{ secrets.CM_SERVICE_ACCOUNT_DEV }} | |
| CM_AUTH_PROD: ${{ secrets.CM_SERVICE_ACCOUNT_PROD }} | |
| steps: | |
| - name: Schedule updates | |
| run: | | |
| echo "🔍 Environment: $ENVIRONMENT" | |
| echo "🔍 Version: $TAG" | |
| echo "🔍 CM URL: $CM_URL" | |
| if [ "$ENVIRONMENT" = "dev" ]; then | |
| auth_json="$CM_AUTH_DEV" | |
| else | |
| auth_json="$CM_AUTH_PROD" | |
| fi | |
| auth_id=$(echo "$auth_json" | jq -r '.id') | |
| auth_key=$(echo "$auth_json" | jq -r '.key') | |
| scheduled=0 | |
| # CM-dev has the products schema (same as v12). CM-prod doesn't | |
| # yet, so production keeps scheduling against the flat instance | |
| # list with no product_id, exactly as before -- only dev goes | |
| # through the per-product loop. | |
| if [ "$ENVIRONMENT" = "dev" ]; then | |
| if ! echo "$SCHEDULE_INSTANCES" | jq empty > /dev/null 2>&1; then | |
| echo "❌ SCHEDULE_INSTANCES is not valid JSON: $SCHEDULE_INSTANCES" | |
| exit 1 | |
| fi | |
| for product_id in $(echo "$SCHEDULE_INSTANCES" | jq -r 'keys[]'); do | |
| for instance_id in $(echo "$SCHEDULE_INSTANCES" | jq -r --arg p "$product_id" '.[$p][]'); do | |
| echo "📅 Scheduling release for instance: $instance_id (product: $product_id)" | |
| response=$(curl -s -w "\n%{http_code}" -X POST "${CM_URL}/api/v1/updates" \ | |
| -H "Content-Type: application/json" \ | |
| -H "id: $auth_id" \ | |
| -H "key: $auth_key" \ | |
| -d "{\"instances_ids\": [\"$instance_id\"], \"version\": \"$TAG\", \"product_id\": \"$product_id\"}") | |
| http_code=$(echo "$response" | tail -n1) | |
| body=$(echo "$response" | sed '$d') | |
| if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then | |
| echo "✅ Successfully scheduled for instance: $instance_id" | |
| scheduled=$((scheduled + 1)) | |
| else | |
| echo "❌ Failed to schedule for instance: $instance_id (HTTP $http_code)" | |
| echo "Response: $body" | |
| exit 1 | |
| fi | |
| done | |
| done | |
| else | |
| IFS=',' read -ra ID_ARRAY <<< "$SCHEDULE_INSTANCES" | |
| for instance_id in "${ID_ARRAY[@]}"; do | |
| instance_id=$(echo "$instance_id" | xargs) | |
| echo "📅 Scheduling release for instance: $instance_id" | |
| response=$(curl -s -w "\n%{http_code}" -X POST "${CM_URL}/api/v1/updates" \ | |
| -H "Content-Type: application/json" \ | |
| -H "id: $auth_id" \ | |
| -H "key: $auth_key" \ | |
| -d "{\"instances_ids\": [\"$instance_id\"], \"version\": \"$TAG\"}") | |
| http_code=$(echo "$response" | tail -n1) | |
| body=$(echo "$response" | sed '$d') | |
| if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then | |
| echo "✅ Successfully scheduled for instance: $instance_id" | |
| scheduled=$((scheduled + 1)) | |
| else | |
| echo "❌ Failed to schedule for instance: $instance_id (HTTP $http_code)" | |
| echo "Response: $body" | |
| exit 1 | |
| fi | |
| done | |
| fi | |
| if [ "$scheduled" -eq 0 ]; then | |
| echo "❌ No instances were scheduled — check SCHEDULE_INSTANCES for this environment" | |
| exit 1 | |
| fi | |
| echo "✅ Scheduled release for all $scheduled instance(s) with version $TAG" | |