Skip to content

Commit

Permalink
Add regex support to include/exclude (replaces starts_with)
Browse files Browse the repository at this point in the history
Also:
- Adds proper unit tests
  • Loading branch information
oNaiPs committed Feb 5, 2023
1 parent 6e5830e commit 80af0bb
Show file tree
Hide file tree
Showing 10 changed files with 419 additions and 86 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: 'build-test'
on:
pull_request:
push:
branches:
- main
- 'releases/*'

jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
npm install
- run: |
npm run all
33 changes: 21 additions & 12 deletions .github/workflows/test.yml → .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
name: 'build-test'
name: 'e2e'
on:
pull_request:
push:
branches:
- main
- 'releases/*'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
npm install
- run: |
npm run all
test-exclude:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -55,7 +45,7 @@ jobs:
[[ "${PREF_SECRET_3}" != "" ]] && echo "PREF_SECRET_3 should be unset, got ${PREF_SECRET_3}" && exit 1
true
shell: bash

test-convert:
runs-on: ubuntu-latest
steps:
Expand All @@ -73,6 +63,25 @@ jobs:
true
shell: bash

test-convert-prefix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Export secrets to env variables (starts with, include)
uses: ./
with:
prefix: MY_
convert_prefix: false
convert: lower
secrets: ${{ toJSON(secrets) }}
- name: Verify secrets to env variables (starts with, include)
run: |
[[ "${MY_secret_1}" != "VALUE_1" ]] && echo "Could not export MY_secret_1 secret, value is ${MY_secret_1}" && exit 1
[[ "${MY_secret_2}" != "VALUE_2" ]] && echo "Could not export MY_secret_2 secret, value is ${MY_secret_2}" && exit 1
[[ "${MY_secret_3}" != "VALUE_3" ]] && echo "Could not export MY_secret_3 secret, value is ${MY_secret_3}" && exit 1
true
shell: bash

# These jobs always fail, for now we just use it to check if the output is the expected
test-errors:
runs-on: ubuntu-latest
Expand Down
39 changes: 14 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,32 @@ steps:

**Include or exclude secrets:**

Exclude defined secret(s) from list of secrets (comma separated).
Exclude defined secret(s) from list of secrets (comma separated, supports regex).

```yaml
steps:
- uses: actions/checkout@v3
- uses: oNaiPs/secrets-to-env-action@v1
with:
secrets: ${{ toJSON(secrets) }}
exclude: MY_SECRET, MY_OTHER_SECRET
exclude: MY_SECRET, MY_OTHER_SECRETS*
# MY_SECRET is not exported
```

**Only** include secret(s) from list of secrets (comma separated).
**Only** include secret(s) from list of secrets (comma separated, supports regex).

```yaml
steps:
- uses: actions/checkout@v3
- uses: oNaiPs/secrets-to-env-action@v1
with:
secrets: ${{ toJSON(secrets) }}
include: MY_SECRET, MY_OTHER_SECRET
include: MY_SECRET, MY_OTHER_SECRETS*
- run: echo "Value of MY_SECRET: $MY_SECRET"
```

To export secrets that start with a given string, you can use `include: PREFIX_*`.

NOTE: If specified secret does not exist, it is ignored.

