forked from actions/toolkit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request actions#4 from actions/features/core
IN PROGRESS: Features/core
- Loading branch information
Showing
14 changed files
with
440 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules/ | ||
packages/*/node_modules/ | ||
packages/*/lib/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules/ | ||
packages/*/node_modules/ | ||
packages/*/lib/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# `@actions/core` | ||
|
||
> Core functions for setting results, logging, registering secrets and exporting variables across actions | ||
## Usage | ||
|
||
See [src/core.ts](src/core.ts). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import * as os from 'os' | ||
import * as core from '../src/core' | ||
|
||
const testEnvVars = { | ||
'my var': '', | ||
'special char var \r\n];': '', | ||
'my var2': '', | ||
'my secret': '', | ||
'special char secret \r\n];': '', | ||
'my secret2': '', | ||
|
||
// Set inputs | ||
INPUT_MY_INPUT: 'val', | ||
INPUT_MISSING: '', | ||
'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ repsonse ' | ||
} | ||
|
||
describe('@actions/core', () => { | ||
beforeEach(() => { | ||
for (const key in testEnvVars) | ||
process.env[key] = testEnvVars[key as keyof typeof testEnvVars] | ||
|
||
process.stdout.write = jest.fn() | ||
}) | ||
|
||
afterEach(() => { | ||
for (const key in testEnvVars) Reflect.deleteProperty(testEnvVars, key) | ||
}) | ||
|
||
it('exportVariable produces the correct command and sets the env', () => { | ||
core.exportVariable('my var', 'var val') | ||
assertWriteCalls([`##[set-env name=my var;]var val${os.EOL}`]) | ||
}) | ||
|
||
it('exportVariable escapes variable names', () => { | ||
core.exportVariable('special char var \r\n];', 'special val') | ||
expect(process.env['special char var \r\n];']).toBe('special val') | ||
assertWriteCalls([ | ||
`##[set-env name=special char var %0D%0A%5D%3B;]special val${os.EOL}` | ||
]) | ||
}) | ||
|
||
it('exportVariable escapes variable values', () => { | ||
core.exportVariable('my var2', 'var val\r\n') | ||
expect(process.env['my var2']).toBe('var val\r\n') | ||
assertWriteCalls([`##[set-env name=my var2;]var val%0D%0A${os.EOL}`]) | ||
}) | ||
|
||
it('exportSecret produces the correct commands and sets the env', () => { | ||
core.exportSecret('my secret', 'secret val') | ||
expect(process.env['my secret']).toBe('secret val') | ||
assertWriteCalls([ | ||
`##[set-env name=my secret;]secret val${os.EOL}`, | ||
`##[set-secret]secret val${os.EOL}` | ||
]) | ||
}) | ||
|
||
it('exportSecret escapes secret names', () => { | ||
core.exportSecret('special char secret \r\n];', 'special secret val') | ||
expect(process.env['special char secret \r\n];']).toBe('special secret val') | ||
assertWriteCalls([ | ||
`##[set-env name=special char secret %0D%0A%5D%3B;]special secret val${ | ||
os.EOL | ||
}`, | ||
`##[set-secret]special secret val${os.EOL}` | ||
]) | ||
}) | ||
|
||
it('exportSecret escapes secret values', () => { | ||
core.exportSecret('my secret2', 'secret val\r\n') | ||
expect(process.env['my secret2']).toBe('secret val\r\n') | ||
assertWriteCalls([ | ||
`##[set-env name=my secret2;]secret val%0D%0A${os.EOL}`, | ||
`##[set-secret]secret val%0D%0A${os.EOL}` | ||
]) | ||
}) | ||
|
||
it('getInput gets non-required input', () => { | ||
expect(core.getInput('my input')).toBe('val') | ||
}) | ||
|
||
it('getInput gets required input', () => { | ||
expect(core.getInput('my input', {required: true})).toBe('val') | ||
}) | ||
|
||
it('getInput throws on missing required input', () => { | ||
expect(() => core.getInput('missing', {required: true})).toThrow( | ||
'Input required and not supplied: missing' | ||
) | ||
}) | ||
|
||
it('getInput doesnt throw on missing non-required input', () => { | ||
expect(core.getInput('missing', {required: false})).toBe('') | ||
}) | ||
|
||
it('getInput is case insensitive', () => { | ||
expect(core.getInput('My InPuT')).toBe('val') | ||
}) | ||
|
||
it('getInput handles special characters', () => { | ||
expect(core.getInput('special chars_\'\t"\\')).toBe('\'\t"\\ repsonse') | ||
}) | ||
|
||
it('setNeutral sets the correct exit code', () => { | ||
core.setFailed('Failure message') | ||
expect(process.exitCode).toBe(1) | ||
}) | ||
|
||
it('setFailure sets the correct exit code and failure message', () => { | ||
core.setFailed('Failure message') | ||
expect(process.exitCode).toBe(1) | ||
assertWriteCalls([`##[error]Failure message${os.EOL}`]) | ||
}) | ||
|
||
it('setFailure escapes the failure message', () => { | ||
core.setFailed('Failure \r\n\nmessage\r') | ||
expect(process.exitCode).toBe(1) | ||
assertWriteCalls([`##[error]Failure %0D%0A%0Amessage%0D${os.EOL}`]) | ||
}) | ||
|
||
it('error sets the correct error message', () => { | ||
core.error('Error message') | ||
assertWriteCalls([`##[error]Error message${os.EOL}`]) | ||
}) | ||
|
||
it('error escapes the error message', () => { | ||
core.error('Error message\r\n\n') | ||
assertWriteCalls([`##[error]Error message%0D%0A%0A${os.EOL}`]) | ||
}) | ||
|
||
it('warning sets the correct message', () => { | ||
core.warning('Warning') | ||
assertWriteCalls([`##[warning]Warning${os.EOL}`]) | ||
}) | ||
|
||
it('warning escapes the message', () => { | ||
core.warning('\r\nwarning\n') | ||
assertWriteCalls([`##[warning]%0D%0Awarning%0A${os.EOL}`]) | ||
}) | ||
|
||
it('debug sets the correct message', () => { | ||
core.debug('Debug') | ||
assertWriteCalls([`##[debug]Debug${os.EOL}`]) | ||
}) | ||
|
||
it('debug escapes the message', () => { | ||
core.debug('\r\ndebug\n') | ||
assertWriteCalls([`##[debug]%0D%0Adebug%0A${os.EOL}`]) | ||
}) | ||
}) | ||
|
||
// Assert that process.stdout.write calls called only with the given arguments. | ||
function assertWriteCalls(calls: string[]) { | ||
expect(process.stdout.write).toHaveBeenCalledTimes(calls.length) | ||
|
||
for (let i = 0; i < calls.length; i++) { | ||
expect(process.stdout.write).toHaveBeenNthCalledWith(i + 1, calls[i]) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
{ | ||
"name": "@actions/core", | ||
"version": "0.1.0", | ||
"description": "Actions core lib", | ||
"keywords": [ | ||
"core", | ||
"actions" | ||
], | ||
"author": "Bryan MacFarlane <[email protected]>", | ||
"homepage": "https://github.com/actions/toolkit/tree/master/packages/core", | ||
"license": "MIT", | ||
"main": "lib/core.js", | ||
"directories": { | ||
"lib": "lib", | ||
"test": "__tests__" | ||
}, | ||
"files": [ | ||
"lib" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/actions/toolkit.git" | ||
}, | ||
"scripts": { | ||
"test": "echo \"Error: run tests from root\" && exit 1", | ||
"tsc": "tsc" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/actions/toolkit/issues" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^12.0.2" | ||
}, | ||
"dependencies": { | ||
"@actions/exit": "^0.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import * as os from 'os' | ||
|
||
// For internal use, subject to change. | ||
|
||
/** | ||
* Commands | ||
* | ||
* Command Format: | ||
* ##[name key=value;key=value]message | ||
* | ||
* Examples: | ||
* ##[warning]This is the user warning message | ||
* ##[set-secret name=mypassword]definatelyNotAPassword! | ||
*/ | ||
export function issueCommand( | ||
command: string, | ||
properties: any, | ||
message: string | ||
) { | ||
const cmd = new Command(command, properties, message) | ||
process.stdout.write(cmd.toString() + os.EOL) | ||
} | ||
|
||
export function issue(name: string, message: string) { | ||
issueCommand(name, {}, message) | ||
} | ||
|
||
const CMD_PREFIX = '##[' | ||
|
||
class Command { | ||
constructor( | ||
command: string, | ||
properties: {[key: string]: string}, | ||
message: string | ||
) { | ||
if (!command) { | ||
command = 'missing.command' | ||
} | ||
|
||
this.command = command | ||
this.properties = properties | ||
this.message = message | ||
} | ||
|
||
command: string | ||
message: string | ||
properties: {[key: string]: string} | ||
|
||
toString() { | ||
let cmdStr = CMD_PREFIX + this.command | ||
|
||
if (this.properties && Object.keys(this.properties).length > 0) { | ||
cmdStr += ' ' | ||
for (const key in this.properties) { | ||
if (this.properties.hasOwnProperty(key)) { | ||
const val = this.properties[key] | ||
if (val) { | ||
// safely append the val - avoid blowing up when attempting to | ||
// call .replace() if message is not a string for some reason | ||
cmdStr += `${key}=${escape(`${val || ''}`)};` | ||
} | ||
} | ||
} | ||
} | ||
|
||
cmdStr += ']' | ||
|
||
// safely append the message - avoid blowing up when attempting to | ||
// call .replace() if message is not a string for some reason | ||
const message: string = `${this.message || ''}` | ||
cmdStr += escapeData(message) | ||
|
||
return cmdStr | ||
} | ||
} | ||
|
||
function escapeData(s: string): string { | ||
return s.replace(/\r/g, '%0D').replace(/\n/g, '%0A') | ||
} | ||
|
||
function escape(s: string): string { | ||
return s | ||
.replace(/\r/g, '%0D') | ||
.replace(/\n/g, '%0A') | ||
.replace(/]/g, '%5D') | ||
.replace(/;/g, '%3B') | ||
} |
Oops, something went wrong.