Build-Test-Deploy #18103
This file contains 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: 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 |