**Add a prefix:**
Expand All @@ -111,20 +113,22 @@ steps:
- run: echo "Value of PREFIXED_MY_SECRET: $PREFIXED_MY_SECRET"
```

**Only export secrets that start with a given string:**
**Convert:**

Converts all exported secrets according to a [template](https://github.com/blakeembrey/change-case#core).
Available: `lower, upper, camel, constant, pascal, snake`.

```yaml
steps:
- uses: actions/checkout@v3
- uses: oNaiPs/secrets-to-env-action@v1
with:
secrets: ${{ toJSON(secrets) }}
starts_with: PREFIX_
- run: env
# observe that only vars with PREFIX_ were exported
convert: lower
- run: echo "Value of my_secret: $my_secret"
```

**Only apply string conversions (see below) for secrets that start with a given string:**
**Include or skip the prefix on conversion (default is true):**

```yaml
steps:
Expand All @@ -133,28 +137,13 @@ steps:
with:
secrets: ${{ toJSON(secrets) }}
starts_with: PREFIX_
starts_with_convert_prefix: false
convert: lower
convert_prefix: false
- run: env
# observe that only vars with PREFIX_ were exported
# E.g. secret with PREFIX_KEY_1 would become PREFIX_key_1
```

**Convert:**

Converts all exported secrets according to a [template](https://github.com/blakeembrey/change-case#core).
Available: `lower, upper, camel, constant, pascal, snake`.

```yaml
steps:
- uses: actions/checkout@v3
- uses: oNaiPs/secrets-to-env-action@v1
with:
secrets: ${{ toJSON(secrets) }}
convert: lower
- run: echo "Value of my_secret: $my_secret"
```

## How it works

This action uses the input in `secrets` to read all the secrets in the JSON format, and exporting all the variables one by one.
Expand Down
223 changes: 212 additions & 11 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,215 @@
import * as cp from 'child_process'
import * as path from 'path'
import {expect, test} from '@jest/globals'

// shows how the runner will run a javascript action with env / stdout protocol
test('test runs', () => {
process.env['INPUT_SECRETS'] = '{"AAA": "123"}'
const np = process.execPath
const ip = path.join(__dirname, '..', 'lib', 'main.js')
const options: cp.ExecFileSyncOptions = {
env: process.env
}
console.log(cp.execFileSync(np, [ip], options).toString())
import {expect, jest, test} from '@jest/globals'
import * as core from '@actions/core'
import main from '../src/main'

jest.mock('@actions/core')

let mockedCore: jest.Mocked<typeof core>

jest.mocked(core.debug).mockImplementation(s => console.log(`DEBUG: ${s}`))
jest.mocked(core.info).mockImplementation(s => console.log(`INFO: ${s}`))
jest.mocked(core.warning).mockImplementation(s => console.log(`WARNING: ${s}`))

function mockInputs(inputs: {[key: string]: string}) {
jest.mocked(core.getInput).mockImplementation(s => inputs[s] || '')
}

describe('secrets-to-env-action', () => {
let inputSecrets: {[key: string]: string}
let newSecrets: {[key: string]: string}

beforeEach(() => {
inputSecrets = {
MY_SECRET_1: 'VALUE_1',
MY_SECRET_2: 'VALUE_2',
my_low_secret_1: 'low_value_1'
}

newSecrets = {}
jest
.mocked(core.exportVariable)
.mockImplementation((k, v) => (newSecrets[k] = v))
})

it('exports all variables', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets)
})
main()
expect(newSecrets).toEqual(inputSecrets)
})

it('excludes variables (single)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
exclude: 'MY_SECRET_1'
})
main()
delete inputSecrets.MY_SECRET_1
expect(newSecrets).toEqual(inputSecrets)
})

it('excludes variables (array)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
exclude: 'MY_SECRET_1,MY_SECRET_2,ignore'
})
main()
delete inputSecrets.MY_SECRET_1
delete inputSecrets.MY_SECRET_2
expect(newSecrets).toEqual(inputSecrets)
})

it('excludes variables (regex)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
exclude: 'MY_SECRET_*,ignore'
})
main()
delete inputSecrets.MY_SECRET_1
delete inputSecrets.MY_SECRET_2
expect(newSecrets).toEqual(inputSecrets)
})

it('includes variables (single)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_1'
})
main()

expect(newSecrets).toEqual({
MY_SECRET_1: inputSecrets.MY_SECRET_1
})
})

it('includes variables (array)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_1, MY_SECRET_2, ignore'
})
main()

expect(newSecrets).toEqual({
MY_SECRET_1: inputSecrets.MY_SECRET_1,
MY_SECRET_2: inputSecrets.MY_SECRET_2
})
})

it('includes variables (regex)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_*'
})
main()

expect(newSecrets).toEqual({
MY_SECRET_1: inputSecrets.MY_SECRET_1,
MY_SECRET_2: inputSecrets.MY_SECRET_2
})
})

it('adds a prefix', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
prefix: 'PREF_',
include: 'MY_SECRET_1, MY_SECRET_2'
})
main()

expect(newSecrets).toEqual({
PREF_MY_SECRET_1: inputSecrets.MY_SECRET_1,
PREF_MY_SECRET_2: inputSecrets.MY_SECRET_2
})
})

it('converts key (lower)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_1, MY_SECRET_2',
convert: 'lower'
})
main()

expect(newSecrets).toEqual({
my_secret_1: inputSecrets.MY_SECRET_1,
my_secret_2: inputSecrets.MY_SECRET_2
})
})

it('converts key (camel)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_1, MY_SECRET_2',
convert: 'camel'
})
main()

expect(newSecrets).toEqual({
mySecret_1: inputSecrets.MY_SECRET_1,
mySecret_2: inputSecrets.MY_SECRET_2
})
})

it('converts key (pascal)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_1, MY_SECRET_2',
convert: 'pascal'
})
main()

expect(newSecrets).toEqual({
MySecret_1: inputSecrets.MY_SECRET_1,
MySecret_2: inputSecrets.MY_SECRET_2
})
})

it('converts key (snake)', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_1, MY_SECRET_2',
convert: 'snake'
})
main()

expect(newSecrets).toEqual({
my_secret_1: inputSecrets.MY_SECRET_1,
my_secret_2: inputSecrets.MY_SECRET_2
})
})

it('converts prefix', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_1, MY_SECRET_2',
prefix: 'PREFIX_',
convert: 'snake',
convert_prefix: 'true'
})
main()

expect(newSecrets).toEqual({
prefix_my_secret_1: inputSecrets.MY_SECRET_1,
prefix_my_secret_2: inputSecrets.MY_SECRET_2
})
})

it('does not convert prefix', () => {
mockInputs({
secrets: JSON.stringify(inputSecrets),
include: 'MY_SECRET_1, MY_SECRET_2',
prefix: 'PREFIX_',
convert: 'snake',
convert_prefix: 'false'
})
main()

expect(newSecrets).toEqual({
PREFIX_my_secret_1: inputSecrets.MY_SECRET_1,
PREFIX_my_secret_2: inputSecrets.MY_SECRET_2
})
})
})
Loading

0 comments on commit 80af0bb

Please sign in to comment.