Compare commits

...

512 Commits

Author SHA1 Message Date
Aiqiao Yan 0607d7a54b release new versions for a few packages 2026-04-21 17:15:02 +00:00
Aiqiao Yan 36d90eb54c Merge pull request #2356 from actions/dependabot/npm_and_yarn/flatted-3.4.2
chore(deps-dev): bump flatted from 3.3.3 to 3.4.2
2026-04-21 12:59:58 -04:00
Aiqiao Yan 4ee32849b4 Merge pull request #2346 from actions/dependabot/npm_and_yarn/packages/github/undici-6.24.0
chore(deps): bump undici from 6.23.0 to 6.24.0 in /packages/github
2026-04-21 12:55:26 -04:00
Aiqiao Yan d76f9fe99a Merge pull request #2348 from actions/dependabot/npm_and_yarn/packages/core/undici-6.24.1
chore(deps): bump undici from 6.23.0 to 6.24.1 in /packages/core
2026-04-21 12:54:48 -04:00
Aiqiao Yan 7e08d73d76 Merge pull request #2345 from actions/dependabot/npm_and_yarn/packages/glob/undici-6.24.0
chore(deps): bump undici from 6.23.0 to 6.24.0 in /packages/glob
2026-04-21 12:42:56 -04:00
Aiqiao Yan 8b842d839b Merge pull request #2355 from shogo82148/bump-minimatch-v10
@actions/glob: bump minimatch from v3.0.4 to v10.2.5
2026-04-21 12:38:52 -04:00
ICHINOSE Shogo 16cd46c365 Merge branch 'main' into bump-minimatch-v10 2026-04-21 21:43:27 +09:00
Aiqiao Yan 75b8dd1009 Merge pull request #2369 from actions/dependabot/npm_and_yarn/packages/glob/brace-expansion-1.1.13
chore(deps): bump brace-expansion from 1.1.12 to 1.1.13 in /packages/glob
2026-04-20 17:44:57 -04:00
Aiqiao Yan a7c6618070 Merge pull request #2381 from actions/dependabot/npm_and_yarn/axios-1.15.1
chore(deps-dev): bump axios from 1.12.2 to 1.15.1
2026-04-20 17:44:12 -04:00
Aiqiao Yan 54ad3ca9ba Merge pull request #2347 from actions/dependabot/npm_and_yarn/packages/http-client/undici-6.24.0
chore(deps): bump undici from 6.23.0 to 6.24.0 in /packages/http-client
2026-04-20 17:34:56 -04:00
dependabot[bot] 3c424f0d63 chore(deps-dev): bump axios from 1.12.2 to 1.15.1
Bumps [axios](https://github.com/axios/axios) from 1.12.2 to 1.15.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.12.2...v1.15.1)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.15.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 21:34:33 +00:00
Aiqiao Yan d9346d8d93 Merge pull request #2378 from actions/dependabot/npm_and_yarn/follow-redirects-1.16.0
chore(deps-dev): bump follow-redirects from 1.15.11 to 1.16.0
2026-04-20 17:33:08 -04:00
dependabot[bot] 1f375f130a chore(deps-dev): bump follow-redirects from 1.15.11 to 1.16.0
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.11 to 1.16.0.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 18:58:03 +00:00
dependabot[bot] 140509034c chore(deps): bump brace-expansion in /packages/glob
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.13.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.13)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-08 20:54:11 +00:00
Salman Chishti b68d046fe3 Merge pull request #2366 from salmanmkc/salmanmkc/github-9.1.0-release
chore: bump @actions/github to 9.1.0 for release
2026-04-08 21:11:16 +01:00
Salman Chishti e4598e374b chore: bump @actions/github to 9.1.0 for release
- Version bump 9.0.0 → 9.1.0 in package.json
- Update RELEASES.md with idempotency guard note and PR link
2026-04-08 20:07:19 +00:00
Salman Chishti 14a090004e Merge pull request #2364 from salmanmkc/salmanmkc/orchestration-id-support
feat(github): add orchestration ID to user-agent in getOctokitOptions
2026-04-08 20:54:57 +01:00
Salman Chishti 3643ce2db4 style: fix prettier formatting in orchestration tests 2026-04-08 19:38:31 +00:00
Salman Chishti ffeb50bd02 fix: prevent duplicate orchestration ID in user-agent
Add idempotency check to getUserAgentWithOrchestrationId — if the
tag is already present in baseUserAgent, return it unchanged. This
prevents doubling when both the exported helper and getOctokitOptions
run for the same client.
2026-04-08 16:49:32 +00:00
Salman Chishti b0917c5a37 style: fix prettier formatting in orchestration tests 2026-04-07 16:35:32 +00:00
Salman Chishti a8ea745713 feat(github): append orchestration ID to user-agent in getOctokitOptions
When ACTIONS_ORCHESTRATION_ID is set, appends
actions_orchestration_id/{sanitizedId} to the user-agent string.

