Rewrite: TypeScript-only (#57)
* chore(deps): bump dependecies * chore: add "inputs" script * chore: commit local work * chore: delete lib folder * chore: commit local work * BREAKING: edit action options - force has been deleted, pass the '-f' parameter to add and remove - ref is now called branch * [auto] build: update compiled version * chore: improve input checks * [auto] build: update compiled version * fix: pathspec errors should be catched differently * [auto] build: update compiled version * fix: check for 'add' and 'remove' before running commands * [auto] build: update compiled version * fix: log git commands * [auto] build: update compiled version * fix: improve logs' readability * [auto] build: update compiled version * chore: try not resetting files * [auto] build: update compiled version * fix: ignore errors on 2nd stage, log them on 1st * [auto] build: update compiled version * fix: signoff * [auto] build: update compiled version * fix: log git tag output * [auto] build: update compiled version * fix: use proper method for tagging * [auto] build: update compiled version * fix: use dedicated method for pushing tags * [auto] build: update compiled version * fix: using pure git tag command is better * [auto] build: update compiled version * fix: improve error handling * [auto] build: update compiled version * fix: I messed up the lines * [auto] build: update compiled version * fix: fix tags push when there's a forced update * [auto] build: update compiled version * fix: log errors too * chore: remove unused dependecy * [auto] build: update compiled version * fix: don't log when using ignoreErrors * [auto] build: update compiled version * fix: fetch tags * [auto] build: update compiled version * fix: try forcing tag fetch * [auto] build: update compiled version * fix: try using original command for pushing tags * [auto] build: update compiled version * fix: try removing repo from tag command * [auto] build: update compiled version * fix: try with object options * [auto] build: update compiled version * i don't even know anymore * [auto] build: update compiled version * fix: try deleting the tag when it's rejected * [auto] build: update compiled version * fix: isolate tag name * [auto] build: update compiled version * fix: log result of tag deletion * [auto] build: update compiled version * fix: use actual values instead of references * [auto] build: update compiled version * fix: join netrc path with module * [auto] build: update compiled version * fix-: try not using .netrc at all * [auto] build: update compiled version * fix: remove .netrc config * [auto] build: update compiled version * chore: remove old shell file * docs: update README * chore: fix typo in action manifest * chore: move ncc to dev-deps * fix: cwd not working properly * [auto] build: update compiled version
This commit is contained in:
@@ -1,88 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -u
|
||||
|
||||
echo "::group::Internal logs"
|
||||
|
||||
cd $INPUT_CWD
|
||||
echo "Running in $PWD."
|
||||
|
||||
# Set up .netrc file with GitHub credentials
|
||||
git_setup() {
|
||||
cat <<-EOF >$HOME/.netrc
|
||||
machine github.com
|
||||
login $GITHUB_ACTOR
|
||||
password $GITHUB_TOKEN
|
||||
|
||||
machine api.github.com
|
||||
login $GITHUB_ACTOR
|
||||
password $GITHUB_TOKEN
|
||||
EOF
|
||||
chmod 600 $HOME/.netrc
|
||||
git config --global user.email "$INPUT_AUTHOR_EMAIL"
|
||||
git config --global user.name "$INPUT_AUTHOR_NAME"
|
||||
}
|
||||
|
||||
add() {
|
||||
if $INPUT_FORCE; then f=-f; else f=; fi
|
||||
git add $INPUT_ADD $f
|
||||
}
|
||||
|
||||
remove() {
|
||||
if [ -n "$INPUT_REMOVE" ]; then git rm $INPUT_REMOVE; fi
|
||||
}
|
||||
|
||||
commit() {
|
||||
if $INPUT_SIGNOFF; then signoffcmd=--signoff; else signoffcmd=; fi
|
||||
git commit -m "$INPUT_MESSAGE" --author="$INPUT_AUTHOR_NAME <$INPUT_AUTHOR_EMAIL>" $signoffcmd
|
||||
}
|
||||
|
||||
tag() {
|
||||
if [ -n "$INPUT_TAG" ]; then git tag $INPUT_TAG; fi
|
||||
}
|
||||
|
||||
# This is needed to make the check work for untracked files
|
||||
echo "Staging files..."
|
||||
add
|
||||
remove
|
||||
|
||||
echo "Checking for uncommitted changes in the git working tree..."
|
||||
# This section only runs if there have been file changes
|
||||
if ! git diff --cached --quiet --exit-code; then
|
||||
git_setup
|
||||
|
||||
git fetch
|
||||
|
||||
# Switch branch (create a new one if it doesn't exist)
|
||||
echo "Switching/creating branch..."
|
||||
git checkout "$INPUT_REF" 2>/dev/null || git checkout -b "$INPUT_REF"
|
||||
|
||||
echo "Pulling from remote..."
|
||||
git fetch && git pull
|
||||
|
||||
echo "Resetting files..."
|
||||
git reset
|
||||
|
||||
echo "Adding files..."
|
||||
add
|
||||
|
||||
echo "Removing files..."
|
||||
remove
|
||||
|
||||
echo "Creating commit..."
|
||||
commit
|
||||
|
||||
echo "Tagging commit..."
|
||||
tag
|
||||
|
||||
echo "Pushing commits to repo..."
|
||||
git push --set-upstream origin "$INPUT_REF"
|
||||
|
||||
echo "Pushing tags to repo..."
|
||||
git push --set-upstream origin "$INPUT_REF" --force --tags
|
||||
|
||||
echo "::endgroup::"
|
||||
echo "Task completed."
|
||||
else
|
||||
echo "::endgroup::"
|
||||
echo "Working tree clean. Nothing to commit."
|
||||
fi
|
||||
3
src/inputs.ts
Normal file
3
src/inputs.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// WARNING: this file is auto-generated by scripts/inputs.ts (npm run inputs), any manual edit will be overwritten.
|
||||
|
||||
export type Input = 'add' | 'author_name' | 'author_email' | 'branch' | 'cwd' | 'message' | 'remove' | 'signoff' | 'tag'
|
||||
178
src/main.ts
178
src/main.ts
@@ -1,34 +1,128 @@
|
||||
import { info, setFailed, getInput, warning } from '@actions/core'
|
||||
import { execFile } from 'child_process'
|
||||
import path from 'path'
|
||||
import { info, setFailed, getInput as getInputCore, warning, debug, startGroup, endGroup, error } from '@actions/core'
|
||||
import axios from 'axios'
|
||||
import path from 'path'
|
||||
import simpleGit, { Response } from 'simple-git'
|
||||
|
||||
checkInputs().then(() => {
|
||||
const child = execFile(path.join(__dirname, 'entrypoint.sh'), [], { shell: true })
|
||||
child.stdout?.pipe(process.stdout)
|
||||
child.stderr?.pipe(process.stderr)
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
setFailed(err instanceof Error ? err.message : err)
|
||||
import { Input } from './inputs'
|
||||
|
||||
const baseDir = path.join(process.cwd(), getInput('cwd') || '')
|
||||
const git = simpleGit({ baseDir })
|
||||
console.log(`Running in ${baseDir}`);
|
||||
|
||||
(async () => {
|
||||
await checkInputs().catch(setFailed)
|
||||
|
||||
startGroup('Internal logs')
|
||||
info('> Staging files...')
|
||||
|
||||
if (getInput('add')) {
|
||||
info('> Adding files...')
|
||||
await add()
|
||||
} else info('> No files to add.')
|
||||
|
||||
if (getInput('remove')) {
|
||||
info('> Removing files...')
|
||||
await remove()
|
||||
} else info('> No files to remove.')
|
||||
|
||||
info('> Checking for uncommitted changes in the git working tree...')
|
||||
const changedFiles = (await git.diffSummary(['--cached'])).files.length
|
||||
if (changedFiles > 0) {
|
||||
info(`> Found ${changedFiles} changed files.`)
|
||||
|
||||
await git
|
||||
.addConfig('user.email', getInput('author_email'), undefined, log)
|
||||
.addConfig('user.name', getInput('author_name'), undefined, log)
|
||||
debug('> Current git config\n' + JSON.stringify((await git.listConfig()).all, null, 2))
|
||||
|
||||
await git.fetch(['--tags', '--force'], log)
|
||||
|
||||
info('> Switching/creating branch...')
|
||||
await git
|
||||
.checkout(getInput('branch'), undefined, log)
|
||||
.catch(() => git.checkoutLocalBranch(getInput('branch'), log))
|
||||
|
||||
info('> Pulling from remote...')
|
||||
await git
|
||||
.fetch(undefined, log)
|
||||
.pull(undefined, undefined, undefined, log)
|
||||
|
||||
info('> Re-staging files...')
|
||||
if (getInput('add')) await add({ ignoreErrors: true })
|
||||
if (getInput('remove')) await remove({ ignoreErrors: true })
|
||||
|
||||
info('> Creating commit...')
|
||||
await git.commit(getInput('message'), undefined, {
|
||||
'--author': `"${getInput('author_name')} <${getInput('author_email')}>"`,
|
||||
...(getInput('signoff') ? {
|
||||
'--signoff': null
|
||||
} : {})
|
||||
}, log)
|
||||
|
||||
if (getInput('tag')) {
|
||||
info('> Tagging commit...')
|
||||
await git.tag(getInput('tag').split(' '), log)
|
||||
} else info('> No tag info provided.')
|
||||
|
||||
info('> Pushing commit to repo...')
|
||||
await git.push('origin', getInput('branch'), { '--set-upstream': null }, log)
|
||||
|
||||
if (getInput('tag')) {
|
||||
info('> Pushing tags to repo...')
|
||||
await git.pushTags('origin', (e, d?) => log(undefined, e || d)).catch(() => {
|
||||
info('> Tag push failed: deleting remote tag and re-pushing...')
|
||||
return git.push(undefined, undefined, {
|
||||
'--delete': null,
|
||||
'origin': null,
|
||||
[getInput('tag').split(' ').filter(w => !w.startsWith('-'))[0]]: null
|
||||
}, log)
|
||||
.pushTags('origin', log)
|
||||
})
|
||||
} else info('> No tags to push.')
|
||||
|
||||
endGroup()
|
||||
info('> Task completed.')
|
||||
} else {
|
||||
endGroup()
|
||||
info('> Working tree clean. Nothing to commit.')
|
||||
}
|
||||
})().catch(e => {
|
||||
endGroup()
|
||||
setFailed(e)
|
||||
})
|
||||
|
||||
async function checkInputs() {
|
||||
function setInput(input: Input, value: string | undefined) {
|
||||
if (value) return process.env[`INPUT_${input.toUpperCase()}`] = value
|
||||
else return delete process.env[`INPUT_${input.toUpperCase()}`]
|
||||
}
|
||||
function setDefault(input: Input, value: string) {
|
||||
if (!getInput(input)) setInput(input, value)
|
||||
return getInput(input)
|
||||
}
|
||||
|
||||
const eventPath = process.env.GITHUB_EVENT_PATH,
|
||||
event = eventPath && require(eventPath),
|
||||
token = process.env.GITHUB_TOKEN,
|
||||
isPR = process.env.GITHUB_EVENT_NAME?.includes('pull_request'),
|
||||
sha = (event?.pull_request?.head?.sha || process.env.GITHUB_SHA) as string,
|
||||
defaultRef = isPR
|
||||
defaultBranch = isPR
|
||||
? event?.pull_request?.head?.ref as string
|
||||
: process.env.GITHUB_REF?.substring(11)
|
||||
|
||||
// #region GITHUB_TOKEN
|
||||
if (!token) warning('The GITHUB_TOKEN env variable is missing: the action may not work as expected.')
|
||||
// #endregion
|
||||
|
||||
const actualRef = setDefault('ref', defaultRef || '')
|
||||
// #region add, remove
|
||||
if (!getInput('add') && !getInput('remove'))
|
||||
throw new Error('Both \'add\' and \'remove\' are empty, the action has nothing to do.')
|
||||
// #endregion
|
||||
|
||||
// #region author_name, author_email
|
||||
let author = event?.head_commit?.author
|
||||
if (sha && !author) {
|
||||
info('Unable to get commit from workflow event: trying with the GitHub API...')
|
||||
info('> Unable to get commit from workflow event: trying with the GitHub API...')
|
||||
|
||||
// https://docs.github.com/en/rest/reference/repos#get-a-commit--code-samples
|
||||
const url = `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/commits/${sha}`,
|
||||
@@ -36,10 +130,9 @@ async function checkInputs() {
|
||||
Authorization: `Bearer ${token}`
|
||||
} : undefined,
|
||||
commit = (await axios.get(url, { headers }).catch(err => {
|
||||
info('::group::Request error:')
|
||||
info(`Request URL: ${url}`)
|
||||
info(err)
|
||||
info('::endgroup::')
|
||||
startGroup('Request error:')
|
||||
info(`> Request URL: ${url}\b${err}`)
|
||||
endGroup()
|
||||
return undefined
|
||||
}))?.data
|
||||
|
||||
@@ -66,12 +159,51 @@ async function checkInputs() {
|
||||
setDefault('author_email', 'actions@github.com')
|
||||
}
|
||||
|
||||
info(`Using '${getInput('author_name')} <${getInput('author_email')}>' as author.`)
|
||||
if (isPR) info(`Running for a PR, the action will use '${actualRef}' as ref.`)
|
||||
info(`> Using '${getInput('author_name')} <${getInput('author_email')}>' as author.`)
|
||||
// #endregion
|
||||
|
||||
// #region branch
|
||||
const branch = setDefault('branch', defaultBranch || '')
|
||||
if (isPR) info(`> Running for a PR, the action will use '${branch}' as ref.`)
|
||||
// #endregion
|
||||
|
||||
// #region signoff
|
||||
if (getInput('signoff')) try {
|
||||
const parsed = JSON.parse(getInput('signoff'))
|
||||
if (typeof parsed == 'boolean' && !parsed)
|
||||
setInput('signoff', undefined)
|
||||
debug(`Current signoff option: ${getInput('signoff')} (${typeof getInput('signoff')})`)
|
||||
} catch {
|
||||
throw new Error(`"${getInput('signoff')}" is not a valid value for the 'signoff' input: only "true" and "false" are allowed.`)
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
|
||||
function setDefault(input: string, value: string) {
|
||||
const key = 'INPUT_' + input.toUpperCase()
|
||||
if (!process.env[key]) process.env[key] = value
|
||||
return process.env[key] as string
|
||||
function getInput(name: Input) {
|
||||
return getInputCore(name)
|
||||
}
|
||||
|
||||
function log(err: any | Error, data?: any) {
|
||||
if (data) console.log(data)
|
||||
if (err) error(err)
|
||||
}
|
||||
|
||||
function add({ logWarning = true, ignoreErrors = false } = {}): Promise<void | Response<void>> | void {
|
||||
if (getInput('add'))
|
||||
return git.add(getInput('add').split(' '), (e: any, d?: any) => log(ignoreErrors ? null : e, d)).catch((e: Error) => {
|
||||
if (ignoreErrors) return
|
||||
if (e.message.includes('fatal: pathspec') && e.message.includes('did not match any files'))
|
||||
logWarning && warning('Add command did not match any file.')
|
||||
else throw e
|
||||
})
|
||||
}
|
||||
|
||||
function remove({ logWarning = true, ignoreErrors = false } = {}): Promise<void | Response<void>> | void {
|
||||
if (getInput('remove'))
|
||||
return git.rm(getInput('remove').split(' '), (e: any, d?: any) => log(ignoreErrors ? null : e, d)).catch((e: Error) => {
|
||||
if (ignoreErrors) return
|
||||
if (e.message.includes('fatal: pathspec') && e.message.includes('did not match any files'))
|
||||
logWarning && warning('Remove command did not match any file.')
|
||||
else throw e
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user