@ronaldphilipsen/local-action 0.0.0-stage → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/.checkov.yml +3 -0
  2. package/.env.example +379 -0
  3. package/.github/codeql/codeql-config.yml +7 -0
  4. package/.github/copilot-instructions.md +79 -0
  5. package/.github/dependabot.yml +32 -0
  6. package/.github/workflows/codeql-analysis.yml +53 -0
  7. package/.github/workflows/continuous-delivery.yml +93 -0
  8. package/.github/workflows/continuous-integration.yml +382 -0
  9. package/.github/workflows/linter.yml +47 -0
  10. package/.github/workflows/release.yaml +99 -0
  11. package/.github/workflows/version-check.yml +44 -0
  12. package/.grype.yml +151 -0
  13. package/.markdown-lint.yml +7 -0
  14. package/.mega-linter.yml +188 -0
  15. package/.node-version +1 -0
  16. package/.prettierignore +4 -0
  17. package/.prettierrc.yml +14 -0
  18. package/.vscode/launch.json +15 -0
  19. package/.vscode/mcp.json +9 -0
  20. package/.vscode/settings.json +15 -0
  21. package/.vscode/tasks.json +51 -0
  22. package/.yaml-lint.yml +10 -0
  23. package/CHANGELOG.md +75 -0
  24. package/CODEOWNERS +3 -0
  25. package/CODE_OF_CONDUCT.md +74 -0
  26. package/CONTRIBUTING.md +140 -0
  27. package/LICENSE +21 -0
  28. package/README.md +285 -2
  29. package/SECURITY.md +42 -0
  30. package/SUPPORT.md +19 -0
  31. package/__fixtures__/@actions/core.ts +14 -0
  32. package/__fixtures__/@actions/exec.ts +9 -0
  33. package/__fixtures__/@actions/github.ts +3 -0
  34. package/__fixtures__/@actions/http-client.ts +9 -0
  35. package/__fixtures__/@actions/io.ts +9 -0
  36. package/__fixtures__/@octokit/rest.ts +18 -0
  37. package/__fixtures__/crypto.ts +7 -0
  38. package/__fixtures__/fs.ts +27 -0
  39. package/__fixtures__/javascript/no-export/.env.fixture +2 -0
  40. package/__fixtures__/javascript/no-export/action.yml +16 -0
  41. package/__fixtures__/javascript/no-export/node_modules/.gitkeep +0 -0
  42. package/__fixtures__/javascript/no-export/package.json +12 -0
  43. package/__fixtures__/javascript/no-export/src/index.js +0 -0
  44. package/__fixtures__/javascript/no-export/src/main.js +0 -0
  45. package/__fixtures__/javascript/no-import/.env.fixture +2 -0
  46. package/__fixtures__/javascript/no-import/action.yml +16 -0
  47. package/__fixtures__/javascript/no-import/node_modules/.gitkeep +0 -0
  48. package/__fixtures__/javascript/no-import/package.json +12 -0
  49. package/__fixtures__/javascript/no-import/src/index.js +3 -0
  50. package/__fixtures__/javascript/no-import/src/main.js +7 -0
  51. package/__fixtures__/javascript/success/.env.fixture +2 -0
  52. package/__fixtures__/javascript/success/action.yml +16 -0
  53. package/__fixtures__/javascript/success/node_modules/.gitkeep +0 -0
  54. package/__fixtures__/javascript/success/package.json +12 -0
  55. package/__fixtures__/javascript/success/post/index.js +3 -0
  56. package/__fixtures__/javascript/success/post/main.js +13 -0
  57. package/__fixtures__/javascript/success/pre/index.js +3 -0
  58. package/__fixtures__/javascript/success/pre/main.js +13 -0
  59. package/__fixtures__/javascript/success/src/index.js +3 -0
  60. package/__fixtures__/javascript/success/src/main.js +13 -0
  61. package/__fixtures__/javascript-esm/no-import/.env.fixture +2 -0
  62. package/__fixtures__/javascript-esm/no-import/action.yml +16 -0
  63. package/__fixtures__/javascript-esm/no-import/node_modules/.gitkeep +0 -0
  64. package/__fixtures__/javascript-esm/no-import/package.json +13 -0
  65. package/__fixtures__/javascript-esm/no-import/src/index.js +3 -0
  66. package/__fixtures__/javascript-esm/no-import/src/main.js +7 -0
  67. package/__fixtures__/javascript-esm/success/.env.fixture +2 -0
  68. package/__fixtures__/javascript-esm/success/action.yml +16 -0
  69. package/__fixtures__/javascript-esm/success/node_modules/.gitkeep +0 -0
  70. package/__fixtures__/javascript-esm/success/package.json +13 -0
  71. package/__fixtures__/javascript-esm/success/post/index.js +3 -0
  72. package/__fixtures__/javascript-esm/success/post/main.js +13 -0
  73. package/__fixtures__/javascript-esm/success/pre/index.js +3 -0
  74. package/__fixtures__/javascript-esm/success/pre/main.js +13 -0
  75. package/__fixtures__/javascript-esm/success/src/index.js +3 -0
  76. package/__fixtures__/javascript-esm/success/src/main.js +13 -0
  77. package/__fixtures__/path.ts +7 -0
  78. package/__fixtures__/payloads/issue_opened.json +33 -0
  79. package/__fixtures__/stream/promises.ts +3 -0
  80. package/__fixtures__/tsconfig-paths.ts +4 -0
  81. package/__fixtures__/typescript/no-import/.env.fixture +2 -0
  82. package/__fixtures__/typescript/no-import/action.yml +16 -0
  83. package/__fixtures__/typescript/no-import/node_modules/.gitkeep +0 -0
  84. package/__fixtures__/typescript/no-import/package.json +15 -0
  85. package/__fixtures__/typescript/no-import/src/index.ts +3 -0
  86. package/__fixtures__/typescript/no-import/src/main.ts +3 -0
  87. package/__fixtures__/typescript/no-import/tsconfig.json +20 -0
  88. package/__fixtures__/typescript/success/.env.fixture +2 -0
  89. package/__fixtures__/typescript/success/action.yml +16 -0
  90. package/__fixtures__/typescript/success/node_modules/.gitkeep +0 -0
  91. package/__fixtures__/typescript/success/package.json +15 -0
  92. package/__fixtures__/typescript/success/post/index.ts +3 -0
  93. package/__fixtures__/typescript/success/post/main.ts +9 -0
  94. package/__fixtures__/typescript/success/pre/index.ts +3 -0
  95. package/__fixtures__/typescript/success/pre/main.ts +9 -0
  96. package/__fixtures__/typescript/success/src/index.ts +3 -0
  97. package/__fixtures__/typescript/success/src/main.ts +9 -0
  98. package/__fixtures__/typescript/success/tsconfig.json +20 -0
  99. package/__fixtures__/typescript/success-yaml/.env.fixture +2 -0
  100. package/__fixtures__/typescript/success-yaml/action.yaml +16 -0
  101. package/__fixtures__/typescript/success-yaml/node_modules/.gitkeep +0 -0
  102. package/__fixtures__/typescript/success-yaml/package.json +15 -0
  103. package/__fixtures__/typescript/success-yaml/post/index.ts +3 -0
  104. package/__fixtures__/typescript/success-yaml/post/main.ts +9 -0
  105. package/__fixtures__/typescript/success-yaml/pre/index.ts +3 -0
  106. package/__fixtures__/typescript/success-yaml/pre/main.ts +9 -0
  107. package/__fixtures__/typescript/success-yaml/src/index.ts +3 -0
  108. package/__fixtures__/typescript/success-yaml/src/main.ts +9 -0
  109. package/__fixtures__/typescript/success-yaml/tsconfig.json +20 -0
  110. package/__fixtures__/typescript-esm/no-export/.env.fixture +2 -0
  111. package/__fixtures__/typescript-esm/no-export/action.yml +17 -0
  112. package/__fixtures__/typescript-esm/no-export/node_modules/.gitkeep +0 -0
  113. package/__fixtures__/typescript-esm/no-export/package.json +17 -0
  114. package/__fixtures__/typescript-esm/no-export/src/index.ts +0 -0
  115. package/__fixtures__/typescript-esm/no-export/src/main.ts +0 -0
  116. package/__fixtures__/typescript-esm/no-export/tsconfig.json +25 -0
  117. package/__fixtures__/typescript-esm/no-import/.env.fixture +2 -0
  118. package/__fixtures__/typescript-esm/no-import/action.yml +17 -0
  119. package/__fixtures__/typescript-esm/no-import/node_modules/.gitkeep +0 -0
  120. package/__fixtures__/typescript-esm/no-import/package.json +17 -0
  121. package/__fixtures__/typescript-esm/no-import/src/index.ts +3 -0
  122. package/__fixtures__/typescript-esm/no-import/src/main.ts +3 -0
  123. package/__fixtures__/typescript-esm/no-import/tsconfig.json +25 -0
  124. package/__fixtures__/typescript-esm/success/.env.fixture +2 -0
  125. package/__fixtures__/typescript-esm/success/action.yml +17 -0
  126. package/__fixtures__/typescript-esm/success/node_modules/.gitkeep +0 -0
  127. package/__fixtures__/typescript-esm/success/package.json +17 -0
  128. package/__fixtures__/typescript-esm/success/post/index.ts +3 -0
  129. package/__fixtures__/typescript-esm/success/post/main.ts +9 -0
  130. package/__fixtures__/typescript-esm/success/pre/index.ts +3 -0
  131. package/__fixtures__/typescript-esm/success/pre/main.ts +9 -0
  132. package/__fixtures__/typescript-esm/success/src/index.ts +3 -0
  133. package/__fixtures__/typescript-esm/success/src/main.ts +9 -0
  134. package/__fixtures__/typescript-esm/success/tsconfig.json +25 -0
  135. package/__fixtures__/typescript-esm/tsconfig-comments/.env.fixture +2 -0
  136. package/__fixtures__/typescript-esm/tsconfig-comments/action.yml +17 -0
  137. package/__fixtures__/typescript-esm/tsconfig-comments/node_modules/.gitkeep +0 -0
  138. package/__fixtures__/typescript-esm/tsconfig-comments/package.json +17 -0
  139. package/__fixtures__/typescript-esm/tsconfig-comments/src/index.ts +3 -0
  140. package/__fixtures__/typescript-esm/tsconfig-comments/src/main.ts +9 -0
  141. package/__fixtures__/typescript-esm/tsconfig-comments/tsconfig.json +21 -0
  142. package/__tests__/command.test.ts +224 -0
  143. package/__tests__/stubs/artifact/internal/client.test.ts +340 -0
  144. package/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts +152 -0
  145. package/__tests__/stubs/artifact/internal/download/download-artifact.test.ts +119 -0
  146. package/__tests__/stubs/artifact/internal/find/get-artifact.test.ts +237 -0
  147. package/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts +218 -0
  148. package/__tests__/stubs/artifact/internal/find/retry-options.test.ts +54 -0
  149. package/__tests__/stubs/artifact/internal/shared/config.test.ts +91 -0
  150. package/__tests__/stubs/artifact/internal/shared/errors.test.ts +38 -0
  151. package/__tests__/stubs/artifact/internal/shared/user-agent.test.ts +22 -0
  152. package/__tests__/stubs/artifact/internal/upload/path-and-artifact-name-validation.test.ts +53 -0
  153. package/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts +126 -0
  154. package/__tests__/stubs/artifact/internal/upload/upload-zip-specification.test.ts +49 -0
  155. package/__tests__/stubs/cache/cache.test.ts +357 -0
  156. package/__tests__/stubs/cache/errors.test.ts +55 -0
  157. package/__tests__/stubs/cache/internal/cacheUtils.test.ts +109 -0
  158. package/__tests__/stubs/cache/internal/constants.test.ts +81 -0
  159. package/__tests__/stubs/cache/internal/tar.test.ts +129 -0
  160. package/__tests__/stubs/core/core.test.ts +760 -0
  161. package/__tests__/stubs/core/path-utils.test.ts +45 -0
  162. package/__tests__/stubs/core/platform.test.ts +89 -0
  163. package/__tests__/stubs/core/summary.test.ts +455 -0
  164. package/__tests__/stubs/env.test.ts +72 -0
  165. package/__tests__/stubs/github/context.test.ts +73 -0
  166. package/__tests__/stubs/github/github.test.ts +15 -0
  167. package/__tests__/stubs/github/internal/utils.test.ts +98 -0
  168. package/__tests__/stubs/github/utils.test.ts +16 -0
  169. package/__tests__/utils/config.test.ts +78 -0
  170. package/__tests__/utils/output.test.ts +32 -0
  171. package/__tests__/utils/package.test.ts +33 -0
  172. package/badges/coverage.svg +1 -0
  173. package/bin/local-action.js +121 -0
  174. package/docs/create-a-job-summary.md +32 -0
  175. package/docs/debugging-in-vscode.md +27 -0
  176. package/docs/img/job-summary.png +0 -0
  177. package/docs/supported-functionality.md +126 -0
  178. package/eslint.config.mjs +74 -0
  179. package/jest.config.ts +41 -0
  180. package/package.json +93 -4
  181. package/src/command.ts +171 -0
  182. package/src/commands/run.ts +616 -0
  183. package/src/index.ts +21 -0
  184. package/src/stubs/artifact/artifact.ts +17 -0
  185. package/src/stubs/artifact/internal/client.ts +400 -0
  186. package/src/stubs/artifact/internal/delete/delete-artifact.ts +126 -0
  187. package/src/stubs/artifact/internal/download/download-artifact.ts +309 -0
  188. package/src/stubs/artifact/internal/find/get-artifact.ts +135 -0
  189. package/src/stubs/artifact/internal/find/list-artifacts.ts +188 -0
  190. package/src/stubs/artifact/internal/find/retry-options.ts +57 -0
  191. package/src/stubs/artifact/internal/shared/config.ts +64 -0
  192. package/src/stubs/artifact/internal/shared/errors.ts +53 -0
  193. package/src/stubs/artifact/internal/shared/interfaces.ts +195 -0
  194. package/src/stubs/artifact/internal/shared/user-agent.ts +13 -0
  195. package/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts +92 -0
  196. package/src/stubs/artifact/internal/upload/upload-artifact.ts +127 -0
  197. package/src/stubs/artifact/internal/upload/upload-zip-specification.ts +108 -0
  198. package/src/stubs/artifact/internal/upload/zip.ts +122 -0
  199. package/src/stubs/cache/cache.ts +334 -0
  200. package/src/stubs/cache/errors.ts +31 -0
  201. package/src/stubs/cache/interfaces.ts +4 -0
  202. package/src/stubs/cache/internal/cacheUtils.ts +207 -0
  203. package/src/stubs/cache/internal/constants.ts +45 -0
  204. package/src/stubs/cache/internal/tar.ts +374 -0
  205. package/src/stubs/cache/options.ts +91 -0
  206. package/src/stubs/core/core.ts +743 -0
  207. package/src/stubs/core/path-utils.ts +39 -0
  208. package/src/stubs/core/platform.ts +132 -0
  209. package/src/stubs/core/summary.ts +399 -0
  210. package/src/stubs/env.ts +40 -0
  211. package/src/stubs/github/context.ts +94 -0
  212. package/src/stubs/github/github.ts +29 -0
  213. package/src/stubs/github/interfaces.ts +52 -0
  214. package/src/stubs/github/internal/utils.ts +83 -0
  215. package/src/stubs/github/utils.ts +52 -0
  216. package/src/types/quibble.d.ts +5 -0
  217. package/src/types.ts +116 -0
  218. package/src/utils/config.ts +47 -0
  219. package/src/utils/output.ts +20 -0
  220. package/src/utils/package.ts +38 -0
  221. package/tsconfig.base.json +29 -0
  222. package/tsconfig.eslint.json +22 -0
  223. package/tsconfig.json +18 -0
