diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 604498e3..6be609e4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,3 +56,51 @@ jobs: work-dir: .github/test-stacks/golang config-map: "{name: {value: test, secret: false}}" - run: echo 'The random string is `${{ steps.pulumi.outputs.name }}`' + test-update-plan: # make sure the action works on a clean machine without building + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '^1.13.1' + - run: | + pulumi login --local + pulumi stack init dev + working-directory: .github/test-stacks/golang + env: + PULUMI_CONFIG_PASSPHRASE: not-a-secret + - uses: actions/setup-node@v3 + with: + node-version: 16.x + cache: yarn + - run: yarn install + - run: yarn build + - name: Generate Update Plan via Pulumi Preview + uses: ./ + if: always() + id: pulumi-preview + env: + PULUMI_CONFIG_PASSPHRASE: not-a-secret + with: + command: preview + cloud-url: file://~ + stack-name: dev + upsert: true + work-dir: .github/test-stacks/golang + config-map: "{name: {value: test, secret: false}}" + plan: /tmp/update-plan.json + - name: Apply Update Plan via Pulumi Up + uses: ./ + if: always() + id: pulumi-up + env: + PULUMI_CONFIG_PASSPHRASE: not-a-secret + with: + command: up + cloud-url: file://~ + stack-name: dev + upsert: true + work-dir: .github/test-stacks/golang + config-map: "{name: {value: test, secret: false}}" + plan: /tmp/update-plan.json + - run: echo 'The random string is `${{ steps.pulumi-up.outputs.name }}`' diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 540fc187..b0539e6a 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -174,6 +174,34 @@ jobs: work-dir: .github/test-stacks/dotnet config-map: '{name: {value: my-pet, secret: false}}' + - name: Ensure Update Plan is Not Generated + shell: bash + if: matrix.command == 'preview' + working-directory: .github/test-stacks/dotnet + run: | + ! test -f update-plan.json || exit 1 + + - name: Run Pulumi Preview with Update Plan + if: matrix.command == 'preview' + uses: ./ + env: + PULUMI_CONFIG_PASSPHRASE: not-a-secret + with: + command: ${{ matrix.command }} + cloud-url: file://~ + stack-name: dev + upsert: true + work-dir: .github/test-stacks/dotnet + config-map: '{name: {value: hostname, secret: false}}' + plan: update-plan.json + + - name: Ensure Update Plan is Generated and Not Empty + shell: bash + if: matrix.command == 'preview' + working-directory: .github/test-stacks/dotnet + run: | + test -s update-plan.json + test-golang-stack: needs: install-and-build if: @@ -226,6 +254,34 @@ jobs: work-dir: .github/test-stacks/golang config-map: '{name: {value: my-user-name, secret: false}}' + - name: Ensure Update Plan is Not Generated + shell: bash + if: matrix.command == 'preview' + working-directory: .github/test-stacks/golang + run: | + ! test -f update-plan.json || exit 1 + + - name: Run Pulumi Preview with Update Plan + if: matrix.command == 'preview' + uses: ./ + env: + PULUMI_CONFIG_PASSPHRASE: not-a-secret + with: + command: ${{ matrix.command }} + cloud-url: file://~ + stack-name: dev + upsert: true + work-dir: .github/test-stacks/golang + config-map: '{name: {value: hostname, secret: false}}' + plan: update-plan.json + + - name: Ensure Update Plan is Generated and Not Empty + shell: bash + if: matrix.command == 'preview' + working-directory: .github/test-stacks/golang + run: | + test -s update-plan.json + test-nodejs-stack: needs: install-and-build if: @@ -282,6 +338,35 @@ jobs: work-dir: .github/test-stacks/nodejs config-map: '{name: {value: hostname, secret: false}}' + - name: Ensure Update Plan is Not Generated + shell: bash + if: matrix.command == 'preview' + working-directory: .github/test-stacks/nodejs + run: | + ! test -f update-plan.json || exit 1 + + - name: Run Pulumi Preview with Update Plan + if: matrix.command == 'preview' + uses: ./ + env: + PULUMI_CONFIG_PASSPHRASE: not-a-secret + with: + command: ${{ matrix.command }} + cloud-url: file://~ + stack-name: dev + upsert: true + work-dir: .github/test-stacks/nodejs + config-map: '{name: {value: hostname, secret: false}}' + plan: update-plan.json + + - name: Ensure Update Plan is Generated and Not Empty + shell: bash + if: matrix.command == 'preview' + working-directory: .github/test-stacks/nodejs + run: | + test -s update-plan.json + + test-python-stack: needs: install-and-build if: @@ -361,6 +446,34 @@ jobs: work-dir: .github/test-stacks/python config-map: '{name: {value: hostname, secret: false}}' + - name: Ensure Update Plan is Not Generated + shell: bash + if: matrix.command == 'preview' + working-directory: .github/test-stacks/python + run: | + ! test -f update-plan.json || exit 1 + + - name: Run Pulumi Preview with Update Plan + if: matrix.command == 'preview' + uses: ./ + env: + PULUMI_CONFIG_PASSPHRASE: not-a-secret + with: + command: ${{ matrix.command }} + cloud-url: file://~ + stack-name: dev + upsert: true + work-dir: .github/test-stacks/python + config-map: '{name: {value: hostname, secret: false}}' + plan: update-plan.json + + - name: Ensure Update Plan is Generated and Not Empty + shell: bash + if: matrix.command == 'preview' + working-directory: .github/test-stacks/python + run: | + test -s update-plan.json + test-generic-inputs: needs: install-and-build if: diff --git a/CHANGELOG.md b/CHANGELOG.md index cbe3bdd1..a0e0ec79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## HEAD (Unreleased) +- feat: Add Update Plan Functionality + ([#994](https://github.com/pulumi/actions/pull/994)) + -- ## 4.4.0 (2023-06-05) @@ -86,7 +89,7 @@ - enhancement: Add `pulumi-version` option to allow pinning the version of the CLI [PR](https://github.com/pulumi/actions/pull/661) fixes - [#437 ](https://github.com/pulumi/actions/issues/437) + [#437](https://github.com/pulumi/actions/issues/437) ## 3.18.1 (2022-07-07) diff --git a/README.md b/README.md index 0604fe53..3e9917d3 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,14 @@ The action can be configured with the following arguments: - `exclude-protected` - (optional) Skip destroying protected resources. Only valid when `command` is `destroy`. +- `plan` - (optional) Used for + [update plans](https://www.pulumi.com/docs/concepts/update-plans/) + - On `preview`: Where to save the update plan. If you choose to use this in a + different run of your workflow (let's say you create an update plan via a + preview on Pull Request creation and want to `up` using the plan) you must + upload the plan as an artifact, and retrieve it wherever you run `up` + - On `up`: Where to read the update plan from. + By default, this action will try to authenticate Pulumi with [Pulumi Cloud](https://app.pulumi.com/). If you have not specified a `PULUMI_ACCESS_TOKEN` then you will need to specify an alternative backend via diff --git a/action.yml b/action.yml index 86654e23..0f5b995e 100644 --- a/action.yml +++ b/action.yml @@ -93,6 +93,9 @@ inputs: description: 'Skip destroying protected resources. Only valid when command is destroy.' required: false default: 'false' + plan: + description: 'Where to either save an Update Plan or read an Update Plan from' + required: false outputs: output: description: Output from running command diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index 57d63460..c6b1344d 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -16,6 +16,7 @@ const defaultConfig: Record = { diff: 'false', 'target-dependents': 'false', 'exclude-protected': 'false', + 'plan': '', }; function setupMockedConfig(config: Record) { @@ -49,6 +50,7 @@ describe('config.ts', () => { "expectNoChanges": false, "message": "", "parallel": undefined, + "plan": "", "policyPackConfigs": Array [], "policyPacks": Array [], "replace": Array [], diff --git a/src/config.ts b/src/config.ts index b75815ed..f84e0270 100644 --- a/src/config.ts +++ b/src/config.ts @@ -69,6 +69,7 @@ export function makeConfig() { alternatives: ['always', 'never', 'raw', 'auto'] as const, }), excludeProtected: getBooleanInput('exclude-protected'), + plan: getInput('plan'), }, }; }