Skip to content

Commit

Permalink
refactor: Change to 'composite' action (#4)
Browse files Browse the repository at this point in the history
* ci: Skip slash-command workflow when possible

* refactor: Change action to be 'composite'

* ci: Fix self-invocation from node to pwsh

* ci: Add return value tests

* docs: Add notes in readme

* ci: disable Get-ActionInput/Set-ActionOutput tests

* tests: add ci self-test for context accessors

* fix: in ci checks use 'exit 1' instead of return

* ci: cleanup demo token as part of user script

* docs: Add changelog about 'composite' refactor

* docs: fix demo workflow comment, simplify example
  • Loading branch information
amis92 authored Aug 24, 2020
1 parent 2c55529 commit 410cfa7
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 138 deletions.
1 change: 1 addition & 0 deletions .github/workflows/chatops.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
jobs:
dispatch:
runs-on: ubuntu-latest
if: startsWith(github.event.comment.body, '/')
steps:
- name: /command dispatch
uses: peter-evans/slash-command-dispatch@v1
Expand Down
64 changes: 58 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ jobs:
Import-Module Pester -MinimumVersion '5.0'
Invoke-Pester -CI
- name: Invoke action
env:
TEMP: ${{ runner.temp }}
run: |
$env:INPUT_SCRIPT = 'Write-Host "ok"; return "value"'
$output = node ./dist/index.js
$env:PWSH_SCRIPT_ACTION_TEXT = 'Write-Host "ok"; return "value"'
$output = pwsh -file ./action.ps1
if ($LASTEXITCODE -ne 0) {
$output | Write-Error
throw 'node failed'
throw 'pwsh failed'
}
$diff = Compare-Object @('ok','::set-output name=result::value') $output -CaseSensitive
if ($diff) {
Expand All @@ -42,7 +44,7 @@ jobs:
./build-docs.ps1 -Clean
if (git status --porcelain) {
Write-Host "::error::Documentation isn't up to date"
return 1
exit 1
}
self-testing:
strategy:
Expand All @@ -52,6 +54,34 @@ jobs:
steps:
- uses: actions/checkout@v2

# return string result
- name: Test that returned result string is not json serialized (act)
uses: ./
id: test-result-string
with:
script: return "testmsg"
- name: Test that returned result string is not json serialized (assert)
run: |
$result = '${{ steps.test-result-string.outputs.result }}'
if ($result -ne 'testmsg') {
Write-Host "::error::Return string test failed: invalid result.`n$result"
exit 1
}
# return object result
- name: Test that returned result object is json serialized (act)
uses: ./
id: test-result-object
with:
script: return [ordered]@{ a = 1; b = "c" }
- name: Test that returned result object is json serialized (assert)
run: |
$result = '${{ steps.test-result-object.outputs.result }}'
if ($result -ne '{"a":1,"b":"c"}') {
Write-Host "::error::Return object test failed: invalid result.`n$result"
exit 1
}
# throw
- name: Test that throwing causes action to fail (act)
uses: ./
Expand All @@ -64,16 +94,35 @@ jobs:
$outcome = '${{ steps.test-throwing-fails-action.outcome }}'
if ($outcome -ne 'failure') {
Write-Host "::error::Throwing test failed: invalid outcome.`n$outcome"
return 1
exit 1
}
$errMsg = '${{ steps.test-throwing-fails-action.outputs.error }}'
if ($errMsg -ne 'test error message') {
Write-Host "::error::Throwing test failed: invalid error output.`n$errMsg"
return 1
exit 1
}
# check access to contexts
- name: Test that workflow contexts are available
uses: ./
with:
script: |
function Test-Value ($expression, $expected) {
$actual = Invoke-Expression $expression
if ("$actual" -ne $expected) {
throw "Failed assertion:'$expression' evaluated to '$actual', expected '$expected'."
}
}
Test-Value '$github.token' '${{ github.token }}'
Test-Value '$job.status' '${{ job.status }}'
Test-Value '$runner.os' '${{ runner.os }}'
Test-Value '$strategy."fail-fast"' '${{ strategy.fail-fast }}'
Test-Value '$matrix.os' '${{ matrix.os }}'
# Get-ActionInput
# Get-ActionInput isn't really testable in a script
- name: Get-ActionInput test
if: "false"
uses: ./
with:
custom-input: "test"
Expand All @@ -88,12 +137,15 @@ jobs:
}
# Set-ActionOutput
# Set-ActionOutput is tested by virtue of setting 'result' and 'error' outputs
- name: Set-ActionOutput test (act)
if: "false"
uses: ./
id: Set-ActionOutput
with:
script: Set-ActionOutput test-output 'test-value'
- name: Set-ActionOutput test (assert)
if: "false"
uses: ./
with:
script: |
Expand Down
17 changes: 8 additions & 9 deletions .github/workflows/demo-command.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: Demo command
# example:
# /demo
# ```powershell
# Write-Host "shows up in a log"
# Write-Host "doesn't show up in a result comment, only in workflow logs"
# Write-Output "shows up in a result comment"
# ```
on:
repository_dispatch:
Expand All @@ -16,7 +17,7 @@ jobs:
uses: peter-evans/create-or-update-comment@v1
with:
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
body: '[Workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})'
body: "[Workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})"
- name: Get script text
uses: Amadevus/pwsh-script@v1
id: get-script-text
Expand All @@ -34,17 +35,15 @@ jobs:
else {
$script = $body
}
# construct safe github ctx without token
$githubSafe = $github.Clone()
$githubSafe.Remove('token')
Set-ActionOutput githubSafe $githubSafe
return $script
- name: Execute user script
uses: Amadevus/pwsh-script@v1
id: user-script
with:
github: ${{ steps.get-script-text.outputs.githubSafe }}
script: ${{ steps.get-script-text.outputs.result }}
script: |
$github.token = $null
$github.event.client_payload = $null
${{ steps.get-script-text.outputs.result }}
- name: Prettify result json
uses: Amadevus/pwsh-script@v1
id: pretty-result
Expand Down Expand Up @@ -86,4 +85,4 @@ jobs:
if: failure()
with:
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
reactions: "-1"
reactions: "-1"
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- Refactored into a 'composite' action which has following implications ([#4]):
- Action runs slightly faster because there's no 'node' process in between (or io stream redirects).
- Action has now just single `script` input, and you cannot "add" outputs other than automatic "result" and "error".

### Removed
- All optional inputs - until "composite" refactor, they were used to pass workflow contexts to the action.
It's no longer necessary, since 'composite' action can "grab" them on it's own.
- Ability to set custom `outputs` from the script - now only `result` and `error` are set (as outlined in readme).

[#4]: https://github.com/Amadevus/pwsh-script/pull/4

## [1.0.0] - 2020-06-10

### Added
Expand Down
49 changes: 28 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,15 @@ GitHub Action to run PowerShell scripts that use the workflow run context - insp
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Amadevus/pwsh-script)](https://github.com/Amadevus/pwsh-script/releases/latest)
![GitHub commits since latest release (by date)](https://img.shields.io/github/commits-since/Amadevus/pwsh-script/latest)

In order to use this action, `script` input is provided. The value of that input should be
In order to use this action, `script` input is required. The value of that input should be
the body of a PowerShell script.

The following is initialized before your script is executed:
The following variables are initialized before your script is executed:
- `$github` is an object representing the workflow's [`github` context]
- `$job` is an object representing the workflow's [`job` context]
- `$runner` is an object representing the workflow's [`runner` context]
- `$strategy` is an object representing the workflow's [`strategy` context]
- `$matrix` is an object representing the workflow's [`matrix` context]

Environment variables are accessed in the standard PowerShell way (`$env:my_var`).

**Note** This action requires `pwsh` to actually be available and on PATH of the runner - which
is the case for all GitHub-provided runner VMs; for your own runners you need to take care of that yourself.

This action has an extensive self-testing suite in [CI workflow](.github/workflows/ci.yml).

[actions/github-script]: https://github.com/actions/github-script
[`@actions/core`]: https://github.com/actions/toolkit/tree/master/packages/core
[`github` context]: https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context
Expand All @@ -32,6 +24,10 @@ This action has an extensive self-testing suite in [CI workflow](.github/workflo
[`strategy` context]: https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#strategy-context
[`matrix` context]: https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#matrix-context

## Demo

You can try out this action yourself by commenting on a [demo issue](https://github.com/Amadevus/pwsh-script/issues/2). Instructions in the issue.

## Reading step results
The return value of the script will be made available in the step's outputs under the `result` key.
```yml
Expand Down Expand Up @@ -75,44 +71,55 @@ will be set as an `error` output of the action.
## Actions cmdlets
A module called `GitHubActionsCore` will be imported in the scope of your script. It provides commands
that are available for JavaScript Actions by [`@actions/core`] package, such as:
- `Set-ActionOutput`
- `Add-ActionPath`
- `Write-ActionWarning`
- `Set-ActionFailed`

For module documentation, see [GitHubActionsCore README](docs/GitHubActionsCore/README.md).

The module has a good test suite written in Pester.

## Notes

- This action requires `pwsh` to actually be available and on PATH of the runner - which
is the case for all GitHub-provided runner VMs; for your own runners you need to take care of that yourself.
- This action is a [`composite` action](https://docs.github.com/en/actions/creating-actions/creating-a-composite-run-steps-action).
- This action has an extensive self-testing suite in [CI workflow](.github/workflows/ci.yml).
- Although available in the imported module, `Get-ActionInput` and `Set-ActionOutput` won't really work when used as part of this action.

## Examples

```yml
- uses: Amadevus/pwsh-script@v1
id: script
with:
script: |
Write-ActionDebug "This will be visible only when ACTIONS_STEP_DEBUG secret is set"
Write-ActionDebug "Visible only when ACTIONS_STEP_DEBUG secret is set"
# we have access to full context objects:
# access full context objects:
if ($github.event.repository.full_name -ne $github.repository) {
throw "something fishy's going on, repos don't match" # will cause step to fail
# throwing causes the step to fail
throw "something fishy's going on, repos don't match"
}
$someData = Get-MyCustomData
# this data may contain action-command-like strings (e.g. '::warning::...')
# we can prevent interpreting these by GitHub by printing them in NoCommandsBlock:
# data may contain workflow command strings (e.g. '::warning::...')
# prevent runner interpreting these
Invoke-ActionNoCommandsBlock -GenerateToken {
Write-Host $someData # this won't result in any commands
# this won't result in any workflow commands
Write-Host $someData
Write-ActionError "not interpreted as error"
}
# now we can send commands again
# commands work again
# let's set env:BE_AWESOME=always, but for all the following actions/steps as well:
# set env:BE_AWESOME=always here and for the following steps
Set-ActionVariable BE_AWESOME always
# also we'll add path to our custom tool to PATH for the following steps:
# add our custom tool to PATH for the following steps:
$toolPath = Resolve-Path ./tools/bin
Add-ActionPath $toolPath
# let's also warn if it's too late for people to work in Greenwich ;)
# warn if it's too late for people to work in Greenwich ;)
if ([datetime]::UtcNow.Hour -ge 22) {
Write-ActionWarning "It's time to go to bed. Don't write code late at night! ⚠"
}
Expand Down
24 changes: 13 additions & 11 deletions action.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@

Import-Module $PSScriptRoot/lib/GitHubActionsCore

function CreateContext($name) {
$ctx = Get-ActionInput $name | ConvertFrom-Json -AsHashtable -NoEnumerate
function Private:CreateContext($name) {
$varName = "PWSH_SCRIPT_ACTION_$($name.ToUpper())"
$value = (Get-ChildItem "Env:$varName" -ErrorAction:SilentlyContinue).Value
$ctx = "$value" | ConvertFrom-Json -AsHashtable -NoEnumerate
Set-Variable -Name $name -Value $ctx -Scope Script -Option Constant
}

CreateContext github
CreateContext job
CreateContext runner
CreateContext strategy
CreateContext matrix

Remove-Item Function:CreateContext
Private:CreateContext github
Private:CreateContext job
Private:CreateContext runner
Private:CreateContext strategy
Private:CreateContext matrix

try {
$result = Invoke-Expression "$(Get-ActionInput 'script' -Required)"
Set-ActionOutput 'result' $result
$Private:scriptFile = New-Item (Join-Path $env:TEMP "$(New-Guid).ps1") -ItemType File
Set-Content $Private:scriptFile "$env:PWSH_SCRIPT_ACTION_TEXT"
$Private:result = Invoke-Expression $Private:scriptFile
Set-ActionOutput 'result' $Private:result
}
catch {
Set-ActionOutput 'error' $_.ToString()
Expand Down
39 changes: 17 additions & 22 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,27 @@ inputs:
script:
description: PowerShell script to execute in Actions-hydrated context
required: true
github:
description: "'github' workflow context"
required: false
default: ${{ toJson(github) }}
job:
description: "'job' workflow context"
required: false
default: ${{ toJson(job) }}
runner:
description: "'runner' workflow context"
required: false
default: ${{ toJson(runner) }}
strategy:
description: "'strategy' workflow context"
required: false
default: ${{ toJson(strategy) }}
matrix:
description: "'matrix' workflow context"
required: false
default: ${{ toJson(matrix) }}
outputs:
result:
description: Return value of script execution
value: ${{ steps.script.outputs.result }}
error:
description: Exception details, if any was thrown during script execution.
value: ${{ steps.script.outputs.error }}
runs:
using: node12
main: dist/index.js
using: composite
steps:
- run: ${{ github.action_path }}/action.ps1
id: script
shell: pwsh
env:
TEMP: ${{ runner.temp }}
PWSH_SCRIPT_ACTION_TEXT: ${{ inputs.script }}
PWSH_SCRIPT_ACTION_GITHUB: ${{ toJson(github) }}
PWSH_SCRIPT_ACTION_JOB: ${{ toJson(job) }}
PWSH_SCRIPT_ACTION_RUNNER: ${{ toJson(runner) }}
PWSH_SCRIPT_ACTION_STRATEGY: ${{ toJson(strategy) }}
PWSH_SCRIPT_ACTION_MATRIX: ${{ toJson(matrix) }}
branding:
icon: terminal
color: blue
10 changes: 0 additions & 10 deletions build.ps1

This file was deleted.

Loading

0 comments on commit 410cfa7

Please sign in to comment.