@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,73 @@
1
+ import { jest } from '@jest/globals'
2
+ import path from 'path'
3
+ import { Context } from '../../../src/stubs/github/context.js'
4
+
5
+ let envBackup: NodeJS.ProcessEnv
6
+
7
+ describe('github/context', () => {
8
+ beforeEach(() => {
9
+ envBackup = process.env
10
+ process.env.GITHUB_REPOSITORY = 'github/local-action'
11
+ })
12
+
13
+ afterEach(() => {
14
+ process.env = envBackup
15
+ jest.resetAllMocks()
16
+ })
17
+
18
+ describe('Context', () => {
19
+ it('Creates a Context object', () => {
20
+ const context = new Context()
21
+ expect(context).toBeInstanceOf(Context)
22
+ })
23
+
24
+ it('Gets the event payload', () => {
25
+ process.env.GITHUB_EVENT_PATH = path.join(
26
+ process.cwd(),
27
+ '__fixtures__',
28
+ 'payloads',
29
+ 'issue_opened.json'
30
+ )
31
+
32
+ const context = new Context()
33
+ expect(context.payload.action).toBe('opened')
34
+ })
35
+
36
+ it('Does not get the event payload if the path does not exist', () => {
37
+ process.env.GITHUB_EVENT_PATH = path.join(
38
+ process.cwd(),
39
+ '__fixtures__',
40
+ 'payloads',
41
+ 'does_not_exist.json'
42
+ )
43
+
44
+ const context = new Context()
45
+ expect(context.payload.action).toBeUndefined()
46
+ })
47
+
48
+ it('Gets the issue payload', () => {
49
+ process.env.GITHUB_EVENT_PATH = path.join(
50
+ process.cwd(),
51
+ '__fixtures__',
52
+ 'payloads',
53
+ 'issue_opened.json'
54
+ )
55
+
56
+ const context = new Context()
57
+ expect(context.issue.number).toBe(1)
58
+ })
59
+
60
+ it('Gets the repo payload', () => {
61
+ process.env.GITHUB_EVENT_PATH = path.join(
62
+ process.cwd(),
63
+ '__fixtures__',
64
+ 'payloads',
65
+ 'issue_opened.json'
66
+ )
67
+
68
+ const context = new Context()
69
+ expect(context.repo.owner).toBe('github')
70
+ expect(context.repo.repo).toBe('local-action')
71
+ })
72
+ })
73
+ })
@@ -0,0 +1,15 @@
1
+ import { jest } from '@jest/globals'
2
+ import * as github from '../../../src/stubs/github/github.js'
3
+ import { GitHub } from '../../../src/stubs/github/utils.js'
4
+
5
+ describe('github/github', () => {
6
+ afterEach(() => {
7
+ jest.resetAllMocks()
8
+ })
9
+
10
+ describe('getOctokit', () => {
11
+ it('Returns the options', () => {
12
+ expect(github.getOctokit('test')).toBeInstanceOf(GitHub)
13
+ })
14
+ })
15
+ })
@@ -0,0 +1,98 @@
1
+ import { jest } from '@jest/globals'
2
+ import * as httpClient from '../../../../__fixtures__/@actions/http-client.js'
3
+
4
+ jest.unstable_mockModule('@actions/http-client', () => httpClient)
5
+
6
+ const utils = await import('../../../../src/stubs/github/internal/utils.js')
7
+
8
+ const destinationUrl = 'http://example.com'
9
+ const expectedProxyAgent = {}
10
+ const expectedProxyAgentDispatcher = {}
11
+
12
+ describe('github/internal/utils', () => {
13
+ afterEach(() => {
14
+ jest.resetAllMocks()
15
+ })
16
+
17
+ describe('getAuthString', () => {
18
+ it('Throws if both inputs are missing', () => {
19
+ expect(() => utils.getAuthString('', {})).toThrow(
20
+ 'Parameter token or opts.auth is required'
21
+ )
22
+ })
23
+
24
+ it('Throws if both inputs are present', () => {
25
+ expect(() => utils.getAuthString('TOKEN', { auth: 'TOKEN' })).toThrow(
26
+ 'Parameters token and opts.auth may not both be specified'
27
+ )
28
+ })
29
+
30
+ it('Returns the options.auth value', () => {
31
+ expect(utils.getAuthString('', { auth: 'TOKEN' })).toBe('TOKEN')
32
+ })
33
+
34
+ it('Returns the token value', () => {
35
+ expect(utils.getAuthString('TOKEN', {})).toBe('token TOKEN')
36
+ })
37
+ })
38
+
39
+ describe('getProxyAgent', () => {
40
+ it('Returns the proxy agent', () => {
41
+ httpClient.getAgent.mockReturnValue(expectedProxyAgent)
42
+
43
+ const result = utils.getProxyAgent(destinationUrl)
44
+
45
+ expect(result).toBe(expectedProxyAgent)
46
+ expect(httpClient.getAgent).toHaveBeenCalledWith(destinationUrl)
47
+ })
48
+
49
+ it('Returns undefined if no proxy agent is provided', () => {
50
+ httpClient.getAgent.mockReturnValue(undefined)
51
+
52
+ const result = utils.getProxyAgent(destinationUrl)
53
+
54
+ expect(result).toBeUndefined()
55
+ expect(httpClient.getAgent).toHaveBeenCalledWith(destinationUrl)
56
+ })
57
+ })
58
+
59
+ describe('getProxyAgentDispatcher', () => {
60
+ it('Returns the proxy agent dispatcher', () => {
61
+ httpClient.getAgentDispatcher.mockReturnValue(
62
+ expectedProxyAgentDispatcher
63
+ )
64
+
65
+ const result = utils.getProxyAgentDispatcher(destinationUrl)
66
+
67
+ expect(result).toBe(expectedProxyAgentDispatcher)
68
+ expect(httpClient.getAgentDispatcher).toHaveBeenCalledWith(destinationUrl)
69
+ })
70
+
71
+ it('Returns undefined if no proxy agent is provided', () => {
72
+ httpClient.getAgentDispatcher.mockReturnValue(undefined)
73
+
74
+ const result = utils.getProxyAgentDispatcher(destinationUrl)
75
+
76
+ expect(result).toBeUndefined()
77
+ expect(httpClient.getAgentDispatcher).toHaveBeenCalledWith(destinationUrl)
78
+ })
79
+ })
80
+
81
+ describe('getApiBaseUrl', () => {
82
+ it('Returns the base URL', () => {
83
+ delete process.env.GITHUB_API_URL
84
+
85
+ const result = utils.getApiBaseUrl()
86
+
87
+ expect(result).toBe('https://api.github.com')
88
+ })
89
+
90
+ it('Returns the base URL from the environment variable', () => {
91
+ process.env.GITHUB_API_URL = 'https://example.com'
92
+
93
+ const result = utils.getApiBaseUrl()
94
+
95
+ expect(result).toBe('https://example.com')
96
+ })
97
+ })
98
+ })
@@ -0,0 +1,16 @@
1
+ import { jest } from '@jest/globals'
2
+ import * as utils from '../../../src/stubs/github/utils.js'
3
+
4
+ describe('github/utils', () => {
5
+ afterEach(() => {
6
+ jest.resetAllMocks()
7
+ })
8
+
9
+ describe('getOctokitOptions', () => {
10
+ it('Returns the options', () => {
11
+ expect(utils.getOctokitOptions('test')).toEqual({
12
+ auth: 'token test'
13
+ })
14
+ })
15
+ })
16
+ })
@@ -0,0 +1,78 @@
1
+ import { jest } from '@jest/globals'
2
+ import * as path from '../../__fixtures__/path.js'
3
+ import { ResetCoreMetadata } from '../../src/stubs/core/core.js'
4
+ import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env.js'
5
+
6
+ jest.unstable_mockModule('path', () => path)
7
+
8
+ const { getOSEntrypoints } = await import('../../src/utils/config.js')
9
+
10
+ describe('Config', () => {
11
+ beforeEach(() => {
12
+ // Reset metadata
13
+ ResetEnvMetadata()
14
+ ResetCoreMetadata()
15
+
16
+ path.resolve.mockImplementation(value => value)
17
+
18
+ EnvMeta.entrypoint = '/action/entrypoint.ts'
19
+ })
20
+
21
+ afterEach(() => {
22
+ jest.resetAllMocks()
23
+ })
24
+
25
+ describe('getOSEntrypoints()', () => {
26
+ it('Gets the ESM entrypoints', () => {
27
+ EnvMeta.preEntrypoint = '/action/pre.ts'
28
+ EnvMeta.postEntrypoint = '/action/post.ts'
29
+
30
+ const result = getOSEntrypoints('esm')
31
+
32
+ expect(result).toEqual({
33
+ main: '/action/entrypoint.ts',
34
+ pre: '/action/pre.ts',
35
+ post: '/action/post.ts'
36
+ })
37
+ })
38
+
39
+ it('Gets the ESM entrypoints (undefined)', () => {
40
+ EnvMeta.preEntrypoint = undefined
41
+ EnvMeta.postEntrypoint = undefined
42
+
43
+ const result = getOSEntrypoints('esm')
44
+
45
+ expect(result).toEqual({
46
+ main: '/action/entrypoint.ts',
47
+ pre: undefined,
48
+ post: undefined
49
+ })
50
+ })
51
+
52
+ it('Gets the CJS entrypoints', () => {
53
+ EnvMeta.preEntrypoint = '/action/pre.ts'
54
+ EnvMeta.postEntrypoint = '/action/post.ts'
55
+
56
+ const result = getOSEntrypoints('cjs')
57
+
58
+ expect(result).toEqual({
59
+ main: '/action/entrypoint.ts',
60
+ pre: '/action/pre.ts',
61
+ post: '/action/post.ts'
62
+ })
63
+ })
64
+
65
+ it('Gets the CJS entrypoints (undefined)', () => {
66
+ EnvMeta.preEntrypoint = undefined
67
+ EnvMeta.postEntrypoint = undefined
68
+
69
+ const result = getOSEntrypoints('cjs')
70
+
71
+ expect(result).toEqual({
72
+ main: '/action/entrypoint.ts',
73
+ pre: undefined,
74
+ post: undefined
75
+ })
76
+ })
77
+ })
78
+ })
@@ -0,0 +1,32 @@
1
+ import { jest } from '@jest/globals'
2
+ import { ResetCoreMetadata } from '../../src/stubs/core/core.js'
3
+ import { ResetEnvMetadata } from '../../src/stubs/env.js'
4
+ import { printTitle } from '../../src/utils/output.js'
5
+
6
+ // Prevent output during tests
7
+ const console_logSpy: jest.SpiedFunction<typeof console.log> = jest
8
+ .spyOn(console, 'log')
9
+ .mockImplementation(() => {})
10
+
11
+ describe('Output', () => {
12
+ beforeEach(() => {
13
+ // Reset metadata
14
+ ResetEnvMetadata()
15
+ ResetCoreMetadata()
16
+ })
17
+
18
+ afterEach(() => {
19
+ jest.resetAllMocks()
20
+ })
21
+
22
+ describe('printTitle()', () => {
23
+ it('Prints the correct number of (=) signs', () => {
24
+ printTitle(console.log, 'Test')
25
+
26
+ expect(console_logSpy).toHaveBeenCalledTimes(3)
27
+ expect(console_logSpy).toHaveBeenNthCalledWith(1, '='.repeat(80))
28
+ expect(console_logSpy).toHaveBeenNthCalledWith(2, `${' '.repeat(38)}Test`)
29
+ expect(console_logSpy).toHaveBeenNthCalledWith(3, '='.repeat(80))
30
+ })
31
+ })
32
+ })
@@ -0,0 +1,33 @@
1
+ import { jest } from '@jest/globals'
2
+ import * as fs from '../../__fixtures__/fs.js'
3
+ import { ResetCoreMetadata } from '../../src/stubs/core/core.js'
4
+ import { ResetEnvMetadata } from '../../src/stubs/env.js'
5
+
6
+ jest.unstable_mockModule('fs', () => fs)
7
+
8
+ const { isESM } = await import('../../src/utils/package.js')
9
+
10
+ describe('Package', () => {
11
+ beforeEach(() => {
12
+ // Reset metadata
13
+ ResetEnvMetadata()
14
+ ResetCoreMetadata()
15
+ })
16
+
17
+ afterEach(() => {
18
+ jest.resetAllMocks()
19
+ })
20
+
21
+ describe('isESM', () => {
22
+ it('Returns true for ESM packages', () => {
23
+ fs.existsSync.mockReturnValue(true)
24
+ fs.readFileSync.mockReturnValue(
25
+ JSON.stringify({
26
+ type: 'module'
27
+ })
28
+ )
29
+
30
+ expect(isESM()).toBe(true)
31
+ })
32
+ })
33
+ })
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="106" height="20" role="img" aria-label="Coverage: 100%"><title>Coverage: 100%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="106" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="43" height="20" fill="#4c1"/><rect width="106" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="835" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="330">100%</text><text x="835" y="140" transform="scale(.1)" fill="#fff" textLength="330">100%</text></g></svg>
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from 'fs'
3
+ import { execSync } from 'node:child_process'
4
+ import { dirname } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+ import * as path from 'path'
7
+
8
+ /**
9
+ * This script is used to run the local action. It sets the NODE_OPTIONS
10
+ * environment variable to require the bootstrap script, which sets up the
11
+ * TypeScript environment for the action.
12
+ */
13
+
14
+ function entrypoint() {
15
+ const __dirname = dirname(fileURLToPath(import.meta.url))
16
+
17
+ // Save the current environment and path.
18
+ const envBackup = { ...process.env }
19
+ const pathBackup = process.env.PATH
20
+
21
+ // Delete the TARGET_ACTION_PATH environment variable.
22
+ delete process.env.TARGET_ACTION_PATH
23
+
24
+ try {
25
+ // Get the absolute path to the `@ronaldphilipsen/local-action` package.
26
+ const packagePath = path.resolve(__dirname, '..')
27
+
28
+ // Disable experimental warnings.
29
+ process.env.NODE_NO_WARNINGS = 1
30
+
31
+ // Start building the command to run local-action. The package manager will
32
+ // be prepended to this later.
33
+ let command = `tsx "${path.join(packagePath, 'src', 'index.ts')}"`
34
+
35
+ const args = process.argv.slice(2)
36
+
37
+ // Iterate over the arguments and build the command. If the argument is a
38
+ // directory and TARGET_ACTION_PATH is not set, set it to the absolute path
39
+ // of the directory. The first directory should be the target action path.
40
+ for (const arg of args)
41
+ if (
42
+ !process.env.TARGET_ACTION_PATH &&
43
+ fs.existsSync(path.resolve(arg)) &&
44
+ fs.lstatSync(path.resolve(arg)).isDirectory()
45
+ )
46
+ process.env.TARGET_ACTION_PATH = path.resolve(arg)
47
+
48
+ // If the TARGET_ACTION_PATH environment variable is not set, display the
49
+ // help message.
50
+ if (!process.env.TARGET_ACTION_PATH) {
51
+ command += ` -- --help`
52
+ execSync(command, { stdio: 'inherit' })
53
+ return
54
+ }
55
+
56
+ // Starting in the TARGET_ACTION_PATH, locate the package.json file and
57
+ // determine the package manager.
58
+ const actionPackageDirs = path
59
+ .resolve(process.env.TARGET_ACTION_PATH)
60
+ .split(path.sep)
61
+
62
+ while (actionPackageDirs.length > 0) {
63
+ const actionPackage = actionPackageDirs.join(path.sep)
64
+
65
+ // Check if the package.json file exists.
66
+ if (fs.existsSync(path.join(actionPackage, 'package.json'))) {
67
+ // Read the package.json file.
68
+ const json = JSON.parse(
69
+ fs.readFileSync(path.join(actionPackage, 'package.json')),
70
+ 'utf8'
71
+ )
72
+
73
+ // If the package.json file has a packageManager field, set the command
74
+ // to use that package manager.
75
+ if (json.packageManager?.startsWith('pnpm')) {
76
+ process.env.NODE_PACKAGE_MANAGER = 'pnpm'
77
+ command = 'pnpm dlx ' + command
78
+ } else if (json.packageManager?.startsWith('yarn')) {
79
+ process.env.NODE_PACKAGE_MANAGER = 'yarn'
80
+
81
+ // The older version of yarn does not support `yarn dlx`, so we fall
82
+ // back to `yarn exec`.
83
+ if (json.packageManager.startsWith('yarn@1'))
84
+ command = 'yarn exec ' + command
85
+ else command = 'yarn dlx ' + command
86
+ } else {
87
+ // Otherwise, fall back to npm.
88
+ process.env.NODE_PACKAGE_MANAGER = 'npm'
89
+ command = 'npm exec ' + command
90
+ }
91
+
92
+ break
93
+ }
94
+
95
+ // Remove the last directory from the path.
96
+ actionPackageDirs.pop()
97
+ }
98
+
99
+ if (actionPackageDirs.length === 0) {
100
+ console.log(
101
+ 'No package.json file found in the action directory or any parent directories.'
102
+ )
103
+ process.exit(1)
104
+ }
105
+
106
+ // Run the command. The original arguments are passed after the `--` to
107
+ // prevent them from being eaten by the package manager.
108
+ execSync(`${command} -- ${args.join(' ')}`, { stdio: 'inherit' })
109
+ } catch (error) {
110
+ console.log()
111
+ console.log(error)
112
+
113
+ // Restore the environment.
114
+ process.env = { ...envBackup }
115
+ process.env.PATH = pathBackup
116
+
117
+ process.exit(error.status)
118
+ }
119
+ }
120
+
121
+ entrypoint()
@@ -0,0 +1,32 @@
1
+ # Create a Job Summary
2
+
3
+ [Job summaries](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/)
4
+ are Markdown files generated by GitHub Actions and displayed on the summary page
5
+ of each job.
6
+
7
+ ![Job summary example](./img/job-summary.png)
8
+
9
+ > [!NOTE]
10
+ >
11
+ > This behavior is slightly different from what happens on GitHub Actions
12
+ > runners. If the file referenced by `GITHUB_STEP_SUMMARY` does not exist, the
13
+ > workflow run will fail with an error like the following:
14
+ >
15
+ > `Unable to access summary file: '/path/to/summary'.`
16
+ >
17
+ > However, GitHub Actions normally creates this file for you, so functionally
18
+ > you should not see a difference :blush:
19
+
20
+ When testing with `local-action`, you can output the summary for your action to
21
+ a file. Just set the `GITHUB_STEP_SUMMARY` environment variable in your
22
+ [`.env` file](../.env.example). If the summary file does not exist, it will be
23
+ created.
24
+
25
+ ```ini
26
+ GITHUB_STEP_SUMMARY="/path/to/summary.md"
27
+ ```
28
+
29
+ > [!WARNING]
30
+ >
31
+ > If your action calls `core.summary.write()` and no value is set for this
32
+ > environment variable, the action will fail!
@@ -0,0 +1,27 @@
1
+ # Debugging in Visual Studio Code
2
+
3
+ This package can also be used with Visual Studio Code's built-in debugging
4
+ tools. You just need to add a `launch.json` to the project containing your local
5
+ action. The following can be used as an example.
6
+
7
+ ```json
8
+ {
9
+ "version": "0.2.0",
10
+ "configurations": [
11
+ {
12
+ "name": "Debug Local Action",
13
+ "type": "node",
14
+ "request": "launch",
15
+ "runtimeExecutable": "local-action",
16
+ "cwd": "${workspaceRoot}",
17
+ "args": [".", "src/main.ts", ".env"],
18
+ "console": "integratedTerminal",
19
+ "skipFiles": ["<node_internals>/**", "node_modules/**"]
20
+ }
21
+ ]
22
+ }
23
+ ```
24
+
25
+ In the `args` section, make sure to specify the appropriate path, entrypoint,
26
+ and dotenv file path. After that, you can add breakpoints to your action code
27
+ and start debugging!
Binary file
@@ -0,0 +1,126 @@
1
+ # Supported Functionality
2
+
3
+ The following tables list the functionality of the GitHub Actions libraries and
4
+ whether or not they are currently supported by `local-action`.
5
+
6
+ > [!NOTE]
7
+ >
8
+ > [Workflow commands](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions)
9
+ > are currently unsupported. Since `local-action` only supports
10
+ > JavaScript/TypeScript actions, it is assumed that they are using the
11
+ > [GitHub Actions Toolkit](https://github.com/actions/toolkit).
12
+
13
+ ## [`@actions/artifact`](https://github.com/actions/toolkit/blob/main/packages/artifact/README.md)
14
+
15
+ The stubbed version of `@actions/artifact` functions similarly to the real
16
+ package. However, any artifacts that are created as part of a `local-action` run
17
+ will be stored on your local workstation. The specific path must be set using
18
+ the `LOCAL_ACTION_ARTIFACT_PATH` environment variable in the `.env` file passed
19
+ to the `local-action` command.
20
+
21
+ > [!NOTE]
22
+ >
23
+ > If this variable is not set, and you attempt to interact with
24
+ > `@actions/artifact`, you will receive an error message.
25
+
26
+ | Feature | Supported | Notes |
27
+ | -------------------- | ------------------ | ------------------------------ |
28
+ | `deleteArtifact()` | :white_check_mark: | |
29
+ | `downloadArtifact()` | :white_check_mark: | |
30
+ | `getArtifact()` | :white_check_mark: | |
31
+ | `listArtifacts()` | :white_check_mark: | |
32
+ | `uploadArtifact()` | :white_check_mark: | Retention settings are ignored |
33
+
34
+ > [!IMPORTANT]
35
+ >
36
+ > When working with artifacts that were created as part of actual GitHub Actions
37
+ > workflow runs (e.g. if you try to download an artifact from a different
38
+ > repository), these requests **will be passed to the GitHub API**.
39
+
40
+ ## [`@actions/cache`](https://github.com/actions/toolkit/tree/main/packages/cache)
41
+
42
+ The stubbed version of `@actions/cache` functions similarly to the real package.
43
+ However, any caches that are created as part of a `local-action` run will be
44
+ stored on your local workstation. The specific path must be set using the
45
+ `LOCAL_ACTION_CACHE_PATH` environment variable in the `.env` file passed to the
46
+ `local-action` command.
47
+
48
+ - Since this is a local cache, the GitHub Actions cache limits (e.g. size,
49
+ retention) do not apply. However, the stubbed version does enforce that the
50
+ `key` and `restoreKeys` parameters are unique per call to `saveCache()`.
51
+ - The current implementation of the `@actions/cache` package checks whether to
52
+ use `v1` or `v2` of the caching service. This is ignored in the local
53
+ implementation.
54
+ - Additionally, since no actual caches are downloaded/uploaded, concepts like
55
+ chunking, concurrency, and timeouts no longer apply. Because of this, most
56
+ properties in the `options` parameter of `restoreCache()` and `saveCache()`
57
+ are ignored.
58
+ - Any upload/download progress tracking is ignored. E.g. the `DownloadProgress`
59
+ and `UploadProgress` classes are not implemented.
60
+
61
+ | Feature | Supported | Notes |
62
+ | ------------------ | ------------------ | ------------------------------------------ |
63
+ | `restoreCache()` | :white_check_mark: | Only the `lookupOnly` option is respected. |
64
+ | `saveCache()` | :white_check_mark: | All options are ignored |
65
+ | `DownloadProgress` | :x: | |
66
+ | `UploadProgress` | :x: | |
67
+
68
+ ## [`@actions/core`](https://github.com/actions/toolkit/blob/main/packages/core/README.md)
69
+
70
+ | Feature | Supported | Notes |
71
+ | --------------------- | ------------------ | ------------------------------- |
72
+ | `exportVariable()` | :white_check_mark: | |
73
+ | `setSecret()` | :white_check_mark: | |
74
+ | `addPath()` | :white_check_mark: | |
75
+ | `getInput()` | :white_check_mark: | |
76
+ | `getMultilineInput()` | :white_check_mark: | |
77
+ | `getBooleanInput()` | :white_check_mark: | |
78
+ | `setOutput()` | :white_check_mark: | |
79
+ | `setCommandEcho()` | :white_check_mark: | Setting is not currently in use |
80
+ | `setFailed()` | :white_check_mark: | |
81
+ | `isDebug()` | :white_check_mark: | |
82
+ | `debug()` | :white_check_mark: | |
83
+ | `error()` | :white_check_mark: | |
84
+ | `warning()` | :white_check_mark: | |
85
+ | `notice()` | :white_check_mark: | |
86
+ | `info()` | :white_check_mark: | |
87
+ | `startGroup()` | :white_check_mark: | |
88
+ | `endGroup()` | :white_check_mark: | |
89
+ | `group()` | :white_check_mark: | |
90
+ | `saveState()` | :white_check_mark: | State is not currently in use |
91
+ | `getState()` | :white_check_mark: | State is not currently in use |
92
+ | `getIDToken()` | :no_entry: | |
93
+ | `summary.*` | :white_check_mark: | |
94
+ | `toPosixPath()` | :white_check_mark: | |
95
+ | `toWin32Path()` | :white_check_mark: | |
96
+ | `toPlatformPath()` | :white_check_mark: | |
97
+ | `platform.*` | :white_check_mark: | |
98
+
99
+ ## [`@actions/github`](https://github.com/actions/toolkit/tree/main/packages/github)
100
+
101
+ The stubbed version of `@actions/github` functions the same as the real package.
102
+ However, the functionality is stubbed in order to ensure that all needed
103
+ environment variables are pulled from the `.env` file passed to the
104
+ `local-action` command. Otherwise, things like `github.context.eventName` will
105
+ be `undefined`. For more information, see
106
+ [#149](https://github.com/github/local-action/issues/149).
107
+
108
+ ## Under Investigation
109
+
110
+ The following packages are under investigation for how to integrate with
111
+ `local-action`. Make sure to check back later!
112
+
113
+ ## No Action Needed
114
+
115
+ Currently, there shouldn't be any need to stub the functionality of the
116
+ following packages from the GitHub Actions Toolkit; they _should_ function as
117
+ expected when run using `local-action`. If you do encounter a scenario where
118
+ this doesn't work correctly, please
119
+ [open an issue!](https://github.com/github/local-action/issues/new)
120
+
121
+ - [`@actions/attest`](https://github.com/actions/toolkit/tree/main/packages/attest)
122
+ - [`@actions/exec`](https://github.com/actions/toolkit/tree/main/packages/exec)
123
+ - [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob)
124
+ - [`@actions/http-client`](https://github.com/actions/toolkit/tree/main/packages/http-client)
125
+ - [`@actions/io`](https://github.com/actions/toolkit/tree/main/packages/io)
126
+ - [`@actions/tool-cache`](https://github.com/actions/toolkit/tree/main/packages/tool-cache)