pulumi/pulumi
28 workflows · maturity 67% · 13 patterns · GitHub ↗
Practices
✓ Matrix✓ Permissions○ Security scan○ AI review✓ Cache✓ Concurrency✓ Reusable workflows
Detected patterns
Security dimensions
Workflows (28)
ci matrix perms .github/workflows/ci.yml
View raw YAML
name: CI
permissions:
contents: read
id-token: write
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
lint:
required: false
default: true
description: "Whether to run lints"
type: boolean
test-version-sets:
required: false
default: minimum current
description: Version sets on which to run integration tests
type: string
integration-test-platforms:
required: false
default: ubuntu-latest
description: Platforms on which to run integration tests, as a space delimited list
type: string
acceptance-test-platforms:
required: false
default: macos-latest
description: Platforms on which to run integration tests, as a space delimited list
type: string
enable-coverage:
description: "Collects coverage stats; requires cov-enabled builds"
default: false
required: false
type: boolean
fail-fast:
required: false
default: false
description: "Fail all workflows whenever one of them fails"
type: boolean
test-retries:
required: false
default: 0
description: "Retry tests n times if there are failures"
type: number
fuzz-test-optional:
required: false
default: false
description: "If true, fuzz test failures won't block the workflow"
type: boolean
secrets:
PULUMI_PROD_ACCESS_TOKEN:
required: false
description: "Pulumi access token, required to run tests against the service"
CODECOV_TOKEN:
required: false
description: "CodeCov token, required to publish CodeCov coverage data"
AZURE_TENANT_ID:
required: false
description: "Azure tenant ID, required to run tests against Azure"
AZURE_CLIENT_ID:
required: false
description: "Azure client ID, required to run tests against Azure"
AZURE_CLIENT_SECRET:
required: false
description: "Azure clients secret, needs to be rotated before 2027-12-22 (see the TestAzureLoginAzLogin test for detailed instructions)"
AZURE_STORAGE_SAS_TOKEN:
required: false
description: "Azure storage SAS token, required to run tests against Azure"
GCP_SERVICE_ACCOUNT:
required: false
description: "GCP service account, required to run tests against GCP"
jobs:
matrix:
runs-on: ubuntu-latest
strategy:
fail-fast: ${{ inputs.fail-fast }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Configure Go Cache Key
env:
CACHE_KEY: "matrix-setup"
run: echo "$CACHE_KEY" > .gocache.tmp
- name: Setup Go Caching
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5 # only used by gotestsum
with:
go-version: '>=1.19.0' # decoupled from version sets, only used by gotestsum
cache: true
cache-dependency-path: |
pkg/go.sum
.gocache.tmp
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
repository: dnephin/gotestsum
ref: d09768c81065b404caed0855eb3ab8f11a2a4431
path: vendor/gotestsum
- run: |
cd vendor/gotestsum
go install .
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: test-results
key: read-gotestsum-timing-${{ github.run_number }}
restore-keys: gotestsum-timing-
- uses: jdx/mise-action@v2
with:
experimental: true
- name: Install CLI
run: SDKS='' make install
- name: build matrix
id: matrix
env:
TEST_VERSION_SETS: ${{ inputs.test-version-sets }}
INPUT_INTEGRATION_TEST_PLATFORMS: ${{ inputs.integration-test-platforms }}
INPUT_ACCEPTANCE_TEST_PLATFORMS: ${{ inputs.acceptance-test-platforms }}
run: |
echo "::group::Prime test timing data"
mkdir -p test-results
find test-results -type f -empty -print -delete || true
echo "::endgroup::"
echo "::group::Remove old test timing data"
# Timing data prior to this date is unreliable. Codegen tests modified in #11052 and
# merged Monday Oct 17 at 6PM Pacific.
find test-results -type f ! -newermt "2022-10-17T18:00-07:00" -print -delete || true
echo "::endgroup::"
echo "::group::Test matrix variables"
readarray -td' ' VERSION_SETS_TO_TEST < <(echo -n "$TEST_VERSION_SETS"); declare -p VERSION_SETS_TO_TEST;
readarray -td' ' INTEGRATION_PLATFORMS < <(echo -n "$INPUT_INTEGRATION_TEST_PLATFORMS"); declare -p INTEGRATION_PLATFORMS;
readarray -td' ' ACCEPTANCE_PLATFORMS < <(echo -n "$INPUT_ACCEPTANCE_TEST_PLATFORMS"); declare -p ACCEPTANCE_PLATFORMS;
BUILD_TARGETS='[
{ "os": "linux", "arch": "amd64", "build-platform": "ubuntu-latest" },
{ "os": "linux", "arch": "arm64", "build-platform": "ubuntu-latest" },
{ "os": "windows", "arch": "amd64", "build-platform": "ubuntu-latest" },
{ "os": "windows", "arch": "arm64", "build-platform": "ubuntu-latest" },
{ "os": "darwin", "arch": "amd64", "build-platform": "ubuntu-latest" },
{ "os": "darwin", "arch": "arm64", "build-platform": "ubuntu-latest" }
]'
CODEGEN_TESTS_FLAG=--codegen-tests
PKG_UNIT_TEST_PARTITIONS=7
UNIT_TEST_MATRIX=$(
./scripts/get-job-matrix.py \
-vvv \
generate-matrix \
--kind unit-test \
"$CODEGEN_TESTS_FLAG" \
--platform "${INTEGRATION_PLATFORMS[@]}" "${ACCEPTANCE_PLATFORMS[@]}" \
--version-set current \
--partition-module pkg "$PKG_UNIT_TEST_PARTITIONS" \
--partition-module sdk 1 \
--partition-module sdk/go/pulumi-language-go 1 \
--partition-module tests 8
)
INTEGRATION_TEST_MATRIX=$(
./scripts/get-job-matrix.py \
-vvv \
generate-matrix \
--kind integration-test \
"$CODEGEN_TESTS_FLAG" \
--platform "${INTEGRATION_PLATFORMS[@]}" \
--version-set "${VERSION_SETS_TO_TEST[@]}" \
--partition-module pkg 1 \
--partition-module sdk 1 \
--partition-module sdk/go/pulumi-language-go 1 \
--partition-package github.com/pulumi/pulumi/sdk/pcl/v3/cmd/pulumi-language-pcl \
sdk/pcl/cmd/pulumi-language-pcl 2 \
--partition-package github.com/pulumi/pulumi/sdk/nodejs/cmd/pulumi-language-nodejs/v3 \
sdk/nodejs/cmd/pulumi-language-nodejs 3 \
--partition-package github.com/pulumi/pulumi/sdk/python/cmd/pulumi-language-python/v3 \
sdk/python/cmd/pulumi-language-python 4 \
--partition-module tests 2 \
--partition-package github.com/pulumi/pulumi/tests/smoke tests/smoke 4 \
--partition-package github.com/pulumi/pulumi/tests/integration tests/integration 8
)
LOWEST_PYTHON_DEPS_TEST_MATRIX=$(
./scripts/get-job-matrix.py \
-vvv \
generate-matrix \
--kind lowest-python-deps-test \
--platform ubuntu-latest \
--version-set minimum
)
INTEGRATION_TEST_MATRIX=$(
./scripts/get-job-matrix.py \
combine-matrices \
"${INTEGRATION_TEST_MATRIX}" \
"${LOWEST_PYTHON_DEPS_TEST_MATRIX}"
)
ACCEPTANCE_TEST_MATRIX_WIN="{}"
if [[ " ${ACCEPTANCE_PLATFORMS[*]} " =~ [[:space:]]windows-latest[[:space:]] ]]; then
ACCEPTANCE_TEST_MATRIX_WIN=$(
./scripts/get-job-matrix.py \
-vvv \
generate-matrix \
--kind acceptance-test \
"$CODEGEN_TESTS_FLAG" \
--platform windows-latest \
--version-set current \
--partition-module pkg 2 \
--partition-module sdk 1 \
--partition-module tests 3 \
--partition-package github.com/pulumi/pulumi/tests/smoke tests/smoke 4 \
--partition-package github.com/pulumi/pulumi/tests/integration tests/integration 8
)
fi
ACCEPTANCE_TEST_MATRIX_MACOS="{}"
if [[ " ${ACCEPTANCE_PLATFORMS[*]} " =~ [[:space:]]macos-latest[[:space:]] ]]; then
ACCEPTANCE_TEST_MATRIX_MACOS=$(
./scripts/get-job-matrix.py \
-vvv \
generate-matrix \
--kind acceptance-test \
"$CODEGEN_TESTS_FLAG" \
--platform macos-latest \
--version-set current \
--partition-module pkg 1 \
--partition-module sdk 1 \
--partition-module tests 4 \
--partition-package github.com/pulumi/pulumi/tests/smoke tests/smoke 4 \
--partition-package github.com/pulumi/pulumi/tests/integration tests/integration 8
)
fi
echo "::endgroup::"
echo "::group::Version set variable"
VERSION_SET=$(./scripts/get-job-matrix.py \
generate-version-set \
--version-set current
)
echo "::endgroup::"
echo "::group::Unit test matrix"
echo "$UNIT_TEST_MATRIX" | yq -P '.'
echo "::endgroup::"
echo "::group::Integration test matrix"
echo "$INTEGRATION_TEST_MATRIX" | yq -P '.'
echo "::endgroup::"
echo "::group::acceptance test matrix windows"
echo "$ACCEPTANCE_TEST_MATRIX_WIN" | yq -P '.'
echo "::endgroup::"
echo "::group::acceptance test matrix macos"
echo "$ACCEPTANCE_TEST_MATRIX_MACOS" | yq -P '.'
echo "::endgroup::"
echo "::group::Version set"
echo "$VERSION_SET" | yq -P '.'
echo "::endgroup::"
echo "::group::Set outputs"
./.github/scripts/set-output unit-test-matrix "${UNIT_TEST_MATRIX}"
./.github/scripts/set-output integration-test-matrix "${INTEGRATION_TEST_MATRIX}"
./.github/scripts/set-output acceptance-test-matrix-win "${ACCEPTANCE_TEST_MATRIX_WIN}"
./.github/scripts/set-output acceptance-test-matrix-macos "${ACCEPTANCE_TEST_MATRIX_MACOS}"
./.github/scripts/set-output version-set "${VERSION_SET}"
./.github/scripts/set-output build-targets "${BUILD_TARGETS}"
echo "::endgroup::"
outputs:
unit-test-matrix: "${{ fromJson(steps.matrix.outputs.unit-test-matrix) }}"
integration-test-matrix: "${{ fromJson(steps.matrix.outputs.integration-test-matrix) }}"
acceptance-test-matrix-win: "${{ fromJson(steps.matrix.outputs.acceptance-test-matrix-win) }}"
acceptance-test-matrix-macos: "${{ fromJson(steps.matrix.outputs.acceptance-test-matrix-macos) }}"
version-set: "${{ fromJson(steps.matrix.outputs.version-set) }}"
build-targets: "${{ fromJson(steps.matrix.outputs.build-targets) }}"
lint:
name: Lint
needs: [matrix]
if: ${{ inputs.lint }}
uses: ./.github/workflows/ci-lint.yml
strategy:
fail-fast: ${{ inputs.fail-fast }}
with:
ref: ${{ inputs.ref }}
version-set: ${{ needs.matrix.outputs.version-set }}
build-binaries:
name: build binaries
needs: [matrix]
strategy:
fail-fast: ${{ inputs.fail-fast }}
matrix:
target: ${{ fromJson(needs.matrix.outputs.build-targets) }}
uses: ./.github/workflows/ci-build-binaries.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
os: ${{ matrix.target.os }}
arch: ${{ matrix.target.arch }}
build-platform: ${{ matrix.target.build-platform }}
version-set: ${{ needs.matrix.outputs.version-set }}
enable-coverage: ${{ inputs.enable-coverage }}
# Windows and Linux need CGO and cross compile support to support -race. So for now only enable it for darwin and amd64 linux.
enable-race-detection: ${{ matrix.target.os == 'darwin' || (matrix.target.os == 'linux' && matrix.target.arch == 'amd64') }}
artifact-suffix: '-integration'
secrets: inherit
build-display-wasm-module:
name: build display WASM module
needs: [matrix]
uses: ./.github/workflows/ci-build-binaries.yml
strategy:
fail-fast: ${{ inputs.fail-fast }}
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
os: js
arch: wasm
build-platform: "ubuntu-latest"
version-set: ${{ needs.matrix.outputs.version-set }}
secrets: inherit
build-sdks:
name: Build SDKs
needs: [matrix]
uses: ./.github/workflows/ci-build-sdks.yml
strategy:
fail-fast: ${{ inputs.fail-fast }}
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
version-set: ${{ needs.matrix.outputs.version-set }}
secrets: inherit
# Tests that can run concurrently with builds.
unit-test:
# By putting a variable in the name, we remove GitHub's auto-generated matrix parameters from
# appearing in the rendered title of the job name: It changes this:
# CI / Unit Test (cd sdk/dotnet && make dotnet_test, cd sdk/dotnet && make dotnet_test, macos-11, mi... / sdk/dotnet dotnet_test on macos-11/current
# (See: https://github.com/pulumi/pulumi/runs/8241055084?check_suite_focus=true#logs)
# To this:
# CI / Unit Test / sdk/dotnet dotnet_test on macos-11/current
name: Unit Test${{ matrix.platform && '' }}
needs: [matrix]
if: ${{ needs.matrix.outputs.unit-test-matrix != '{}' }}
strategy:
fail-fast: ${{ inputs.fail-fast }}
matrix: ${{ fromJson(needs.matrix.outputs.unit-test-matrix) }}
uses: ./.github/workflows/ci-run-test.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
platform: ${{ matrix.platform }}
test-name: ${{ matrix.test-suite.name || matrix.test-suite.command }} on ${{ matrix.platform }}/${{ matrix.version-set.name }}
test-command: ${{ matrix.test-suite.command }}
is-integration-test: false
enable-coverage: ${{ inputs.enable-coverage }}
test-retries: ${{ inputs.test-retries }}
# require-build: false # TODO, remove ${{ matrix.require-build || false }}
version-set: ${{ toJson(matrix.version-set) }}
secrets: inherit
# Tests that depend on builds
integration-test:
# By putting a variable in the name, we remove GitHub's auto-generated matrix parameters from
# appearing in the rendered title of the job name. See: unit test.
name: Integration Test${{ matrix.platform && '' }}
needs: [matrix, build-binaries, build-sdks]
if: ${{ needs.matrix.outputs.integration-test-matrix != '{}' }}
strategy:
fail-fast: ${{ inputs.fail-fast }}
matrix: ${{ fromJson(needs.matrix.outputs.integration-test-matrix) }}
uses: ./.github/workflows/ci-run-test.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
platform: ${{ matrix.platform }}
test-name: ${{ matrix.test-suite.name || matrix.test-suite.command }} on ${{ matrix.platform }}/${{ matrix.version-set.name }}
test-command: ${{ matrix.test-suite.command }}
is-integration-test: true
enable-coverage: ${{ inputs.enable-coverage }}
test-retries: ${{ inputs.test-retries }}
# require-build: false # TODO, remove ${{ matrix.require-build || false }}
version-set: ${{ toJson(matrix.version-set) }}
secrets: inherit
# Tests that depend on builds, but a smaller subset against Windows platform.
acceptance-test-win:
# By putting a variable in the name, we remove GitHub's auto-generated matrix parameters from
# appearing in the rendered title of the job name. See: unit test.
name: Acceptance Test${{ matrix.platform && '' }}
needs: [matrix, build-binaries, build-sdks]
if: ${{ needs.matrix.outputs.acceptance-test-matrix-win != '{}' }}
# allow jobs to fail if the platform contains windows
strategy:
fail-fast: ${{ inputs.fail-fast }}
matrix: ${{ fromJson(needs.matrix.outputs.acceptance-test-matrix-win) }}
uses: ./.github/workflows/ci-run-test.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
platform: ${{ matrix.platform }}
test-name: ${{ matrix.test-suite.name || matrix.test-suite.command }} on ${{ matrix.platform }}/${{ matrix.version-set.name }}
test-command: ${{ matrix.test-suite.command }}
is-integration-test: true
enable-coverage: false
test-retries: ${{ inputs.test-retries }}
# require-build: false # TODO, remove ${{ matrix.require-build || false }}
version-set: ${{ toJson(matrix.version-set) }}
secrets: inherit
# Tests that depend on builds, but a smaller subset against MacOS platform.
acceptance-test-macos:
# By putting a variable in the name, we remove GitHub's auto-generated matrix parameters from
# appearing in the rendered title of the job name. See: unit test.
name: Acceptance Test${{ matrix.platform && '' }}
needs: [matrix, build-binaries, build-sdks]
if: ${{ needs.matrix.outputs.acceptance-test-matrix-macos != '{}' }}
# allow jobs to fail if the platform contains windows
strategy:
fail-fast: ${{ inputs.fail-fast }}
matrix: ${{ fromJson(needs.matrix.outputs.acceptance-test-matrix-macos) }}
uses: ./.github/workflows/ci-run-test.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
platform: ${{ matrix.platform }}
test-name: ${{ matrix.test-suite.name || matrix.test-suite.command }} on ${{ matrix.platform }}/${{ matrix.version-set.name }}
test-command: ${{ matrix.test-suite.command }}
is-integration-test: true
enable-coverage: false
test-retries: ${{ inputs.test-retries }}
# require-build: false # TODO, remove ${{ matrix.require-build || false }}
version-set: ${{ toJson(matrix.version-set) }}
secrets: inherit
fuzz-test:
name: Fuzz Test
uses: ./.github/workflows/ci-run-test.yml
needs: [matrix]
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
platform: ubuntu-latest
test-name: Fuzz test
test-command: make test_lifecycle_fuzz
is-integration-test: false
enable-coverage: false
test-retries: ${{ inputs.test-retries }}
version-set: ${{ needs.matrix.outputs.version-set }}
continue-on-error: ${{ inputs.fuzz-test-optional }}
secrets: inherit
test-collect-reports:
needs: [unit-test, integration-test, acceptance-test-macos]
if: ${{ always() }}
runs-on: ubuntu-latest
steps:
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: test-results
key: gotestsum-timing-${{ github.run_number }}
restore-keys: gotestsum-timing-
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
continue-on-error: true
with:
pattern: gotestsum-test-results-*
merge-multiple: true
path: test-results
- name: List and clean up test results
continue-on-error: true
run: |
ls -lhR test-results
find test-results -mindepth 1 -name '*.json' -mtime +7 -delete
test-collect-coverage:
needs: [unit-test, integration-test]
if: ${{ inputs.enable-coverage }}
runs-on: ubuntu-latest
steps:
# Check that there are no failed tests.
- name: Check tests completed successfully.
run: |
JSON='${{ toJson(needs) }}'
if test -z "$(echo $JSON | jq -r '.[] | .result' | grep -v 'success')"; then
echo "Tests OK!"
else
echo "Test Failure.. skipping CodeCov upload."
exit 1
fi
# Checkout repository to upload coverage results.
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Retrieve code coverage reports
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: coverage-*
merge-multiple: true
path: coverage
# For PRs we need to set the merge base manually, otherwise it is sometimes incorrect. See https://github.com/pulumi/pulumi/pull/17204#issuecomment-2405599310
- name: Set codecov PR base
if: github.event_name == 'pull_request'
run: |
curl -Os https://cli.codecov.io/latest/linux/codecov
sudo chmod +x codecov
./codecov pr-base-picking --base-sha "$(git merge-base origin/master HEAD)" --pr ${{ github.event.number }} --slug pulumi/pulumi --token ${{ secrets.CODECOV_TOKEN }} --service github
- name: Upload code coverage
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
with:
directory: coverage/
files: "*,!.gitkeep"
fail_ci_if_error: false
verbose: true
# TODO: stop pinning to v10.0.1 when https://github.com/codecov/codecov-cli/issues/651 is fixed.
# At that point, we should consider switching to codecov/codecov-action@v5
version: v10.0.1
token: ${{ secrets.CODECOV_TOKEN }}
build-release-binaries:
# This overwrites the previously built testing binaries with versions without coverage.
name: Rebuild binaries
needs: [matrix]
if: ${{ inputs.enable-coverage }}
strategy:
fail-fast: ${{ inputs.fail-fast }}
matrix:
target: ${{ fromJson(needs.matrix.outputs.build-targets) }}
uses: ./.github/workflows/ci-build-binaries.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
os: ${{ matrix.target.os }}
arch: ${{ matrix.target.arch }}
build-platform: ${{ matrix.target.build-platform }}
version-set: ${{ needs.matrix.outputs.version-set }}
enable-coverage: false
secrets: inherit
ci-build-binaries perms .github/workflows/ci-build-binaries.yml
View raw YAML
name: Build Binaries
permissions:
contents: read
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
os:
required: true
description: "Target OS (i.e.: GOOS)"
type: string
arch:
required: true
description: "Target Architecture (i.e.: GOARCH)"
type: string
build-platform:
required: false
default: ubuntu-latest
description: 'Build platform (i.e.: runs-on) for job'
type: string
artifact-suffix:
required: false
description: "Suffix to append to the artifact names"
type: string
dev-version:
required: false
description: "Dev version to bake into the binary"
type: string
version:
required: true
description: "Version to produce"
type: string
version-set:
required: true
description: "Set of language versions to use for builds, lints, releases, etc."
type: string
enable-coverage:
description: "Build coverage instrumented binaries"
default: false
required: false
type: boolean
enable-race-detection:
description: "Build binaries with race detection"
default: false
required: false
type: boolean
defaults:
run:
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
build:
name: ${{ inputs.os }}-${{ inputs.arch }}
runs-on: ${{ inputs.build-platform }}
steps:
- name: "Windows cache workaround"
# https://github.com/actions/cache/issues/752#issuecomment-1222415717
# but only modify the path by adding tar.exe
if: ${{ runner.os == 'Windows' }}
env:
TOOL_BIN: ${{ runner.temp }}/tar-bin
run: |
set -x
mkdir -p "${TOOL_BIN}"
cp "C:/Program Files/Git/usr/bin/tar.exe" "${TOOL_BIN}"
PATH="${TOOL_BIN}:${PATH}"
echo "$TOOL_BIN" | tee -a "$GITHUB_PATH"
command -v tar
tar --version
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Configure Go Cache Key
env:
CACHE_KEY: "${{ fromJson(inputs.version-set).go }}-${{ runner.os }}-${{ runner.arch }}"
run: echo "$CACHE_KEY" > .gocache.tmp
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: ${{ fromJson(inputs.version-set).go }}
check-latest: true
cache: true
cache-dependency-path: |
pkg/go.sum
.gocache.tmp
- name: Setup versioning env vars
run: |
./scripts/versions.sh | tee -a "${GITHUB_ENV}"
- name: Install GoReleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6
with:
install-only: true
distribution: goreleaser-pro
- name: Prepare bin dir for goreleaser
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GOOS: ${{ inputs.os }}
GOARCH: ${{ inputs.arch }}
run: ./scripts/prep-for-goreleaser.sh local
- name: Show files and permissions
if: ${{ runner.os != 'macOS'}}
run: find bin -type f -printf "%M %p/"\\n
- name: Show files and permissions
if: ${{ runner.os == 'macOS'}}
run: |
brew install findutils
gfind bin -type f -printf "%M %p/"\\n
- name: Package
shell: bash
env:
GORELEASER_CURRENT_TAG: v${{ inputs.version }}
PULUMI_VERSION: ${{ inputs.dev-version || inputs.version }}
PULUMI_BUILD_MODE: ${{ inputs.enable-coverage && inputs.os == 'linux' && 'coverage' || 'normal' }}
PULUMI_ENABLE_RACE_DETECTION: ${{ inputs.enable-race-detection && 'true' || 'false' }}
run: |
set -euxo pipefail
# Spurious, this command requires piping via stdin
# shellcheck disable=SC2002
cat .goreleaser.yml \
| go run github.com/t0yv0/goreleaser-filter@v0.3.0 -goos ${{ inputs.os }} -goarch ${{ inputs.arch }} \
| goreleaser release -f - -p 5 --skip=validate --clean --snapshot
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ inputs.os != 'js' }}
with:
name: artifacts-cli-${{ inputs.os }}-${{ inputs.arch }}${{ inputs.artifact-suffix || ''}}
overwrite: true
retention-days: 7
path: |
goreleaser/*.tar.gz
goreleaser/*.zip
ci-build-sdks perms .github/workflows/ci-build-sdks.yml
View raw YAML
name: Build SDKs
permissions:
contents: read
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
version-set:
required: false
description: "Set of language versions to use for builds, lints, releases, etc."
type: string
# Example provided for illustration, this value is derived by scripts/get-job-matrix.py build
default: |
{
"dotnet": "8.0.x",
"go": "1.18.x",
"nodejs": "20.x",
"python": "3.9.x"
}
defaults:
run:
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULUMI_VERSION: ${{ inputs.version }}
jobs:
build_python_sdk:
name: python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Setup versioning env vars
env:
version: ${{ inputs.version }}
run: |
./scripts/versions.sh | tee -a "${GITHUB_ENV}"
- name: Set up uv
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
enable-cache: true
cache-dependency-glob: sdk/python/uv.lock
- name: Set up Python ${{ fromJson(inputs.version-set).python }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ fromJson(inputs.version-set).python }}
- name: Build Pulumi Python SDK wheel
run: |
cd sdk/python
sed -i.bak "s/^version = .*/version = \"${PYPI_VERSION}\"/g" pyproject.toml && rm pyproject.toml.bak
make build_package
- name: Upload pulumi.whl
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: artifacts-python-sdk
path: sdk/python/build/*.whl
retention-days: 1
if-no-files-found: error
build_node_sdk:
name: nodejs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Setup versioning env vars
env:
version: ${{ inputs.version }}
run: |
./scripts/versions.sh | tee -a "${GITHUB_ENV}"
- name: Set up Node ${{ fromJson(inputs.version-set).nodejs }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ fromJson(inputs.version-set).nodejs }}
cache: yarn
cache-dependency-path: sdk/nodejs/yarn.lock
- name: Install yarn
run: |
npm install -g yarn
# TODO something in `cd sdk/nodejs && make ensure` executes Go
# downloads, which is unfortunate and wasteful in this context.
# When this is fixed the no-op Go command can be removed.
- name: Make no-op Go command to avoid Go builds
run: |
cd sdk/nodejs
mkdir -p bin
# shellcheck disable=SC2230 # need to locate echo binary
ln -s "$(which echo)" bin/go
- name: Ensure installed dependencies
run: |
cd sdk/nodejs
PATH=./bin:$PATH make ensure
- name: Build the Node SDK package
run: |
cd sdk/nodejs
PATH=./bin:$PATH make build_package
- name: Pack the Node SDK
run: |
cd sdk/nodejs/bin
npm pack
- name: Upload pulumi-node-sdk.tgz
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: artifacts-nodejs-sdk
path: sdk/nodejs/bin/*.tgz
retention-days: 1
if-no-files-found: error
ci-dev-release matrix perms .github/workflows/ci-dev-release.yml
View raw YAML
name: Create dev release
permissions:
contents: read
# To sign artifacts.
id-token: write
on:
workflow_call:
inputs:
version:
required: true
description: "Version to use for the release"
type: string
ref:
required: true
description: "GitHub ref to use"
type: string
jobs:
gather-info:
name: gather-info
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Git describe
id: ghd
uses: proudust/gh-describe@v1
- name: strip prefix
id: strip-prefix
# Always prefix the short_sha with a letter to ensure it's a valid semver prerelease,
# see https://github.com/pulumi/pulumi/issues/15471 for context.
run: |
short_sha="x${{ steps.ghd.outputs.short-sha }}"
version="${{ inputs.version }}"
version="${version#v}-alpha.${short_sha}"
echo dev-version="$version" >>"${GITHUB_OUTPUT}"
outputs:
dev-version: ${{ steps.strip-prefix.outputs.dev-version }}
version: ${{ inputs.version }}
matrix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
cache: true
cache-dependency-path: pkg/go.sum
go-version-file: pkg/go.mod
- uses: jdx/mise-action@v2
with:
experimental: true
- name: Install CLI
run: SDKS='' make install
- name: build matrix
id: matrix
run: |
echo "::group::Version set variable"
VERSION_SET=$(./scripts/get-job-matrix.py \
generate-version-set \
--version-set current
)
echo "::endgroup::"
echo "::group::Version set"
echo "$VERSION_SET" | yq -P '.'
echo "::endgroup::"
echo "::group::Set outputs"
./.github/scripts/set-output version-set "${VERSION_SET}"
echo "::endgroup::"
outputs:
version-set: "${{ fromJson(steps.matrix.outputs.version-set) }}"
build-release:
name: build-release
needs: [gather-info, matrix]
strategy:
fail-fast: true
matrix:
os: ["linux", "darwin", "windows"]
arch: ["amd64", "arm64"]
build-platform: ["ubuntu-latest"]
uses: ./.github/workflows/ci-build-binaries.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
dev-version: ${{ needs.gather-info.outputs.dev-version }}
os: ${{ matrix.os }}
arch: ${{ matrix.arch }}
build-platform: ${{ matrix.build-platform }}
version-set: ${{ needs.matrix.outputs.version-set }}
enable-coverage: false
secrets: inherit
build-sdks:
name: Build SDKs
needs: [matrix, gather-info]
uses: ./.github/workflows/ci-build-sdks.yml
with:
ref: ${{ inputs.ref }}
version: ${{ needs.gather-info.outputs.dev-version }}
version-set: ${{ needs.matrix.outputs.version-set }}
secrets: inherit
sign:
name: sign
needs: [build-release, build-sdks]
uses: ./.github/workflows/sign.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
# Check if we need to create a new SDK dev release
sdk-check-release:
name: sdk-check-release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Check for changes
id: check-changes
run: |
# We need to fetch one more commit to compare to
git fetch --deepen 1
if ! git diff --exit-code HEAD~...HEAD sdk/nodejs; then
echo "nodejs-release=true" >>"${GITHUB_OUTPUT}"
else
echo "nodejs-release=false" >>"${GITHUB_OUTPUT}"
fi
if ! git diff --exit-code HEAD~...HEAD sdk/python; then
echo "python-release=true" >>"${GITHUB_OUTPUT}"
else
echo "python-release=false" >>"${GITHUB_OUTPUT}"
fi
outputs:
nodejs-release: ${{ steps.check-changes.outputs.nodejs-release }}
python-release: ${{ steps.check-changes.outputs.python-release }}
nodejs-dev-sdk-release:
needs: [gather-info, build-sdks, sdk-check-release, matrix]
runs-on: ubuntu-latest
if: ${{ needs.sdk-check-release.outputs.nodejs-release == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Make artifacts directory
run: |
mkdir -p artifacts.tmp
- name: Download artifacts from previous step
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts.tmp
- name: Move artifacts to the right place
run: |
mkdir -p artifacts
(
cd artifacts.tmp/artifacts-nodejs-sdk
version="${{ inputs.version }}"
for file in *"${version}"-alpha*.tgz ; do
mv -vT "$file" "../../artifacts/sdk-nodejs-${file}"
done
)
- name: Set up Node ${{ fromJson(needs.matrix.outputs.version-set).nodejs }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ fromJson(needs.matrix.outputs.version-set).nodejs }}
registry-url: https://registry.npmjs.org
always-auth: true
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish nodejs release
run: |
find artifacts
make -C sdk/nodejs publish
env:
PULUMI_VERSION: ${{ needs.gather-info.outputs.dev-version }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GIT_REF: ${{ inputs.ref }}
python-dev-sdk-release:
needs: [gather-info, build-sdks, sdk-check-release, matrix]
runs-on: ubuntu-latest
if: ${{ needs.sdk-check-release.outputs.python-release == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Set up Python ${{ fromJson(needs.matrix.outputs.version-set).python }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ fromJson(needs.matrix.outputs.version-set).python }}
- name: Set up uv
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
enable-cache: true
cache-dependency-glob: sdk/python/uv.lock
- name: Make artifacts directory
run: |
mkdir -p artifacts.tmp
- name: Download artifacts from previous step
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts.tmp
- name: Move artifacts to the right place
run: |
mkdir -p artifacts
version="${{ inputs.version }}"
mkdir -p artifacts
(
cd artifacts.tmp/artifacts-python-sdk
for file in *"${version}a"*.whl ; do
mv -vT "$file" "../../artifacts/sdk-python-${file}"
done
)
- name: Publish python release
run: |
find artifacts
make -C sdk/python publish
env:
PYPI_USERNAME: __token__
PYPI_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
s3-blobs:
name: s3 blobs
runs-on: ubuntu-latest
needs: [sign, gather-info]
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-region: us-east-2
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
role-duration-seconds: 3600
role-external-id: upload-pulumi-release
role-session-name: pulumi@githubActions
role-to-assume: ${{ secrets.AWS_UPLOAD_ROLE_ARN }}
- name: Make artifacts directory
run: |
mkdir -p artifacts.tmp
- name: Download artifacts from previous step
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts.tmp
- name: Remove performance test artifacts
run: rm -rf artifacts.tmp/*-perf
- name: Remove integration test artifacts
run: rm -rf artifacts.tmp/*-integration
- name: Flatten artifact directories
run: |
find artifacts.tmp
mkdir -p ./artifacts
mv ./artifacts.tmp/artifacts-signatures/pulumi-*.tar.gz.sig ./artifacts
mv ./artifacts.tmp/artifacts-signatures/pulumi-*.zip.sig ./artifacts
mv ./artifacts.tmp/artifacts-signatures/pulumi-*.txt.sig ./artifacts
mv ./artifacts.tmp/artifacts-signatures/pulumi-*.txt ./artifacts
mv ./artifacts.tmp/artifacts-cli-*/pulumi-*.tar.gz ./artifacts
mv ./artifacts.tmp/artifacts-cli-*/pulumi-*.zip ./artifacts
- name: Find artifacts
run: |
find artifacts
- name: Rename artifacts
shell: bash
run: |
(
cd artifacts
version="${{ inputs.version }}"
dev_version="${{ needs.gather-info.outputs.dev-version }}"
for file in *; do
mv "$file" "${file//$version/$dev_version}"
done
)
- name: Find artifacts
run: |
find artifacts | sort
- name: Publish Blobs
run: |
aws s3 sync artifacts s3://get.pulumi.com/releases/sdk --acl public-read
ci-info perms .github/workflows/ci-info.yml
View raw YAML
name: Info
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
is-snapshot:
required: false
default: true
description: "Is this a snapshot release?"
type: boolean
outputs:
version:
description: "Version to produce"
value: ${{ jobs.info.outputs.version }}
release-notes:
description: "Release notes for CHANGELOG"
value: ${{ jobs.info.outputs.release-notes }}
permissions:
contents: read
defaults:
run:
shell: bash
jobs:
info:
name: gather
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version: "${{ fromJSON(steps.version.outputs.version) }}"
release-notes: "${{ fromJSON(steps.notes.outputs.release-notes) }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
fetch-depth: 0
- name: Print rate limits
continue-on-error: true
run: gh api -i repos/${{ github.repository }}/releases/latest
- name: Compute version
id: version
env:
IS_SNAPSHOT: ${{ inputs.is-snapshot }}
run: |
PLAIN_VERSION="$(./.github/scripts/get-version)"
PULUMI_VERSION="${PLAIN_VERSION}"
if [ "$IS_SNAPSHOT" = "true" ]; then
TIMESTAMP=$(date +%s)
PULUMI_VERSION="${PULUMI_VERSION%-*}-alpha.$TIMESTAMP"
fi
./.github/scripts/set-output version "${PULUMI_VERSION}"
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: '>=1.19.0' # decoupled from version sets, used by changelog tool
cache: true
cache-dependency-path: '.github/scripts/get-changelog'
- name: Extract release notes
id: notes
run: |
PREVIOUS_VERSION="$(./.github/scripts/get-previous-version)"
CHANGELOG="$(./.github/scripts/get-changelog "${PREVIOUS_VERSION}" --version "${{ fromJSON(steps.version.outputs.version) }}")"
./.github/scripts/set-output release-notes "${CHANGELOG}"
- name: Check version
if: ${{ !inputs.is-snapshot }}
run: |
PULUMI_VERSION="${{ fromJSON(steps.version.outputs.version) }}"
./.github/scripts/update-versions "${PULUMI_VERSION}"
ERROR=false
if [ -n "$(git status --porcelain)" ]; then
ERROR=true
echo "::error::Versions in files do not match expected version ${PULUMI_VERSION}."
echo "::group::git diff"
git diff
echo "::endgroup::"
fi
if EXISTING_RELEASE_DRAFT="$(gh release view "v${PULUMI_VERSION}" --json isDraft --jq '.isDraft')"; then
if [ "$EXISTING_RELEASE_DRAFT" != "true" ]; then
echo "::error::This version has already been released!"
echo "::group::Release ${PULUMI_VERSION}"
echo "$EXISTING_RELEASE"
echo "::endgroup::"
else
echo "::info Preparing to update draft release ${PULUMI_VERSION}"
fi
fi
if $ERROR; then
exit 1;
fi
ci-lint perms .github/workflows/ci-lint.yml
View raw YAML
name: Lint
permissions:
contents: read
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version-set:
required: true
description: "Version matrix to use"
type: string
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GOLANGCI_LINT_VERSION: v2.9.0
REQUIREDFIELD_VERSION: v0.8.0
jobs:
golangci:
name: Lint Go
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: ${{ fromJson(inputs.version-set).go }}
check-latest: true
# golangci-lint-action handles all the caching for Go.
cache: false
# We leverage the golangci-lint action to install
# and maintain the cache,
# but we want to run the command ourselves.
# The action doesn't have an install-only mode,
# so we'll ask it to print its help output instead.
- name: Install golangci-lint
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8
with:
version: ${{ env.GOLANGCI_LINT_VERSION }}
args: --help
- name: Install requiredfield
run: go install go.abhg.dev/requiredfield/cmd/requiredfield@${{ env.REQUIREDFIELD_VERSION }}
- name: Run golangci-lint
run: make lint_golang
tidy:
name: go mod tidy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: ${{ fromJson(inputs.version-set).go }}
check-latest: true
- name: Run go mod tidy
run: make tidy
gen:
name: Generate Go SDK
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: ${{ fromJson(inputs.version-set).go }}
check-latest: true
- name: Run make gen
run: cd sdk/go && make gen
- name: Fail if there are changes
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "::error Running 'make gen' in sdk/go resulted in changes"
echo "::error Please run 'make gen' in sdk/go and commit the changes"
exit 1
fi
protobuf-lint:
name: Lint Protobufs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- uses: jdx/mise-action@v2
with:
experimental: true
- name: Check Protobufs
run: make check_proto
sdk-lint:
name: Lint SDKs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: ${{ fromJson(inputs.version-set).go }}
check-latest: true
- name: Set up uv
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
enable-cache: true
cache-dependency-glob: sdk/python/uv.lock
- name: Set up Python ${{ fromJson(inputs.version-set).python }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ fromJson(inputs.version-set).python }}
- name: Set up Node ${{ fromJson(inputs.version-set).nodejs }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ fromJson(inputs.version-set).nodejs }}
cache: yarn
cache-dependency-path: sdk/nodejs/yarn.lock
- name: Install Python deps
run: |
python -m pip install --upgrade pip requests wheel urllib3 chardet
- name: Setup git
run: |
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
- name: Update path
run: |
echo "$RUNNER_TEMP/opt/pulumi/bin" >> "$GITHUB_PATH"
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Set Go Dep path
run: |
echo "PULUMI_GO_DEP_ROOT=$(dirname "$(pwd)")" >> "$GITHUB_ENV"
- name: Ensure
run: |
make ensure
- name: Lint Node
run: |
cd sdk/nodejs && make lint
- name: Lint Python
run: |
cd sdk/python && make lint
actionlint:
name: Lint GHA
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: ${{ fromJson(inputs.version-set).go }}
check-latest: true
- run: |
make lint_actions
pulumi-json:
name: Lint pulumi.json
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: ${{ fromJson(inputs.version-set).go }}
check-latest: true
- name: Set up Node ${{ fromJson(inputs.version-set).nodejs }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ fromJson(inputs.version-set).nodejs }}
cache: yarn
cache-dependency-path: sdk/nodejs/yarn.lock
- name: Ensure Node # We need to install Node.js deps so that biome is available
run: |
cd sdk/nodejs && make ensure
- run: |
make lint_pulumi_json
ci-performance-gate matrix perms .github/workflows/ci-performance-gate.yml
View raw YAML
# This workflow runs performance tests to ensure we do not introduce performance
# regressions. This runs for every PR, as well as on every merge to the master
# branch.
#
# The Pulumi CLI binaries used in this workflow are built without race detection
# or coverage instrumentation to ensure that the performance tests are not
# affected by these flags.
name: Performance Gate
permissions:
contents: read
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
test-version-sets:
required: false
default: minimum current
description: Version sets on which to run integration tests
type: string
performance-test-platforms:
required: false
default: ubuntu-latest
description: Platforms on which to run performance tests, as a space delimited list
type: string
fail-fast:
required: false
default: false
description: "Fail all workflows whenever one of them fails"
type: boolean
test-retries:
required: false
default: 0
description: "Retry tests n times if there are failures"
type: number
secrets:
PULUMI_PROD_ACCESS_TOKEN:
required: false
description: "Pulumi access token, required to run tests against the service"
AZURE_TENANT_ID:
required: false
description: "Azure tenant ID, required to run tests against Azure"
AZURE_CLIENT_ID:
required: false
description: "Azure client ID, required to run tests against Azure"
AZURE_CLIENT_SECRET:
required: false
description: "Azure clients secret, needs to be rotated before 2025-12-21 (see the pulumi-test user in Azure portal)"
AZURE_STORAGE_SAS_TOKEN:
required: false
description: "Azure storage SAS token, required to run tests against Azure"
GCP_SERVICE_ACCOUNT:
required: false
description: "GCP service account, required to run tests against GCP"
jobs:
matrix:
runs-on: ubuntu-latest
strategy:
fail-fast: ${{ inputs.fail-fast }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
cache: true
cache-dependency-path: pkg/go.sum
go-version-file: pkg/go.mod
- uses: jdx/mise-action@v2
with:
experimental: true
- name: Install CLI
run: SDKS='' make install
- name: build matrix
id: matrix
env:
TEST_VERSION_SETS: ${{ inputs.test-version-sets }}
INPUT_PERFORMANCE_TEST_PLATFORMS: ${{ inputs.performance-test-platforms }}
run: |
echo "::group::Test matrix variables"
readarray -td' ' VERSION_SETS_TO_TEST < <(echo -n "$TEST_VERSION_SETS"); declare -p VERSION_SETS_TO_TEST;
readarray -td' ' PERFORMANCE_TEST_PLATFORMS < <(echo -n "$INPUT_PERFORMANCE_TEST_PLATFORMS"); declare -p PERFORMANCE_TEST_PLATFORMS;
BUILD_TARGETS='[
{ "os": "linux", "arch": "amd64", "build-platform": "ubuntu-latest" },
{ "os": "windows", "arch": "amd64", "build-platform": "ubuntu-latest" },
{ "os": "darwin", "arch": "arm64", "build-platform": "ubuntu-latest" }
]'
PERFORMANCE_TEST_MATRIX=$(
./scripts/get-job-matrix.py \
-vvv \
generate-matrix \
--kind performance-test \
--platform "${PERFORMANCE_TEST_PLATFORMS[@]}" \
--version-set "${VERSION_SETS_TO_TEST[@]}"
)
echo "::endgroup::"
echo "::group::Version set variable"
VERSION_SET=$(./scripts/get-job-matrix.py \
generate-version-set \
--version-set current
)
echo "::endgroup::"
echo "::group::Performance test matrix"
echo "$PERFORMANCE_TEST_MATRIX" | yq -P '.'
echo "::endgroup::"
echo "::group::Version set"
echo "$VERSION_SET" | yq -P '.'
echo "::endgroup::"
echo "::group::Set outputs"
./.github/scripts/set-output performance-test-matrix "${PERFORMANCE_TEST_MATRIX}"
./.github/scripts/set-output version-set "${VERSION_SET}"
./.github/scripts/set-output build-targets "${BUILD_TARGETS}"
echo "::endgroup::"
outputs:
performance-test-matrix: "${{ fromJson(steps.matrix.outputs.performance-test-matrix) }}"
version-set: "${{ fromJson(steps.matrix.outputs.version-set) }}"
build-targets: "${{ fromJson(steps.matrix.outputs.build-targets) }}"
build-binaries:
name: build binaries
needs: [matrix]
strategy:
fail-fast: ${{ inputs.fail-fast }}
matrix:
target: ${{ fromJson(needs.matrix.outputs.build-targets) }}
uses: ./.github/workflows/ci-build-binaries.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
os: ${{ matrix.target.os }}
arch: ${{ matrix.target.arch }}
build-platform: ${{ matrix.target.build-platform }}
version-set: ${{ needs.matrix.outputs.version-set }}
# For performance tests, we do not want coverage or race detection enabled.
enable-coverage: false
enable-race-detection: false
# Suffix the artifacts so they do not clash with those of the main build.
artifact-suffix: '-perf'
secrets: inherit
performance-test:
# By putting a variable in the name, we remove GitHub's auto-generated matrix parameters from
# appearing in the rendered title of the job name.
name: Performance Test${{ matrix.platform && '' }}
needs: [matrix, build-binaries]
strategy:
fail-fast: ${{ inputs.fail-fast }}
matrix: ${{ fromJson(needs.matrix.outputs.performance-test-matrix) }}
uses: ./.github/workflows/ci-run-test.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
platform: ${{ matrix.platform }}
test-name: ${{ matrix.test-suite.name || matrix.test-suite.command }} on ${{ matrix.platform }}/${{ matrix.version-set.name }}
test-command: ${{ matrix.test-suite.command }}
is-performance-test: true
enable-coverage: false
test-retries: ${{ inputs.test-retries }}
version-set: ${{ toJson(matrix.version-set) }}
secrets: inherit
ci-prepare-release perms .github/workflows/ci-prepare-release.yml
View raw YAML
name: Prepare
permissions:
# To create a draft release
contents: write
# To sign artifacts.
id-token: write
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
project:
required: true
description: "Project name, e.g.: the repository name"
type: string
release-notes:
required: true
description: "Release notes"
type: string
prerelease:
required: false
default: true
description: "Whether to create a prerelease"
type: boolean
draft:
required: false
default: true
description: "Whether to create a draft release"
type: boolean
env:
PULUMI_VERSION: ${{ inputs.version }}
jobs:
sign:
name: sign
uses: ./.github/workflows/sign.yml
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
publish:
name: release
needs: [sign]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Get commit hash
id: commit-info
run: |
SHA=$(git rev-parse HEAD)
./.github/scripts/set-output sha "$SHA"
- name: Download all artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts.tmp
- name: Remove performance test artifacts
run: rm -rf artifacts.tmp/*-perf
- name: Remove integration test artifacts
run: rm -rf artifacts.tmp/*-integration
- name: Rename SDKs
# This step must match the rename SDKs step in the "sign" job above.
run: |
(
cd artifacts.tmp/artifacts-python-sdk
for file in *.whl ; do
mv -vT "$file" "sdk-python-$file"
done
)
(
cd artifacts.tmp/artifacts-nodejs-sdk
for file in *.tgz ; do
mv -vT "$file" "sdk-nodejs-$file"
done
)
- name: Flatten artifact directories
run: |
mkdir -p ./artifacts
mv ./artifacts.tmp/artifacts-*/* ./artifacts
- uses: ncipollo/release-action@3d2de22e3d0beab188d8129c27f103d8e91bf13a
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: v${{ inputs.version }}
tag: v${{ inputs.version }}
commit: "${{ fromJSON(steps.commit-info.outputs.sha) }}"
draft: ${{ inputs.draft }}
prerelease: ${{ inputs.prerelease }}
allowUpdates: true
body: |
${{ inputs.release-notes }}
removeArtifacts: true
replacesArtifacts: true
artifactErrorsFailBuild: true
artifacts: |
artifacts/*
ci-run-test perms .github/workflows/ci-run-test.yml
View raw YAML
name: Test
permissions:
contents: read
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
platform:
description: "OS to run tests on, e.g.: ubuntu-latest"
required: true
type: string
test-name:
description: "Name of the test to run"
required: true
type: string
test-command:
description: Test command to run
required: true
type: string
is-integration-test:
description: Whether to download and install build artifacts
required: false
default: false
type: boolean
is-performance-test:
description: Whether to download and install build artifacts for performance tests
required: false
default: false
type: boolean
enable-coverage:
description: "Collects coverage stats; requires cov-enabled builds"
default: false
required: false
type: boolean
test-retries:
description: "How often tests are retried"
default: 0
required: false
type: number
version-set:
required: false
description: "Set of language versions to use for builds, lints, releases, etc."
type: string
# Example provided for illustration, this value is derived by scripts/get-job-matrix.py build
default: |
{
"dotnet": "8.0.x",
"go": "1.18.x",
"nodejs": "20.x",
"python": "3.9.x"
}
continue-on-error:
description: "Whether to continue running the job if the step fails"
required: false
default: false
type: boolean
defaults:
run:
shell: bash
env:
PULUMI_VERSION: ${{ inputs.version }}
PULUMI_TEST_OWNER: "moolumi"
PULUMI_TEST_ORG: "moolumi"
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_PROD_ACCESS_TOKEN }}
# Release builds use the service, PR checks and snapshots will use the local backend if possible.
PULUMI_TEST_USE_SERVICE: ${{ !contains(inputs.version, '-') }}
# We're hitting a lot of github limits because of deploytest trying to auto install plugins, skip that for now.
PULUMI_DISABLE_AUTOMATIC_PLUGIN_ACQUISITION: "true"
PYTHON: python
GO_TEST_PARALLELISM: 8
GO_TEST_PKG_PARALLELISM: 2
GO_TEST_SHUFFLE: off
PULUMI_TEST_RETRIES: ${{ inputs.test-retries }}
DOTNET_CLI_TELEMETRY_OPTOUT: "true"
DOTNET_ROLL_FORWARD: "Major"
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 10
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
jobs:
test:
name: ${{ inputs.test-name }}
env:
PULUMI_HOME: ${{ github.workspace }}/home
TEST_ALL_DEPS: ""
# Point $TMP and $TEMP to the value of `runner.temp` on Windows. This ensures that our Go tests which use
# os.MkdirTemp create their test environments in this directory. This directory is on the D: drive, as opposed to
# the C: drive for the default temp directory, and IO seems to be massively faster on that drive.
# Note: Windows will first use $TMP, then fallback to $TEMP. We set both here for consistency.
# Similarly, we set GOPATH and GOCACHE to be on the D: drive, so ProgramTest with the Go language runtime will
# also run on the D: drive, see pkg/testing/integration/program.go#L1990.
TMP: ${{ contains(inputs.platform, 'windows') && 'D:/a/_temp' || '' }}
TEMP: ${{ contains(inputs.platform, 'windows') && 'D:/a/_temp' || '' }}
GOPATH: ${{ contains(inputs.platform, 'windows') && 'D:/go' || '' }}
GOCACHE: ${{ contains(inputs.platform, 'windows') && 'D:/go-build-cache' || '' }}
runs-on: ${{ inputs.platform }}
timeout-minutes: 70
continue-on-error: ${{ inputs.continue-on-error }}
steps:
- if: contains(inputs.platform, 'ubuntu')
name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
tool-cache: false
swap-storage: false
large-packages: false
dotnet: false
# Create a coverage file to register a testing job was started.
# This artifact will be overwritten with "OK" at the end of the job.
- name: Write test started file
shell: bash
run: |
HASH=$(echo '${{ inputs.test-name }}-${{ inputs.is-integration-test && 'integration' || 'unit' }}' | git hash-object --literally --stdin | awk '{ print $1 }')
echo "TEST_HASH_ID=${HASH}" >> "$GITHUB_ENV"
- name: "Windows cache workaround"
# https://github.com/actions/cache/issues/752#issuecomment-1222415717
# but only modify the path by adding tar.exe
if: ${{ runner.os == 'Windows' }}
env:
TOOL_BIN: ${{ runner.temp }}/tar-bin
run: |
set -x
mkdir -p "${TOOL_BIN}"
cp "C:/Program Files/Git/usr/bin/tar.exe" "${TOOL_BIN}"
PATH="${TOOL_BIN}:${PATH}"
echo "$TOOL_BIN" | tee -a "$GITHUB_PATH"
command -v tar
tar --version
- name: Reduce Windows test parallelism
if: ${{ runner.os == 'Windows' }}
run: |
{
echo "GO_TEST_PARALLELISM=4"
echo "GO_TEST_PKG_PARALLELISM=1"
echo "GO_TEST_RACE=false"
} >> "$GITHUB_ENV"
# For debugging:
ps aux
- name: Install tsc on windows and macos
if: ${{ runner.os == 'windows' || runner.os == 'macOS' }}
run: |
npm install typescript -g
- name: "macOS use coreutils"
if: ${{ runner.os == 'macOS' }}
run: |
set -x
brew install coreutils
echo "/usr/local/opt/coreutils/libexec/gnubin" | tee -a "$GITHUB_PATH"
command -v bash
bash --version
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Setup versioning env vars
env:
version: ${{ inputs.version }}
run: |
./scripts/versions.sh | tee -a "$GITHUB_ENV"
- name: Enable code coverage
if: ${{ inputs.enable-coverage }}
run: |
echo "PULUMI_TEST_COVERAGE_PATH=$(pwd)/coverage" >> "$GITHUB_ENV"
# Post integration test coverage to a temporary directory.
# This will be merged at a later step. Note that we can't
# use $GOCOVERDIR here, because Go overwrites that env
# variable when the '-cover' flag is used. Use
# $PULUMI_GOCOVERDIR instead, which the integration tests
# then turn into $GOCOVERDIR. See also https://github.com/golang/go/issues/66225.
#
# Also set `GOCOVERDIR` for tests that don't use `go test` (e.g.
# automation API tests)
coverdir="$(mktemp -d)"
echo "PULUMI_GOCOVERDIR=$coverdir" >> "$GITHUB_ENV"
echo "GOCOVERDIR=$coverdir" >> "$GITHUB_ENV"
- name: Configure Go Cache Key
env:
CACHE_KEY: "${{ fromJson(inputs.version-set).go }}-${{ runner.os }}-${{ runner.arch }}"
run: echo "$CACHE_KEY" > .gocache.tmp
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
id: setup-go
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 2
with:
go-version: ${{ fromJson(inputs.version-set).go }}
check-latest: true
cache: true
cache-dependency-path: |
pkg/go.sum
.gocache.tmp
- name: Prime Go cache
if: ${{ ! steps.setup-go.outputs.cache-hit }}
# Compile every test to ensure we populate a good cache entry.
run: |
( cd sdk && go test -run "_________" ./... )
( cd pkg && go test -run "_________" ./... )
- name: Install delve
run: |
go install github.com/go-delve/delve/cmd/dlv@latest
- name: Set up uv
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
enable-cache: true
cache-dependency-glob: sdk/python/uv.lock
- name: Set up Python ${{ fromJson(inputs.version-set).python }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ fromJson(inputs.version-set).python }}
- name: Set up DotNet ${{ fromJson(inputs.version-set).dotnet }}
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
dotnet-version: ${{ fromJson(inputs.version-set).dotnet }}
dotnet-quality: ga
- name: Set up Node ${{ fromJson(inputs.version-set).nodejs }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ fromJson(inputs.version-set).nodejs }}
cache: yarn
cache-dependency-path: sdk/nodejs/yarn.lock
- name: Set up JDK 11
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
with:
java-version: '11'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
with:
gradle-version: "7.6"
- name: Uninstall pre-installed Pulumi (windows)
if: inputs.platform == 'windows-latest'
run: |
if command -v pulumi.exe; then
echo "Deleting pulumi"
rm -rf "$(command -v pulumi.exe)/../pulumi*"
fi
- name: Install yarn and pnpm
run: |
npm install -g yarn pnpm
- name: Install bun
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2
- name: Install Python deps
run: |
python -m pip install --upgrade pip requests wheel urllib3 chardet build
- name: Install Poetry
run: python -m pip install poetry
- name: Setup git
run: |
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
- name: Set Go Dep path
run: |
echo "PULUMI_GO_DEP_ROOT=$(dirname "$(pwd)")" | tee -a "${GITHUB_ENV}"
- name: Install gotestsum
uses: jaxxstorm/action-install-gh-release@71d17cb091aa850acb2a1a4cf87258d183eb941b # v1.11.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
repo: gotestyourself/gotestsum
tag: v1.8.1
cache: enable
- name: Install goteststats
uses: jaxxstorm/action-install-gh-release@71d17cb091aa850acb2a1a4cf87258d183eb941b # v1.11.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
repo: pulumi/goteststats
tag: v0.0.7
cache: enable
- name: Generate artifact name
if: ${{ inputs.is-integration-test || inputs.is-performance-test }}
id: goenv
shell: bash
run: |
echo "CLI-TARGET=$(go env GOOS)-$(go env GOARCH)" >> "${GITHUB_OUTPUT}"
# Ensure tests do not rely on pre-installed packages in CI. Unit tests must run absent a local
# Pulumi install, and integration tests must only test the version built by CI.
- name: Remove Pre-installed Pulumi
env:
RUNNER_OS: ${{ runner.os }}
run: |
EXT=""
if [ "$RUNNER_OS" == "Windows" ]; then
EXT=".exe"
fi
if command -v "pulumi${EXT}"; then
PULUMI_INSTALL_DIR=$(dirname "$(command -v "pulumi${EXT}")")
echo "Deleting Pulumi"
rm -v "$PULUMI_INSTALL_DIR"/pulumi*
fi
# Integration and performance test only steps:
- name: Download pulumi-${{ steps.goenv.outputs.cli-target }}
if: ${{ inputs.is-integration-test }}
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: artifacts-cli-${{ steps.goenv.outputs.cli-target }}-integration
path: artifacts/cli
- name: Download pulumi-${{ steps.goenv.outputs.cli-target }}
if: ${{ inputs.is-performance-test }}
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: artifacts-cli-${{ steps.goenv.outputs.cli-target }}-perf
path: artifacts/cli
- name: Install Pulumi Go Binaries
if: ${{ inputs.is-integration-test || inputs.is-performance-test }}
run: |
echo "Checking contents of $PWD/artifacts/cli"
find "$PWD/artifacts/cli"
TMPDIR="$(mktemp -d)"
mkdir -p bin
# Extract files to temporary directory:
find "$PWD/artifacts/cli" -name '*.zip' -print0 -exec unzip {} -d "$TMPDIR" \;
find "$PWD/artifacts/cli" -name '*.tar.gz' -print0 -exec tar -xzvf {} -C "$TMPDIR" \;
# Windows .zip files have an extra "bin" path part, support both:
if [ -d "$TMPDIR/pulumi/bin" ]; then
mv "${TMPDIR}/pulumi/bin/"* "$PWD/bin/"
else
mv "${TMPDIR}/pulumi/"* "$PWD/bin/"
fi
echo "Checking contents of $PWD/bin"
find "$PWD/bin"
LOCAL_PATH=$(./scripts/normpath "${{ github.workspace }}/bin")
echo "Adding LOCAL_PATH=$LOCAL_PATH to PATH"
echo "$LOCAL_PATH" >> "$GITHUB_PATH"
# /end integration and performance test steps
- name: Verify Pulumi Version
run: |
command -v pulumi || echo "no pulumi"
pulumi version || echo "no pulumi"
- name: Ensure dependencies for the Node SDK
run: |
cd sdk/nodejs
make ensure
- name: Build the Node SDK
run: |
cd sdk/nodejs
make build_package
cd bin
yarn link
bun link
- name: Ensure dependencies for the Python SDK
run: |
cd sdk/python
make ensure
- name: Install Python SDK
run: |
cd sdk/python
make build_package
- name: Set PULUMI_ACCEPT if version-set is not current
if: ${{ fromJson(inputs.version-set).name != 'current' }}
run: echo "PULUMI_ACCEPT=TRUE" >> "${GITHUB_ENV}"
- name: Create GCP service account key file
run: |
echo -n '${{ secrets.GCP_SERVICE_ACCOUNT }}' > '${{ runner.temp }}/application_default_credentials.json'
- name: run tests "${{ inputs.test-command }}"
id: test
run: ${{ inputs.test-command }}
continue-on-error: ${{ inputs.continue-on-error }}
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_STORAGE_SAS_TOKEN: ${{ secrets.AZURE_STORAGE_SAS_TOKEN }}
PULUMI_NODE_MODULES: ${{ runner.temp }}/opt/pulumi/node_modules
PULUMI_ROOT: ${{ runner.temp }}/opt/pulumi
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ runner.temp }}/application_default_credentials.json
AWS_ACCESS_KEY: ${{ secrets.AWS_CI_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_CI_ACCESS_SECRET }}
DOTNET_VERSION: ${{ fromJson(inputs.version-set).dotnet }}
- name: Convert Node coverage data
if: ${{ inputs.platform != 'windows-latest' }}
run: |
cd sdk/nodejs
if [ -e .nyc_output ]; then
UUID=$(python -c "import uuid; print(str(uuid.uuid4()).replace('-', '').lower())")
COVERAGE_PATH="${PULUMI_TEST_COVERAGE_PATH:-./coverage}"
yarn run nyc report -r cobertura --report-dir "$COVERAGE_PATH"
mv "$COVERAGE_PATH/cobertura-coverage.xml" "$COVERAGE_PATH/cobertura-coverage-$UUID.xml"
fi
- name: Merge integration test code coverage
if: ${{ inputs.enable-coverage }}
run: |
# Merge coverage data from coverage-instrumented binaries
# if available.
if [ -n "$(ls -A "$PULUMI_GOCOVERDIR")" ]; then
# Cross-platform way to get milliseconds since Unix epoch.
UUID=$(python -c "import uuid; print(str(uuid.uuid4()).replace('-', '').lower())")
# First merge coverage data to resolve incompatibilities between different coverage files
MERGED_DIR=$(mktemp -d)
if go tool covdata merge -i="$PULUMI_GOCOVERDIR" -o="$MERGED_DIR" 2>/dev/null; then
# If merge succeeds, convert merged data to text format
go tool covdata textfmt -i="$MERGED_DIR" -o="$PULUMI_TEST_COVERAGE_PATH/integration-$UUID.cov"
else
# If merge fails (e.g., incompatible coverage data), try direct conversion
# and if that also fails, skip coverage collection for this run
go tool covdata textfmt -i="$PULUMI_GOCOVERDIR" -o="$PULUMI_TEST_COVERAGE_PATH/integration-$UUID.cov" 2>/dev/null
fi
rm -rf "$MERGED_DIR"
fi
- name: Upload code coverage
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ inputs.enable-coverage }}
with:
name: coverage-${{ env.TEST_HASH_ID }}
path: |
coverage/*
!coverage/.gitkeep
- name: Upload codecov test results
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1
with:
directory: junit/
fail_ci_if_error: false
override_branch: ${{ github.event_name == 'merge_group' && 'master' || github.head_ref }}
verbose: true
token: ${{ secrets.CODECOV_TOKEN }}
- name: Summarize Test Time by Package
continue-on-error: true
env:
RUNNER_OS: ${{ runner.os }}
run: |
mkdir -p test-results
touch test-results/empty.json # otherwise goteststats fails below when no files match
# Remove output lines, they make analysis slower & could leak logs:
if [ "$RUNNER_OS" == "macOS" ]; then
# It's just another case of BSD sed, GNU sed.
sed -i '' -e '/"Action":"output"/d' ./test-results/*.json
else
sed -i'' -e '/"Action":"output"/d' ./test-results/*.json
fi
goteststats -statistic pkg-time test-results/*.json
- name: Summarize Test Times by Individual Test
continue-on-error: true
run: |
goteststats -statistic test-time test-results/*.json | head -n 100
exit_status=$?
# Ignore SIGPIPE (exit status 141)
if [[ exit_status -eq 0 || exit_status -eq 141 ]]; then true; else exit $exit_status; fi
- name: Upload artifacts
if: ${{ always() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: gotestsum-test-results-${{ env.TEST_HASH_ID }}
path: test-results/*.json
command-dispatch .github/workflows/command-dispatch.yml
View raw YAML
name: Command Dispatch for PR events
on:
issue_comment:
types: [created]
# Enables maintainers with "write" permission to trigger jobs on external pull requests.
jobs:
command-dispatch:
runs-on: ubuntu-latest
steps:
- name: Dispatch command
uses: peter-evans/slash-command-dispatch@40877f718dce0101edfc7aea2b3800cc192f9ed5 # v2
with:
token: ${{ secrets.PULUMI_BOT_TOKEN }}
reaction-token: ${{ secrets.GITHUB_TOKEN }}
permission: write
issue-type: pull-request
repository: pulumi/pulumi
commands: |
run-acceptance-tests
run-docs-gen
run-codegen
cron-direct-build .github/workflows/cron-direct-build.yml
View raw YAML
name: Direct build
on:
schedule:
# 1030 UTC every weekday
- cron: "30 10 * * 1-5"
jobs:
pkg:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: "1.24"
- uses: jdx/mise-action@v2
with:
experimental: true
- name: Build
shell: bash
run: GOPROXY=direct SDKS="" make build
sdk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: "1.24"
- uses: pulumi/actions@v6
with:
pulumi-version: dev
- name: Build
shell: bash
run: |
mkdir test
cd test
pulumi new random-go --generate-only --yes
go mod edit -replace github.com/pulumi/pulumi/sdk/v3=../sdk
GOPROXY=direct pulumi install
cron-test-all perms .github/workflows/cron-test-all.yml
View raw YAML
name: Run full language matrix of tests daily
permissions:
contents: read
id-token: write
on:
schedule:
- cron: "27 5 * * *"
workflow_dispatch: {}
jobs:
info:
name: info
uses: ./.github/workflows/ci-info.yml
permissions:
contents: read
with:
ref: ${{ github.ref }}
is-snapshot: true
secrets: inherit
ci:
name: CI
needs: [info]
uses: ./.github/workflows/ci.yml
permissions:
contents: read
id-token: write
with:
ref: ${{ github.ref }}
version: ${{ needs.info.outputs.version }}
lint: true
test-version-sets: 'all'
integration-test-platforms: ubuntu-latest
acceptance-test-platforms: 'macos-latest windows-latest'
# Disable coverage in daily runs. This is unfortunately flaky
# on windows, and the daily cron job runs the most extensive set
# of tests, leading to quite a bit of flakyness. We get coverage
# data on every merge to main, so getting it in the daily cro
# job is not important.
enable-coverage: false
secrets: inherit
performance-gate:
name: Performance Gate
needs: [info]
uses: ./.github/workflows/ci-performance-gate.yml
permissions:
contents: read
with:
ref: ${{ github.ref }}
version: ${{ needs.info.outputs.version }}
test-version-sets: 'all'
performance-test-platforms: ubuntu-latest
secrets: inherit
ci-ok:
name: ci-ok
needs: [ci, performance-gate]
if: always()
runs-on: ubuntu-latest
steps:
- name: CI failed
if: ${{ needs.ci.result != 'success' }}
run: exit 1
- name: CI succeeded
run: exit 0
download-pulumi-cron matrix .github/workflows/download-pulumi-cron.yml
View raw YAML
name: Download Pulumi Cron
"on":
workflow_dispatch: {}
schedule:
- cron: 0 8 * * *
defaults:
run:
shell: bash
jobs:
macos-homebrew-install:
name: Install Pulumi with Homebrew on macOS
runs-on: macos-13
steps:
# Workaround for https://github.com/pulumi/pulumi/issues/13938
- name: Delete default golang installed on the runner
run: |
rm /usr/local/bin/go || true
rm /usr/local/bin/gofmt || true
- name: homedate homebrew formulae
run: brew update
- name: homebrew install
run: brew install pulumi
- name: Pulumi Version Details
id: vars
run: |
# shellcheck disable=SC2129
echo "installed-version=$(pulumi version)" >> "${GITHUB_OUTPUT}"
echo "expected-version=v$(curl -sS https://www.pulumi.com/latest-version)" >> "${GITHUB_OUTPUT}"
echo "previous-version=$(curl -sS https://raw.githubusercontent.com/pulumi/docs/master/data/versions.json | jq -r '.[1].version')" >> "${GITHUB_OUTPUT}"
- name: Error if incorrect version found
if: ${{ steps.vars.outputs.expected-version != steps.vars.outputs.installed-version && steps.vars.outputs.previous-version != steps.vars.outputs.installed-version}}
run: |
echo "Expected version ${{ steps.vars.outputs.expected-version }} but found ${{ steps.vars.outputs.installed-version }}"
exit 1
macOS-direct-install:
name: Install Pulumi via script on macOS
runs-on: macos-13
steps:
- run: curl -fsSL https://get.pulumi.com | sh
- run: echo "/Users/runner/.pulumi/bin" >> "${GITHUB_PATH}"
- name: Pulumi Version Details
id: vars
run: |
echo "installed-version=$(pulumi version)" >> "${GITHUB_OUTPUT}"
echo "expected-version=v$(curl -sS https://www.pulumi.com/latest-version)" >> "${GITHUB_OUTPUT}"
- run: command -v pulumi
- name: Error if incorrect version found
if: ${{ steps.vars.outputs.expected-version != steps.vars.outputs.installed-version }}
run: |
echo "Expected version ${{ steps.vars.outputs.expected-version }} but found ${{ steps.vars.outputs.installed-version }}"
exit 1
macos-verify-download-link:
name: Verify Direct Download link on macOS
runs-on: macos-13
steps:
- name: Direct Download
run: curl -L -o pulumi.tar.gz "https://get.pulumi.com/releases/sdk/pulumi-v$(curl -sS https://www.pulumi.com/latest-version)-darwin-x64.tar.gz"
- run: ls -la
linux-direct-install:
name: Install Pulumi via script on Ubuntu
runs-on: ubuntu-latest
steps:
- name: Remove existing version
run: sudo rm /usr/local/bin/pulumi
- run: curl -fsSL https://get.pulumi.com | sh
- run: echo "/home/runner/.pulumi/bin" >> "${GITHUB_PATH}"
- name: Pulumi Version Details
id: vars
run: |
echo "installed-version=$(pulumi version)" >> "${GITHUB_OUTPUT}"
echo "expected-version=v$(curl -sS https://www.pulumi.com/latest-version)" >> "${GITHUB_OUTPUT}"
- run: command -v pulumi
- name: Error if incorrect version found
if: ${{ steps.vars.outputs.expected-version != steps.vars.outputs.installed-version }}
run: |
echo "Expected version ${{ steps.vars.outputs.expected-version }} but found ${{ steps.vars.outputs.installed-version }}"
exit 1
linux-verify-download-link:
name: Verify Direct Download link on Linux
runs-on: ubuntu-latest
steps:
- name: Direct Download
run: curl -L -o pulumi.tar.gz "https://get.pulumi.com/releases/sdk/pulumi-v$(curl -sS https://www.pulumi.com/latest-version)-linux-x64.tar.gz"
- run: ls -la
windows-choco-install:
name: Install Pulumi with Chocolatey on Windows
runs-on: windows-latest
steps:
- name: choco install
run: choco upgrade pulumi
- name: Pulumi Version Details
id: vars
shell: bash
run: |
# shellcheck disable=SC2129
echo "installed-version=$(pulumi version)" >> "${GITHUB_OUTPUT}"
echo "expected-version=v$(curl -sS https://www.pulumi.com/latest-version)" >> "${GITHUB_OUTPUT}"
echo "previous-version=$(curl -sS https://raw.githubusercontent.com/pulumi/docs/master/data/versions.json | jq -r '.[1].version')" >> "${GITHUB_OUTPUT}"
- name: Error if incorrect version found
if: ${{ steps.vars.outputs.expected-version != steps.vars.outputs.installed-version && steps.vars.outputs.previous-version != steps.vars.outputs.installed-version}}
run: |
echo "Expected version ${{ steps.vars.outputs.expected-version }} but found ${{ steps.vars.outputs.installed-version }}"
exit 1
windows-winget-install:
name: Install Pulumi with WinGet on Windows
runs-on: windows-latest
steps:
- name: Remove Pre-installed Pulumi
shell: bash
run: |
if command -v "pulumi.exe"; then
PULUMI_INSTALL_DIR=$(dirname "$(command -v "pulumi.exe")")
echo "Deleting Pulumi"
rm -v "$PULUMI_INSTALL_DIR"/pulumi*
fi
- name: Install winget
uses: Cyberboss/install-winget@1c6f189175e9f015bf1aa113cd1cc6c73efb9d47 # v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install Pulumi Using WinGet
run: winget install pulumi --disable-interactivity --accept-source-agreements --verbose
- run: echo "C:/Program Files (x86)/Pulumi" >> "${GITHUB_PATH}"
shell: bash
- name: Pulumi Version Details
id: vars
shell: bash
run: |
echo "installed-version=$(pulumi version)" >> "${GITHUB_OUTPUT}"
echo "expected-version=v$(curl -sS https://www.pulumi.com/latest-version)" >> "${GITHUB_OUTPUT}"
- name: Error if incorrect version found
if: ${{ steps.vars.outputs.expected-version != steps.vars.outputs.installed-version }}
run: |
echo "Expected version ${{ steps.vars.outputs.expected-version }} but found ${{ steps.vars.outputs.installed-version }}"
exit 1
windows-direct-install:
name: Install Pulumi via script on Windows
runs-on: windows-latest
steps:
- run: '[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; iex ((New-Object System.Net.WebClient).DownloadString("https://get.pulumi.com/install.ps1")) && SET \"PATH=%PATH%;%USERPROFILE%\.pulumi\bin\"'
shell: pwsh
- run: echo "C:/Users/runneradmin/.pulumi/bin" >> "${GITHUB_PATH}"
shell: bash
- name: Pulumi Version Details
id: vars
shell: bash
run: |
echo "installed-version=$(pulumi version)" >> "${GITHUB_OUTPUT}"
echo "expected-version=v$(curl -sS https://www.pulumi.com/latest-version)" >> "${GITHUB_OUTPUT}"
- name: Error if incorrect version found
if: ${{ steps.vars.outputs.expected-version != steps.vars.outputs.installed-version }}
run: |
echo "Expected version ${{ steps.vars.outputs.expected-version }} but found ${{ steps.vars.outputs.installed-version }}"
exit 1
windows-verify-download-link:
name: Verify Direct Download link on Windows
runs-on: windows-latest
steps:
- name: Direct Download
shell: pwsh
run: |
$latestVersion = (Invoke-WebRequest -UseBasicParsing https://www.pulumi.com/latest-version).Content.Trim()
$downloadUrl = "https://get.pulumi.com/releases/sdk/pulumi-v${latestVersion}-windows-x64.zip"
$tempZip = New-Item -Type File (Join-Path $env:TEMP ([System.IO.Path]::ChangeExtension(([System.IO.Path]::GetRandomFileName()), "zip")))
Invoke-WebRequest https://get.pulumi.com/releases/sdk/pulumi-v${latestVersion}-windows-x64.zip -OutFile $tempZip
- run: ls -la
shell: bash
windows-verify-msi-download-link:
name: Verify Direct MSI Download link on Windows
runs-on: windows-latest
steps:
- name: Direct Download
shell: pwsh
run: |
$latestVersion = (Invoke-WebRequest -UseBasicParsing https://www.pulumi.com/latest-version).Content.Trim()
$tempMsi = New-Item -Type File (Join-Path $env:TEMP ([System.IO.Path]::ChangeExtension(([System.IO.Path]::GetRandomFileName()), "msi")))
Invoke-WebRequest https://github.com/pulumi/pulumi-winget/releases/download/v${latestVersion}/pulumi-${latestVersion}-windows-x64.msi -OutFile $tempMsi
- run: ls -la
shell: bash
install-via-gha:
name: Install via GHA on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-latest", "macos-13"]
steps:
- name: Install Pulumi CLI
uses: pulumi/action-install-pulumi-cli@v1.0.1
- name: Pulumi Version Details
id: vars
run: |
echo "installed-version=$(pulumi version)" >> "${GITHUB_OUTPUT}"
echo "expected-version=v$(curl -sS https://www.pulumi.com/latest-version)" >> "${GITHUB_OUTPUT}"
- name: Error if incorrect version found
if: ${{ steps.vars.outputs.expected-version != steps.vars.outputs.installed-version }}
run: |
echo "Expected version ${{ steps.vars.outputs.expected-version }} but found ${{ steps.vars.outputs.installed-version }}"
exit 1
on-community-pr perms .github/workflows/on-community-pr.yml
View raw YAML
# SECURITY: This PR run on untrusted branches.
#
# Changes to "permissions" and "secrets" should be narrowly scoped and carefully reviewed.
#
# Reusable workflows, "uses" jobs, *must* specify the main branch.
name: Community Pull Request
on:
pull_request_target:
permissions:
contents: read
# Only required for the PR and changelog comment.
pull-requests: write
jobs:
comment-on-pr:
name: Maintainer comment
# We only care about commenting on a PR if the PR is from a fork
if: github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-latest
steps:
- name: Comment PR
uses: thollander/actions-comment-pull-request@71efef56b184328c7ef1f213577c3a90edaa4aff # 1.0.1
with:
message: |
PR is now waiting for a maintainer to take action.
**Note for the maintainer:** Commands available:
* `/run-acceptance-tests` - used to test run the acceptance tests for the project
* `/run-codegen` - used to test the Pull Request against downstream codegen
* `/run-docs-gen` - used to test the Pull Request against documentation generation
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
on-merge perms .github/workflows/on-merge.yml
View raw YAML
name: Merge
permissions:
# To create a draft release.
contents: write
# To sign artifacts.
id-token: write
on:
merge_group:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
info:
name: info
uses: ./.github/workflows/ci-info.yml
permissions:
contents: read
with:
ref: ${{ github.ref }}
is-snapshot: false
secrets: inherit
ci:
name: CI
needs: [info]
uses: ./.github/workflows/ci.yml
strategy:
fail-fast: true
permissions:
contents: read
# To sign artifacts.
id-token: write
with:
ref: ${{ github.ref }}
version: ${{ needs.info.outputs.version }}
enable-coverage: true
fail-fast: true
test-retries: 2
acceptance-test-platforms: 'macos-latest'
fuzz-test-optional: true
secrets: inherit
performance-gate:
name: Performance Gate
needs: [info]
uses: ./.github/workflows/ci-performance-gate.yml
strategy:
fail-fast: true
permissions:
contents: read
with:
ref: ${{ github.ref }}
version: ${{ needs.info.outputs.version }}
test-version-sets: 'minimum current'
fail-fast: true
test-retries: 2
performance-test-platforms: ubuntu-latest
secrets: inherit
prepare-release:
name: prepare
needs: [info, ci]
uses: ./.github/workflows/ci-prepare-release.yml
permissions:
contents: write
# To sign artifacts.
id-token: write
with:
ref: ${{ github.ref }}
version: ${{ needs.info.outputs.version }}
release-notes: ${{ needs.info.outputs.release-notes }}
project: ${{ github.repository }}
secrets: inherit
ci-ok:
name: ci-ok
needs: [ci, performance-gate, prepare-release]
if: always() # always report a status
runs-on: ubuntu-latest
steps:
- name: CI failed
if: ${{ needs.ci.result != 'success' }}
run: exit 1
- name: Release failed
if: ${{ needs.prepare-release.result != 'success' }}
run: exit 1
- name: CI succeeded
run: exit 0
on-pr perms .github/workflows/on-pr.yml
View raw YAML
name: Pull Request
permissions:
# To create a draft release.
contents: write
# To comment on PRs.
pull-requests: write
# To sign artifacts.
id-token: write
on:
pull_request:
paths-ignore:
- "sdk/.version"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
# The jobs in this workflow are only run on branches. The `on-community-pr.yml` job provides
# commands for running workflows from forks.
jobs:
info:
name: info
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
uses: ./.github/workflows/ci-info.yml
permissions:
contents: read
with:
ref: ${{ github.head_ref }}
is-snapshot: true
secrets: inherit
ci:
name: CI
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
needs: [info]
uses: ./.github/workflows/ci.yml
permissions:
contents: read
# To sign artifacts.
id-token: write
with:
ref: ${{ github.head_ref }}
version: ${{ needs.info.outputs.version }}
lint: true
# codegen tests are not the fastest, but we want to run them
# on PR to get correct coverage numbers.
test-version-sets: >- # No newlines or trailing newline.
${{
contains(github.event.pull_request.labels.*.name, 'ci/test')
&& 'minimum current'
|| 'current'
}}
integration-test-platforms: ubuntu-latest
acceptance-test-platforms: >- # No newlines or trailing newline.
${{
contains(github.event.pull_request.labels.*.name, 'ci/test')
&& 'macos-latest windows-latest'
|| 'windows-latest'
}}
enable-coverage: true
secrets: inherit
performance-gate:
name: Performance Gate
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
needs: [info]
uses: ./.github/workflows/ci-performance-gate.yml
permissions:
contents: read
with:
ref: ${{ github.ref }}
version: ${{ needs.info.outputs.version }}
test-version-sets: 'minimum current'
performance-test-platforms: ubuntu-latest
secrets: inherit
ci-ok:
name: ci-ok
needs: [ci, performance-gate]
if: always()
runs-on: ubuntu-latest
steps:
# If the PR is a community PR, we don't run the usual checks, but instead
# require maintainers to run them manually by commenting with
# /run-acceptance-tests. Since the checks then run outside of the PR,
# ci-ok is not a useful status in these cases. We thus want to skip this
# job for community PRs. We detect community contributions by checking if
# the head repository is different to the base repository.
- name: CI failed
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && needs.ci.result != 'success' }}
run: exit 1
- name: CI succeeded
run: exit 0
on-pr-close .github/workflows/on-pr-close.yml
View raw YAML
name: On PR Close
on:
pull_request:
types: [ closed ]
jobs:
cleanup:
name: Remove Existing Codegen PRs
runs-on: ubuntu-latest
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
steps:
- name: "Close existing Downstream PRs"
run: |
for url in $(gh search prs --json url --owner pulumi --state open --match body "This is a downstream codegen test for pulumi/pulumi#${{ github.event.pull_request.number }}." | jq -r '.[].url'); do
gh pr close "$url"
done
on-pr-default .github/workflows/on-pr-default.yml
View raw YAML
# This file fires for the same events as `on-pr.yml`,
# except where `on-pr.yml` skips over changes in paths-ignore,
# this event fires on the inverse.
# This is an officially blessed pattern for handling skipped but
# required status checks. See
# https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
# for more information.
name: Pull Request
on:
pull_request:
paths-ignore:
- "**" # Skip all files…
- "!sdk/.version" # …except if only this file changes and nothing else.
jobs:
no-op:
name: Skip CI on .version changes
runs-on: ubuntu-latest
steps:
- name: Skip CI on .version changes
run: echo 'No need to run CI tests when only .version changes'
ci-ok:
name: ci-ok
runs-on: ubuntu-latest
steps:
- name: CI succeeded
run: exit 0
on-push perms .github/workflows/on-push.yml
View raw YAML
name: On Push
permissions:
# To push the tag
contents: write
on:
push:
branches:
- 'master'
jobs:
info:
name: info
uses: ./.github/workflows/ci-info.yml
permissions:
contents: read
with:
ref: ${{ github.ref }}
is-snapshot: false
secrets: inherit
dev-release:
name: dev-release
needs: [info]
uses: ./.github/workflows/ci-dev-release.yml
permissions:
contents: write
# To sign artifacts.
id-token: write
with:
ref: ${{ github.ref }}
version: ${{ needs.info.outputs.version }}
secrets: inherit
update-dev-version:
name: update-dev-version
needs: [dev-release, info]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ github.ref }}
- name: Install Pulumictl
uses: jaxxstorm/action-install-gh-release@71d17cb091aa850acb2a1a4cf87258d183eb941b # v1.11.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
repo: pulumi/pulumictl
tag: v0.0.46
cache: enable
- name: Git describe
id: ghd
uses: proudust/gh-describe@v1
- name: Dispatch event to docs repo
env:
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
# Always prefix the short_sha with a letter to ensure it's a valid semver prerelease,
# see https://github.com/pulumi/pulumi/issues/15471 for context.
run: |
if [ "$(git ls-remote https://github.com/pulumi/pulumi HEAD | cut -f 1)" = "${{ github.sha }}" ]; then
short_sha="x${{ steps.ghd.outputs.short-sha }}"
version="${{ needs.info.outputs.version }}"
version="${version#v}-alpha.${short_sha}"
pulumictl dispatch -c pulumi-cli-dev-version -r pulumi/docs dev_version="$version"
else
echo "Not updating latest version as there has been a new push to the main branch"
fi
tag-pkg:
name: tag-pkg
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Tag and push pkg version if necessary
run: |
title=$(git show --pretty=format:%s -s HEAD)
if [[ $title == "Changelog and go.mod updates for"* ]]; then
pkg_version="${title#*updates for }"
pkg_version="${pkg_version% \(*}"
git tag "pkg/${pkg_version}"
git push origin "pkg/${pkg_version}"
fi
on-release perms .github/workflows/on-release.yml
View raw YAML
name: Release
permissions:
# To create the follow-up PR.
contents: write
pull-requests: write
on:
release:
types:
- released
concurrency: release
jobs:
info:
name: gather
runs-on: ubuntu-latest
outputs:
version: "${{ fromJSON(steps.version.outputs.version) }}"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# Uses release ref (tag)
- name: Info
id: version
run: |
TAG="${{ github.event.release.tag_name }}"
PULUMI_VERSION="${TAG#v}" # remove prefix
./.github/scripts/set-output version "${PULUMI_VERSION}"
release:
name: release
needs: [info]
uses: ./.github/workflows/release.yml
with:
ref: ${{ github.event.release.tag_name }}
version: ${{ needs.info.outputs.version }}
release-notes: ${{ github.event.release.body }}
queue-merge: true
run-dispatch-commands: true
secrets: inherit
pr-test-acceptance-on-dispatch perms .github/workflows/pr-test-acceptance-on-dispatch.yml
View raw YAML
# SECURITY: This PR run on untrusted branches when a maintainer comments "/run-acceptance-tests".
#
# Changes "permissions" and "secrets" should be narrowly scoped and carefully reviewed.
#
# Reusable workflows, "uses" jobs, *must* specify the main branch.
name: dispatched-acceptance-test
on:
repository_dispatch:
types: [run-acceptance-tests-command]
permissions:
contents: read
# Only the 'changelog-comment' job should use this permission.
pull-requests: write
# To sign artifacts.
id-token: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.client_payload.pull_request.number }}
cancel-in-progress: true
jobs:
info:
name: info
uses: pulumi/pulumi/.github/workflows/ci-info.yml@master
permissions:
contents: read
with:
ref: ${{ github.ref }}
is-snapshot: true
secrets: inherit
comment-notification:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'repository_dispatch' }}
permissions:
contents: read
pull-requests: write
steps:
- name: Update with Result
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
token: ${{ secrets.PULUMI_BOT_TOKEN }}
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
issue-number: ${{ github.event.client_payload.github.payload.issue.number }}
body: |
Please view the results of the acceptance tests [Here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
ci:
name: CI
needs: [info]
uses: pulumi/pulumi/.github/workflows/ci.yml@master
permissions:
contents: read
# To sign artifacts.
id-token: write
with:
ref: refs/pull/${{ github.event.client_payload.pull_request.number }}/merge
version: ${{ needs.info.outputs.version }}
lint: true
test-version-sets: current
integration-test-platforms: ubuntu-latest
acceptance-test-platforms: 'windows-latest'
# We'll only upload coverage artifacts with the periodic-coverage cron workflow.
enable-coverage: false
secrets:
# Scope secrets to the minimum required:
PULUMI_PROD_ACCESS_TOKEN: ${{ secrets.PULUMI_PROD_ACCESS_TOKEN }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_STORAGE_SAS_TOKEN: ${{ secrets.AZURE_STORAGE_SAS_TOKEN }}
GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }}
rebase .github/workflows/rebase.yml
View raw YAML
name: Automatic Rebase
on:
issue_comment:
types: [created]
jobs:
rebase:
name: Rebase
runs-on: ubuntu-latest
if: >-
contains(fromJSON('["COLLABORATOR", "MEMBER", "OWNER"]'), github.event.comment.author_association) &&
github.event.issue.pull_request != '' &&
(
contains(github.event.comment.body, '/rebase') ||
contains(github.event.comment.body, '/autosquash')
)
steps:
- name: Checkout the latest code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
token: ${{ secrets.PULUMI_BOT_TOKEN }}
fetch-depth: 0 # otherwise, you will fail to push refs to dest repo
- name: Automatic Rebase
uses: cirrus-actions/rebase@50007412be906b9cc222db3dfc469f325031f1b2
with:
autosquash: ${{ contains(github.event.comment.body, '/autosquash') || contains(github.event.comment.body, '/rebase-autosquash') }}
env:
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
release matrix perms .github/workflows/release.yml
View raw YAML
name: Release Actions
permissions:
# To create a PR
contents: write
pull-requests: write
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
branch_from_ref:
required: false
description: "Commit to branch from, if not the tag"
type: string
release-notes:
required: true
description: "Release notes to publish"
type: string
queue-merge:
required: false
default: false
description: "Whether to queue the release for immediate merge"
type: boolean
run-dispatch-commands:
required: false
default: false
# If version contains a '-', i.e.: a prerelease build, these commands are disabled until further notice.
description: "Whether to run dispatch commands"
type: boolean
version-set:
required: false
description: "Set of language versions to use for builds, lints, releases, etc."
type: string
# Example provided for illustration, this value is derived by scripts/get-job-matrix.py build
default: |
{
"dotnet": "8.0.x",
"go": "1.18.x",
"nodejs": "20.x",
"python": "3.9.x"
}
env:
PULUMI_VERSION: ${{ inputs.version }}
GIT_REF: ${{ inputs.ref }}
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_PROD_ACCESS_TOKEN }}
PULUMI_TEST_OWNER: "moolumi"
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
PYPI_USERNAME: __token__
PYPI_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
jobs:
sdks:
name: ${{ matrix.language }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ["nodejs", "python", "go"]
steps:
- name: Checkout Repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Set up uv
if: ${{ matrix.language == 'python' }}
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
enable-cache: true
cache-dependency-glob: sdk/python/uv.lock
- name: Set up Python ${{ fromJson(inputs.version-set).python }}
if: ${{ matrix.language == 'python' }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ fromJson(inputs.version-set).python }}
- name: Set up Node ${{ fromJson(inputs.version-set).nodejs }}
if: ${{ matrix.language == 'nodejs' }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ fromJson(inputs.version-set).nodejs }}
registry-url: https://registry.npmjs.org
always-auth: true
- name: Download release artifacts
if: ${{ matrix.language != 'go' }}
run: |
mkdir -p artifacts
gh release download "v${PULUMI_VERSION}" --dir ./artifacts --pattern 'sdk-${{ matrix.language }}-*'
find artifacts
- name: Publish Packages
run: |
make -C sdk/${{ matrix.language}} publish
s3-blobs:
name: s3 blobs
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-region: us-east-2
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
role-duration-seconds: 3600
role-external-id: upload-pulumi-release
role-session-name: pulumi@githubActions
role-to-assume: ${{ secrets.AWS_UPLOAD_ROLE_ARN }}
- name: Download release artifacts
run: |
mkdir -p artifacts
gh release download "v${PULUMI_VERSION}" --dir ./artifacts --pattern 'pulumi-*'
find artifacts
- name: Publish Blobs
run: |
aws s3 sync artifacts s3://get.pulumi.com/releases/sdk --acl public-read
pr:
# Relies on the Go SDK being published to update pkg
name: PR
needs: [sdks]
uses: ./.github/workflows/release-pr.yml
permissions:
contents: write
pull-requests: write
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
release-notes: ${{ inputs.release-notes }}
queue-merge: ${{ inputs.queue-merge }}
secrets: inherit
dispatch:
name: ${{ matrix.job.name }}
if: inputs.run-dispatch-commands && !contains(inputs.version, '-')
runs-on: ubuntu-latest
needs: [pr]
strategy:
fail-fast: false
matrix:
job:
- name: Update Templates Version
run-command: pulumictl dispatch -r pulumi/templates -c update-templates "${PULUMI_VERSION}"
- name: Chocolatey Update
run-command: pulumictl create choco-deploy "${PULUMI_VERSION}"
- name: Winget Update
run-command: pulumictl winget-deploy
- name: Build Package Docs
run-command: pulumictl create cli-docs-build "${PULUMI_VERSION}"
- name: Homebrew
run-command: pulumictl create homebrew-bump "${PULUMI_VERSION}" "$(git rev-parse HEAD)"
- name: Docker containers
run-command: pulumictl dispatch -r pulumi/pulumi-docker-containers -c release-build "${PULUMI_VERSION}"
steps:
- name: Checkout Repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Install Pulumictl
uses: jaxxstorm/action-install-gh-release@71d17cb091aa850acb2a1a4cf87258d183eb941b # v1.11.0
env:
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
with:
repo: pulumi/pulumictl
tag: v0.0.42
cache: enable
- name: Repository Dispatch
run: ${{ matrix.job.run-command }}
update-homebrew-tap:
name: Update Homebrew Tap
if: inputs.run-dispatch-commands && !contains(inputs.version, '-')
needs: [dispatch]
uses: ./.github/workflows/release-homebrew-tap.yml
permissions:
contents: read
with:
ref: ${{ inputs.ref }}
version: ${{ inputs.version }}
dry-run: false
secrets: inherit
release-homebrew-tap perms .github/workflows/release-homebrew-tap.yml
View raw YAML
name: Post-Release Homebrew Tap
permissions:
contents: read
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
dry-run:
required: false
default: true
description: "Whether to run in dry-run mode and skip pushing the commit"
type: boolean
workflow_dispatch:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
dry-run:
required: false
default: true
description: "Whether to run in dry-run mode and skip pushing the commit"
type: boolean
env:
PULUMI_VERSION: ${{ inputs.version }}
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
jobs:
update-homebrew-tap:
name: Update Homebrew Tap
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
path: pulumi
- name: Checkout tap repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
repository: pulumi/homebrew-tap
path: homebrew-tap
token: ${{ secrets.PULUMI_BOT_TOKEN }}
- name: Update Homebrew Tap
run: |
set -euo pipefail
# Can simulate this by cloning pulumi/pulumi & pulumi/homebrew-tap to adacent directories
# and running from their parent:
./pulumi/.github/scripts/generate-homebrew-tap \
"${PULUMI_VERSION}" ./pulumi/.github/scripts/pulumi-tap-template.rb \
> ./homebrew-tap/Formula/pulumi.rb
- name: Commit updated formula
working-directory: homebrew-tap
run: |
set -euo pipefail
git config user.name pulumi-bot
git config user.email bot@pulumi.com
git add Formula/pulumi.rb
echo "::group::git diff"
git --no-pager diff
echo "::endgroup::"
git commit -m "Brew formula update for Pulumi version ${PULUMI_VERSION}"
- name: Push formula
working-directory: homebrew-tap
if: ${{ !inputs.dry-run }}
run: |
set -euo pipefail
git push origin HEAD:main
release-pr perms .github/workflows/release-pr.yml
View raw YAML
name: Post-Release PR
permissions:
# To create a PR
contents: write
pull-requests: write
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
branch_from_ref:
required: false
description: "Commit to branch from, if not the tag"
type: string
release-notes:
required: true
description: "Release notes to publish"
type: string
queue-merge:
required: false
default: false
description: "Whether to queue the release for immediate merge"
type: boolean
jobs:
version-bump:
name: version bump
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version: '>=1.19.0' # decoupled from version sets, only used by changelog tool
- name: Create PR
env:
PULUMI_VERSION: ${{ inputs.version }}
RELEASE_NOTES: ${{ inputs.release-notes }}
QUEUE_MERGE: ${{ inputs.queue-merge }}
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
run: |
set -euo pipefail
git switch --create "automation/release-v${PULUMI_VERSION}"
echo -en "# Changelog\n\n${RELEASE_NOTES}\n\n$(tail -n+3 CHANGELOG.md)" > ./CHANGELOG.md
git config user.name github-actions
git config user.email github-actions@github.com
rm ./changelog/pending/*.yaml || true
git add -A
git commit -m "chore: changelog for v${PULUMI_VERSION}"
# Update go module dependencies
(
cd pkg
go get -u "github.com/pulumi/pulumi/sdk/v3@v${PULUMI_VERSION}"
)
make tidy_fix
git add -A
git commit -m "chore: post-release go.mod updates"
git push -u origin HEAD
# Note that the title of the PR is used in the on-push workflow. If the title needs to
# change here, please also check on-push.yml
PR=$(gh pr create --title "Changelog and go.mod updates for v${PULUMI_VERSION}" --body "")
if [ "${QUEUE_MERGE}" = "true" ]; then
gh pr merge --auto --squash "${PR}"
fi
sign perms .github/workflows/sign.yml
View raw YAML
name: Sign
permissions:
# To sign artifacts.
id-token: write
on:
workflow_call:
inputs:
ref:
required: true
description: "GitHub ref to use"
type: string
version:
required: true
description: "Version to produce"
type: string
jobs:
sign:
name: sign
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ inputs.ref }}
- name: Install b3sum
uses: baptiste0928/cargo-install@b687c656bda5733207e629b50a22bf68974a0305 # v3
with:
crate: b3sum
version: 1.3.0
- uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
- name: Download all artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: artifacts.tmp
- name: Remove performance test artifacts
run: rm -rf artifacts.tmp/*-perf
- name: Remove integration test artifacts
run: rm -rf artifacts.tmp/*-integration
- name: Rename SDKs
# This step must match the rename SDKs step in the "publish" job below.
run: |
(
cd artifacts.tmp/artifacts-python-sdk
for file in *.whl ; do
mv -vT "$file" "sdk-python-$file"
done
)
(
cd artifacts.tmp/artifacts-nodejs-sdk
for file in *.tgz ; do
mv -vT "$file" "sdk-nodejs-$file"
done
)
- name: Flatten artifact directories
run: |
mkdir -p ./artifacts
mv ./artifacts.tmp/artifacts-*/* ./artifacts
- name: Ensure coverage not enabled on release
run: |
# Extract pulumi binary to bintest rather than pollute artifacts directory.
mkdir './bintest' && tar -xvf ./artifacts/pulumi-*-linux-x64.tar.gz -C './bintest/.'
# Ensure pulumi binary exists.
stat './bintest/pulumi/pulumi' || exit 1
# Check binary not built with coverage.
if ./bintest/pulumi/pulumi version 2>&1 | grep coverage; then
echo 'Aborting! Pulumi binary built with coverage data.'
exit 2
else
echo 'Pulumi binary OK!'
fi
- name: Create sums.tmp
run: mkdir -p ./sums.tmp ./sigs.tmp
# Each of these commands strips the ./ prefix to match existing (<=3.39) formatting.
- name: Checksums with SHA256
working-directory: artifacts
env:
version: ${{ inputs.version }}
run: sha256sum ./pulumi-*.{tar.gz,zip} | sed 's/.\///' | tee "../sums.tmp/pulumi-${version}-checksums.txt"
- name: Checksums with BLAKE3
working-directory: artifacts
run: b3sum ./* | sed 's/.\///' | tee ../sums.tmp/B3SUMS
- name: Checksums with SHA512
working-directory: artifacts
run: sha512sum ./* | sed 's/.\///' | tee ../sums.tmp/SHA512SUMS
- name: Sign binaries and checksums
shell: bash
env:
version: ${{ inputs.version }}
run: |
ls -la
# Sign all artifacts and checksums:
for dir in "artifacts" "sums.tmp"; do
pushd "$dir"
for file in ./*; do
echo "$file"
COSIGN_EXPERIMENTAL=1 cosign sign-blob --yes \
--bundle="../sigs.tmp/${file}".sig \
"${file}"
done
popd
done
# flatten to a single directory to upload:
mv sums.tmp/* sigs.tmp
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: artifacts-signatures
retention-days: 1
path: |
sigs.tmp/*
if-no-files-found: error
trigger-homebrew-event .github/workflows/trigger-homebrew-event.yml
View raw YAML
env:
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
VERSION: ${{ github.event.client_payload.ref }}
COMMIT_SHA: ${{ github.event.client_payload.commitSha }}
on:
repository_dispatch:
types:
- homebrew-bump
jobs:
homebrew:
name: Bump Homebrew formula
runs-on: ubuntu-latest
steps:
- name: Add Homebrew to the PATH
run: |
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> "${GITHUB_PATH}"
- uses: dawidd6/action-homebrew-bump-formula@1446dca236b0440c6f02723a3f14f13be2c04ab0 # v7
with:
token: ${{secrets.PULUMI_BOT_HOMEBREW_TOKEN}}
formula: pulumi
tag: v${{env.VERSION}}
revision: ${{env.COMMIT_SHA}}
trigger-release-docs-event .github/workflows/trigger-release-docs-event.yml
View raw YAML
env:
GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
VERSION: ${{ github.event.client_payload.ref }}
COMMIT_SHA: ${{ github.event.client_payload.commitSha }}
on:
repository_dispatch:
types:
- docs
jobs:
docs:
name: Build Package Docs
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Checkout Scripts Repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
path: ci-scripts
repository: pulumi/scripts
- name: Trigger Docs Build
run: |
./ci-scripts/ci/build-package-docs.sh "pulumi"
env:
TRAVIS: true
PULUMI_BOT_GITHUB_API_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }}
TRAVIS_TAG: ${{ env.VERSION }}