Skip to content

Commit

Permalink
feat: implement action
Browse files Browse the repository at this point in the history
Signed-off-by: Sam Gammon <[email protected]>
  • Loading branch information
sgammon committed Dec 1, 2023
1 parent c81550c commit 093c198
Show file tree
Hide file tree
Showing 10 changed files with 1,323 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .dev/test-env.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ if (!process.env.RUNNER_TEMP) {
__dirname,
'tmp'
)
process.env.ELIDE_HOME = path.resolve(
process.env.BIN_HOME = path.resolve(
__dirname,
'target'
)
Expand Down
14 changes: 12 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,19 @@ inputs:
description: 'Buildless API key'
required: false

tenant:
description: 'Buildless Tenant'
required: false

project:
description: 'Buildless Project'
required: false

outputs:
time:
description: 'Your output description here'
path:
description: 'Path to Buildless'
agentConfig:
description: 'Agent Configuration'

runs:
using: node20
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@
]
},
"dependencies": {
"@actions/core": "^1.10.1"
"@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"@actions/io": "^1.1.3",
"@actions/tool-cache": "^2.0.1",
"octokit": "^3.1.2"
},
"devDependencies": {
"@types/jest": "^29.5.10",
Expand Down
562 changes: 552 additions & 10 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions src/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as core from '@actions/core'
import * as exec from '@actions/exec'

async function execBuildless(bin: string, args?: string[]): Promise<void> {
core.debug(`Executing: bin=${bin}, args=${args}`)
await exec.exec(`"${bin}"`, args)
}

/**
* Enumerates available commands which can be run with the Buildless CLI tool.
*/
export enum BuildlessCommand {
// Print version and exit.
VERSION = '--version'
}

/**
* Enumerates well-known arguments that can be passed to the Buildless CLI tool.
*/
export enum BuildlessArgument {
DEBUG = '--debug=true',
VERBOSE = '--verbose=true'
}

/**
* Interrogate the specified binary to obtain the version.
*
* @param bin Path to the Buildless CLI tools binary.
* @return Promise which resolves to the obtained version.
*/
export async function obtainVersion(bin: string): Promise<string> {
core.debug(`Obtaining version of Buildless binary at: ${bin}`)
return (await exec.getExecOutput(`"${bin}"`, [BuildlessCommand.VERSION])).stdout
.trim()
.replaceAll('%0A', '')
}
188 changes: 177 additions & 11 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,190 @@
import * as core from '@actions/core'
import { wait } from './wait'
import * as io from '@actions/io'
import { ActionOutputName, BuildlessSetupActionOutputs as Outputs } from './outputs'
import { obtainVersion } from './command'

import buildOptions, {
OptionName,
BuildlessSetupActionOptions as Options,
defaults,
normalizeOs,
normalizeArch
} from './options'

import { downloadRelease } from './releases'

function stringOption(
option: string,
defaultValue?: string
): string | undefined {
const value: string = core.getInput(option)
core.debug(`Property value: ${option}=${value || defaultValue}`)
return value || defaultValue || undefined
}

function getBooleanOption(booleanInputName: string): boolean {
const trueValue = [
'true',
'True',
'TRUE',
'yes',
'Yes',
'YES',
'y',
'Y',
'on',
'On',
'ON'
]
const falseValue = [
'false',
'False',
'FALSE',
'no',
'No',
'NO',
'n',
'N',
'off',
'Off',
'OFF'
]
const stringInput = core.getInput(booleanInputName)
/* istanbul ignore next */
if (trueValue.includes(stringInput)) return true
/* istanbul ignore next */
if (falseValue.includes(stringInput)) return false
return false // default to `false`
}

function booleanOption(option: string, defaultValue: boolean): boolean {
const value: boolean = getBooleanOption(option)
/* istanbul ignore next */
return value !== null && value !== undefined ? value : defaultValue
}

export function notSupported(options: Options): null | Error {
const spec = `${options.os}-${options.arch}`
switch (spec) {
case 'linux-amd64':
return null
case 'darwin-aarch64':
return null
default:
core.error(`Platform is not supported: ${spec}`)
return new Error(`Platform not supported: ${spec}`)
}
}

export async function postInstall(
bin: string,
options: Options
): Promise<void> {
// nothing yet
}

export async function resolveExistingBinary(): Promise<string | null> {
try {
return await io.which('buildless', true)
} catch (err) {
// ignore: no existing copy
return null
}
}

/**
* The main function for the action.
* @returns {Promise<void>} Resolves when the action is complete.
*/
export async function run(): Promise<void> {
export async function run(
options?: Partial<Options>
): Promise<void> {
try {
const ms: string = core.getInput('milliseconds')
// resolve effective plugin options
core.info('Installing Buildless with GitHub Actions')
const effectiveOptions: Options = options
? buildOptions(options)
: buildOptions({
version: stringOption(OptionName.VERSION, 'latest'),
target: stringOption(
OptionName.TARGET,
/* istanbul ignore next */
process.env.BIN_HOME || defaults.target
),
os: normalizeOs(
stringOption(OptionName.OS, process.platform) as string
),
arch: normalizeArch(
stringOption(OptionName.ARCH, process.arch) as string
),
export_path: booleanOption(OptionName.EXPORT_PATH, true),
token: stringOption(OptionName.TOKEN, process.env.GITHUB_TOKEN),
custom_url: stringOption(OptionName.CUSTOM_URL)
})

// make sure the requested version, platform, and os triple is supported
const supportErr = notSupported(effectiveOptions)
if (supportErr) {
core.setFailed(supportErr.message)
return
}

// if the tool is already installed and the user didn't set `force`, we can bail
if (!effectiveOptions.force) {
const existing: string | null = await resolveExistingBinary()
if (existing) {
core.debug(
`Located existing Buildless binary at: '${existing}'. Obtaining version...`
)
await postInstall(existing, effectiveOptions)
const version = await obtainVersion(existing)

/* istanbul ignore next */
if (
version !== effectiveOptions.version ||
effectiveOptions.version === 'latest'
) {
core.warning(
`Existing Buildless installation at version '${version}' was preserved`
)
core.setOutput(ActionOutputName.PATH, existing)
core.setOutput(ActionOutputName.VERSION, version)
return
}
}
}

// download the release tarball (resolving version if needed)
const release = await downloadRelease(effectiveOptions)
core.debug(`Release version: '${release.version.tag_name}'`)

// if instructed, add binary to the path
if (effectiveOptions.export_path) {
core.info(`Adding '${release.path}' to PATH`)
core.addPath(release.path)
}

// begin preparing outputs
const outputs: Outputs = {
path: release.path,
version: effectiveOptions.version
}

// Debug logs are only output if the `ACTIONS_STEP_DEBUG` secret is true
core.debug(`Waiting ${ms} milliseconds ...`)
// verify installed version
await postInstall(release.path, effectiveOptions)
const version = await obtainVersion(release.path)

// Log the current timestamp, wait, then log the new timestamp
core.debug(new Date().toTimeString())
await wait(parseInt(ms, 10))
core.debug(new Date().toTimeString())
/* istanbul ignore next */
if (version !== release.version.tag_name) {
core.warning(
`Buildless version mismatch: expected '${release.version.tag_name}', but got '${version}'`
)
}

// Set outputs for other workflow steps to use
core.setOutput('time', new Date().toTimeString())
// mount outputs
core.setOutput(ActionOutputName.PATH, outputs.path)
core.setOutput(ActionOutputName.VERSION, version)
core.info(`Buildless installed at version ${release.version.tag_name} 🎉`)
} catch (error) {
// Fail the workflow run if an error occurs
if (error instanceof Error) core.setFailed(error.message)
Expand Down
Loading

0 comments on commit 093c198

Please sign in to comment.