- Add getUserAgentWithOrchestrationId() to internal/utils.ts
- Wire into getOctokitOptions() so all getOctokit() calls include it
- Re-export helper from @actions/github/lib/utils for downstream consumers
- 14 deterministic unit tests covering helper, integration, edge cases
2026-04-07 16:16:11 +00:00
Salman Chishti 0df75b91ff Merge pull request #2359 from actions/dependabot/npm_and_yarn/picomatch-2.3.2
chore(deps-dev): bump picomatch from 2.3.1 to 2.3.2
2026-03-26 12:25:19 +00:00
dependabot[bot] 233d556477 chore(deps-dev): bump picomatch from 2.3.1 to 2.3.2
Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2.
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 22:41:36 +00:00
dependabot[bot] 23cbecacad chore(deps-dev): bump flatted from 3.3.3 to 3.4.2
Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2.
- [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-21 10:32:03 +00:00
ICHINOSE Shogo 74fcfdbd10 @actions/glob: add some comments for the regression testing
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-20 19:56:02 +09:00
ICHINOSE Shogo 20647b6bcf @actions/core: update regression test with minimatch v3 2026-03-20 19:24:19 +09:00
ICHINOSE Shogo 6bd5e50ee1 @actions/glob: bump minimatch from v3.0.4 to v10.2.4 2026-03-20 18:00:34 +09:00
Bassem Dghaidi 44d43b5490 Merge pull request #2351 from actions/Link-/add-releases-docs
Add docs for the releases workflow
2026-03-16 16:45:42 +01:00
Bassem Dghaidi 76ac4dd95f Update contributing.md 2026-03-16 08:39:20 -07:00
Bassem Dghaidi 632b2cbff6 Add screenshot 2026-03-16 08:33:25 -07:00
Bassem Dghaidi 73bdb59ac2 Explain the new releases workflow 2026-03-16 08:29:53 -07:00
Bassem Dghaidi 22d35395d4 Merge pull request #2350 from actions/Link-/fix-tests-in-releases
Scope tests to the package being published
2026-03-16 15:01:41 +01:00
Bassem Dghaidi ed4fdc98c4 Add input to run isolated tests 2026-03-16 06:50:25 -07:00
Bassem Dghaidi 6211ca9cbd Merge pull request #2349 from actions/Link-/update-release-workflow
Update release workflow to permit shipping from non main branches
2026-03-16 14:29:21 +01:00
Bassem Dghaidi 99dfdab194 Fix the description of npm-tag 2026-03-16 06:14:29 -07:00
Bassem Dghaidi 6ec76cbf3d Update release workflow to permit shipping from non main branches 2026-03-16 06:05:25 -07:00
dependabot[bot] 7c6cc28ed5 chore(deps): bump undici from 6.23.0 to 6.24.1 in /packages/core
Bumps [undici](https://github.com/nodejs/undici) from 6.23.0 to 6.24.1.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.23.0...v6.24.1)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.24.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-14 09:09:41 +00:00
dependabot[bot] 8f62bc23d1 chore(deps): bump undici from 6.23.0 to 6.24.0 in /packages/http-client
Bumps [undici](https://github.com/nodejs/undici) from 6.23.0 to 6.24.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.23.0...v6.24.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.24.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-14 05:22:04 +00:00
dependabot[bot] bbaffb4bb3 chore(deps): bump undici from 6.23.0 to 6.24.0 in /packages/github
Bumps [undici](https://github.com/nodejs/undici) from 6.23.0 to 6.24.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.23.0...v6.24.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.24.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-14 04:56:28 +00:00
dependabot[bot] c23cc6e61c chore(deps): bump undici from 6.23.0 to 6.24.0 in /packages/glob
Bumps [undici](https://github.com/nodejs/undici) from 6.23.0 to 6.24.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.23.0...v6.24.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.24.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-14 04:40:06 +00:00
Salman Chishti 943ff82d3d Merge pull request #2344 from actions/dependabot/npm_and_yarn/packages/artifact/undici-6.24.0
chore(deps): bump undici from 6.23.0 to 6.24.0 in /packages/artifact
2026-03-14 04:35:35 +00:00
dependabot[bot] 06bca4509d chore(deps): bump undici from 6.23.0 to 6.24.0 in /packages/artifact
Bumps [undici](https://github.com/nodejs/undici) from 6.23.0 to 6.24.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.23.0...v6.24.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.24.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-14 04:17:32 +00:00
Daniel Kennedy 21229dc09e Artifact: support downloading artifacts with CJK characters in their name (#2341)
* Artifact: support downloading artifacts with CJK characters in their name

* Fix some linting/PR comments

* One more linting fix
2026-03-11 09:30:15 -04:00
Meredith Lancaster 6fd292ebdd Merge pull request #2337 from actions/dependabot/npm_and_yarn/packages/attest/tar-7.5.10
chore(deps): bump tar from 7.5.7 to 7.5.10 in /packages/attest
2026-03-06 06:54:55 -08:00
dependabot[bot] 89f01c9125 chore(deps): bump tar from 7.5.7 to 7.5.10 in /packages/attest
Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.7 to 7.5.10.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v7.5.7...v7.5.10)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 7.5.10
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-06 13:29:24 +00:00
Zachary Taylor 85466c0f54 Merge pull request #2323 from actions/zaataylor-update-artifact-storage-err-msg
Update artifact storage error message
2026-02-26 14:37:38 -05:00
Zachary Taylor bd4fb086f1 Update UsageError in cache 2026-02-26 14:31:12 -05:00
Zachary Taylor 49c3d09c01 Update error message 2026-02-26 14:28:46 -05:00
Brian DeHamer 91d76bea50 Merge pull request #2321 from actions/bdehamer/storage-record-orchestration-id
Custom user-agent string for storage record API reqs
2026-02-26 10:52:42 -08:00
Brian DeHamer 69f29a1b1c Update packages/attest/src/artifactMetadata.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-25 17:40:10 -08:00
Brian DeHamer 7987771a2b new user-agent string for storage record API reqs
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2026-02-25 15:36:27 -08:00
Brian DeHamer 605cc18397 Merge pull request #2320 from actions/bdehamer/attest-orchestration-id
custom user-agent string for attestation API reqs
2026-02-25 11:25:53 -08:00
Brian DeHamer 27e5a955bf custom user-agent string for attestation API reqs
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2026-02-25 11:20:54 -08:00
Daniel Kennedy 6fe3c0f3e6 Artifact upload: support uploading single un-zipped files (#2256)
* Artifact upload: support uploading single un-zipped files

* Fix linters

* Fix lint again

* Fix tests

* Check for 0 sized artifact lists

* Add some more stream tests and handle an upload failure gracefully

* Add CI tests for non-zipped artifacts

* Add an html report to test rendering in the browser

* Fix linting issue

* Artifact: bump the version and add release notes

* Fix Windows tests

* Fix linting

* stream: switch the error details to error type

* Refactor the validation logic in `uploadArtifact` a bit

* Added more details about how the name parameter is handled
2026-02-25 11:01:38 -05:00
Daniel Kennedy 8c90e2297a fix(tests): close sockets to remove a Jest warning about resources outliving their tests (#2279) 2026-02-13 12:05:37 -05:00
dependabot[bot] 8351a5d84d chore(deps): bump fast-xml-parser in /packages/artifact (#2285)
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-version: 5.3.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-30 16:26:32 -05:00
Daniel Kennedy 5203b671f1 Releases: use ubuntu-latest instead of macos-latest-large (#2284) 2026-01-30 16:04:52 -05:00
Daniel Kennedy 975fcbd402 Artifact download: don't unzip non-zip artifacts (#2253)
* Download artifact: don't extract the downloaded file if the content-type isn't a zip

* Remove unused `import`

* Add support for specifying whether to skip decompressing

* Prevent path traversal attacks

* Fix indenting

* Update packages/artifact/__tests__/download-artifact.test.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Parse the mime type out of the content-type header

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix some linting issues

* Swap `zip` for `application/zip-compressed`

* Test: negative check for malicious paths

* Increase the timeout on one of the tests

* Check the URL path for `.zip` to see if we can auto-decompress

* Fix linting issue

* Bump the package version and add release notes

* Remove `launch.json`

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-30 12:18:38 -05:00
Tingting Wang ffae274475 Merge pull request #2268 from actions/dependabot/npm_and_yarn/packages/attest/tar-7.5.7
chore(deps): bump tar from 7.5.6 to 7.5.7 in /packages/attest
2026-01-29 13:01:33 -08:00
dependabot[bot] 1c20378379 chore(deps): bump tar from 7.5.6 to 7.5.7 in /packages/attest
Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.6 to 7.5.7.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v7.5.6...v7.5.7)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 7.5.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-29 20:20:49 +00:00
Daniel Kennedy 0be0a6ef89 @actions/attest: convert to an ESM module (#2278) 2026-01-29 15:19:39 -05:00
Daniel Kennedy ae29a2751b @actions/cache: convert to an ESM module (#2275)
* `@actions/cache`: convert to an ESM module

* Update the fixture to ESM syntax

* Update the cache workflows

* Bump `@actions/glob` to `0.6.1`

* Fix awaiting in the cache unit tests

* Fix a type issues in contracts

* Export the `DownloadOptions`/`UploadOptions` like before

* More cache test fixes

* Make the cache units tests better

* Add some more logging

* Add retries to restore-cache.mjs
2026-01-29 14:23:32 -05:00
Daniel Kennedy b48854e1ac @actions/glob: fix minimatch imports (#2276) 2026-01-29 13:30:54 -05:00
Daniel Kennedy 9d912b1840 @actions/tool-cache: convert to an ESM module (#2274)
* `@actions/tool-cache`: convert to an ESM module

* Fix jest config

* Downgrade `nock` since it's conflicting with `@actions/attest`'s version
2026-01-29 11:26:14 -05:00
Daniel Kennedy 7a0147b5c6 @actions/glob: convert to an ESM module (#2273)
* `@actions/glob`: convert to an ESM module

* Update packages/glob/RELEASES.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-29 10:41:33 -05:00
Daniel Kennedy 5793b08cd9 @actions/artifact: convert to an ESM module (#2266)
* `@actions/artifact`: convert to an ESM module

* Update the package-lock.json

* Undo the GHES ignores

* Fix the reference to `@actions/http-client` in the lock file

* Bump `@actions/core` to `3.0.0`

* Remove `jest.config.cjs`

* Import `OctoKitOptions` from `@octokit/core/types`

* Pull the package version from `package.json`

* Workaround getting the package version for the user-agent

* Fix the `archiver` import

* Fix linting
2026-01-29 09:52:09 -05:00
Daniel Kennedy ed3ea3b5ba @actions/core: convert to ESM module 2026-01-28 20:50:58 -05:00
Daniel Kennedy c9c663babe Bump @actions/io to 3.0.2 2026-01-28 15:59:40 -05:00
Daniel Kennedy 0fc1805b46 @actions/exec: convert to ESM module 2026-01-28 15:59:40 -05:00
Daniel Kennedy a6e9f4bab2 @actions/io: update lock file version 2026-01-28 14:36:03 -05:00
Daniel Kennedy 758b556388 @actions/io: export lib/io-util 2026-01-28 14:08:19 -05:00
Daniel Kennedy 9e060cb3e1 Add release notes 2026-01-28 13:33:17 -05:00
Daniel Kennedy 5501ba08b7 @actions/io: convert to ESM module 2026-01-28 13:33:17 -05:00
Daniel Kennedy 4446f00fc7 Add a release entry for 4.0.0 2026-01-28 10:27:09 -05:00
Daniel Kennedy 965dcc7493 Fix a JSON lint issue 2026-01-28 10:27:09 -05:00
Daniel Kennedy d464f9dd60 Add proxy/interfaces exports 2026-01-28 10:27:09 -05:00
Daniel Kennedy c9ab4f9548 http-client: convert to ESM 2026-01-28 10:27:09 -05:00
Lokesh Gopu a2986ee511 Merge pull request #2260 from actions/lokesh755-actions-github-v9-esm
ESM-only with updated @octokit dependencies
2026-01-27 15:58:18 -05:00
Daniel Kennedy e827417593 Bump @actions/glob to 0.5.1 in @actions/cache 2026-01-27 15:43:44 -05:00
Lokesh Gopu b05d26b3fa ESM-only with updated @octokit dependencies 2026-01-27 15:35:32 -05:00
Daniel Kennedy ecdfc18bf2 Bump @actions/glob version to 0.5.1 2026-01-27 14:55:56 -05:00
Daniel Kennedy e8e0ce7ad8 Bump @actions/core to 2.0.3 on @actions/glob 2026-01-27 14:55:56 -05:00
Daniel Kennedy dc6427f3c3 Attest: undo the @actions/github/@octokit bumps 2026-01-27 13:31:31 -05:00
Daniel Kennedy 76339b5f68 Bump @actions/http-client and @actions/github on all packages 2026-01-27 13:31:31 -05:00
Daniel Kennedy c0ef67ec49 Release @actions/github v8.0.1 2026-01-27 10:29:06 -05:00
Daniel Kennedy 968fd7f8d3 Bump undici to v6.23.0 and @actions/http-client to v3.0.2 in @actions/github 2026-01-27 10:29:06 -05:00
Daniel Kennedy 9b27fa97f9 Release @actions/http-client version 3.0.2 2026-01-27 09:53:50 -05:00
Daniel Kennedy 065cf9f0b1 Bump undici to v6.23.0 in @actions/http-client 2026-01-27 09:38:40 -05:00
Lokesh Gopu b77f226465 Merge pull request #2249 from actions/fix/upgrade-octokit-dependencies
upgrade octokit dependencies
2026-01-22 14:46:12 -05:00
Lokesh Gopu f61ae48376 upgrade octokit dependencies 2026-01-22 11:59:59 -05:00
Salman Chishti 4236fc3e78 Merge pull request #2246 from actions/dependabot/npm_and_yarn/packages/attest/tar-7.5.6
chore(deps): bump tar from 7.5.2 to 7.5.6 in /packages/attest
2026-01-22 15:53:06 +00:00
Salman Chishti f366966232 Merge pull request #2248 from actions/dependabot/npm_and_yarn/lodash-4.17.23
chore(deps): bump lodash from 4.17.21 to 4.17.23
2026-01-22 14:48:46 +00:00
dependabot[bot] bd561a6765 chore(deps): bump lodash from 4.17.21 to 4.17.23
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-22 00:03:54 +00:00
dependabot[bot] 26490f0d3b chore(deps): bump tar from 7.5.2 to 7.5.6 in /packages/attest
Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.2 to 7.5.6.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v7.5.2...v7.5.6)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 7.5.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-21 21:04:00 +00:00
Ryan Ghadimi ee91adfbc4 Merge pull request #2243 from GhadimiR/main
Don't retry 429s returned from the cache service
2026-01-16 10:43:35 +00:00
Ryan Ghadimi a039cff4a1 Add comment for rate limiting handling
Added a comment regarding rate limiting and retry behavior.
2026-01-16 10:26:45 +00:00
Ryan Ghadimi 9dd77993e7 Update packages/cache/RELEASES.md
Co-authored-by: Bassem Dghaidi <568794+Link-@users.noreply.github.com>
2026-01-16 10:25:44 +00:00
Ryan Ghadimi dd1bb93c72 New error type for cache RL 2026-01-16 10:00:15 +00:00
Ryan Ghadimi 7292b3508f update release doc 2026-01-16 09:51:21 +00:00
Ryan Ghadimi 4a47af6481 bump pkg 2026-01-16 09:46:28 +00:00
Ryan Ghadimi a68693e20a tests & releases 2026-01-16 09:44:57 +00:00
Ryan Ghadimi acf4bd70fb Add handling for 429s from cache service 2026-01-14 17:01:59 +00:00
Tingluo Huang 7ae5c2f423 Merge pull request #2237 from actions/users/tihuang/bump
Bump version to 2.2.0 and update dependencies for @actions/core, @actions/github, and @actions/http-client
2026-01-08 11:41:34 -05:00
Tingluo Huang d5470e6023 . 2026-01-08 16:19:53 +00:00
Tingluo Huang e68ab4b91a Bump version to 3.0.0 and update dependencies for @actions/core, @actions/github, and @actions/http-client 2026-01-08 14:34:05 +00:00
Tingluo Huang d3a48dd52f Merge pull request #2233 from actions/copilot/update-http-client-dependency
Update selected packages to consume @actions/http-client@3.0.1
2026-01-08 09:04:43 -05:00
Tingluo Huang c4d47c1922 Merge pull request #2236 from actions/users/tihuang/bump
Bump dependency version for actions/github and actions/tool-cache.
2026-01-08 08:38:35 -05:00
Tingluo Huang 5fc5cdde44 Bump dependency version for actions/github and actions/tool-cache. 2026-01-08 04:58:48 +00:00
copilot-swe-agent[bot] 2a9d836b08 Revert @actions/attest http-client dependency to ^2.2.3
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 23:17:02 +00:00
copilot-swe-agent[bot] 3a3b073ef2 Revert changes to github, tool-cache packages and .gitignore
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 22:38:20 +00:00
copilot-swe-agent[bot] d3a0fb260e Revert @actions/attest version to 2.1.0 - no new release needed
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 22:33:05 +00:00
copilot-swe-agent[bot] be4fdc505f Fix package-lock.json to mark typescript as dev dependency
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 22:10:30 +00:00
copilot-swe-agent[bot] 6be37922c5 Add .nx/ to .gitignore and remove cached build artifacts
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 22:04:45 +00:00
copilot-swe-agent[bot] dfc20acda2 Verify changes with tests - all package-specific tests passing
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 22:03:56 +00:00
copilot-swe-agent[bot] dc1fec82a6 Update all packages to use @actions/http-client@^3.0.1 and bump patch versions
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 21:55:41 +00:00
copilot-swe-agent[bot] 9339b3573b Initial plan 2026-01-07 21:51:58 +00:00
Tingluo Huang 67a08de5c7 Merge pull request #2231 from actions/copilot/prepare-http-client-release
Release @actions/http-client v3.0.1
2026-01-07 15:35:32 -05:00
copilot-swe-agent[bot] d73fffceed Combine duplicate RELEASES.md entries into single line
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 18:44:34 +00:00
copilot-swe-agent[bot] 16f0b3d28e Revert .gitignore change as requested
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 18:40:39 +00:00
copilot-swe-agent[bot] 398e2cb68b Change version from 3.1.0 to 3.0.1 as requested
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 18:31:01 +00:00
copilot-swe-agent[bot] 4b9031fa77 Bump http-client version to 3.1.0 and update RELEASES.md
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-07 18:13:07 +00:00
copilot-swe-agent[bot] 959cb66bd5 Initial plan 2026-01-07 18:02:29 +00:00
Tingluo Huang 3e0b611f99 Merge pull request #2229 from actions/copilot/extend-user-agent-http-requests
Extend user-agent with orchestration ID and add default user-agent
2026-01-07 10:42:56 -05:00
copilot-swe-agent[bot] 83c13c81ba Improve test cleanup and add missing test coverage
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 19:19:35 +00:00
copilot-swe-agent[bot] bccbba401a Fix lint error: remove unnecessary escape in regex
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 18:30:18 +00:00
copilot-swe-agent[bot] 3a191eecf6 Add default user-agent 'actions/http-client' when none provided
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 18:10:12 +00:00
copilot-swe-agent[bot] 97f5a6f0dc Simplify constructor by handling undefined in helper method
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 18:03:26 +00:00
copilot-swe-agent[bot] 48a7cdbf9c Remove .gitignore change for .nx/ directory
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 17:54:40 +00:00
copilot-swe-agent[bot] 3f1933edf9 Revert unrelated version bumps in package-lock.json files
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 17:50:28 +00:00
copilot-swe-agent[bot] 2215c8e5aa Restrict sanitization to only allow 0-9, a-z, _, -, .
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 17:37:28 +00:00
copilot-swe-agent[bot] af6de2cb95 Move orchestration ID logic to constructor for efficiency
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 17:30:28 +00:00
copilot-swe-agent[bot] 1dc58e3080 Rename github_orchestration_id to actions_orchestration_id
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 17:20:43 +00:00
copilot-swe-agent[bot] 20596c1d96 Move hyphen to end of character class for cleaner regex
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 16:30:09 +00:00
copilot-swe-agent[bot] 557f80fd03 Fix regex to properly escape hyphen in character class
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 16:28:52 +00:00
copilot-swe-agent[bot] 32c52bb78a Use product/version format and sanitize orchestration ID
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 16:26:29 +00:00
copilot-swe-agent[bot] 6d9a3fe547 Format code and update .gitignore to exclude .nx cache
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 15:36:15 +00:00
copilot-swe-agent[bot] 4e1c194b34 Add ACTIONS_ORCHESTRATION_ID support to http-client user-agent
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 15:32:55 +00:00
copilot-swe-agent[bot] 09cb71a033 Initial plan 2026-01-06 15:22:42 +00:00
Salman Chishti 2506e78e82 Merge pull request #2212 from actions/artifact-v5.0.1-release
docs(artifact): release @actions/artifact v5.0.1
2025-12-12 15:39:59 +00:00
Salman Muin Kayser Chishti f8003d52ff docs: add PR reference to artifact v5.0.1 release notes 2025-12-12 15:33:41 +00:00
Salman Muin Kayser Chishti e263dfb89d Merge remote-tracking branch 'origin/main' into artifact-v5.0.1-release 2025-12-12 15:33:18 +00:00
Salman Chishti 2e53bd8485 Merge pull request #2214 from actions/cache-v5.0.1-release
docs(cache): release @actions/cache v5.0.1
2025-12-12 15:30:16 +00:00
Salman Chishti b5e3b25b34 Merge pull request #2211 from actions/fix-artifact-storage-blob
fix(artifact): update @azure/storage-blob to fix Node.js 24 punycode deprecation
2025-12-12 15:29:23 +00:00
Salman Muin Kayser Chishti b71834a510 fix(artifact): update @azure/storage-blob to ^12.29.1 and remove deprecated packages 2025-12-12 15:01:31 +00:00
Salman Muin Kayser Chishti c6f0239e63 Merge remote-tracking branch 'origin/main' into fix-artifact-storage-blob 2025-12-12 14:59:31 +00:00
Salman Muin Kayser Chishti c655f38a0f docs(cache): add PR reference to v5.0.1 release notes 2025-12-12 14:53:26 +00:00
Salman Muin Kayser Chishti cf8caa4e0d Merge remote-tracking branch 'origin/main' into cache-v5.0.1-release 2025-12-12 14:50:27 +00:00
Salman Chishti 8734e578c6 Merge pull request #2213 from actions/fix-cache-storage-blob
fix(cache): update @azure/storage-blob to fix Node.js 24 punycode deprecation
2025-12-12 14:40:41 +00:00
Salman Muin Kayser Chishti 74ac6db523 fix(cache): update @azure/storage-blob to ^12.29.1 to address punycode deprecation 2025-12-12 14:05:14 +00:00
Salman Muin Kayser Chishti 6fc2f678c8 docs(cache): bump to v5.0.1 and add release notes 2025-12-12 13:42:58 +00:00
Salman Muin Kayser Chishti 5ef62e14dd fix(cache): update @azure/storage-blob to ^12.29.1 to fix punycode deprecation
- Updated @azure/storage-blob from ^12.13.0 to ^12.29.1
- Newer storage-blob uses @azure/core-rest-pipeline instead of deprecated @azure/core-http
- Fixes Node.js 24 deprecation warning for punycode module
2025-12-12 13:41:31 +00:00
Salman Muin Kayser Chishti 7b29e67278 docs(artifact): bump to v5.0.1 and add release notes 2025-12-12 13:40:07 +00:00
Salman Muin Kayser Chishti 9d2227dbb0 fix(artifact): update @azure/storage-blob to ^12.29.1 to fix punycode deprecation
- Removed direct @azure/core-http dependency
- Updated @azure/storage-blob from ^12.15.0 to ^12.29.1
- Newer storage-blob uses @azure/core-rest-pipeline instead of deprecated @azure/core-http
- Fixes Node.js 24 deprecation warning for punycode module
2025-12-12 13:38:13 +00:00
Salman Chishti 5a8462ec27 Merge pull request #2210 from actions/prepare-artifact-release
docs(artifact): add v5.0.0 release notes
2025-12-11 21:42:59 +00:00
Salman Muin Kayser Chishti fcaf488df6 chore(artifact): bump version to v5.0.0 2025-12-11 20:45:49 +00:00
Salman Muin Kayser Chishti 2b48e40e62 docs(artifact): add v5.0.0 release notes 2025-12-11 20:37:18 +00:00
Salman Chishti 44ec738e27 Merge pull request #2209 from actions/version-bumps
chore(artifact): bump dependencies for Node.js 24 support
2025-12-11 20:23:43 +00:00
Salman Muin Kayser Chishti 3af0128b01 chore(artifact): bump dependencies for Node.js 24 support 2025-12-11 19:20:51 +00:00
Salman Chishti e74405f68c Merge pull request #2194 from actions/prepare-cache-release-v5.0.0
Prepare cache v5 release
2025-12-11 16:00:48 +00:00
Salman Muin Kayser Chishti cc6abe3c3a chore(releases): update release notes for v5.0.0 to remove punycode deprecation warning 2025-12-11 14:43:23 +00:00
Salman Muin Kayser Chishti c6502bc679 PR number update in releases 2025-12-11 14:33:30 +00:00
Salman Muin Kayser Chishti bdd6eb4293 update releases 2025-12-11 14:32:58 +00:00
Salman Muin Kayser Chishti 6785788751 Merge remote-tracking branch 'origin/main' into prepare-cache-release-v5.0.0 2025-12-11 14:27:41 +00:00
Salman Chishti ddf2d52556 Merge pull request #2198 from actions/cache-bump-deps
chore(cache): bump @actions/* dependencies to v2/v3
2025-12-11 14:25:35 +00:00
Salman Muin Kayser Chishti 7c1b12a15e chore(cache): update @actions/core to 2.0.1 2025-12-11 13:57:58 +00:00
Salman Muin Kayser Chishti fdbf9e3ec2 Merge remote-tracking branch 'origin/main' into cache-bump-deps 2025-12-11 13:54:20 +00:00
Salman Muin Kayser Chishti 369aa55cdc update to core 2.0.1 which has exec 2.0.0 2025-12-11 13:54:17 +00:00
Brian DeHamer e1191599bb Merge pull request #2203 from actions/node-update-for-publish
Use node24 for publishing
2025-12-10 14:19:00 -08:00
Brian DeHamer c043714a35 use node24 for publishing
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2025-12-10 14:14:15 -08:00
Brian DeHamer 3ac6e0fdf2 Merge pull request #2201 from actions/npm-trusted-publishing
Enable npm trusted publishing
2025-12-10 13:32:35 -08:00
Brian DeHamer d9f9074fee npm trusted publishing
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2025-12-10 13:27:16 -08:00
Salman Chishti 2c52220624 Merge pull request #2199 from actions/core-bump-exec
prepare @actions/exec 2.0.1 relesae + chore(core): bump @actions/exec from ^1.1.1 to ^2.0.0
2025-12-10 12:08:47 +00:00
Salman Muin Kayser Chishti b2e6a5a284 chore(core): bump @actions/exec from ^1.1.1 to ^2.0.0
Aligns with cache package which already uses exec@2.0.0, avoiding nested duplicate dependencies.
2025-12-10 11:54:17 +00:00
Salman Muin Kayser Chishti e48877e66c chore(cache): bump @actions/* dependencies to v2/v3
- @actions/core: ^1.11.1 → ^2.0.0
- @actions/exec: ^1.0.1 → ^2.0.0
- @actions/glob: ^0.1.0 → ^0.5.0
- @actions/http-client: ^2.1.1 → ^3.0.0
- @actions/io: ^1.0.1 → ^2.0.0
2025-12-10 11:38:25 +00:00
Salman Chishti bdddd872e3 Merge pull request #2197 from actions/cache-remove-ms-rest-js
fix(cache): replace @azure/ms-rest-js with @azure/core-rest-pipeline
2025-12-10 11:36:53 +00:00
Salman Muin Kayser Chishti 8a2701f328 fix(cache): replace @azure/ms-rest-js with @azure/core-rest-pipeline
Remove abandoned @azure/ms-rest-js dependency which pulls in node-fetch@v2, causing punycode deprecation warnings on Node.js 24+.

The TransferProgressEvent type is now imported from @azure/core-rest-pipeline instead.
2025-12-10 11:23:06 +00:00
Salman Muin Kayser Chishti eb7ff8401e chore(cache): regenerate package-lock.json with Node 24 2025-12-10 11:09:56 +00:00
Salman Muin Kayser Chishti 45ec4a2087 chore: update dependencies and remove deprecated package
- Removed `@azure/ms-rest-js` dependency to fix Node.js 24+ punycode deprecation warning.
  - The `TransferProgressEvent` type is now imported from `@azure/core-rest-pipeline`.
- Updated `package.json` to reflect the new dependency.
- Updated tests to import `TransferProgressEvent` from the new package.
- Updated `package-lock.json` to remove `@azure/ms-rest-js` and include `@azure/core-rest-pipeline`.
- Bumped versions of several dependencies including `@azure/storage-blob` and `@azure/storage-common`.
2025-12-10 11:04:34 +00:00
Meredith Lancaster 02869fefb4 Merge pull request #2196 from actions/update-attest-storage-record-params
Refactor attest package createStorageRecord function params
2025-12-09 11:49:14 -08:00
Meredith Lancaster 701191f50e fix linter issues
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-09 11:40:40 -08:00
Meredith Lancaster 539724611c param name
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-09 11:39:12 -08:00
Meredith Lancaster 3d01d7ed69 Update packages/attest/README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-09 11:38:06 -08:00
Meredith Lancaster d75223fd4a split mega param into several different ones
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-09 11:37:04 -08:00
Meredith Lancaster 056c217a52 Merge pull request #2192 from actions/malancas/create-artifact-metadata-storage-record
Add the `createStorageRecord` function to the `attest` package for creating artifact storage records
2025-12-09 08:55:21 -08:00
Meredith Lancaster d795a0ad0d linter fix
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-09 08:32:31 -08:00
Salman Muin Kayser Chishti b0464628c0 Prepare cache v5 release 2025-12-09 16:11:32 +00:00
Meredith Lancaster 0380590fdd fix expected endpoint response
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-09 08:02:38 -08:00
Salman Chishti 5e183dabac Merge pull request #2193 from actions/fix-audit-vulnerabilities
chore: fix npm audit vulnerabilities (glob, js-yaml)
2025-12-09 15:39:33 +00:00
Meredith Lancaster 97b7fa81c8 regenerate package lock
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 19:22:04 -08:00
Meredith Lancaster 87afd16bb2 bump to next minor version
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 19:19:29 -08:00
Meredith Lancaster c40fa0d905 formatting
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 19:19:11 -08:00
Meredith Lancaster dc9f635a0d Update packages/attest/src/artifactMetadata.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-08 16:30:37 -08:00
Meredith Lancaster 7847d31696 Update packages/attest/README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-08 16:30:25 -08:00
Meredith Lancaster 10d3b034e0 fix linter issues
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 16:22:59 -08:00
Meredith Lancaster 8eca440361 fix test and function calls
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 15:59:25 -08:00
Meredith Lancaster 6ec87f46b7 add back param parsing function
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 15:39:26 -08:00
Meredith Lancaster d1f9584cda fix test calls
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 15:33:01 -08:00
Meredith Lancaster b8933d0495 reorganize function options and document
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 15:25:34 -08:00
Meredith Lancaster 0a988d204e rename file
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 15:16:26 -08:00
Meredith Lancaster 136f9dfe37 fix header link
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 14:07:17 -08:00
Meredith Lancaster ed78411ffb fix expected response
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 14:03:23 -08:00
Meredith Lancaster dd097c7f4e add section on createStorageRecord func
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 13:57:00 -08:00
Meredith Lancaster f01262913d table of contents
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 13:55:24 -08:00
Meredith Lancaster c034e76488 fix function exporting and test results
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 13:49:54 -08:00
Meredith Lancaster 9ca26d4946 regenerate package lock
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 13:17:18 -08:00
Meredith Lancaster 417dbfff73 use parameter objects and add tests
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 13:17:08 -08:00
Salman Muin Kayser Chishti 8883833d6d chore: fix npm audit vulnerabilities (glob, js-yaml) 2025-12-08 21:14:00 +00:00
Meredith Lancaster 79efd648ac condense parameters
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 11:02:59 -08:00
Meredith Lancaster e8c242695d add function for creating storage record
Signed-off-by: Meredith Lancaster <malancas@github.com>
2025-12-08 10:49:24 -08:00
Salman Chishti 48f166f6d5 Merge pull request #2175 from actions/prepare-core-1.12.0-release
Prepare @actions/core 2.0.0 release
2025-12-08 16:28:47 +00:00
Salman Muin Kayser Chishti 4bc377e1b4 Timeout time back to normal, as it was like this for debugging 2025-12-08 12:47:19 +00:00
Salman Muin Kayser Chishti bf1b64008f fix: use detached:true on all platforms for exec stream tests
On Windows, detached:true is needed to properly keep stdio handles
open after the parent process exits.
2025-12-08 12:35:33 +00:00
Salman Muin Kayser Chishti 894f77901e fix: improve exec stream tests cross-platform handling
- Update spawn-wait-for-file.js to use proper stdio inheritance
- Add small delay before exit to ensure child process inherits handles
- Simplify test code to use the helper script instead of shell commands
2025-12-08 12:28:52 +00:00
Tingting Wang 7993066184 Merge pull request #2174 from actions/dependabot/npm_and_yarn/packages/attest/tar-7.5.2
Bump tar from 7.5.1 to 7.5.2 in /packages/attest
2025-12-05 17:13:09 -08:00
dependabot[bot] f014075da9 Bump tar from 7.5.1 to 7.5.2 in /packages/attest
Bumps [tar](https://github.com/isaacs/node-tar) from 7.5.1 to 7.5.2.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v7.5.1...v7.5.2)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 7.5.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-06 01:06:03 +00:00
Tingting Wang 4500de75c1 Merge pull request #2187 from actions/dependabot/npm_and_yarn/packages/attest/glob-10.5.0
Bump glob from 10.4.5 to 10.5.0 in /packages/attest
2025-12-05 17:04:14 -08:00
dependabot[bot] 47017fa24b Bump glob from 10.4.5 to 10.5.0 in /packages/attest
Bumps [glob](https://github.com/isaacs/node-glob) from 10.4.5 to 10.5.0.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.4.5...v10.5.0)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 10.5.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-19 17:36:35 +00:00
Salman Muin Kayser Chishti d97deb1f60 tests(exec): use platform-aware spawn options in spawn-wait-for-file script (detach on Unix, hide window on Windows) 2025-11-18 15:45:59 +00:00
Salman Muin Kayser Chishti df111e1104 tests(glob): set GITHUB_WORKSPACE to __dirname in hash-files.test 2025-11-18 15:32:19 +00:00
Salman Muin Kayser Chishti a3588a70ba update hash files test 2025-11-18 14:21:47 +00:00
Salman Chishti 6b63a2bfc3 Merge pull request #2176 from actions/prepare-exec-2.0.0-release
Prepare @actions/exec 2.0.0 release
2025-11-18 14:13:24 +00:00
Salman Muin Kayser Chishti 290017ff81 update package json 2025-11-04 13:53:28 +00:00
Salman Chishti 2a876cd69d Update packages/exec/RELEASES.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-04 13:50:24 +00:00
Salman Muin Kayser Chishti 7cba4c8084 npm install 2025-10-31 16:02:54 +00:00
Salman Muin Kayser Chishti f79b906406 Prepare @actions/exec 2.0.0 release 2025-10-31 15:55:29 +00:00
Salman Muin Kayser Chishti 1bcc453b44 Prepare @actions/core 2.0.0 release 2025-10-31 15:52:21 +00:00
Salman Chishti dcae869a03 Merge pull request #2167 from actions/prepare-http-client-3.0.0-release
Prepare @actions/http-client 3.0.0 release
2025-10-31 15:27:38 +00:00
Salman Chishti 23769d04c7 Merge pull request #2166 from actions/prepare-io-2.0.0-release
Prepare @actions/io 2.0.0 release
2025-10-31 15:27:26 +00:00
Daniel Kennedy d3ab50471b Merge pull request #2168 from actions/danwkennedy/prepare-4.0.0
Artifact: prepare `v4.0.0`
2025-10-24 13:38:36 -04:00
Daniel Kennedy 1388fd1cac Artifact: prepare 4.0.0 2025-10-24 13:28:26 -04:00
Bassem Dghaidi 5b446d2657 Merge pull request #2165 from austenstone/max-list-artifact-2k
fix: artifact pagination bugs and configurable artifact count limits
2025-10-24 17:23:44 +02:00
Austen Stone 006d6978c1 linting 2025-10-22 11:44:21 -04:00
Austen Stone 02afeb1577 style: wrap ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT env var assignment for readability 2025-10-22 11:42:01 -04:00
Salman Chishti d47594b536 Update packages/http-client/RELEASES.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-22 15:44:09 +01:00
Salman Muin Kayser Chishti 2823824b94 Prepare @actions/http-client 3.0.0 release 2025-10-22 15:40:17 +01:00
Austen Stone cbc06d6766 fix: ensure max artifact count variable is treated as a string 2025-10-22 07:47:51 -04:00
Austen Stone 9bb6708527 fix: remove redundant check for max artifact count variable 2025-10-22 07:41:31 -04:00
Austen Stone be1151df02 Apply suggestion from @Link-
Co-authored-by: Bassem Dghaidi <568794+Link-@users.noreply.github.com>
2025-10-22 07:39:45 -04:00
Salman Muin Kayser Chishti 130842f4e8 Prepare @actions/io 2.0.0 release 2025-10-21 15:55:10 +01:00
Salman Chishti ab82301c62 Merge pull request #2164 from actions/prepare-attest-2.0.0-release
Prepare @actions/attest 2.0.0 release
2025-10-21 15:08:28 +01:00
Austen Stone fea4f6b5c5 fix: resolve critical pagination bugs and add comprehensive testing
- Fix off-by-one error in pagination loop (< to <=) that prevented fetching last page
- Add Math.ceil() to maxNumberOfPages calculation for proper limit handling
- Replace hardcoded 2000 limit with configurable getMaxArtifactListCount()
- Add pagination test for multi-page artifact listing
- Add environment variable test for ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT
- Add comprehensive test coverage for getMaxArtifactListCount() function

Fixes compound bug where pagination and limit logic capped results at 900 artifacts instead of intended 1000.
2025-10-21 09:22:11 -04:00
Salman Muin Kayser Chishti d3ade9ecfc Prepare @actions/attest 2.0.0 release 2025-10-20 12:07:20 +01:00
functionstackx fb592eec03 Update packages/artifact/src/internal/find/list-artifacts.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-17 18:05:06 -04:00
functionstackx 70e79399a2 fix: bumping max list artifact to 2k 2025-10-16 23:13:34 -04:00
Eugene acb230b99a Merge pull request #2160 from actions/ejahnGithub-patch-1
Remove unnecessary Buffer to Uint8Array conversion
2025-10-16 12:22:23 -04:00
Eugene 5e0fa1aaaa Remove unnecessary Buffer to Uint8Array conversion
Removed unnecessary conversion of Buffer to Uint8Array for compatibility.
2025-10-16 12:08:05 -04:00
Salman Chishti ac2468e605 Support Nodejs.24 - Merge pull request #2110 from actions/salmanmkc/node24
Support Node.js 24
2025-10-16 16:25:47 +01:00
Salman Muin Kayser Chishti 3c8fcfce19 del file 2025-10-16 14:37:05 +01:00
Salman Muin Kayser Chishti 45467b9199 LInt 2025-10-16 14:34:09 +01:00
Salman Muin Kayser Chishti 700a55077d spacing 2025-10-16 14:27:39 +01:00
Salman Muin Kayser Chishti 6fa8f07827 Update based on testing to add trailing back slash to all results 2025-10-16 13:52:43 +01:00
Salman Muin Kayser Chishti d16e86a709 Add workflow to test readlink behavior on Windows across Node versions 2025-10-16 13:03:06 +01:00
Salman Muin Kayser Chishti ae3ac0db0c change back to lstat 2025-10-15 17:25:34 +01:00
Salman Muin Kayser Chishti b319d6afff Add comment to explain the method and return types 2025-10-15 17:14:54 +01:00
Salman Muin Kayser Chishti b8ac8fc14a lint 2025-10-15 17:08:33 +01:00
Salman Muin Kayser Chishti 028d621193 Merge remote-tracking branch 'origin/main' into salmanmkc/node24 2025-10-15 16:41:54 +01:00
Salman Muin Kayser Chishti b0d901f9c2 rebase led to this changing so reverting 2025-10-15 16:37:38 +01:00
Salman Muin Kayser Chishti 394e804dc8 remove skip lib check 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti d402248c45 Lint fix 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti 66e8437b3e Revert "Io util package usage update"
This reverts commit 783332a4b57e9455ec3a361c4e16f659a35f3a97.
2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti 9c7501a5f3 Io util package usage update 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti 3b4b5725f0 Update packages, core doesn't need updates and update to use IO util update 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti 9a364e607b update io utils 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti 625c3f4856 change version for http-client 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti 1c3a637017 Update documentation 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti ec0ca1b19b fix typo 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti 57cd003e61 Update tests to use HTTPS for postman-echo.com and adjust proxy environment variable 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti b5befc6c6d Update HTTP tests to use HTTPS for postman-echo.com 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti 88a490d2ce override for node-fetch 2025-10-15 16:28:21 +01:00
Salman Muin Kayser Chishti a8d1fb0687 remove node 18 2025-10-15 16:27:29 +01:00
Salman Muin Kayser Chishti 347c887e54 package json 2025-10-15 16:27:29 +01:00
Salman Muin Kayser Chishti d5af54ee78 Update package versions 2025-10-15 16:27:29 +01:00
Salman Muin Kayser Chishti 44b9401378 Remove the need to update packages/core 2025-10-15 16:27:29 +01:00
Salman Muin Kayser Chishti fb5ae2a0e0 Keep attest at the same version 2025-10-15 16:27:29 +01:00
Salman Muin Kayser Chishti 8024983ab0 Update workflows and documentation to use the latest versions of first party actions that are available (checkout, setup-node, github-script)
=
2025-10-15 16:27:29 +01:00
Salman Muin Kayser Chishti d44f9b8f13 update some version numbers, will revise in a bit 2025-10-15 16:27:29 +01:00
Daniel Kennedy 9b4ee219ef fix: only mock the cpus() function on the os module instead of the whole module 2025-10-15 16:26:39 +01:00
Daniel Kennedy ee5d8970ad Take a direct dependency on @azure/core-http 2025-10-15 16:26:39 +01:00
dependabot[bot] 2874e3a741 Bump the artifact-minor-patch group in /packages/artifact with 5 updates
Bumps the artifact-minor-patch group in /packages/artifact with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) | `1.10.0` | `1.11.1` |
| [@azure/storage-blob](https://github.com/Azure/azure-sdk-for-js) | `12.15.0` | `12.28.0` |
| [@protobuf-ts/plugin](https://github.com/timostamm/protobuf-ts/tree/HEAD/packages/plugin) | `2.9.1` | `2.11.1` |
| [typedoc](https://github.com/TypeStrong/TypeDoc) | `0.25.4` | `0.28.13` |
| [typescript](https://github.com/microsoft/TypeScript) | `5.2.2` | `5.9.2` |

Updates `@actions/core` from 1.10.0 to 1.11.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Updates `@azure/storage-blob` from 12.15.0 to 12.28.0
- [Release notes](https://github.com/Azure/azure-sdk-for-js/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Changelog-for-next-generation.md)
- [Commits](https://github.com/Azure/azure-sdk-for-js/compare/@azure/storage-blob_12.15.0...@azure/storage-blob_12.28.0)

Updates `@protobuf-ts/plugin` from 2.9.1 to 2.11.1
- [Release notes](https://github.com/timostamm/protobuf-ts/releases)
- [Commits](https://github.com/timostamm/protobuf-ts/commits/v2.11.1/packages/plugin)

Updates `typedoc` from 0.25.4 to 0.28.13
- [Release notes](https://github.com/TypeStrong/TypeDoc/releases)
- [Changelog](https://github.com/TypeStrong/typedoc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.25.4...v0.28.13)

Updates `typescript` from 5.2.2 to 5.9.2
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.2.2...v5.9.2)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-version: 1.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
- dependency-name: "@azure/storage-blob"
  dependency-version: 12.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
- dependency-name: "@protobuf-ts/plugin"
  dependency-version: 2.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
- dependency-name: typedoc
  dependency-version: 0.28.13
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
- dependency-name: typescript
  dependency-version: 5.9.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-15 16:25:54 +01:00
Daniel Kennedy ad4afeeff1 Update the group names 2025-10-15 16:24:47 +01:00
Daniel Kennedy f9bdf6a054 Dependabot: add support for /packages/artifact and `/packages/cache 2025-10-15 16:24:47 +01:00
Bassem Dghaidi 59c7ebde79 Prepapre cache v4.1.0 release 2025-10-15 16:24:47 +01:00
Ryan Ghadimi 0c907a43d3 no need to resolve 2025-10-15 16:23:02 +01:00
Ryan Ghadimi d1c1fc4108 lint 2025-10-15 16:23:02 +01:00
Ryan Ghadimi 36f30e6d37 new error state, tests to cover 2025-10-15 16:23:02 +01:00
Ryan Ghadimi 308e05bc50 remove cache size limit 2025-10-15 16:23:02 +01:00
Salman Muin Kayser Chishti 33a9b6c09c update with dist updates 2025-10-15 16:22:51 +01:00
Daniel Kennedy ddc5fa4ae8 Merge pull request #2133 from actions/danwkennedy/test-blob-stream-timeout
Test: add a timeout test for downloading chunks from the stream
2025-09-25 10:54:19 -04:00
Daniel Kennedy 9b08f07cd3 Fix linting 2025-09-25 09:26:13 -04:00
Daniel Kennedy d26e9423f4 Test: add a timeout test for downloading chunks from the stream 2025-09-25 09:11:38 -04:00
Daniel Kennedy 714f93aedc Merge pull request #2124 from akashchi/reject-on-download-failure
[ARTIFACT] Reject download promise if timeout was reached
2025-09-25 09:06:20 -04:00
Andrei Kashchikhin 844423665b lint 2025-09-25 10:53:34 +02:00
Daniel Kennedy f2ba502b92 Merge pull request #2136 from actions/dependabot/npm_and_yarn/packages/artifact/artifact-minor-patch-612b72ffd4
Bump the artifact-minor-patch group in /packages/artifact with 5 updates
2025-09-24 20:21:48 -04:00
Daniel Kennedy 1db3130eb3 fix: only mock the cpus() function on the os module instead of the whole module 2025-09-24 20:01:53 -04:00
Daniel Kennedy ca8a35d78f Take a direct dependency on @azure/core-http 2025-09-24 16:46:53 -04:00
dependabot[bot] f7f057193f Bump the artifact-minor-patch group in /packages/artifact with 5 updates
Bumps the artifact-minor-patch group in /packages/artifact with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) | `1.10.0` | `1.11.1` |
| [@azure/storage-blob](https://github.com/Azure/azure-sdk-for-js) | `12.15.0` | `12.28.0` |
| [@protobuf-ts/plugin](https://github.com/timostamm/protobuf-ts/tree/HEAD/packages/plugin) | `2.9.1` | `2.11.1` |
| [typedoc](https://github.com/TypeStrong/TypeDoc) | `0.25.4` | `0.28.13` |
| [typescript](https://github.com/microsoft/TypeScript) | `5.2.2` | `5.9.2` |


Updates `@actions/core` from 1.10.0 to 1.11.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Updates `@azure/storage-blob` from 12.15.0 to 12.28.0
- [Release notes](https://github.com/Azure/azure-sdk-for-js/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Changelog-for-next-generation.md)
- [Commits](https://github.com/Azure/azure-sdk-for-js/compare/@azure/storage-blob_12.15.0...@azure/storage-blob_12.28.0)

Updates `@protobuf-ts/plugin` from 2.9.1 to 2.11.1
- [Release notes](https://github.com/timostamm/protobuf-ts/releases)
- [Commits](https://github.com/timostamm/protobuf-ts/commits/v2.11.1/packages/plugin)

Updates `typedoc` from 0.25.4 to 0.28.13
- [Release notes](https://github.com/TypeStrong/TypeDoc/releases)
- [Changelog](https://github.com/TypeStrong/typedoc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/TypeDoc/compare/v0.25.4...v0.28.13)

Updates `typescript` from 5.2.2 to 5.9.2
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.2.2...v5.9.2)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-version: 1.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
- dependency-name: "@azure/storage-blob"
  dependency-version: 12.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
- dependency-name: "@protobuf-ts/plugin"
  dependency-version: 2.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
- dependency-name: typedoc
  dependency-version: 0.28.13
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
- dependency-name: typescript
  dependency-version: 5.9.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: artifact-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-24 20:07:58 +00:00
Daniel Kennedy 8e146e124e Merge pull request #2134 from actions/danwkennedy/dependabot-artifact-cache
Dependabot: add support for `/packages/artifact` and `/packages/cache`
2025-09-24 16:06:29 -04:00
Daniel Kennedy 1ea77a84d7 Update the group names 2025-09-24 16:04:16 -04:00
Daniel Kennedy 7da95b182e Dependabot: add support for /packages/artifact and `/packages/cache 2025-09-24 16:00:44 -04:00
Andrei Kashchikhin 7c689a5156 use error in both reject and destroy 2025-09-24 17:05:25 +02:00
Andrei Kashchikhin 8c6c662cda Merge remote-tracking branch 'upstream/main' into reject-on-download-failure 2025-09-24 17:03:35 +02:00
Bassem Dghaidi 3898ed70c4 Merge pull request #2132 from actions/Link-/cache-4.1.0
Prepare cache `v4.1.0` release
2025-09-24 14:35:45 +02:00
Bassem Dghaidi 9a41b33065 Prepapre cache v4.1.0 release 2025-09-24 05:23:58 -07:00
Salman Muin Kayser Chishti 7aea3e735f changes 2025-09-08 15:37:51 +01:00
Salman Muin Kayser Chishti b1eb18b224 http 2025-09-08 15:36:39 +01:00
Salman Muin Kayser Chishti 48e42b1fdd linting 2025-09-04 15:24:57 +01:00
Salman Muin Kayser Chishti b738f10ef3 package updates 2025-09-04 15:15:02 +01:00
Salman Muin Kayser Chishti 8f32f385e0 Bump package versions, and fix issues 2025-09-04 14:16:27 +01:00
Salman Muin Kayser Chishti 011f07d1dc package changes 2025-09-04 12:58:54 +01:00
Salman Muin Kayser Chishti aa7077acfb Override to fix npm audit stuff 2025-09-04 12:49:31 +01:00
Salman Muin Kayser Chishti 86207b5042 remove engines 24 reuqirement from toolkit and fix test 2025-09-04 12:41:43 +01:00
Andrei Kashchikhin 523ce8ccda add reject 2025-09-01 11:52:11 +02:00
Ryan Ghadimi f58042f9cc Merge pull request #2118 from actions/ghadimir/cache_size_restriction
Remove 10GB Cache Size Limit for Cache Service V2
2025-08-21 15:14:38 +01:00
Ryan Ghadimi 091616a0b8 no need to resolve 2025-08-13 13:38:51 +00:00
Ryan Ghadimi 8da1e670b6 lint 2025-08-13 13:37:36 +00:00
Ryan Ghadimi 06f7fd9df1 new error state, tests to cover 2025-08-13 13:00:46 +00:00
Ryan Ghadimi 0fe20e9d56 remove cache size limit 2025-08-13 10:14:18 +00:00
Salman Muin Kayser Chishti f82db4c00b audit fix 2025-08-08 12:26:34 +01:00
Salman Muin Kayser Chishti b8cca0c71f fix lint errors 2025-08-08 04:02:29 +01:00
Salman Muin Kayser Chishti 6f0cb0c45e Merge branch 'main' into salmanmkc/node24 2025-08-08 03:54:30 +01:00
Salman Muin Kayser Chishti 944ede4d09 custom readlink implementation for Windows compatibility with trailing backslashes 2025-08-08 03:46:42 +01:00
Bassem Dghaidi 227b1ce741 Merge pull request #2115 from actions/Link-/release-4.0.5
Prepare release `4.0.5`
2025-08-07 13:08:34 +02:00
Bassem Dghaidi 447ee85f36 Prepare release 4.0.5 2025-08-07 04:00:47 -07:00
Bassem Dghaidi a6be3de743 Merge pull request #2114 from actions/Link-/fix-cache-tests
Update cache package compilation step to only install runtime dependencies
2025-08-07 12:58:32 +02:00
Bassem Dghaidi 26b94036cb Merge branch 'Link-/fix-cache-tests' of github.com:actions/toolkit into Link-/fix-cache-tests 2025-08-07 03:50:04 -07:00
Bassem Dghaidi f3e6fb165e Fix linter complaints 2025-08-07 03:49:51 -07:00
Bassem Dghaidi 3a607d0f00 Update .github/workflows/cache-tests.yml
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-07 12:40:20 +02:00
Bassem Dghaidi c9316bb4a7 Update cache package compilation step to install only runtime dependencies 2025-08-07 03:38:19 -07:00
Bassem Dghaidi ec43e5810d Merge pull request #2113 from actions/Link-/fix-runtime-deps
Reintroduce `@protobuf-ts/runtime-rpc` as a runtime dependency
2025-08-07 12:31:57 +02:00
Bassem Dghaidi 01715621b0 Replace @protobuf-ts/runtime with higher level dep @protobuf-ts/runtime-rpc 2025-08-07 03:21:24 -07:00
Bassem Dghaidi 6c64260c6d Reintroduce @protobuf-ts/runtime as a runtime dependency v2.11.1 2025-08-07 03:15:33 -07:00
Bassem Dghaidi bf3fc9226a Merge pull request #2111 from actions/Link-/cache-4.0.4
Prepare `@actions/cache` 4.0.4
2025-08-06 21:11:24 +02:00
Bassem Dghaidi c6723084aa Prepare release 4.0.4 2025-08-06 11:37:53 -07:00
Salman Muin Kayser Chishti bcb928642f format 2025-08-06 12:57:10 +01:00
Salman Muin Kayser Chishti 8c3fc9ed99 Update test to use mock 2025-08-06 12:49:50 +01:00
Salman Muin Kayser Chishti 1ef3214cee update for types 2025-08-01 11:50:25 +01:00
Salman Muin Kayser Chishti ece2273b24 updates 2025-07-31 23:48:44 +01:00
Salman Muin Kayser Chishti 717b895584 support node 24 2025-07-31 23:37:22 +01:00
Bassem Dghaidi 8ff772deb1 Merge pull request #2106 from actions/Link-/optimise-cache-deps
Move `@protobuf-ts/plugin` to dev dependencies
2025-07-31 14:03:14 +02:00
Bassem Dghaidi 8a3652e16d Optimise cache dependencies 2025-07-31 04:20:29 -07:00
Salman Chishti eb6226501b Merge pull request #2076 from esainane/what-the-word-is-is
Fix typo in `core/README.md`
2025-07-31 12:03:28 +01:00
Bassem Dghaidi d65ee66d9b Move @protobuf-ts/plugin to dev dependencies 2025-07-28 07:58:41 -07:00
Bassem Dghaidi 6d3feab2bf Merge pull request #2100 from actions/copilot/fix-2099
Improve cache service availability determination and implement conditional error logging
2025-07-28 16:49:10 +02:00
Bassem Dghaidi 79e1d8bb74 Merge pull request #2101 from actions/Link-/clarify-cache-hit-log
Explicit logging of cache key and restore key matches
2025-07-18 15:20:45 +02:00
copilot-swe-agent[bot] a0907ed2e2 Remove .nx/ from .gitignore as requested
Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 13:49:49 +00:00
copilot-swe-agent[bot] bd54a2413a Fix v1 cache service to only check ACTIONS_CACHE_URL
Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 13:39:13 +00:00
copilot-swe-agent[bot] 89397db14b Restore server error test and confirm logCacheError function removal
Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 13:01:02 +00:00
copilot-swe-agent[bot] d48d6b62a4 Remove logCacheError function and implement inline 5xx error detection as requested
Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 12:42:37 +00:00
copilot-swe-agent[bot] bab3dcf7f3 Complete PR feedback implementation: all cache tests passing
Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 12:26:40 +00:00
copilot-swe-agent[bot] c51178a15e Implement 5xx server error detection and fix most cache tests
Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 12:23:44 +00:00
copilot-swe-agent[bot] bbc6082700 Add .nx/ to .gitignore to exclude build cache files 2025-07-14 12:09:41 +00:00
copilot-swe-agent[bot] cf3aaeb491 Update tests to expect warnings instead of errors for non-5xx cache failures
Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 12:07:37 +00:00
Bassem Dghaidi cf4886cccb Fix linting issues 2025-07-14 03:49:28 -07:00
Bassem Dghaidi 0c5da92b52 Fix logging of cache key and restore key matches 2025-07-14 03:45:17 -07:00
copilot-swe-agent[bot] 513216f1dd Fix tests to expect errors instead of warnings for cache failures
- Update restoreCacheV2.test.ts, restoreCache.test.ts, saveCacheV2.test.ts, and saveCache.test.ts
- Change test expectations from core.warning to core.error for cache operation failures
- All tests now pass successfully

Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 10:38:47 +00:00
copilot-swe-agent[bot] 3c90578c30 Improve cache service availability determination and change warnings to errors
- Update isFeatureAvailable() to leverage ACTIONS_CACHE_SERVICE_V2 feature flag
- For v2: check ACTIONS_RESULTS_URL availability
- For v1: check either ACTIONS_CACHE_URL or ACTIONS_RESULTS_URL availability
- Change warning logs to error logs for cache failures
- Add comprehensive tests covering all scenarios

Co-authored-by: Link- <568794+Link-@users.noreply.github.com>
2025-07-14 10:32:34 +00:00
copilot-swe-agent[bot] be5a2ce677 Initial plan 2025-07-14 10:19:51 +00:00
Ben De St Paer-Gotch 683703c114 Merge pull request #2086 from actions/nebuk89-patch-1
Update README.md
2025-06-16 10:06:48 +01:00
Ben De St Paer-Gotch c28e7d4d5f Update README.md
Co-authored-by: Remy Suen <remy.suen@gmail.com>
2025-06-12 10:28:03 +01:00
Ben De St Paer-Gotch 12e323ae30 Update README.md 2025-06-10 16:39:47 +01:00
Sai Nane dbb1ea35ff Fix typo in core/README.md 2025-05-27 04:27:17 +00:00
Brian DeHamer f31c2921c1 Merge pull request #2058 from actions/dependabot/npm_and_yarn/packages/attest/undici-5.29.0
Bump undici from 5.28.5 to 5.29.0 in /packages/attest
2025-05-25 16:30:11 -07:00
dependabot[bot] 41b3ce3141 Bump undici from 5.28.5 to 5.29.0 in /packages/attest
Bumps [undici](https://github.com/nodejs/undici) from 5.28.5 to 5.29.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.28.5...v5.29.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 5.29.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-15 16:30:57 +00:00
Josh Gross 8d8a914a94 Document context.runAttempt in @actions/github 6.0.1 (#2054) 2025-05-13 10:37:14 -04:00
Brian DeHamer 36db4d62ad Merge pull request #2045 from actions/dependabot/npm_and_yarn/packages/attest/octokit/endpoint-9.0.6
Bump @octokit/endpoint from 9.0.5 to 9.0.6 in /packages/attest
2025-05-08 10:47:59 -07:00
Brian DeHamer a25b686a45 Merge pull request #2044 from actions/dependabot/npm_and_yarn/packages/attest/octokit/request-error-5.1.1
Bump @octokit/request-error from 5.1.0 to 5.1.1 in /packages/attest
2025-05-08 10:47:20 -07:00
dependabot[bot] 957610a37a Bump @octokit/request-error from 5.1.0 to 5.1.1 in /packages/attest
Bumps [@octokit/request-error](https://github.com/octokit/request-error.js) from 5.1.0 to 5.1.1.
- [Release notes](https://github.com/octokit/request-error.js/releases)
- [Commits](https://github.com/octokit/request-error.js/compare/v5.1.0...v5.1.1)

---
updated-dependencies:
- dependency-name: "@octokit/request-error"
  dependency-version: 5.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-08 11:19:50 +00:00
dependabot[bot] 6ed621e7d1 Bump @octokit/endpoint from 9.0.5 to 9.0.6 in /packages/attest
Bumps [@octokit/endpoint](https://github.com/octokit/endpoint.js) from 9.0.5 to 9.0.6.
- [Release notes](https://github.com/octokit/endpoint.js/releases)
- [Commits](https://github.com/octokit/endpoint.js/compare/v9.0.5...v9.0.6)

---
updated-dependencies:
- dependency-name: "@octokit/endpoint"
  dependency-version: 9.0.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-08 11:19:48 +00:00
Ryan Ghadimi 8007c1c535 Merge pull request #2049 from actions/ghadimir/audit_fix
NPM audit fixes
2025-05-08 12:18:34 +01:00
Ryan Ghadimi 6444290c57 release prep 2025-05-08 08:53:55 +00:00
Ryan Ghadimi f32d6bc043 bump octokit core 2025-05-08 08:42:32 +00:00
Ryan Ghadimi 2e4ab87130 artifact deps 2025-05-08 08:38:48 +00:00
Ryan Ghadimi ef199a9ab0 Merge pull request #2043 from actions/ghadimir/audit_fix
NPM Audit Fixes
2025-05-07 15:58:29 +01:00
Ryan Ghadimi 917a43eb6e bump octokit methods 2025-05-07 11:17:56 +00:00
Ryan Ghadimi 07cac0a6b3 bump gh package ver 2025-05-07 11:12:29 +00:00
Ryan Ghadimi 2046ee6d6b gh package release prep 2025-05-07 11:08:28 +00:00
Ryan Ghadimi 2b476323c4 fix packages/gh deps 2025-05-07 11:05:00 +00:00
Ryan Ghadimi aebe304a19 Merge pull request #2041 from actions/ghadimir/fix_cache_tests
Fix cache tests
2025-05-07 09:53:32 +01:00
Ryan Ghadimi e8f276a715 alphabetically order them 2025-05-07 08:31:17 +00:00
Ryan Ghadimi d156bcaa78 maybe this works instead 2025-05-06 20:22:05 +00:00
Ryan Ghadimi 5ae4c5be28 don't need that maybe 2025-05-06 20:08:50 +00:00
Ryan Ghadimi d50f1ac1b9 change url 2025-05-06 20:02:27 +00:00
Ryan Ghadimi 87cb7035bb add env variable for cache tests 2025-05-06 19:50:44 +00:00
Alisson Tenório 1b1e81526b Update README.md (#1719) 2025-04-09 10:46:07 -04:00
Salman Chishti 525ebf0c50 Merge pull request #2004 from AbhiPrasad/patch-1
fix link in `@actions/artifact` `RELEASES.md`
2025-04-09 15:34:10 +01:00
Abhijeet Prasad 07341e11d8 fix link in @actions/artifact RELEASES.md 2025-03-26 11:22:14 -04:00
Salman Chishti 930c890727 Merge pull request #1995 from actions/salmanmkc/2-new-cache-artifacts-release
Prepare Cache v4.0.3 & Artifact v2.3.2 releases
2025-03-17 21:22:10 +00:00
Salman Chishti a410c4a9cf remove extra brace 2025-03-17 17:14:25 +00:00
Salman Chishti 10277d48ca Add update to release doc, as will include it in this release 2025-03-17 17:12:32 +00:00
JoannaaKL 857c61a9df Merge pull request #1994 from gitulisca-enterprise-cloud-testing/gitulisca/log-restore-request-version
Log cache version requested on debugging message
2025-03-17 17:58:16 +01:00
Salman Chishti c40bccc9c3 Use patch instead of minor 2025-03-17 14:08:42 +00:00
Salman Chishti ff4d4afef8 shared instead of secure 2025-03-17 12:48:56 +00:00
Salman Chishti 4d4bbebd6a update package-lock.json 2025-03-17 12:47:54 +00:00
Salman Chishti 261fcae498 change it to minor version instead of patch 2025-03-17 12:44:51 +00:00
Salman Chishti 4059d2af66 update versions for cache and artifact 2025-03-17 12:09:16 +00:00
Salman Chishti 2559a2ac8a Merge pull request #1982 from actions/salmanmkc/obfuscate-sas
Remove logging of any SAS tokens in Actions/Cache and Actions/Artifact
2025-03-17 11:47:29 +00:00
Art Leo 514314311c Log cache version requested 2025-03-15 10:13:43 +11:00
Salman Chishti 957d42e6c5 add encoding back with extra tests 2025-03-14 06:38:57 -07:00
Salman Chishti 39419dd8c3 don't need to url encode or set var 2025-03-14 06:21:41 -07:00
Salman Chishti d13e6311f1 fix tests 2025-03-14 04:28:22 -07:00
Salman Chishti 6876e2a664 update ts docs 2025-03-13 04:47:49 -07:00
Salman Chishti fc482662af PR feedback, back to simplified approach, no export on client as well 2025-03-13 04:23:45 -07:00
Salman Chishti abd9054c61 Log debug error when failing to decode 2025-03-12 08:14:01 -07:00
Ryan Ghadimi 253e837c4d Merge pull request #1991 from actions/ghadimir/hash_to_digest_upload
Change hash to digest for consistent terminology across runner logs
2025-03-12 12:26:25 +00:00
Salman Chishti 3ac34ffcb7 Mask different situations, malformed URL, encoded, decoded, raw signatures, nested parameters, and moved to a utility file 2025-03-12 03:17:35 -07:00
Ryan Ghadimi 56c5a39afb Update blob-upload.ts 2025-03-12 07:59:00 +00:00
Ryan Ghadimi 7ae578ddd1 Merge pull request #1987 from actions/ghadimir/digest_typo
Bump release version
2025-03-11 11:07:20 +00:00
Ryan Ghadimi b2d2270685 Bump package.json 2025-03-11 11:02:42 +00:00
Ryan Ghadimi 0d1d5c7687 Bump release version 2025-03-11 10:58:38 +00:00
Ryan Ghadimi 769bb0fea1 Merge pull request #1986 from actions/ghadimir/digest_typo
Fix comment on expectedHash
2025-03-11 10:57:05 +00:00
Ryan Ghadimi d7ddca4309 Fix comment on expectedHash 2025-03-11 10:52:19 +00:00
Ryan Ghadimi 8780507298 Merge pull request #1985 from actions/ghadimir/dropdown_releases
Dropdown for package when releasing
2025-03-10 15:42:45 +00:00
Ryan Ghadimi 790c56665a Update releases.yml 2025-03-10 15:33:38 +00:00
Ryan Ghadimi 9d8017eadb Merge pull request #1976 from actions/ghadimir/prep_artifact_release
Prepare for Artifact v2.3.0 release
2025-03-10 15:23:55 +00:00
Ryan Ghadimi 20fee3ea63 Update @actions/artifact version to 2.3.0 2025-03-10 15:12:36 +00:00
Ryan Ghadimi 7501423b6f Update RELEASES.md for version 2.3.0 2025-03-10 15:11:43 +00:00
Ryan Ghadimi d0cc3418ea Bump version to 2.3.0
Better semver
2025-03-10 15:11:18 +00:00
Salman Chishti 5007821c77 Remove clean script 2025-03-10 06:51:30 -07:00
Salman Chishti 47c4fa85df masks the whole URL, update tests 2025-03-10 06:47:52 -07:00
Salman Chishti 1cd2f8a538 Instead of using utility method in core lib, use method in both twirp clients 2025-03-07 06:01:25 -08:00
Ryan Ghadimi b85d4e6b38 Prepare for Artifact v2.2.3 release 2025-03-07 10:14:36 +00:00
Ryan Ghadimi dc22dc7cad Merge pull request #1975 from actions/ghadimir/update_call_to_list_artifacts
Compare Artifact Digests
2025-03-07 09:51:05 +00:00
Ryan Ghadimi 8c05dc87d8 Change info logs to debug logs 2025-03-07 09:38:33 +00:00
Salman Chishti 884aa17886 remove these changes 2025-03-06 14:31:21 -08:00
Salman Chishti 944e6b78db Add secret and signature masking for cache and artifact packages 2025-03-06 14:25:32 -08:00
JoannaaKL d70fb49aaa Merge pull request #1974 from actions/list-artifacts-fix
Dont skip pages
2025-03-06 09:35:57 +01:00
Ryan Ghadimi 3726c11433 Please the linter 2025-03-05 14:44:58 +00:00
Ryan Ghadimi 71b40f7024 nicer wording 2025-03-05 14:35:01 +00:00
Ryan Ghadimi 83e5e2517b Change some debug -> info for artifacts hash logging 2025-03-05 14:30:51 +00:00
Ryan Ghadimi d5c8a0fa27 Update proto artifact interface, retrieve artifact digests, return indicator of mismatch failure 2025-03-05 11:29:44 +00:00
JoannaaKL 780e24be34 Dont skip pages 2025-03-05 09:27:35 +00:00
Brian DeHamer ec9716b3cc Merge pull request #1969 from actions/bdehamer/workflow-ref
set workflow.ref provenance field from ref claim
2025-02-26 09:50:14 -08:00
Brian DeHamer 0bc338adab set workflow.ref provenance field from ref claim
Updates the `buildSLSAProvenancePredicate` function to populate the
`workflow.ref` field from the `ref` claim in the OIDC token.

Signed-off-by: Brian DeHamer <bdehamer@github.com>
2025-02-26 08:47:27 -08:00
Rob Herley 5378ea8eca Merge pull request #1968 from actions/robherley/cache/v4.0.2
cache: prep v4.0.2 release
2025-02-25 16:00:06 -05:00
Brian DeHamer b95b593ca5 Merge pull request #1957 from actions/bdehamer/update-undici
Bump undici to v5.28.5
2025-02-25 12:54:29 -08:00
Rob Herley 4fedf471b1 cache: prep v4.0.2 release 2025-02-25 15:03:37 -05:00
Rob Herley 1b9063ee0e Merge pull request #1966 from actions/robherley/wrap-create-cache-err
cache: wrap create failures in ReserveCacheError
2025-02-25 15:00:25 -05:00
Rob Herley d096588f08 cache: wrap create failures in ReserveCacheError 2025-02-25 12:49:08 -05:00
Yang Cao 662b9d91f5 Merge pull request #1963 from actions/yacaovsnc/release_2_2_2
Prepare artifact release 2.2.2
2025-02-20 16:29:30 -05:00
Yang Cao a62f530b6f Update package-lock.json 2025-02-20 21:20:28 +00:00
Yang Cao 2995cdf0a1 Prepare artifact release 2.2.2 2025-02-20 21:12:25 +00:00
Yang Cao f10f9c8217 Merge pull request #1962 from actions/yacaovsnc/set_default_concurrency_to_5
Default upload artifacts concurrency to 5
2025-02-20 13:56:30 -05:00
Yang Cao c26e6f3aba Default upload artifacts concurrency to 5 2025-02-20 17:03:29 +00:00
Rob Herley 2b08dc18f2 Merge pull request #1958 from actions/robherley/cache/v4.0.1
Update manifests & release notes for cache v4.0.1
2025-02-14 12:20:52 -05:00
Brian DeHamer 412108cd55 add undici to @actions/github dependencies
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2025-02-14 08:12:00 -08:00
Rob Herley 8fcec1fb58 update manifests & release notes for cache v4.0.1 2025-02-14 11:02:13 -05:00
Brian DeHamer 95e747361e bump undici to 5.28.5
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2025-02-14 08:02:10 -08:00
Rob Herley aad39a371f Merge pull request #1954 from actions/robherley/miss-msg
Cache miss as debug, not warning annotation
2025-02-14 10:58:45 -05:00
Rob Herley 7fe619c58c update mocks 2025-02-14 09:42:41 -05:00
Rob Herley e6fb8f1c5d cache miss as debug, not warning annotation 2025-02-14 09:28:01 -05:00
Rob Herley 6a942b304d Merge pull request #1947 from actions/robherley/rm-twirp-ts
Remove runtime dependency on `twirp-ts`
2025-02-14 09:14:17 -05:00
Ehsan Hosseini 340a6b15b5 update undici package to 5.25.5 (#1942) 2025-01-28 10:14:55 -05:00
Rob Herley e0c069db55 remove runtime dependency on twirp-ts 2025-01-27 17:52:55 +00:00
Josh Gross 1f7c2c79e0 [tool-cache] Update @actions/core and prepare 2.0.2 release (#1872)
* Update `@actions/core` and prepare 2.0.2 release

* Include these changes in the release notes
2025-01-15 15:57:09 -05:00
Yang Cao 5e8c25d1f5 Merge pull request #1929 from actions/yacaovsnc/release_artifact_2_2_1
Prep release packages/artifact v2.2.1
2025-01-09 09:21:32 -05:00
Yang Cao 3095d112ef Prep release packages/artifact v2.2.1 2025-01-08 21:11:59 +00:00
Yang Cao 16ef1448d7 Merge pull request #1928 from actions/yacaovsnc/artifact_upload_concurrency_and_timeout
Make both upload concurrency and timeout settings configurable with env variables.
2025-01-08 16:07:30 -05:00
Yang Cao e55409315f Rename the prefix to be more specific 2025-01-08 20:32:45 +00:00
Yang Cao d4385a64a7 Concurrency has a min of 1 2025-01-08 18:14:04 +00:00
Yang Cao ede05b95d7 Make concurrency change opt-in, but can only go lower 2025-01-08 18:11:38 +00:00
Yang Cao f3c12d5561 Set default concurrency to 10 and make timeout configurable 2025-01-08 16:19:09 +00:00
Josh Gross adb9c4a7f4 Remove more unused cache APIs (#1909) 2024-12-19 13:26:19 -05:00
Josh Gross 01f21badd5 Remove more unused cache APIs 2024-12-17 14:51:57 -05:00
Josh Gross 26f8f84a96 Remove unused cache API (#1907) 2024-12-17 14:04:05 -05:00
Brian DeHamer 433f76091b Merge pull request #1908 from actions/bdehamer/artifact-2.2.0
Prepare artifact release 2.2.0
2024-12-17 10:24:18 -08:00
Brian DeHamer 4426b4ea91 Prepare artifact release 2.2.0
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-12-17 10:05:45 -08:00
Brian DeHamer f522fdf89d Merge pull request #1896 from actions/bdehamer/artifact-digest
return artifact digest on upload
2024-12-17 10:01:16 -08:00
Brian DeHamer 1e0c16f0dc return artifact digest on upload
Signed-off-by: Brian DeHamer <bdehamer@github.com>
2024-12-06 14:27:46 -08:00
Bassem Dghaidi b7a00a3203 Merge pull request #1886 from actions/Link-/cache-4.0.0
Prepare `@actions/cache` `4.0.0` release
2024-12-04 20:09:19 +01:00
Bassem Dghaidi 0827eef58f Rerun CI 2024-12-04 10:53:00 -08:00
Bassem Dghaidi cd9197e9bd Add announcement link 2024-12-04 08:23:10 -08:00
Bassem Dghaidi 72447df44c Update deprecation notice 2024-12-04 05:33:47 -08:00
Bassem Dghaidi 59845ec372 Update deprecation notice 2024-12-04 05:30:50 -08:00
Bassem Dghaidi cb001af8a3 Update README to include deprecation notice 2024-12-03 02:52:39 -08:00
Bassem Dghaidi 4498687c5e Prepare @actions/cache 4.0.0 release 2024-12-03 02:40:00 -08:00
Bassem Dghaidi a10e209c8d Merge pull request #1882 from actions/enhance-blob-client
Enhance blob client resilience & performance
2024-12-02 20:48:46 +01:00
Bassem Dghaidi c02c929c56 Minor comment adjustments 2024-12-02 11:10:25 -08:00
Bassem Dghaidi c649df4b94 Minor comment adjustments 2024-12-02 10:55:33 -08:00
Bassem Dghaidi fb40492b6f Merge branch 'enhance-blob-client' of github.com:actions/toolkit into enhance-blob-client 2024-12-02 10:55:00 -08:00
Bassem Dghaidi 502e8ce651 Minor comment adjustments 2024-12-02 10:53:29 -08:00
Bassem Dghaidi 3f7df8ec5a Fix comments
Co-authored-by: Josh Gross <joshmgross@github.com>
2024-12-02 19:46:18 +01:00
Bassem Dghaidi b24632bd80 Fix comments
Co-authored-by: Josh Gross <joshmgross@github.com>
2024-12-02 19:46:11 +01:00
Bassem Dghaidi 792ec716de Tune upload options 2024-12-02 07:32:33 -08:00
Bassem Dghaidi 7ad18fd6bd Fix linter complaints 2024-12-02 04:24:17 -08:00
Bassem Dghaidi 87171e29ca Fix tests 2024-12-02 04:18:46 -08:00
Bassem Dghaidi a762876d6d Minor refactoring 2024-12-02 04:08:21 -08:00
Bassem Dghaidi d89855bb90 Fix upload progress bug 2024-12-02 03:55:57 -08:00
Bassem Dghaidi db1d01308c Troubleshoot 2024-12-02 03:35:20 -08:00
Bassem Dghaidi 4a272e9053 Troubleshoot 2024-12-02 03:08:05 -08:00
Bassem Dghaidi ee1c07d0aa Add error handling for failed uploads 2024-12-02 02:38:51 -08:00
Bassem Dghaidi c6f1224d30 Add progress tracking for blob uploads 2024-12-02 02:33:27 -08:00
Bassem Dghaidi 1d403c2fd8 Fix tests 2024-11-29 07:36:51 -08:00
Bassem Dghaidi 65892d5ffe Fine tune blob uploads 2024-11-29 07:09:05 -08:00
Bassem Dghaidi 8c5f6f2dc5 Force use of Azure for restoreCacheV2 2024-11-28 07:42:07 -08:00
Bassem Dghaidi 62f5f1885b Refactor saveCacheV2 to use saveCache from cacheHttpClient 2024-11-28 07:22:01 -08:00
Bassem Dghaidi eaf0083ee2 Respect download options for restore 2024-11-28 04:56:37 -08:00
Bassem Dghaidi c1fb081674 Linter fixes 2024-11-28 03:53:34 -08:00
Bassem Dghaidi df166709a3 Refactor cache upload functionality and improve test cases 2024-11-28 03:52:09 -08:00
Bassem Dghaidi c5a5de05f6 Delete download-cache 2024-11-28 03:36:32 -08:00
Bassem Dghaidi 3a128c88c3 Merge branch 'main' into enhance-blob-client 2024-11-27 08:25:51 -08:00
John Sudol 9cc30cb0d3 Add saveCacheV2 tests (#1879) 2024-11-27 09:30:36 -05:00
Bassem Dghaidi 35d87ab129 Refactor code formatting for consistency and readability 2024-11-27 05:58:22 -08:00
Bassem Dghaidi af3981c955 Update the useragent of the old http client to pass cache version 2024-11-27 05:50:01 -08:00
Bassem Dghaidi 27e5cf2514 Replace downloadCacheFile with downloadCacheStorageSDK 2024-11-27 04:51:21 -08:00
John Sudol b050504b2d Add test case for when the uploadFile fails on the blobclient 2024-11-27 01:45:46 +00:00
John Sudol 5d0a4af70a Remove unused mock 2024-11-26 23:33:19 +00:00
John Sudol 94f18eb26e Only mock the cacheUtil methods we need 2024-11-26 23:05:11 +00:00
John Sudol 208dbe2131 PR feedback 2024-11-26 16:36:12 +00:00
John Sudol 46174ed573 run prettier 2024-11-26 00:56:07 +00:00
John Sudol 1f087496ca Add debug message for uploadResponse 2024-11-26 00:43:37 +00:00
John Sudol 8f606682c2 Add saveCacheV2 tests 2024-11-26 00:23:42 +00:00
Bassem Dghaidi 928d3e806d Merge pull request #1876 from actions/add-restore-tests
Add `restoreCacheV2` tests
2024-11-25 21:35:31 +01:00
226 changed files with 15400 additions and 13791 deletions
+5
View File
@@ -57,3 +57,8 @@ This will ask you some questions about the new package. Start with `0.0.0` as th
```
3. Start developing 😄.
## Releasing Packages
For information on how packages are published to npm, including workflow inputs, dist-tags, and safety guards, see the [release documentation](../docs/release.md).
+27
View File
@@ -0,0 +1,27 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "npm"
directory: "/packages/artifact"
schedule:
interval: "daily"
groups:
# Group minor and patch updates together but keep major separate
artifact-minor-patch:
update-types:
- "minor"
- "patch"
- package-ecosystem: "npm"
directory: "/packages/cache"
schedule:
interval: "daily"
groups:
# Group minor and patch updates together but keep major separate
cache-minor-patch:
update-types:
- "minor"
- "patch"
+220 -14
View File
@@ -22,12 +22,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set Node.js 20.x
uses: actions/setup-node@v4
- name: Set Node.js 24.x
uses: actions/setup-node@v5
with:
node-version: 20.x
node-version: 24.x
# Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible
# without these to just compile the artifacts package
@@ -47,7 +47,7 @@ jobs:
echo -n 'hello from file 2' > artifact-path/second.txt
- name: Upload Artifacts
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const {default: artifact} = require('./packages/artifact/lib/artifact')
@@ -71,18 +71,151 @@ jobs:
} catch (err) {
console.log('Successfully blocked second artifact upload')
}
upload-single-file:
name: Upload Single File (no zip)
strategy:
matrix:
runs-on: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false
runs-on: ${{ matrix.runs-on }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set Node.js 24.x
uses: actions/setup-node@v5
with:
node-version: 24.x
- name: Install root npm packages
run: npm ci
- name: Compile artifact package
run: |
npm ci
npm run tsc
working-directory: packages/artifact
- name: Create file that will be uploaded
run: |
echo -n 'hello from single file upload' > single-file-${{ matrix.runs-on }}.txt
- name: Upload Single File Artifact (skipArchive)
uses: actions/github-script@v8
with:
script: |
const {default: artifact} = require('./packages/artifact/lib/artifact')
const artifactName = 'my-single-file-${{ matrix.runs-on }}'
console.log('artifactName: ' + artifactName)
const uploadResult = await artifact.uploadArtifact(
artifactName,
['single-file-${{ matrix.runs-on }}.txt'],
'./',
{skipArchive: true}
)
console.log(uploadResult)
const size = uploadResult.size
const id = uploadResult.id
if (!id) {
throw new Error('Artifact ID is missing from upload result')
}
console.log(`Successfully uploaded single file artifact ${id}`)
upload-html-file:
name: Upload HTML File (no zip)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set Node.js 24.x
uses: actions/setup-node@v5
with:
node-version: 24.x
- name: Install root npm packages
run: npm ci
- name: Compile artifact package
run: |
npm ci
npm run tsc
working-directory: packages/artifact
- name: Create HTML file
run: |
cat > report.html << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Artifact Upload Test Report</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; color: #24292f; }
h1 { border-bottom: 1px solid #d0d7de; padding-bottom: 8px; }
.success { color: #1a7f37; }
.info { background: #ddf4ff; border: 1px solid #54aeff; border-radius: 6px; padding: 12px 16px; margin: 16px 0; }
table { border-collapse: collapse; width: 100%; margin: 16px 0; }
th, td { border: 1px solid #d0d7de; padding: 8px 12px; text-align: left; }
th { background: #f6f8fa; }
</style>
</head>
<body>
<h1>Artifact Upload Test Report</h1>
<div class="info">
<strong>This HTML file was uploaded as a single un-zipped artifact.</strong>
If you can see this in the browser, the feature is working correctly!
</div>
<table>
<tr><th>Property</th><th>Value</th></tr>
<tr><td>Upload method</td><td><code>skipArchive: true</code></td></tr>
<tr><td>Content-Type</td><td><code>text/html</code></td></tr>
<tr><td>File</td><td><code>report.html</code></td></tr>
</table>
<p class="success">&#10004; Single file upload is working!</p>
</body>
</html>
EOF
- name: Upload HTML Artifact (skipArchive)
uses: actions/github-script@v8
with:
script: |
const {default: artifact} = require('./packages/artifact/lib/artifact')
const uploadResult = await artifact.uploadArtifact(
'test-report',
['report.html'],
'./',
{skipArchive: true}
)
console.log(uploadResult)
console.log(`Successfully uploaded HTML artifact ${uploadResult.id}`)
console.log('This artifact is intentionally kept for manual browser verification')
verify:
name: Verify and Delete
runs-on: ubuntu-latest
needs: [upload]
needs: [upload, upload-single-file, upload-html-file]
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set Node.js 20.x
uses: actions/setup-node@v4
- name: Set Node.js 24.x
uses: actions/setup-node@v5
with:
node-version: 20.x
node-version: 24.x
# Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible
# without these to just compile the artifacts package
@@ -96,7 +229,7 @@ jobs:
working-directory: packages/artifact
- name: List and Download Artifacts
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const {default: artifactClient} = require('./packages/artifact/lib/artifact')
@@ -164,8 +297,73 @@ jobs:
}
}
}
- name: Download and Verify Single File Artifacts
uses: actions/github-script@v8
with:
script: |
const {default: artifactClient} = require('./packages/artifact/lib/artifact')
const {readFile} = require('fs/promises')
const path = require('path')
const findBy = {
repositoryOwner: process.env.GITHUB_REPOSITORY.split('/')[0],
repositoryName: process.env.GITHUB_REPOSITORY.split('/')[1],
token: '${{ secrets.GITHUB_TOKEN }}',
workflowRunId: process.env.GITHUB_RUN_ID
}
const listResult = await artifactClient.listArtifacts({latest: true, findBy})
const expectedSingleFiles = [
'single-file-ubuntu-latest.txt',
'single-file-windows-latest.txt',
'single-file-macos-latest.txt'
]
// Single file artifacts are named after the file basename
const singleFileArtifacts = listResult.artifacts.filter(a =>
expectedSingleFiles.includes(a.name)
)
console.log('Found single file artifacts:', singleFileArtifacts.length)
if (singleFileArtifacts.length !== 3) {
console.log('Unexpected single file artifacts:', singleFileArtifacts)
throw new Error(
`Expected 3 single-file artifacts but found ${singleFileArtifacts.length}`
)
}
for (const artifact of singleFileArtifacts) {
const downloadDir = `single-file-download-${artifact.id}`
const {downloadPath} = await artifactClient.downloadArtifact(artifact.id, {
path: downloadDir,
findBy
})
console.log('Downloaded single file artifact to:', downloadPath)
const filePath = path.join(
process.env.GITHUB_WORKSPACE,
downloadPath,
artifact.name
)
console.log('Checking file:', filePath)
const content = await readFile(filePath, 'utf8')
if (content.trim() !== 'hello from single file upload') {
throw new Error(
`Expected single file to contain 'hello from single file upload' but found '${content}'`
)
}
console.log(`Successfully verified single file artifact ${artifact.id}`)
}
- name: Delete Artifacts
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const {default: artifactClient} = require('./packages/artifact/lib/artifact')
@@ -173,11 +371,19 @@ jobs:
const artifactsToDelete = [
'my-artifact-ubuntu-latest',
'my-artifact-windows-latest',
'my-artifact-macos-latest'
'my-artifact-macos-latest',
'single-file-ubuntu-latest.txt',
'single-file-windows-latest.txt',
'single-file-macos-latest.txt'
]
for (const artifactName of artifactsToDelete) {
const {id} = await artifactClient.deleteArtifact(artifactName)
try {
const {id} = await artifactClient.deleteArtifact(artifactName)
console.log(`Deleted artifact '${artifactName}' (ID: ${id})`)
} catch (err) {
console.log(`Could not delete artifact '${artifactName}': ${err.message}`)
}
}
const {artifacts} = await artifactClient.listArtifacts({latest: true})
+4 -4
View File
@@ -18,12 +18,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set Node.js 20.x
uses: actions/setup-node@v4
- name: Set Node.js 24.x
uses: actions/setup-node@v5
with:
node-version: 20.x
node-version: 24.x
- name: npm install
run: npm install
+10 -12
View File
@@ -22,12 +22,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set Node.js 20.x
uses: actions/setup-node@v4
- name: Set Node.js 24.x
uses: actions/setup-node@v5
with:
node-version: 20.x
node-version: 24.x
# In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the
# node context. This runs a local action that gets and sets the necessary env variables that are needed
@@ -39,9 +39,11 @@ jobs:
- name: Install root npm packages
run: npm ci
# We need to install only runtime dependencies (omit dev dependencies) to verify that what we're shipping is all
# that is needed
- name: Compile cache package
run: |
npm ci
npm ci --omit=dev
npm run tsc
working-directory: packages/cache
@@ -53,10 +55,8 @@ jobs:
shell: bash
run: packages/cache/__tests__/create-cache-files.sh ${{ runner.os }} ~/test-cache
# We're using node -e to call the functions directly available in the @actions/cache package
- name: Save cache using saveCache()
run: |
node -e "Promise.resolve(require('./packages/cache/lib/cache').saveCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))"
run: node packages/cache/__tests__/save-cache.mjs ${{ runner.os }} ${{ github.run_id }}
- name: Delete cache folders before restoring
shell: bash
@@ -65,8 +65,7 @@ jobs:
rm -rf ~/test-cache
- name: Restore cache using restoreCache() with http-client
run: |
node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}',[],{useAzureSdk: false}))"
run: node packages/cache/__tests__/restore-cache.mjs ${{ runner.os }} ${{ github.run_id }} false
- name: Verify cache restored with http-client
shell: bash
@@ -81,8 +80,7 @@ jobs:
rm -rf ~/test-cache
- name: Restore cache using restoreCache() with Azure SDK
run: |
node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))"
run: node packages/cache/__tests__/restore-cache.mjs ${{ runner.os }} ${{ github.run_id }} true
- name: Verify cache restored with Azure SDK
shell: bash
+7 -11
View File
@@ -17,16 +17,16 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v5
- shell: bash
run: |
rm "C:\Program Files\Git\usr\bin\tar.exe"
- name: Set Node.js 20.x
uses: actions/setup-node@v1
- name: Set Node.js 24.x
uses: actions/setup-node@v5
with:
node-version: 20.x
node-version: 24.x
# In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the
# node context. This runs a local action that gets and sets the necessary env variables that are needed
@@ -52,10 +52,8 @@ jobs:
shell: bash
run: packages/cache/__tests__/create-cache-files.sh ${{ runner.os }} ~/test-cache
# We're using node -e to call the functions directly available in the @actions/cache package
- name: Save cache using saveCache()
run: |
node -e "Promise.resolve(require('./packages/cache/lib/cache').saveCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))"
run: node packages/cache/__tests__/save-cache.mjs ${{ runner.os }} ${{ github.run_id }}
- name: Delete cache folders before restoring
shell: bash
@@ -64,8 +62,7 @@ jobs:
rm -rf ~/test-cache
- name: Restore cache using restoreCache() with http-client
run: |
node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}',[],{useAzureSdk: false}))"
run: node packages/cache/__tests__/restore-cache.mjs ${{ runner.os }} ${{ github.run_id }} false
- name: Verify cache restored with http-client
shell: bash
@@ -80,8 +77,7 @@ jobs:
rm -rf ~/test-cache
- name: Restore cache using restoreCache() with Azure SDK
run: |
node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))"
run: node packages/cache/__tests__/restore-cache.mjs ${{ runner.os }} ${{ github.run_id }} true
- name: Verify cache restored with Azure SDK
shell: bash
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
+60 -20
View File
@@ -1,29 +1,58 @@
name: Publish NPM
run-name: Publish NPM - ${{ github.event.inputs.package }}
run-name: Publish NPM - ${{ inputs.package }} from ${{ inputs.branch }}
on:
workflow_dispatch:
inputs:
package:
type: choice
required: true
description: 'core, artifact, cache, exec, github, glob, http-client, io, tool-cache, attest'
description: 'Which package to release'
options:
- artifact
- attest
- cache
- core
- exec
- github
- glob
- http-client
- io
- tool-cache
branch:
type: string
required: false
default: 'main'
description: 'Branch to release from'
npm-tag:
type: string
required: false
default: 'latest'
description: 'npm dist-tag for the release. Use "latest" for main branch releases. For non-main branches, use a non-semver tag like "v1-longlived". Semver values (e.g. "5.0.0") are not valid dist-tags and will be rejected by npm.'
test-all:
type: boolean
required: false
default: false
description: 'Run tests for all packages instead of only the package being published'
jobs:
test:
runs-on: macos-latest-large
runs-on: ubuntu-latest
steps:
- name: setup repo
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ inputs.branch }}
- name: verify package exists
run: ls packages/${{ github.event.inputs.package }}
- name: Set Node.js 20.x
uses: actions/setup-node@v4
- name: Set Node.js 24.x
uses: actions/setup-node@v6
with:
node-version: 20.x
node-version: 24.x
- name: npm install
run: npm install
@@ -35,20 +64,25 @@ jobs:
run: npm run build
- name: test
run: npm run test
run: |
if [ "${{ inputs.test-all }}" = "true" ]; then
npm run test
else
npm run test -- --testPathPattern="packages/${{ inputs.package }}"
fi
- name: pack
run: npm pack
working-directory: packages/${{ github.event.inputs.package }}
- name: upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ github.event.inputs.package }}
path: packages/${{ github.event.inputs.package }}/*.tgz
publish:
runs-on: macos-latest-large
runs-on: ubuntu-slim
needs: test
environment: npm-publish
permissions:
@@ -56,30 +90,36 @@ jobs:
id-token: write
steps:
- name: download artifact
uses: actions/download-artifact@v4
- name: Set Node.js 24.x
uses: actions/setup-node@v6
with:
name: ${{ github.event.inputs.package }}
node-version: 24.x
- name: setup authentication
run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
env:
NPM_TOKEN: ${{ secrets.TOKEN }}
- name: download artifact
uses: actions/download-artifact@v8
with:
name: ${{ inputs.package }}
- name: guard against publishing latest from non-main branch
if: inputs.branch != 'main' && inputs.npm-tag == 'latest'
run: |
echo "::error::Publishing with the 'latest' dist-tag from a non-main branch ('${{ inputs.branch }}') is not allowed. Use the package major version as the tag (e.g. v5)."
exit 1
- name: publish
run: npm publish --provenance *.tgz
run: npm publish --provenance --tag "${{ inputs.npm-tag }}" *.tgz
- name: notify slack on failure
if: failure()
run: |
curl -X POST -H 'Content-type: application/json' --data '{"text":":pb__failed: Failed to publish a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK
curl -X POST -H 'Content-type: application/json' --data '{"text":":pb__failed: Failed to publish a new version of ${{ inputs.package }} from ${{ inputs.branch }} (tag: ${{ inputs.npm-tag }})"}' $SLACK_WEBHOOK
env:
SLACK_WEBHOOK: ${{ secrets.SLACK }}
- name: notify slack on success
if: success()
run: |
curl -X POST -H 'Content-type: application/json' --data '{"text":":dance: Successfully published a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK
curl -X POST -H 'Content-type: application/json' --data '{"text":":dance: Successfully published a new version of ${{ inputs.package }} from ${{ inputs.branch }} (tag: ${{ inputs.npm-tag }})"}' $SLACK_WEBHOOK
env:
SLACK_WEBHOOK: ${{ secrets.SLACK }}
+5 -5
View File
@@ -18,19 +18,19 @@ jobs:
matrix:
runs-on: [ubuntu-latest, macos-latest-large, windows-latest]
# Node 18 is the current default Node version in hosted runners, so users may still use the toolkit with it when running tests (see https://github.com/actions/toolkit/issues/1841)
# Node 20 is the currently support Node version for actions - https://docs.github.com/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions#runsusing-for-javascript-actions
node-version: [18.x, 20.x]
# Node 20 is the currently supported stable Node version for actions - https://docs.github.com/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions#runsusing-for-javascript-actions
# Node 24 is the new version being added with support in actions runners
node-version: [20.x, 24.x]
fail-fast: false
runs-on: ${{ matrix.runs-on }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Node ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node-version }}
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
if: ${{ github.repository_owner == 'actions' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Update Octokit
working-directory: packages/github
run: |
+26 -12
View File
@@ -24,7 +24,7 @@ The GitHub Actions ToolKit provides a set of packages to make creating actions e
Provides functions for inputs, outputs, results, logging, secrets and variables. Read more [here](packages/core)
```bash
$ npm install @actions/core
npm install @actions/core
```
<br/>
@@ -33,7 +33,7 @@ $ npm install @actions/core
Provides functions to exec cli tools and process output. Read more [here](packages/exec)
```bash
$ npm install @actions/exec
npm install @actions/exec
```
<br/>
@@ -42,7 +42,7 @@ $ npm install @actions/exec
Provides functions to search for files matching glob patterns. Read more [here](packages/glob)
```bash
$ npm install @actions/glob
npm install @actions/glob
```
<br/>
@@ -51,7 +51,7 @@ $ npm install @actions/glob
A lightweight HTTP client optimized for building actions. Read more [here](packages/http-client)
```bash
$ npm install @actions/http-client
npm install @actions/http-client
```
<br/>
@@ -60,7 +60,7 @@ $ npm install @actions/http-client
Provides disk i/o functions like cp, mv, rmRF, which etc. Read more [here](packages/io)
```bash
$ npm install @actions/io
npm install @actions/io
```
<br/>
@@ -71,7 +71,7 @@ Provides functions for downloading and caching tools. e.g. setup-* actions. Rea
See @actions/cache for caching workflow dependencies.
```bash
$ npm install @actions/tool-cache
npm install @actions/tool-cache
```
<br/>
@@ -80,7 +80,7 @@ $ npm install @actions/tool-cache
Provides an Octokit client hydrated with the context that the current action is being run in. Read more [here](packages/github)
```bash
$ npm install @actions/github
npm install @actions/github
```
<br/>
@@ -89,7 +89,7 @@ $ npm install @actions/github
Provides functions to interact with actions artifacts. Read more [here](packages/artifact)
```bash
$ npm install @actions/artifact
npm install @actions/artifact
```
<br/>
@@ -98,7 +98,7 @@ $ npm install @actions/artifact
Provides functions to cache dependencies and build outputs to improve workflow execution time. Read more [here](packages/cache)
```bash
$ npm install @actions/cache
npm install @actions/cache
```
<br/>
@@ -107,7 +107,7 @@ $ npm install @actions/cache
Provides functions to write attestations for workflow artifacts. Read more [here](packages/attest)
```bash
$ npm install @actions/attest
npm install @actions/attest
```
<br/>
@@ -227,9 +227,23 @@ console.log(`We can even get context data, like the repo: ${context.repo.repo}`)
```
<br/>
## Contributing
## Note
We welcome contributions. See [how to contribute](.github/CONTRIBUTING.md).
Thank you for your interest in this GitHub repo, however, right now we are not taking contributions.
We continue to focus our resources on strategic areas that help our customers be successful while making developers' lives easier. While GitHub Actions remains a key part of this vision, we are allocating resources towards other areas of Actions and are not taking contributions to this repository at this time. The GitHub public roadmap is the best place to follow along for any updates on features were working on and what stage theyre in.
We are taking the following steps to better direct requests related to GitHub Actions, including:
1. We will be directing questions and support requests to our [Community Discussions area](https://github.com/orgs/community/discussions/categories/actions)
2. High Priority bugs can be reported through Community Discussions or you can report these to our support team https://support.github.com/contact/bug-report.
3. Security Issues should be handled as per our [security.md](SECURITY.md).
We will still provide security updates for this project and fix major breaking changes during this time.
You are welcome to still raise bugs in this repo.
## Code of Conduct
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
os: [ubuntu-16.04, windows-2019]
runs-on: ${{matrix.os}}
actions:
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
version: ${{matrix.node}}
- run: |
Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

+1 -1
View File
@@ -18,7 +18,7 @@ e.g. To use https://github.com/actions/setup-node, users will author:
```yaml
steps:
using: actions/setup-node@v4
using: actions/setup-node@v5
```
# Define Metadata
+82
View File
@@ -0,0 +1,82 @@
# Releasing Packages
Packages are published to npm via the **Publish NPM** workflow ([`.github/workflows/releases.yml`](../.github/workflows/releases.yml)). The workflow is triggered manually through `workflow_dispatch` from the GitHub Actions UI.
## How it works
The workflow has two jobs:
1. **test** — Checks out the specified branch, installs dependencies, bootstraps the monorepo, builds all packages, runs tests, then packs the target package into a `.tgz` archive and uploads it as a workflow artifact.
2. **publish** — Downloads the packed artifact and publishes it to npm with `--provenance` (OIDC-based). Sends a Slack notification on success or failure. Requires the `npm-publish` environment.
## Inputs
| Input | Type | Required | Default | Description |
|---|---|---|---|---|
| `package` | choice | **yes** | — | Which package to release. One of: `artifact`, `attest`, `cache`, `core`, `exec`, `github`, `glob`, `http-client`, `io`, `tool-cache`. |
| `branch` | string | no | `main` | The branch to check out and release from. |
| `npm-tag` | string | no | `latest` | The npm dist-tag to publish under. See [Dist-tags](#dist-tags) below. |
| `test-all` | boolean | no | `false` | When `false`, only tests for the selected package are run. Set to `true` to run the full test suite across all packages. |
## Dist-tags
npm dist-tags control which version users get when they `npm install @actions/<package>` (or `@<tag>`).
> **Important:** npm dist-tags **cannot** be valid semver strings. Values like `5.0.0` or `1.2.3` will be rejected by npm. Use a descriptive name instead.
- **`latest`** — The default tag. This is what users get with a plain `npm install`. Should only be used for releases from `main`.
- **Custom tags** (e.g. `v1-longlived`) — Used for releases from long-lived or experimental branches.
Examples of **valid** dist-tags: `latest`, `next`, `beta`, `v1-longlived`
Examples of **invalid** dist-tags: `5.0.0`, `1.2.3`, `6.0.0-rc.1` (these are semver and will be rejected)
| ![Screenshot showcasing the npm distribution tags](assets/npm-dist-tags.png) |
|---|
| npm distribution tags |
### Safety guard
The workflow **blocks** publishing with the `latest` dist-tag from any branch other than `main`. This prevents accidentally overwriting `latest` with a version from an older or experimental branch. If you're releasing from a non-main branch, use the package's major version as the tag (e.g. `v5`).
## Examples
### Standard release from main
Use default inputs — just pick the package:
| Input | Value |
|---|---|
| `package` | `core` |
| `branch` | `main` (default) |
| `npm-tag` | `latest` (default) |
| `test-all` | `false` (default) |
This publishes the version in `packages/core/package.json` on `main` as `@actions/core@latest`.
### Patch release from a long-lived branch
| Input | Value |
|---|---|
| `package` | `artifact` |
| `branch` | `releases/v5` |
| `npm-tag` | `v5` |
| `test-all` | `false` (default) |
This publishes the version in `packages/artifact/package.json` on the `releases/v5` branch under the `v5` dist-tag. The `latest` tag remains untouched.
### Release with full test suite
| Input | Value |
|---|---|
| `package` | `cache` |
| `branch` | `main` (default) |
| `npm-tag` | `latest` (default) |
| `test-all` | `true` |
Same as a standard release, but runs tests for all packages before publishing.
## Prerequisites
- You must have permission to trigger workflows on this repository.
- The `npm-publish` environment must be configured with npm credentials and OIDC.
- The `SLACK` secret must be set for Slack notifications to work.
+29 -2
View File
@@ -4,8 +4,35 @@ module.exports = {
roots: ['<rootDir>/packages'],
testEnvironment: 'node',
testMatch: ['**/__tests__/*.test.ts'],
transform: {
'^.+\\.ts$': 'ts-jest'
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
'^@actions/core$': '<rootDir>/packages/core/lib/core.js',
'^@actions/exec$': '<rootDir>/packages/exec/lib/exec.js',
'^@actions/io$': '<rootDir>/packages/io/lib/io.js',
'^@actions/io/lib/io-util$': '<rootDir>/packages/io/lib/io-util.js',
'^@actions/http-client$': '<rootDir>/packages/http-client/lib/index.js',
'^@actions/http-client/lib/auth$': '<rootDir>/packages/http-client/lib/auth.js',
'^@actions/http-client/lib/interfaces$': '<rootDir>/packages/http-client/lib/interfaces.js',
'^@actions/github$': '<rootDir>/packages/github/lib/github.js',
'^@actions/github/lib/utils$': '<rootDir>/packages/github/lib/utils.js',
'^@actions/glob$': '<rootDir>/packages/glob/lib/glob.js',
'^@actions/tool-cache$': '<rootDir>/packages/tool-cache/lib/tool-cache.js',
'^@actions/cache$': '<rootDir>/packages/cache/lib/cache.js',
'^@actions/attest$': '<rootDir>/packages/attest/lib/index.js'
},
transform: {
'^.+\\.(ts|js)$': ['ts-jest', {
diagnostics: {warnOnly: true},
tsconfig: {
allowJs: true,
esModuleInterop: true,
module: 'commonjs',
moduleResolution: 'node'
}
}]
},
transformIgnorePatterns: [
'/node_modules/(?!(@octokit|@actions/github|@actions/http-client|@actions/io|@actions/exec|@actions/core|@actions/glob|@actions/tool-cache|@actions/cache|@actions/attest|universal-user-agent|before-after-hook)/)'
],
verbose: true
}
+6694 -4922
View File
File diff suppressed because it is too large Load Diff
+16 -2
View File
@@ -1,6 +1,6 @@
{
"name": "root",
"private": true,
"private": true,
"scripts": {
"audit-all": "lerna run audit-moderate",
"bootstrap": "lerna exec -- npm install",
@@ -17,7 +17,7 @@
},
"devDependencies": {
"@types/jest": "^29.5.4",
"@types/node": "^20.5.7",
"@types/node": "^24.1.0",
"@types/signale": "^1.4.1",
"concurrently": "^6.1.0",
"eslint": "^8.0.1",
@@ -32,5 +32,19 @@
"prettier": "^3.0.0",
"ts-jest": "^29.1.1",
"typescript": "^5.2.2"
},
"overrides": {
"semver": "^7.6.0",
"tar": "^6.2.1",
"@octokit/plugin-paginate-rest": "^14.0.0",
"@octokit/request": "^10.0.7",
"@octokit/request-error": "^7.1.0",
"@octokit/core": "^7.0.6",
"tmp": "^0.2.4",
"@types/node": "^24.1.0",
"brace-expansion": "^2.0.2",
"form-data": "^4.0.4",
"uri-js": "npm:uri-js-replace@^1.0.1",
"node-fetch": "^3.3.2"
}
}
+1
View File
@@ -41,3 +41,4 @@ Any easy way to test changes for the official upload/download actions is to fork
1. In the locally cloned fork, link to your local toolkit changes: `npm link @actions/artifact`
2. Then, compile your changes with: `npm run release`. The local `dist/index.js` should be updated with your changes.
3. Commit and push to your fork, you can then test with a `uses:` in your workflow pointed at your fork.
4. The format for the above is `<username>/<repository-name>/@<ref>`, i.e. `me/myrepo/@HEAD`
-1
View File
@@ -4,7 +4,6 @@ Interact programmatically with [Actions Artifacts](https://docs.github.com/en/ac
This is the core library that powers the [`@actions/upload-artifact`](https://github.com/actions/upload-artifact) and [`@actions/download-artifact`](https://github.com/actions/download-artifact) actions.
- [`@actions/artifact`](#actionsartifact)
- [v2 - What's New](#v2---whats-new)
- [Improvements](#improvements)
+104 -36
View File
@@ -1,93 +1,161 @@
# @actions/artifact Releases
### 2.1.11
## 6.2.1
- Support the RFC 5987 `filename*` field in the `content-disposition` header. This allows us to correctly download files and artifacts with Chinese/Japanese/Korean (among other) characters in their name.
## 6.2.0
- Support uploading single un-archived files (not zipped). Direct uploads are only supported for artifacts version 7+ (based on the major version of `actions/upload-artifact`). Callers must pass the `skipArchive` option to `uploadArtifact`. Only single files can be uploaded at a time right now. Default behavior should remain unchanged if `skipArchive = false`. When `skipArchive = true`, the name of the file is used as the name of the artifact for consistency with the downloads: you upload `artifact.txt`, you download `artifact.txt`.
## 6.1.0
- Support downloading non-zip artifacts. Zipped artifacts will be decompressed automatically (with an optional override). Un-zipped artifacts will be downloaded as-is.
## 6.0.0
- **Breaking change**: Package is now ESM-only
- CommonJS consumers must use dynamic `import()` instead of `require()`
## 5.0.3
- Bump `@actions/http-client` to `3.0.2`
## 5.0.1
- Fix Node.js 24 punycode deprecation warning by updating `@azure/storage-blob` from `^12.15.0` to `^12.29.1` [#2211](https://github.com/actions/toolkit/pull/2211)
- Removed direct `@azure/core-http` dependency (now uses `@azure/core-rest-pipeline` via storage-blob)
## 5.0.0
- Dependency updates for Node.js 24 runtime support
- Update `@actions/core` to v2
- Update `@actions/http-client` to v3
## 4.0.0
- Add support for Node 24 [#2110](https://github.com/actions/toolkit/pull/2110)
- Fix: artifact pagination bugs and configurable artifact count limits [#2165](https://github.com/actions/toolkit/pull/2165)
- Fix: reject the promise on timeout [#2124](https://github.com/actions/toolkit/pull/2124)
- Update dependency versions
## 2.3.3
- Dependency updates [#2049](https://github.com/actions/toolkit/pull/2049)
## 2.3.2
- Added masking for Shared Access Signature (SAS) artifact URLs [#1982](https://github.com/actions/toolkit/pull/1982)
- Change hash to digest for consistent terminology across runner logs [#1991](https://github.com/actions/toolkit/pull/1991)
## 2.3.1
- Fix comment typo on expectedHash. [#1986](https://github.com/actions/toolkit/pull/1986)
## 2.3.0
- Allow ArtifactClient to perform digest comparisons, if supplied. [#1975](https://github.com/actions/toolkit/pull/1975)
## 2.2.2
- Default concurrency to 5 for uploading artifacts [#1962](https://github.com/actions/toolkit/pull/1962)
## 2.2.1
- Add `ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY` and `ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS` environment variables [#1928](https://github.com/actions/toolkit/pull/1928)
## 2.2.0
- Return artifact digest on upload [#1896](https://github.com/actions/toolkit/pull/1896)
## 2.1.11
- Fixed a bug with relative symlinks resolution [#1844](https://github.com/actions/toolkit/pull/1844)
- Use native `crypto` [#1815](https://github.com/actions/toolkit/pull/1815)
### 2.1.10
## 2.1.10
- Fixed a regression with symlinks not being automatically resolved [#1830](https://github.com/actions/toolkit/pull/1830)
- Fixed a regression with chunk timeout [#1786](https://github.com/actions/toolkit/pull/1786)
### 2.1.9
## 2.1.9
- Fixed artifact upload chunk timeout logic [#1774](https://github.com/actions/toolkit/pull/1774)
- Use lazy stream to prevent issues with open file limits [#1771](https://github.com/actions/toolkit/pull/1771)
### 2.1.8
## 2.1.8
- Allows `*.localhost` domains for hostname checks for local development.
### 2.1.7
## 2.1.7
- Update unzip-stream dependency and reverted to using `unzip.Extract()`
### 2.1.6
## 2.1.6
- Will retry on invalid request responses.
### 2.1.5
## 2.1.5
- Bumped `archiver` dependency to 7.0.1
### 2.1.4
## 2.1.4
- Adds info-level logging for zip extraction
### 2.1.3
## 2.1.3
- Fixes a bug in the extract logic updated in 2.1.2
### 2.1.2
## 2.1.2
- Updated the stream extract functionality to use `unzip.Parse()` instead of `unzip.Extract()` for greater control of unzipping artifacts
### 2.1.1
## 2.1.1
- Updated `isGhes` check to include `.ghe.com` and `.ghe.localhost` as accepted hosts
### 2.1.0
## 2.1.0
- Added `ArtifactClient#deleteArtifact` to delete artifacts by name [#1626](https://github.com/actions/toolkit/pull/1626)
- Update error messaging to be more useful [#1628](https://github.com/actions/toolkit/pull/1628)
### 2.0.1
## 2.0.1
- Patch to fix transient request timeouts https://github.com/actions/download-artifact/issues/249
- Patch to fix transient request timeouts <https://github.com/actions/download-artifact/issues/249>
### 2.0.0
## 2.0.0
- Major release. Supports new Artifact backend for improved speed, reliability and behavior.
- Numerous API changes, [some breaking](./README.md#breaking-changes).
- [Blog post with more info](https://github.blog/2024-02-12-get-started-with-v4-of-github-actions-artifacts/)
### 1.1.1
## 1.1.1
- Fixed a bug in Node16 where if an HTTP download finished too quickly (<1ms, e.g. when it's mocked) we attempt to delete a temp file that has not been created yet [#1278](https://github.com/actions/toolkit/pull/1278/commits/b9de68a590daf37c6747e38d3cb4f1dd2cfb791c)
### 1.1.0
## 1.1.0
- Add `x-actions-results-crc64` and `x-actions-results-md5` checksum headers on upload [#1063](https://github.com/actions/toolkit/pull/1063)
### 1.0.2
## 1.0.2
- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087)
### 1.0.1
## 1.0.1
- Update to v2.0.0 of `@actions/http-client`
### 1.0.0
## 1.0.0
- Update `lockfileVersion` to `v2` in `package-lock.json` [#1009](https://github.com/actions/toolkit/pull/1009)
### 0.6.1
## 0.6.1
- Fix for failing 0 byte file uploads on Windows [#962](https://github.com/actions/toolkit/pull/962)
### 0.6.0
## 0.6.0
- Support upload from named pipes [#748](https://github.com/actions/toolkit/pull/748)
- Fixes to percentage values being greater than 100% when downloading all artifacts [#889](https://github.com/actions/toolkit/pull/889)
@@ -96,49 +164,49 @@
- Faster upload speeds for certain types of large files by exempting gzip compression [#956](https://github.com/actions/toolkit/pull/956)
- More detailed logging when dealing with chunked uploads [#957](https://github.com/actions/toolkit/pull/957)
### 0.5.2
## 0.5.2
- Add HTTP 500 as a retryable status code for artifact upload and download.
### 0.5.1
## 0.5.1
- Bump @actions/http-client to version 1.0.11 to fix proxy related issues during artifact upload and download
### 0.5.0
## 0.5.0
- Improved retry-ability for all http calls during artifact upload and download if an error is encountered
### 0.4.2
## 0.4.2
- Improved retry-ability when a partial artifact download is encountered
### 0.4.1
## 0.4.1
- Update to latest @actions/core version
### 0.4.0
## 0.4.0
- Add option to specify custom retentions on artifacts
-
### 0.3.5
## 0.3.5
- Retry in the event of a 413 response
### 0.3.3
## 0.3.3
- Increase chunk size during upload from 4MB to 8MB
- Improve user-agent strings during API calls to help internally diagnose issues
### 0.3.2
## 0.3.2
- Fix to ensure readstreams get correctly reset in the event of a retry
### 0.3.1
## 0.3.1
- Fix to ensure temporary gzip files get correctly deleted during artifact upload
- Remove spaces as a forbidden character during upload
### 0.3.0
## 0.3.0
- Fixes to gzip decompression when downloading artifacts
- Support handling 429 response codes
@@ -147,13 +215,13 @@
- Clearer error message if storage quota has been reached
- Improved logging and output during artifact download
### 0.2.0
## 0.2.0
- Fixes to TCP connections not closing
- GZip file compression to speed up downloads
- Improved logging and output
- Extra documentation
### 0.1.0
## 0.1.0
- Initial release
@@ -1,10 +1,10 @@
import * as http from 'http'
import * as net from 'net'
import {HttpClient} from '@actions/http-client'
import * as config from '../src/internal/shared/config'
import {internalArtifactTwirpClient} from '../src/internal/shared/artifact-twirp-client'
import {noopLogs} from './common'
import {NetworkError, UsageError} from '../src/internal/shared/errors'
import * as config from '../src/internal/shared/config.js'
import {internalArtifactTwirpClient} from '../src/internal/shared/artifact-twirp-client.js'
import {noopLogs} from './common.js'
import {NetworkError, UsageError} from '../src/internal/shared/errors.js'
jest.mock('@actions/http-client')
+118 -1
View File
@@ -1,4 +1,14 @@
import * as config from '../src/internal/shared/config'
import * as config from '../src/internal/shared/config.js'
import os from 'os'
// Mock the `cpus()` function in the `os` module
jest.mock('os', () => {
const osActual = jest.requireActual('os')
return {
...osActual,
cpus: jest.fn()
}
})
beforeEach(() => {
jest.resetModules()
@@ -30,3 +40,110 @@ describe('isGhes', () => {
expect(config.isGhes()).toBe(true)
})
})
describe('uploadChunkTimeoutEnv', () => {
it('should return default 300000 when no env set', () => {
expect(config.getUploadChunkTimeout()).toBe(300000)
})
it('should return value set in ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS', () => {
process.env.ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS = '150000'
expect(config.getUploadChunkTimeout()).toBe(150000)
})
it('should throw if value set in ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS is invalid', () => {
process.env.ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS = 'abc'
expect(() => {
config.getUploadChunkTimeout()
}).toThrow()
})
})
describe('uploadConcurrencyEnv', () => {
it('Concurrency default to 5', () => {
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
expect(config.getConcurrency()).toBe(5)
})
it('Concurrency max out at 300 on systems with many CPUs', () => {
;(os.cpus as jest.Mock).mockReturnValue(new Array(32))
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '301'
expect(config.getConcurrency()).toBe(300)
})
it('Concurrency can be set to 32 when cpu num is <= 4', () => {
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '32'
expect(config.getConcurrency()).toBe(32)
})
it('Concurrency can be set 16 * num of cpu when cpu num is > 4', () => {
;(os.cpus as jest.Mock).mockReturnValue(new Array(6))
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '96'
expect(config.getConcurrency()).toBe(96)
})
it('Concurrency can be overridden by env var ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY', () => {
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '10'
expect(config.getConcurrency()).toBe(10)
})
it('should throw with invalid value of ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY', () => {
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = 'abc'
expect(() => {
config.getConcurrency()
}).toThrow()
})
it('should throw if ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY is < 1', () => {
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '0'
expect(() => {
config.getConcurrency()
}).toThrow()
})
})
describe('getMaxArtifactListCount', () => {
beforeEach(() => {
delete process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT
})
it('should return default 1000 when no env set', () => {
expect(config.getMaxArtifactListCount()).toBe(1000)
})
it('should return value set in ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT', () => {
process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT = '2000'
expect(config.getMaxArtifactListCount()).toBe(2000)
})
it('should throw if value set in ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT is invalid', () => {
process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT = 'abc'
expect(() => {
config.getMaxArtifactListCount()
}).toThrow(
'Invalid value set for ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT env variable'
)
})
it('should throw if ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT is < 1', () => {
process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT = '0'
expect(() => {
config.getMaxArtifactListCount()
}).toThrow(
'Invalid value set for ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT env variable'
)
})
it('should throw if ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT is negative', () => {
process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT = '-100'
expect(() => {
config.getMaxArtifactListCount()
}).toThrow(
'Invalid value set for ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT env variable'
)
})
})
@@ -4,11 +4,11 @@ import type {RequestInterface} from '@octokit/types'
import {
deleteArtifactInternal,
deleteArtifactPublic
} from '../src/internal/delete/delete-artifact'
import * as config from '../src/internal/shared/config'
import {ArtifactServiceClientJSON, Timestamp} from '../src/generated'
import * as util from '../src/internal/shared/util'
import {noopLogs} from './common'
} from '../src/internal/delete/delete-artifact.js'
import * as config from '../src/internal/shared/config.js'
import {ArtifactServiceClientJSON, Timestamp} from '../src/generated/index.js'
import * as util from '../src/internal/shared/util.js'
import {noopLogs} from './common.js'
type MockedRequest = jest.MockedFunction<RequestInterface<object>>
@@ -11,12 +11,12 @@ import {
downloadArtifactInternal,
downloadArtifactPublic,
streamExtractExternal
} from '../src/internal/download/download-artifact'
import {getUserAgentString} from '../src/internal/shared/user-agent'
import {noopLogs} from './common'
import * as config from '../src/internal/shared/config'
import {ArtifactServiceClientJSON} from '../src/generated'
import * as util from '../src/internal/shared/util'
} from '../src/internal/download/download-artifact.js'
import {getUserAgentString} from '../src/internal/shared/user-agent.js'
import {noopLogs} from './common.js'
import * as config from '../src/internal/shared/config.js'
import {ArtifactServiceClientJSON} from '../src/generated/index.js'
import * as util from '../src/internal/shared/util.js'
type MockedDownloadArtifact = jest.MockedFunction<
RestEndpointMethods['actions']['downloadArtifact']
@@ -104,6 +104,7 @@ const cleanup = async (): Promise<void> => {
const mockGetArtifactSuccess = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'application/zip'
message.push(fs.readFileSync(fixtures.exampleArtifact.path))
message.push(null)
return {
@@ -111,6 +112,17 @@ const mockGetArtifactSuccess = jest.fn(() => {
}
})
const mockGetArtifactHung = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'application/zip'
// Don't push any data or call push(null) to end the stream
// This creates a stream that hangs and never completes
return {
message
}
})
const mockGetArtifactFailure = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 500
@@ -124,6 +136,7 @@ const mockGetArtifactFailure = jest.fn(() => {
const mockGetArtifactMalicious = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'application/zip'
message.push(fs.readFileSync(path.join(__dirname, 'fixtures', 'evil.zip'))) // evil.zip contains files that are formatted x/../../etc/hosts
message.push(null)
return {
@@ -319,14 +332,6 @@ describe('download-artifact', () => {
const mockGet = jest.fn(async () => {
return new Promise((resolve, reject) => {
// Resolve with a 200 status code immediately
resolve({
message: msg,
readBody: async () => {
return Promise.resolve(`{"ok": true}`)
}
})
// Reject with an error after 31 seconds
setTimeout(() => {
reject(new Error('Request timeout'))
@@ -617,6 +622,480 @@ describe('download-artifact', () => {
...fixtures.backendIds,
name: fixtures.artifactName
})
}, 38000)
})
describe('streamExtractExternal', () => {
beforeEach(async () => {
await setup()
// Create workspace directory for streamExtractExternal tests
await fs.promises.mkdir(fixtures.workspaceDir, {recursive: true})
})
afterEach(cleanup)
it('should fail if the timeout is exceeded', async () => {
const mockSlowGetArtifact = jest.fn(mockGetArtifactHung)
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockSlowGetArtifact
}
}
)
try {
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir,
{timeout: 2}
)
expect(true).toBe(false) // should not be called
} catch (error: unknown) {
const e = error as Error
expect(e).toBeInstanceOf(Error)
expect(e.message).toContain('did not respond in 2ms')
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
expect(mockSlowGetArtifact).toHaveBeenCalledTimes(1)
}
})
it('should extract zip file when content-type is application/zip', async () => {
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifactSuccess
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Verify files were extracted (not saved as a single file)
await expectExtractedArchive(fixtures.workspaceDir)
})
it('should save raw file without extracting when content-type is not a zip', async () => {
const rawFileContent = 'This is a raw text file, not a zip'
const rawFileName = 'my-artifact.txt'
const mockGetRawFile = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'text/plain'
message.headers['content-disposition'] =
`attachment; filename="${rawFileName}"`
message.push(Buffer.from(rawFileContent))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetRawFile
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Verify file was saved as-is, not extracted
const savedFilePath = path.join(fixtures.workspaceDir, rawFileName)
expect(fs.existsSync(savedFilePath)).toBe(true)
expect(fs.readFileSync(savedFilePath, 'utf8')).toBe(rawFileContent)
})
it('should save raw file with default name when content-disposition is missing', async () => {
const rawFileContent = 'Binary content here'
const mockGetRawFileNoDisposition = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'application/octet-stream'
// No content-disposition header
message.push(Buffer.from(rawFileContent))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetRawFileNoDisposition
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Verify file was saved with default name 'artifact'
const savedFilePath = path.join(fixtures.workspaceDir, 'artifact')
expect(fs.existsSync(savedFilePath)).toBe(true)
expect(fs.readFileSync(savedFilePath, 'utf8')).toBe(rawFileContent)
})
it('should not attempt to unzip when content-type is image/png', async () => {
const pngFileName = 'screenshot.png'
// Simple PNG header bytes for testing
const pngContent = Buffer.from([
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a
])
const mockGetPngFile = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'image/png'
message.headers['content-disposition'] =
`attachment; filename="${pngFileName}"`
message.push(pngContent)
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetPngFile
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Verify PNG was saved as-is
const savedFilePath = path.join(fixtures.workspaceDir, pngFileName)
expect(fs.existsSync(savedFilePath)).toBe(true)
expect(fs.readFileSync(savedFilePath)).toEqual(pngContent)
})
it('should extract when content-type is application/x-zip-compressed', async () => {
const mockGetZipCompressed = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'application/x-zip-compressed'
message.push(fs.readFileSync(fixtures.exampleArtifact.path))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetZipCompressed
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Verify files were extracted
await expectExtractedArchive(fixtures.workspaceDir)
})
it('should extract zip when URL ends with .zip even if content-type is not application/zip', async () => {
const blobUrlWithZipExtension =
'https://blob-storage.local/artifact.zip?sig=abc123'
const mockGetZipByUrl = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
// Azure Blob Storage may return a generic content-type
message.headers['content-type'] = 'application/octet-stream'
message.push(fs.readFileSync(fixtures.exampleArtifact.path))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetZipByUrl
}
}
)
await streamExtractExternal(
blobUrlWithZipExtension,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Verify files were extracted based on URL .zip extension
await expectExtractedArchive(fixtures.workspaceDir)
})
it('should skip decompression when skipDecompress option is true even for zip content-type', async () => {
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifactSuccess
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir,
{skipDecompress: true}
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Verify zip was saved as-is, not extracted
// When skipDecompress is true, the file should be saved with default name 'artifact'
const savedFilePath = path.join(fixtures.workspaceDir, 'artifact')
expect(fs.existsSync(savedFilePath)).toBe(true)
// The saved file should be the raw zip content
const savedContent = fs.readFileSync(savedFilePath)
const originalZipContent = fs.readFileSync(fixtures.exampleArtifact.path)
expect(savedContent).toEqual(originalZipContent)
})
it('should sanitize path traversal attempts in Content-Disposition filename', async () => {
const rawFileContent = 'malicious content'
const maliciousFileName = '../../../etc/passwd'
const mockGetMaliciousFile = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'text/plain'
message.headers['content-disposition'] =
`attachment; filename="${maliciousFileName}"`
message.push(Buffer.from(rawFileContent))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetMaliciousFile
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Verify file was saved with sanitized name (just 'passwd', not the full path)
const sanitizedFileName = 'passwd'
const savedFilePath = path.join(fixtures.workspaceDir, sanitizedFileName)
expect(fs.existsSync(savedFilePath)).toBe(true)
expect(fs.readFileSync(savedFilePath, 'utf8')).toBe(rawFileContent)
// Verify the file was NOT written outside the workspace directory
const maliciousPath = path.resolve(
fixtures.workspaceDir,
maliciousFileName
)
expect(fs.existsSync(maliciousPath)).toBe(false)
})
it('should handle encoded path traversal attempts in Content-Disposition filename', async () => {
const rawFileContent = 'encoded malicious content'
// URL encoded version of ../../../etc/passwd
const encodedMaliciousFileName = '..%2F..%2F..%2Fetc%2Fpasswd'
const mockGetEncodedMaliciousFile = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'application/octet-stream'
message.headers['content-disposition'] =
`attachment; filename="${encodedMaliciousFileName}"`
message.push(Buffer.from(rawFileContent))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetEncodedMaliciousFile
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// After decoding and sanitizing, should just be 'passwd'
const sanitizedFileName = 'passwd'
const savedFilePath = path.join(fixtures.workspaceDir, sanitizedFileName)
expect(fs.existsSync(savedFilePath)).toBe(true)
expect(fs.readFileSync(savedFilePath, 'utf8')).toBe(rawFileContent)
// Verify the file was NOT written outside the workspace directory
const maliciousPathEncoded = path.resolve(
fixtures.workspaceDir,
encodedMaliciousFileName
)
expect(fs.existsSync(maliciousPathEncoded)).toBe(false)
const maliciousPath = path.resolve(
fixtures.workspaceDir,
'../../../etc/passwd'
)
expect(fs.existsSync(maliciousPath)).toBe(false)
})
it('should correctly handle Content-Disposition with filename* parameter (RFC 5987)', async () => {
const rawFileContent = 'content with rfc5987 encoding'
const expectedFileName = '报告-土-x.txt'
const asciiFileName = '__-_-x.txt'
const mockGetRfc5987File = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'text/plain'
// Server sends both: filename with _ fallbacks, filename* with UTF-8 encoding
message.headers['content-disposition'] =
`attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodeURIComponent(expectedFileName)}`
message.push(Buffer.from(rawFileContent, 'utf8'))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetRfc5987File
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
const savedFilePath = path.join(fixtures.workspaceDir, expectedFileName)
expect(fs.existsSync(savedFilePath)).toBe(true)
expect(fs.readFileSync(savedFilePath, 'utf8')).toBe(rawFileContent)
})
it('should handle zip artifacts with Chinese characters in the artifact name', async () => {
// Simulate Azure Blob Storage URL with rscd containing Chinese filename
const chineseArtifactName = 'probe-土-x'
const asciiArtifactName = 'probe-_-x'
const blobUrlWithChineseName = `https://blob-storage.local/artifact.zip?rscd=${encodeURIComponent(`attachment; filename="${asciiArtifactName}.zip"; filename*=UTF-8''${encodeURIComponent(`${chineseArtifactName}.zip`)}`)}&rsct=application%2Fzip&sig=abc123`
const mockGetZip = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'application/zip'
message.headers['content-disposition'] =
`attachment; filename="${asciiArtifactName}.zip"; filename*=UTF-8''${encodeURIComponent(`${chineseArtifactName}.zip`)}`
message.push(fs.readFileSync(fixtures.exampleArtifact.path))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetZip
}
}
)
await streamExtractExternal(blobUrlWithChineseName, fixtures.workspaceDir)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
// Zip should be extracted normally regardless of Chinese artifact name
await expectExtractedArchive(fixtures.workspaceDir)
})
it.each([
['土', '_'], // U+571F - known to cause 400 errors
['日', '_'], // U+65E5 - reported to work fine
['中文测试', '____'], // multiple Chinese characters
['文件-2026年', '__-2026_'], // mixed Chinese and numbers
['データ', '___'], // Japanese katakana
['테스트', '___'] // Korean characters
])(
'should prefer filename* over filename for non-ASCII character %s (%s)',
async (chars, asciiReplacement) => {
const rawFileContent = `content for ${chars}`
const expectedFileName = `artifact-${chars}.txt`
const asciiFileName = `artifact-${asciiReplacement}.txt`
const mockGetFile = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.headers['content-type'] = 'text/plain'
// Server sends filename with _ replacing non-ASCII, filename* with proper encoding
message.headers['content-disposition'] =
`attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodeURIComponent(expectedFileName)}`
message.push(Buffer.from(rawFileContent, 'utf8'))
message.push(null)
return {
message
}
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetFile
}
}
)
await streamExtractExternal(
fixtures.blobStorageUrl,
fixtures.workspaceDir
)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
const savedFilePath = path.join(fixtures.workspaceDir, expectedFileName)
expect(fs.existsSync(savedFilePath)).toBe(true)
expect(fs.readFileSync(savedFilePath, 'utf8')).toBe(rawFileContent)
}
)
})
})
@@ -3,15 +3,15 @@ import type {RequestInterface} from '@octokit/types'
import {
getArtifactInternal,
getArtifactPublic
} from '../src/internal/find/get-artifact'
import * as config from '../src/internal/shared/config'
import {ArtifactServiceClientJSON, Timestamp} from '../src/generated'
import * as util from '../src/internal/shared/util'
import {noopLogs} from './common'
} from '../src/internal/find/get-artifact.js'
import * as config from '../src/internal/shared/config.js'
import {ArtifactServiceClientJSON, Timestamp} from '../src/generated/index.js'
import * as util from '../src/internal/shared/util.js'
import {noopLogs} from './common.js'
import {
ArtifactNotFoundError,
InvalidResponseError
} from '../src/internal/shared/errors'
} from '../src/internal/shared/errors.js'
type MockedRequest = jest.MockedFunction<RequestInterface<object>>
@@ -1,22 +1,21 @@
import * as github from '@actions/github'
import type {RestEndpointMethods} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/method-types'
import type {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types'
import {
listArtifactsInternal,
listArtifactsPublic
} from '../src/internal/find/list-artifacts'
import * as config from '../src/internal/shared/config'
import {ArtifactServiceClientJSON, Timestamp} from '../src/generated'
import * as util from '../src/internal/shared/util'
import {noopLogs} from './common'
import {Artifact} from '../src/internal/shared/interfaces'
} from '../src/internal/find/list-artifacts.js'
import * as config from '../src/internal/shared/config.js'
import {ArtifactServiceClientJSON, Timestamp} from '../src/generated/index.js'
import * as util from '../src/internal/shared/util.js'
import {noopLogs} from './common.js'
import {Artifact} from '../src/internal/shared/interfaces.js'
import {RequestInterface} from '@octokit/types'
type MockedListWorkflowRunArtifacts = jest.MockedFunction<
RestEndpointMethods['actions']['listWorkflowRunArtifacts']
>
type MockedRequest = jest.MockedFunction<RequestInterface<object>>
jest.mock('@actions/github', () => ({
getOctokit: jest.fn().mockReturnValue({
request: jest.fn(),
rest: {
actions: {
listWorkflowRunArtifacts: jest.fn()
@@ -81,10 +80,10 @@ describe('list-artifact', () => {
describe('public', () => {
it('should return a list of artifacts', async () => {
const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions
.listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
mockListArtifacts.mockResolvedValueOnce({
mockRequest.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
@@ -105,10 +104,10 @@ describe('list-artifact', () => {
})
it('should return the latest artifact when latest is specified', async () => {
const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions
.listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
mockListArtifacts.mockResolvedValueOnce({
mockRequest.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
@@ -129,10 +128,10 @@ describe('list-artifact', () => {
})
it('can return empty artifacts', async () => {
const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions
.listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
mockListArtifacts.mockResolvedValueOnce({
mockRequest.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
@@ -156,10 +155,10 @@ describe('list-artifact', () => {
})
it('should fail if non-200 response', async () => {
const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions
.listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
mockListArtifacts.mockRejectedValue(new Error('boom'))
mockRequest.mockRejectedValueOnce(new Error('boom'))
await expect(
listArtifactsPublic(
@@ -171,6 +170,126 @@ describe('list-artifact', () => {
)
).rejects.toThrow('boom')
})
it('should handle pagination correctly when fetching multiple pages', async () => {
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
const manyArtifacts = Array.from({length: 150}, (_, i) => ({
id: i + 1,
name: `artifact-${i + 1}`,
size: 100,
createdAt: new Date('2023-12-01')
}))
mockRequest
.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: {
...artifactsToListResponse(manyArtifacts.slice(0, 100)),
total_count: 150
}
})
.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: {
...artifactsToListResponse(manyArtifacts.slice(100, 150)),
total_count: 150
}
})
const response = await listArtifactsPublic(
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token,
false
)
// Verify that both API calls were made
expect(mockRequest).toHaveBeenCalledTimes(2)
// Should return all 150 artifacts across both pages
expect(response.artifacts).toHaveLength(150)
// Verify we got artifacts from both pages
expect(response.artifacts[0].name).toBe('artifact-1')
expect(response.artifacts[99].name).toBe('artifact-100')
expect(response.artifacts[100].name).toBe('artifact-101')
expect(response.artifacts[149].name).toBe('artifact-150')
})
it('should respect ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT environment variable', async () => {
const originalEnv = process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT
process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT = '150'
jest.resetModules()
try {
const {listArtifactsPublic: listArtifactsPublicReloaded} = await import(
'../src/internal/find/list-artifacts'
)
const githubReloaded = await import('@actions/github')
const mockRequest = (githubReloaded.getOctokit as jest.Mock)(
fixtures.token
).request as MockedRequest
const manyArtifacts = Array.from({length: 200}, (_, i) => ({
id: i + 1,
name: `artifact-${i + 1}`,
size: 100,
createdAt: new Date('2023-12-01')
}))
mockRequest
.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: {
...artifactsToListResponse(manyArtifacts.slice(0, 100)),
total_count: 200
}
})
.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: {
...artifactsToListResponse(manyArtifacts.slice(100, 150)),
total_count: 200
}
})
const response = await listArtifactsPublicReloaded(
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token,
false
)
// Should only return 150 artifacts due to the limit
expect(response.artifacts).toHaveLength(150)
expect(response.artifacts[0].name).toBe('artifact-1')
expect(response.artifacts[149].name).toBe('artifact-150')
} finally {
// Restore original environment variable
if (originalEnv !== undefined) {
process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT = originalEnv
} else {
delete process.env.ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT
}
// Reset modules again to restore original state
jest.resetModules()
}
})
})
describe('internal', () => {
@@ -1,9 +1,9 @@
import {
validateArtifactName,
validateFilePath
} from '../src/internal/upload/path-and-artifact-name-validation'
} from '../src/internal/upload/path-and-artifact-name-validation.js'
import {noopLogs} from './common'
import {noopLogs} from './common.js'
describe('Path and artifact name validation', () => {
beforeAll(() => {
@@ -1,5 +1,5 @@
import {Timestamp} from '../src/generated'
import * as retention from '../src/internal/upload/retention'
import {Timestamp} from '../src/generated/index.js'
import * as retention from '../src/internal/upload/retention.js'
describe('retention', () => {
beforeEach(() => {
@@ -0,0 +1,59 @@
import * as fs from 'fs'
import * as path from 'path'
import {createRawFileUploadStream} from '../src/internal/upload/stream.js'
import {noopLogs} from './common.js'
const fixtures = {
testDirectory: path.join(__dirname, '_temp', 'stream-test'),
testFile: path.join(__dirname, '_temp', 'stream-test', 'test-file.txt'),
testContent: 'hello stream test'
}
describe('createRawFileUploadStream', () => {
beforeAll(() => {
fs.mkdirSync(fixtures.testDirectory, {recursive: true})
fs.writeFileSync(fixtures.testFile, fixtures.testContent)
})
beforeEach(() => {
noopLogs()
})
afterEach(() => {
jest.restoreAllMocks()
})
it('should stream file contents through the upload stream', async () => {
const uploadStream = await createRawFileUploadStream(fixtures.testFile)
const chunks: Buffer[] = []
const result = await new Promise<string>((resolve, reject) => {
uploadStream.on('data', chunk => chunks.push(Buffer.from(chunk)))
uploadStream.on('end', () =>
resolve(Buffer.concat(chunks).toString('utf-8'))
)
uploadStream.on('error', reject)
})
expect(result).toBe(fixtures.testContent)
})
it('should propagate file read errors through the upload stream', async () => {
// Use a directory path — createReadStream on a directory fails cross-platform
// Mock lstat to return a non-symlink result so we reach createReadStream
const dirPath = fixtures.testDirectory
jest
.spyOn(fs.promises, 'lstat')
.mockResolvedValue({isSymbolicLink: () => false} as fs.Stats)
const uploadStream = await createRawFileUploadStream(dirPath)
await expect(
new Promise((resolve, reject) => {
uploadStream.on('data', resolve)
uploadStream.on('end', resolve)
uploadStream.on('error', reject)
})
).rejects.toThrow('An error has occurred during file read for the artifact')
})
})
@@ -1,12 +1,13 @@
import * as uploadZipSpecification from '../src/internal/upload/upload-zip-specification'
import * as zip from '../src/internal/upload/zip'
import * as util from '../src/internal/shared/util'
import * as config from '../src/internal/shared/config'
import {ArtifactServiceClientJSON} from '../src/generated'
import * as blobUpload from '../src/internal/upload/blob-upload'
import {uploadArtifact} from '../src/internal/upload/upload-artifact'
import {noopLogs} from './common'
import {FilesNotFoundError} from '../src/internal/shared/errors'
import * as uploadZipSpecification from '../src/internal/upload/upload-zip-specification.js'
import * as zip from '../src/internal/upload/zip.js'
import * as util from '../src/internal/shared/util.js'
import * as config from '../src/internal/shared/config.js'
import {ArtifactServiceClientJSON} from '../src/generated/index.js'
import * as blobUpload from '../src/internal/upload/blob-upload.js'
import {uploadArtifact} from '../src/internal/upload/upload-artifact.js'
import {noopLogs} from './common.js'
import {FilesNotFoundError} from '../src/internal/shared/errors.js'
import * as stream from '../src/internal/upload/stream.js'
import {BlockBlobUploadStreamOptions} from '@azure/storage-blob'
import * as fs from 'fs'
import * as path from 'path'
@@ -108,7 +109,7 @@ describe('upload-artifact', () => {
fixtures.files.map(file => ({
sourcePath: path.join(fixtures.uploadDirectory, file.name),
destinationPath: file.name,
stats: new fs.Stats()
stats: fs.statSync(path.join(fixtures.uploadDirectory, file.name))
}))
)
jest.spyOn(config, 'getRuntimeToken').mockReturnValue(fixtures.runtimeToken)
@@ -150,7 +151,7 @@ describe('upload-artifact', () => {
it('should return false if the creation request fails', async () => {
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
.mockReturnValue(Promise.resolve(new stream.WaterMarkedUploadStream(1)))
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(Promise.resolve({ok: false, signedUploadUrl: ''}))
@@ -167,7 +168,7 @@ describe('upload-artifact', () => {
it('should return false if blob storage upload is unsuccessful', async () => {
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
.mockReturnValue(Promise.resolve(new stream.WaterMarkedUploadStream(1)))
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
@@ -177,7 +178,7 @@ describe('upload-artifact', () => {
})
)
jest
.spyOn(blobUpload, 'uploadZipToBlobStorage')
.spyOn(blobUpload, 'uploadToBlobStorage')
.mockReturnValue(Promise.reject(new Error('boom')))
const uploadResp = uploadArtifact(
@@ -192,7 +193,7 @@ describe('upload-artifact', () => {
it('should reject if finalize artifact fails', async () => {
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
.mockReturnValue(Promise.resolve(new stream.WaterMarkedUploadStream(1)))
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
@@ -201,7 +202,7 @@ describe('upload-artifact', () => {
signedUploadUrl: 'https://signed-upload-url.com'
})
)
jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue(
jest.spyOn(blobUpload, 'uploadToBlobStorage').mockReturnValue(
Promise.resolve({
uploadSize: 1234,
sha256Hash: 'test-sha256-hash'
@@ -281,7 +282,7 @@ describe('upload-artifact', () => {
}
)
const {id, size} = await uploadArtifact(
const {id, size, digest} = await uploadArtifact(
fixtures.inputs.artifactName,
fixtures.files.map(file =>
path.join(fixtures.uploadDirectory, file.name)
@@ -291,6 +292,8 @@ describe('upload-artifact', () => {
expect(id).toBe(1)
expect(size).toBe(loadedBytes)
expect(digest).toBeDefined()
expect(digest).toHaveLength(64)
const extractedDirectory = path.join(
fixtures.uploadDirectory,
@@ -368,4 +371,284 @@ describe('upload-artifact', () => {
await expect(uploadResp).rejects.toThrow('Upload progress stalled.')
})
describe('skipArchive option', () => {
it('should throw an error if skipArchive is true and files array is empty', async () => {
const uploadResp = uploadArtifact(
fixtures.inputs.artifactName,
[],
fixtures.inputs.rootDirectory,
{skipArchive: true}
)
await expect(uploadResp).rejects.toThrow(FilesNotFoundError)
})
it('should throw an error if skipArchive is true and multiple files are provided', async () => {
const uploadResp = uploadArtifact(
fixtures.inputs.artifactName,
fixtures.inputs.files,
fixtures.inputs.rootDirectory,
{skipArchive: true}
)
await expect(uploadResp).rejects.toThrow(
'skipArchive option is only supported when uploading a single file'
)
})
it('should upload a single file without archiving when skipArchive is true', async () => {
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockRestore()
const singleFile = path.join(fixtures.uploadDirectory, 'file1.txt')
const expectedContent = 'test 1 file content'
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
signedUploadUrl: 'https://signed-upload-url.local'
})
)
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
artifactId: '1'
})
)
let uploadedContent = ''
let loadedBytes = 0
uploadStreamMock.mockImplementation(
async (
stream: NodeJS.ReadableStream,
bufferSize?: number,
maxConcurrency?: number,
options?: BlockBlobUploadStreamOptions
) => {
const {onProgress} = options || {}
onProgress?.({loadedBytes: 0})
return new Promise((resolve, reject) => {
stream.on('data', chunk => {
loadedBytes += chunk.length
uploadedContent += chunk.toString()
onProgress?.({loadedBytes})
})
stream.on('end', () => {
onProgress?.({loadedBytes})
resolve({})
})
stream.on('error', err => {
reject(err)
})
})
}
)
const {id, size, digest} = await uploadArtifact(
fixtures.inputs.artifactName,
[singleFile],
fixtures.uploadDirectory,
{skipArchive: true}
)
expect(id).toBe(1)
expect(size).toBe(loadedBytes)
expect(digest).toBeDefined()
expect(digest).toHaveLength(64)
// Verify the uploaded content is the raw file, not a zip
expect(uploadedContent).toBe(expectedContent)
})
it('should use the correct MIME type when skipArchive is true', async () => {
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockRestore()
const singleFile = path.join(fixtures.uploadDirectory, 'file1.txt')
const createArtifactSpy = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
signedUploadUrl: 'https://signed-upload-url.local'
})
)
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
artifactId: '1'
})
)
uploadStreamMock.mockImplementation(
async (
stream: NodeJS.ReadableStream,
bufferSize?: number,
maxConcurrency?: number,
options?: BlockBlobUploadStreamOptions
) => {
const {onProgress} = options || {}
onProgress?.({loadedBytes: 0})
return new Promise((resolve, reject) => {
stream.on('data', chunk => {
onProgress?.({loadedBytes: chunk.length})
})
stream.on('end', () => {
resolve({})
})
stream.on('error', err => {
reject(err)
})
})
}
)
await uploadArtifact(
fixtures.inputs.artifactName,
[singleFile],
fixtures.uploadDirectory,
{skipArchive: true}
)
// Verify CreateArtifact was called with the correct MIME type for .txt file
expect(createArtifactSpy).toHaveBeenCalledWith(
expect.objectContaining({
mimeType: expect.objectContaining({value: 'text/plain'})
})
)
})
it('should use application/zip MIME type when skipArchive is false', async () => {
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockRestore()
const createArtifactSpy = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
signedUploadUrl: 'https://signed-upload-url.local'
})
)
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
artifactId: '1'
})
)
uploadStreamMock.mockImplementation(
async (
stream: NodeJS.ReadableStream,
bufferSize?: number,
maxConcurrency?: number,
options?: BlockBlobUploadStreamOptions
) => {
const {onProgress} = options || {}
onProgress?.({loadedBytes: 0})
return new Promise((resolve, reject) => {
stream.on('data', chunk => {
onProgress?.({loadedBytes: chunk.length})
})
stream.on('end', () => {
resolve({})
})
stream.on('error', err => {
reject(err)
})
})
}
)
await uploadArtifact(
fixtures.inputs.artifactName,
fixtures.files.map(file =>
path.join(fixtures.uploadDirectory, file.name)
),
fixtures.uploadDirectory
)
// Verify CreateArtifact was called with application/zip MIME type
expect(createArtifactSpy).toHaveBeenCalledWith(
expect.objectContaining({
mimeType: expect.objectContaining({value: 'application/zip'})
})
)
})
it('should use the file basename as artifact name when skipArchive is true', async () => {
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockRestore()
const singleFile = path.join(fixtures.uploadDirectory, 'file1.txt')
const createArtifactSpy = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
signedUploadUrl: 'https://signed-upload-url.local'
})
)
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
artifactId: '1'
})
)
uploadStreamMock.mockImplementation(
async (
stream: NodeJS.ReadableStream,
bufferSize?: number,
maxConcurrency?: number,
options?: BlockBlobUploadStreamOptions
) => {
const {onProgress} = options || {}
onProgress?.({loadedBytes: 0})
return new Promise((resolve, reject) => {
stream.on('data', chunk => {
onProgress?.({loadedBytes: chunk.length})
})
stream.on('end', () => {
resolve({})
})
stream.on('error', err => {
reject(err)
})
})
}
)
await uploadArtifact(
'original-name',
[singleFile],
fixtures.uploadDirectory,
{skipArchive: true}
)
// Verify CreateArtifact was called with the file basename, not the original name
expect(createArtifactSpy).toHaveBeenCalledWith(
expect.objectContaining({
name: 'file1.txt'
})
)
})
})
})
@@ -4,8 +4,8 @@ import {promises as fs} from 'fs'
import {
getUploadZipSpecification,
validateRootDirectory
} from '../src/internal/upload/upload-zip-specification'
import {noopLogs} from './common'
} from '../src/internal/upload/upload-zip-specification.js'
import {noopLogs} from './common.js'
const root = path.join(__dirname, '_temp', 'upload-specification')
const goodItem1Path = path.join(
+160 -2
View File
@@ -1,5 +1,7 @@
import * as config from '../src/internal/shared/config'
import * as util from '../src/internal/shared/util'
import * as config from '../src/internal/shared/config.js'
import * as util from '../src/internal/shared/util.js'
import {maskSigUrl, maskSecretUrls} from '../src/internal/shared/util.js'
import {setSecret, debug} from '@actions/core'
export const testRuntimeToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhOmNhMzk1MDg1LTA0MGEtNTI2Yi0yY2U4LWJkYzg1ZjY5Mjc3NCIsImlhdCI6MTUxNjIzOTAyMn0.XYnI_wHPBlUi1mqYveJnnkJhp4dlFjqxzRmISPsqfw8'
@@ -59,3 +61,159 @@ describe('get-backend-ids-from-token', () => {
)
})
})
jest.mock('@actions/core')
describe('maskSigUrl', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('does nothing if no sig parameter is present', () => {
const url = 'https://example.com'
maskSigUrl(url)
expect(setSecret).not.toHaveBeenCalled()
})
it('masks the sig parameter in the middle of the URL and sets it as a secret', () => {
const url = 'https://example.com/?param1=value1&sig=12345&param2=value2'
maskSigUrl(url)
expect(setSecret).toHaveBeenCalledWith('12345')
expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('12345'))
})
it('does nothing if the URL is empty', () => {
const url = ''
maskSigUrl(url)
expect(setSecret).not.toHaveBeenCalled()
})
it('handles URLs with fragments', () => {
const url = 'https://example.com?sig=12345#fragment'
maskSigUrl(url)
expect(setSecret).toHaveBeenCalledWith('12345')
expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('12345'))
})
})
describe('maskSigUrl handles special characters in signatures', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('handles signatures with slashes', () => {
const url = 'https://example.com/?sig=abc/123'
maskSigUrl(url)
expect(setSecret).toHaveBeenCalledWith('abc/123')
expect(setSecret).toHaveBeenCalledWith('abc%2F123')
})
it('handles signatures with plus signs', () => {
const url = 'https://example.com/?sig=abc+123'
maskSigUrl(url)
expect(setSecret).toHaveBeenCalledWith('abc 123')
expect(setSecret).toHaveBeenCalledWith('abc%20123')
})
it('handles signatures with equals signs', () => {
const url = 'https://example.com/?sig=abc=123'
maskSigUrl(url)
expect(setSecret).toHaveBeenCalledWith('abc=123')
expect(setSecret).toHaveBeenCalledWith('abc%3D123')
})
it('handles already percent-encoded signatures', () => {
const url = 'https://example.com/?sig=abc%2F123%3D'
maskSigUrl(url)
expect(setSecret).toHaveBeenCalledWith('abc/123=')
expect(setSecret).toHaveBeenCalledWith('abc%2F123%3D')
})
it('handles complex Azure SAS signatures', () => {
const url =
'https://example.com/container/file.txt?sig=nXyQIUj%2F%2F06Cxt80pBRYiiJlYqtPYg5sz%2FvEh5iHAhw%3D&se=2023-12-31'
maskSigUrl(url)
expect(setSecret).toHaveBeenCalledWith(
'nXyQIUj//06Cxt80pBRYiiJlYqtPYg5sz/vEh5iHAhw='
)
expect(setSecret).toHaveBeenCalledWith(
'nXyQIUj%2F%2F06Cxt80pBRYiiJlYqtPYg5sz%2FvEh5iHAhw%3D'
)
})
it('handles signatures with multiple special characters', () => {
const url = 'https://example.com/?sig=a/b+c=d&e=f'
maskSigUrl(url)
expect(setSecret).toHaveBeenCalledWith('a/b c=d')
expect(setSecret).toHaveBeenCalledWith('a%2Fb%20c%3Dd')
})
})
describe('maskSecretUrls', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('masks sig parameters in signed_upload_url and signed_url', () => {
const body = {
signed_upload_url: 'https://upload.com?sig=upload123',
signed_url: 'https://download.com?sig=download123'
}
maskSecretUrls(body)
expect(setSecret).toHaveBeenCalledWith('upload123')
expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('upload123'))
expect(setSecret).toHaveBeenCalledWith('download123')
expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('download123'))
})
it('handles case where only upload_url is present', () => {
const body = {
signed_upload_url: 'https://upload.com?sig=upload123'
}
maskSecretUrls(body)
expect(setSecret).toHaveBeenCalledWith('upload123')
expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('upload123'))
})
it('handles case where only download_url is present', () => {
const body = {
signed_url: 'https://download.com?sig=download123'
}
maskSecretUrls(body)
expect(setSecret).toHaveBeenCalledWith('download123')
expect(setSecret).toHaveBeenCalledWith(encodeURIComponent('download123'))
})
it('handles case where URLs do not contain sig parameters', () => {
const body = {
signed_upload_url: 'https://upload.com?token=abc',
signed_url: 'https://download.com?token=xyz'
}
maskSecretUrls(body)
expect(setSecret).not.toHaveBeenCalled()
})
it('handles empty string URLs', () => {
const body = {
signed_upload_url: '',
signed_url: ''
}
maskSecretUrls(body)
expect(setSecret).not.toHaveBeenCalled()
})
it('does nothing if body is not an object or is null', () => {
maskSecretUrls(null)
expect(debug).toHaveBeenCalledWith('body is not an object or is null')
expect(setSecret).not.toHaveBeenCalled()
})
it('does nothing if signed_upload_url and signed_url are not strings', () => {
const body = {
signed_upload_url: 123,
signed_url: 456
}
maskSecretUrls(body)
expect(setSecret).not.toHaveBeenCalled()
})
})
+1 -1
View File
@@ -40,4 +40,4 @@
#### Defined in
[src/artifact.ts:7](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/artifact.ts#L7)
[src/artifact.ts:7](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/artifact.ts#L7)
@@ -48,7 +48,7 @@ Error.constructor
#### Defined in
[src/internal/shared/errors.ts:24](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L24)
[src/internal/shared/errors.ts:24](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L24)
## Properties
@@ -61,7 +61,7 @@ single DeleteArtifactResponse object
#### Defined in
[src/internal/client.ts:248](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L248)
[src/internal/client.ts:248](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L248)
___
@@ -92,7 +92,7 @@ single DownloadArtifactResponse object
#### Defined in
[src/internal/client.ts:138](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L138)
[src/internal/client.ts:138](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L138)
___
@@ -127,7 +127,7 @@ If there are multiple artifacts with the same name in the same workflow run this
#### Defined in
[src/internal/client.ts:212](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L212)
[src/internal/client.ts:212](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L212)
___
@@ -159,7 +159,7 @@ ListArtifactResponse object
#### Defined in
[src/internal/client.ts:176](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L176)
[src/internal/client.ts:176](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L176)
___
@@ -190,4 +190,4 @@ single UploadArtifactResponse object
#### Defined in
[src/internal/client.ts:113](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L113)
[src/internal/client.ts:113](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L113)
@@ -49,7 +49,7 @@ Error.constructor
#### Defined in
[src/internal/shared/errors.ts:4](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L4)
[src/internal/shared/errors.ts:4](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L4)
## Properties
@@ -59,7 +59,7 @@ Error.constructor
#### Defined in
[src/internal/shared/errors.ts:2](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L2)
[src/internal/shared/errors.ts:2](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L2)
___
@@ -48,7 +48,7 @@ Error.constructor
#### Defined in
[src/internal/shared/errors.ts:31](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L31)
[src/internal/shared/errors.ts:31](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L31)
## Properties
@@ -48,7 +48,7 @@ Error.constructor
#### Defined in
[src/internal/shared/errors.ts:17](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L17)
[src/internal/shared/errors.ts:17](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L17)
## Properties
@@ -50,7 +50,7 @@ Error.constructor
#### Defined in
[src/internal/shared/errors.ts:42](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L42)
[src/internal/shared/errors.ts:42](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L42)
## Properties
@@ -60,7 +60,7 @@ Error.constructor
#### Defined in
[src/internal/shared/errors.ts:40](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L40)
[src/internal/shared/errors.ts:40](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L40)
___
@@ -198,4 +198,4 @@ ___
#### Defined in
[src/internal/shared/errors.ts:49](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L49)
[src/internal/shared/errors.ts:49](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L49)
@@ -43,7 +43,7 @@ Error.constructor
#### Defined in
[src/internal/shared/errors.ts:62](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L62)
[src/internal/shared/errors.ts:62](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L62)
## Properties
@@ -181,4 +181,4 @@ ___
#### Defined in
[src/internal/shared/errors.ts:68](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/errors.ts#L68)
[src/internal/shared/errors.ts:68](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/errors.ts#L68)
@@ -23,7 +23,7 @@ The time when the artifact was created
#### Defined in
[src/internal/shared/interfaces.ts:123](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L123)
[src/internal/shared/interfaces.ts:128](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L128)
___
@@ -35,7 +35,7 @@ The ID of the artifact
#### Defined in
[src/internal/shared/interfaces.ts:113](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L113)
[src/internal/shared/interfaces.ts:118](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L118)
___
@@ -47,7 +47,7 @@ The name of the artifact
#### Defined in
[src/internal/shared/interfaces.ts:108](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L108)
[src/internal/shared/interfaces.ts:113](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L113)
___
@@ -59,4 +59,4 @@ The size of the artifact in bytes
#### Defined in
[src/internal/shared/interfaces.ts:118](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L118)
[src/internal/shared/interfaces.ts:123](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L123)
@@ -43,7 +43,7 @@ single DeleteArtifactResponse object
#### Defined in
[src/internal/client.ts:103](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L103)
[src/internal/client.ts:103](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L103)
___
@@ -70,7 +70,7 @@ single DownloadArtifactResponse object
#### Defined in
[src/internal/client.ts:89](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L89)
[src/internal/client.ts:89](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L89)
___
@@ -101,7 +101,7 @@ If there are multiple artifacts with the same name in the same workflow run this
#### Defined in
[src/internal/client.ts:75](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L75)
[src/internal/client.ts:75](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L75)
___
@@ -129,7 +129,7 @@ ListArtifactResponse object
#### Defined in
[src/internal/client.ts:57](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L57)
[src/internal/client.ts:57](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L57)
___
@@ -156,4 +156,4 @@ single UploadArtifactResponse object
#### Defined in
[src/internal/client.ts:40](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/client.ts#L40)
[src/internal/client.ts:40](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/client.ts#L40)
@@ -20,4 +20,4 @@ The id of the artifact that was deleted
#### Defined in
[src/internal/shared/interfaces.ts:158](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L158)
[src/internal/shared/interfaces.ts:163](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L163)
@@ -20,4 +20,4 @@ Denotes where the artifact will be downloaded to. If not specified then the arti
#### Defined in
[src/internal/shared/interfaces.ts:98](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L98)
[src/internal/shared/interfaces.ts:103](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L103)
@@ -20,4 +20,4 @@ The path where the artifact was downloaded to
#### Defined in
[src/internal/shared/interfaces.ts:88](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L88)
[src/internal/shared/interfaces.ts:93](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L93)
@@ -27,4 +27,4 @@ The criteria for finding Artifact(s) out of the scope of the current run.
#### Defined in
[src/internal/shared/interfaces.ts:131](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L131)
[src/internal/shared/interfaces.ts:136](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L136)
@@ -20,4 +20,4 @@ Metadata about the artifact that was found
#### Defined in
[src/internal/shared/interfaces.ts:57](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L57)
[src/internal/shared/interfaces.ts:62](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L62)
@@ -21,4 +21,4 @@ In the case of reruns, this can be useful to avoid duplicates
#### Defined in
[src/internal/shared/interfaces.ts:68](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L68)
[src/internal/shared/interfaces.ts:73](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L73)
@@ -20,4 +20,4 @@ A list of artifacts that were found
#### Defined in
[src/internal/shared/interfaces.ts:78](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L78)
[src/internal/shared/interfaces.ts:83](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L83)
@@ -28,7 +28,7 @@ For large files that are not easily compressed, a value of 0 is recommended for
#### Defined in
[src/internal/shared/interfaces.ts:47](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L47)
[src/internal/shared/interfaces.ts:52](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L52)
___
@@ -52,4 +52,4 @@ input of 0 assumes default retention setting.
#### Defined in
[src/internal/shared/interfaces.ts:36](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L36)
[src/internal/shared/interfaces.ts:41](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L41)
@@ -8,11 +8,24 @@ Response from the server when an artifact is uploaded
### Properties
- [digest](UploadArtifactResponse.md#digest)
- [id](UploadArtifactResponse.md#id)
- [size](UploadArtifactResponse.md#size)
## Properties
### digest
`Optional` **digest**: `string`
The SHA256 digest of the artifact that was created. Not provided if no artifact was uploaded
#### Defined in
[src/internal/shared/interfaces.ts:19](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L19)
___
### id
`Optional` **id**: `number`
@@ -22,7 +35,7 @@ This ID can be used as input to other APIs to download, delete or get more infor
#### Defined in
[src/internal/shared/interfaces.ts:14](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L14)
[src/internal/shared/interfaces.ts:14](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L14)
___
@@ -34,4 +47,4 @@ Total size of the artifact in bytes. Not provided if no artifact was uploaded
#### Defined in
[src/internal/shared/interfaces.ts:8](https://github.com/actions/toolkit/blob/daf23ba/packages/artifact/src/internal/shared/interfaces.ts#L8)
[src/internal/shared/interfaces.ts:8](https://github.com/actions/toolkit/blob/f522fdf/packages/artifact/src/internal/shared/interfaces.ts#L8)
+960 -904
View File
File diff suppressed because it is too large Load Diff
+28 -16
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/artifact",
"version": "2.1.11",
"version": "6.2.1",
"preview": true,
"description": "Actions artifact lib",
"keywords": [
@@ -10,8 +10,15 @@
],
"homepage": "https://github.com/actions/toolkit/tree/main/packages/artifact",
"license": "MIT",
"type": "module",
"main": "lib/artifact.js",
"types": "lib/artifact.d.ts",
"exports": {
".": {
"types": "./lib/artifact.d.ts",
"import": "./lib/artifact.js"
}
},
"directories": {
"lib": "lib",
"test": "__tests__"
@@ -32,7 +39,7 @@
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "cd ../../ && npm run test ./packages/artifact",
"bootstrap": "cd ../../ && npm run bootstrap",
"tsc-run": "tsc",
"tsc-run": "tsc && cp src/internal/shared/package-version.cjs lib/internal/shared/",
"tsc": "npm run bootstrap && npm run tsc-run",
"gen:docs": "typedoc --plugin typedoc-plugin-markdown --out docs/generated src/artifact.ts --githubPages false --readme none"
},
@@ -40,25 +47,30 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@actions/http-client": "^2.1.0",
"@azure/storage-blob": "^12.15.0",
"@octokit/core": "^3.5.1",
"@octokit/plugin-request-log": "^1.0.4",
"@octokit/plugin-retry": "^3.0.9",
"@octokit/request-error": "^5.0.0",
"@actions/core": "^3.0.0",
"@actions/github": "^9.0.0",
"@actions/http-client": "^4.0.0",
"@azure/storage-blob": "^12.30.0",
"@octokit/core": "^7.0.6",
"@octokit/plugin-request-log": "^6.0.0",
"@octokit/plugin-retry": "^8.0.0",
"@octokit/request": "^10.0.7",
"@octokit/request-error": "^7.1.0",
"@protobuf-ts/plugin": "^2.2.3-alpha.1",
"@protobuf-ts/runtime": "^2.9.4",
"archiver": "^7.0.1",
"jwt-decode": "^3.1.2",
"twirp-ts": "^2.5.0",
"jwt-decode": "^4.0.0",
"unzip-stream": "^0.3.1"
},
"devDependencies": {
"@types/archiver": "^5.3.2",
"@types/archiver": "^7.0.0",
"@types/unzip-stream": "^0.3.4",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "^3.17.1",
"typescript": "^5.2.2"
"typedoc": "^0.28.16",
"typedoc-plugin-markdown": "^4.9.0",
"typescript": "^5.9.3"
},
"overrides": {
"uri-js": "npm:uri-js-replace@^1.0.1",
"node-fetch": "^3.3.2"
}
}
+4 -4
View File
@@ -1,8 +1,8 @@
import {ArtifactClient, DefaultArtifactClient} from './internal/client'
import {ArtifactClient, DefaultArtifactClient} from './internal/client.js'
export * from './internal/shared/interfaces'
export * from './internal/shared/errors'
export * from './internal/client'
export * from './internal/shared/interfaces.js'
export * from './internal/shared/errors.js'
export * from './internal/client.js'
const client: ArtifactClient = new DefaultArtifactClient()
export default client
+4 -4
View File
@@ -1,4 +1,4 @@
export * from './google/protobuf/timestamp'
export * from './google/protobuf/wrappers'
export * from './results/api/v1/artifact'
export * from './results/api/v1/artifact.twirp'
export * from './google/protobuf/timestamp.js'
export * from './google/protobuf/wrappers.js'
export * from './results/api/v1/artifact.js'
export * from './results/api/v1/artifact.twirp-client.js'
@@ -12,9 +12,9 @@ import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { MESSAGE_TYPE } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
import { Int64Value } from "../../../google/protobuf/wrappers";
import { StringValue } from "../../../google/protobuf/wrappers";
import { Timestamp } from "../../../google/protobuf/timestamp";
import { Int64Value } from "../../../google/protobuf/wrappers.js";
import { StringValue } from "../../../google/protobuf/wrappers.js";
import { Timestamp } from "../../../google/protobuf/timestamp.js";
/**
* @generated from protobuf message github.actions.results.api.v1.CreateArtifactRequest
*/
@@ -39,6 +39,10 @@ export interface CreateArtifactRequest {
* @generated from protobuf field: int32 version = 5;
*/
version: number;
/**
* @generated from protobuf field: google.protobuf.StringValue mime_type = 6;
*/
mimeType?: StringValue; // optional
}
/**
* @generated from protobuf message github.actions.results.api.v1.CreateArtifactResponse
@@ -169,6 +173,12 @@ export interface ListArtifactsResponse_MonolithArtifact {
* @generated from protobuf field: google.protobuf.Timestamp created_at = 6;
*/
createdAt?: Timestamp;
/**
* The SHA-256 digest of the artifact, calculated on upload for upload-artifact v4 & newer
*
* @generated from protobuf field: google.protobuf.StringValue digest = 7;
*/
digest?: StringValue;
}
/**
* @generated from protobuf message github.actions.results.api.v1.GetSignedArtifactURLRequest
@@ -234,7 +244,8 @@ class CreateArtifactRequest$Type extends MessageType<CreateArtifactRequest> {
{ no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 3, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 4, name: "expires_at", kind: "message", T: () => Timestamp },
{ no: 5, name: "version", kind: "scalar", T: 5 /*ScalarType.INT32*/ }
{ no: 5, name: "version", kind: "scalar", T: 5 /*ScalarType.INT32*/ },
{ no: 6, name: "mime_type", kind: "message", T: () => StringValue }
]);
}
create(value?: PartialMessage<CreateArtifactRequest>): CreateArtifactRequest {
@@ -264,6 +275,9 @@ class CreateArtifactRequest$Type extends MessageType<CreateArtifactRequest> {
case /* int32 version */ 5:
message.version = reader.int32();
break;
case /* google.protobuf.StringValue mime_type */ 6:
message.mimeType = StringValue.internalBinaryRead(reader, reader.uint32(), options, message.mimeType);
break;
default:
let u = options.readUnknownField;
if (u === "throw")
@@ -291,6 +305,9 @@ class CreateArtifactRequest$Type extends MessageType<CreateArtifactRequest> {
/* int32 version = 5; */
if (message.version !== 0)
writer.tag(5, WireType.Varint).int32(message.version);
/* google.protobuf.StringValue mime_type = 6; */
if (message.mimeType)
StringValue.internalBinaryWrite(message.mimeType, writer.tag(6, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -556,7 +573,7 @@ export const ListArtifactsRequest = new ListArtifactsRequest$Type();
class ListArtifactsResponse$Type extends MessageType<ListArtifactsResponse> {
constructor() {
super("github.actions.results.api.v1.ListArtifactsResponse", [
{ no: 1, name: "artifacts", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => ListArtifactsResponse_MonolithArtifact }
{ no: 1, name: "artifacts", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => ListArtifactsResponse_MonolithArtifact }
]);
}
create(value?: PartialMessage<ListArtifactsResponse>): ListArtifactsResponse {
@@ -608,7 +625,8 @@ class ListArtifactsResponse_MonolithArtifact$Type extends MessageType<ListArtifa
{ no: 3, name: "database_id", kind: "scalar", T: 3 /*ScalarType.INT64*/ },
{ no: 4, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 5, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/ },
{ no: 6, name: "created_at", kind: "message", T: () => Timestamp }
{ no: 6, name: "created_at", kind: "message", T: () => Timestamp },
{ no: 7, name: "digest", kind: "message", T: () => StringValue }
]);
}
create(value?: PartialMessage<ListArtifactsResponse_MonolithArtifact>): ListArtifactsResponse_MonolithArtifact {
@@ -641,6 +659,9 @@ class ListArtifactsResponse_MonolithArtifact$Type extends MessageType<ListArtifa
case /* google.protobuf.Timestamp created_at */ 6:
message.createdAt = Timestamp.internalBinaryRead(reader, reader.uint32(), options, message.createdAt);
break;
case /* google.protobuf.StringValue digest */ 7:
message.digest = StringValue.internalBinaryRead(reader, reader.uint32(), options, message.digest);
break;
default:
let u = options.readUnknownField;
if (u === "throw")
@@ -671,6 +692,9 @@ class ListArtifactsResponse_MonolithArtifact$Type extends MessageType<ListArtifa
/* google.protobuf.Timestamp created_at = 6; */
if (message.createdAt)
Timestamp.internalBinaryWrite(message.createdAt, writer.tag(6, WireType.LengthDelimited).fork(), options).join();
/* google.protobuf.StringValue digest = 7; */
if (message.digest)
StringValue.internalBinaryWrite(message.digest, writer.tag(7, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -913,4 +937,4 @@ export const ArtifactService = new ServiceType("github.actions.results.api.v1.Ar
{ name: "ListArtifacts", options: {}, I: ListArtifactsRequest, O: ListArtifactsResponse },
{ name: "GetSignedArtifactURL", options: {}, I: GetSignedArtifactURLRequest, O: GetSignedArtifactURLResponse },
{ name: "DeleteArtifact", options: {}, I: DeleteArtifactRequest, O: DeleteArtifactResponse }
]);
]);
@@ -0,0 +1,232 @@
import {
CreateArtifactRequest,
CreateArtifactResponse,
FinalizeArtifactRequest,
FinalizeArtifactResponse,
ListArtifactsRequest,
ListArtifactsResponse,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse,
DeleteArtifactRequest,
DeleteArtifactResponse,
} from "./artifact.js";
//==================================//
// Client Code //
//==================================//
interface Rpc {
request(
service: string,
method: string,
contentType: "application/json" | "application/protobuf",
data: object | Uint8Array
): Promise<object | Uint8Array>;
}
export interface ArtifactServiceClient {
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse>;
FinalizeArtifact(
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse>;
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse>;
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse>;
DeleteArtifact(
request: DeleteArtifactRequest
): Promise<DeleteArtifactResponse>;
}
export class ArtifactServiceClientJSON implements ArtifactServiceClient {
private readonly rpc: Rpc;
constructor(rpc: Rpc) {
this.rpc = rpc;
this.CreateArtifact.bind(this);
this.FinalizeArtifact.bind(this);
this.ListArtifacts.bind(this);
this.GetSignedArtifactURL.bind(this);
this.DeleteArtifact.bind(this);
}
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse> {
const data = CreateArtifactRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"CreateArtifact",
"application/json",
data as object
);
return promise.then((data) =>
CreateArtifactResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
}
FinalizeArtifact(
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse> {
const data = FinalizeArtifactRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"FinalizeArtifact",
"application/json",
data as object
);
return promise.then((data) =>
FinalizeArtifactResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
}
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse> {
const data = ListArtifactsRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"ListArtifacts",
"application/json",
data as object
);
return promise.then((data) =>
ListArtifactsResponse.fromJson(data as any, { ignoreUnknownFields: true })
);
}
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse> {
const data = GetSignedArtifactURLRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"GetSignedArtifactURL",
"application/json",
data as object
);
return promise.then((data) =>
GetSignedArtifactURLResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
}
DeleteArtifact(
request: DeleteArtifactRequest
): Promise<DeleteArtifactResponse> {
const data = DeleteArtifactRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"DeleteArtifact",
"application/json",
data as object
);
return promise.then((data) =>
DeleteArtifactResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
}
}
export class ArtifactServiceClientProtobuf implements ArtifactServiceClient {
private readonly rpc: Rpc;
constructor(rpc: Rpc) {
this.rpc = rpc;
this.CreateArtifact.bind(this);
this.FinalizeArtifact.bind(this);
this.ListArtifacts.bind(this);
this.GetSignedArtifactURL.bind(this);
this.DeleteArtifact.bind(this);
}
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse> {
const data = CreateArtifactRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"CreateArtifact",
"application/protobuf",
data
);
return promise.then((data) =>
CreateArtifactResponse.fromBinary(data as Uint8Array)
);
}
FinalizeArtifact(
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse> {
const data = FinalizeArtifactRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"FinalizeArtifact",
"application/protobuf",
data
);
return promise.then((data) =>
FinalizeArtifactResponse.fromBinary(data as Uint8Array)
);
}
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse> {
const data = ListArtifactsRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"ListArtifacts",
"application/protobuf",
data
);
return promise.then((data) =>
ListArtifactsResponse.fromBinary(data as Uint8Array)
);
}
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse> {
const data = GetSignedArtifactURLRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"GetSignedArtifactURL",
"application/protobuf",
data
);
return promise.then((data) =>
GetSignedArtifactURLResponse.fromBinary(data as Uint8Array)
);
}
DeleteArtifact(
request: DeleteArtifactRequest
): Promise<DeleteArtifactResponse> {
const data = DeleteArtifactRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"DeleteArtifact",
"application/protobuf",
data
);
return promise.then((data) =>
DeleteArtifactResponse.fromBinary(data as Uint8Array)
);
}
}
@@ -1,976 +0,0 @@
import {
TwirpContext,
TwirpServer,
RouterEvents,
TwirpError,
TwirpErrorCode,
Interceptor,
TwirpContentType,
chainInterceptors,
} from "twirp-ts";
import {
CreateArtifactRequest,
CreateArtifactResponse,
FinalizeArtifactRequest,
FinalizeArtifactResponse,
ListArtifactsRequest,
ListArtifactsResponse,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse,
DeleteArtifactRequest,
DeleteArtifactResponse,
} from "./artifact";
//==================================//
// Client Code //
//==================================//
interface Rpc {
request(
service: string,
method: string,
contentType: "application/json" | "application/protobuf",
data: object | Uint8Array
): Promise<object | Uint8Array>;
}
export interface ArtifactServiceClient {
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse>;
FinalizeArtifact(
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse>;
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse>;
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse>;
DeleteArtifact(
request: DeleteArtifactRequest
): Promise<DeleteArtifactResponse>;
}
export class ArtifactServiceClientJSON implements ArtifactServiceClient {
private readonly rpc: Rpc;
constructor(rpc: Rpc) {
this.rpc = rpc;
this.CreateArtifact.bind(this);
this.FinalizeArtifact.bind(this);
this.ListArtifacts.bind(this);
this.GetSignedArtifactURL.bind(this);
this.DeleteArtifact.bind(this);
}
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse> {
const data = CreateArtifactRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"CreateArtifact",
"application/json",
data as object
);
return promise.then((data) =>
CreateArtifactResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
}
FinalizeArtifact(
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse> {
const data = FinalizeArtifactRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"FinalizeArtifact",
"application/json",
data as object
);
return promise.then((data) =>
FinalizeArtifactResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
}
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse> {
const data = ListArtifactsRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"ListArtifacts",
"application/json",
data as object
);
return promise.then((data) =>
ListArtifactsResponse.fromJson(data as any, { ignoreUnknownFields: true })
);
}
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse> {
const data = GetSignedArtifactURLRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"GetSignedArtifactURL",
"application/json",
data as object
);
return promise.then((data) =>
GetSignedArtifactURLResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
}
DeleteArtifact(
request: DeleteArtifactRequest
): Promise<DeleteArtifactResponse> {
const data = DeleteArtifactRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"DeleteArtifact",
"application/json",
data as object
);
return promise.then((data) =>
DeleteArtifactResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
}
}
export class ArtifactServiceClientProtobuf implements ArtifactServiceClient {
private readonly rpc: Rpc;
constructor(rpc: Rpc) {
this.rpc = rpc;
this.CreateArtifact.bind(this);
this.FinalizeArtifact.bind(this);
this.ListArtifacts.bind(this);
this.GetSignedArtifactURL.bind(this);
this.DeleteArtifact.bind(this);
}
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse> {
const data = CreateArtifactRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"CreateArtifact",
"application/protobuf",
data
);
return promise.then((data) =>
CreateArtifactResponse.fromBinary(data as Uint8Array)
);
}
FinalizeArtifact(
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse> {
const data = FinalizeArtifactRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"FinalizeArtifact",
"application/protobuf",
data
);
return promise.then((data) =>
FinalizeArtifactResponse.fromBinary(data as Uint8Array)
);
}
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse> {
const data = ListArtifactsRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"ListArtifacts",
"application/protobuf",
data
);
return promise.then((data) =>
ListArtifactsResponse.fromBinary(data as Uint8Array)
);
}
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse> {
const data = GetSignedArtifactURLRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"GetSignedArtifactURL",
"application/protobuf",
data
);
return promise.then((data) =>
GetSignedArtifactURLResponse.fromBinary(data as Uint8Array)
);
}
DeleteArtifact(
request: DeleteArtifactRequest
): Promise<DeleteArtifactResponse> {
const data = DeleteArtifactRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"DeleteArtifact",
"application/protobuf",
data
);
return promise.then((data) =>
DeleteArtifactResponse.fromBinary(data as Uint8Array)
);
}
}
//==================================//
// Server Code //
//==================================//
export interface ArtifactServiceTwirp<T extends TwirpContext = TwirpContext> {
CreateArtifact(
ctx: T,
request: CreateArtifactRequest
): Promise<CreateArtifactResponse>;
FinalizeArtifact(
ctx: T,
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse>;
ListArtifacts(
ctx: T,
request: ListArtifactsRequest
): Promise<ListArtifactsResponse>;
GetSignedArtifactURL(
ctx: T,
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse>;
DeleteArtifact(
ctx: T,
request: DeleteArtifactRequest
): Promise<DeleteArtifactResponse>;
}
export enum ArtifactServiceMethod {
CreateArtifact = "CreateArtifact",
FinalizeArtifact = "FinalizeArtifact",
ListArtifacts = "ListArtifacts",
GetSignedArtifactURL = "GetSignedArtifactURL",
DeleteArtifact = "DeleteArtifact",
}
export const ArtifactServiceMethodList = [
ArtifactServiceMethod.CreateArtifact,
ArtifactServiceMethod.FinalizeArtifact,
ArtifactServiceMethod.ListArtifacts,
ArtifactServiceMethod.GetSignedArtifactURL,
ArtifactServiceMethod.DeleteArtifact,
];
export function createArtifactServiceServer<
T extends TwirpContext = TwirpContext
>(service: ArtifactServiceTwirp<T>) {
return new TwirpServer<ArtifactServiceTwirp, T>({
service,
packageName: "github.actions.results.api.v1",
serviceName: "ArtifactService",
methodList: ArtifactServiceMethodList,
matchRoute: matchArtifactServiceRoute,
});
}
function matchArtifactServiceRoute<T extends TwirpContext = TwirpContext>(
method: string,
events: RouterEvents<T>
) {
switch (method) {
case "CreateArtifact":
return async (
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
CreateArtifactRequest,
CreateArtifactResponse
>[]
) => {
ctx = { ...ctx, methodName: "CreateArtifact" };
await events.onMatch(ctx);
return handleArtifactServiceCreateArtifactRequest(
ctx,
service,
data,
interceptors
);
};
case "FinalizeArtifact":
return async (
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
FinalizeArtifactRequest,
FinalizeArtifactResponse
>[]
) => {
ctx = { ...ctx, methodName: "FinalizeArtifact" };
await events.onMatch(ctx);
return handleArtifactServiceFinalizeArtifactRequest(
ctx,
service,
data,
interceptors
);
};
case "ListArtifacts":
return async (
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
ListArtifactsRequest,
ListArtifactsResponse
>[]
) => {
ctx = { ...ctx, methodName: "ListArtifacts" };
await events.onMatch(ctx);
return handleArtifactServiceListArtifactsRequest(
ctx,
service,
data,
interceptors
);
};
case "GetSignedArtifactURL":
return async (
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>[]
) => {
ctx = { ...ctx, methodName: "GetSignedArtifactURL" };
await events.onMatch(ctx);
return handleArtifactServiceGetSignedArtifactURLRequest(
ctx,
service,
data,
interceptors
);
};
case "DeleteArtifact":
return async (
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
DeleteArtifactRequest,
DeleteArtifactResponse
>[]
) => {
ctx = { ...ctx, methodName: "DeleteArtifact" };
await events.onMatch(ctx);
return handleArtifactServiceDeleteArtifactRequest(
ctx,
service,
data,
interceptors
);
};
default:
events.onNotFound();
const msg = `no handler found`;
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
}
}
function handleArtifactServiceCreateArtifactRequest<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, CreateArtifactRequest, CreateArtifactResponse>[]
): Promise<string | Uint8Array> {
switch (ctx.contentType) {
case TwirpContentType.JSON:
return handleArtifactServiceCreateArtifactJSON<T>(
ctx,
service,
data,
interceptors
);
case TwirpContentType.Protobuf:
return handleArtifactServiceCreateArtifactProtobuf<T>(
ctx,
service,
data,
interceptors
);
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
}
}
function handleArtifactServiceFinalizeArtifactRequest<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
FinalizeArtifactRequest,
FinalizeArtifactResponse
>[]
): Promise<string | Uint8Array> {
switch (ctx.contentType) {
case TwirpContentType.JSON:
return handleArtifactServiceFinalizeArtifactJSON<T>(
ctx,
service,
data,
interceptors
);
case TwirpContentType.Protobuf:
return handleArtifactServiceFinalizeArtifactProtobuf<T>(
ctx,
service,
data,
interceptors
);
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
}
}
function handleArtifactServiceListArtifactsRequest<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, ListArtifactsRequest, ListArtifactsResponse>[]
): Promise<string | Uint8Array> {
switch (ctx.contentType) {
case TwirpContentType.JSON:
return handleArtifactServiceListArtifactsJSON<T>(
ctx,
service,
data,
interceptors
);
case TwirpContentType.Protobuf:
return handleArtifactServiceListArtifactsProtobuf<T>(
ctx,
service,
data,
interceptors
);
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
}
}
function handleArtifactServiceGetSignedArtifactURLRequest<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>[]
): Promise<string | Uint8Array> {
switch (ctx.contentType) {
case TwirpContentType.JSON:
return handleArtifactServiceGetSignedArtifactURLJSON<T>(
ctx,
service,
data,
interceptors
);
case TwirpContentType.Protobuf:
return handleArtifactServiceGetSignedArtifactURLProtobuf<T>(
ctx,
service,
data,
interceptors
);
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
}
}
function handleArtifactServiceDeleteArtifactRequest<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, DeleteArtifactRequest, DeleteArtifactResponse>[]
): Promise<string | Uint8Array> {
switch (ctx.contentType) {
case TwirpContentType.JSON:
return handleArtifactServiceDeleteArtifactJSON<T>(
ctx,
service,
data,
interceptors
);
case TwirpContentType.Protobuf:
return handleArtifactServiceDeleteArtifactProtobuf<T>(
ctx,
service,
data,
interceptors
);
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
}
}
async function handleArtifactServiceCreateArtifactJSON<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, CreateArtifactRequest, CreateArtifactResponse>[]
) {
let request: CreateArtifactRequest;
let response: CreateArtifactResponse;
try {
const body = JSON.parse(data.toString() || "{}");
request = CreateArtifactRequest.fromJson(body, {
ignoreUnknownFields: true,
});
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
CreateArtifactRequest,
CreateArtifactResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.CreateArtifact(ctx, inputReq);
});
} else {
response = await service.CreateArtifact(ctx, request!);
}
return JSON.stringify(
CreateArtifactResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
}) as string
);
}
async function handleArtifactServiceFinalizeArtifactJSON<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
FinalizeArtifactRequest,
FinalizeArtifactResponse
>[]
) {
let request: FinalizeArtifactRequest;
let response: FinalizeArtifactResponse;
try {
const body = JSON.parse(data.toString() || "{}");
request = FinalizeArtifactRequest.fromJson(body, {
ignoreUnknownFields: true,
});
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
FinalizeArtifactRequest,
FinalizeArtifactResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.FinalizeArtifact(ctx, inputReq);
});
} else {
response = await service.FinalizeArtifact(ctx, request!);
}
return JSON.stringify(
FinalizeArtifactResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
}) as string
);
}
async function handleArtifactServiceListArtifactsJSON<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, ListArtifactsRequest, ListArtifactsResponse>[]
) {
let request: ListArtifactsRequest;
let response: ListArtifactsResponse;
try {
const body = JSON.parse(data.toString() || "{}");
request = ListArtifactsRequest.fromJson(body, {
ignoreUnknownFields: true,
});
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
ListArtifactsRequest,
ListArtifactsResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.ListArtifacts(ctx, inputReq);
});
} else {
response = await service.ListArtifacts(ctx, request!);
}
return JSON.stringify(
ListArtifactsResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
}) as string
);
}
async function handleArtifactServiceGetSignedArtifactURLJSON<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>[]
) {
let request: GetSignedArtifactURLRequest;
let response: GetSignedArtifactURLResponse;
try {
const body = JSON.parse(data.toString() || "{}");
request = GetSignedArtifactURLRequest.fromJson(body, {
ignoreUnknownFields: true,
});
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.GetSignedArtifactURL(ctx, inputReq);
});
} else {
response = await service.GetSignedArtifactURL(ctx, request!);
}
return JSON.stringify(
GetSignedArtifactURLResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
}) as string
);
}
async function handleArtifactServiceDeleteArtifactJSON<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, DeleteArtifactRequest, DeleteArtifactResponse>[]
) {
let request: DeleteArtifactRequest;
let response: DeleteArtifactResponse;
try {
const body = JSON.parse(data.toString() || "{}");
request = DeleteArtifactRequest.fromJson(body, {
ignoreUnknownFields: true,
});
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
DeleteArtifactRequest,
DeleteArtifactResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.DeleteArtifact(ctx, inputReq);
});
} else {
response = await service.DeleteArtifact(ctx, request!);
}
return JSON.stringify(
DeleteArtifactResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
}) as string
);
}
async function handleArtifactServiceCreateArtifactProtobuf<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, CreateArtifactRequest, CreateArtifactResponse>[]
) {
let request: CreateArtifactRequest;
let response: CreateArtifactResponse;
try {
request = CreateArtifactRequest.fromBinary(data);
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
CreateArtifactRequest,
CreateArtifactResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.CreateArtifact(ctx, inputReq);
});
} else {
response = await service.CreateArtifact(ctx, request!);
}
return Buffer.from(CreateArtifactResponse.toBinary(response));
}
async function handleArtifactServiceFinalizeArtifactProtobuf<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
FinalizeArtifactRequest,
FinalizeArtifactResponse
>[]
) {
let request: FinalizeArtifactRequest;
let response: FinalizeArtifactResponse;
try {
request = FinalizeArtifactRequest.fromBinary(data);
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
FinalizeArtifactRequest,
FinalizeArtifactResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.FinalizeArtifact(ctx, inputReq);
});
} else {
response = await service.FinalizeArtifact(ctx, request!);
}
return Buffer.from(FinalizeArtifactResponse.toBinary(response));
}
async function handleArtifactServiceListArtifactsProtobuf<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, ListArtifactsRequest, ListArtifactsResponse>[]
) {
let request: ListArtifactsRequest;
let response: ListArtifactsResponse;
try {
request = ListArtifactsRequest.fromBinary(data);
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
ListArtifactsRequest,
ListArtifactsResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.ListArtifacts(ctx, inputReq);
});
} else {
response = await service.ListArtifacts(ctx, request!);
}
return Buffer.from(ListArtifactsResponse.toBinary(response));
}
async function handleArtifactServiceGetSignedArtifactURLProtobuf<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>[]
) {
let request: GetSignedArtifactURLRequest;
let response: GetSignedArtifactURLResponse;
try {
request = GetSignedArtifactURLRequest.fromBinary(data);
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.GetSignedArtifactURL(ctx, inputReq);
});
} else {
response = await service.GetSignedArtifactURL(ctx, request!);
}
return Buffer.from(GetSignedArtifactURLResponse.toBinary(response));
}
async function handleArtifactServiceDeleteArtifactProtobuf<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, DeleteArtifactRequest, DeleteArtifactResponse>[]
) {
let request: DeleteArtifactRequest;
let response: DeleteArtifactResponse;
try {
request = DeleteArtifactRequest.fromBinary(data);
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
DeleteArtifactRequest,
DeleteArtifactResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.DeleteArtifact(ctx, inputReq);
});
} else {
response = await service.DeleteArtifact(ctx, request!);
}
return Buffer.from(DeleteArtifactResponse.toBinary(response));
}
+11 -8
View File
@@ -1,5 +1,5 @@
import {warning} from '@actions/core'
import {isGhes} from './shared/config'
import {isGhes} from './shared/config.js'
import {
UploadArtifactOptions,
UploadArtifactResponse,
@@ -10,19 +10,22 @@ import {
DownloadArtifactResponse,
FindOptions,
DeleteArtifactResponse
} from './shared/interfaces'
import {uploadArtifact} from './upload/upload-artifact'
} from './shared/interfaces.js'
import {uploadArtifact} from './upload/upload-artifact.js'
import {
downloadArtifactPublic,
downloadArtifactInternal
} from './download/download-artifact'
} from './download/download-artifact.js'
import {
deleteArtifactPublic,
deleteArtifactInternal
} from './delete/delete-artifact'
import {getArtifactPublic, getArtifactInternal} from './find/get-artifact'
import {listArtifactsPublic, listArtifactsInternal} from './find/list-artifacts'
import {GHESNotSupportedError} from './shared/errors'
} from './delete/delete-artifact.js'
import {getArtifactPublic, getArtifactInternal} from './find/get-artifact.js'
import {
listArtifactsPublic,
listArtifactsInternal
} from './find/list-artifacts.js'
import {GHESNotSupportedError} from './shared/errors.js'
/**
* Generic interface for the artifact client.
@@ -1,21 +1,21 @@
import {info, debug} from '@actions/core'
import {getOctokit} from '@actions/github'
import {DeleteArtifactResponse} from '../shared/interfaces'
import {getUserAgentString} from '../shared/user-agent'
import {getRetryOptions} from '../find/retry-options'
import {DeleteArtifactResponse} from '../shared/interfaces.js'
import {getUserAgentString} from '../shared/user-agent.js'
import {getRetryOptions} from '../find/retry-options.js'
import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils'
import {requestLog} from '@octokit/plugin-request-log'
import {retry} from '@octokit/plugin-retry'
import {OctokitOptions} from '@octokit/core/dist-types/types'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {getBackendIdsFromToken} from '../shared/util'
import type {OctokitOptions} from '@octokit/core/types'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client.js'
import {getBackendIdsFromToken} from '../shared/util.js'
import {
DeleteArtifactRequest,
ListArtifactsRequest,
StringValue
} from '../../generated'
import {getArtifactPublic} from '../find/get-artifact'
import {ArtifactNotFoundError, InvalidResponseError} from '../shared/errors'
} from '../../generated/index.js'
import {getArtifactPublic} from '../find/get-artifact.js'
import {ArtifactNotFoundError, InvalidResponseError} from '../shared/errors.js'
export async function deleteArtifactPublic(
artifactName: string,
@@ -1,22 +1,28 @@
import fs from 'fs/promises'
import * as fsSync from 'fs'
import * as crypto from 'crypto'
import * as stream from 'stream'
import * as path from 'path'
import * as github from '@actions/github'
import * as core from '@actions/core'
import * as httpClient from '@actions/http-client'
import unzip from 'unzip-stream'
import {
DownloadArtifactOptions,
DownloadArtifactResponse
} from '../shared/interfaces'
import {getUserAgentString} from '../shared/user-agent'
import {getGitHubWorkspaceDir} from '../shared/config'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
DownloadArtifactResponse,
StreamExtractResponse
} from '../shared/interfaces.js'
import {getUserAgentString} from '../shared/user-agent.js'
import {getGitHubWorkspaceDir} from '../shared/config.js'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client.js'
import {
GetSignedArtifactURLRequest,
Int64Value,
ListArtifactsRequest
} from '../../generated'
import {getBackendIdsFromToken} from '../shared/util'
import {ArtifactNotFoundError} from '../shared/errors'
} from '../../generated/index.js'
import {getBackendIdsFromToken} from '../shared/util.js'
import {ArtifactNotFoundError} from '../shared/errors.js'
const scrubQueryParameters = (url: string): string => {
const parsed = new URL(url)
@@ -37,12 +43,15 @@ async function exists(path: string): Promise<boolean> {
}
}
async function streamExtract(url: string, directory: string): Promise<void> {
async function streamExtract(
url: string,
directory: string,
skipDecompress?: boolean
): Promise<StreamExtractResponse> {
let retryCount = 0
while (retryCount < 5) {
try {
await streamExtractExternal(url, directory)
return
return await streamExtractExternal(url, directory, {skipDecompress})
} catch (error) {
retryCount++
core.debug(
@@ -58,8 +67,10 @@ async function streamExtract(url: string, directory: string): Promise<void> {
export async function streamExtractExternal(
url: string,
directory: string
): Promise<void> {
directory: string,
opts: {timeout?: number; skipDecompress?: boolean} = {}
): Promise<StreamExtractResponse> {
const {timeout = 30 * 1000, skipDecompress = false} = opts
const client = new httpClient.HttpClient(getUserAgentString())
const response = await client.get(url)
if (response.message.statusCode !== 200) {
@@ -68,35 +79,97 @@ export async function streamExtractExternal(
)
}
const timeout = 30 * 1000 // 30 seconds
const contentType = response.message.headers['content-type'] || ''
const mimeType = contentType.split(';', 1)[0].trim().toLowerCase()
// Check if the URL path ends with .zip (ignoring query parameters)
const urlPath = new URL(url).pathname.toLowerCase()
const urlEndsWithZip = urlPath.endsWith('.zip')
const isZip =
mimeType === 'application/zip' ||
mimeType === 'application/x-zip-compressed' ||
mimeType === 'application/zip-compressed' ||
urlEndsWithZip
// Extract filename from Content-Disposition header
// Prefer filename* (RFC 5987) which supports UTF-8 encoded filenames,
// fall back to filename which may contain ASCII-only replacements
const contentDisposition =
response.message.headers['content-disposition'] || ''
let fileName = 'artifact'
const filenameStar = contentDisposition.match(
/filename\*\s*=\s*UTF-8''([^;\r\n]*)/i
)
const filenamePlain = contentDisposition.match(
/(?<!\*)filename\s*=\s*['"]?([^;\r\n"']*)['"]?/i
)
const rawName = filenameStar?.[1] || filenamePlain?.[1]
if (rawName) {
// Sanitize fileName to prevent path traversal attacks
// Use path.basename to extract only the filename component
fileName = path.basename(decodeURIComponent(rawName.trim()))
}
core.debug(
`Content-Type: ${contentType}, mimeType: ${mimeType}, urlEndsWithZip: ${urlEndsWithZip}, isZip: ${isZip}, skipDecompress: ${skipDecompress}`
)
core.debug(
`Content-Disposition: ${contentDisposition}, fileName: ${fileName}`
)
let sha256Digest: string | undefined = undefined
return new Promise((resolve, reject) => {
const timerFn = (): void => {
response.message.destroy(
new Error(`Blob storage chunk did not respond in ${timeout}ms`)
const timeoutError = new Error(
`Blob storage chunk did not respond in ${timeout}ms`
)
response.message.destroy(timeoutError)
reject(timeoutError)
}
const timer = setTimeout(timerFn, timeout)
response.message
const onError = (error: Error): void => {
core.debug(`response.message: Artifact download failed: ${error.message}`)
clearTimeout(timer)
reject(error)
}
const hashStream = crypto.createHash('sha256').setEncoding('hex')
const passThrough = new stream.PassThrough()
.on('data', () => {
timer.refresh()
})
.on('error', (error: Error) => {
core.debug(
`response.message: Artifact download failed: ${error.message}`
)
clearTimeout(timer)
reject(error)
})
.pipe(unzip.Extract({path: directory}))
.on('close', () => {
clearTimeout(timer)
resolve()
})
.on('error', (error: Error) => {
reject(error)
})
.on('error', onError)
response.message.pipe(passThrough)
passThrough.pipe(hashStream)
const onClose = (): void => {
clearTimeout(timer)
if (hashStream) {
hashStream.end()
sha256Digest = hashStream.read() as string
core.info(`SHA256 digest of downloaded artifact is ${sha256Digest}`)
}
resolve({sha256Digest: `sha256:${sha256Digest}`})
}
if (isZip && !skipDecompress) {
// Extract zip file
passThrough
.pipe(unzip.Extract({path: directory}))
.on('close', onClose)
.on('error', onError)
} else {
// Save raw file without extracting
const filePath = path.join(directory, fileName)
const writeStream = fsSync.createWriteStream(filePath)
core.info(`Downloading raw file (non-zip) to: ${filePath}`)
passThrough.pipe(writeStream).on('close', onClose).on('error', onError)
}
})
}
@@ -111,6 +184,8 @@ export async function downloadArtifactPublic(
const api = github.getOctokit(token)
let digestMismatch = false
core.info(
`Downloading artifact '${artifactId}' from '${repositoryOwner}/${repositoryName}'`
)
@@ -140,13 +215,24 @@ export async function downloadArtifactPublic(
try {
core.info(`Starting download of artifact to: ${downloadPath}`)
await streamExtract(location, downloadPath)
const extractResponse = await streamExtract(
location,
downloadPath,
options?.skipDecompress
)
core.info(`Artifact download completed successfully.`)
if (options?.expectedHash) {
if (options?.expectedHash !== extractResponse.sha256Digest) {
digestMismatch = true
core.debug(`Computed digest: ${extractResponse.sha256Digest}`)
core.debug(`Expected digest: ${options.expectedHash}`)
}
}
} catch (error) {
throw new Error(`Unable to download and extract artifact: ${error.message}`)
}
return {downloadPath}
return {downloadPath, digestMismatch}
}
export async function downloadArtifactInternal(
@@ -157,6 +243,8 @@ export async function downloadArtifactInternal(
const artifactClient = internalArtifactTwirpClient()
let digestMismatch = false
const {workflowRunBackendId, workflowJobRunBackendId} =
getBackendIdsFromToken()
@@ -192,13 +280,24 @@ export async function downloadArtifactInternal(
try {
core.info(`Starting download of artifact to: ${downloadPath}`)
await streamExtract(signedUrl, downloadPath)
const extractResponse = await streamExtract(
signedUrl,
downloadPath,
options?.skipDecompress
)
core.info(`Artifact download completed successfully.`)
if (options?.expectedHash) {
if (options?.expectedHash !== extractResponse.sha256Digest) {
digestMismatch = true
core.debug(`Computed digest: ${extractResponse.sha256Digest}`)
core.debug(`Expected digest: ${options.expectedHash}`)
}
}
} catch (error) {
throw new Error(`Unable to download and extract artifact: ${error.message}`)
}
return {downloadPath}
return {downloadPath, digestMismatch}
}
async function resolveOrCreateDirectory(
@@ -1,16 +1,20 @@
import {getOctokit} from '@actions/github'
import {retry} from '@octokit/plugin-retry'
import * as core from '@actions/core'
import {OctokitOptions} from '@octokit/core/dist-types/types'
import type {OctokitOptions} from '@octokit/core/types'
import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils'
import {getRetryOptions} from './retry-options'
import {getRetryOptions} from './retry-options.js'
import {requestLog} from '@octokit/plugin-request-log'
import {GetArtifactResponse} from '../shared/interfaces'
import {getBackendIdsFromToken} from '../shared/util'
import {getUserAgentString} from '../shared/user-agent'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {ListArtifactsRequest, StringValue, Timestamp} from '../../generated'
import {ArtifactNotFoundError, InvalidResponseError} from '../shared/errors'
import {GetArtifactResponse} from '../shared/interfaces.js'
import {getBackendIdsFromToken} from '../shared/util.js'
import {getUserAgentString} from '../shared/user-agent.js'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client.js'
import {
ListArtifactsRequest,
StringValue,
Timestamp
} from '../../generated/index.js'
import {ArtifactNotFoundError, InvalidResponseError} from '../shared/errors.js'
export async function getArtifactPublic(
artifactName: string,
@@ -68,7 +72,10 @@ export async function getArtifactPublic(
name: artifact.name,
id: artifact.id,
size: artifact.size_in_bytes,
createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined
createdAt: artifact.created_at
? new Date(artifact.created_at)
: undefined,
digest: artifact.digest
}
}
}
@@ -115,7 +122,8 @@ export async function getArtifactInternal(
size: Number(artifact.size),
createdAt: artifact.createdAt
? Timestamp.toDate(artifact.createdAt)
: undefined
: undefined,
digest: artifact.digest?.value
}
}
}
@@ -1,20 +1,20 @@
import {info, warning, debug} from '@actions/core'
import {getOctokit} from '@actions/github'
import {ListArtifactsResponse, Artifact} from '../shared/interfaces'
import {getUserAgentString} from '../shared/user-agent'
import {getRetryOptions} from './retry-options'
import {ListArtifactsResponse, Artifact} from '../shared/interfaces.js'
import {getUserAgentString} from '../shared/user-agent.js'
import {getRetryOptions} from './retry-options.js'
import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils'
import {requestLog} from '@octokit/plugin-request-log'
import {retry} from '@octokit/plugin-retry'
import {OctokitOptions} from '@octokit/core/dist-types/types'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {getBackendIdsFromToken} from '../shared/util'
import {ListArtifactsRequest, Timestamp} from '../../generated'
import type {OctokitOptions} from '@octokit/core/types'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client.js'
import {getBackendIdsFromToken} from '../shared/util.js'
import {getMaxArtifactListCount} from '../shared/config.js'
import {ListArtifactsRequest, Timestamp} from '../../generated/index.js'
// Limiting to 1000 for perf reasons
const maximumArtifactCount = 1000
const maximumArtifactCount = getMaxArtifactListCount()
const paginationCount = 100
const maxNumberOfPages = maximumArtifactCount / paginationCount
const maxNumberOfPages = Math.ceil(maximumArtifactCount / paginationCount)
export async function listArtifactsPublic(
workflowRunId: number,
@@ -41,14 +41,17 @@ export async function listArtifactsPublic(
const github = getOctokit(token, opts, retry, requestLog)
let currentPageNumber = 1
const {data: listArtifactResponse} =
await github.rest.actions.listWorkflowRunArtifacts({
const {data: listArtifactResponse} = await github.request(
'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts',
{
owner: repositoryOwner,
repo: repositoryName,
run_id: workflowRunId,
per_page: paginationCount,
page: currentPageNumber
})
}
)
let numberOfPages = Math.ceil(
listArtifactResponse.total_count / paginationCount
@@ -56,7 +59,7 @@ export async function listArtifactsPublic(
const totalArtifactCount = listArtifactResponse.total_count
if (totalArtifactCount > maximumArtifactCount) {
warning(
`Workflow run ${workflowRunId} has more than 1000 artifacts. Results will be incomplete as only the first ${maximumArtifactCount} artifacts will be returned`
`Workflow run ${workflowRunId} has ${totalArtifactCount} artifacts, exceeding the limit of ${maximumArtifactCount}. Results will be incomplete as only the first ${maximumArtifactCount} artifacts will be returned`
)
numberOfPages = maxNumberOfPages
}
@@ -67,27 +70,32 @@ export async function listArtifactsPublic(
name: artifact.name,
id: artifact.id,
size: artifact.size_in_bytes,
createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined
createdAt: artifact.created_at
? new Date(artifact.created_at)
: undefined,
digest: (artifact as ArtifactResponse).digest
})
}
// Move to the next page
currentPageNumber++
// Iterate over any remaining pages
for (
currentPageNumber;
currentPageNumber < numberOfPages;
currentPageNumber <= numberOfPages;
currentPageNumber++
) {
currentPageNumber++
debug(`Fetching page ${currentPageNumber} of artifact list`)
const {data: listArtifactResponse} =
await github.rest.actions.listWorkflowRunArtifacts({
const {data: listArtifactResponse} = await github.request(
'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts',
{
owner: repositoryOwner,
repo: repositoryName,
run_id: workflowRunId,
per_page: paginationCount,
page: currentPageNumber
})
}
)
for (const artifact of listArtifactResponse.artifacts) {
artifacts.push({
@@ -96,7 +104,8 @@ export async function listArtifactsPublic(
size: artifact.size_in_bytes,
createdAt: artifact.created_at
? new Date(artifact.created_at)
: undefined
: undefined,
digest: (artifact as ArtifactResponse).digest
})
}
}
@@ -132,7 +141,8 @@ export async function listArtifactsInternal(
size: Number(artifact.size),
createdAt: artifact.createdAt
? Timestamp.toDate(artifact.createdAt)
: undefined
: undefined,
digest: artifact.digest?.value
}))
if (latest) {
@@ -146,6 +156,18 @@ export async function listArtifactsInternal(
}
}
/**
* This exists so that we don't have to use 'any' when receiving the artifact list from the GitHub API.
* The digest field is not present in OpenAPI/types at time of writing, which necessitates this change.
*/
interface ArtifactResponse {
name: string
id: number
size_in_bytes: number
created_at?: string
digest?: string
}
/**
* Filters a list of artifacts to only include the latest artifact for each name
* @param artifacts The artifacts to filter
@@ -1,5 +1,5 @@
import * as core from '@actions/core'
import {OctokitOptions} from '@octokit/core/dist-types/types'
import type {OctokitOptions} from '@octokit/core/types'
import {RequestRequestOptions} from '@octokit/types'
export type RetryOptions = {
@@ -1,10 +1,11 @@
import {HttpClient, HttpClientResponse, HttpCodes} from '@actions/http-client'
import {BearerCredentialHandler} from '@actions/http-client/lib/auth'
import {info, debug} from '@actions/core'
import {ArtifactServiceClientJSON} from '../../generated'
import {getResultsServiceUrl, getRuntimeToken} from './config'
import {getUserAgentString} from './user-agent'
import {NetworkError, UsageError} from './errors'
import {ArtifactServiceClientJSON} from '../../generated/index.js'
import {getResultsServiceUrl, getRuntimeToken} from './config.js'
import {getUserAgentString} from './user-agent.js'
import {NetworkError, UsageError} from './errors.js'
import {maskSecretUrls} from './util.js'
// The twirp http client must implement this interface
interface Rpc {
@@ -86,6 +87,7 @@ class ArtifactHttpClient implements Rpc {
debug(`[Response] - ${response.message.statusCode}`)
debug(`Headers: ${JSON.stringify(response.message.headers, null, 2)}`)
const body = JSON.parse(rawBody)
maskSecretUrls(body)
debug(`Body: ${JSON.stringify(body, null, 2)}`)
if (this.isSuccessStatusCode(statusCode)) {
return {response, body}
@@ -1,4 +1,5 @@
import os from 'os'
import {info} from '@actions/core'
// Used for controlling the highWaterMark value of the zip that is being streamed
// The same value is used as the chunk size that is use during upload to blob storage
@@ -44,20 +45,71 @@ export function getGitHubWorkspaceDir(): string {
return ghWorkspaceDir
}
// Mimics behavior of azcopy: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-optimize
// If your machine has fewer than 5 CPUs, then the value of this variable is set to 32.
// Otherwise, the default value is equal to 16 multiplied by the number of CPUs. The maximum value of this variable is 300.
// The maximum value of concurrency is 300.
// This value can be changed with ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY variable.
export function getConcurrency(): number {
const numCPUs = os.cpus().length
let concurrencyCap = 32
if (numCPUs <= 4) {
return 32
if (numCPUs > 4) {
const concurrency = 16 * numCPUs
concurrencyCap = concurrency > 300 ? 300 : concurrency
}
const concurrency = 16 * numCPUs
return concurrency > 300 ? 300 : concurrency
const concurrencyOverride = process.env['ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY']
if (concurrencyOverride) {
const concurrency = parseInt(concurrencyOverride)
if (isNaN(concurrency) || concurrency < 1) {
throw new Error(
'Invalid value set for ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY env variable'
)
}
if (concurrency < concurrencyCap) {
info(
`Set concurrency based on the value set in ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY.`
)
return concurrency
}
info(
`ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY is higher than the cap of ${concurrencyCap} based on the number of cpus. Set it to the maximum value allowed.`
)
return concurrencyCap
}
// default concurrency to 5
return 5
}
export function getUploadChunkTimeout(): number {
return 300_000 // 5 minutes
const timeoutVar = process.env['ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS']
if (!timeoutVar) {
return 300000 // 5 minutes
}
const timeout = parseInt(timeoutVar)
if (isNaN(timeout)) {
throw new Error(
'Invalid value set for ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS env variable'
)
}
return timeout
}
// This value can be changed with ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT variable.
// Defaults to 1000 as a safeguard for rate limiting.
export function getMaxArtifactListCount(): number {
const maxCountVar =
process.env['ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT'] || '1000'
const maxCount = parseInt(maxCountVar)
if (isNaN(maxCount) || maxCount < 1) {
throw new Error(
'Invalid value set for ACTIONS_ARTIFACT_MAX_ARTIFACT_COUNT env variable'
)
}
return maxCount
}
@@ -60,7 +60,7 @@ export class NetworkError extends Error {
export class UsageError extends Error {
constructor() {
const message = `Artifact storage quota has been hit. Unable to upload any new artifacts. Usage is recalculated every 6-12 hours.\nMore info on storage limits: https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#calculating-minute-and-storage-spending`
const message = `Artifact storage quota has been hit. Unable to upload any new artifacts.\nMore info on storage limits: https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#calculating-minute-and-storage-spending`
super(message)
this.name = 'UsageError'
}
@@ -12,6 +12,11 @@ export interface UploadArtifactResponse {
* This ID can be used as input to other APIs to download, delete or get more information about an artifact: https://docs.github.com/en/rest/actions/artifacts
*/
id?: number
/**
* The SHA256 digest of the artifact that was created. Not provided if no artifact was uploaded
*/
digest?: string
}
/**
@@ -45,6 +50,13 @@ export interface UploadArtifactOptions {
* For large files that are not easily compressed, a value of 0 is recommended for significantly faster uploads.
*/
compressionLevel?: number
/**
* If true, the artifact will be uploaded without being archived (zipped).
* This is only supported when uploading a single file.
* When using this option, the artifact will not be compressed.
* When using this option, the name parameter passed to the upload is ignored. Instead, the name of the file is used as the name of the artifact.
*/
skipArchive?: boolean
}
/**
@@ -86,6 +98,11 @@ export interface DownloadArtifactResponse {
* The path where the artifact was downloaded to
*/
downloadPath?: string
/**
* Returns true if the digest of the downloaded artifact does not match the expected hash
*/
digestMismatch?: boolean
}
/**
@@ -96,6 +113,26 @@ export interface DownloadArtifactOptions {
* Denotes where the artifact will be downloaded to. If not specified then the artifact is download to GITHUB_WORKSPACE
*/
path?: string
/**
* The hash that was computed for the artifact during upload. If provided, the outcome of the download
* will provide a digestMismatch property indicating whether the hash of the downloaded artifact
* matches the expected hash.
*/
expectedHash?: string
/**
* If true, the downloaded artifact will not be automatically extracted/decompressed.
* The artifact will be saved as-is to the destination path.
*/
skipDecompress?: boolean
}
export interface StreamExtractResponse {
/**
* The SHA256 hash of the downloaded file
*/
sha256Digest?: string
}
/**
@@ -121,6 +158,11 @@ export interface Artifact {
* The time when the artifact was created
*/
createdAt?: Date
/**
* The digest of the artifact, computed at time of upload.
*/
digest?: string
}
// FindOptions are for fetching Artifact(s) out of the scope of the current run.
@@ -0,0 +1,7 @@
// This file exists as a CommonJS module to read the version from package.json.
// In an ESM package, using `require()` directly in .ts files requires disabling
// ESLint rules and doesn't work reliably across all Node.js versions.
// By keeping this as a .cjs file, we can use require() naturally and export
// the version for the ESM modules to import.
const packageJson = require('../../../package.json')
module.exports = { version: packageJson.version }
@@ -1,9 +1,8 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const packageJson = require('../../../package.json')
import {version} from './package-version.cjs'
/**
* Ensure that this User Agent String is used in all HTTP calls so that we can monitor telemetry between different versions of this package
*/
export function getUserAgentString(): string {
return `@actions/artifact-${packageJson.version}`
return `@actions/artifact-${version}`
}
+77 -3
View File
@@ -1,6 +1,7 @@
import * as core from '@actions/core'
import {getRuntimeToken} from './config'
import jwt_decode from 'jwt-decode'
import {getRuntimeToken} from './config.js'
import {jwtDecode} from 'jwt-decode'
import {debug, setSecret} from '@actions/core'
export interface BackendIds {
workflowRunBackendId: string
@@ -19,7 +20,7 @@ const InvalidJwtError = new Error(
// workflow run and workflow job run backend ids
export function getBackendIdsFromToken(): BackendIds {
const token = getRuntimeToken()
const decoded = jwt_decode<ActionsToken>(token)
const decoded = jwtDecode<ActionsToken>(token)
if (!decoded.scp) {
throw InvalidJwtError
}
@@ -69,3 +70,76 @@ export function getBackendIdsFromToken(): BackendIds {
throw InvalidJwtError
}
/**
* Masks the `sig` parameter in a URL and sets it as a secret.
*
* @param url - The URL containing the signature parameter to mask
* @remarks
* This function attempts to parse the provided URL and identify the 'sig' query parameter.
* If found, it registers both the raw and URL-encoded signature values as secrets using
* the Actions `setSecret` API, which prevents them from being displayed in logs.
*
* The function handles errors gracefully if URL parsing fails, logging them as debug messages.
*
* @example
* ```typescript
* // Mask a signature in an Azure SAS token URL
* maskSigUrl('https://example.blob.core.windows.net/container/file.txt?sig=abc123&se=2023-01-01');
* ```
*/
export function maskSigUrl(url: string): void {
if (!url) return
try {
const parsedUrl = new URL(url)
const signature = parsedUrl.searchParams.get('sig')
if (signature) {
setSecret(signature)
setSecret(encodeURIComponent(signature))
}
} catch (error) {
debug(
`Failed to parse URL: ${url} ${
error instanceof Error ? error.message : String(error)
}`
)
}
}
/**
* Masks sensitive information in URLs containing signature parameters.
* Currently supports masking 'sig' parameters in the 'signed_upload_url'
* and 'signed_download_url' properties of the provided object.
*
* @param body - The object should contain a signature
* @remarks
* This function extracts URLs from the object properties and calls maskSigUrl
* on each one to redact sensitive signature information. The function doesn't
* modify the original object; it only marks the signatures as secrets for
* logging purposes.
*
* @example
* ```typescript
* const responseBody = {
* signed_upload_url: 'https://example.com?sig=abc123',
* signed_download_url: 'https://example.com?sig=def456'
* };
* maskSecretUrls(responseBody);
* ```
*/
export function maskSecretUrls(body: Record<string, unknown> | null): void {
if (typeof body !== 'object' || body === null) {
debug('body is not an object or is null')
return
}
if (
'signed_upload_url' in body &&
typeof body.signed_upload_url === 'string'
) {
maskSigUrl(body.signed_upload_url)
}
if ('signed_url' in body && typeof body.signed_url === 'string') {
maskSigUrl(body.signed_url)
}
}
@@ -1,15 +1,15 @@
import {BlobClient, BlockBlobUploadStreamOptions} from '@azure/storage-blob'
import {TransferProgressEvent} from '@azure/core-http'
import {ZipUploadStream} from './zip'
import {TransferProgressEvent} from '@azure/core-http-compat'
import {WaterMarkedUploadStream} from './stream.js'
import {
getUploadChunkSize,
getConcurrency,
getUploadChunkTimeout
} from '../shared/config'
} from '../shared/config.js'
import * as core from '@actions/core'
import * as crypto from 'crypto'
import * as stream from 'stream'
import {NetworkError} from '../shared/errors'
import {NetworkError} from '../shared/errors.js'
export interface BlobUploadResponse {
/**
@@ -23,9 +23,10 @@ export interface BlobUploadResponse {
sha256Hash?: string
}
export async function uploadZipToBlobStorage(
export async function uploadToBlobStorage(
authenticatedUploadURL: string,
zipUploadStream: ZipUploadStream
uploadStream: WaterMarkedUploadStream,
contentType: string
): Promise<BlobUploadResponse> {
let uploadByteCount = 0
let lastProgressTime = Date.now()
@@ -51,7 +52,7 @@ export async function uploadZipToBlobStorage(
const blockBlobClient = blobClient.getBlockBlobClient()
core.debug(
`Uploading artifact zip to blob storage with maxConcurrency: ${maxConcurrency}, bufferSize: ${bufferSize}`
`Uploading artifact to blob storage with maxConcurrency: ${maxConcurrency}, bufferSize: ${bufferSize}, contentType: ${contentType}`
)
const uploadCallback = (progress: TransferProgressEvent): void => {
@@ -61,24 +62,24 @@ export async function uploadZipToBlobStorage(
}
const options: BlockBlobUploadStreamOptions = {
blobHTTPHeaders: {blobContentType: 'zip'},
blobHTTPHeaders: {blobContentType: contentType},
onProgress: uploadCallback,
abortSignal: abortController.signal
}
let sha256Hash: string | undefined = undefined
const uploadStream = new stream.PassThrough()
const blobUploadStream = new stream.PassThrough()
const hashStream = crypto.createHash('sha256')
zipUploadStream.pipe(uploadStream) // This stream is used for the upload
zipUploadStream.pipe(hashStream).setEncoding('hex') // This stream is used to compute a hash of the zip content that gets used. Integrity check
uploadStream.pipe(blobUploadStream) // This stream is used for the upload
uploadStream.pipe(hashStream).setEncoding('hex') // This stream is used to compute a hash of the content for integrity check
core.info('Beginning upload of artifact content to blob storage')
try {
await Promise.race([
blockBlobClient.uploadStream(
uploadStream,
blobUploadStream,
bufferSize,
maxConcurrency,
options
@@ -98,7 +99,7 @@ export async function uploadZipToBlobStorage(
hashStream.end()
sha256Hash = hashStream.read() as string
core.info(`SHA256 hash of uploaded artifact zip is ${sha256Hash}`)
core.info(`SHA256 digest of uploaded artifact is ${sha256Hash}`)
if (uploadByteCount === 0) {
core.warning(
@@ -1,4 +1,4 @@
import {Timestamp} from '../../generated'
import {Timestamp} from '../../generated/index.js'
import * as core from '@actions/core'
export function getExpiration(retentionDays?: number): Timestamp | undefined {
@@ -0,0 +1,53 @@
import * as stream from 'stream'
import * as fs from 'fs'
import {realpath} from 'fs/promises'
import * as core from '@actions/core'
import {getUploadChunkSize} from '../shared/config.js'
// Custom stream transformer so we can set the highWaterMark property
// See https://github.com/nodejs/node/issues/8855
export class WaterMarkedUploadStream extends stream.Transform {
constructor(bufferSize: number) {
super({
highWaterMark: bufferSize
})
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_transform(chunk: any, enc: any, cb: any): void {
cb(null, chunk)
}
}
export async function createRawFileUploadStream(
filePath: string
): Promise<WaterMarkedUploadStream> {
core.debug(`Creating raw file upload stream for: ${filePath}`)
const bufferSize = getUploadChunkSize()
const uploadStream = new WaterMarkedUploadStream(bufferSize)
// Check if symlink and resolve the source path
let sourcePath = filePath
const stats = await fs.promises.lstat(filePath)
if (stats.isSymbolicLink()) {
sourcePath = await realpath(filePath)
}
// Create a read stream from the file and pipe it to the upload stream
const fileStream = fs.createReadStream(sourcePath, {
highWaterMark: bufferSize
})
fileStream.on('error', error => {
core.error('An error has occurred while reading the file for upload')
core.error(String(error))
uploadStream.destroy(
new Error('An error has occurred during file read for the artifact')
)
})
fileStream.pipe(uploadStream)
return uploadStream
}
@@ -0,0 +1,82 @@
import * as path from 'path'
/**
* Maps file extensions to MIME types
*/
const mimeTypes: Record<string, string> = {
// Text
'.txt': 'text/plain',
'.html': 'text/html',
'.htm': 'text/html',
'.css': 'text/css',
'.csv': 'text/csv',
'.xml': 'text/xml',
'.md': 'text/markdown',
// JavaScript/JSON
'.js': 'application/javascript',
'.mjs': 'application/javascript',
'.json': 'application/json',
// Images
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.webp': 'image/webp',
'.ico': 'image/x-icon',
'.bmp': 'image/bmp',
'.tiff': 'image/tiff',
'.tif': 'image/tiff',
// Audio
'.mp3': 'audio/mpeg',
'.wav': 'audio/wav',
'.ogg': 'audio/ogg',
'.flac': 'audio/flac',
// Video
'.mp4': 'video/mp4',
'.webm': 'video/webm',
'.avi': 'video/x-msvideo',
'.mov': 'video/quicktime',
// Documents
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.docx':
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.xls': 'application/vnd.ms-excel',
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'.ppt': 'application/vnd.ms-powerpoint',
'.pptx':
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
// Archives
'.zip': 'application/zip',
'.tar': 'application/x-tar',
'.gz': 'application/gzip',
'.rar': 'application/vnd.rar',
'.7z': 'application/x-7z-compressed',
// Code/Data
'.wasm': 'application/wasm',
'.yaml': 'application/x-yaml',
'.yml': 'application/x-yaml',
// Fonts
'.woff': 'font/woff',
'.woff2': 'font/woff2',
'.ttf': 'font/ttf',
'.otf': 'font/otf',
'.eot': 'application/vnd.ms-fontobject'
}
/**
* Gets the MIME type for a file based on its extension
*/
export function getMimeType(filePath: string): string {
const ext = path.extname(filePath).toLowerCase()
return mimeTypes[ext] || 'application/octet-stream'
}
@@ -1,25 +1,29 @@
import * as core from '@actions/core'
import * as fs from 'fs'
import * as path from 'path'
import {
UploadArtifactOptions,
UploadArtifactResponse
} from '../shared/interfaces'
import {getExpiration} from './retention'
import {validateArtifactName} from './path-and-artifact-name-validation'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
} from '../shared/interfaces.js'
import {getExpiration} from './retention.js'
import {validateArtifactName} from './path-and-artifact-name-validation.js'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client.js'
import {
UploadZipSpecification,
getUploadZipSpecification,
validateRootDirectory
} from './upload-zip-specification'
import {getBackendIdsFromToken} from '../shared/util'
import {uploadZipToBlobStorage} from './blob-upload'
import {createZipUploadStream} from './zip'
} from './upload-zip-specification.js'
import {getBackendIdsFromToken} from '../shared/util.js'
import {uploadToBlobStorage} from './blob-upload.js'
import {createZipUploadStream} from './zip.js'
import {createRawFileUploadStream, WaterMarkedUploadStream} from './stream.js'
import {
CreateArtifactRequest,
FinalizeArtifactRequest,
StringValue
} from '../../generated'
import {FilesNotFoundError, InvalidResponseError} from '../shared/errors'
} from '../../generated/index.js'
import {FilesNotFoundError, InvalidResponseError} from '../shared/errors.js'
import {getMimeType} from './types.js'
export async function uploadArtifact(
name: string,
@@ -27,19 +31,43 @@ export async function uploadArtifact(
rootDirectory: string,
options?: UploadArtifactOptions | undefined
): Promise<UploadArtifactResponse> {
let artifactFileName = `${name}.zip`
if (options?.skipArchive) {
if (files.length === 0) {
throw new FilesNotFoundError([])
}
if (files.length > 1) {
throw new Error(
'skipArchive option is only supported when uploading a single file'
)
}
if (!fs.existsSync(files[0])) {
throw new FilesNotFoundError(files)
}
artifactFileName = path.basename(files[0])
name = artifactFileName
}
validateArtifactName(name)
validateRootDirectory(rootDirectory)
const zipSpecification: UploadZipSpecification[] = getUploadZipSpecification(
files,
rootDirectory
)
if (zipSpecification.length === 0) {
throw new FilesNotFoundError(
zipSpecification.flatMap(s => (s.sourcePath ? [s.sourcePath] : []))
)
let zipSpecification: UploadZipSpecification[] = []
if (!options?.skipArchive) {
zipSpecification = getUploadZipSpecification(files, rootDirectory)
if (zipSpecification.length === 0) {
throw new FilesNotFoundError(
zipSpecification.flatMap(s => (s.sourcePath ? [s.sourcePath] : []))
)
}
}
const contentType = getMimeType(artifactFileName)
// get the IDs needed for the artifact creation
const backendIds = getBackendIdsFromToken()
@@ -51,7 +79,8 @@ export async function uploadArtifact(
workflowRunBackendId: backendIds.workflowRunBackendId,
workflowJobRunBackendId: backendIds.workflowJobRunBackendId,
name,
version: 4
mimeType: StringValue.create({value: contentType}),
version: 7
}
// if there is a retention period, add it to the request
@@ -68,15 +97,24 @@ export async function uploadArtifact(
)
}
const zipUploadStream = await createZipUploadStream(
zipSpecification,
options?.compressionLevel
)
let stream: WaterMarkedUploadStream
// Upload zip to blob storage
const uploadResult = await uploadZipToBlobStorage(
if (options?.skipArchive) {
// Upload raw file without archiving
stream = await createRawFileUploadStream(files[0])
} else {
// Create and upload zip archive
stream = await createZipUploadStream(
zipSpecification,
options?.compressionLevel
)
}
core.info(`Uploading artifact: ${artifactFileName}`)
const uploadResult = await uploadToBlobStorage(
createArtifactResp.signedUploadUrl,
zipUploadStream
stream,
contentType
)
// finalize the artifact
@@ -105,11 +143,12 @@ export async function uploadArtifact(
const artifactId = BigInt(finalizeArtifactResp.artifactId)
core.info(
`Artifact ${name}.zip successfully finalized. Artifact ID ${artifactId}`
`Artifact ${name} successfully finalized. Artifact ID ${artifactId}`
)
return {
size: uploadResult.uploadSize,
digest: uploadResult.sha256Hash,
id: Number(artifactId)
}
}
@@ -1,7 +1,7 @@
import * as fs from 'fs'
import {info} from '@actions/core'
import {normalize, resolve} from 'path'
import {validateFilePath} from './path-and-artifact-name-validation'
import {validateFilePath} from './path-and-artifact-name-validation.js'
export interface UploadZipSpecification {
/**
+6 -21
View File
@@ -1,31 +1,16 @@
import * as stream from 'stream'
import {realpath} from 'fs/promises'
import * as archiver from 'archiver'
import archiver from 'archiver'
import * as core from '@actions/core'
import {UploadZipSpecification} from './upload-zip-specification'
import {getUploadChunkSize} from '../shared/config'
import {UploadZipSpecification} from './upload-zip-specification.js'
import {getUploadChunkSize} from '../shared/config.js'
import {WaterMarkedUploadStream} from './stream.js'
export const DEFAULT_COMPRESSION_LEVEL = 6
// Custom stream transformer so we can set the highWaterMark property
// See https://github.com/nodejs/node/issues/8855
export class ZipUploadStream extends stream.Transform {
constructor(bufferSize: number) {
super({
highWaterMark: bufferSize
})
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_transform(chunk: any, enc: any, cb: any): void {
cb(null, chunk)
}
}
export async function createZipUploadStream(
uploadSpecification: UploadZipSpecification[],
compressionLevel: number = DEFAULT_COMPRESSION_LEVEL
): Promise<ZipUploadStream> {
): Promise<WaterMarkedUploadStream> {
core.debug(
`Creating Artifact archive with compressionLevel: ${compressionLevel}`
)
@@ -60,7 +45,7 @@ export async function createZipUploadStream(
}
const bufferSize = getUploadChunkSize()
const zipUploadStream = new ZipUploadStream(bufferSize)
const zipUploadStream = new WaterMarkedUploadStream(bufferSize)
core.debug(
`Zip write high watermark value ${zipUploadStream.writableHighWaterMark}`
+2
View File
@@ -4,6 +4,8 @@
"baseUrl": "./",
"outDir": "./lib",
"rootDir": "./src",
"module": "node16",
"moduleResolution": "node16",
"paths": {
"@actions/core": [
"../core"
+76
View File
@@ -15,6 +15,14 @@ initiated.
See [Using artifact attestations to establish provenance for builds](https://docs.github.com/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)
for more information on artifact attestations.
## Table of Contents
- [Usage](#usage)
- [attest](#attest)
- [attestProvenance](#attestprovenance)
- [Attestation](#attestation)
- [Sigstore Instance](#sigstore-instance)
- [Storage](#storage)
## Usage
### `attest`
@@ -165,6 +173,74 @@ export type Attestation = {
For details about the Sigstore bundle format, see the [Bundle protobuf
specification](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto).
### createStorageRecord
The `createStorageRecord` function creates an
[artifact metadata storage record](https://docs.github.com/en/rest/orgs/artifact-metadata?apiVersion=2022-11-28#create-artifact-metadata-storage-record)
on behalf of an attested artifact. It accepts parameters defining artifact
and package registry details. The storage record contains metadata about where the artifact is stored on a given package registry.
```js
const { createStorageRecord } = require('@actions/attest');
const core = require('@actions/core');
async function run() {
// In order to persist attestations to the repo, this should be a token with
// repository write permissions.
const ghToken = core.getInput('gh-token');
const record = await createStorageRecord(
artifactOptions: {
name: 'my-artifact-name',
digest: { 'sha256': '36ab4667...'},
version: "v1.0.0"
},
packageRegistryOptions: {
registryUrl: "https://my-fave-pkg-registry.com"
},
token: ghToken
);
console.log(record);
}
run();
```
The `createStorageRecord` function supports the following options:
```typescript
// Artifact details to associate the record with
export type ArtifactOptions = {
// The name of the artifact
name: string
// The digest of the artifact
digest: string
// The version of the artifact
version?: string
// The status of the artifact
status?: string
}
// Includes details about the package registry the artifact was published to
export type PackageRegistryOptions = {
// The URL of the package registry
registryUrl: string
// The URL of the artifact in the package registry
artifactUrl?: string
// The package registry repository the artifact was published to.
repo?: string
// The path of the artifact in the package registry repository.
path?: string
}
// GitHub token for writing attestations.
token: string
// Optional parameters for the write operation.
// The number of times to retry the request.
retryAttempts?: number
// HTTP headers to include in request to Artifact Metadata API.
headers?: RequestHeaders
```
## Sigstore Instance
When generating the signed attestation there are two different Sigstore
+48 -10
View File
@@ -1,6 +1,43 @@
# @actions/attest Releases
### 1.5.0
## 3.2.0
- Add custom user-agent for more API calls [#2321](https://github.com/actions/toolkit/pull/2321)
## 3.1.0
- Add support for `ACTIONS_ORCHESTRATION_ID` in user-agent [#2320](https://github.com/actions/toolkit/pull/2320)
## 3.0.0
- **Breaking change**: Package is now ESM-only
- CommonJS consumers must use dynamic `import()` instead of `require()`
- Bump `@actions/core` to `^3.0.0`
- Bump `@actions/http-client` to `^4.0.0`
## 2.2.1
- Bump `@actions/http-client` to `3.0.2`
- Bump `undici` to `6.23.0`
## 2.2.0
- Bump @actions/core from 1.11.1 to 2.0.2
- Bump @actions/github from 6.0.0 to 7.0.0
- Bump @actions/http-client from 2.2.3 to 3.0.1
## 2.0.0
- Add support for Node 24 [#2110](https://github.com/actions/toolkit/pull/2110)
- Bump @sigstore/bundle from 3.0.0 to 3.1.0
- Bump @sigstore/sign from 3.0.0 to 3.1.0
- Bump jose from 5.2.3 to 5.10.0
## 1.6.0
- Update `buildSLSAProvenancePredicate` to populate `workflow.ref` field from the `ref` claim in the OIDC token [#1969](https://github.com/actions/toolkit/pull/1969)
## 1.5.0
- Bump @actions/core from 1.10.1 to 1.11.1 [#1847](https://github.com/actions/toolkit/pull/1847)
- Bump @sigstore/bundle from 2.3.2 to 3.0.0 [#1846](https://github.com/actions/toolkit/pull/1846)
@@ -8,23 +45,24 @@
- Support for generating multi-subject attestations [#1864](https://github.com/actions/toolkit/pull/1865)
- Fix bug in `buildSLSAProvenancePredicate` related to `workflow_ref` OIDC token claims containing the "@" symbol in the tag name [#1863](https://github.com/actions/toolkit/pull/1863)
### 1.4.2
## 1.4.2
- Fix bug in `buildSLSAProvenancePredicate`/`attestProvenance` when generating provenance statement for enterprise account using customized OIDC issuer value [#1823](https://github.com/actions/toolkit/pull/1823)
### 1.4.1
## 1.4.1
- Bump @actions/http-client from 2.2.1 to 2.2.3 [#1805](https://github.com/actions/toolkit/pull/1805)
### 1.4.0
## 1.4.0
- Add new `headers` parameter to the `attest` and `attestProvenance` functions [#1790](https://github.com/actions/toolkit/pull/1790)
- Update `buildSLSAProvenancePredicate`/`attestProvenance` to automatically derive default OIDC issuer URL from current execution context [#1796](https://github.com/actions/toolkit/pull/1796)
### 1.3.1
## 1.3.1
- Fix bug with proxy support when retrieving JWKS for OIDC issuer [#1776](https://github.com/actions/toolkit/pull/1776)
### 1.3.0
## 1.3.0
- Dynamic construction of Sigstore API URLs [#1735](https://github.com/actions/toolkit/pull/1735)
- Switch to new GH provenance build type [#1745](https://github.com/actions/toolkit/pull/1745)
@@ -32,21 +70,21 @@
- Bump @sigstore/bundle from 2.3.0 to 2.3.2 [#1738](https://github.com/actions/toolkit/pull/1738)
- Bump @sigstore/sign from 2.3.0 to 2.3.2 [#1738](https://github.com/actions/toolkit/pull/1738)
### 1.2.1
## 1.2.1
- Retry request on attestation persistence failure [#1725](https://github.com/actions/toolkit/pull/1725)
### 1.2.0
## 1.2.0
- Generate attestations using the v0.3 Sigstore bundle format [#1701](https://github.com/actions/toolkit/pull/1701)
- Bump @sigstore/bundle from 2.2.0 to 2.3.0 [#1701](https://github.com/actions/toolkit/pull/1701)
- Bump @sigstore/sign from 2.2.3 to 2.3.0 [#1701](https://github.com/actions/toolkit/pull/1701)
- Remove dependency on make-fetch-happen [#1714](https://github.com/actions/toolkit/pull/1714)
### 1.1.0
## 1.1.0
- Updates the `attestProvenance` function to retrieve a token from the GitHub OIDC provider and use the token claims to populate the provenance statement [#1693](https://github.com/actions/toolkit/pull/1693)
### 1.0.0
## 1.0.0
- Initial release
@@ -1,47 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`provenance functions buildSLSAProvenancePredicate handle tags including "@" character 1`] = `
{
"params": {
"buildDefinition": {
"buildType": "https://actions.github.io/buildtypes/workflow/v1",
"externalParameters": {
"workflow": {
"path": ".github/workflows/main.yml",
"ref": "foo@1.0.0",
"repository": "https://foo.ghe.com/owner/repo",
},
},
"internalParameters": {
"github": {
"event_name": "push",
"repository_id": "repo-id",
"repository_owner_id": "owner-id",
"runner_environment": "github-hosted",
},
},
"resolvedDependencies": [
{
"digest": {
"gitCommit": "babca52ab0c93ae16539e5923cb0d7403b9a093b",
},
"uri": "git+https://foo.ghe.com/owner/repo@refs/heads/main",
},
],
},
"runDetails": {
"builder": {
"id": "https://foo.ghe.com/owner/workflows/.github/workflows/publish.yml@main",
},
"metadata": {
"invocationId": "https://foo.ghe.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
},
},
},
"type": "https://slsa.dev/provenance/v1",
}
`;
exports[`provenance functions buildSLSAProvenancePredicate returns a provenance hydrated from an OIDC token 1`] = `
{
"params": {
@@ -50,7 +8,7 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
"externalParameters": {
"workflow": {
"path": ".github/workflows/main.yml",
"ref": "main",
"ref": "refs/heads/main",
"repository": "https://foo.ghe.com/owner/repo",
},
},
@@ -0,0 +1,137 @@
import {MockAgent, setGlobalDispatcher} from 'undici'
import {createStorageRecord} from '../src/artifactMetadata'
describe('createStorageRecord', () => {
const originalEnv = process.env
const token = 'token'
const headers = {'X-GitHub-Foo': 'true'}
const artifactOptions = {
name: 'my-lib',
version: '1.0.0',
digest: `sha256:${'a'.repeat(64)}`
}
const packageRegistryOptions = {
registryUrl: 'https://my-registry.org'
}
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
beforeEach(() => {
process.env = {
...originalEnv,
GITHUB_REPOSITORY: 'foo/bar'
}
})
afterEach(() => {
process.env = originalEnv
})
describe('when the api call is successful', () => {
beforeEach(() => {
mockAgent
.get('https://api.github.com')
.intercept({
path: '/orgs/foo/artifacts/metadata/storage-record',
method: 'POST',
headers: {authorization: `token ${token}`, ...headers},
body: JSON.stringify({
name: 'my-lib',
version: '1.0.0',
digest: `sha256:${'a'.repeat(64)}`,
registry_url: 'https://my-registry.org'
})
})
.reply(200, {storage_records: [{id: 123}, {id: 456}]})
})
it('persists the storage record', async () => {
await expect(
createStorageRecord(
artifactOptions,
packageRegistryOptions,
token,
undefined,
headers
)
).resolves.toEqual([123, 456])
})
})
describe('when the api call fails', () => {
beforeEach(() => {
mockAgent
.get('https://api.github.com')
.intercept({
path: '/orgs/foo/artifacts/metadata/storage-record',
method: 'POST',
headers: {authorization: `token ${token}`},
body: JSON.stringify({
name: 'my-lib',
version: '1.0.0',
digest: `sha256:${'a'.repeat(64)}`,
registry_url: 'https://my-registry.org'
})
})
.reply(500, 'oops')
})
it('throws an error', async () => {
await expect(
createStorageRecord(
artifactOptions,
packageRegistryOptions,
token,
0,
headers
)
).rejects.toThrow(/oops/)
})
})
describe('when the api call fails but succeeds on retry', () => {
beforeEach(() => {
const pool = mockAgent.get('https://api.github.com')
pool
.intercept({
path: '/orgs/foo/artifacts/metadata/storage-record',
method: 'POST',
headers: {authorization: `token ${token}`},
body: JSON.stringify({
...artifactOptions,
registry_url: packageRegistryOptions.registryUrl
})
})
.reply(500, 'oops')
.times(1)
pool
.intercept({
path: '/orgs/foo/artifacts/metadata/storage-record',
method: 'POST',
headers: {authorization: `token ${token}`},
body: JSON.stringify({
...artifactOptions,
registry_url: packageRegistryOptions.registryUrl
})
})
.reply(200, {storage_records: [{id: 123}, {id: 456}]})
.times(1)
})
it('persists the storage record', async () => {
await expect(
createStorageRecord(
artifactOptions,
packageRegistryOptions,
token,
undefined,
headers
)
).resolves.toEqual([123, 456])
})
})
})
@@ -75,16 +75,6 @@ describe('provenance functions', () => {
const predicate = await buildSLSAProvenancePredicate()
expect(predicate).toMatchSnapshot()
})
it('handle tags including "@" character', async () => {
nock.cleanAll()
await mockIssuer({
...claims,
workflow_ref: 'owner/repo/.github/workflows/main.yml@foo@1.0.0'
})
const predicate = await buildSLSAProvenancePredicate()
expect(predicate).toMatchSnapshot()
})
})
describe('attestProvenance', () => {
+417 -1832
View File
File diff suppressed because it is too large Load Diff
+18 -16
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/attest",
"version": "1.5.0",
"version": "3.2.0",
"description": "Actions attestation lib",
"keywords": [
"github",
@@ -9,8 +9,15 @@
],
"homepage": "https://github.com/actions/toolkit/tree/main/packages/attest",
"license": "MIT",
"type": "module",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"import": "./lib/index.js"
}
},
"directories": {
"lib": "lib",
"test": "__tests__"
@@ -29,30 +36,25 @@
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
"tsc": "tsc && cp src/internal/package-version.cjs lib/internal/"
},
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"devDependencies": {
"@sigstore/mock": "^0.8.0",
"@sigstore/mock": "^0.10.0",
"@sigstore/rekor-types": "^3.0.0",
"@types/jsonwebtoken": "^9.0.6",
"nock": "^13.5.1",
"undici": "^5.28.4"
"undici": "^6.23.0"
},
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.0",
"@actions/http-client": "^2.2.3",
"@octokit/plugin-retry": "^6.0.1",
"@sigstore/bundle": "^3.0.0",
"@sigstore/sign": "^3.0.0",
"jose": "^5.2.3"
},
"overrides": {
"@octokit/plugin-retry": {
"@octokit/core": "^5.2.0"
}
"@actions/core": "^3.0.0",
"@actions/github": "^9.0.0",
"@actions/http-client": "^4.0.0",
"@octokit/plugin-retry": "^8.0.3",
"@sigstore/bundle": "^3.1.0",
"@sigstore/sign": "^3.1.0",
"jose": "^5.10.0"
}
}
+92
View File
@@ -0,0 +1,92 @@
import * as github from '@actions/github'
import {retry} from '@octokit/plugin-retry'
import {RequestHeaders} from '@octokit/types'
import {getUserAgent} from './internal/utils.js'
const CREATE_STORAGE_RECORD_REQUEST =
'POST /orgs/{owner}/artifacts/metadata/storage-record'
const DEFAULT_RETRY_COUNT = 5
/**
* Options for creating a storage record for an attested artifact.
*/
export type ArtifactOptions = {
// Includes details about the attested artifact
// The name of the artifact
name: string
// The digest of the artifact
digest: string
// The version of the artifact
version?: string
// The status of the artifact
status?: string
}
// Includes details about the package registry the artifact was published to
export type PackageRegistryOptions = {
// The URL of the package registry
registryUrl: string
// The URL of the artifact in the package registry
artifactUrl?: string
// The package registry repository the artifact was published to.
repo?: string
// The path of the artifact in the package registry repository.
path?: string
}
/**
* Writes a storage record on behalf of an artifact that has been attested
* @param artifactOptions - parameters for the storage record API request.
* @param packageRegistryOptions - parameters for the package registry API request.
* @param token - GitHub token used to authenticate the request.
* @param retryAttempts - The number of retries to attempt if the request fails.
* @param headers - Additional headers to include in the request.
*
* @returns The ID of the storage record.
* @throws Error if the storage record fails to persist.
*/
export async function createStorageRecord(
artifactOptions: ArtifactOptions,
packageRegistryOptions: PackageRegistryOptions,
token: string,
retryAttempts?: number,
headers?: RequestHeaders
): Promise<number[]> {
const retries = retryAttempts ?? DEFAULT_RETRY_COUNT
const octokit = github.getOctokit(token, {retry: {retries}}, retry)
const headersWithUserAgent = {
'User-Agent': getUserAgent(),
...headers
}
try {
const response = await octokit.request(CREATE_STORAGE_RECORD_REQUEST, {
owner: github.context.repo.owner,
headers: headersWithUserAgent,
...buildRequestParams(artifactOptions, packageRegistryOptions)
})
const data =
typeof response.data == 'string'
? JSON.parse(response.data)
: response.data
return data?.storage_records.map((r: {id: number}) => r.id)
} catch (err) {
const message = err instanceof Error ? err.message : err
throw new Error(`Failed to persist storage record: ${message}`)
}
}
function buildRequestParams(
artifactOptions: ArtifactOptions,
packageRegistryOptions: PackageRegistryOptions
): Record<string, unknown> {
const {registryUrl, artifactUrl, ...rest} = packageRegistryOptions
return {
...artifactOptions,
registry_url: registryUrl,
artifact_url: artifactUrl,
...rest
}
}
+5 -5
View File
@@ -1,12 +1,12 @@
import {bundleToJSON} from '@sigstore/bundle'
import {X509Certificate} from 'crypto'
import {SigstoreInstance, signingEndpoints} from './endpoints'
import {buildIntotoStatement} from './intoto'
import {Payload, signPayload} from './sign'
import {writeAttestation} from './store'
import {SigstoreInstance, signingEndpoints} from './endpoints.js'
import {buildIntotoStatement} from './intoto.js'
import {Payload, signPayload} from './sign.js'
import {writeAttestation} from './store.js'
import type {Bundle} from '@sigstore/sign'
import type {Attestation, Predicate, Subject} from './shared.types'
import type {Attestation, Predicate, Subject} from './shared.types.js'
const INTOTO_PAYLOAD_TYPE = 'application/vnd.in-toto+json'
+9 -3
View File
@@ -1,9 +1,15 @@
export {AttestOptions, attest} from './attest'
export {
createStorageRecord,
ArtifactOptions,
PackageRegistryOptions
} from './artifactMetadata.js'
export {AttestOptions, attest} from './attest.js'
export {
AttestProvenanceOptions,
attestProvenance,
buildSLSAProvenancePredicate
} from './provenance'
} from './provenance.js'
export type {SerializedBundle} from '@sigstore/bundle'
export type {Attestation, Predicate, Subject} from './shared.types'
export type {Attestation, Predicate, Subject} from './shared.types.js'
export type {SigstoreInstance} from './endpoints.js'
@@ -0,0 +1,7 @@
// This file exists as a CommonJS module to read the version from package.json.
// In an ESM package, using `require()` directly in .ts files requires disabling
// ESLint rules and doesn't work reliably across all Node.js versions.
// By keeping this as a .cjs file, we can use require() naturally and export
// the version for the ESM modules to import.
const packageJson = require('../../package.json')
module.exports = {version: packageJson.version}
+15
View File
@@ -0,0 +1,15 @@
import {version} from './package-version.cjs'
export const getUserAgent = (): string => {
const baseUserAgent = `@actions/attest-${version}`
const orchId = process.env['ACTIONS_ORCHESTRATION_ID']
if (orchId) {
// Sanitize the orchestration ID to ensure it contains only valid characters
// Valid characters: 0-9, a-z, _, -, .
const sanitizedId = orchId.replace(/[^a-z0-9_.-]/gi, '_')
return `${baseUserAgent} actions_orchestration_id/${sanitizedId}`
}
return baseUserAgent
}
+1 -1
View File
@@ -1,4 +1,4 @@
import {Predicate, Subject} from './shared.types'
import {Predicate, Subject} from './shared.types.js'
const INTOTO_STATEMENT_V1_TYPE = 'https://in-toto.io/Statement/v1'
+5 -7
View File
@@ -1,6 +1,6 @@
import {attest, AttestOptions} from './attest'
import {getIDTokenClaims} from './oidc'
import type {Attestation, Predicate} from './shared.types'
import {attest, AttestOptions} from './attest.js'
import {getIDTokenClaims} from './oidc.js'
import type {Attestation, Predicate} from './shared.types.js'
const SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1'
const GITHUB_BUILD_TYPE = 'https://actions.github.io/buildtypes/workflow/v1'
@@ -30,11 +30,9 @@ export const buildSLSAProvenancePredicate = async (
// Split just the path and ref from the workflow string.
// owner/repo/.github/workflows/main.yml@main =>
// .github/workflows/main.yml, main
const [workflowPath, ...workflowRefChunks] = claims.workflow_ref
const [workflowPath] = claims.workflow_ref
.replace(`${claims.repository}/`, '')
.split('@')
// Handle case where tag contains `@` (e.g: when using changesets in a monorepo context),
const workflowRef = workflowRefChunks.join('@')
return {
type: SLSA_PREDICATE_V1_TYPE,
@@ -43,7 +41,7 @@ export const buildSLSAProvenancePredicate = async (
buildType: GITHUB_BUILD_TYPE,
externalParameters: {
workflow: {
ref: workflowRef,
ref: claims.ref,
repository: `${serverURL}/${claims.repository}`,
path: workflowPath
}
+12 -2
View File
@@ -1,6 +1,7 @@
import * as github from '@actions/github'
import {retry} from '@octokit/plugin-retry'
import {RequestHeaders} from '@octokit/types'
import {getUserAgent} from './internal/utils.js'
const CREATE_ATTESTATION_REQUEST = 'POST /repos/{owner}/{repo}/attestations'
const DEFAULT_RETRY_COUNT = 5
@@ -24,12 +25,21 @@ export const writeAttestation = async (
const retries = options.retry ?? DEFAULT_RETRY_COUNT
const octokit = github.getOctokit(token, {retry: {retries}}, retry)
const headers = {
'User-Agent': getUserAgent(),
...options.headers
}
try {
const response = await octokit.request(CREATE_ATTESTATION_REQUEST, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
headers: options.headers,
data: {bundle: attestation}
headers,
bundle: attestation as {
mediaType?: string
verificationMaterial?: {[key: string]: unknown}
dsseEnvelope?: {[key: string]: unknown}
}
})
const data =
+3 -1
View File
@@ -4,7 +4,9 @@
"baseUrl": "./",
"outDir": "./lib",
"declaration": true,
"rootDir": "./src"
"rootDir": "./src",
"module": "node16",
"moduleResolution": "node16"
},
"include": [
"./src"
+14 -2
View File
@@ -6,6 +6,20 @@ See ["Caching dependencies to speed up workflows"](https://docs.github.com/en/ac
Note that GitHub will remove any cache entries that have not been accessed in over 7 days. There is no limit on the number of caches you can store, but the total size of all caches in a repository is limited to 10 GB. If you exceed this limit, GitHub will save your cache but will begin evicting caches until the total size is less than 10 GB.
## ⚠️ Important changes
The cache backend service has been rewritten from the ground up for improved performance and reliability. The [@actions/cache](https://github.com/actions/toolkit/tree/main/packages/cache) package now integrates with the new cache service (v2) APIs.
The new service will gradually roll out as of **February 1st, 2025**. The legacy service will also be sunset on the same date. Changes in this release are **fully backward compatible**.
**All previous versions of this package will be deprecated**. We recommend upgrading to version `4.0.0` as soon as possible before **February 1st, 2025.**
If you do not upgrade, all workflow runs using any of the deprecated [@actions/cache](https://github.com/actions/toolkit/tree/main/packages/cache) packages will fail.
Upgrading to the recommended version should not break or require any changes to your workflows beyond updating your `package.json` to version `4.0.0`.
Read more about the change & access the migration guide: [reference to the announcement](https://github.com/actions/toolkit/discussions/1890).
## Usage
This package is used by the v2+ versions of our first party cache action. You can find an example implementation in the cache repo [here](https://github.com/actions/cache).
@@ -47,5 +61,3 @@ const cacheKey = await cache.restoreCache(paths, key, restoreKeys)
A cache gets downloaded in multiple segments of fixed sizes (now `128MB` to fail-fast, previously `1GB` for a `32-bit` runner and `2GB` for a `64-bit` runner were used). Sometimes, a segment download gets stuck which causes the workflow job to be stuck forever and fail. Version `v3.0.4` of cache package introduces a segment download timeout. The segment download timeout will allow the segment download to get aborted and hence allow the job to proceed with a cache miss.
Default value of this timeout is 10 minutes (starting `v3.2.1` and higher, previously 60 minutes in versions between `v.3.0.4` and `v3.2.0`, both included) and can be customized by specifying an [environment variable](https://docs.github.com/en/actions/learn-github-actions/environment-variables) named `SEGMENT_DOWNLOAD_TIMEOUT_MINS` with timeout value in minutes.
+128 -40
View File
@@ -1,174 +1,262 @@
# @actions/cache Releases
### 3.3.0
## 6.0.0
- **Breaking change**: Package is now ESM-only
- CommonJS consumers must use dynamic `import()` instead of `require()`
## 5.0.5
- Bump `@actions/glob` to `0.5.1`
## 5.0.4
- Bump `@actions/http-client` to `3.0.2`
## 5.0.3
Prevent retries for rate limited cache operations [2243](https://github.com/actions/toolkit/pull/2243).
## 5.0.1
- Fix Node.js 24 punycode deprecation warning by updating `@azure/storage-blob` from `^12.13.0` to `^12.29.1` [#2213](https://github.com/actions/toolkit/pull/2213)
- Newer storage-blob uses `@azure/core-rest-pipeline` instead of deprecated `@azure/core-http`, which eliminates the transitive dependency on `node-fetch@2``whatwg-url@5``tr46@0.0.3` that used the deprecated punycode module
## 5.0.0
- Remove `@azure/ms-rest-js` dependency [#2197](https://github.com/actions/toolkit/pull/2197)
- The `TransferProgressEvent` type is now imported from `@azure/core-rest-pipeline` instead of `@azure/ms-rest-js`
- Bump `@actions/core` from `^1.11.1` to `^2.0.0` [#2198](https://github.com/actions/toolkit/pull/2198)
- Bump `@actions/exec` from `^1.0.1` to `^2.0.0` [#2198](https://github.com/actions/toolkit/pull/2198)
- Bump `@actions/glob` from `^0.1.0` to `^0.5.0` [#2198](https://github.com/actions/toolkit/pull/2198)
- Bump `@actions/http-client` from `^2.1.1` to `^3.0.0` [#2198](https://github.com/actions/toolkit/pull/2198)
- Bump `@actions/io` from `^1.0.1` to `^2.0.0` [#2198](https://github.com/actions/toolkit/pull/2198)
- Add support for Node.js 24 [#2110](https://github.com/actions/toolkit/pull/2110)
- Add `node-fetch` override to resolve audit vulnerabilities [#2110](https://github.com/actions/toolkit/pull/2110)
## 4.1.0
- Remove client side 10GiB cache size limit check & update twirp client [#2118](https://github.com/actions/toolkit/pull/2118)
## 4.0.5
- Reintroduce @protobuf-ts/runtime-rpc as a runtime dependency [#2113](https://github.com/actions/toolkit/pull/2113)
## 4.0.4
⚠️ Faulty patch release. Upgrade to 4.0.5 instead.
- Optimized cache dependencies by moving `@protobuf-ts/plugin` to dev dependencies [#2106](https://github.com/actions/toolkit/pull/2106)
- Improved cache service availability determination for different cache service versions (v1 and v2) [#2100](https://github.com/actions/toolkit/pull/2100)
- Enhanced server error handling: 5xx HTTP errors are now logged as errors instead of warnings [#2099](https://github.com/actions/toolkit/pull/2099)
- Fixed cache hit logging to properly distinguish between exact key matches and restore key matches [#2101](https://github.com/actions/toolkit/pull/2101)
## 4.0.3
- Added masking for Shared Access Signature (SAS) cache entry URLs [#1982](https://github.com/actions/toolkit/pull/1982)
- Improved debugging by logging both the cache version alongside the keys requested when a cache restore fails [#1994](https://github.com/actions/toolkit/pull/1994)
## 4.0.2
- Wrap create failures in ReserveCacheError [#1966](https://github.com/actions/toolkit/pull/1966)
## 4.0.1
- Remove runtime dependency on `twirp-ts` [#1947](https://github.com/actions/toolkit/pull/1947)
- Cache miss as debug, not warning annotation [#1954](https://github.com/actions/toolkit/pull/1954)
## 4.0.0
### Important changes
The cache backend service has been rewritten from the ground up for improved performance and reliability. The [@actions/cache](https://github.com/actions/toolkit/tree/main/packages/cache) package now integrates with the new cache service (v2) APIs.
The new service will gradually roll out as of **February 1st, 2025**. The legacy service will also be sunset on the same date. Changes in this release are **fully backward compatible**.
**All previous versions of this package will be deprecated**. We recommend upgrading to version `4.0.0` as soon as possible before **February 1st, 2025.**
If you do not upgrade, all workflow runs using any of the deprecated [@actions/cache](https://github.com/actions/toolkit/tree/main/packages/cache) packages will fail.
Upgrading to the recommended version should not break or require any changes to your workflows beyond updating your `package.json` to version `4.0.0`.
Read more about the change & access the migration guide: [reference to the announcement](https://github.com/actions/toolkit/discussions/1890).
### Minor changes
- Update `@actions/core` to `1.11.0`
- Update `semver` `6.3.1`
- Add `twirp-ts` `2.5.0` to dependencies
## 3.3.0
- Update `@actions/core` to `1.11.1`
- Remove dependency on `uuid` package [#1824](https://github.com/actions/toolkit/pull/1824), [#1842](https://github.com/actions/toolkit/pull/1842)
### 3.2.4
## 3.2.4
- Updated `isGhes` check to include `.ghe.com` and `.ghe.localhost` as accepted hosts
### 3.2.3
## 3.2.3
- Fixed a bug that mutated path arguments to `getCacheVersion` [#1378](https://github.com/actions/toolkit/pull/1378)
### 3.2.2
## 3.2.2
- Add new default cache download method to improve performance and reduce hangs [#1484](https://github.com/actions/toolkit/pull/1484)
### 3.2.1
## 3.2.1
- Updated @azure/storage-blob to `v12.13.0`
### 3.2.0
## 3.2.0
- Add `lookupOnly` to cache restore `DownloadOptions`.
### 3.1.4
## 3.1.4
- Fix zstd not being used due to `zstd --version` output change in zstd 1.5.4 release. See [#1353](https://github.com/actions/toolkit/pull/1353).
### 3.1.3
## 3.1.3
- Fix to prevent from setting MYSYS environement variable globally [#1329](https://github.com/actions/toolkit/pull/1329).
### 3.1.2
## 3.1.2
- Fix issue with symlink restoration on windows.
### 3.1.1
## 3.1.1
- Reverted changes in 3.1.0 to fix issue with symlink restoration on windows.
- Added support for verbose logging about cache version during cache miss.
### 3.1.0
## 3.1.0
- Update actions/cache on windows to use gnu tar and zstd by default
- Update actions/cache on windows to fallback to bsdtar and zstd if gnu tar is not available.
- Added support for fallback to gzip to restore old caches on windows.
### 3.1.0-beta.3
## 3.1.0-beta.3
- Bug Fixes for fallback to gzip to restore old caches on windows and bsdtar if gnutar is not available.
### 3.1.0-beta.2
## 3.1.0-beta.2
- Added support for fallback to gzip to restore old caches on windows.
### 3.0.6
## 3.0.6
- Added `@azure/abort-controller` to dependencies to fix compatibility issue with ESM [#1208](https://github.com/actions/toolkit/issues/1208)
### 3.0.5
## 3.0.5
- Update `@actions/cache` to use `@actions/core@^1.10.0`
### 3.0.4
## 3.0.4
- Fix zstd not working for windows on gnu tar in issues [#888](https://github.com/actions/cache/issues/888) and [#891](https://github.com/actions/cache/issues/891).
- Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable `SEGMENT_DOWNLOAD_TIMEOUT_MINS`. Default is 60 minutes.
### 3.0.3
## 3.0.3
- Bug fixes for download stuck issue [#810](https://github.com/actions/cache/issues/810).
### 3.0.2
## 3.0.2
- Added 1 hour timeout for the download stuck issue [#810](https://github.com/actions/cache/issues/810).
### 3.0.1
## 3.0.1
- Fix [#833](https://github.com/actions/cache/issues/833) - cache doesn't work with github workspace directory.
- Fix [#809](https://github.com/actions/cache/issues/809) `zstd -d: no such file or directory` error on AWS self-hosted runners.
### 3.0.0
## 3.0.0
- Updated actions/cache to suppress Actions cache server error and log warning for those error [#1122](https://github.com/actions/toolkit/pull/1122)
### 2.0.6
## 2.0.6
- Fix `Tar failed with error: The process '/usr/bin/tar' failed with exit code 1` issue when temp directory where tar is getting created is actually the subdirectory of the path mentioned by the user for caching. ([issue](https://github.com/actions/cache/issues/689))
### 2.0.5
## 2.0.5
- Fix to avoid saving empty cache when no files are available for caching. ([issue](https://github.com/actions/cache/issues/624))
### 2.0.4
## 2.0.4
- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087)
### 2.0.3
## 2.0.3
- Update to v2.0.0 of `@actions/http-client`
### 2.0.0
## 2.0.0
- Added support to check if Actions cache service feature is available or not [#1028](https://github.com/actions/toolkit/pull/1028)
### 1.0.11
## 1.0.11
- Fix file downloads > 2GB([issue](https://github.com/actions/cache/issues/773))
### 1.0.10
## 1.0.10
- Update `lockfileVersion` to `v2` in `package-lock.json [#1022](https://github.com/actions/toolkit/pull/1022)
### 1.0.9
## 1.0.9
- Use @azure/ms-rest-js v2.6.0
- Use @azure/storage-blob v12.8.0
### 1.0.8
## 1.0.8
- Increase the allowed artifact cache size from 5GB to 10GB ([issue](https://github.com/actions/cache/discussions/497))
### 1.0.7
## 1.0.7
- Fixes permissions issue extracting archives with GNU tar on macOS ([issue](https://github.com/actions/cache/issues/527))
### 1.0.6
## 1.0.6
- Make caching more verbose [#650](https://github.com/actions/toolkit/pull/650)
- Use GNU tar on macOS if available [#701](https://github.com/actions/toolkit/pull/701)
### 1.0.5
## 1.0.5
- Fix to ensure Windows cache paths get resolved correctly
### 1.0.4
## 1.0.4
- Use @actions/core v1.2.6
- Fixes uploadChunk to throw an error if any unsuccessful response code is received
### 1.0.3
## 1.0.3
- Use http-client v1.0.9
- Fixes error handling so retries are not attempted on non-retryable errors (409 Conflict, for example)
- Adds 5 second delay between retry attempts
### 1.0.2
## 1.0.2
- Use posix archive format to add support for some tools
### 1.0.1
## 1.0.1
- Fix bug in downloading large files (> 2 GBs) with the Azure SDK
### 1.0.0
## 1.0.0
- Downloads Azure-hosted caches using the Azure SDK for speed and reliability
- Displays download progress
- Includes changes that break compatibility with earlier versions, including:
- `retry`, `retryTypedResponse`, and `retryHttpClientResponse` moved from `cacheHttpClient` to `requestUtils`
### 0.2.1
## 0.2.1
- Fix to await async function getCompressionMethod
### 0.2.0
## 0.2.0
- Fixes issues with the zstd compression algorithm on Windows and Ubuntu 16.04 [#469](https://github.com/actions/toolkit/pull/469)
### 0.1.0
## 0.1.0
- Initial release
+1 -1
View File
@@ -1,5 +1,5 @@
name: 'Set env variables'
description: 'Sets certain env variables so that e2e restore and save cache can be tested in a shell'
runs:
using: 'node12'
using: 'node20'
main: 'index.js'
+9 -5
View File
@@ -1,12 +1,16 @@
// Certain env variables are not set by default in a shell context and are only available in a node context from a running action
// In order to be able to restore and save cache e2e in a shell when running CI tests, we need these env variables set
const fs = require('fs');
const os = require('os');
const filePath = process.env[`GITHUB_ENV`]
fs.appendFileSync(filePath, `ACTIONS_RUNTIME_TOKEN=${process.env.ACTIONS_RUNTIME_TOKEN}${os.EOL}`, {
import fs from 'fs'
import os from 'os'
const filePath = process.env['GITHUB_ENV']
fs.appendFileSync(filePath, `ACTIONS_CACHE_SERVICE_V2=true${os.EOL}`, {
encoding: 'utf8'
})
fs.appendFileSync(filePath, `ACTIONS_CACHE_URL=${process.env.ACTIONS_CACHE_URL}${os.EOL}`, {
fs.appendFileSync(filePath, `ACTIONS_RESULTS_URL=${process.env.ACTIONS_RESULTS_URL}${os.EOL}`, {
encoding: 'utf8'
})
fs.appendFileSync(filePath, `ACTIONS_RUNTIME_TOKEN=${process.env.ACTIONS_RUNTIME_TOKEN}${os.EOL}`, {
encoding: 'utf8'
})
fs.appendFileSync(filePath, `GITHUB_RUN_ID=${process.env.GITHUB_RUN_ID}${os.EOL}`, {

Some files were not shown because too many files have changed in this diff Show More