Skip to content

Build-Test-Deploy #17996

Build-Test-Deploy

Build-Test-Deploy #17996

Workflow file for this run

name: Build-Test-Deploy
# === Triggers ===
"on":
push:
branches:
- master
- beta
- release
- LTS*
pull_request:
types:
- labeled
- opened
- synchronize
- reopened
schedule:
- cron: 0 0 * * *
# === Workflow Permissions ===
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
# === Workflow-level environment variables ===
env:
AWS_REGION: us-east-1
AWS_ROLE_SESSION_NAME: gha-activestate-cli
# === JOBS ===
jobs:
# === OS Specific Job (runs on each OS) ===
os_specific:
name: ${{ matrix.sys.os }}
timeout-minutes: 90
strategy:
matrix:
go-version:
- 1.22.x
sys:
- {os: ubuntu-latest}
- {os: macos-13, shell: zsh}
- {os: windows-2019}
fail-fast: false
runs-on: ${{ matrix.sys.os }}
env:
ACTIVESTATE_CI: true
SHELL: bash
GITHUB_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_SHA_OVERRIDE: ${{ github.event.pull_request.head.sha || github.sha }}
concurrency:
group: ${{ github.ref }}-${{ github.event_name }}-${{ matrix.sys.os }}
cancel-in-progress: true
# === OS Specific Steps ===
steps:
- # === Disable Windows Defender as it slows things down significantly ===
name: Disabling Windows Defender
if: runner.os == 'Windows'
shell: powershell
run: Set-MpPreference -DisableRealtimeMonitoring $true
- # === Checkout Code ===
name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- # === Install Go ===
name: Install Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
# === Install gotestfmt ===
- name: Set up gotestfmt
uses: gotesttools/gotestfmt-action@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
- # === Install State Tool ===
name: Install State Tool
uses: ActiveState/setup-state-tool@v1
- # === Setup ===
name: Setup
shell: bash
run: |
bin=$(pwd)/.github/deps/${{ runner.os }}/bin
echo "Adding $bin to PATH"
echo "$bin" >> $GITHUB_PATH
if [ -x "$(command -v apt-get)" ]; then
sudo apt-get update
sudo apt-get install fish zsh tcsh -y
# Prevent zsh insecure directory warning.
sudo chmod -R 755 /usr/share/zsh/vendor-completions /usr/share/zsh
sudo chown -R root:root /usr/share/zsh/vendor-completions /usr/share/zsh
touch ~/.zshrc
fi
printenv
- # === Setup Windows ===
name: Setup (Windows)
shell: pwsh
run: |
echo "${PSScriptRoot}/.github/deps/${{ runner.os }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
state run install-deps-ci
- # == Setup macOS ==
name: Setup (macOS)
shell: bash
run: brew install fish
if: runner.os == 'macOS'
- # === Preprocess ===
name: Preprocess
shell: bash
timeout-minutes: 3
run: state run preprocess -v
- # === Parallel Tasks ===
name: Parallel Tasks
shell: bash
timeout-minutes: 15
run: |
export PATH="$(pwd)/.github/deps/${{ runner.os }}/bin:$PATH"
parallelize "$(cat <<'EOF'
[
{
"ID": "Check-Format",
"Args": ["state", "run", "check-format"]
},
{
"ID": "Unit-Tests",
"Args": ["state", "run", "test", "-json", "2>&1"]
},
{
"ID": "Build-CLI",
"Args": ["state", "run", "build"]
},
{
"ID": "Build-Service",
"Args": ["state", "run", "build-svc"]
},
{
"ID": "Build-Installer",
"Args": ["state", "run", "build-installer"]
},
{
"ID": "Build-Remote-Installer",
"Args": ["state", "run", "build-remote-installer"]
},
{
"ID": "Build-Install-Scripts",
"Args": ["state", "run", "build-install-scripts"]
},
{
"ID": "Build-Executor",
"Args": ["state", "run", "build-exec"]
}
]
EOF
)"
env:
CODE_SIGNING_PASSWD: ${{ secrets.CODE_SIGNING_PASSWD }}
MSI_CERT_BASE64: ${{ secrets.MSI_CERT_BASE64 }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JIRA_USERNAME: ${{ secrets.JIRA_EMAIL }}
JIRA_TOKEN: ${{ secrets.JIRA_TOKEN }}
- # === Unit Tests ===
name: Check Format
id: check_format
shell: bash
if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\", \"refs/heads/master\"]'), github.ref) && !startsWith(github.event.pull_request.head.ref, 'version/')"
run: parallelize results Check-Format
- # === Unit Tests ===
name: Unit Tests
id: unit_tests
shell: bash
run: parallelize results Unit-Tests | gotestfmt -hide empty-packages
continue-on-error: ${{ github.event_name != 'schedule' }}
- # === "Build: CLI" ===
name: "Build: CLI"
shell: bash
run: parallelize results Build-CLI
- # === "Build: Service" ===
name: "Build: Service"
shell: bash
run: parallelize results Build-Service
- # === "Build: Installer" ===
name: "Build: Installer"
shell: bash
run: parallelize results Build-Installer
- # === "Build: Remote Installer" ===
name: "Build: Remote Installer"
shell: bash
run: parallelize results Build-Remote-Installer
- # === "Build: Install Scripts" ===
name: "Build: Install Scripts"
shell: bash
run: parallelize results Build-Install-Scripts
- # === "Build: Executor" ===
name: "Build: Executor"
shell: bash
run: parallelize results Build-Executor
- # === Prepare Windows Cert ===
name: Prepare Windows Cert
shell: bash
if: runner.os == 'Windows'
run: |
echo $MSI_CERT_BASE64 | base64 --decode > Cert.p12
env:
MSI_CERT_BASE64: ${{ secrets.MSI_CERT_BASE64 }}
- # === Sign Binaries (Windows only) ===
name: Sign Binaries (Windows only)
shell: bash
if: runner.os == 'Windows' && contains(fromJSON('["refs/heads/beta", "refs/heads/release", "refs/heads/LTS"]'), github.ref)
run: |
export PATH=/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/:/c/Program\ Files\ \(x86\)/Windows\ Kits/10/bin/10.0.16299.0/x86/:$PATH
signtool.exe sign -d "ActiveState State Tool" -f "Cert.p12" -p ${CODE_SIGNING_PASSWD} ./build/state.exe
signtool.exe sign -d "ActiveState State Service" -f "Cert.p12" -p ${CODE_SIGNING_PASSWD} ./build/state-svc.exe
signtool.exe sign -d "ActiveState State Installer" -f "Cert.p12" -p ${CODE_SIGNING_PASSWD} ./build/state-installer.exe
signtool.exe sign -d "ActiveState State Tool Remote Installer" -f "Cert.p12" -p ${CODE_SIGNING_PASSWD} ./build/state-remote-installer.exe
env:
CODE_SIGNING_PASSWD: ${{ secrets.CODE_SIGNING_PASSWD }}
- # === Sign Install Scripts (Windows only) ===
name: Sign Install Scripts (Windows only)
shell: powershell
if: runner.os == 'Windows' && contains(fromJSON('["refs/heads/beta", "refs/heads/release", "refs/heads/LTS"]'), github.ref)
run: |
$branchInfix = $Env:GITHUB_REF.Replace("refs/heads/", "").Replace("release", "")
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import('Cert.p12',$env:CODE_SIGNING_PASSWD,'DefaultKeySet')
Set-AuthenticodeSignature -FilePath build\installers\$branchInfix\install.ps1 -Certificate $cert
Set-AuthenticodeSignature -FilePath build\installers\$branchInfix\legacy-install.ps1 -Certificate $cert
env:
CODE_SIGNING_PASSWD: ${{ secrets.CODE_SIGNING_PASSWD }}
- # === Generate Update ===
name: Generate Update
shell: bash
run: state run generate-update
- # === Generate Remote Install Deployment ==
name: Generate Remote Install Deployment
shell: bash
run: state run generate-remote-install-deployment
- # === Configure AWS credentials ==
name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
role-session-name: ${{ env.AWS_ROLE_SESSION_NAME }}
aws-region: ${{ env.AWS_REGION }}
mask-aws-account-id: true
- # === Deploy for Integration Tests # NEVER run this against production branches. This is meant for PR deployments. ===
name: Deploy for Integration Tests # NEVER run this against production branches. This is meant for PR deployments.
if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\"]'), github.ref)"
shell: bash
run: |
if [ "$GITHUB_EVENT_NAME" != "schedule" ]; then
set +e
LABELS="${{ join(github.event.pull_request.labels.*.name, ',') }}"
LABELCHECK="$(echo $LABELS | grep "Test:")" # This line is causing a non-zero exit if `set -e` is set, and I can't figure out why
TARGET_BRANCH="${{ github.event.pull_request.base.ref }}"
set -e
echo "Labels: $LABELS"
echo "Target Branch: $TARGET_BRANCH"
if [ "$LABELCHECK" == "" ] && [ "$TARGET_BRANCH" != "beta" ] && [ "$TARGET_BRANCH" != "release" ] && [[ -z "`echo $TARGET_BRANCH | grep -o '^LTS'`" ]]; then
echo "Not running because no test labels were set nor beta, release or LTS were targeted."
exit 0
fi
fi
state run deploy-updates
state run deploy-installers
state run deploy-remote-installer
- # === Integration Tests ===
name: Integration Tests
id: integration_tests
if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\"]'), github.ref)"
shell: bash
run: |
if [ "$GITHUB_EVENT_NAME" != "schedule" ]; then
LABELS="${{ join(github.event.pull_request.labels.*.name, ',') }}"
IFS=',' read -r -a TESTS <<< "$LABELS"
TEST_SUITE_TAGS=""
for i in "${TESTS[@]}"; do
START=${i%:*}
if [ "$START" == "Test" ]; then
TAG=${i##*:}
TAG=$(echo $TAG | xargs)
if [[ "$TEST_SUITE_TAGS" == "" ]]; then
TEST_SUITE_TAGS=$TAG
else
TEST_SUITE_TAGS="$TAG:$TEST_SUITE_TAGS"
fi
fi
done
TARGET_BRANCH="${{ github.event.pull_request.base.ref }}"
echo "Target branch: $TARGET_BRANCH"
if [ "$TEST_SUITE_TAGS" == "" ] && [ "$TARGET_BRANCH" != "master" ] && [ "$TARGET_BRANCH" != "beta" ] && [ "$TARGET_BRANCH" != "release" ] && [ "$TARGET_BRANCH" != "lts-release" ]; then
echo "Not running because no test labels were set nor master, beta or release were targeted."
exit 0
fi
else
TEST_SUITE_TAGS="all"
fi
echo "Running integration tests with tags: $TEST_SUITE_TAGS (empty means every test not specifically tagged)"
export TEST_SUITE_TAGS="$TEST_SUITE_TAGS"
TIMEOUT=30m
if [[ "$TEST_SUITE_TAGS" == "all" ]]; then
TIMEOUT=90m
fi
SHELL='${{ matrix.sys.shell }}' go test -timeout $TIMEOUT -v `go list ./... | grep "integration"` -json 2>&1 | gotestfmt -hide empty-packages
continue-on-error: ${{ github.event_name == 'schedule' }}
env:
INTEGRATION_TEST_USERNAME: ${{ secrets.INTEGRATION_TEST_USERNAME }}
INTEGRATION_TEST_PASSWORD: ${{ secrets.INTEGRATION_TEST_PASSWORD }}
INTEGRATION_TEST_TOKEN: ${{ secrets.INTEGRATION_TEST_TOKEN }}
PLATFORM_API_TOKEN: ${{ secrets.PLATFORM_API_TOKEN }}
- # === Fail If Unscheduled Unit Tests Failed (Expand 'Unit Tests' above for more information) ===
name: Fail If Unscheduled Unit Tests Failed
if: github.event_name != 'schedule' && steps.unit_tests.outcome == 'failure'
shell: bash
run: exit 1
- # === Notify Slack of Nightly Integration Test Failures ===
name: Notify Slack of Nightly Integration Test Failures
if: github.event_name == 'schedule' && steps.integration_tests.outcome == 'failure'
uses: slackapi/[email protected]
with:
payload: |
{
"text": "Nightly integration test failure(s) on ${{ runner.os }}",
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Nightly integration test failure(s) on ${{ runner.os }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Select the '${{ matrix.sys.os }}' job and expand 'Integration Tests' to inspect the failures."
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
- # === Fail If Nightly Integration Tests Failed (Expand 'Integration Tests' above for more information) ===
name: Fail If Nightly Integration Tests Failed
if: github.event_name == 'schedule' && steps.integration_tests.outcome == 'failure'
shell: bash
run: exit 1
- # === Upload Session Artifacts ===
name: Upload Session Artifacts
uses: actions/upload-artifact@v4
with:
name: session-build-${{ matrix.sys.os }}
path: build/
scan:
name: Scan
needs:
- os_specific
runs-on: ubuntu-latest
steps:
- name: Download All Build Session Artifacts
uses: actions/download-artifact@v4
with:
path: build/
merge-multiple: true
- name: Scan for CVEs
if: runner.os == 'Linux'
uses: aquasecurity/[email protected]
env:
TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db
with:
scan-type: rootfs
scan-ref: build
list-all-pkgs: true
ignore-unfixed: true
format: table
exit-code: 1
# === Deploy job (runs once with combined artifacts from OS specific job) ===
deploy:
name: Deploy
needs:
- scan
runs-on: ubuntu-20.04
env:
ACTIVESTATE_CI: true
SHELL: bash
GITHUB_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_SHA_OVERRIDE: ${{ github.event.pull_request.head.sha || github.sha }}
timeout-minutes: 10
if: contains(fromJSON('["refs/heads/master", "refs/heads/beta", "refs/heads/release", "refs/heads/LTS"]'), github.ref) || startsWith(github.event.pull_request.head.ref, 'version/')
# === Deploy Steps ===
steps:
- # === Checkout code ===
name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- # === Install Go ===
name: Install Go
uses: actions/setup-go@v5
with:
go-version: 1.22.x
- # === Install State Tool ===
name: Install State Tool
uses: ActiveState/setup-state-tool@v1
- # === Download All Build Session Artifacts ===
name: Download All Build Session Artifacts
uses: actions/download-artifact@v4
with:
path: build/
merge-multiple: true
- # === Sanitize All Session Artifacts ===
name: Sanitize All Session Artifacts
shell: bash
run: |
cd build
rm -Rf session-shared-build
find . -mindepth 2 -maxdepth 2 -print0 | xargs -0 -I file rsync -av file .
rm -Rf session*
- # === Preprocess ===
name: Preprocess
shell: bash
run: state run preprocess -v
- # === Cleanup Build Dir ===
name: Cleanup Build Dir
shell: bash
run: rm build/state* || true
- # === Configure AWS credentials ==
name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
role-session-name: ${{ env.AWS_ROLE_SESSION_NAME }}
aws-region: ${{ env.AWS_REGION }}
mask-aws-account-id: true
- # === Generate updated master versions.json if necessary ===
name: Generate version list
shell: bash
run: state run generate-versions-list
- # === Deploy ===
name: Deploy
shell: bash
run: |
state run deploy-updates
state run deploy-installers
state run deploy-remote-installer
- # === Cleanup Session Artifacts ===
name: Cleanup Session Artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: |
session-build-ubuntu-20.04
session-build-macos-11
session-build-windows-2019