Skip to content

Commit 7774ba8

Browse files
authored
Action interface cleanup: deprecate inputs, add cache-hit output, fix extra-args (#110)
## Summary - Add `deprecationMessage` to `extra_args` and `token` inputs so GitHub renders deprecation warnings in the workflow UI - Change `token` default from `${{ github.token }}` to `''` to avoid unnecessary token injection - Add `cache-hit` output reporting whether the restored cache exactly matched the primary key - Fix `extra-args: ''` being impossible to express — the `||` fallback now relies on the YAML default rather than a hardcoded `'--all-files'` - Remove dead `_token` parameter from `resolveVersion` and `token` from the `Inputs` type
1 parent ae5fe13 commit 7774ba8

12 files changed

Lines changed: 304 additions & 51 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@ For a stable reference, pin to a specific release tag such as `v1.2.3`, or pin t
4141
| `prek-version` | Version or semver range to install, for example `0.2.30`, `0.3.x`, `<=1.0.0`, or `latest` | No | `latest` |
4242
| `working-directory` | Directory where `prek run` is executed | No | `.` |
4343
| `show-verbose-logs` | Print the `prek` verbose log after `prek run` completes | No | `true` |
44-
| `token` | Unused; retained for backward compatibility | No | `${{ github.token }}` |
44+
| `token` | Deprecated and unused; retained for backward compatibility | No | `''` |
4545

4646
## Outputs
4747

4848
| Output | Description |
4949
| --- | --- |
5050
| `prek-version` | The resolved `prek` version, normalized to a `v`-prefixed tag |
51+
| `cache-hit` | Whether the restored prek cache exactly matched the computed primary cache key |
5152

5253
## Examples
5354

action.yaml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,37 @@ inputs:
44
extra-args:
55
description: Extra options to pass to `prek run`, appended after `--show-diff-on-failure --color=always`
66
required: false
7-
default: '--all-files'
7+
default: "--all-files"
88
extra_args:
99
description: Options to pass to `prek run` (deprecated, use extra-args)
10+
deprecationMessage: The extra_args input has been renamed to extra-args. Update your workflow.
1011
required: false
1112
install-only:
1213
description: Only install prek, do not run it
1314
required: false
14-
default: 'false'
15+
default: "false"
1516
prek-version:
1617
description: Version or semver range of prek to install (for example `0.2.1`, `0.3.x`, `<=1.0.0`, or `latest`)
1718
required: false
18-
default: 'latest'
19+
default: "latest"
1920
show-verbose-logs:
2021
description: Whether to print the prek verbose log after `prek run`
2122
required: false
22-
default: 'true'
23+
default: "true"
2324
working-directory:
2425
description: The working directory to run prek in
2526
required: false
26-
default: '.'
27+
default: "."
2728
token:
2829
description: Unused; retained for backward compatibility
30+
deprecationMessage: The token input is unused and will be removed in a future major version.
2931
required: false
30-
default: ${{ github.token }}
32+
default: ""
3133
outputs:
3234
prek-version:
3335
description: The version of prek that was installed
36+
cache-hit:
37+
description: Whether the prek environment cache was an exact match
3438
runs:
3539
using: node24
3640
main: dist/index.cjs

dist/index.cjs

Lines changed: 29 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cache.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ import {
1212
PREK_CACHE_KEY_PREFIX,
1313
} from './types'
1414

15+
export type RestorePrekCacheResult = {
16+
matchedKey?: string
17+
primaryKey: string
18+
}
19+
1520
function formatError(error: unknown): string {
1621
return error instanceof Error ? error.message : String(error)
1722
}
1823

19-
export async function restorePrekCache(workingDirectory: string): Promise<void> {
24+
export async function restorePrekCache(workingDirectory: string): Promise<RestorePrekCacheResult> {
2025
core.startGroup('Restore prek cache')
2126

2227
const cacheDir = await getPrekCacheDir()
@@ -25,11 +30,13 @@ export async function restorePrekCache(workingDirectory: string): Promise<void>
2530
core.saveState(CACHE_KEY_STATE, primaryKey)
2631
core.saveState(CACHE_PATHS_STATE, JSON.stringify(paths))
2732

33+
let matchedKey: string | undefined
2834
try {
2935
const restoredKey = await cache.restoreCache(paths, primaryKey)
3036
if (restoredKey) {
3137
core.info(`Restored prek cache with key ${restoredKey}`)
3238
core.saveState(CACHE_MATCHED_KEY_STATE, restoredKey)
39+
matchedKey = restoredKey
3340
} else {
3441
core.info(`No cache found for key ${primaryKey}`)
3542
}
@@ -38,6 +45,8 @@ export async function restorePrekCache(workingDirectory: string): Promise<void>
3845
} finally {
3946
core.endGroup()
4047
}
48+
49+
return { matchedKey, primaryKey }
4150
}
4251

4352
export async function savePrekCache(): Promise<void> {

src/inputs.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import type { Inputs } from './types'
33

44
// Parse and normalize action inputs, including the legacy `extra_args` alias.
55
export function getInputs(): Inputs {
6+
const legacyExtraArgs = core.getInput('extra_args')
7+
const modernExtraArgs = core.getInput('extra-args')
68
const showVerboseLogsInput = core.getInput('show-verbose-logs')
9+
710
return {
8-
extraArgs: core.getInput('extra_args') || core.getInput('extra-args') || '--all-files',
11+
extraArgs: legacyExtraArgs || modernExtraArgs,
912
installOnly: core.getBooleanInput('install-only'),
1013
prekVersion: core.getInput('prek-version') || 'latest',
1114
showVerboseLogs: showVerboseLogsInput === '' ? true : core.getBooleanInput('show-verbose-logs'),
12-
token: core.getInput('token'),
1315
workingDirectory: core.getInput('working-directory') || '.',
1416
}
1517
}

src/main.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ import { installPrek } from './install'
55
import { normalizeVersion, resolveVersion } from './manifest'
66
import { pruneCache, runPrek, showVerboseLogs } from './prek'
77

8-
async function run(): Promise<void> {
8+
export async function run(): Promise<void> {
99
const inputs = getInputs()
1010

1111
core.startGroup('Resolving prek version')
12-
const version = await resolveVersion(inputs.prekVersion, inputs.token)
12+
const version = await resolveVersion(inputs.prekVersion)
1313
core.info(`Using prek ${version}`)
1414
core.endGroup()
1515
core.setOutput('prek-version', normalizeVersion(version))
1616

1717
await installPrek(version)
18-
await restorePrekCache(inputs.workingDirectory)
18+
const { matchedKey, primaryKey } = await restorePrekCache(inputs.workingDirectory)
19+
core.setOutput('cache-hit', String(matchedKey === primaryKey))
1920

2021
if (inputs.installOnly) {
2122
core.info('Skipping prek run because install-only=true')
@@ -39,6 +40,12 @@ async function run(): Promise<void> {
3940
}
4041
}
4142

42-
run().catch(error => {
43-
core.setFailed(error instanceof Error ? error.message : String(error))
44-
})
43+
function isMainModule(): boolean {
44+
return typeof require !== 'undefined' && typeof module !== 'undefined' && require.main === module
45+
}
46+
47+
if (isMainModule()) {
48+
void run().catch(error => {
49+
core.setFailed(error instanceof Error ? error.message : String(error))
50+
})
51+
}

src/manifest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ let versionManifestPromise: Promise<VersionManifest> | undefined
1212

1313
// Resolve user input to a bare version. Exact versions pass through directly; ranges and `latest`
1414
// are resolved from the downloaded version manifest.
15-
export async function resolveVersion(versionInput: string, _token: string): Promise<Version> {
15+
export async function resolveVersion(versionInput: string): Promise<Version> {
1616
const normalizedInput = versionInput.trim() || 'latest'
1717
const exactVersion = semver.valid(toVersion(normalizedInput))
1818
if (exactVersion) {

src/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export type Inputs = {
1111
installOnly: boolean
1212
prekVersion: string
1313
showVerboseLogs: boolean
14-
token: string
1514
workingDirectory: string
1615
}
1716

test/inputs.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest'
2+
3+
const mockContext = vi.hoisted(() => ({
4+
inputs: {} as Record<string, string>,
5+
warnings: [] as string[],
6+
}))
7+
8+
const toolkitMocks = vi.hoisted(() => ({
9+
getBooleanInput: vi.fn((name: string) => (mockContext.inputs[name] ?? '') === 'true'),
10+
getInput: vi.fn((name: string) => mockContext.inputs[name] ?? ''),
11+
warning: vi.fn((message: string) => {
12+
mockContext.warnings.push(message)
13+
}),
14+
}))
15+
16+
vi.mock('@actions/core', () => ({
17+
getBooleanInput: toolkitMocks.getBooleanInput,
18+
getInput: toolkitMocks.getInput,
19+
warning: toolkitMocks.warning,
20+
}))
21+
22+
async function importInputsModule() {
23+
vi.resetModules()
24+
return import('../src/inputs')
25+
}
26+
27+
describe('getInputs', () => {
28+
beforeEach(() => {
29+
vi.resetModules()
30+
vi.clearAllMocks()
31+
mockContext.inputs = {}
32+
mockContext.warnings = []
33+
})
34+
35+
it('uses the configured extra-args default when no explicit value is provided', async () => {
36+
mockContext.inputs['extra-args'] = '--all-files'
37+
38+
const { getInputs } = await importInputsModule()
39+
40+
expect(getInputs().extraArgs).toBe('--all-files')
41+
})
42+
43+
it('does not emit a runtime warning for extra_args (deprecationMessage handles it)', async () => {
44+
mockContext.inputs.extra_args = '--files foo.py'
45+
46+
const { getInputs } = await importInputsModule()
47+
48+
expect(getInputs().extraArgs).toBe('--files foo.py')
49+
expect(mockContext.warnings).toEqual([])
50+
})
51+
52+
it('preserves an explicit empty extra-args value', async () => {
53+
mockContext.inputs['extra-args'] = ''
54+
55+
const { getInputs } = await importInputsModule()
56+
57+
expect(getInputs().extraArgs).toBe('')
58+
})
59+
60+
it('prefers extra_args over extra-args when both are set', async () => {
61+
mockContext.inputs.extra_args = '--legacy'
62+
mockContext.inputs['extra-args'] = '--modern'
63+
64+
const { getInputs } = await importInputsModule()
65+
66+
expect(getInputs().extraArgs).toBe('--legacy')
67+
})
68+
69+
it('does not expose the deprecated token value to runtime code', async () => {
70+
mockContext.inputs.token = 'secret'
71+
72+
const { getInputs } = await importInputsModule()
73+
const inputs = getInputs() as Record<string, unknown>
74+
75+
expect('token' in inputs).toBe(false)
76+
})
77+
78+
it('enables verbose logs by default and allows opting out', async () => {
79+
let { getInputs } = await importInputsModule()
80+
expect(getInputs().showVerboseLogs).toBe(true)
81+
82+
mockContext.inputs['show-verbose-logs'] = 'false'
83+
;({ getInputs } = await importInputsModule())
84+
expect(getInputs().showVerboseLogs).toBe(false)
85+
})
86+
})

0 commit comments

Comments
 (0)