mirror of
https://github.com/vxcontrol/run-on-arch-action.git
synced 2026-07-01 10:04:29 -04:00
Better dockerRunArgs handling, ensure slug is lowercase
Some `dockerRunArg` values would fail, due to improper handling of quotes. This is sidestepped by parsing the arguments into an array using `shlex`, then passing the array items as separate N arguments to `run-on-arch.sh`. It also seems that the container names must be lower-case, so we ensure the `slug()` function returns a lower-case string. Some GitHub repositories will have upper-case letters.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
on: [push]
|
||||
name: Advanced Example
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_job:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
on: [push]
|
||||
name: Basic Example
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
armv7_job:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: swift-build
|
||||
on: [push,pull_request]
|
||||
name: Build with Swift on armv7
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
testactions_job:
|
||||
@@ -8,7 +8,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Build with Swift on armv7
|
||||
- name: Build
|
||||
uses: ./
|
||||
with:
|
||||
arch: armv7
|
||||
|
||||
+53
-29
@@ -1,4 +1,4 @@
|
||||
name: test
|
||||
name: Unit Tests
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
@@ -38,41 +38,53 @@ jobs:
|
||||
arch: ${{ matrix.arch }}
|
||||
distro: ${{ matrix.distro }}
|
||||
env: |
|
||||
var_1: value_1
|
||||
var_2: value_2
|
||||
env_arch: ${{ matrix.arch }}
|
||||
env_distro: ${{ matrix.distro }}
|
||||
|
||||
# Test multiple argument formats
|
||||
dockerRunArgs: |
|
||||
-v "${PWD}/volume_1:/volume_1"
|
||||
-v "${PWD}/volume_2:/volume_2"
|
||||
--volume=${PWD}/volume_2:/volume_2
|
||||
"-v${PWD}/volume_3:/volume_3"
|
||||
-v "${PWD}/volume_4:/volume_4" -v "${PWD}/volume_5:/volume_5"
|
||||
|
||||
# Sourced on host, after container build, before container run
|
||||
setup: |
|
||||
distro_info=$(cat /etc/*-release | tr '[:upper:]' '[:lower:]' | tr '"' ' ' | tr '\n' ' ')
|
||||
|
||||
echo ::set-output name=host_arch::$(uname -m)
|
||||
echo ::set-output name=host_distro_info::$distro_info
|
||||
echo ::set-output name=host_var_1::$var_1
|
||||
echo ::set-output name=host_var_2::$var_2
|
||||
echo ::set-output name=host_shell_options::$-
|
||||
echo ::set-output name=host_arch::"$(uname -m)"
|
||||
echo ::set-output name=host_distro_info::"$distro_info"
|
||||
echo ::set-output name=host_env_arch::"$env_arch"
|
||||
echo ::set-output name=host_env_distro::"$env_distro"
|
||||
echo ::set-output name=host_shell_options::"$-"
|
||||
|
||||
# Setup volumes
|
||||
mkdir "${PWD}/volume_1"
|
||||
touch "${PWD}/volume_1/file_1"
|
||||
mkdir "${PWD}/volume_2"
|
||||
touch "${PWD}/volume_2/file_2"
|
||||
mkdir "${PWD}/volume_3"
|
||||
touch "${PWD}/volume_3/file_3"
|
||||
mkdir "${PWD}/volume_4"
|
||||
touch "${PWD}/volume_4/file_4"
|
||||
mkdir "${PWD}/volume_5"
|
||||
touch "${PWD}/volume_5/file_5"
|
||||
|
||||
# Run on container
|
||||
run: |
|
||||
distro_info=$(cat /etc/*-release | tr '[:upper:]' '[:lower:]' | sed 's/"//g' | tr '\n' ';')
|
||||
|
||||
echo ::set-output name=arch::$(uname -m)
|
||||
echo ::set-output name=distro_info::$distro_info
|
||||
echo ::set-output name=shebang::$(head -n 1 "$0")
|
||||
echo ::set-output name=var_1::$var_1
|
||||
echo ::set-output name=var_2::$var_2
|
||||
echo ::set-output name=volume_1_ls::$(ls /volume_1 || true)
|
||||
echo ::set-output name=volume_2_ls::$(ls /volume_2 || true)
|
||||
echo ::set-output name=shell_options::$-
|
||||
echo ::set-output name=arch::"$(uname -m)"
|
||||
echo ::set-output name=distro_info::"$distro_info"
|
||||
echo ::set-output name=shebang::"$(head -n 1 "$0")"
|
||||
echo ::set-output name=env_arch::"$env_arch"
|
||||
echo ::set-output name=env_distro::"$env_distro"
|
||||
echo ::set-output name=volume_1_ls::"$(ls /volume_1 || true)"
|
||||
echo ::set-output name=volume_2_ls::"$(ls /volume_2 || true)"
|
||||
echo ::set-output name=volume_3_ls::"$(ls /volume_3 || true)"
|
||||
echo ::set-output name=volume_4_ls::"$(ls /volume_4 || true)"
|
||||
echo ::set-output name=volume_5_ls::"$(ls /volume_5 || true)"
|
||||
echo ::set-output name=shell_options::"$-"
|
||||
|
||||
- name: Assert setup script runs on host (check arch info)
|
||||
run: |
|
||||
@@ -90,14 +102,14 @@ jobs:
|
||||
|
||||
- name: Assert setup script receives environment variables
|
||||
run: |
|
||||
var_1="${{ steps.build.outputs.host_var_1 }}"
|
||||
var_2="${{ steps.build.outputs.host_var_2 }}"
|
||||
arch="${{ steps.build.outputs.host_env_arch }}"
|
||||
distro="${{ steps.build.outputs.host_env_distro }}"
|
||||
|
||||
echo "Assert var_1: '$var_1' == 'value_1'"
|
||||
test "$var_1" == "value_1"
|
||||
echo "Assert env_arch: '$arch' == '${{ matrix.arch }}'"
|
||||
test "$arch" == "${{ matrix.arch }}"
|
||||
|
||||
echo "Assert var_2: '$var_2' == 'value_2'"
|
||||
test "$var_1" == "value_1"
|
||||
echo "Assert env_distro: '$distro' == '${{ matrix.distro }}'"
|
||||
test "$distro" == "${{ matrix.distro }}"
|
||||
|
||||
- name: Assert job would fail on setup script error
|
||||
run: |
|
||||
@@ -137,14 +149,14 @@ jobs:
|
||||
|
||||
- name: Assert container receives environment variables
|
||||
run: |
|
||||
var_1="${{ steps.build.outputs.var_1 }}"
|
||||
var_2="${{ steps.build.outputs.var_2 }}"
|
||||
arch="${{ steps.build.outputs.env_arch }}"
|
||||
distro="${{ steps.build.outputs.env_distro }}"
|
||||
|
||||
echo "Assert var_1: '$var_1' == 'value_1'"
|
||||
test "$var_1" == "value_1"
|
||||
echo "Assert env_arch: '$arch' == '${{ matrix.arch }}'"
|
||||
test "$arch" == "${{ matrix.arch }}"
|
||||
|
||||
echo "Assert var_2: '$var_2' == 'value_2'"
|
||||
test "$var_1" == "value_1"
|
||||
echo "Assert env_distro: '$distro' == '${{ matrix.distro }}'"
|
||||
test "$distro" == "${{ matrix.distro }}"
|
||||
|
||||
- name: Assert job would fail on run script error
|
||||
run: |
|
||||
@@ -159,6 +171,9 @@ jobs:
|
||||
run: |
|
||||
volume_1_ls="${{ steps.build.outputs.volume_1_ls }}"
|
||||
volume_2_ls="${{ steps.build.outputs.volume_2_ls }}"
|
||||
volume_3_ls="${{ steps.build.outputs.volume_3_ls }}"
|
||||
volume_4_ls="${{ steps.build.outputs.volume_4_ls }}"
|
||||
volume_5_ls="${{ steps.build.outputs.volume_5_ls }}"
|
||||
|
||||
echo "Assert volume_1_ls: '$volume_1_ls' == 'file_1'"
|
||||
test "$volume_1_ls" == "file_1"
|
||||
@@ -166,6 +181,15 @@ jobs:
|
||||
echo "Assert volume_2_ls: '$volume_2_ls' == 'file_2'"
|
||||
test "$volume_2_ls" == "file_2"
|
||||
|
||||
echo "Assert volume_3_ls: '$volume_3_ls' == 'file_3'"
|
||||
test "$volume_3_ls" == "file_3"
|
||||
|
||||
echo "Assert volume_4_ls: '$volume_4_ls' == 'file_4'"
|
||||
test "$volume_4_ls" == "file_4"
|
||||
|
||||
echo "Assert volume_5_ls: '$volume_5_ls' == 'file_5'"
|
||||
test "$volume_5_ls" == "file_5"
|
||||
|
||||
- name: Assert container determines correct default shell for distro
|
||||
run: |
|
||||
shebang="${{ steps.build.outputs.shebang }}"
|
||||
|
||||
@@ -27,7 +27,7 @@ The action also accepts some optional input parameters:
|
||||
A basic example that sets an output variable for use in subsequent steps:
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
armv7_job:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
name: Build on ubuntu-18.04 armv7
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- uses: uraimo/run-on-arch-action@v2.0.1
|
||||
- uses: uraimo/run-on-arch-action@v2.0.2
|
||||
name: Run commands
|
||||
id: runcmd
|
||||
with:
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
This shows how to use a matrix to produce platform-specific artifacts, and includes example values for the optional input parameters `setup`, `shell`, `env`, and `dockerRunArgs`.
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_job:
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- uses: uraimo/run-on-arch-action@v2.0.1
|
||||
- uses: uraimo/run-on-arch-action@v2.0.2
|
||||
name: Build artifact
|
||||
id: build
|
||||
with:
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "standard",
|
||||
"rules": {
|
||||
"camelcase": "off",
|
||||
"no-multiple-empty-lines": "off"
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- run: npm test
|
||||
env:
|
||||
CI: true
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Govostes
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
# node-shlex
|
||||
|
||||

|
||||
|
||||
`node-shlex` is a Node.js module for quoting and parsing shell commands.
|
||||
|
||||
The API was inspired by the [`shlex`][pyshlex] module from the Python Standard
|
||||
Library. However, the Python implementation is fairly complex, and supports a
|
||||
confusing matrix of modes that is not replicated here. `node-shlex` always
|
||||
operates in what the Python module calls "POSIX mode."
|
||||
|
||||
[pyshlex]: https://docs.python.org/3/library/shlex.html
|
||||
|
||||
As of version 2.0.0, Bash's [ANSI C strings][ansi-c] (`$'x'`) and
|
||||
[locale-specific translation strings][locale] (`$"x"`) are supported. This
|
||||
diverges from the Python `shlex` behavior but makes parsing more accurate.
|
||||
|
||||
[ansi-c]: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html
|
||||
[locale]: https://www.gnu.org/software/bash/manual/html_node/Locale-Translation.html
|
||||
|
||||
Note that `node-shlex` does not attempt to split on or otherwise parse
|
||||
operators (such as `2>/dev/null`), and it does not perform variable
|
||||
interpolation.
|
||||
|
||||
## Usage
|
||||
|
||||
### `shlex.quote()`
|
||||
|
||||
```node
|
||||
var quote = require("shlex").quote
|
||||
quote("abc") // returns: abc
|
||||
quote("abc def") // returns: 'abc def'
|
||||
quote("can't") // returns: 'can'"'"'t'
|
||||
```
|
||||
|
||||
### `shlex.split()`
|
||||
|
||||
```node
|
||||
var split = require("shlex").split
|
||||
split('ls -al /') // returns: [ 'ls', '-al', '/' ]
|
||||
split('rm -f "/Volumes/Macintosh HD"') // returns [ 'rm', '-f', '/Volumes/Macintosh HD' ]
|
||||
```
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"_from": "shlex@2.0.2",
|
||||
"_id": "shlex@2.0.2",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-i4p9nNXgBTILspHwZlBCNsZzwuVWW8SFx5dyIONrjL0R+AbMOPbg7ndqgGfjYivkYRTtZMKqIT8HT+QyOhPQWA==",
|
||||
"_location": "/shlex",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "shlex@2.0.2",
|
||||
"name": "shlex",
|
||||
"escapedName": "shlex",
|
||||
"rawSpec": "2.0.2",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "2.0.2"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/shlex/-/shlex-2.0.2.tgz",
|
||||
"_shasum": "debf51a145f1df9e7cb7cce04340ccb45120092d",
|
||||
"_spec": "shlex@2.0.2",
|
||||
"_where": "/Users/elijahrutschman/Development/run-on-arch-action",
|
||||
"author": {
|
||||
"name": "Ryan Govostes"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/rgov/node-shlex/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {},
|
||||
"deprecated": false,
|
||||
"description": "Node.js port of Python's shlex shell-like lexer",
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^5.15.3",
|
||||
"eslint-config-standard": "^12.0.0",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-node": "^7.0.1",
|
||||
"eslint-plugin-promise": "^4.0.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"mocha": "^5.2.0"
|
||||
},
|
||||
"homepage": "https://github.com/rgov/node-shlex#readme",
|
||||
"keywords": [
|
||||
"shell",
|
||||
"command-line",
|
||||
"cli",
|
||||
"lexer"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "shlex.js",
|
||||
"name": "shlex",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rgov/node-shlex.git"
|
||||
},
|
||||
"scripts": {
|
||||
"posttest": "eslint .",
|
||||
"test": "mocha"
|
||||
},
|
||||
"types": "shlex.d.ts",
|
||||
"version": "2.0.2"
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export function split(s: string): string[];
|
||||
export function quote(s: string): string;
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
'use strict'
|
||||
|
||||
/*
|
||||
Port of a subset of the features of CPython's shlex module, which provides a
|
||||
shell-like lexer. Original code by Eric S. Raymond and other contributors.
|
||||
*/
|
||||
|
||||
class Shlexer {
|
||||
constructor (string) {
|
||||
this.i = 0
|
||||
this.string = string
|
||||
|
||||
/**
|
||||
* Characters that will be considered whitespace and skipped. Whitespace
|
||||
* bounds tokens. By default, includes space, tab, linefeed and carriage
|
||||
* return.
|
||||
*/
|
||||
this.whitespace = ' \t\r\n'
|
||||
|
||||
/**
|
||||
* Characters that will be considered string quotes. The token accumulates
|
||||
* until the same quote is encountered again (thus, different quote types
|
||||
* protect each other as in the shell.) By default, includes ASCII single
|
||||
* and double quotes.
|
||||
*/
|
||||
this.quotes = `'"`
|
||||
|
||||
/**
|
||||
* Characters that will be considered as escape. Just `\` by default.
|
||||
*/
|
||||
this.escapes = '\\'
|
||||
|
||||
/**
|
||||
* The subset of quote types that allow escaped characters. Just `"` by default.
|
||||
*/
|
||||
this.escapedQuotes = '"'
|
||||
|
||||
/**
|
||||
* Whether to support ANSI C-style $'' quotes
|
||||
* https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html
|
||||
*/
|
||||
this.ansiCQuotes = true
|
||||
|
||||
/**
|
||||
* Whether to support localized $"" quotes
|
||||
* https://www.gnu.org/software/bash/manual/html_node/Locale-Translation.html
|
||||
*
|
||||
* The behavior is as if the current locale is set to C or POSIX, i.e., the
|
||||
* contents are not translated.
|
||||
*/
|
||||
this.localeQuotes = true
|
||||
|
||||
this.debug = false
|
||||
}
|
||||
|
||||
readChar () {
|
||||
return this.string.charAt(this.i++)
|
||||
}
|
||||
|
||||
processEscapes (string, quote, isAnsiCQuote) {
|
||||
if (!isAnsiCQuote && !this.escapedQuotes.includes(quote)) {
|
||||
// This quote type doesn't support escape sequences
|
||||
return string
|
||||
}
|
||||
|
||||
// We need to form a regex that matches any of the escape characters,
|
||||
// without interpreting any of the characters as a regex special character.
|
||||
let anyEscape = '[' + this.escapes.replace(/(.)/g, '\\$1') + ']'
|
||||
|
||||
// In regular quoted strings, we can only escape an escape character, and
|
||||
// the quote character itself.
|
||||
if (!isAnsiCQuote && this.escapedQuotes.includes(quote)) {
|
||||
let re = new RegExp(
|
||||
anyEscape + '(' + anyEscape + '|\\' + quote + ')', 'g')
|
||||
return string.replace(re, '$1')
|
||||
}
|
||||
|
||||
// ANSI C quoted strings support a wide variety of escape sequences
|
||||
if (isAnsiCQuote) {
|
||||
let patterns = {
|
||||
// Literal characters
|
||||
'([\\\\\'"?])': (x) => x,
|
||||
|
||||
// Non-printable ASCII characters
|
||||
'a': () => '\x07',
|
||||
'b': () => '\x08',
|
||||
'e|E': () => '\x1b',
|
||||
'f': () => '\x0c',
|
||||
'n': () => '\x0a',
|
||||
'r': () => '\x0d',
|
||||
't': () => '\x09',
|
||||
'v': () => '\x0b',
|
||||
|
||||
// Octal bytes
|
||||
'([0-7]{1,3})': (x) => String.fromCharCode(parseInt(x, 8)),
|
||||
|
||||
// Hexadecimal bytes
|
||||
'x([0-9a-fA-F]{1,2})': (x) => String.fromCharCode(parseInt(x, 16)),
|
||||
|
||||
// Unicode code units
|
||||
'u([0-9a-fA-F]{1,4})': (x) => String.fromCharCode(parseInt(x, 16)),
|
||||
'U([0-9a-fA-F]{1,8})': (x) => String.fromCharCode(parseInt(x, 16)),
|
||||
|
||||
// Control characters
|
||||
// https://en.wikipedia.org/wiki/Control_character#How_control_characters_map_to_keyboards
|
||||
'c(.)': (x) => {
|
||||
if (x === '?') {
|
||||
return '\x7f'
|
||||
} else if (x === '@') {
|
||||
return '\x00'
|
||||
} else {
|
||||
return String.fromCharCode(x.charCodeAt(0) & 31)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct an uber-RegEx that catches all of the above pattern
|
||||
let re = new RegExp(
|
||||
anyEscape + '(' + Object.keys(patterns).join('|') + ')', 'g')
|
||||
|
||||
// For each match, figure out which subpattern matched, and apply the
|
||||
// corresponding function
|
||||
return string.replace(re, function (m, p1) {
|
||||
for (let matched in patterns) {
|
||||
let mm = new RegExp('^' + matched + '$').exec(p1)
|
||||
if (mm === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
return patterns[matched].apply(null, mm.slice(1))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Should not get here
|
||||
return undefined
|
||||
}
|
||||
|
||||
* [Symbol.iterator] () {
|
||||
let inQuote = false
|
||||
let inDollarQuote = false
|
||||
let escaped = false
|
||||
let lastDollar = -2 // position of last dollar sign we saw
|
||||
let token
|
||||
|
||||
if (this.debug) {
|
||||
console.log('full input:', '>' + this.string + '<')
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const pos = this.i
|
||||
const char = this.readChar()
|
||||
|
||||
if (this.debug) {
|
||||
console.log(
|
||||
'position:', pos,
|
||||
'input:', '>' + char + '<',
|
||||
'accumulated:', token,
|
||||
'inQuote:', inQuote,
|
||||
'inDollarQuote:', inDollarQuote,
|
||||
'lastDollar:', lastDollar,
|
||||
'escaped:', escaped
|
||||
)
|
||||
}
|
||||
|
||||
// Ran out of characters, we're done
|
||||
if (char === '') {
|
||||
if (inQuote) { throw new Error('Got EOF while in a quoted string') }
|
||||
if (escaped) { throw new Error('Got EOF while in an escape sequence') }
|
||||
if (token !== undefined) { yield token }
|
||||
return
|
||||
}
|
||||
|
||||
// We were in an escape sequence, complete it
|
||||
if (escaped) {
|
||||
if (char === '\n') {
|
||||
// An escaped newline just means to continue the command on the next
|
||||
// line. We just need to ignore it.
|
||||
} else if (inQuote) {
|
||||
// If we are in a quote, just accumulate the whole escape sequence,
|
||||
// as we will interpret escape sequences later.
|
||||
token = (token || '') + escaped + char
|
||||
} else {
|
||||
// Just use the literal character
|
||||
token = (token || '') + char
|
||||
}
|
||||
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
|
||||
if (this.escapes.includes(char)) {
|
||||
if (!inQuote || inDollarQuote !== false || this.escapedQuotes.includes(inQuote)) {
|
||||
// We encountered an escape character, which is going to affect how
|
||||
// we treat the next character.
|
||||
escaped = char
|
||||
continue
|
||||
} else {
|
||||
// This string type doesn't use escape characters. Ignore for now.
|
||||
}
|
||||
}
|
||||
|
||||
// We were in a string
|
||||
if (inQuote !== false) {
|
||||
// String is finished. Don't grab the quote character.
|
||||
if (char === inQuote) {
|
||||
token = this.processEscapes(token, inQuote, inDollarQuote === '\'')
|
||||
inQuote = false
|
||||
inDollarQuote = false
|
||||
continue
|
||||
}
|
||||
|
||||
// String isn't finished yet, accumulate the character
|
||||
token = (token || '') + char
|
||||
continue
|
||||
}
|
||||
|
||||
// This is the start of a new string, don't accumulate the quotation mark
|
||||
if (this.quotes.includes(char)) {
|
||||
inQuote = char
|
||||
if (lastDollar === pos - 1) {
|
||||
if (char === '\'' && !this.ansiCQuotes) {
|
||||
// Feature not enabled
|
||||
} else if (char === '"' && !this.localeQuotes) {
|
||||
// Feature not enabled
|
||||
} else {
|
||||
inDollarQuote = char
|
||||
}
|
||||
}
|
||||
|
||||
token = (token || '') // fixes blank string
|
||||
|
||||
if (inDollarQuote !== false) {
|
||||
// Drop the opening $ we captured before
|
||||
token = token.slice(0, -1)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a dollar sign, record that we saw it in case it's the start of
|
||||
// an ANSI C or localized string
|
||||
if (inQuote === false && char === '$') {
|
||||
lastDollar = pos
|
||||
}
|
||||
|
||||
// This is whitespace, so yield the token if we have one
|
||||
if (this.whitespace.includes(char)) {
|
||||
if (token !== undefined) { yield token }
|
||||
token = undefined
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, accumulate the character
|
||||
token = (token || '') + char
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splits a given string using shell-like syntax.
|
||||
*
|
||||
* @param {String} s String to split.
|
||||
* @returns {String[]}
|
||||
*/
|
||||
exports.split = function (s) {
|
||||
return Array.from(new Shlexer(s))
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a potentially shell-unsafe string using quotes.
|
||||
*
|
||||
* @param {String} s String to quote
|
||||
* @returns {String}
|
||||
*/
|
||||
exports.quote = function (s) {
|
||||
if (s === '') { return '\'\'' }
|
||||
|
||||
var unsafeRe = /[^\w@%\-+=:,./]/
|
||||
if (!unsafeRe.test(s)) { return s }
|
||||
|
||||
return '\'' + s.replace(/'/g, '\'"\'"\'') + '\''
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import * as shlex from '../'
|
||||
|
||||
shlex.quote('test text')
|
||||
shlex.split('test text "multi word thing"')
|
||||
|
||||
// Should error
|
||||
shlex.quote()
|
||||
shlex.split()
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { assert } = require('chai')
|
||||
const shlex = require('../shlex')
|
||||
|
||||
describe('shlex.quote()', function () {
|
||||
const safeUnquoted = 'abcdefghijklmnopqrstuvwxyz' +
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
||||
'0123456789' +
|
||||
'@%_-+=:,./'
|
||||
const unicodeSample = '\xe9\xe0\xdf' // e + acute accent, a + grave, sharp s
|
||||
const unsafe = '"`$\\!' + unicodeSample
|
||||
|
||||
it('should escape the empty string', function () {
|
||||
assert.equal(shlex.quote(''), '\'\'')
|
||||
})
|
||||
|
||||
it('should not escape safe strings', function () {
|
||||
assert.equal(shlex.quote(safeUnquoted), safeUnquoted)
|
||||
})
|
||||
|
||||
it('should escape strings containing spaces', function () {
|
||||
assert.equal(shlex.quote('test file name'), "'test file name'")
|
||||
})
|
||||
|
||||
it('should escape unsafe characters', function () {
|
||||
for (var char of unsafe) {
|
||||
const input = 'test' + char + 'file'
|
||||
const expected = '\'' + input + '\''
|
||||
|
||||
assert.equal(shlex.quote(input), expected)
|
||||
}
|
||||
})
|
||||
|
||||
it('should escape single quotes', function () {
|
||||
assert.equal(shlex.quote('test\'file'), '\'test\'"\'"\'file\'')
|
||||
})
|
||||
})
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { assert } = require('chai')
|
||||
const shlex = require('../shlex')
|
||||
|
||||
describe('shlex.split()', function () {
|
||||
// The original test data set was from shellwords, by Hartmut Goebel
|
||||
|
||||
const posixTestcases = [
|
||||
['x', 'x'],
|
||||
['foo bar', 'foo', 'bar'],
|
||||
[' foo bar', 'foo', 'bar'],
|
||||
[' foo bar ', 'foo', 'bar'],
|
||||
['foo bar bla fasel', 'foo', 'bar', 'bla', 'fasel'],
|
||||
['x y z xxxx', 'x', 'y', 'z', 'xxxx'],
|
||||
['\\x bar', 'x', 'bar'],
|
||||
['\\ x bar', ' x', 'bar'],
|
||||
['\\ bar', ' bar'],
|
||||
['foo \\x bar', 'foo', 'x', 'bar'],
|
||||
['foo \\ x bar', 'foo', ' x', 'bar'],
|
||||
['foo \\ bar', 'foo', ' bar'],
|
||||
['foo "bar" bla', 'foo', 'bar', 'bla'],
|
||||
['"foo" "bar" "bla"', 'foo', 'bar', 'bla'],
|
||||
['"foo" bar "bla"', 'foo', 'bar', 'bla'],
|
||||
['"foo" bar bla', 'foo', 'bar', 'bla'],
|
||||
["foo 'bar' bla", 'foo', 'bar', 'bla'],
|
||||
["'foo' 'bar' 'bla'", 'foo', 'bar', 'bla'],
|
||||
["'foo' bar 'bla'", 'foo', 'bar', 'bla'],
|
||||
["'foo' bar bla", 'foo', 'bar', 'bla'],
|
||||
['blurb foo"bar"bar"fasel" baz', 'blurb', 'foobarbarfasel', 'baz'],
|
||||
["blurb foo'bar'bar'fasel' baz", 'blurb', 'foobarbarfasel', 'baz'],
|
||||
['""', ''],
|
||||
["''", ''],
|
||||
['foo "" bar', 'foo', '', 'bar'],
|
||||
["foo '' bar", 'foo', '', 'bar'],
|
||||
['foo "" "" "" bar', 'foo', '', '', '', 'bar'],
|
||||
["foo '' '' '' bar", 'foo', '', '', '', 'bar'],
|
||||
['\\"', '"'],
|
||||
['"\\""', '"'],
|
||||
['"foo\\ bar"', 'foo\\ bar'],
|
||||
['"foo\\\\ bar"', 'foo\\ bar'],
|
||||
['"foo\\\\ bar\\""', 'foo\\ bar"'],
|
||||
['"foo\\\\" bar\\"', 'foo\\', 'bar"'],
|
||||
['"foo\\\\ bar\\" dfadf"', 'foo\\ bar" dfadf'],
|
||||
['"foo\\\\\\ bar\\" dfadf"', 'foo\\\\ bar" dfadf'],
|
||||
['"foo\\\\\\x bar\\" dfadf"', 'foo\\\\x bar" dfadf'],
|
||||
['"foo\\x bar\\" dfadf"', 'foo\\x bar" dfadf'],
|
||||
["\\'", "'"],
|
||||
["'foo\\ bar'", 'foo\\ bar'],
|
||||
["'foo\\\\ bar'", 'foo\\\\ bar'],
|
||||
["\"foo\\\\\\x bar\\\" df'a\\ 'df\"", "foo\\\\x bar\" df'a\\ 'df"],
|
||||
['\\"foo', '"foo'],
|
||||
['\\"foo\\x', '"foox'],
|
||||
['"foo\\x"', 'foo\\x'],
|
||||
['"foo\\ "', 'foo\\ '],
|
||||
['foo\\ xx', 'foo xx'],
|
||||
['foo\\ x\\x', 'foo xx'],
|
||||
['foo\\ x\\x\\"', 'foo xx"'],
|
||||
['"foo\\ x\\x"', 'foo\\ x\\x'],
|
||||
['"foo\\ x\\x\\\\"', 'foo\\ x\\x\\'],
|
||||
['"foo\\ x\\x\\\\""foobar"', 'foo\\ x\\x\\foobar'],
|
||||
["\"foo\\ x\\x\\\\\"\\'\"foobar\"", "foo\\ x\\x\\'foobar"],
|
||||
["\"foo\\ x\\x\\\\\"\\'\"fo'obar\"", "foo\\ x\\x\\'fo'obar"],
|
||||
["\"foo\\ x\\x\\\\\"\\'\"fo'obar\" 'don'\\''t'", "foo\\ x\\x\\'fo'obar", "don't"],
|
||||
["\"foo\\ x\\x\\\\\"\\'\"fo'obar\" 'don'\\''t' \\\\", "foo\\ x\\x\\'fo'obar", "don't", '\\'],
|
||||
["'foo\\ bar'", 'foo\\ bar'],
|
||||
["'foo\\\\ bar'", 'foo\\\\ bar'],
|
||||
['foo\\ bar', 'foo bar'],
|
||||
// ["foo#bar\nbaz", "foo", "baz"], // FIXME: Comments are not implemented
|
||||
[':-) ;-)', ':-)', ';-)'],
|
||||
['\u00e1\u00e9\u00ed\u00f3\u00fa', '\u00e1\u00e9\u00ed\u00f3\u00fa'],
|
||||
['hello \\\n world', 'hello', 'world']
|
||||
]
|
||||
|
||||
const ansiCTestcases = [
|
||||
['$\'x\'', 'x'], // non-escaped character
|
||||
['$\'\\a\'', '\x07'], // alert (bell)
|
||||
['$\'\\b\'', '\x08'], // backspace
|
||||
['$\'\\e\'', '\x1b'], // escape character
|
||||
['$\'\\E\'', '\x1b'], // escape character
|
||||
['$\'\\f\'', '\x0c'], // form feed / new page
|
||||
['$\'\\n\'', '\x0a'], // newline
|
||||
['$\'\\r\'', '\x0d'], // carriage return
|
||||
['$\'\\t\'', '\x09'], // horizontal tab
|
||||
['$\'\\v\'', '\x0b'], // vertical tab
|
||||
['$\'\\\\\'', '\\'], // backslash
|
||||
['$\'\\\'\'', '\''], // single quote
|
||||
['$\'\\"\'', '"'], // double quote
|
||||
['$\'\\?\'', '?'], // question mark
|
||||
['$\'\\79\'', '\x07\x39'], // octal + non-octal
|
||||
['$\'\\07\'', '\x07'], // octal, zero prefix
|
||||
['$\'\\xfx\'', '\x0f\x78'], // hex (one digit) + non-hex
|
||||
['$\'\\xffx\'', '\xff\x78'], // hex (two digits) + non-hex
|
||||
['$\'\\xxx\'', '\\xxx'], // invalid hex
|
||||
['$\'\\u2603\'', '☃'], // unicode character
|
||||
['$\'\\U2603\'', '☃'], // unicode character
|
||||
['$\'\\ca\'', '\x01'], // control-a character
|
||||
['$\'\\cA\'', '\x01'], // control-A character, same as above
|
||||
['$\'\\c@\'', '\x00'], // control-@ character: null
|
||||
['$\'\\c?\'', '\x7f'], // control-? character: del
|
||||
['$\'\\\\x30\'', '\\x30'],
|
||||
['x$\'y\'z', 'xyz'],
|
||||
['"x"$\'y\'"z"', 'xyz'],
|
||||
['$\'x\'"y"$\'z\'', 'xyz'],
|
||||
['x"$\'y\'"z', 'x$\'y\'z']
|
||||
]
|
||||
|
||||
const localeTestcases = [
|
||||
['$"x"', 'x'], // non-escaped character
|
||||
['$"\\""', '"'], // escaped quotation mark
|
||||
['$"\\\\"', '\\'], // escaped escape character
|
||||
['$"\\x33"', '\\x33'], // other escape sequences do not work
|
||||
['x$"y"z', 'xyz'],
|
||||
['"x"$"y""z"', 'xyz'],
|
||||
['$"x""y"$"z"', 'xyz'],
|
||||
['x"$"y""z', 'x$yz']
|
||||
]
|
||||
|
||||
it('should split according to POSIX rules', function () {
|
||||
posixTestcases.forEach(function (test) {
|
||||
const input = test[0]
|
||||
const expected = test.slice(1)
|
||||
|
||||
assert.deepEqual(shlex.split(input), expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('should split ANSI C strings', function () {
|
||||
ansiCTestcases.forEach(function (test) {
|
||||
const input = test[0]
|
||||
const expected = test.slice(1)
|
||||
|
||||
assert.deepEqual(shlex.split(input), expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('should split localized strings', function () {
|
||||
localeTestcases.forEach(function (test) {
|
||||
const input = test[0]
|
||||
const expected = test.slice(1)
|
||||
|
||||
assert.deepEqual(shlex.split(input), expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
Generated
+6
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "run-on-arch",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -14,6 +14,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz",
|
||||
"integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ=="
|
||||
},
|
||||
"shlex": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/shlex/-/shlex-2.0.2.tgz",
|
||||
"integrity": "sha512-i4p9nNXgBTILspHwZlBCNsZzwuVWW8SFx5dyIONrjL0R+AbMOPbg7ndqgGfjYivkYRTtZMKqIT8HT+QyOhPQWA=="
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
|
||||
|
||||
+2
-1
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "run-on-arch",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"description": "Multi architecture support for GitHub Actions",
|
||||
"author": "Umberto Raimondi, Elijah Shaw-Rutschman",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.1.1",
|
||||
"@actions/exec": "^1.0.1",
|
||||
"shlex": "^2.0.2",
|
||||
"yaml": "^1.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -2,10 +2,11 @@ const core = require('@actions/core')
|
||||
const fs = require('fs');
|
||||
const path = require('path')
|
||||
const YAML = require('yaml');
|
||||
const shlex = require('shlex');
|
||||
const { exec } = require('@actions/exec')
|
||||
|
||||
function slug(str) {
|
||||
return str.replace(/[^a-zA-Z0-9]/g, '-');
|
||||
return str.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
@@ -48,9 +49,8 @@ async function main() {
|
||||
commands,
|
||||
);
|
||||
|
||||
// Sanitize dockerRunArgs: replace escaped and unescaped newlines with a space
|
||||
let dockerRunArgs = (core.getInput('dockerRunArgs') || '')
|
||||
.replace(/\\?[\r\n]+/g, ' ');
|
||||
// Parse dockerRunArgs into an array with shlex
|
||||
const dockerRunArgs = shlex.split(core.getInput('dockerRunArgs') || '');
|
||||
|
||||
const githubToken = core.getInput('githubToken') || '';
|
||||
|
||||
@@ -76,7 +76,7 @@ async function main() {
|
||||
throw new Error(`run-on-arch: env ${key} value must be flat.`);
|
||||
}
|
||||
env[key] = value;
|
||||
dockerRunArgs += ` -e ${key} `;
|
||||
dockerRunArgs.push(`-e${key}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ async function main() {
|
||||
console.log('Configuring Docker for multi-architecture support')
|
||||
await exec(
|
||||
path.join(__dirname, 'run-on-arch.sh'),
|
||||
[ dockerFile, dockerRunArgs, containerName, shell],
|
||||
[ dockerFile, containerName, ...dockerRunArgs ],
|
||||
{ env },
|
||||
);
|
||||
}
|
||||
|
||||
+11
-7
@@ -4,12 +4,12 @@ set -euo pipefail
|
||||
|
||||
# Args
|
||||
DOCKERFILE=$1
|
||||
DOCKER_RUN_ARGS=$2
|
||||
CONTAINER_NAME=$3
|
||||
CONTAINER_NAME=$2
|
||||
# Remainder of args get passed to docker
|
||||
declare -a DOCKER_RUN_ARGS=${@:3:${#@}}
|
||||
|
||||
# Defaults
|
||||
ACTION_DIR="$(cd "$(dirname "$0")"/.. >/dev/null 2>&1 ; pwd -P)"
|
||||
GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
||||
PACKAGE_REGISTRY="docker.pkg.github.com/${GITHUB_REPOSITORY}/${CONTAINER_NAME}"
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -44,7 +44,7 @@ build_container () {
|
||||
|
||||
# If the GITHUB_TOKEN env var has a value, the container images will be
|
||||
# cached between builds.
|
||||
if [[ -z "$GITHUB_TOKEN" ]]
|
||||
if [[ -z "${GITHUB_TOKEN:-}" ]]
|
||||
then
|
||||
docker build . --file "$DOCKERFILE" --tag "${CONTAINER_NAME}:latest"
|
||||
else
|
||||
@@ -61,6 +61,7 @@ build_container () {
|
||||
echo "$GITHUB_TOKEN" | docker login docker.pkg.github.com \
|
||||
-u "$GITHUB_ACTOR" \
|
||||
--password-stdin
|
||||
set -x
|
||||
set "$BASH_FLAGS"
|
||||
|
||||
docker pull "$PACKAGE_REGISTRY:latest" || true
|
||||
@@ -78,8 +79,11 @@ run_container () {
|
||||
# Run user-provided setup script, in same shell
|
||||
source "${ACTION_DIR}/src/run-on-arch-setup.sh"
|
||||
|
||||
# Interpolate DOCKER_RUN_ARGS, to support evaluation of $VAR references
|
||||
DOCKER_RUN_ARGS=$(eval echo "$DOCKER_RUN_ARGS")
|
||||
# Interpolate DOCKER_RUN_ARGS, to support evaluation of $VAR references
|
||||
for i in "${!DOCKER_RUN_ARGS[@]}"
|
||||
do
|
||||
DOCKER_RUN_ARGS[$i]=$(eval echo "${DOCKER_RUN_ARGS[$i]}")
|
||||
done
|
||||
|
||||
chmod +x "${ACTION_DIR}/src/run-on-arch-commands.sh"
|
||||
|
||||
@@ -123,7 +127,7 @@ run_container () {
|
||||
-v "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \
|
||||
-v "${ACTION_DIR}:${ACTION_DIR}" \
|
||||
--tty \
|
||||
$DOCKER_RUN_ARGS \
|
||||
${DOCKER_RUN_ARGS[@]} \
|
||||
"${CONTAINER_NAME}:latest" \
|
||||
"${ACTION_DIR}/src/run-on-arch-commands.sh"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user