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

Support install-only workflow. #834

Merged
merged 9 commits into from Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/workflow.yml
Expand Up @@ -45,6 +45,49 @@ jobs:
outputs:
changed: ${{ steps.changes.outputs.dist }}

test-install-only:
needs: install-and-build
if: ${{ needs.install-and-build.outputs.changed == 'true' }}
runs-on: ${{ matrix.os }}
name: Install-only on ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
steps:
- name: Remove pre-installed Pulumi
shell: bash
env:
RUNNER_OS: ${{ matrix.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
- uses: actions/checkout@v3

- name: Download dist artifact
uses: actions/download-artifact@v3
with:
name: dist
path: dist

# If no action is specified, just install.
- uses: ./
env:
PULUMI_CONFIG_PASSPHRASE: not-a-secret
with:
config-map: "{name: {value: my-pet, secret: false}}"

- run: pulumi version

test-dotnet-stack:
needs: install-and-build
if: ${{ needs.install-and-build.outputs.changed == 'true' }}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -16,6 +16,10 @@
## 3.21.0 (2023-19-01)

- feat: Add deletion of stack after destroy (remove flag)
- feat: Add support for an `install` command, similar to
[setup-pulumi](https://github.com/marketplace/actions/setup-pulumi)
Copy link
Member

Choose a reason for hiding this comment

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

Why is this listed under 3.2.1 and not HEAD?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, probably because there was a release since this change was made and git merge decided to put it in the old position. Good catch.


--
Copy link
Member

Choose a reason for hiding this comment

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

What's with this trailing --?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure why this was added. Will fix.
(Somehow I didn't look over the changelog thoroughly.)

Copy link
Contributor

Choose a reason for hiding this comment

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

Figured out where the -- came from.
The -- separates the unreleased section from the rest. (https://github.com/pulumi/actions/blob/master/CONTRIBUTING.md)
However, because the original changelog entry was added below a then-unreleased entry,
when git merged these changes with master, it placed this entry under the original unreleased entry.


## 3.20.0 (2022-11-10)

Expand Down
21 changes: 17 additions & 4 deletions README.md
Expand Up @@ -34,12 +34,14 @@ This will check out the existing directory and run `pulumi preview`.

The action can be configured with the following arguments:

- `command` (required) - The command to run as part of the action. Accepted
values are `up` (update), `refresh`, `destroy` and `preview`.
- `command` (optional) - The command to run as part of the action. Accepted
values are `up` (alias: update), `refresh`, `destroy`, and `preview`. If
unspecified, the action will stop after installing Pulumi.

- `stack-name` (required) - The name of the stack that Pulumi will be operating
- `stack-name` (optional) - 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.
outside of your individual account. This field is required if a `command` was
specified.

- `work-dir` (optional) - The location of your Pulumi files. Defaults to `./`.

Expand Down Expand Up @@ -124,6 +126,17 @@ By default, this action will try to authenticate Pulumi with the
`PULUMI_ACCESS_TOKEN` then you will need to specify an alternative backend via
the `cloud-url` argument.

### Installation Only

If you want to only install the Pulumi CLI, omit the `command` field of the
action.

```yaml
- uses: pulumi/actions@v4
```

This will install Pulumi and exit without performing any other operations.

### Stack Outputs

[Stack outputs](https://www.pulumi.com/docs/intro/concepts/stack/#outputs) are
Expand Down
4 changes: 2 additions & 2 deletions action.yml
Expand Up @@ -7,10 +7,10 @@ branding:
inputs:
command:
description: 'Pulumi command to run, eg. up'
required: true
required: false
stack-name:
description: 'Which stack you want to apply to, eg. dev'
required: true
required: false
work-dir:
description: 'Location of your Pulumi files. Defaults to ./'
required: false
Expand Down
47 changes: 47 additions & 0 deletions src/__tests__/run.test.ts
Expand Up @@ -3,6 +3,53 @@ import { login } from '../login';

const spy = jest.spyOn(pulumiCli, 'run');

const installConfig: Record<string, string> = {
command: undefined,
pulumiVersion: "^4",
Copy link
Member

Choose a reason for hiding this comment

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

Why is this ^4 vs. ^3?

Copy link
Contributor

@abhinav abhinav Mar 9, 2023

Choose a reason for hiding this comment

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

Likely because the test wanted to verify with a non-default value.
However, I also see that this test doesn't actually verify the result at all.
I'm going to use something older (^2?) here and update the test to verify that the non-default value is picked up.

};

describe('Config without a provided command', () => {
let oldWorkspace = '';
beforeEach(() => {
spy.mockClear();
jest.resetModules();
// Save, then restore the current env var for GITHUB_WORKSPACE
oldWorkspace = process.env.GITHUB_WORKSPACE;
process.env.GITHUB_WORKSPACE = 'n/a';
});
afterEach(() => {
process.env.GITHUB_WORKSPACE = oldWorkspace;
});

it('should not be validated by makeConfig', async () => {
jest.mock('@actions/core', () => ({
getInput: jest.fn((name: string) => {
return installConfig[name];
}),
info: jest.fn(),
}));
jest.mock('@actions/github', () => ({
context: {},
}));
const { makeConfig } = require('../config');
await expect(makeConfig())
.rejects
.toThrow();
});

it('should be validated by makeInstallationConfig', async() => {
jest.mock('@actions/core', () => ({
getInput: jest.fn((name: string) => {
return installConfig[name];
}),
info: jest.fn(),
}));
const { makeInstallationConfig } = require('../config');
const conf = await makeInstallationConfig()
expect(conf).toBeTruthy();
});
});

describe('main.login', () => {
beforeEach(() => {
spy.mockClear();
Expand Down
24 changes: 21 additions & 3 deletions src/config.ts
Expand Up @@ -4,15 +4,33 @@ import * as rt from 'runtypes';
import { parseArray, parseBoolean, parseNumber } from './libs/utils';

export const command = rt.Union(
rt.Literal('up'),
rt.Literal('update'),
rt.Literal('refresh'),
rt.Literal('destroy'),
rt.Literal('preview'),
rt.Literal('refresh'),
rt.Literal('up'),
rt.Literal('update'),
);

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

// installationConfig is the expected Action inputs when
// the user intends to download the Pulumi CLI without
// running any other Pulumi operations.
// We expect command NOT to be provided.
export const installationConfig = rt.Record({
command: rt.Undefined,
pulumiVersion: rt.String,
});

export type InstallationConfig = rt.Static<typeof installationConfig>;

export function makeInstallationConfig(): rt.Result<InstallationConfig> {
return installationConfig.validate({
command: getInput('command') || undefined,
pulumiVersion: getInput('pulumi-version') || "^3",
});
}

export const options = rt.Partial({
parallel: rt.Number,
message: rt.String,
Expand Down
20 changes: 19 additions & 1 deletion src/main.ts
Expand Up @@ -8,16 +8,34 @@ import {
} from '@pulumi/pulumi/automation';
import invariant from 'ts-invariant';
import YAML from 'yaml';
import { Commands, makeConfig } from './config';
import { Commands, Config, InstallationConfig, makeConfig, makeInstallationConfig } from './config';
import { environmentVariables } from './libs/envs';
import { handlePullRequestMessage } from './libs/pr';
import * as pulumiCli from './libs/pulumi-cli';
import { login } from './login';

const main = async () => {
const downloadConfig = makeInstallationConfig();
if (downloadConfig.success) {
await installOnly(downloadConfig.value);
core.info("Pulumi has been successfully installed. Exiting.");
return;
}

// If we get here, we're not in install-only mode.
// Attempt to parse the full configuration and run the action.
const config = await makeConfig();
core.debug('Configuration is loaded');
runAction(config);
};

// installOnly is the main entrypoint of the program when the user
// intends to install the Pulumi CLI without running additional commands.
const installOnly = async (config: InstallationConfig): Promise<void> => {
await pulumiCli.downloadCli(config.pulumiVersion);
}

const runAction = async (config: Config): Promise<void> => {
await pulumiCli.downloadCli(config.options.pulumiVersion);
await login(config.cloudUrl, environmentVariables.PULUMI_ACCESS_TOKEN);

Expand Down