Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add extra options #117

Merged
merged 8 commits into from
Mar 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended",
"prettier",
"prettier/@typescript-eslint"
"prettier"
Copy link
Contributor Author

@simenandre simenandre Mar 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prettier/@typescript-eslint has been merged into prettier in eslint-config-prettier 8.0.0. See: https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md#version-800-2021-02-21

],
"env": {
"browser": false,
Expand Down
55 changes: 1 addition & 54 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,64 +34,11 @@ jobs:
- uses: ./
if: always()
id: pulumi
env:
PULUMI_CONFIG_PASSPHRASE: not-a-secret
with:
command: up --skip-preview
cloud-url: file://~
stack-name: dev
work-dir: .github/test-stacks/golang
- run: echo "The random string is ${{ steps.pulumi.outputs.name }}"
test-args:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.13.1'
- name: Install pulumi
uses: pulumi/action-install-pulumi-cli@v1.0.1
- run: |
pulumi login --local
pulumi stack init dev
working-directory: .github/test-stacks/golang
env:
PULUMI_CONFIG_PASSPHRASE: not-a-secret
- run: yarn install
- run: yarn build
- uses: ./
if: always()
env:
PULUMI_CONFIG_PASSPHRASE: not-a-secret
with:
command: up
cloud-url: file://~
stack-name: dev
work-dir: .github/test-stacks/golang
args: --skip-preview
test-command-args:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.13.1'
- name: Install pulumi
uses: pulumi/action-install-pulumi-cli@v1.0.1
- run: |
pulumi login --local
pulumi stack init dev
working-directory: .github/test-stacks/golang
env:
PULUMI_CONFIG_PASSPHRASE: not-a-secret
- run: yarn install
- run: yarn build
- uses: ./
if: always()
env:
PULUMI_CONFIG_PASSPHRASE: not-a-secret
with:
command: up --skip-preview
cloud-url: file://~
stack-name: dev
work-dir: .github/test-stacks/golang
- run: echo "The random string is ${{ steps.pulumi.outputs.name }}"
45 changes: 45 additions & 0 deletions .github/workflows/workflow_options.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: 'Application with Extra Options'
on:
pull_request:
push:
branches:
- main
- master

jobs:
test-workflow:
runs-on: ${{ matrix.os }}
name: Testing Pulumi ${{ matrix.command }} on ${{ matrix.os }}
strategy:
matrix:
command: [ up, refresh, destroy, preview ]
os: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 14.x
- name: Install pulumi
uses: pulumi/action-install-pulumi-cli@v1.0.1
- name: Create local stack
run: |
pulumi login --local
pulumi stack init dev
working-directory: .github/test-stacks/nodejs
env:
PULUMI_CONFIG_PASSPHRASE: not-a-secret
- run: npm install
working-directory: .github/test-stacks/nodejs
- uses: ./
env:
PULUMI_CONFIG_PASSPHRASE: not-a-secret
with:
command: ${{ matrix.command }}
cloud-url: file://~
stack-name: dev
work-dir: .github/test-stacks/nodejs
parallel: 3
message: this-is-just-a-test
expect-no-changes: false
diff: true
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,35 @@ The action can be configured with the following arguments:
equivalent of what would be passed to the `pulumi login` command. The action
will login to the appropriate backend on your behalf provided it is configured
with the correct access credentials for that backend.

- `comment-on-pr` - (optional) If `true`, then the action will add the results
of the Pulumi action to the PR

- `github-token` - (required if comment-on-pr) A GitHub token that has access
levels to allow the Action to comment on a PR.

### Extra options
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a small comment here to suggest that these extra options, are the equivalent of the command line args


- `parallel` - (optional) Allow P resource operations to run in parallel at once
(1 for no parallelism). Defaults to unbounded.

- `message` - (optional) Optional message to associate with the update operation

- `expect-no-changes` - (optional) Return an error if any changes occur during
this update

- `diff` - (optional) Display operation as a rich diff showing the overall
change

- `replace` - (optional) Specify resources to replace. Multiple resources can be
specified one per line

- `target` - (optional) Specify a single resource URN to update. Other resources
will not be updated. Multiple resources can be specified one per line

- `target-dependents` - (optional) Allows updating of dependent targets
discovered but not specified in target.

By default, this action will try to authenticate Pulumi with the
[Pulumi SaaS](https://app.pulumi.com/). If you have not specified a
`PULUMI_ACCESS_TOKEN` then you will need to specify an alternative backend via
Expand Down
24 changes: 24 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,30 @@ inputs:
description: 'A cloud URL to log in to'
required: false
default: ''
parallel:
description: 'Allow P resource operations to run in parallel at once (1 for no parallelism). Defaults to unbounded.'
required: false
default: '2147483647'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this relates to what the automation api does - this isn't actually the number we pass, this is to document what we can do

message:
description: 'Optional message to associate with the update operation'
required: false
default: ''
expect-no-changes:
description: 'Return an error if any changes occur during this update'
required: false
diff:
description: 'Display operation as a rich diff showing the overall change'
required: false
replace:
description: 'Specify resources to replace. Multiple resources can be specified one per line'
required: false
target:
description: 'Specify a single resource URN to update. Other resources will not be updated. Multiple resources can be specified one per line'
required: false
target-dependents:
description: 'Allows updating of dependent targets discovered but not specified in target.'
required: false
default: 'false'
outputs:
output:
description: Output from running command
Expand Down
24 changes: 20 additions & 4 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,22 @@ describe('config.ts', () => {
const c = await makeConfig();
expect(c).toBeTruthy();
expect(c).toEqual({
args: '',
command: 'up',
stackName: 'dev',
cloudUrl: 'file://~',
githubToken: 'n/a',
commentOnPr: false,
workDir: './',
upsert: false,
upsert: undefined,
options: {
parallel: undefined,
message: undefined,
diff: undefined,
expectNoChanges: undefined,
replace: undefined,
target: undefined,
targetDependents: undefined,
},
});
});
it('should fail if configuration are invalid', async () => {
Expand Down Expand Up @@ -66,14 +74,22 @@ describe('config.ts', () => {
const c = await makeConfig();
expect(c).toBeTruthy();
expect(c).toEqual({
args: '',
command: 'up',
stackName: 'dev',
cloudUrl: 'file://~',
githubToken: 'n/a',
commentOnPr: true,
workDir: './',
upsert: false,
upsert: undefined,
options: {
parallel: undefined,
message: undefined,
diff: undefined,
expectNoChanges: undefined,
replace: undefined,
target: undefined,
targetDependents: undefined,
},
});
});
});
53 changes: 39 additions & 14 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getInput } from '@actions/core';
import * as rt from 'runtypes';
import { parseArray, parseBoolean, parseNumber } from './libs/utils';

export const command = rt.Union(
rt.Literal('up'),
Expand All @@ -11,29 +12,53 @@ export const command = rt.Union(

export type Commands = rt.Static<typeof command>;

export const config = rt.Record({
command: command,
stackName: rt.String,
workDir: rt.String,
cloudUrl: rt.String.Or(rt.Undefined),
githubToken: rt.String.Or(rt.Undefined),
commentOnPr: rt.Boolean,
args: rt.String.Or(rt.Undefined),
upsert: rt.Boolean.Or(rt.Undefined),
export const options = rt.Partial({
parallel: rt.Number,
message: rt.String,
expectNoChanges: rt.Boolean,
diff: rt.Boolean,
replace: rt.Array(rt.String),
target: rt.Array(rt.String),
targetDependents: rt.Boolean,
});

export const config = rt
.Record({
// Required options
command: command,
stackName: rt.String,
workDir: rt.String,
commentOnPr: rt.Boolean,
options: options,
})
.And(
rt.Partial({
// Optional options
cloudUrl: rt.String,
githubToken: rt.String,
upsert: rt.Boolean,
}),
);

export type Config = rt.Static<typeof config>;

export async function makeConfig(): Promise<Config> {
const [command, ...args] = getInput('command', { required: true }).split(' ');
return config.check({
command,
command: getInput('command', { required: true }),
stackName: getInput('stack-name', { required: true }),
workDir: getInput('work-dir') || './',
cloudUrl: getInput('cloud-url'),
githubToken: getInput('github-token'),
commentOnPr: getInput('comment-on-pr') === 'true' ? true : false,
args: getInput('args') || args.join(' '),
upsert: getInput('upsert') === 'true' ? true : false,
commentOnPr: parseBoolean(getInput('comment-on-pr')),
upsert: parseBoolean(getInput('upsert')),
options: {
parallel: parseNumber(getInput('parallel')),
message: getInput('message'),
expectNoChanges: parseBoolean(getInput('expect-no-changes')),
diff: parseBoolean(getInput('diff')),
replace: parseArray(getInput('replace')),
target: parseArray(getInput('target')),
targetDependents: parseBoolean(getInput('target-dependents')),
},
});
}
40 changes: 40 additions & 0 deletions src/libs/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { parseArray, parseBoolean, parseNumber } from '../utils';

describe('utils.ts', () => {
it('should parse input to array', () => {
const input = `
hello
world
`;

const undef = '';

const inputArray = parseArray(input);

expect(inputArray[0]).toBe('hello');
expect(inputArray[1]).toBe('world');
expect(parseArray(undef)).toBeUndefined();
});

it('should parse boolean', () => {
const truthy = 'true';
const falsy = 'false';
const falsyTypo = 'fl';
const undef = '';

expect(parseBoolean(truthy)).toBeTruthy();
expect(parseBoolean(falsy)).toBeFalsy();
expect(parseBoolean(falsyTypo)).toBeFalsy();
expect(parseBoolean(undef)).toBeUndefined();
});

it('should parse number', () => {
const truthy = '1';
const falsy = 'false';
const undef = '';

expect(parseNumber(truthy)).toBe(1);
expect(parseNumber(falsy)).toBeNaN();
expect(parseNumber(undef)).toBeUndefined();
});
});
25 changes: 25 additions & 0 deletions src/libs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,28 @@ export function invariant(
throw new Error(message);
}
}

export function parseArray(input: string): string[] {
return parseUndefined(input)
? input.split(/\r?\n/).reduce<string[]>(
(acc, line) =>
acc
.concat(line.split(','))
.filter((pat) => pat)
.map((pat) => pat.trim()),
[],
)
: undefined;
}

export function parseUndefined(input: string): string | undefined {
return input === undefined || input === '' ? undefined : input;
}

export function parseBoolean(input: string): boolean | undefined {
return parseUndefined(input) ? input === 'true' : undefined;
}

export function parseNumber(input: string): number | undefined {
return parseUndefined(input) ? Number(input) : undefined;
}
Loading