diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1bc69b70..29265561 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,6 +12,11 @@ on: # rebuild any PRs and main branch changes - 'CHANGELOG.md' - 'README.md' +env: + TEST_PLUGIN_NAME: "time" + TEST_PLUGIN_VERSION: "0.0.9" + TEST_PLUGIN_SERVER: "https://github.com/pulumiverse/pulumi-time/releases/download/v0.0.9" + jobs: build-test: # make sure build/ci work properly runs-on: ubuntu-latest @@ -55,4 +60,8 @@ jobs: upsert: true work-dir: .github/test-stacks/golang config-map: "{name: {value: test, secret: false}}" + plugins: "[{name: ${{ env.TEST_PLUGIN_NAME }}, version: ${{ env.TEST_PLUGIN_VERSION }}, server: ${{ env.TEST_PLUGIN_SERVER }}}]" - run: echo 'The random string is `${{ steps.pulumi.outputs.name }}`' + - run: | + pulumi plugin ls | grep ${{ env.TEST_PLUGIN_NAME }} | grep ${{ env.TEST_PLUGIN_VERSION }} + exit $? diff --git a/CHANGELOG.md b/CHANGELOG.md index 38efb3ec..bfed7a8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # CHANGELOG - feat: Add deletion of stack after destroy (remove flag) +- feat: add plugins installation -- diff --git a/README.md b/README.md index 47da07d1..5e160a0c 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ The action can be configured with the following arguments: values are `up` (update), `refresh`, `destroy` and `preview`. - `stack-name` (required) - The name of the stack that Pulumi will be operating - on. Use the fully quaified org-name/stack-name when operating on a stack outside - of your individual account. + on. Use the fully quaified org-name/stack-name when operating on a stack + outside of your individual account. - `work-dir` (optional) - The location of your Pulumi files. Defaults to `./`. @@ -63,7 +63,7 @@ The action can be configured with the following arguments: - `secrets-provider` - (optional) The type of the provider that should be used to encrypt and decrypt secrets. Possible choices: `default`, `passphrase`, `awskms`, `azurekeyvault`, `gcpkms`, `hashivault`. e.g. - `gcpkms://projects//locations/us-west1/keyRings/acmecorpsec/cryptoKeys/payroll ` + `gcpkms://projects//locations/us-west1/keyRings/acmecorpsec/cryptoKeys/payroll` - `color` - (optional) Colorize output. Choices are: always, never, raw, auto (default "auto"). @@ -100,13 +100,16 @@ The action can be configured with the following arguments: - `config-map` - (optional) Configuration of the stack. Format Yaml string: `{: {value: , secret: },}`. +- `plugins` - (optional) Installation Pulumi plugins. Format Yaml string: + `[{name: , version: , kind?: , server?: }, ...]`. + - `upsert` - (optional) Allows the creation of the specified stack if it currently doesn't exist. **PLEASE NOTE:** This will create a `Pulumi..yaml` file that you will need to add back to source control as part of the action if you wish to perform any further tasks with that stack. -- `remove` - (optional) Removes the target stack if all resources are - destroyed. Used only with `destroy` command. +- `remove` - (optional) Removes the target stack if all resources are destroyed. + Used only with `destroy` command. - `pulumi-version` - (optional) Install a specific version of the Pulumi CLI. Defaults to "^3" diff --git a/action.yml b/action.yml index 4048c55c..a60f1481 100644 --- a/action.yml +++ b/action.yml @@ -47,6 +47,10 @@ inputs: description: 'Config to use during the operations' required: false default: '' + plugins: + description: 'Plugins which will install' + required: false + default: '[]' expect-no-changes: description: 'Return an error if any changes occur during this update' required: false diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index b8da0c92..c268b3d4 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -53,6 +53,7 @@ describe('config.ts', () => { "targetDependents": undefined, "userAgent": "pulumi/actions@v3", }, + "plugins": Array [], "refresh": undefined, "remove": undefined, "secretsProvider": undefined, @@ -118,6 +119,7 @@ describe('config.ts', () => { "targetDependents": undefined, "userAgent": "pulumi/actions@v3", }, + "plugins": Array [], "refresh": undefined, "remove": undefined, "secretsProvider": undefined, @@ -175,6 +177,7 @@ describe('config.ts', () => { "targetDependents": undefined, "userAgent": "pulumi/actions@v3", }, + "plugins": Array [], "refresh": undefined, "remove": undefined, "secretsProvider": undefined, diff --git a/src/config.ts b/src/config.ts index a46ed684..82923f47 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +1,7 @@ import { getInput } from '@actions/core'; import { context } from '@actions/github'; import * as rt from 'runtypes'; -import { parseArray, parseBoolean, parseNumber } from './libs/utils'; +import { parseArray, parseBoolean, parseNumber, parsePlugins } from './libs/utils'; export const command = rt.Union( rt.Literal('up'), @@ -10,9 +10,18 @@ export const command = rt.Union( rt.Literal('destroy'), rt.Literal('preview'), ); - export type Commands = rt.Static; +const plugin = rt.Record({ + name: rt.String, + version: rt.String, +}).And(rt.Partial({ + server: rt.String, + kind: rt.String +})); +export type Plugin = rt.Static; +export type Plugins = Plugin[]; + export const options = rt.Partial({ parallel: rt.Number, message: rt.String, @@ -48,6 +57,7 @@ export const config = rt upsert: rt.Boolean, remove: rt.Boolean, refresh: rt.Boolean, + plugins: rt.Array(plugin), secretsProvider: rt.String, commentOnPrNumber: rt.Number, }), @@ -69,6 +79,7 @@ export async function makeConfig(): Promise { remove: parseBoolean(getInput('remove')), refresh: parseBoolean(getInput('refresh')), configMap: getInput('config-map'), + plugins: parsePlugins(getInput('plugins')), isPullRequest: context?.payload?.pull_request !== undefined, options: { parallel: parseNumber(getInput('parallel')), diff --git a/src/libs/utils.ts b/src/libs/utils.ts index 60531188..da411ee1 100644 --- a/src/libs/utils.ts +++ b/src/libs/utils.ts @@ -1,3 +1,6 @@ +import YAML from 'yaml'; +import { Plugin, Plugins } from '../config'; + export function parseArray(input: string): string[] { return parseUndefined(input) ? input.split(/\r?\n/).reduce( @@ -22,3 +25,12 @@ export function parseBoolean(input: string): boolean | undefined { export function parseNumber(input: string): number | undefined { return parseUndefined(input) ? Number(input) : undefined; } + +export function parsePlugins(input: string): Plugins { + if (parseUndefined(input)) { + const plugins = YAML.parse(input); + return plugins.map(plugin => plugin as Plugin) as Plugins; + } else { + return [] as Plugins; + } +} diff --git a/src/main.ts b/src/main.ts index 052fe08b..c191c68c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -54,6 +54,19 @@ const main = async () => { await stack.setAllConfig(configMap); } + if (config.plugins != []) { + for(const plugin of config.plugins) { + if (plugin.server) { + await stack.workspace.installPluginFromServer(plugin.name, + plugin.version, + plugin.server); + } else { + await stack.workspace.installPlugin(plugin.name, plugin.version, + plugin.kind || 'resource'); + } + } + } + if (config.refresh) { core.startGroup(`Refresh stack on ${config.stackName}`); await stack.refresh({ onOutput });