@@ -0,0 +1,616 @@
1
+ import { parse } from 'comment-json'
2
+ import { config } from 'dotenv'
3
+ import { createRequire } from 'module'
4
+ import { execSync } from 'node:child_process'
5
+ import path from 'path'
6
+ import * as quibble from 'quibble'
7
+ import { loadConfig, register } from 'tsconfig-paths'
8
+ import { fileURLToPath } from 'url'
9
+ import { ARTIFACT_STUBS } from '../stubs/artifact/artifact.js'
10
+ import { CACHE_STUBS } from '../stubs/cache/cache.js'
11
+ import { CORE_STUBS, CoreMeta } from '../stubs/core/core.js'
12
+ import { EnvMeta } from '../stubs/env.js'
13
+ import { Context } from '../stubs/github/context.js'
14
+ import { getOctokit } from '../stubs/github/github.js'
15
+ import type { Action } from '../types.js'
16
+ import { getOSEntrypoints } from '../utils/config.ts'
17
+ import { printTitle } from '../utils/output.js'
18
+ import { isESM } from '../utils/package.js'
19
+
20
+ const require = createRequire(import.meta.url)
21
+ let needsReplug: boolean = false
22
+
23
+ export async function action(
24
+ actionPath: string,
25
+ entrypoint: string,
26
+ dotenvFile: string,
27
+ options: {
28
+ pre: string | undefined
29
+ post: string | undefined
30
+ }
31
+ ): Promise<void> {
32
+ const { Chalk } = await import('chalk')
33
+ const chalk = new Chalk()
34
+ const fs = await import('fs')
35
+ const YAML = await import('yaml')
36
+
37
+ if (process.env.TARGET_ACTION_PATH && process.env.TARGET_ACTION_PATH !== '') {
38
+ // Check if the action has a `tsconfig.json` file.
39
+ if (fs.existsSync(`${process.env.TARGET_ACTION_PATH}/tsconfig.json`)) {
40
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
41
+
42
+ // Load the `tsconfig.json` from the action directory.
43
+ const actionTsConfig = parse(
44
+ fs.readFileSync(
45
+ `${process.env.TARGET_ACTION_PATH}/tsconfig.json`,
46
+ 'utf-8'
47
+ )
48
+ )
49
+
50
+ // Load the current `tsconfig.json` from the root of this directory.
51
+ loadConfig(__dirname)
52
+
53
+ // Get the paths from the action's `tsconfig.json`, if any.
54
+ // @ts-expect-error comment-json type mismatch
55
+ const paths = actionTsConfig?.compilerOptions?.paths ?? {}
56
+
57
+ // Add any path mappings from the imported action. Replace the base URL with
58
+ // the target action path.
59
+ // @todo Should this take into account the previous `baseUrl` value?
60
+ register({
61
+ baseUrl: process.env.TARGET_ACTION_PATH,
62
+ paths,
63
+ addMatchAll: true
64
+ })
65
+ }
66
+ }
67
+
68
+ CoreMeta.colors = {
69
+ cyan: (msg: string) => console.log(chalk.cyan(msg)),
70
+ blue: (msg: string) => console.log(chalk.blue(msg)),
71
+ gray: (msg: string) => console.log(chalk.gray(msg)),
72
+ green: (msg: string) => console.log(chalk.green(msg)),
73
+ magenta: (msg: string) => console.log(chalk.magenta(msg)),
74
+ red: (msg: string) => console.log(chalk.red(msg)),
75
+ white: (msg: string) => console.log(chalk.white(msg)),
76
+ yellow: (msg: string) => console.log(chalk.yellow(msg))
77
+ }
78
+
79
+ const actionType = isESM() ? 'esm' : 'cjs'
80
+
81
+ // Output the configuration
82
+ const startConfig = [
83
+ {
84
+ Field: 'Action Path',
85
+ Value: actionPath
86
+ },
87
+ {
88
+ Field: 'Entrypoint',
89
+ Value: entrypoint
90
+ }
91
+ ]
92
+
93
+ if (options.pre)
94
+ startConfig.push({
95
+ Field: 'Pre Entrypoint',
96
+ Value: options.pre
97
+ })
98
+
99
+ if (options.post)
100
+ startConfig.push({
101
+ Field: 'Post Entrypoint',
102
+ Value: options.post
103
+ })
104
+
105
+ startConfig.push({
106
+ Field: 'Environment File',
107
+ Value: dotenvFile
108
+ })
109
+
110
+ printTitle(CoreMeta.colors.cyan, 'Configuration')
111
+ console.log()
112
+ console.table(startConfig)
113
+ console.log()
114
+
115
+ // Load the environment file
116
+ // @todo Load this into EnvMeta directly? What about secrets...
117
+ config({
118
+ path: path.resolve(process.cwd(), dotenvFile),
119
+ override: true,
120
+ quiet: true
121
+ })
122
+
123
+ // Load action settings
124
+ CoreMeta.stepDebug = process.env.ACTIONS_STEP_DEBUG === 'true'
125
+ CoreMeta.stepSummaryPath = process.env.GITHUB_STEP_SUMMARY ?? ''
126
+
127
+ // Read the action.yml file and parse the expected inputs/outputs
128
+ const actionYaml: Action = YAML.parse(
129
+ fs.readFileSync(EnvMeta.actionFile, { encoding: 'utf8', flag: 'r' })
130
+ ) as Action
131
+
132
+ EnvMeta.inputs = actionYaml.inputs || {}
133
+ EnvMeta.outputs = actionYaml.outputs || {}
134
+
135
+ // If the LOCAL_ACTION_CACHE_PATH env var is set, read it to populate the
136
+ // existing caches.
137
+ if (
138
+ process.env.LOCAL_ACTION_CACHE_PATH &&
139
+ process.env.LOCAL_ACTION_CACHE_PATH !== ''
140
+ ) {
141
+ const cacheFiles = fs
142
+ .readdirSync(process.env.LOCAL_ACTION_CACHE_PATH)
143
+ .filter(file => file.endsWith('.cache'))
144
+
145
+ // The cache ID will be the next available ID based on existing caches.
146
+ let nextCacheId = 1
147
+
148
+ for (const file of cacheFiles) {
149
+ EnvMeta.caches[nextCacheId] = file
150
+ nextCacheId++
151
+ }
152
+ }
153
+
154
+ // Output the action metadata
155
+ printTitle(CoreMeta.colors.blue, 'Action Metadata')
156
+ console.log()
157
+ console.table(
158
+ Object.keys(EnvMeta.inputs).map(i => ({
159
+ Input: i,
160
+ Description: EnvMeta.inputs[i].description
161
+ }))
162
+ )
163
+ console.log()
164
+ console.table(
165
+ Object.keys(EnvMeta.outputs).map(i => ({
166
+ Output: i,
167
+ Description: EnvMeta.outputs[i].description
168
+ }))
169
+ )
170
+ console.log()
171
+
172
+ if (Object.keys(EnvMeta.caches).length > 0) {
173
+ console.table(
174
+ Object.keys(EnvMeta.caches).map(i => ({
175
+ 'Cache ID': i,
176
+ Filename: EnvMeta.caches[parseInt(i)]
177
+ }))
178
+ )
179
+ console.log()
180
+ }
181
+
182
+ // Defining the stubs. Next, we will load their paths based on the package
183
+ // manager in use.
184
+ const stubs = {
185
+ '@actions/artifact': {
186
+ base: undefined as string | undefined,
187
+ lib: ['lib', 'artifact.js'],
188
+ stubs: ARTIFACT_STUBS
189
+ },
190
+ '@actions/cache': {
191
+ base: undefined as string | undefined,
192
+ lib: ['lib', 'cache.js'],
193
+ stubs: CACHE_STUBS
194
+ },
195
+ '@actions/core': {
196
+ base: undefined as string | undefined,
197
+ lib: ['lib', 'core.js'],
198
+ stubs: CORE_STUBS
199
+ },
200
+ '@actions/github': {
201
+ base: undefined as string | undefined,
202
+ lib: ['lib', 'github.js'],
203
+ stubs: {
204
+ getOctokit,
205
+ // The context object needs to be created **after** the dotenv file is
206
+ // loaded. Otherwise, the GITHUB_* environment variables will not be
207
+ // available to the action.
208
+ context: new Context()
209
+ }
210
+ }
211
+ }
212
+
213
+ // Starting at the target action's entrypoint, find the package.json file.
214
+ const dirs = path.dirname(entrypoint).split(path.sep)
215
+ let packageJsonPath
216
+ let found = false
217
+
218
+ // Move up the directory tree until we find a package.json directory.
219
+ while (dirs.length > 0) {
220
+ packageJsonPath = path.resolve(dirs.join(path.sep), 'package.json')
221
+
222
+ // Check if the current directory has a package.json file.
223
+ if (fs.existsSync(packageJsonPath)) {
224
+ found = true
225
+ break
226
+ }
227
+
228
+ // Move up the directory tree.
229
+ dirs.pop()
230
+ }
231
+
232
+ if (!found || !packageJsonPath)
233
+ throw new Error(
234
+ 'No package.json file found in the action directory or any parent directories.'
235
+ )
236
+
237
+ // If the package manager is `npm`, we can assume there is a `node_modules`
238
+ // directory somewhere in the project.
239
+ //
240
+ // Actions that use `pnpm` will also have a `node_modules` directory, however
241
+ // in testing it seems that the `node_modules/@actions/<pkg>` dependency can
242
+ // only be properly stubbed for CJS actions. For ESM actions, this should
243
+ // instead point to the pnpm cache.
244
+ //
245
+ // If the package manager is `yarn`, then we need to first check for
246
+ // `node_modules` (for non-PnP versions), or we can try unplugging the
247
+ // dependencies from the yarn cache.
248
+ if (
249
+ process.env.NODE_PACKAGE_MANAGER === 'npm' ||
250
+ (process.env.NODE_PACKAGE_MANAGER === 'pnpm' && actionType === 'cjs') ||
251
+ (process.env.NODE_PACKAGE_MANAGER === 'yarn' &&
252
+ fs.existsSync(path.join(actionPath, 'node_modules')))
253
+ ) {
254
+ /**
255
+ * Get the path in the npm cache for each package.
256
+ * `npm ls <package> --json --long`
257
+ *
258
+ * Example Output
259
+ *
260
+ * {
261
+ * "path": "<workspace>/typescript-action",
262
+ * "_dependencies": {
263
+ * "@actions/core": "^1.11.1",
264
+ * "@actions/github": "^6.0.0"
265
+ * },
266
+ * "dependencies": {
267
+ * "@actions/core": {
268
+ * "path": "<workspace>/typescript-action/node_modules/@actions/core",
269
+ * },
270
+ * "@actions/github": {
271
+ * "path": "<workspace>/typescript-action/node_modules/@actions/github",
272
+ * }
273
+ * }
274
+ * }
275
+ */
276
+
277
+ const npmList = JSON.parse(
278
+ execSync(
279
+ `npm ls ${Object.keys(stubs).join(' ')} --json --long`
280
+ ).toString()
281
+ ) as {
282
+ path: string
283
+ dependencies?: {
284
+ [key: string]: { path: string }
285
+ }
286
+ }
287
+
288
+ if (Object.keys(npmList.dependencies ?? {}).length === 0)
289
+ throw new Error('Something went wrong with npm list')
290
+
291
+ Object.keys(stubs).forEach(key => {
292
+ stubs[key as keyof typeof stubs].base = npmList.dependencies?.[key]?.path
293
+ })
294
+ } else if (
295
+ process.env.NODE_PACKAGE_MANAGER === 'pnpm' &&
296
+ actionType === 'esm'
297
+ ) {
298
+ /**
299
+ * Get the path in the pnpm cache for each package.
300
+ * `pnpm list <package> --json`
301
+ *
302
+ * Example Output
303
+ *
304
+ * [
305
+ * {
306
+ * "path": "<workspace>/typescript-action",
307
+ * "dependencies": {
308
+ * "@actions/core": {
309
+ * "path": "<workspace>/typescript-action/node_modules/.pnpm/@actions+core@1.11.1/node_modules/@actions/core"
310
+ * },
311
+ * "@actions/github": {
312
+ * "path": "<workspace>/typescript-action/node_modules/.pnpm/@actions+github@6.0.0/node_modules/@actions/github"
313
+ * }
314
+ * }
315
+ * }
316
+ * ]
317
+ */
318
+ const pnpmList = JSON.parse(
319
+ execSync(`pnpm list ${Object.keys(stubs).join(' ')} --json`).toString()
320
+ ) as {
321
+ path: string
322
+ dependencies?: {
323
+ [key: string]: { path: string }
324
+ }
325
+ }[]
326
+
327
+ if (pnpmList.length === 0)
328
+ throw new Error('Something went wrong with pnpm list')
329
+
330
+ Object.keys(stubs).forEach(key => {
331
+ stubs[key as keyof typeof stubs].base =
332
+ pnpmList[0].dependencies?.[key]?.path
333
+ })
334
+ } else if (process.env.NODE_PACKAGE_MANAGER === 'yarn') {
335
+ // Note: The CLI commands are different across yarn versions for getting the
336
+ // path to a dependency.
337
+
338
+ // At this point, it's likely yarn is running in PnP mode.
339
+ printTitle(CoreMeta.colors.magenta, 'Yarn: Unplugging Dependencies')
340
+ console.log()
341
+
342
+ // For now, we need to `unplug` each dependency to get the path to the
343
+ // package.
344
+ needsReplug = true
345
+
346
+ for (const key of Object.keys(stubs)) {
347
+ // This may fail if the package is not a dependency for this action.
348
+ try {
349
+ const output = execSync(`yarn unplug ${key}`).toString()
350
+ console.log(`Unplugged: ${key}`)
351
+
352
+ // Next, get the path to the package. Unfortunately using the `--json`
353
+ // flag with `yarn unplug` does not output the target path, so we need
354
+ // to parse it from the plaintext output.
355
+ const packagePath = output.match(/Will unpack .* to (?<packagePath>.*)/)
356
+ ?.groups?.packagePath
357
+
358
+ if (!packagePath) throw new Error(`Could not unplug ${key}`)
359
+
360
+ stubs[key as keyof typeof stubs].base = path.join(
361
+ packagePath,
362
+ 'node_modules',
363
+ key
364
+ )
365
+ } catch {
366
+ // This is fine...
367
+ }
368
+ }
369
+ }
370
+
371
+ const osEntrypoints = getOSEntrypoints(actionType)
372
+
373
+ // Stub the `@actions/toolkit` libraries and run the action. Quibble and
374
+ // local-action require a different approach depending on if the called action
375
+ // is written in ESM. The stubs should only be loaded if the corresponding
376
+ // package is installed.
377
+ if (actionType === 'esm') {
378
+ if (stubs['@actions/artifact'].base)
379
+ await quibble.default.esm(
380
+ path.resolve(
381
+ stubs['@actions/artifact'].base,
382
+ ...stubs['@actions/artifact'].lib
383
+ ),
384
+ stubs['@actions/artifact'].stubs
385
+ )
386
+
387
+ if (stubs['@actions/cache'].base)
388
+ await quibble.default.esm(
389
+ path.resolve(
390
+ stubs['@actions/cache'].base,
391
+ ...stubs['@actions/cache'].lib
392
+ ),
393
+ stubs['@actions/cache'].stubs
394
+ )
395
+
396
+ if (stubs['@actions/core'].base)
397
+ await quibble.default.esm(
398
+ path.resolve(
399
+ stubs['@actions/core'].base,
400
+ ...stubs['@actions/core'].lib
401
+ ),
402
+ stubs['@actions/core'].stubs
403
+ )
404
+
405
+ if (stubs['@actions/github'].base)
406
+ await quibble.default.esm(
407
+ path.resolve(
408
+ stubs['@actions/github'].base,
409
+ ...stubs['@actions/github'].lib
410
+ ),
411
+ stubs['@actions/github'].stubs
412
+ )
413
+
414
+ try {
415
+ if (osEntrypoints.pre) {
416
+ console.log('')
417
+ printTitle(CoreMeta.colors.red, 'Running Pre Step')
418
+ console.log('')
419
+
420
+ const { run: pre } = await import(osEntrypoints.pre)
421
+
422
+ // Check if the required path is a function.
423
+ if (typeof pre !== 'function')
424
+ throw new Error(
425
+ `Entrypoint ${options.pre} does not export a run() function`
426
+ )
427
+
428
+ await pre()
429
+ }
430
+
431
+ console.log('')
432
+ printTitle(CoreMeta.colors.green, 'Running Action')
433
+ console.log('')
434
+
435
+ // ESM actions need to be imported, not required.
436
+ const { run } = await import(osEntrypoints.main)
437
+
438
+ // Check if the required path is a function.
439
+ if (typeof run !== 'function')
440
+ throw new Error(
441
+ `Entrypoint ${entrypoint} does not export a run() function`
442
+ )
443
+
444
+ await run()
445
+
446
+ if (osEntrypoints.post) {
447
+ console.log('')
448
+ printTitle(CoreMeta.colors.red, 'Running Post Step')
449
+ console.log('')
450
+
451
+ const { run: post } = await import(osEntrypoints.post)
452
+
453
+ // Check if the required path is a function.
454
+ if (typeof post !== 'function')
455
+ throw new Error(
456
+ `Entrypoint ${options.post} does not export a run() function`
457
+ )
458
+
459
+ await post()
460
+ }
461
+ } finally {
462
+ if (process.env.NODE_PACKAGE_MANAGER === 'yarn' && needsReplug)
463
+ replug(fs, packageJsonPath, Object.keys(stubs))
464
+ }
465
+ } else {
466
+ if (stubs['@actions/artifact'].base)
467
+ quibble.default(
468
+ path.resolve(
469
+ stubs['@actions/artifact'].base,
470
+ ...stubs['@actions/artifact'].lib
471
+ ),
472
+ stubs['@actions/artifact'].stubs
473
+ )
474
+
475
+ if (stubs['@actions/cache'].base)
476
+ quibble.default(
477
+ path.resolve(
478
+ stubs['@actions/cache'].base,
479
+ ...stubs['@actions/cache'].lib
480
+ ),
481
+ stubs['@actions/cache'].stubs
482
+ )
483
+
484
+ if (stubs['@actions/core'].base)
485
+ quibble.default(
486
+ path.resolve(
487
+ stubs['@actions/core'].base,
488
+ ...stubs['@actions/core'].lib
489
+ ),
490
+ stubs['@actions/core'].stubs
491
+ )
492
+
493
+ if (stubs['@actions/github'].base)
494
+ quibble.default(
495
+ path.resolve(
496
+ stubs['@actions/github'].base,
497
+ ...stubs['@actions/github'].lib
498
+ ),
499
+ stubs['@actions/github'].stubs
500
+ )
501
+
502
+ try {
503
+ if (osEntrypoints.pre) {
504
+ console.log('')
505
+ printTitle(CoreMeta.colors.red, 'Running Pre Step')
506
+ console.log('')
507
+
508
+ // CJS actions need to be required, not imported.
509
+ const { run: pre } = require(osEntrypoints.pre)
510
+
511
+ // Check if the required path is a function.
512
+ if (typeof pre !== 'function')
513
+ throw new Error(
514
+ `Entrypoint ${options.pre} does not export a run() function`
515
+ )
516
+
517
+ await pre()
518
+ }
519
+
520
+ console.log('')
521
+ printTitle(CoreMeta.colors.green, 'Running Action')
522
+ console.log('')
523
+
524
+ // CJS actions need to be required, not imported.
525
+ const { run } = require(osEntrypoints.main)
526
+
527
+ // Check if the required path is a function.
528
+ if (typeof run !== 'function')
529
+ throw new Error(
530
+ `Entrypoint ${EnvMeta.entrypoint} does not export a run() function`
531
+ )
532
+
533
+ await run()
534
+
535
+ if (osEntrypoints.post) {
536
+ console.log('')
537
+ printTitle(CoreMeta.colors.red, 'Running Post Step')
538
+ console.log('')
539
+
540
+ // CJS actions need to be required, not imported.
541
+ const { run: post } = require(osEntrypoints.post)
542
+
543
+ // Check if the required path is a function.
544
+ if (typeof post !== 'function')
545
+ throw new Error(
546
+ `Entrypoint ${options.post} does not export a run() function`
547
+ )
548
+
549
+ await post()
550
+ }
551
+ } finally {
552
+ if (process.env.NODE_PACKAGE_MANAGER === 'yarn' && needsReplug)
553
+ replug(fs, packageJsonPath, Object.keys(stubs))
554
+ }
555
+ }
556
+
557
+ // Print the action outputs, if any.
558
+ console.log('')
559
+ printTitle(CoreMeta.colors.yellow, 'Action Outputs')
560
+ console.log('')
561
+
562
+ if (Object.keys(CoreMeta.outputs).length > 0)
563
+ console.table(
564
+ Object.keys(CoreMeta.outputs).map(key => ({
565
+ Output: key,
566
+ Value: CoreMeta.outputs[key]
567
+ }))
568
+ )
569
+ else console.log('No outputs were set!')
570
+ console.log('')
571
+ }
572
+
573
+ /**
574
+ * "Re-plugs" yarn dependencies.
575
+ *
576
+ * For yarn PnP support, we need to unplug any stubbed dependencies. This should
577
+ * only be temporary, so as a final step we need to "re-plug" them.
578
+ *
579
+ * In this case, we're taking the easy way and just updating the
580
+ * `dependenciesMeta` property in `package.json`.
581
+ */
582
+ export function replug(
583
+ fs: typeof import('fs'),
584
+ packageJsonPath: string,
585
+ stubs: string[]
586
+ ): void {
587
+ console.log()
588
+ printTitle(CoreMeta.colors.magenta, 'Yarn: Re-Plugging Dependencies')
589
+ console.log()
590
+
591
+ // For each of the stubs, remove the "unplugged" property from the
592
+ // `dependenciesMeta` property in package.json.
593
+ const packageJson = JSON.parse(
594
+ fs.readFileSync(packageJsonPath, { encoding: 'utf8', flag: 'r' })
595
+ ) as {
596
+ dependenciesMeta: {
597
+ [key: string]: {
598
+ unplugged?: boolean
599
+ }
600
+ }
601
+ }
602
+
603
+ // Remove the unplugged property from the dependenciesMeta object.
604
+ for (const stub of stubs)
605
+ for (const key of Object.keys(packageJson.dependenciesMeta ?? {}))
606
+ if (key.startsWith(stub)) {
607
+ packageJson.dependenciesMeta[key].unplugged = false
608
+ console.log(`Replugged: ${stub}`)
609
+ }
610
+
611
+ // Save the file.
612
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), {
613
+ encoding: 'utf8',
614
+ flag: 'w'
615
+ })
616
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env tsx
2
+ import type { Command } from 'commander'
3
+ import { makeProgram } from './command.js'
4
+
5
+ /**
6
+ * Runs the Program
7
+ */
8
+ export async function run(): Promise<void> {
9
+ const chalk = (await import('chalk')).default
10
+ const { textSync } = (await import('figlet')).default
11
+
12
+ // Print the header
13
+ console.log(chalk.blue(textSync('Action Debugger')))
14
+
15
+ // Run the program
16
+ const program: Command = await makeProgram()
17
+ program.parse()
18
+ }
19
+
20
+ /* istanbul ignore next */
21
+ run()
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Last Reviewed Commit: https://github.com/actions/toolkit/blob/ee91adfbc4f4d1470e176a8d568c0cbdabaf98e0/packages/artifact/src/artifact.ts
3
+ * Last Reviewed Date: 2026-01-16
4
+ */
5
+
6
+ import {
7
+ type ArtifactClient,
8
+ DefaultArtifactClient
9
+ } from './internal/client.js'
10
+
11
+ const client: ArtifactClient = new DefaultArtifactClient()
12
+ export default client
13
+
14
+ export const ARTIFACT_STUBS = {
15
+ DefaultArtifactClient,
16
+ client
17
+ }