Compare commits

...
Author SHA1 Message Date
Fredrik Skogman 90cb9652e6 When creating the workflow claims, actually use the data from the workflow
claim in the JWT
2026-01-20 13:42:14 +01:00
Ryan GhadimiandGitHub 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 GhadimiandGitHub a039cff4a1 Add comment for rate limiting handling
Added a comment regarding rate limiting and retry behavior.
2026-01-16 10:26:45 +00:00
9dd77993e7 Update packages/cache/RELEASES.md
Co-authored-by: Bassem Dghaidi <[email protected]>
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 HuangandGitHub 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 HuangandGitHub d3a48dd52f Merge pull request #2233 from actions/copilot/update-http-client-dependency
Update selected packages to consume @actions/[email protected]
2026-01-08 09:04:43 -05:00
Tingluo HuangandGitHub 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]andTingluoHuang 2a9d836b08 Revert @actions/attest http-client dependency to ^2.2.3
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 23:17:02 +00:00
copilot-swe-agent[bot]andTingluoHuang 3a3b073ef2 Revert changes to github, tool-cache packages and .gitignore
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 22:38:20 +00:00
copilot-swe-agent[bot]andTingluoHuang d3a0fb260e Revert @actions/attest version to 2.1.0 - no new release needed
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 22:33:05 +00:00
copilot-swe-agent[bot]andTingluoHuang be4fdc505f Fix package-lock.json to mark typescript as dev dependency
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 22:10:30 +00:00
copilot-swe-agent[bot]andTingluoHuang 6be37922c5 Add .nx/ to .gitignore and remove cached build artifacts
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 22:04:45 +00:00
copilot-swe-agent[bot]andTingluoHuang dfc20acda2 Verify changes with tests - all package-specific tests passing
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 22:03:56 +00:00
copilot-swe-agent[bot]andTingluoHuang dc1fec82a6 Update all packages to use @actions/http-client@^3.0.1 and bump patch versions
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 21:55:41 +00:00
copilot-swe-agent[bot] 9339b3573b Initial plan 2026-01-07 21:51:58 +00:00
Tingluo HuangandGitHub 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]andTingluoHuang d73fffceed Combine duplicate RELEASES.md entries into single line
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 18:44:34 +00:00
copilot-swe-agent[bot]andTingluoHuang 16f0b3d28e Revert .gitignore change as requested
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 18:40:39 +00:00
copilot-swe-agent[bot]andTingluoHuang 398e2cb68b Change version from 3.1.0 to 3.0.1 as requested
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 18:31:01 +00:00
copilot-swe-agent[bot]andTingluoHuang 4b9031fa77 Bump http-client version to 3.1.0 and update RELEASES.md
Co-authored-by: TingluoHuang <[email protected]>
2026-01-07 18:13:07 +00:00
copilot-swe-agent[bot] 959cb66bd5 Initial plan 2026-01-07 18:02:29 +00:00
Tingluo HuangandGitHub 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]andTingluoHuang 83c13c81ba Improve test cleanup and add missing test coverage
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 19:19:35 +00:00
copilot-swe-agent[bot]andTingluoHuang bccbba401a Fix lint error: remove unnecessary escape in regex
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 18:30:18 +00:00
copilot-swe-agent[bot]andTingluoHuang 3a191eecf6 Add default user-agent 'actions/http-client' when none provided
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 18:10:12 +00:00
copilot-swe-agent[bot]andTingluoHuang 97f5a6f0dc Simplify constructor by handling undefined in helper method
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 18:03:26 +00:00
copilot-swe-agent[bot]andTingluoHuang 48a7cdbf9c Remove .gitignore change for .nx/ directory
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 17:54:40 +00:00
copilot-swe-agent[bot]andTingluoHuang 3f1933edf9 Revert unrelated version bumps in package-lock.json files
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 17:50:28 +00:00
copilot-swe-agent[bot]andTingluoHuang 2215c8e5aa Restrict sanitization to only allow 0-9, a-z, _, -, .
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 17:37:28 +00:00
copilot-swe-agent[bot]andTingluoHuang af6de2cb95 Move orchestration ID logic to constructor for efficiency
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 17:30:28 +00:00
copilot-swe-agent[bot]andTingluoHuang 1dc58e3080 Rename github_orchestration_id to actions_orchestration_id
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 17:20:43 +00:00
copilot-swe-agent[bot]andTingluoHuang 20596c1d96 Move hyphen to end of character class for cleaner regex
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 16:30:09 +00:00
copilot-swe-agent[bot]andTingluoHuang 557f80fd03 Fix regex to properly escape hyphen in character class
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 16:28:52 +00:00
copilot-swe-agent[bot]andTingluoHuang 32c52bb78a Use product/version format and sanitize orchestration ID
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 16:26:29 +00:00
copilot-swe-agent[bot]andTingluoHuang 6d9a3fe547 Format code and update .gitignore to exclude .nx cache
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 15:36:15 +00:00
copilot-swe-agent[bot]andTingluoHuang 4e1c194b34 Add ACTIONS_ORCHESTRATION_ID support to http-client user-agent
Co-authored-by: TingluoHuang <[email protected]>
2026-01-06 15:32:55 +00:00
copilot-swe-agent[bot] 09cb71a033 Initial plan 2026-01-06 15:22:42 +00:00
Salman ChishtiandGitHub 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
27 changed files with 485 additions and 112 deletions
+6 -6
View File
@@ -1,17 +1,17 @@
{
"name": "@actions/artifact",
"version": "5.0.0",
"version": "5.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@actions/artifact",
"version": "5.0.0",
"version": "5.0.2",
"license": "MIT",
"dependencies": {
"@actions/core": "^2.0.0",
"@actions/github": "^6.0.1",
"@actions/http-client": "^3.0.0",
"@actions/http-client": "^3.0.1",
"@azure/storage-blob": "^12.29.1",
"@octokit/core": "^5.2.1",
"@octokit/plugin-request-log": "^1.0.4",
@@ -76,9 +76,9 @@
}
},
"node_modules/@actions/http-client": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.0.tgz",
"integrity": "sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/artifact",
"version": "5.0.1",
"version": "5.0.2",
"preview": true,
"description": "Actions artifact lib",
"keywords": [
@@ -42,7 +42,7 @@
"dependencies": {
"@actions/core": "^2.0.0",
"@actions/github": "^6.0.1",
"@actions/http-client": "^3.0.0",
"@actions/http-client": "^3.0.1",
"@azure/storage-blob": "^12.29.1",
"@octokit/core": "^5.2.1",
"@octokit/plugin-request-log": "^1.0.4",
+6
View File
@@ -1,5 +1,11 @@
# @actions/attest Releases
### 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)
@@ -1,6 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`provenance functions buildSLSAProvenancePredicate returns a provenance hydrated from an OIDC token 1`] = `
exports[`provenance functions buildSLSAProvenancePredicate returns a provenance from an org required workflow: provenance from org required workflow 1`] = `
{
"params": {
"buildDefinition": {
"buildType": "https://actions.github.io/buildtypes/workflow/v1",
"externalParameters": {
"workflow": {
"path": ".github/workflows/enforce.yml",
"ref": "refs/heads/vetted",
"repository": "https://foo.ghe.com/enterprise/org-workflows",
},
},
"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/enterprise/org-workflows/.github/workflows/enforce.yml@refs/heads/vetted",
},
"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: provenance with OIDC token 1`] = `
{
"params": {
"buildDefinition": {
@@ -31,7 +73,7 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
},
"runDetails": {
"builder": {
"id": "https://foo.ghe.com/owner/workflows/.github/workflows/publish.yml@main",
"id": "https://foo.ghe.com/owner/workflows/.github/workflows/publish.yml@refs/heads/main",
},
"metadata": {
"invocationId": "https://foo.ghe.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
+29 -3
View File
@@ -23,8 +23,27 @@ describe('provenance functions', () => {
repository: 'owner/repo',
ref: 'refs/heads/main',
sha: 'babca52ab0c93ae16539e5923cb0d7403b9a093b',
job_workflow_ref: 'owner/workflows/.github/workflows/publish.yml@main',
workflow_ref: 'owner/repo/.github/workflows/main.yml@main',
job_workflow_ref:
'owner/workflows/.github/workflows/publish.yml@refs/heads/main',
workflow_ref: 'owner/repo/.github/workflows/main.yml@refs/heads/main',
event_name: 'push',
repository_id: 'repo-id',
repository_owner_id: 'owner-id',
run_id: 'run-id',
run_attempt: 'run-attempt',
runner_environment: 'github-hosted'
}
const orgEnforcedclaims = {
iss: issuer,
aud: 'nobody',
repository: 'owner/repo',
ref: 'refs/heads/main',
sha: 'babca52ab0c93ae16539e5923cb0d7403b9a093b',
job_workflow_ref:
'enterprise/org-workflows/.github/workflows/enforce.yml@refs/heads/vetted',
workflow_ref:
'enterprise/org-workflows/.github/workflows/enforce.yml@refs/heads/vetted',
event_name: 'push',
repository_id: 'repo-id',
repository_owner_id: 'owner-id',
@@ -73,7 +92,14 @@ describe('provenance functions', () => {
describe('buildSLSAProvenancePredicate', () => {
it('returns a provenance hydrated from an OIDC token', async () => {
const predicate = await buildSLSAProvenancePredicate()
expect(predicate).toMatchSnapshot()
expect(predicate).toMatchSnapshot('provenance with OIDC token')
})
it('returns a provenance from an org required workflow', async () => {
nock.cleanAll()
await mockIssuer(orgEnforcedclaims)
const predicate = await buildSLSAProvenancePredicate()
expect(predicate).toMatchSnapshot('provenance from org required workflow')
})
})
+26 -25
View File
@@ -1,17 +1,17 @@
{
"name": "@actions/attest",
"version": "2.1.0",
"version": "2.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@actions/attest",
"version": "2.1.0",
"version": "2.2.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.0",
"@actions/http-client": "^2.2.3",
"@actions/core": "^2.0.2",
"@actions/github": "^7.0.0",
"@actions/http-client": "^3.0.1",
"@octokit/plugin-retry": "^6.0.1",
"@sigstore/bundle": "^3.1.0",
"@sigstore/sign": "^3.1.0",
@@ -26,31 +26,31 @@
}
},
"node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.2.tgz",
"integrity": "sha512-Ast1V7yHbGAhplAsuVlnb/5J8Mtr/Zl6byPPL+Qjq3lmfIgWF1ak1iYfF/079cRERiuTALTXkSuEUdZeDCfGtA==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.1"
}
},
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-2.0.0.tgz",
"integrity": "sha512-k8ngrX2voJ/RIN6r9xB82NVqKpnMRtxDoiO+g3olkIUpQNqjArXrCQceduQZCQj3P3xm32pChRLqRrtXTlqhIw==",
"license": "MIT",
"dependencies": {
"@actions/io": "^1.0.1"
"@actions/io": "^2.0.0"
}
},
"node_modules/@actions/github": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.1.tgz",
"integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-7.0.0.tgz",
"integrity": "sha512-PyGODO938aoBTZd/IfN/+e+Pd5hUcVpyf+thm4CPESLeqhdSkq5QwMTGX9v84XHE1ifmHWBQ60KB8kIgm96opw==",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^2.2.0",
"@actions/http-client": "^3.0.1",
"@octokit/core": "^5.0.1",
"@octokit/plugin-paginate-rest": "^9.2.2",
"@octokit/plugin-rest-endpoint-methods": "^10.4.0",
@@ -72,13 +72,13 @@
}
},
"node_modules/@actions/http-client": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^5.25.4"
"undici": "^5.28.5"
}
},
"node_modules/@actions/http-client/node_modules/undici": {
@@ -94,9 +94,9 @@
}
},
"node_modules/@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-2.0.0.tgz",
"integrity": "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg==",
"license": "MIT"
},
"node_modules/@fastify/busboy": {
@@ -192,6 +192,7 @@
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
"integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.1.0",
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/attest",
"version": "2.1.0",
"version": "2.2.0",
"description": "Actions attestation lib",
"keywords": [
"github",
@@ -42,9 +42,9 @@
"undici": "^6.20.0"
},
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.0",
"@actions/http-client": "^2.2.3",
"@actions/core": "^2.0.2",
"@actions/github": "^7.0.0",
"@actions/http-client": "^3.0.1",
"@octokit/plugin-retry": "^6.0.1",
"@sigstore/bundle": "^3.1.0",
"@sigstore/sign": "^3.1.0",
+11 -8
View File
@@ -27,12 +27,15 @@ export const buildSLSAProvenancePredicate = async (
const serverURL = process.env.GITHUB_SERVER_URL
const claims = await getIDTokenClaims(issuer)
// Split just the path and ref from the workflow string.
// owner/repo/.github/workflows/main.yml@main =>
// .github/workflows/main.yml, main
const [workflowPath] = claims.workflow_ref
.replace(`${claims.repository}/`, '')
.split('@')
// Split up the repo, path and ref from the workflow string.
// owner/repo/.github/workflows/main.yml@refs/heads/main =>
// owner/repo, .github/workflows/main.yml, main
const workflowRef = claims.workflow_ref
const pathStart = workflowRef.indexOf('/.github/')
const refStart = workflowRef.lastIndexOf('@')
const workflowRepo = workflowRef.substring(0, pathStart)
const workflowPath = workflowRef.substring(pathStart + 1, refStart)
const workflowGitRef = workflowRef.substring(refStart + 1)
return {
type: SLSA_PREDICATE_V1_TYPE,
@@ -41,8 +44,8 @@ export const buildSLSAProvenancePredicate = async (
buildType: GITHUB_BUILD_TYPE,
externalParameters: {
workflow: {
ref: claims.ref,
repository: `${serverURL}/${claims.repository}`,
ref: workflowGitRef,
repository: `${serverURL}/${workflowRepo}`,
path: workflowPath
}
},
+4
View File
@@ -1,5 +1,9 @@
# @actions/cache Releases
### 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)
+174
View File
@@ -0,0 +1,174 @@
import * as http from 'http'
import * as net from 'net'
import {HttpClient} from '@actions/http-client'
import * as core from '@actions/core'
import * as config from '../src/internal/config'
import * as cacheUtils from '../src/internal/cacheUtils'
import {internalCacheTwirpClient} from '../src/internal/shared/cacheTwirpClient'
jest.mock('@actions/http-client')
const clientOptions = {
maxAttempts: 5,
retryIntervalMs: 1,
retryMultiplier: 1.5
}
// noopLogs mocks the console.log and core.* functions to prevent output in the console while testing
const noopLogs = (): void => {
jest.spyOn(console, 'log').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
}
describe('cacheTwirpClient', () => {
beforeAll(() => {
noopLogs()
jest
.spyOn(config, 'getCacheServiceURL')
.mockReturnValue('http://localhost:8080')
jest.spyOn(cacheUtils, 'getRuntimeToken').mockReturnValue('token')
})
beforeEach(() => {
jest.clearAllMocks()
})
it('should fail immediately on 429 rate limit without retrying', async () => {
const mockPost = jest.fn(() => {
const msg = new http.IncomingMessage(new net.Socket())
msg.statusCode = 429
msg.statusMessage = 'Too Many Requests'
return {
message: msg,
readBody: async () => {
return Promise.resolve(`{"ok": false}`)
}
}
})
;(HttpClient as unknown as jest.Mock).mockImplementation(() => {
return {
post: mockPost
}
})
const client = internalCacheTwirpClient(clientOptions)
await expect(
client.CreateCacheEntry({
key: 'test-key',
version: 'test-version'
})
).rejects.toThrow(
'Failed to CreateCacheEntry: Rate limited: Failed request: (429) Too Many Requests'
)
// Should only be called once - no retries for 429
expect(mockPost).toHaveBeenCalledTimes(1)
})
it('should log warning with retry-after header on 429', async () => {
const warningSpy = jest.spyOn(core, 'warning')
const mockPost = jest.fn(() => {
const msg = new http.IncomingMessage(new net.Socket())
msg.statusCode = 429
msg.statusMessage = 'Too Many Requests'
msg.headers = {'retry-after': '60'}
return {
message: msg,
readBody: async () => {
return Promise.resolve(`{"ok": false}`)
}
}
})
;(HttpClient as unknown as jest.Mock).mockImplementation(() => {
return {
post: mockPost
}
})
const client = internalCacheTwirpClient(clientOptions)
await expect(
client.CreateCacheEntry({
key: 'test-key',
version: 'test-version'
})
).rejects.toThrow('Rate limited')
expect(mockPost).toHaveBeenCalledTimes(1)
expect(warningSpy).toHaveBeenCalledWith(
"You've hit a rate limit, your rate limit will reset in 60 seconds"
)
})
it('should not log warning if retry-after header is missing on 429', async () => {
const warningSpy = jest.spyOn(core, 'warning')
const mockPost = jest.fn(() => {
const msg = new http.IncomingMessage(new net.Socket())
msg.statusCode = 429
msg.statusMessage = 'Too Many Requests'
// No retry-after header
return {
message: msg,
readBody: async () => {
return Promise.resolve(`{"ok": false}`)
}
}
})
;(HttpClient as unknown as jest.Mock).mockImplementation(() => {
return {
post: mockPost
}
})
const client = internalCacheTwirpClient(clientOptions)
await expect(
client.CreateCacheEntry({
key: 'test-key',
version: 'test-version'
})
).rejects.toThrow('Rate limited')
expect(mockPost).toHaveBeenCalledTimes(1)
expect(warningSpy).not.toHaveBeenCalled()
})
it('should not log warning if retry-after header is invalid on 429', async () => {
const warningSpy = jest.spyOn(core, 'warning')
const mockPost = jest.fn(() => {
const msg = new http.IncomingMessage(new net.Socket())
msg.statusCode = 429
msg.statusMessage = 'Too Many Requests'
msg.headers = {'retry-after': 'invalid'}
return {
message: msg,
readBody: async () => {
return Promise.resolve(`{"ok": false}`)
}
}
})
;(HttpClient as unknown as jest.Mock).mockImplementation(() => {
return {
post: mockPost
}
})
const client = internalCacheTwirpClient(clientOptions)
await expect(
client.CreateCacheEntry({
key: 'test-key',
version: 'test-version'
})
).rejects.toThrow('Rate limited')
expect(mockPost).toHaveBeenCalledTimes(1)
expect(warningSpy).not.toHaveBeenCalled()
})
})
+6 -6
View File
@@ -1,18 +1,18 @@
{
"name": "@actions/cache",
"version": "5.0.0",
"version": "5.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@actions/cache",
"version": "5.0.0",
"version": "5.0.2",
"license": "MIT",
"dependencies": {
"@actions/core": "^2.0.0",
"@actions/exec": "^2.0.0",
"@actions/glob": "^0.5.0",
"@actions/http-client": "^3.0.0",
"@actions/http-client": "^3.0.1",
"@actions/io": "^2.0.0",
"@azure/abort-controller": "^1.1.0",
"@azure/core-rest-pipeline": "^1.22.0",
@@ -92,9 +92,9 @@
"license": "MIT"
},
"node_modules/@actions/http-client": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.0.tgz",
"integrity": "sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/cache",
"version": "5.0.1",
"version": "5.0.3",
"preview": true,
"description": "Actions cache lib",
"keywords": [
@@ -41,7 +41,7 @@
"@actions/exec": "^2.0.0",
"@actions/glob": "^0.5.0",
"@protobuf-ts/runtime-rpc": "^2.11.1",
"@actions/http-client": "^3.0.0",
"@actions/http-client": "^3.0.1",
"@actions/io": "^2.0.0",
"@azure/abort-controller": "^1.1.0",
"@azure/core-rest-pipeline": "^1.22.0",
+22 -4
View File
@@ -1,6 +1,6 @@
import {info, debug} from '@actions/core'
import {info, debug, warning} from '@actions/core'
import {getUserAgentString} from './user-agent'
import {NetworkError, UsageError} from './errors'
import {NetworkError, RateLimitError, UsageError} from './errors'
import {getCacheServiceURL} from '../config'
import {getRuntimeToken} from '../cacheUtils'
import {BearerCredentialHandler} from '@actions/http-client/lib/auth'
@@ -109,6 +109,21 @@ class CacheServiceClient implements Rpc {
errorMessage = `${errorMessage}: ${body.msg}`
}
// Handle rate limiting - don't retry, just warn and exit
// For more info, see https://docs.github.com/en/actions/reference/limits
if (statusCode === HttpCodes.TooManyRequests) {
const retryAfterHeader = response.message.headers['retry-after']
if (retryAfterHeader) {
const parsedSeconds = parseInt(retryAfterHeader, 10)
if (!isNaN(parsedSeconds) && parsedSeconds > 0) {
warning(
`You've hit a rate limit, your rate limit will reset in ${parsedSeconds} seconds`
)
}
}
throw new RateLimitError(`Rate limited: ${errorMessage}`)
}
} catch (error) {
if (error instanceof SyntaxError) {
debug(`Raw Body: ${rawBody}`)
@@ -118,6 +133,10 @@ class CacheServiceClient implements Rpc {
throw error
}
if (error instanceof RateLimitError) {
throw error
}
if (NetworkError.isNetworkErrorCode(error?.code)) {
throw new NetworkError(error?.code)
}
@@ -162,8 +181,7 @@ class CacheServiceClient implements Rpc {
HttpCodes.BadGateway,
HttpCodes.GatewayTimeout,
HttpCodes.InternalServerError,
HttpCodes.ServiceUnavailable,
HttpCodes.TooManyRequests
HttpCodes.ServiceUnavailable
]
return retryableStatusCodes.includes(statusCode)
+7
View File
@@ -70,3 +70,10 @@ export class UsageError extends Error {
return msg.includes('insufficient usage')
}
}
export class RateLimitError extends Error {
constructor(message: string) {
super(message)
this.name = 'RateLimitError'
}
}
+6 -6
View File
@@ -1,16 +1,16 @@
{
"name": "@actions/core",
"version": "2.0.1",
"version": "2.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@actions/core",
"version": "2.0.1",
"version": "2.0.2",
"license": "MIT",
"dependencies": {
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.0"
"@actions/http-client": "^3.0.1"
},
"devDependencies": {
"@types/node": "^16.18.112"
@@ -26,9 +26,9 @@
}
},
"node_modules/@actions/http-client": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.0.tgz",
"integrity": "sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/core",
"version": "2.0.1",
"version": "2.0.2",
"description": "Actions core lib",
"keywords": [
"github",
@@ -37,7 +37,7 @@
},
"dependencies": {
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.0"
"@actions/http-client": "^3.0.1"
},
"devDependencies": {
"@types/node": "^16.18.112"
+4
View File
@@ -1,5 +1,9 @@
# @actions/github Releases
### 7.0.0
- Update to v3.0.1 of `@actions/http-client`
### 6.0.1
- Dependency updates [#2043](https://github.com/actions/toolkit/pull/2043)
+8 -7
View File
@@ -1,15 +1,15 @@
{
"name": "@actions/github",
"version": "6.0.1",
"version": "7.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@actions/github",
"version": "6.0.1",
"version": "7.0.0",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^2.2.0",
"@actions/http-client": "^3.0.1",
"@octokit/core": "^5.0.1",
"@octokit/plugin-paginate-rest": "^9.2.2",
"@octokit/plugin-rest-endpoint-methods": "^10.4.0",
@@ -22,13 +22,13 @@
}
},
"node_modules/@actions/http-client": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^5.25.4"
"undici": "^5.28.5"
}
},
"node_modules/@fastify/busboy": {
@@ -54,6 +54,7 @@
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
"integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.1.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/github",
"version": "6.0.1",
"version": "7.0.0",
"description": "Actions github lib",
"keywords": [
"github",
@@ -38,7 +38,7 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/http-client": "^2.2.0",
"@actions/http-client": "^3.0.1",
"@octokit/core": "^5.0.1",
"@octokit/plugin-paginate-rest": "^9.2.2",
"@octokit/plugin-rest-endpoint-methods": "^10.4.0",
+3
View File
@@ -1,5 +1,8 @@
## Releases
## 3.0.1
- Add support for ACTIONS_ORCHESTRATION_ID in user-agent and default user-agent [#2229](https://github.com/actions/toolkit/pull/2229)
## 3.0.0
- Add support for Node 24 [#2110](https://github.com/actions/toolkit/pull/2110)
+67 -2
View File
@@ -21,7 +21,10 @@ describe('basics', () => {
_http = new httpm.HttpClient('http-client-tests')
})
afterEach(() => {})
afterEach(() => {
// Clean up environment variable to prevent test pollution
delete process.env['ACTIONS_ORCHESTRATION_ID']
})
it('constructs', () => {
const http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests')
@@ -60,7 +63,7 @@ describe('basics', () => {
const body: string = await res.readBody()
const obj = JSON.parse(body)
expect(obj.url).toBe('https://postman-echo.com/get')
expect(obj.headers['user-agent']).toBeFalsy()
expect(obj.headers['user-agent']).toBe('actions/http-client')
})
/* TODO write a mock rather then relying on a third party
@@ -374,4 +377,66 @@ describe('basics', () => {
httpm.MediaTypes.ApplicationJson
)
})
it('appends orchestration ID to user-agent when ACTIONS_ORCHESTRATION_ID is set', async () => {
const orchId = 'test-orch-id-12345'
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests')
const res: httpm.HttpClientResponse = await http.get(
'https://postman-echo.com/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
expect(obj.headers['user-agent']).toBe(
`http-client-tests actions_orchestration_id/${orchId}`
)
})
it('sanitizes invalid characters in orchestration ID', async () => {
const orchId = 'test (with) special/chars'
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests')
const res: httpm.HttpClientResponse = await http.get(
'https://postman-echo.com/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
// Spaces, parentheses, and slashes should be replaced with underscores
expect(obj.headers['user-agent']).toBe(
'http-client-tests actions_orchestration_id/test__with__special_chars'
)
})
it('does not modify user-agent when ACTIONS_ORCHESTRATION_ID is not set', async () => {
delete process.env['ACTIONS_ORCHESTRATION_ID']
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests')
const res: httpm.HttpClientResponse = await http.get(
'https://postman-echo.com/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
expect(obj.headers['user-agent']).toBe('http-client-tests')
})
it('uses default user-agent with orchestration ID when no custom user-agent provided', async () => {
const orchId = 'test-default-id-12345'
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
const http: httpm.HttpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await http.get(
'https://postman-echo.com/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
expect(obj.headers['user-agent']).toBe(
`actions/http-client actions_orchestration_id/${orchId}`
)
})
})
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@actions/http-client",
"version": "3.0.0",
"version": "3.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@actions/http-client",
"version": "3.0.0",
"version": "3.0.1",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/http-client",
"version": "3.0.0",
"version": "3.0.1",
"description": "Actions Http Client",
"keywords": [
"github",
+13 -1
View File
@@ -147,7 +147,7 @@ export class HttpClient {
handlers?: ifm.RequestHandler[],
requestOptions?: ifm.RequestOptions
) {
this.userAgent = userAgent
this.userAgent = this._getUserAgentWithOrchestrationId(userAgent)
this.handlers = handlers || []
this.requestOptions = requestOptions
if (requestOptions) {
@@ -816,6 +816,18 @@ export class HttpClient {
return proxyAgent
}
private _getUserAgentWithOrchestrationId(userAgent?: string): string {
const baseUserAgent = userAgent || 'actions/http-client'
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
}
private async _performExponentialBackoff(retryNumber: number): Promise<void> {
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber)
const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber)
+7
View File
@@ -1,5 +1,12 @@
# @actions/tool-cache Releases
### 3.0.0
- Update to v2.0.1 of `@actions/core`
- Update to v2.0.0 of `@actions/exec`
- Update to v3.0.1 of `@actions/http-client`
- Update to v2.0.0 of `@actions/io`
### 2.0.2
- Update `@actions/core` to v1.11.1 [#1872](https://github.com/actions/toolkit/pull/1872)
+22 -22
View File
@@ -1,18 +1,18 @@
{
"name": "@actions/tool-cache",
"version": "2.0.2",
"version": "3.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@actions/tool-cache",
"version": "2.0.2",
"version": "3.0.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^2.0.1",
"@actions/io": "^1.1.1",
"@actions/core": "^2.0.1",
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.1",
"@actions/io": "^2.0.0",
"semver": "^6.1.0"
},
"devDependencies": {
@@ -22,38 +22,38 @@
}
},
"node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.1.tgz",
"integrity": "sha512-oBfqT3GwkvLlo1fjvhQLQxuwZCGTarTE5OuZ2Wg10hvhBj7LRIlF611WT4aZS6fDhO5ZKlY7lCAZTlpmyaHaeg==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.0"
}
},
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-2.0.0.tgz",
"integrity": "sha512-k8ngrX2voJ/RIN6r9xB82NVqKpnMRtxDoiO+g3olkIUpQNqjArXrCQceduQZCQj3P3xm32pChRLqRrtXTlqhIw==",
"license": "MIT",
"dependencies": {
"@actions/io": "^1.0.1"
"@actions/io": "^2.0.0"
}
},
"node_modules/@actions/http-client": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^5.25.4"
"undici": "^5.28.5"
}
},
"node_modules/@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-2.0.0.tgz",
"integrity": "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg==",
"license": "MIT"
},
"node_modules/@fastify/busboy": {
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/tool-cache",
"version": "2.0.2",
"version": "3.0.0",
"description": "Actions tool-cache lib",
"keywords": [
"github",
@@ -36,10 +36,10 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^2.0.1",
"@actions/io": "^1.1.1",
"@actions/core": "^2.0.1",
"@actions/exec": "^2.0.0",
"@actions/http-client": "^3.0.1",
"@actions/io": "^2.0.0",
"semver": "^6.1.0"
},
"devDependencies": {