init
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
const assert = require('assert')
|
||||
const exec = require('./exec')
|
||||
const fs = require('fs')
|
||||
const git = require('./git')
|
||||
const path = require('path')
|
||||
const paths = require('./paths')
|
||||
const Patterns = require('./patterns').Patterns
|
||||
|
||||
const defaultPatterns = ['+^master$', '+^v[0-9]+(\\.[0-9]+){0,2}$']
|
||||
exports.defaultPatterns = defaultPatterns
|
||||
|
||||
class ActionConfig {
|
||||
/**
|
||||
* Repository owner
|
||||
*/
|
||||
owner = ''
|
||||
|
||||
/**
|
||||
* Repository name
|
||||
*/
|
||||
repo = ''
|
||||
|
||||
/**
|
||||
* Ref include/exclude regexp patterns
|
||||
* @type {string[]}
|
||||
*/
|
||||
patterns = []
|
||||
|
||||
/**
|
||||
* Branch versions (ref to commit SHA)
|
||||
* @type {{[ref: string]: string}}
|
||||
*/
|
||||
branches = {}
|
||||
|
||||
/**
|
||||
* Default branch to checkout, defaults to master
|
||||
* @type {string}
|
||||
*/
|
||||
defaultBranch = 'master'
|
||||
|
||||
/**
|
||||
* Tag versions
|
||||
* @type {{[ref: string]: TagVersion}}
|
||||
*/
|
||||
tags = {}
|
||||
}
|
||||
exports.ActionConfig = ActionConfig
|
||||
|
||||
class TagVersion {
|
||||
/**
|
||||
* Commit SHA
|
||||
*/
|
||||
commit = ''
|
||||
|
||||
/**
|
||||
* SHA of the annotated tag, or undefined for a lightweight tag
|
||||
* @type {string|undefined}
|
||||
*/
|
||||
tag = undefined
|
||||
}
|
||||
exports.TagVersion = TagVersion
|
||||
|
||||
/**
|
||||
* Adds a new action config file
|
||||
* @param {string} owner
|
||||
* @param {string} repos
|
||||
* @param {string[]} patternStrings
|
||||
* @param {string} defaultBranch
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function add(owner, repo, patternStrings, defaultBranch) {
|
||||
assert.ok(owner, "Arg 'owner' must not be empty")
|
||||
assert.ok(repo, "Arg 'repo' must not be empty")
|
||||
assert.ok(patternStrings, "Arg 'patternStrings' must not be null")
|
||||
assert.ok(defaultBranch, "Arg 'defaultBranch' must not be empty")
|
||||
|
||||
if (patternStrings.length === 0) {
|
||||
patternStrings = defaultPatterns
|
||||
}
|
||||
|
||||
const patterns = new Patterns(patternStrings)
|
||||
const file = getFilePath(owner, repo)
|
||||
const config = new ActionConfig()
|
||||
config.owner = owner
|
||||
config.repo = repo
|
||||
config.patterns = patternStrings
|
||||
config.defaultBranch = defaultBranch
|
||||
|
||||
const tempDir = path.join(paths.temp, `${owner}_${repo}`)
|
||||
await exec.exec('rm', ['-rf', tempDir])
|
||||
await exec.exec('mkdir', ['-p', tempDir])
|
||||
const originalCwd = process.cwd()
|
||||
try {
|
||||
process.chdir(tempDir)
|
||||
await exec.exec('pwd')
|
||||
|
||||
// Clone
|
||||
await git.init()
|
||||
await git.gcAutoDisable()
|
||||
await git.remoteAdd(owner, repo)
|
||||
await git.fetch()
|
||||
|
||||
// Snapshot branches
|
||||
let branches = await git.branchList()
|
||||
branches = branches.filter(x => patterns.test(x))
|
||||
for (const branch of branches) {
|
||||
config.branches[branch] = await git.logCommitSha(`refs/remotes/origin/${branch}`)
|
||||
}
|
||||
|
||||
// Snapshot tags
|
||||
let tags = await git.tagList()
|
||||
tags = tags.filter(x => patterns.test(x))
|
||||
for (const tag of tags) {
|
||||
const tagVersion = new TagVersion()
|
||||
tagVersion.commit = await git.logCommitSha(`refs/tags/${tag}`)
|
||||
tagVersion.tag = await git.revParse(`refs/tags/${tag}`)
|
||||
if (tagVersion.commit === tagVersion.tag) {
|
||||
delete tagVersion.tag
|
||||
}
|
||||
config.tags[tag] = tagVersion
|
||||
}
|
||||
|
||||
// Write config
|
||||
await exec.exec('mkdir', ['-p', path.dirname(file)])
|
||||
await fs.promises.writeFile(file, JSON.stringify(config, null, ' '))
|
||||
console.log(`Added config file: ${file}`)
|
||||
}
|
||||
finally {
|
||||
process.chdir(originalCwd)
|
||||
}
|
||||
}
|
||||
exports.add = add
|
||||
|
||||
/**
|
||||
* Returns the action config file path
|
||||
* @param {string} owner
|
||||
* @param {string} repo
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
function getFilePath(owner, repo) {
|
||||
assert.ok(owner, "Arg 'owner' must not be empty")
|
||||
assert.ok(repo, "Arg 'repo' must not be empty")
|
||||
return path.join(paths.actionsConfig, `${owner}_${repo}.json`)
|
||||
}
|
||||
exports.getFilePath = getFilePath
|
||||
|
||||
/**
|
||||
* Returns the action config file paths
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
async function getFilePaths() {
|
||||
const names = await fs.promises.readdir(paths.actionsConfig)
|
||||
return names.filter(x => x.endsWith('.json')).map(x => path.join(paths.actionsConfig, x))
|
||||
}
|
||||
exports.getFilePaths = getFilePaths
|
||||
|
||||
/**
|
||||
* Loads an action config file
|
||||
* @param {string} owner
|
||||
* @param {string} repo
|
||||
* @returns {Promise<ActionConfig>}
|
||||
*/
|
||||
async function load(owner, repo) {
|
||||
assert.ok(owner, "Arg 'owner' must not be empty")
|
||||
assert.ok(repo, "Arg 'repo' must not be empty")
|
||||
const file = getFilePath(owner, repo)
|
||||
const buffer = await fs.promises.readFile(file)
|
||||
return JSON.parse(buffer.toString())
|
||||
}
|
||||
exports.load = load
|
||||
|
||||
/**
|
||||
* Loads an action config file from a specific path
|
||||
* @param {string} file File path
|
||||
* @returns {Promise<ActionConfig>}
|
||||
*/
|
||||
async function loadFromPath(file) {
|
||||
assert.ok(file, "Arg 'file' must not be empty")
|
||||
const buffer = await fs.promises.readFile(file)
|
||||
return JSON.parse(buffer.toString())
|
||||
}
|
||||
exports.loadFromPath = loadFromPath
|
||||
@@ -0,0 +1,99 @@
|
||||
const actionConfig = require('./action-config')
|
||||
const argHelper = require('./arg-helper')
|
||||
const assert = require('assert')
|
||||
const debugHelper = require('./debug-helper')
|
||||
const exec = require('./exec')
|
||||
const fsHelper = require('./fs-helper')
|
||||
const Patterns = require('./patterns').Patterns
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// Command line args
|
||||
const args = getArgs()
|
||||
const owner = args.owner
|
||||
const repo = args.repo
|
||||
const patterns = args.patterns
|
||||
const defaultBranch = args.defaultBranch || 'master'
|
||||
|
||||
// File exists?
|
||||
const file = actionConfig.getFilePath(owner, repo)
|
||||
assert.ok(!(await fsHelper.exists(file)), `File '${file}' already exists. Use 'update-action.sh' instead.`)
|
||||
|
||||
// Reinit _temp
|
||||
await fsHelper.reinitTemp()
|
||||
|
||||
// Add the config
|
||||
await actionConfig.add(owner, repo, patterns, defaultBranch)
|
||||
}
|
||||
catch (err) {
|
||||
// Help
|
||||
if (err.code === argHelper.helpCode) {
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
|
||||
// Arg error?
|
||||
if (err.code === argHelper.errorCode) {
|
||||
printUsage()
|
||||
console.error('')
|
||||
}
|
||||
|
||||
// Print error
|
||||
debugHelper.debug(err.stack)
|
||||
console.error(`ERROR: ${err.message}`)
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
class Args {
|
||||
owner = ''
|
||||
repo = ''
|
||||
patterns = []
|
||||
defaultBranch = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command line args
|
||||
* @returns {Args}
|
||||
*/
|
||||
function getArgs() {
|
||||
// Parse
|
||||
const parsedArgs = argHelper.parse([], ['default-branch'])
|
||||
if (parsedArgs.arguments.length < 1) {
|
||||
argHelper.throwError('Expected at least one arg')
|
||||
}
|
||||
|
||||
// Validate name with owner
|
||||
const nwo = parsedArgs.arguments[0]
|
||||
const splitNwo = nwo.split('/')
|
||||
if (splitNwo.length !== 2 || !splitNwo[0] || !splitNwo[1]) {
|
||||
argHelper.throwError(`Invalid nwo '${nwo}'`)
|
||||
}
|
||||
|
||||
// Validate patterns
|
||||
const patterns = parsedArgs.arguments.slice(1)
|
||||
if (patterns.length) {
|
||||
try {
|
||||
new Patterns(patterns)
|
||||
}
|
||||
catch (err) {
|
||||
argHelper.throwError(err.message)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
owner: splitNwo[0],
|
||||
repo: splitNwo[1],
|
||||
patterns: patterns,
|
||||
defaultBranch: parsedArgs.options['default-branch']
|
||||
}
|
||||
}
|
||||
|
||||
function printUsage() {
|
||||
console.error('USAGE: add-action.sh [--default-branch branch] nwo [(+|-)regexp [...]]')
|
||||
console.error(` --default-branch Default branch name. For example: master`)
|
||||
console.error(` nwo Name with owner. For example: actions/checkout`)
|
||||
console.error(` regexp Refs to include or exclude. Default: ${actionConfig.defaultPatterns.join(' ')}`)
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,93 @@
|
||||
const debugHelper = require('./debug-helper')
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*/
|
||||
class Arguments {
|
||||
flags = {}
|
||||
|
||||
options = {}
|
||||
|
||||
arguments = []
|
||||
}
|
||||
exports.Arguments = Arguments
|
||||
|
||||
const helpCode = 'ARGHELP'
|
||||
exports.helpCode = helpCode
|
||||
|
||||
const errorCode = 'ARGV'
|
||||
exports.errorCode = errorCode
|
||||
|
||||
/**
|
||||
* Parses the command line arguments
|
||||
* @param {string[]} flags Allowed flags
|
||||
* @param {string[]} options Allowed options
|
||||
* @param {boolean} allowArgs Whether to allow unnamed arguments
|
||||
* @returns {Arguments}
|
||||
*/
|
||||
function parse(flags, options, allowArgs) {
|
||||
flags = flags || []
|
||||
options = options || []
|
||||
allowArgs = typeof(allowArgs) === 'boolean' ? allowArgs : true
|
||||
|
||||
const result = new Arguments()
|
||||
|
||||
// Check for --help
|
||||
const argv = process.argv.slice(2)
|
||||
if (argv.some(x => x === '--help')) {
|
||||
throwError('Help requested', helpCode)
|
||||
}
|
||||
|
||||
// Parse
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const arg = argv[i]
|
||||
const nextArg = i + 1 < argv.length ? argv[i + 1] : null
|
||||
|
||||
// Starts with "--"
|
||||
if (arg.startsWith('--')) {
|
||||
const name = arg.substr(2)
|
||||
|
||||
// Legal flag
|
||||
if (flags.some(x => x === name)) {
|
||||
debugHelper.debug(`arg: ${arg} (flag)`)
|
||||
result.flags[name] = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Unknown option
|
||||
if (!options.some(x => x === name)) {
|
||||
throwError(`Unknown option '${name}'`)
|
||||
}
|
||||
|
||||
// Missing value following option
|
||||
if (nextArg === null || nextArg.startsWith('--')) {
|
||||
throwError(`Option '${name}' must have a value`)
|
||||
}
|
||||
|
||||
// Legal option
|
||||
debugHelper.debug(`arg: ${arg} (option)`)
|
||||
result.options[name] = nextArg
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
// Unexpected argument
|
||||
if (!allowArgs) {
|
||||
throwError(`Unexpected argument '${arg}'`)
|
||||
}
|
||||
|
||||
// Legal argument
|
||||
debugHelper.debug(`arg: ${arg}`)
|
||||
result.arguments.push(arg)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
exports.parse = parse
|
||||
|
||||
function throwError(message, code) {
|
||||
const err = new Error(message)
|
||||
err.code = code || errorCode
|
||||
throw err
|
||||
}
|
||||
exports.throwError = throwError
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Lowest version tested against :)
|
||||
expected_major=2
|
||||
expected_minor=11
|
||||
|
||||
if [ -z "$(which git || exit 0)" ]; then
|
||||
echo "Unable to find 'git' in the PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get <major>.<minor>
|
||||
# Note:
|
||||
# - Use "-o" instead of "--only-matching" for compatibility
|
||||
# - Use "-E" instead of "--extended-regexp" for compatibility
|
||||
major_minor=$(git --version | grep -o -E '[0-9]+\.[0-9]+')
|
||||
if [ -z "$major_minor" ]; then
|
||||
echo "Unable to determine git version. Expected output of 'git --version' to contain a version, for example '2.1'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
major=$(echo "$major_minor" | grep -o -E '^[0-9]+')
|
||||
minor=$(echo "$major_minor" | grep -o -E '[0-9]+$')
|
||||
|
||||
# Higher major version
|
||||
if [ $major -gt $expected_major ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Lower major version
|
||||
if [ $major -lt $expected_major ]; then
|
||||
echo "Git $expected_major.$expected_minor or higher must be in the PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Lower minor version
|
||||
if [ $minor -lt $expected_minor ]; then
|
||||
echo "Git $expected_major.$expected_minor or higher must be in the PATH"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$(which node || exit 0)" ]; then
|
||||
echo "Unable to find 'node' in the PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
node_version=$(node --version | grep --only-matching --extended-regexp 'v[0-9]+' | tr -d 'v')
|
||||
if [ -z "$node_version" ]; then
|
||||
echo "Unable to determine node version. Expected output of 'node --version' to contain a version, for example 'v12.13.1'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $node_version -lt 12 ]; then
|
||||
echo "Node 12 or higher must be in the PATH"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,12 @@
|
||||
const isDebug = ['TRUE', '1'].some(x => x === (process.env['GITHUB_ACTIONS_DEBUG'] || '').toUpperCase())
|
||||
|
||||
/**
|
||||
* Log the message if debug mode
|
||||
* @param {string} message
|
||||
*/
|
||||
function debug(message) {
|
||||
if (isDebug) {
|
||||
console.log(message)
|
||||
}
|
||||
}
|
||||
exports.debug = debug
|
||||
@@ -0,0 +1,49 @@
|
||||
const child_process = require('child_process')
|
||||
|
||||
class ExecResult {
|
||||
stdout = ''
|
||||
exitCode = 0
|
||||
}
|
||||
exports.ExecResult = ExecResult
|
||||
|
||||
/**
|
||||
* Executes a process
|
||||
* @param {string} command
|
||||
* @param {string[]} args
|
||||
* @param {boolea} allowAllExitCodes
|
||||
* @returns {Promise<ExecResult>}
|
||||
*/
|
||||
function exec(
|
||||
command = '',
|
||||
args = [],
|
||||
allowAllExitCodes = false
|
||||
) {
|
||||
process.stdout.write(`EXEC: ${command} ${args.join(' ')}\n`)
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
const execResult = new ExecResult()
|
||||
const cp = child_process.spawn(command, args, {})
|
||||
|
||||
// STDOUT
|
||||
cp.stdout.on('data', (data) => {
|
||||
process.stdout.write(data)
|
||||
execResult.stdout += data.toString()
|
||||
})
|
||||
|
||||
// STDERR
|
||||
cp.stderr.on('data', (data) => {
|
||||
process.stderr.write(data)
|
||||
})
|
||||
|
||||
// Close
|
||||
cp.on('close', (code) => {
|
||||
execResult.exitCode = code
|
||||
if (code === 0 || allowAllExitCodes) {
|
||||
resolve(execResult)
|
||||
} else {
|
||||
reject(new Error(`Command exited with code ${code}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
exports.exec = exec
|
||||
@@ -0,0 +1,37 @@
|
||||
const assert = require('assert')
|
||||
const exec = require('./exec')
|
||||
const fs = require('fs')
|
||||
const paths = require('./paths')
|
||||
|
||||
/**
|
||||
* Checks whether a path exists
|
||||
* @param {string} p File or directory path
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function exists(p) {
|
||||
assert.ok(p, "Arg 'p' must not be empty")
|
||||
try {
|
||||
await fs.promises.stat(p)
|
||||
return true
|
||||
}
|
||||
catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return false
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
exports.exists = exists
|
||||
|
||||
/**
|
||||
* Recreates the _temp directory
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function reinitTemp() {
|
||||
const temp = paths.temp
|
||||
assert.ok(temp, 'Expected paths.temp to be defined')
|
||||
await exec.exec('rm', ['-rf', temp])
|
||||
await exec.exec('mkdir', ['-p', temp])
|
||||
}
|
||||
exports.reinitTemp = reinitTemp
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Script dir
|
||||
script_dir="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
|
||||
# Trace
|
||||
. $script_dir/set-trace.sh
|
||||
|
||||
# Recreate script/generated/
|
||||
target_dir="$script_dir/../generated"
|
||||
rm -rf "$target_dir"
|
||||
mkdir -p "$target_dir"
|
||||
cd "$target_dir"
|
||||
|
||||
# Create each script
|
||||
for json_file in $script_dir/../../config/actions/*.json; do
|
||||
json="$(cat "$json_file")"
|
||||
split=( $(echo "$json" | jq --raw-output '.owner + " " + .repo') )
|
||||
owner="${split[0]}"
|
||||
repo="${split[1]}"
|
||||
script_file="${owner}_${repo}.sh"
|
||||
echo "Generating $PWD/$script_file"
|
||||
|
||||
# Append curl download command
|
||||
curl_download_commands=()
|
||||
|
||||
# Get an array of branch info. Each item contains "<branch> <sha>"
|
||||
branch_info=()
|
||||
IFS=$'\n' read -r -d '' -a branch_info < <( echo "$json" | jq --raw-output '.branches | to_entries | .[] | .key + " " + .value' && printf '\0' )
|
||||
|
||||
for item in "${branch_info[@]}"; do
|
||||
split=( $(echo $item) )
|
||||
branch="${split[0]}"
|
||||
sha="${split[1]}"
|
||||
|
||||
# Append curl download command
|
||||
curl_download_commands+=("curl -s -S -L -o '$sha.tar.gz' 'https://api.github.com/repos/$owner/$repo/tarball/$sha'")
|
||||
curl_download_commands+=("curl -s -S -L -o '$sha.zip' 'https://api.github.com/repos/$owner/$repo/zipball/$sha'")
|
||||
done
|
||||
|
||||
# Get an array of tag info. Each item contains "<tag> <tag_or_commit_sha> <commit_sha>"
|
||||
tag_info=()
|
||||
IFS=$'\n' read -r -d '' -a tag_info < <( echo "$json" | jq --raw-output '.tags | to_entries | .[] | .key + " " + if .value.tag? then .value.tag else .value.commit end + " " + .value.commit' && printf '\0' )
|
||||
|
||||
for item in "${tag_info[@]}"; do
|
||||
split=( $(echo $item) )
|
||||
tag="${split[0]}"
|
||||
sha="${split[1]}"
|
||||
commit="${split[2]}"
|
||||
|
||||
# Append curl download command
|
||||
curl_download_commands+=("curl -s -S -L -o '$sha.tar.gz' 'https://api.github.com/repos/$owner/$repo/tarball/$sha'")
|
||||
curl_download_commands+=("curl -s -S -L -o '$sha.zip' 'https://api.github.com/repos/$owner/$repo/zipball/$sha'")
|
||||
done
|
||||
|
||||
# Append curl commands
|
||||
echo "mkdir ${owner}_$repo" >> "$script_file"
|
||||
echo "pushd ${owner}_$repo" >> "$script_file"
|
||||
for curl_download_command in "${curl_download_commands[@]}"
|
||||
do
|
||||
echo "$curl_download_command" >> "$script_file"
|
||||
done
|
||||
echo "popd" >> "$script_file"
|
||||
done
|
||||
@@ -0,0 +1,116 @@
|
||||
const assert = require('assert')
|
||||
const exec = require('./exec')
|
||||
|
||||
/**
|
||||
* Gets a list of branches from origin. For example: ["master"]
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
async function branchList() {
|
||||
const result = []
|
||||
|
||||
// Note, this implementation uses "rev-parse --symbolic-full-name" because:
|
||||
// - The output from "branch --list" is difficult when in a detached HEAD state.
|
||||
// - There is a bug in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names.
|
||||
const execResult = await exec.exec('git', ['rev-parse', '--symbolic-full-name', '--remotes=origin'])
|
||||
for (let ref of execResult.stdout.trim().split('\n')) {
|
||||
ref = ref.trim()
|
||||
if (!ref) {
|
||||
continue
|
||||
}
|
||||
|
||||
const prefix = 'refs/remotes/origin/'
|
||||
if (!ref.startsWith(prefix) || ref.length === prefix.length) {
|
||||
throw new Error(`Unexpected branch format '${ref}'`)
|
||||
}
|
||||
|
||||
ref = ref.substr(prefix.length)
|
||||
result.push(ref)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
exports.branchList = branchList
|
||||
|
||||
/**
|
||||
* Disable automatic garbage collection
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function gcAutoDisable() {
|
||||
await exec.exec('git', ['config', '--local', 'gc.auto', '0'])
|
||||
}
|
||||
exports.gcAutoDisable = gcAutoDisable
|
||||
|
||||
/**
|
||||
* Fetches the repo
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function fetch() {
|
||||
const args = [
|
||||
'-c',
|
||||
'protocol.version=2',
|
||||
'fetch',
|
||||
'--tags',
|
||||
'--no-recurse-submodules',
|
||||
'origin'
|
||||
]
|
||||
|
||||
await exec.exec('git', args)
|
||||
}
|
||||
exports.fetch = fetch
|
||||
|
||||
/**
|
||||
* Initializes a repo
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function init() {
|
||||
await exec.exec('git', ['init'])
|
||||
}
|
||||
exports.init = init
|
||||
|
||||
/**
|
||||
* Resolves a ref to a commit SHA
|
||||
* @param {string} ref
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function logCommitSha(ref) {
|
||||
assert.ok(ref, "Arg 'ref' must not be empty")
|
||||
const execResult = await exec.exec('git', ['log', '-1', '--format=format:%H%n', ref])
|
||||
return execResult.stdout.trim()
|
||||
}
|
||||
exports.logCommitSha = logCommitSha
|
||||
|
||||
/**
|
||||
* Adds a remote
|
||||
* @param {string} owner
|
||||
* @param {string} repo
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function remoteAdd(owner, repo) {
|
||||
assert.ok(owner, "Arg 'owner' must not be empty")
|
||||
assert.ok(repo, "Arg 'repo' must not be empty")
|
||||
await exec.exec('git', ['remote', 'add', 'origin', `https://github.com/${owner}/${repo}.git`])
|
||||
}
|
||||
exports.remoteAdd = remoteAdd
|
||||
|
||||
/**
|
||||
* Resolves a ref to a SHA. For a branch or lightweight tag, the commit SHA is returned.
|
||||
* For an annotated tag, the tag SHA is returned.
|
||||
* @param {string} ref For example: 'refs/heads/master' or '/refs/tags/v1'
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function revParse(ref) {
|
||||
assert.ok(ref, "Arg 'ref' must not be empty")
|
||||
const execResult = await exec.exec('git', ['rev-parse', ref])
|
||||
return execResult.stdout.trim()
|
||||
}
|
||||
exports.revParse = revParse
|
||||
|
||||
/**
|
||||
* Returns a list of tags. For example: ["v1", "v2"]
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
async function tagList() {
|
||||
const execResult = await exec.exec('git', ['tag', '--list'])
|
||||
return execResult.stdout.trim().split('\n').filter(str => str && str.length > 0)
|
||||
}
|
||||
exports.tagList = tagList
|
||||
@@ -0,0 +1,39 @@
|
||||
const https = require("https");
|
||||
const fs = require("fs");
|
||||
const assert = require("assert");
|
||||
|
||||
/**
|
||||
* Performs a get request
|
||||
* @param {string} url
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function get(url) {
|
||||
assert.ok(url, "Arg 'url' must not be empty");
|
||||
var options = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"User-Agent": "node/" + process.version,
|
||||
},
|
||||
};
|
||||
var promise = new Promise((resolve, reject) => {
|
||||
var req = https.request(url, options, function (res) {
|
||||
var chunks = [];
|
||||
|
||||
res.on("data", function (chunk) {
|
||||
chunks.push(chunk);
|
||||
});
|
||||
|
||||
res.on("end", function (chunk) {
|
||||
var body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
||||
resolve(body);
|
||||
});
|
||||
res.on("error", function (error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
exports.get = get;
|
||||
@@ -0,0 +1 @@
|
||||
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
|
||||
@@ -0,0 +1,4 @@
|
||||
const path = require('path')
|
||||
|
||||
exports.actionsConfig = path.join(__dirname, '..', '..', 'config', 'actions')
|
||||
exports.temp = path.join(__dirname, '..', '..', '_temp')
|
||||
@@ -0,0 +1,66 @@
|
||||
const assert = require('assert')
|
||||
|
||||
class Patterns {
|
||||
patterns = []
|
||||
|
||||
/**
|
||||
* @param {string[]} p Array of pattern strings
|
||||
*/
|
||||
constructor(p) {
|
||||
assert.ok(p && p.length, "Arg 'p' must not be empty")
|
||||
for (const pattern of p) {
|
||||
this.patterns.push(new Pattern(pattern))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the ref is a match
|
||||
* @param {string} str
|
||||
* @returns {boolean}
|
||||
*/
|
||||
test(str) {
|
||||
assert.ok(str, "Arg 'str' must not be empty")
|
||||
let result = false
|
||||
for (const pattern of this.patterns) {
|
||||
result = pattern.test(str, result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class Pattern {
|
||||
include = true
|
||||
regexp = undefined
|
||||
|
||||
/**
|
||||
* @param {string} pattern
|
||||
*/
|
||||
constructor(pattern) {
|
||||
assert.ok(pattern, "Arg 'pattern' must not be empty")
|
||||
if (pattern.startsWith('-')) {
|
||||
this.include = false
|
||||
}
|
||||
else {
|
||||
assert.ok(pattern.startsWith('+'), 'Pattern must start with + or -')
|
||||
}
|
||||
|
||||
pattern = pattern.substr(1)
|
||||
assert.ok(pattern, 'Pattern must not be empty')
|
||||
this.regexp = new RegExp(pattern)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str String to test
|
||||
* @param {boolean} status Whether currently included or excluded
|
||||
*/
|
||||
test(str, status) {
|
||||
assert.ok(str, "Arg 'str' must not be empty")
|
||||
if (this.include) {
|
||||
return status || this.regexp.test(str)
|
||||
}
|
||||
|
||||
return status && !this.regexp.test(str)
|
||||
}
|
||||
}
|
||||
exports.Patterns = Patterns
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
GITHUB_ACTIONS_DEBUG="$(echo "$GITHUB_ACTIONS_DEBUG" | tr a-z A-Z)"
|
||||
if [ "$GITHUB_ACTIONS_DEBUG" = 'TRUE' -o "$GITHUB_ACTIONS_DEBUG" = '1' ]; then
|
||||
set -x
|
||||
fi
|
||||
@@ -0,0 +1,95 @@
|
||||
const actionConfig = require('./action-config')
|
||||
const argHelper = require('./arg-helper')
|
||||
const assert = require('assert')
|
||||
const debugHelper = require('./debug-helper')
|
||||
const fsHelper = require('./fs-helper')
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// Command line args
|
||||
const args = getArgs()
|
||||
|
||||
// Reinit _temp
|
||||
await fsHelper.reinitTemp()
|
||||
|
||||
// Get a list of action config files
|
||||
const files = args.all ? await actionConfig.getFilePaths() : [actionConfig.getFilePath(args.owner, args.repo)]
|
||||
debugHelper.debug(`files: ${files}`)
|
||||
for (const file of files) {
|
||||
// Load the config
|
||||
const config = await actionConfig.loadFromPath(file)
|
||||
const owner = config.owner
|
||||
const repo = config.repo
|
||||
const patterns = config.patterns
|
||||
const defaultBranch = config.defaultBranch
|
||||
assert.ok(patterns && patterns.length, 'Existing patterns must not be empty')
|
||||
await actionConfig.add(owner, repo, patterns, defaultBranch)
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
// Help
|
||||
if (err.code === argHelper.helpCode) {
|
||||
printUsage()
|
||||
return
|
||||
}
|
||||
|
||||
// Arg error?
|
||||
if (err.code === argHelper.errorCode) {
|
||||
printUsage()
|
||||
console.error('')
|
||||
}
|
||||
|
||||
// Print error
|
||||
debugHelper.debug(err.stack)
|
||||
console.error(`ERROR: ${err.message}`)
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
class Args {
|
||||
all = false
|
||||
owner = ''
|
||||
repo = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command line args
|
||||
* @returns {Args}
|
||||
*/
|
||||
function getArgs() {
|
||||
const parsedArgs = argHelper.parse(['all'])
|
||||
const result = new Args()
|
||||
result.all = !!parsedArgs.flags['all']
|
||||
|
||||
// All
|
||||
if (result.all) {
|
||||
// Validate no args
|
||||
if (parsedArgs.arguments.length) {
|
||||
argHelper.throwError(`Expected zero args when '--all' is specified`)
|
||||
}
|
||||
}
|
||||
// Not all
|
||||
else {
|
||||
// Validate exactly one arg
|
||||
if (parsedArgs.arguments.length !== 1) {
|
||||
argHelper.throwError('Expected exactly one arg')
|
||||
}
|
||||
|
||||
const nwo = parsedArgs.arguments[0]
|
||||
const splitNwo = nwo.split('/')
|
||||
if (splitNwo.length !== 2 || !splitNwo[0] || !splitNwo[1]) {
|
||||
argHelper.throwError(`Invalid nwo '${nwo}'`)
|
||||
}
|
||||
result.owner = splitNwo[0]
|
||||
result.repo = splitNwo[1]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function printUsage() {
|
||||
console.error('USAGE: update-action.sh nwo')
|
||||
console.error(` nwo Name with owner. For example: actions/checkout`)
|
||||
}
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user