mirror of
				https://github.com/actions/checkout.git
				synced 2025-11-01 06:18:06 +08:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			8edcb1bdb4
			...
			users/eric
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | cb17bfb94d | ||
|  | ff7abcd0c3 | ||
|  | 08c6903cd8 | ||
|  | 9f265659d3 | ||
|  | 08eba0b27e | ||
|  | 631c7dc4f8 | 
							
								
								
									
										4
									
								
								.github/workflows/check-dist.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/check-dist.yml
									
									
									
									
										vendored
									
									
								
							| @@ -24,10 +24,10 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4.1.6 |       - uses: actions/checkout@v4.1.6 | ||||||
|  |  | ||||||
|       - name: Set Node.js 20.x |       - name: Set Node.js 24.x | ||||||
|         uses: actions/setup-node@v4 |         uses: actions/setup-node@v4 | ||||||
|         with: |         with: | ||||||
|           node-version: 20.x |           node-version: 24.x | ||||||
|  |  | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: npm ci |         run: npm ci | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @@ -18,7 +18,7 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|       - uses: actions/setup-node@v4 |       - uses: actions/setup-node@v4 | ||||||
|         with: |         with: | ||||||
|           node-version: 20.x |           node-version: 24.x | ||||||
|       - uses: actions/checkout@v4.1.6 |       - uses: actions/checkout@v4.1.6 | ||||||
|       - run: npm ci |       - run: npm ci | ||||||
|       - run: npm run build |       - run: npm run build | ||||||
| @@ -302,12 +302,15 @@ jobs: | |||||||
|       # Clone this repo |       # Clone this repo | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v4.1.6 |         uses: actions/checkout@v4.1.6 | ||||||
|  |         with: | ||||||
|  |           path: actions-checkout | ||||||
|  |  | ||||||
|       # Basic checkout using git |       # Basic checkout using git | ||||||
|       - name: Checkout basic |       - name: Checkout basic | ||||||
|         id: checkout |         id: checkout | ||||||
|         uses: ./ |         uses: ./actions-checkout | ||||||
|         with: |         with: | ||||||
|  |           path: cloned-using-local-action | ||||||
|           ref: test-data/v2/basic |           ref: test-data/v2/basic | ||||||
|  |  | ||||||
|       # Verify output |       # Verify output | ||||||
| @@ -325,7 +328,3 @@ jobs: | |||||||
|             echo "Expected commit to be 82f71901cf8c021332310dcc8cdba84c4193ff5d" |             echo "Expected commit to be 82f71901cf8c021332310dcc8cdba84c4193ff5d" | ||||||
|             exit 1 |             exit 1 | ||||||
|           fi |           fi | ||||||
|  |  | ||||||
|       # needed to make checkout post cleanup succeed |  | ||||||
|       - name: Fix Checkout |  | ||||||
|         uses: actions/checkout@v4.1.6 |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/update-main-version.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/update-main-version.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,6 +11,7 @@ on: | |||||||
|         type: choice |         type: choice | ||||||
|         description: The major version to update |         description: The major version to update | ||||||
|         options: |         options: | ||||||
|  |           - v5 | ||||||
|           - v4 |           - v4 | ||||||
|           - v3 |           - v3 | ||||||
|           - v2 |           - v2 | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.licenses/npm/@octokit/endpoint.dep.yml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								.licenses/npm/@octokit/endpoint.dep.yml
									
									
									
										generated
									
									
									
								
							| @@ -1,9 +1,9 @@ | |||||||
| --- | --- | ||||||
| name: "@octokit/endpoint" | name: "@octokit/endpoint" | ||||||
| version: 9.0.5 | version: 9.0.6 | ||||||
| type: npm | type: npm | ||||||
| summary: Turns REST API endpoints into generic request options | summary: Turns REST API endpoints into generic request options | ||||||
| homepage:  | homepage: | ||||||
| license: mit | license: mit | ||||||
| licenses: | licenses: | ||||||
| - sources: LICENSE | - sources: LICENSE | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| --- | --- | ||||||
| name: "@octokit/plugin-paginate-rest" | name: "@octokit/plugin-paginate-rest" | ||||||
| version: 9.2.1 | version: 9.2.2 | ||||||
| type: npm | type: npm | ||||||
| summary: Octokit plugin to paginate REST API endpoint responses | summary: Octokit plugin to paginate REST API endpoint responses | ||||||
| homepage:  | homepage: | ||||||
| license: mit | license: mit | ||||||
| licenses: | licenses: | ||||||
| - sources: LICENSE | - sources: LICENSE | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.licenses/npm/@octokit/request-error.dep.yml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								.licenses/npm/@octokit/request-error.dep.yml
									
									
									
										generated
									
									
									
								
							| @@ -1,9 +1,9 @@ | |||||||
| --- | --- | ||||||
| name: "@octokit/request-error" | name: "@octokit/request-error" | ||||||
| version: 5.1.0 | version: 5.1.1 | ||||||
| type: npm | type: npm | ||||||
| summary: Error class for Octokit request errors | summary: Error class for Octokit request errors | ||||||
| homepage:  | homepage: | ||||||
| license: mit | license: mit | ||||||
| licenses: | licenses: | ||||||
| - sources: LICENSE | - sources: LICENSE | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.licenses/npm/@octokit/request.dep.yml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								.licenses/npm/@octokit/request.dep.yml
									
									
									
										generated
									
									
									
								
							| @@ -1,10 +1,10 @@ | |||||||
| --- | --- | ||||||
| name: "@octokit/request" | name: "@octokit/request" | ||||||
| version: 8.4.0 | version: 8.4.1 | ||||||
| type: npm | type: npm | ||||||
| summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers | summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers | ||||||
|   and Node |   and Node | ||||||
| homepage:  | homepage: | ||||||
| license: mit | license: mit | ||||||
| licenses: | licenses: | ||||||
| - sources: LICENSE | - sources: LICENSE | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.licenses/npm/undici.dep.yml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.licenses/npm/undici.dep.yml
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| --- | --- | ||||||
| name: undici | name: undici | ||||||
| version: 5.28.4 | version: 5.29.0 | ||||||
| type: npm | type: npm | ||||||
| summary: An HTTP/1.1 client, written from scratch for Node.js | summary: An HTTP/1.1 client, written from scratch for Node.js | ||||||
| homepage: https://undici.nodejs.org | homepage: https://undici.nodejs.org | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,18 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  | ## V5.0.0 | ||||||
|  | * Update actions checkout to use node 24 by @salmanmkc in https://github.com/actions/checkout/pull/2226 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## V4.3.0 | ||||||
|  | * docs: update README.md by @motss in https://github.com/actions/checkout/pull/1971 | ||||||
|  | * Add internal repos for checking out multiple repositories by @mouismail in https://github.com/actions/checkout/pull/1977 | ||||||
|  | * Documentation update - add recommended permissions to Readme by @benwells in https://github.com/actions/checkout/pull/2043 | ||||||
|  | * Adjust positioning of user email note and permissions heading by @joshmgross in https://github.com/actions/checkout/pull/2044 | ||||||
|  | * Update README.md by @nebuk89 in https://github.com/actions/checkout/pull/2194 | ||||||
|  | * Update CODEOWNERS for actions by @TingluoHuang in https://github.com/actions/checkout/pull/2224 | ||||||
|  | * Update package dependencies by @salmanmkc in https://github.com/actions/checkout/pull/2236 | ||||||
|  |  | ||||||
| ## v4.2.2 | ## v4.2.2 | ||||||
| * `url-helper.ts` now leverages well-known environment variables by @jww3 in https://github.com/actions/checkout/pull/1941 | * `url-helper.ts` now leverages well-known environment variables by @jww3 in https://github.com/actions/checkout/pull/1941 | ||||||
| * Expand unit test coverage for `isGhes` by @jww3 in https://github.com/actions/checkout/pull/1946 | * Expand unit test coverage for `isGhes` by @jww3 in https://github.com/actions/checkout/pull/1946 | ||||||
|   | |||||||
							
								
								
									
										77
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,5 +1,13 @@ | |||||||
| [](https://github.com/actions/checkout/actions/workflows/test.yml) | [](https://github.com/actions/checkout/actions/workflows/test.yml) | ||||||
|  |  | ||||||
|  | # Checkout V5 | ||||||
|  |  | ||||||
|  | ## What's new | ||||||
|  |  | ||||||
|  | - Updated to the node24 runtime | ||||||
|  |   - This requires a minimum Actions Runner version of [v2.327.1](https://github.com/actions/runner/releases/tag/v2.327.1) to run. | ||||||
|  |  | ||||||
|  |  | ||||||
| # Checkout V4 | # Checkout V4 | ||||||
|  |  | ||||||
| This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it. | This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it. | ||||||
| @@ -36,7 +44,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
|  |  | ||||||
| <!-- start usage --> | <!-- start usage --> | ||||||
| ```yaml | ```yaml | ||||||
| - uses: actions/checkout@v4 | - uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     # Repository name with owner. For example, actions/checkout |     # Repository name with owner. For example, actions/checkout | ||||||
|     # Default: ${{ github.repository }} |     # Default: ${{ github.repository }} | ||||||
| @@ -149,24 +157,33 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
|  |  | ||||||
| # Scenarios | # Scenarios | ||||||
|  |  | ||||||
| - [Fetch only the root files](#Fetch-only-the-root-files) | - [Checkout V5](#checkout-v5) | ||||||
| - [Fetch only the root files and `.github` and `src` folder](#Fetch-only-the-root-files-and-github-and-src-folder) |   - [What's new](#whats-new) | ||||||
| - [Fetch only a single file](#Fetch-only-a-single-file) | - [Checkout V4](#checkout-v4) | ||||||
| - [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches) |     - [Note](#note) | ||||||
| - [Checkout a different branch](#Checkout-a-different-branch) | - [What's new](#whats-new-1) | ||||||
| - [Checkout HEAD^](#Checkout-HEAD) | - [Usage](#usage) | ||||||
| - [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side) | - [Scenarios](#scenarios) | ||||||
| - [Checkout multiple repos (nested)](#Checkout-multiple-repos-nested) |   - [Fetch only the root files](#fetch-only-the-root-files) | ||||||
| - [Checkout multiple repos (private)](#Checkout-multiple-repos-private) |   - [Fetch only the root files and `.github` and `src` folder](#fetch-only-the-root-files-and-github-and-src-folder) | ||||||
| - [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit) |   - [Fetch only a single file](#fetch-only-a-single-file) | ||||||
| - [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event) |   - [Fetch all history for all tags and branches](#fetch-all-history-for-all-tags-and-branches) | ||||||
| - [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token) |   - [Checkout a different branch](#checkout-a-different-branch) | ||||||
| - [Push a commit to a PR using the built-in token](#Push-a-commit-to-a-PR-using-the-built-in-token) |   - [Checkout HEAD^](#checkout-head) | ||||||
|  |   - [Checkout multiple repos (side by side)](#checkout-multiple-repos-side-by-side) | ||||||
|  |   - [Checkout multiple repos (nested)](#checkout-multiple-repos-nested) | ||||||
|  |   - [Checkout multiple repos (private)](#checkout-multiple-repos-private) | ||||||
|  |   - [Checkout pull request HEAD commit instead of merge commit](#checkout-pull-request-head-commit-instead-of-merge-commit) | ||||||
|  |   - [Checkout pull request on closed event](#checkout-pull-request-on-closed-event) | ||||||
|  |   - [Push a commit using the built-in token](#push-a-commit-using-the-built-in-token) | ||||||
|  |   - [Push a commit to a PR using the built-in token](#push-a-commit-to-a-pr-using-the-built-in-token) | ||||||
|  | - [Recommended permissions](#recommended-permissions) | ||||||
|  | - [License](#license) | ||||||
|  |  | ||||||
| ## Fetch only the root files | ## Fetch only the root files | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - uses: actions/checkout@v4 | - uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     sparse-checkout: . |     sparse-checkout: . | ||||||
| ``` | ``` | ||||||
| @@ -174,7 +191,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
| ## Fetch only the root files and `.github` and `src` folder | ## Fetch only the root files and `.github` and `src` folder | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - uses: actions/checkout@v4 | - uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     sparse-checkout: | |     sparse-checkout: | | ||||||
|       .github |       .github | ||||||
| @@ -184,7 +201,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
| ## Fetch only a single file | ## Fetch only a single file | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - uses: actions/checkout@v4 | - uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     sparse-checkout: | |     sparse-checkout: | | ||||||
|       README.md |       README.md | ||||||
| @@ -194,7 +211,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
| ## Fetch all history for all tags and branches | ## Fetch all history for all tags and branches | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - uses: actions/checkout@v4 | - uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     fetch-depth: 0 |     fetch-depth: 0 | ||||||
| ``` | ``` | ||||||
| @@ -202,7 +219,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
| ## Checkout a different branch | ## Checkout a different branch | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - uses: actions/checkout@v4 | - uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     ref: my-branch |     ref: my-branch | ||||||
| ``` | ``` | ||||||
| @@ -210,7 +227,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
| ## Checkout HEAD^ | ## Checkout HEAD^ | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - uses: actions/checkout@v4 | - uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     fetch-depth: 2 |     fetch-depth: 2 | ||||||
| - run: git checkout HEAD^ | - run: git checkout HEAD^ | ||||||
| @@ -220,12 +237,12 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - name: Checkout | - name: Checkout | ||||||
|   uses: actions/checkout@v4 |   uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     path: main |     path: main | ||||||
|  |  | ||||||
| - name: Checkout tools repo | - name: Checkout tools repo | ||||||
|   uses: actions/checkout@v4 |   uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     repository: my-org/my-tools |     repository: my-org/my-tools | ||||||
|     path: my-tools |     path: my-tools | ||||||
| @@ -236,10 +253,10 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - name: Checkout | - name: Checkout | ||||||
|   uses: actions/checkout@v4 |   uses: actions/checkout@v5 | ||||||
|  |  | ||||||
| - name: Checkout tools repo | - name: Checkout tools repo | ||||||
|   uses: actions/checkout@v4 |   uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     repository: my-org/my-tools |     repository: my-org/my-tools | ||||||
|     path: my-tools |     path: my-tools | ||||||
| @@ -250,12 +267,12 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - name: Checkout | - name: Checkout | ||||||
|   uses: actions/checkout@v4 |   uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     path: main |     path: main | ||||||
|  |  | ||||||
| - name: Checkout private tools | - name: Checkout private tools | ||||||
|   uses: actions/checkout@v4 |   uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     repository: my-org/my-private-tools |     repository: my-org/my-private-tools | ||||||
|     token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT |     token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT | ||||||
| @@ -268,7 +285,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/ | |||||||
| ## Checkout pull request HEAD commit instead of merge commit | ## Checkout pull request HEAD commit instead of merge commit | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| - uses: actions/checkout@v4 | - uses: actions/checkout@v5 | ||||||
|   with: |   with: | ||||||
|     ref: ${{ github.event.pull_request.head.sha }} |     ref: ${{ github.event.pull_request.head.sha }} | ||||||
| ``` | ``` | ||||||
| @@ -284,7 +301,7 @@ jobs: | |||||||
|   build: |   build: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - uses: actions/checkout@v5 | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ## Push a commit using the built-in token | ## Push a commit using the built-in token | ||||||
| @@ -295,7 +312,7 @@ jobs: | |||||||
|   build: |   build: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - uses: actions/checkout@v5 | ||||||
|       - run: | |       - run: | | ||||||
|           date > generated.txt |           date > generated.txt | ||||||
|           # Note: the following account information will not work on GHES |           # Note: the following account information will not work on GHES | ||||||
| @@ -317,7 +334,7 @@ jobs: | |||||||
|   build: |   build: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - uses: actions/checkout@v5 | ||||||
|         with: |         with: | ||||||
|           ref: ${{ github.head_ref }} |           ref: ${{ github.head_ref }} | ||||||
|       - run: | |       - run: | | ||||||
|   | |||||||
| @@ -86,16 +86,29 @@ describe('git-auth-helper tests', () => { | |||||||
|     // Act |     // Act | ||||||
|     await authHelper.configureAuth() |     await authHelper.configureAuth() | ||||||
|  |  | ||||||
|     // Assert config |     // Assert config - check that .git/config contains includeIf entries | ||||||
|     const configContent = ( |     const localConfigContent = ( | ||||||
|       await fs.promises.readFile(localGitConfigPath) |       await fs.promises.readFile(localGitConfigPath) | ||||||
|     ).toString() |     ).toString() | ||||||
|  |     expect( | ||||||
|  |       localConfigContent.indexOf('includeIf.gitdir:') | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |     // Assert credentials config file contains the actual credentials | ||||||
|  |     const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||||
|  |       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||||
|  |     ) | ||||||
|  |     expect(credentialsFiles.length).toBe(1) | ||||||
|  |     const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0]) | ||||||
|  |     const credentialsContent = ( | ||||||
|  |       await fs.promises.readFile(credentialsConfigPath) | ||||||
|  |     ).toString() | ||||||
|     const basicCredential = Buffer.from( |     const basicCredential = Buffer.from( | ||||||
|       `x-access-token:${settings.authToken}`, |       `x-access-token:${settings.authToken}`, | ||||||
|       'utf8' |       'utf8' | ||||||
|     ).toString('base64') |     ).toString('base64') | ||||||
|     expect( |     expect( | ||||||
|       configContent.indexOf( |       credentialsContent.indexOf( | ||||||
|         `http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}` |         `http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||||
|       ) |       ) | ||||||
|     ).toBeGreaterThanOrEqual(0) |     ).toBeGreaterThanOrEqual(0) | ||||||
| @@ -120,7 +133,7 @@ describe('git-auth-helper tests', () => { | |||||||
|     'inject https://github.com as github server url' |     'inject https://github.com as github server url' | ||||||
|   it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => { |   it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => { | ||||||
|     await testAuthHeader( |     await testAuthHeader( | ||||||
|       configureAuth_AcceptsGitHubServerUrl, |       configureAuth_AcceptsGitHubServerUrlSetToGHEC, | ||||||
|       'https://github.com' |       'https://github.com' | ||||||
|     ) |     ) | ||||||
|   }) |   }) | ||||||
| @@ -141,12 +154,17 @@ describe('git-auth-helper tests', () => { | |||||||
|       // Act |       // Act | ||||||
|       await authHelper.configureAuth() |       await authHelper.configureAuth() | ||||||
|  |  | ||||||
|       // Assert config |       // Assert config - check credentials config file (not local .git/config) | ||||||
|       const configContent = ( |       const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||||
|         await fs.promises.readFile(localGitConfigPath) |         f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||||
|  |       ) | ||||||
|  |       expect(credentialsFiles.length).toBe(1) | ||||||
|  |       const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0]) | ||||||
|  |       const credentialsContent = ( | ||||||
|  |         await fs.promises.readFile(credentialsConfigPath) | ||||||
|       ).toString() |       ).toString() | ||||||
|       expect( |       expect( | ||||||
|         configContent.indexOf( |         credentialsContent.indexOf( | ||||||
|           `http.https://github.com/.extraheader AUTHORIZATION` |           `http.https://github.com/.extraheader AUTHORIZATION` | ||||||
|         ) |         ) | ||||||
|       ).toBeGreaterThanOrEqual(0) |       ).toBeGreaterThanOrEqual(0) | ||||||
| @@ -251,13 +269,16 @@ describe('git-auth-helper tests', () => { | |||||||
|       expectedSshCommand |       expectedSshCommand | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     // Asserty git config |     // Assert git config | ||||||
|     const gitConfigLines = (await fs.promises.readFile(localGitConfigPath)) |     const gitConfigLines = (await fs.promises.readFile(localGitConfigPath)) | ||||||
|       .toString() |       .toString() | ||||||
|       .split('\n') |       .split('\n') | ||||||
|       .filter(x => x) |       .filter(x => x) | ||||||
|     expect(gitConfigLines).toHaveLength(1) |     // Should have includeIf entries pointing to credentials file | ||||||
|     expect(gitConfigLines[0]).toMatch(/^http\./) |     expect(gitConfigLines.length).toBeGreaterThan(0) | ||||||
|  |     expect( | ||||||
|  |       gitConfigLines.some(line => line.indexOf('includeIf.gitdir:') >= 0) | ||||||
|  |     ).toBeTruthy() | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   const configureAuth_setsSshCommandWhenPersistCredentialsTrue = |   const configureAuth_setsSshCommandWhenPersistCredentialsTrue = | ||||||
| @@ -419,8 +440,20 @@ describe('git-auth-helper tests', () => { | |||||||
|     expect( |     expect( | ||||||
|       configContent.indexOf('value-from-global-config') |       configContent.indexOf('value-from-global-config') | ||||||
|     ).toBeGreaterThanOrEqual(0) |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |     // Global config should have include.path pointing to credentials file | ||||||
|  |     expect(configContent.indexOf('include.path')).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |     // Check credentials in the separate config file | ||||||
|  |     const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||||
|  |       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||||
|  |     ) | ||||||
|  |     expect(credentialsFiles.length).toBeGreaterThan(0) | ||||||
|  |     const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0]) | ||||||
|  |     const credentialsContent = ( | ||||||
|  |       await fs.promises.readFile(credentialsConfigPath) | ||||||
|  |     ).toString() | ||||||
|     expect( |     expect( | ||||||
|       configContent.indexOf( |       credentialsContent.indexOf( | ||||||
|         `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` |         `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||||
|       ) |       ) | ||||||
|     ).toBeGreaterThanOrEqual(0) |     ).toBeGreaterThanOrEqual(0) | ||||||
| @@ -463,8 +496,20 @@ describe('git-auth-helper tests', () => { | |||||||
|       const configContent = ( |       const configContent = ( | ||||||
|         await fs.promises.readFile(path.join(git.env['HOME'], '.gitconfig')) |         await fs.promises.readFile(path.join(git.env['HOME'], '.gitconfig')) | ||||||
|       ).toString() |       ).toString() | ||||||
|  |       // Global config should have include.path pointing to credentials file | ||||||
|  |       expect(configContent.indexOf('include.path')).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |       // Check credentials in the separate config file | ||||||
|  |       const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||||
|  |         f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||||
|  |       ) | ||||||
|  |       expect(credentialsFiles.length).toBeGreaterThan(0) | ||||||
|  |       const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0]) | ||||||
|  |       const credentialsContent = ( | ||||||
|  |         await fs.promises.readFile(credentialsConfigPath) | ||||||
|  |       ).toString() | ||||||
|       expect( |       expect( | ||||||
|         configContent.indexOf( |         credentialsContent.indexOf( | ||||||
|           `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` |           `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||||
|         ) |         ) | ||||||
|       ).toBeGreaterThanOrEqual(0) |       ).toBeGreaterThanOrEqual(0) | ||||||
| @@ -550,15 +595,15 @@ describe('git-auth-helper tests', () => { | |||||||
|       await authHelper.configureSubmoduleAuth() |       await authHelper.configureSubmoduleAuth() | ||||||
|  |  | ||||||
|       // Assert |       // Assert | ||||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(4) |       // Should configure insteadOf (2 calls for two values) | ||||||
|  |       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3) | ||||||
|       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( |       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( | ||||||
|         /unset-all.*insteadOf/ |         /unset-all.*insteadOf/ | ||||||
|       ) |       ) | ||||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/) |       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch( | ||||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch( |  | ||||||
|         /url.*insteadOf.*git@github.com:/ |         /url.*insteadOf.*git@github.com:/ | ||||||
|       ) |       ) | ||||||
|       expect(mockSubmoduleForeach.mock.calls[3][0]).toMatch( |       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch( | ||||||
|         /url.*insteadOf.*org-123456@github.com:/ |         /url.*insteadOf.*org-123456@github.com:/ | ||||||
|       ) |       ) | ||||||
|     } |     } | ||||||
| @@ -589,12 +634,12 @@ describe('git-auth-helper tests', () => { | |||||||
|       await authHelper.configureSubmoduleAuth() |       await authHelper.configureSubmoduleAuth() | ||||||
|  |  | ||||||
|       // Assert |       // Assert | ||||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3) |       // Should configure sshCommand (1 call) | ||||||
|  |       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2) | ||||||
|       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( |       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( | ||||||
|         /unset-all.*insteadOf/ |         /unset-all.*insteadOf/ | ||||||
|       ) |       ) | ||||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/) |       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/core\.sshCommand/) | ||||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/core\.sshCommand/) |  | ||||||
|     } |     } | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
| @@ -660,19 +705,201 @@ describe('git-auth-helper tests', () => { | |||||||
|     await setup(removeAuth_removesToken) |     await setup(removeAuth_removesToken) | ||||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) |     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||||
|     await authHelper.configureAuth() |     await authHelper.configureAuth() | ||||||
|     let gitConfigContent = ( |  | ||||||
|  |     // Verify includeIf entries exist in local config | ||||||
|  |     let localConfigContent = ( | ||||||
|       await fs.promises.readFile(localGitConfigPath) |       await fs.promises.readFile(localGitConfigPath) | ||||||
|     ).toString() |     ).toString() | ||||||
|     expect(gitConfigContent.indexOf('http.')).toBeGreaterThanOrEqual(0) // sanity check |     expect( | ||||||
|  |       localConfigContent.indexOf('includeIf.gitdir:') | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |     // Verify both host and container includeIf entries are present | ||||||
|  |     const hostGitDir = path.join(workspace, '.git').replace(/\\/g, '/') | ||||||
|  |     expect( | ||||||
|  |       localConfigContent.indexOf(`includeIf.gitdir:${hostGitDir}.path`) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |     expect( | ||||||
|  |       localConfigContent.indexOf('includeIf.gitdir:/github/workspace/.git.path') | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |     // Verify credentials file exists | ||||||
|  |     let credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||||
|  |       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||||
|  |     ) | ||||||
|  |     expect(credentialsFiles.length).toBe(1) | ||||||
|  |     const credentialsFilePath = path.join(runnerTemp, credentialsFiles[0]) | ||||||
|  |  | ||||||
|  |     // Verify credentials file contains the auth token | ||||||
|  |     let credentialsContent = ( | ||||||
|  |       await fs.promises.readFile(credentialsFilePath) | ||||||
|  |     ).toString() | ||||||
|  |     const basicCredential = Buffer.from( | ||||||
|  |       `x-access-token:${settings.authToken}`, | ||||||
|  |       'utf8' | ||||||
|  |     ).toString('base64') | ||||||
|  |     expect( | ||||||
|  |       credentialsContent.indexOf( | ||||||
|  |         `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||||
|  |       ) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |     // Verify the includeIf entries point to the credentials file | ||||||
|  |     const containerCredentialsPath = path.posix.join( | ||||||
|  |       '/github/runner_temp', | ||||||
|  |       path.basename(credentialsFilePath) | ||||||
|  |     ) | ||||||
|  |     expect( | ||||||
|  |       localConfigContent.indexOf(credentialsFilePath) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |     expect( | ||||||
|  |       localConfigContent.indexOf(containerCredentialsPath) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|     // Act |     // Act | ||||||
|     await authHelper.removeAuth() |     await authHelper.removeAuth() | ||||||
|  |  | ||||||
|     // Assert git config |     // Assert all includeIf entries removed from local git config | ||||||
|     gitConfigContent = ( |     localConfigContent = ( | ||||||
|       await fs.promises.readFile(localGitConfigPath) |       await fs.promises.readFile(localGitConfigPath) | ||||||
|     ).toString() |     ).toString() | ||||||
|     expect(gitConfigContent.indexOf('http.')).toBeLessThan(0) |     expect(localConfigContent.indexOf('includeIf.gitdir:')).toBeLessThan(0) | ||||||
|  |     expect( | ||||||
|  |       localConfigContent.indexOf(`includeIf.gitdir:${hostGitDir}.path`) | ||||||
|  |     ).toBeLessThan(0) | ||||||
|  |     expect( | ||||||
|  |       localConfigContent.indexOf('includeIf.gitdir:/github/workspace/.git.path') | ||||||
|  |     ).toBeLessThan(0) | ||||||
|  |     expect(localConfigContent.indexOf(credentialsFilePath)).toBeLessThan(0) | ||||||
|  |     expect(localConfigContent.indexOf(containerCredentialsPath)).toBeLessThan(0) | ||||||
|  |  | ||||||
|  |     // Assert credentials config file deleted | ||||||
|  |     credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||||
|  |       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||||
|  |     ) | ||||||
|  |     expect(credentialsFiles.length).toBe(0) | ||||||
|  |  | ||||||
|  |     // Verify credentials file no longer exists on disk | ||||||
|  |     try { | ||||||
|  |       await fs.promises.stat(credentialsFilePath) | ||||||
|  |       throw new Error('Credentials file should have been deleted') | ||||||
|  |     } catch (err) { | ||||||
|  |       if ((err as any)?.code !== 'ENOENT') { | ||||||
|  |         throw err | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   const removeAuth_removesTokenFromSubmodules = | ||||||
|  |     'removeAuth removes token from submodules' | ||||||
|  |   it(removeAuth_removesTokenFromSubmodules, async () => { | ||||||
|  |     // Arrange | ||||||
|  |     await setup(removeAuth_removesTokenFromSubmodules) | ||||||
|  |  | ||||||
|  |     // Create fake submodule config paths | ||||||
|  |     const submodule1Dir = path.join(workspace, '.git', 'modules', 'submodule-1') | ||||||
|  |     const submodule2Dir = path.join(workspace, '.git', 'modules', 'submodule-2') | ||||||
|  |     const submodule1ConfigPath = path.join(submodule1Dir, 'config') | ||||||
|  |     const submodule2ConfigPath = path.join(submodule2Dir, 'config') | ||||||
|  |  | ||||||
|  |     await fs.promises.mkdir(submodule1Dir, {recursive: true}) | ||||||
|  |     await fs.promises.mkdir(submodule2Dir, {recursive: true}) | ||||||
|  |     await fs.promises.writeFile(submodule1ConfigPath, '') | ||||||
|  |     await fs.promises.writeFile(submodule2ConfigPath, '') | ||||||
|  |  | ||||||
|  |     // Mock getSubmoduleConfigPaths to return our fake submodules (for both configure and remove) | ||||||
|  |     const mockGetSubmoduleConfigPaths = | ||||||
|  |       git.getSubmoduleConfigPaths as jest.Mock<any, any> | ||||||
|  |     mockGetSubmoduleConfigPaths.mockResolvedValue([ | ||||||
|  |       submodule1ConfigPath, | ||||||
|  |       submodule2ConfigPath | ||||||
|  |     ]) | ||||||
|  |  | ||||||
|  |     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||||
|  |     await authHelper.configureAuth() | ||||||
|  |     await authHelper.configureSubmoduleAuth() | ||||||
|  |  | ||||||
|  |     // Verify credentials file exists | ||||||
|  |     let credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||||
|  |       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||||
|  |     ) | ||||||
|  |     expect(credentialsFiles.length).toBe(1) | ||||||
|  |     const credentialsFilePath = path.join(runnerTemp, credentialsFiles[0]) | ||||||
|  |  | ||||||
|  |     // Verify submodule 1 config has includeIf entries | ||||||
|  |     let submodule1Content = ( | ||||||
|  |       await fs.promises.readFile(submodule1ConfigPath) | ||||||
|  |     ).toString() | ||||||
|  |     const submodule1GitDir = submodule1Dir.replace(/\\/g, '/') | ||||||
|  |     expect( | ||||||
|  |       submodule1Content.indexOf(`includeIf.gitdir:${submodule1GitDir}.path`) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |     expect( | ||||||
|  |       submodule1Content.indexOf(credentialsFilePath) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |     // Verify submodule 2 config has includeIf entries | ||||||
|  |     let submodule2Content = ( | ||||||
|  |       await fs.promises.readFile(submodule2ConfigPath) | ||||||
|  |     ).toString() | ||||||
|  |     const submodule2GitDir = submodule2Dir.replace(/\\/g, '/') | ||||||
|  |     expect( | ||||||
|  |       submodule2Content.indexOf(`includeIf.gitdir:${submodule2GitDir}.path`) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |     expect( | ||||||
|  |       submodule2Content.indexOf(credentialsFilePath) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |     // Verify both host and container paths are in each submodule config | ||||||
|  |     const containerCredentialsPath = path.posix.join( | ||||||
|  |       '/github/runner_temp', | ||||||
|  |       path.basename(credentialsFilePath) | ||||||
|  |     ) | ||||||
|  |     expect( | ||||||
|  |       submodule1Content.indexOf(containerCredentialsPath) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |     expect( | ||||||
|  |       submodule2Content.indexOf(containerCredentialsPath) | ||||||
|  |     ).toBeGreaterThanOrEqual(0) | ||||||
|  |  | ||||||
|  |     // Act - ensure mock persists for removeAuth | ||||||
|  |     mockGetSubmoduleConfigPaths.mockResolvedValue([ | ||||||
|  |       submodule1ConfigPath, | ||||||
|  |       submodule2ConfigPath | ||||||
|  |     ]) | ||||||
|  |     await authHelper.removeAuth() | ||||||
|  |  | ||||||
|  |     // Assert submodule 1 includeIf entries removed | ||||||
|  |     submodule1Content = ( | ||||||
|  |       await fs.promises.readFile(submodule1ConfigPath) | ||||||
|  |     ).toString() | ||||||
|  |     expect(submodule1Content.indexOf('includeIf.gitdir:')).toBeLessThan(0) | ||||||
|  |     expect(submodule1Content.indexOf(credentialsFilePath)).toBeLessThan(0) | ||||||
|  |     expect(submodule1Content.indexOf(containerCredentialsPath)).toBeLessThan(0) | ||||||
|  |  | ||||||
|  |     // Assert submodule 2 includeIf entries removed | ||||||
|  |     submodule2Content = ( | ||||||
|  |       await fs.promises.readFile(submodule2ConfigPath) | ||||||
|  |     ).toString() | ||||||
|  |     expect(submodule2Content.indexOf('includeIf.gitdir:')).toBeLessThan(0) | ||||||
|  |     expect(submodule2Content.indexOf(credentialsFilePath)).toBeLessThan(0) | ||||||
|  |     expect(submodule2Content.indexOf(containerCredentialsPath)).toBeLessThan(0) | ||||||
|  |  | ||||||
|  |     // Assert credentials config file deleted | ||||||
|  |     credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter( | ||||||
|  |       f => f.startsWith('git-credentials-') && f.endsWith('.config') | ||||||
|  |     ) | ||||||
|  |     expect(credentialsFiles.length).toBe(0) | ||||||
|  |  | ||||||
|  |     // Verify credentials file no longer exists on disk | ||||||
|  |     try { | ||||||
|  |       await fs.promises.stat(credentialsFilePath) | ||||||
|  |       throw new Error('Credentials file should have been deleted') | ||||||
|  |     } catch (err) { | ||||||
|  |       if ((err as any)?.code !== 'ENOENT') { | ||||||
|  |         throw err | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   const removeGlobalConfig_removesOverride = |   const removeGlobalConfig_removesOverride = | ||||||
| @@ -701,6 +928,52 @@ describe('git-auth-helper tests', () => { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|  |   const testCredentialsConfigPath_matchesCredentialsConfigPaths = | ||||||
|  |     'testCredentialsConfigPath matches credentials config paths' | ||||||
|  |   it(testCredentialsConfigPath_matchesCredentialsConfigPaths, async () => { | ||||||
|  |     // Arrange | ||||||
|  |     await setup(testCredentialsConfigPath_matchesCredentialsConfigPaths) | ||||||
|  |     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||||
|  |  | ||||||
|  |     // Get a real credentials config path | ||||||
|  |     const credentialsConfigPath = await ( | ||||||
|  |       authHelper as any | ||||||
|  |     ).getCredentialsConfigPath() | ||||||
|  |  | ||||||
|  |     // Act & Assert | ||||||
|  |     expect( | ||||||
|  |       (authHelper as any).testCredentialsConfigPath(credentialsConfigPath) | ||||||
|  |     ).toBe(true) | ||||||
|  |     expect( | ||||||
|  |       (authHelper as any).testCredentialsConfigPath( | ||||||
|  |         '/some/path/git-credentials-12345678-abcd-1234-5678-123456789012.config' | ||||||
|  |       ) | ||||||
|  |     ).toBe(true) | ||||||
|  |     expect( | ||||||
|  |       (authHelper as any).testCredentialsConfigPath( | ||||||
|  |         '/some/path/git-credentials-abcdef12-3456-7890-abcd-ef1234567890.config' | ||||||
|  |       ) | ||||||
|  |     ).toBe(true) | ||||||
|  |  | ||||||
|  |     // Test invalid paths | ||||||
|  |     expect( | ||||||
|  |       (authHelper as any).testCredentialsConfigPath( | ||||||
|  |         '/some/path/other-config.config' | ||||||
|  |       ) | ||||||
|  |     ).toBe(false) | ||||||
|  |     expect( | ||||||
|  |       (authHelper as any).testCredentialsConfigPath( | ||||||
|  |         '/some/path/git-credentials-invalid.config' | ||||||
|  |       ) | ||||||
|  |     ).toBe(false) | ||||||
|  |     expect( | ||||||
|  |       (authHelper as any).testCredentialsConfigPath( | ||||||
|  |         '/some/path/git-credentials-.config' | ||||||
|  |       ) | ||||||
|  |     ).toBe(false) | ||||||
|  |     expect((authHelper as any).testCredentialsConfigPath('')).toBe(false) | ||||||
|  |   }) | ||||||
| }) | }) | ||||||
|  |  | ||||||
| async function setup(testName: string): Promise<void> { | async function setup(testName: string): Promise<void> { | ||||||
| @@ -715,6 +988,7 @@ async function setup(testName: string): Promise<void> { | |||||||
|   await fs.promises.mkdir(tempHomedir, {recursive: true}) |   await fs.promises.mkdir(tempHomedir, {recursive: true}) | ||||||
|   process.env['RUNNER_TEMP'] = runnerTemp |   process.env['RUNNER_TEMP'] = runnerTemp | ||||||
|   process.env['HOME'] = tempHomedir |   process.env['HOME'] = tempHomedir | ||||||
|  |   process.env['GITHUB_WORKSPACE'] = workspace | ||||||
|  |  | ||||||
|   // Create git config |   // Create git config | ||||||
|   globalGitConfigPath = path.join(tempHomedir, '.gitconfig') |   globalGitConfigPath = path.join(tempHomedir, '.gitconfig') | ||||||
| @@ -733,10 +1007,20 @@ async function setup(testName: string): Promise<void> { | |||||||
|     checkout: jest.fn(), |     checkout: jest.fn(), | ||||||
|     checkoutDetach: jest.fn(), |     checkoutDetach: jest.fn(), | ||||||
|     config: jest.fn( |     config: jest.fn( | ||||||
|       async (key: string, value: string, globalConfig?: boolean) => { |       async ( | ||||||
|         const configPath = globalConfig |         key: string, | ||||||
|           ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') |         value: string, | ||||||
|           : localGitConfigPath |         globalConfig?: boolean, | ||||||
|  |         add?: boolean, | ||||||
|  |         configFile?: string | ||||||
|  |       ) => { | ||||||
|  |         const configPath = | ||||||
|  |           configFile || | ||||||
|  |           (globalConfig | ||||||
|  |             ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||||
|  |             : localGitConfigPath) | ||||||
|  |         // Ensure directory exists | ||||||
|  |         await fs.promises.mkdir(path.dirname(configPath), {recursive: true}) | ||||||
|         await fs.promises.appendFile(configPath, `\n${key} ${value}`) |         await fs.promises.appendFile(configPath, `\n${key} ${value}`) | ||||||
|       } |       } | ||||||
|     ), |     ), | ||||||
| @@ -756,6 +1040,7 @@ async function setup(testName: string): Promise<void> { | |||||||
|     env: {}, |     env: {}, | ||||||
|     fetch: jest.fn(), |     fetch: jest.fn(), | ||||||
|     getDefaultBranch: jest.fn(), |     getDefaultBranch: jest.fn(), | ||||||
|  |     getSubmoduleConfigPaths: jest.fn(async () => []), | ||||||
|     getWorkingDirectory: jest.fn(() => workspace), |     getWorkingDirectory: jest.fn(() => workspace), | ||||||
|     init: jest.fn(), |     init: jest.fn(), | ||||||
|     isDetached: jest.fn(), |     isDetached: jest.fn(), | ||||||
| @@ -794,8 +1079,72 @@ async function setup(testName: string): Promise<void> { | |||||||
|         return true |         return true | ||||||
|       } |       } | ||||||
|     ), |     ), | ||||||
|  |     tryConfigUnsetValue: jest.fn( | ||||||
|  |       async ( | ||||||
|  |         key: string, | ||||||
|  |         value: string, | ||||||
|  |         globalConfig?: boolean, | ||||||
|  |         configPath?: string | ||||||
|  |       ): Promise<boolean> => { | ||||||
|  |         const targetConfigPath = | ||||||
|  |           configPath || | ||||||
|  |           (globalConfig | ||||||
|  |             ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||||
|  |             : localGitConfigPath) | ||||||
|  |         let content = await fs.promises.readFile(targetConfigPath) | ||||||
|  |         let lines = content | ||||||
|  |           .toString() | ||||||
|  |           .split('\n') | ||||||
|  |           .filter(x => x) | ||||||
|  |           .filter(x => !(x.startsWith(key) && x.includes(value))) | ||||||
|  |         await fs.promises.writeFile(targetConfigPath, lines.join('\n')) | ||||||
|  |         return true | ||||||
|  |       } | ||||||
|  |     ), | ||||||
|     tryDisableAutomaticGarbageCollection: jest.fn(), |     tryDisableAutomaticGarbageCollection: jest.fn(), | ||||||
|     tryGetFetchUrl: jest.fn(), |     tryGetFetchUrl: jest.fn(), | ||||||
|  |     tryGetConfigValues: jest.fn( | ||||||
|  |       async ( | ||||||
|  |         key: string, | ||||||
|  |         globalConfig?: boolean, | ||||||
|  |         configPath?: string | ||||||
|  |       ): Promise<string[]> => { | ||||||
|  |         const targetConfigPath = | ||||||
|  |           configPath || | ||||||
|  |           (globalConfig | ||||||
|  |             ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||||
|  |             : localGitConfigPath) | ||||||
|  |         const content = await fs.promises.readFile(targetConfigPath) | ||||||
|  |         const lines = content | ||||||
|  |           .toString() | ||||||
|  |           .split('\n') | ||||||
|  |           .filter(x => x && x.startsWith(key)) | ||||||
|  |           .map(x => x.substring(key.length).trim()) | ||||||
|  |         return lines | ||||||
|  |       } | ||||||
|  |     ), | ||||||
|  |     tryGetConfigKeys: jest.fn( | ||||||
|  |       async ( | ||||||
|  |         pattern: string, | ||||||
|  |         globalConfig?: boolean, | ||||||
|  |         configPath?: string | ||||||
|  |       ): Promise<string[]> => { | ||||||
|  |         const targetConfigPath = | ||||||
|  |           configPath || | ||||||
|  |           (globalConfig | ||||||
|  |             ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig') | ||||||
|  |             : localGitConfigPath) | ||||||
|  |         const content = await fs.promises.readFile(targetConfigPath) | ||||||
|  |         const lines = content | ||||||
|  |           .toString() | ||||||
|  |           .split('\n') | ||||||
|  |           .filter(x => x) | ||||||
|  |         const keys = lines | ||||||
|  |           .filter(x => new RegExp(pattern).test(x.split(' ')[0])) | ||||||
|  |           .map(x => x.split(' ')[0]) | ||||||
|  |         return [...new Set(keys)] // Remove duplicates | ||||||
|  |       } | ||||||
|  |     ), | ||||||
|     tryReset: jest.fn(), |     tryReset: jest.fn(), | ||||||
|     version: jest.fn() |     version: jest.fn() | ||||||
|   } |   } | ||||||
| @@ -830,6 +1179,7 @@ async function setup(testName: string): Promise<void> { | |||||||
|  |  | ||||||
| async function getActualSshKeyPath(): Promise<string> { | async function getActualSshKeyPath(): Promise<string> { | ||||||
|   let actualTempFiles = (await fs.promises.readdir(runnerTemp)) |   let actualTempFiles = (await fs.promises.readdir(runnerTemp)) | ||||||
|  |     .filter(x => !x.startsWith('git-credentials-')) // Exclude credentials config file | ||||||
|     .sort() |     .sort() | ||||||
|     .map(x => path.join(runnerTemp, x)) |     .map(x => path.join(runnerTemp, x)) | ||||||
|   if (actualTempFiles.length === 0) { |   if (actualTempFiles.length === 0) { | ||||||
| @@ -843,6 +1193,7 @@ async function getActualSshKeyPath(): Promise<string> { | |||||||
|  |  | ||||||
| async function getActualSshKnownHostsPath(): Promise<string> { | async function getActualSshKnownHostsPath(): Promise<string> { | ||||||
|   let actualTempFiles = (await fs.promises.readdir(runnerTemp)) |   let actualTempFiles = (await fs.promises.readdir(runnerTemp)) | ||||||
|  |     .filter(x => !x.startsWith('git-credentials-')) // Exclude credentials config file | ||||||
|     .sort() |     .sort() | ||||||
|     .map(x => path.join(runnerTemp, x)) |     .map(x => path.join(runnerTemp, x)) | ||||||
|   if (actualTempFiles.length === 0) { |   if (actualTempFiles.length === 0) { | ||||||
|   | |||||||
| @@ -471,6 +471,7 @@ async function setup(testName: string): Promise<void> { | |||||||
|     configExists: jest.fn(), |     configExists: jest.fn(), | ||||||
|     fetch: jest.fn(), |     fetch: jest.fn(), | ||||||
|     getDefaultBranch: jest.fn(), |     getDefaultBranch: jest.fn(), | ||||||
|  |     getSubmoduleConfigPaths: jest.fn(async () => []), | ||||||
|     getWorkingDirectory: jest.fn(() => repositoryPath), |     getWorkingDirectory: jest.fn(() => repositoryPath), | ||||||
|     init: jest.fn(), |     init: jest.fn(), | ||||||
|     isDetached: jest.fn(), |     isDetached: jest.fn(), | ||||||
| @@ -493,12 +494,15 @@ async function setup(testName: string): Promise<void> { | |||||||
|       return true |       return true | ||||||
|     }), |     }), | ||||||
|     tryConfigUnset: jest.fn(), |     tryConfigUnset: jest.fn(), | ||||||
|  |     tryConfigUnsetValue: jest.fn(), | ||||||
|     tryDisableAutomaticGarbageCollection: jest.fn(), |     tryDisableAutomaticGarbageCollection: jest.fn(), | ||||||
|     tryGetFetchUrl: jest.fn(async () => { |     tryGetFetchUrl: jest.fn(async () => { | ||||||
|       // Sanity check - this function shouldn't be called when the .git directory doesn't exist |       // Sanity check - this function shouldn't be called when the .git directory doesn't exist | ||||||
|       await fs.promises.stat(path.join(repositoryPath, '.git')) |       await fs.promises.stat(path.join(repositoryPath, '.git')) | ||||||
|       return repositoryUrl |       return repositoryUrl | ||||||
|     }), |     }), | ||||||
|  |     tryGetConfigValues: jest.fn(), | ||||||
|  |     tryGetConfigKeys: jest.fn(), | ||||||
|     tryReset: jest.fn(async () => { |     tryReset: jest.fn(async () => { | ||||||
|       return true |       return true | ||||||
|     }), |     }), | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ fi | |||||||
|  |  | ||||||
| echo "Testing persisted credential" | echo "Testing persisted credential" | ||||||
| pushd ./submodules-recursive/submodule-level-1/submodule-level-2 | pushd ./submodules-recursive/submodule-level-1/submodule-level-2 | ||||||
| git config --local --name-only --get-regexp http.+extraheader && git fetch | git config --local --includes --name-only --get-regexp http.+extraheader && git fetch | ||||||
| if [ "$?" != "0" ]; then | if [ "$?" != "0" ]; then | ||||||
|     echo "Failed to validate persisted credential" |     echo "Failed to validate persisted credential" | ||||||
|     popd |     popd | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ fi | |||||||
|  |  | ||||||
| echo "Testing persisted credential" | echo "Testing persisted credential" | ||||||
| pushd ./submodules-true/submodule-level-1 | pushd ./submodules-true/submodule-level-1 | ||||||
| git config --local --name-only --get-regexp http.+extraheader && git fetch | git config --local --includes --name-only --get-regexp http.+extraheader && git fetch | ||||||
| if [ "$?" != "0" ]; then | if [ "$?" != "0" ]; then | ||||||
|     echo "Failed to validate persisted credential" |     echo "Failed to validate persisted credential" | ||||||
|     popd |     popd | ||||||
|   | |||||||
| @@ -104,6 +104,6 @@ outputs: | |||||||
|   commit: |   commit: | ||||||
|     description: 'The commit SHA that was checked out' |     description: 'The commit SHA that was checked out' | ||||||
| runs: | runs: | ||||||
|   using: node20 |   using: node24 | ||||||
|   main: dist/index.js |   main: dist/index.js | ||||||
|   post: dist/index.js |   post: dist/index.js | ||||||
|   | |||||||
							
								
								
									
										436
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										436
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @@ -162,6 +162,7 @@ class GitAuthHelper { | |||||||
|         this.sshKeyPath = ''; |         this.sshKeyPath = ''; | ||||||
|         this.sshKnownHostsPath = ''; |         this.sshKnownHostsPath = ''; | ||||||
|         this.temporaryHomePath = ''; |         this.temporaryHomePath = ''; | ||||||
|  |         this.credentialsConfigPath = ''; // Path to separate credentials config file in RUNNER_TEMP
 | ||||||
|         this.git = gitCommandManager; |         this.git = gitCommandManager; | ||||||
|         this.settings = gitSourceSettings || {}; |         this.settings = gitSourceSettings || {}; | ||||||
|         // Token auth header
 |         // Token auth header
 | ||||||
| @@ -229,15 +230,17 @@ class GitAuthHelper { | |||||||
|     configureGlobalAuth() { |     configureGlobalAuth() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             // 'configureTempGlobalConfig' noops if already set, just returns the path
 |             // 'configureTempGlobalConfig' noops if already set, just returns the path
 | ||||||
|             const newGitConfigPath = yield this.configureTempGlobalConfig(); |             yield this.configureTempGlobalConfig(); | ||||||
|             try { |             try { | ||||||
|                 // Configure the token
 |                 // Configure the token
 | ||||||
|                 yield this.configureToken(newGitConfigPath, true); |                 yield this.configureToken(true); | ||||||
|                 // Configure HTTPS instead of SSH
 |                 // Configure HTTPS instead of SSH
 | ||||||
|                 yield this.git.tryConfigUnset(this.insteadOfKey, true); |                 yield this.git.tryConfigUnset(this.insteadOfKey, true); | ||||||
|                 if (!this.settings.sshKey) { |                 if (!this.settings.sshKey) { | ||||||
|                     for (const insteadOfValue of this.insteadOfValues) { |                     for (const insteadOfValue of this.insteadOfValues) { | ||||||
|                         yield this.git.config(this.insteadOfKey, insteadOfValue, true, true); |                         yield this.git.config(this.insteadOfKey, insteadOfValue, true, // globalConfig?
 | ||||||
|  |                         true // add?
 | ||||||
|  |                         ); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -252,19 +255,34 @@ class GitAuthHelper { | |||||||
|     configureSubmoduleAuth() { |     configureSubmoduleAuth() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             // Remove possible previous HTTPS instead of SSH
 |             // Remove possible previous HTTPS instead of SSH
 | ||||||
|             yield this.removeGitConfig(this.insteadOfKey, true); |             yield this.removeSubmoduleGitConfig(this.insteadOfKey); | ||||||
|             if (this.settings.persistCredentials) { |             if (this.settings.persistCredentials) { | ||||||
|                 // Configure a placeholder value. This approach avoids the credential being captured
 |                 // Get the credentials config file path in RUNNER_TEMP
 | ||||||
|                 // by process creation audit events, which are commonly logged. For more information,
 |                 const credentialsConfigPath = this.getCredentialsConfigPath(); | ||||||
|                 // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 |                 // Container credentials config path
 | ||||||
|                 const output = yield this.git.submoduleForeach( |                 const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath)); | ||||||
|                 // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
 |                 // Get submodule config file paths.
 | ||||||
|                 `sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`, this.settings.nestedSubmodules); |                 const configPaths = yield this.git.getSubmoduleConfigPaths(this.settings.nestedSubmodules); | ||||||
|                 // Replace the placeholder
 |                 // For each submodule, configure includeIf entries pointing to the shared credentials file.
 | ||||||
|                 const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []; |                 // Configure both host and container paths to support Docker container actions.
 | ||||||
|                 for (const configPath of configPaths) { |                 for (const configPath of configPaths) { | ||||||
|                     core.debug(`Replacing token placeholder in '${configPath}'`); |                     // Submodule Git directory
 | ||||||
|                     yield this.replaceTokenPlaceholder(configPath); |                     let submoduleGitDir = path.dirname(configPath); // The config file is at .git/modules/submodule-name/config
 | ||||||
|  |                     submoduleGitDir = submoduleGitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||||
|  |                     // Configure host includeIf
 | ||||||
|  |                     yield this.git.config(`includeIf.gitdir:${submoduleGitDir}.path`, credentialsConfigPath, false, // globalConfig?
 | ||||||
|  |                     false, // add?
 | ||||||
|  |                     configPath); | ||||||
|  |                     // Container submodule git directory
 | ||||||
|  |                     const githubWorkspace = process.env['GITHUB_WORKSPACE']; | ||||||
|  |                     assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); | ||||||
|  |                     let relativeSubmoduleGitDir = path.relative(githubWorkspace, submoduleGitDir); | ||||||
|  |                     relativeSubmoduleGitDir = relativeSubmoduleGitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||||
|  |                     const containerSubmoduleGitDir = path.posix.join('/github/workspace', relativeSubmoduleGitDir); | ||||||
|  |                     // Configure container includeIf
 | ||||||
|  |                     yield this.git.config(`includeIf.gitdir:${containerSubmoduleGitDir}.path`, containerCredentialsPath, false, // globalConfig?
 | ||||||
|  |                     false, // add?
 | ||||||
|  |                     configPath); | ||||||
|                 } |                 } | ||||||
|                 if (this.settings.sshKey) { |                 if (this.settings.sshKey) { | ||||||
|                     // Configure core.sshCommand
 |                     // Configure core.sshCommand
 | ||||||
| @@ -295,6 +313,10 @@ class GitAuthHelper { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     /** | ||||||
|  |      * Configures SSH authentication by writing the SSH key and known hosts, | ||||||
|  |      * and setting up the GIT_SSH_COMMAND environment variable. | ||||||
|  |      */ | ||||||
|     configureSsh() { |     configureSsh() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             if (!this.settings.sshKey) { |             if (!this.settings.sshKey) { | ||||||
| @@ -351,43 +373,88 @@ class GitAuthHelper { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     configureToken(configPath, globalConfig) { |     /** | ||||||
|  |      * Configures token-based authentication by creating a credentials config file | ||||||
|  |      * and setting up includeIf entries to reference it. | ||||||
|  |      * @param globalConfig Whether to configure global config instead of local | ||||||
|  |      */ | ||||||
|  |     configureToken(globalConfig) { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             // Validate args
 |             // Get the credentials config file path in RUNNER_TEMP
 | ||||||
|             assert.ok((configPath && globalConfig) || (!configPath && !globalConfig), 'Unexpected configureToken parameter combinations'); |             const credentialsConfigPath = this.getCredentialsConfigPath(); | ||||||
|             // Default config path
 |             // Write placeholder to the separate credentials config file using git config.
 | ||||||
|             if (!configPath && !globalConfig) { |             // This approach avoids the credential being captured by process creation audit events,
 | ||||||
|                 configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config'); |             // which are commonly logged. For more information, refer to
 | ||||||
|             } |             // https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | ||||||
|             // Configure a placeholder value. This approach avoids the credential being captured
 |             yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, false, // globalConfig?
 | ||||||
|             // by process creation audit events, which are commonly logged. For more information,
 |             false, // add?
 | ||||||
|             // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 |             credentialsConfigPath); | ||||||
|             yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, globalConfig); |             // Replace the placeholder in the credentials config file
 | ||||||
|             // Replace the placeholder
 |             let content = (yield fs.promises.readFile(credentialsConfigPath)).toString(); | ||||||
|             yield this.replaceTokenPlaceholder(configPath || ''); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     replaceTokenPlaceholder(configPath) { |  | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |  | ||||||
|             assert.ok(configPath, 'configPath is not defined'); |  | ||||||
|             let content = (yield fs.promises.readFile(configPath)).toString(); |  | ||||||
|             const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue); |             const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue); | ||||||
|             if (placeholderIndex < 0 || |             if (placeholderIndex < 0 || | ||||||
|                 placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue)) { |                 placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue)) { | ||||||
|                 throw new Error(`Unable to replace auth placeholder in ${configPath}`); |                 throw new Error(`Unable to replace auth placeholder in ${credentialsConfigPath}`); | ||||||
|             } |             } | ||||||
|             assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined'); |             assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined'); | ||||||
|             content = content.replace(this.tokenPlaceholderConfigValue, this.tokenConfigValue); |             content = content.replace(this.tokenPlaceholderConfigValue, this.tokenConfigValue); | ||||||
|             yield fs.promises.writeFile(configPath, content); |             yield fs.promises.writeFile(credentialsConfigPath, content); | ||||||
|  |             // Add include or includeIf to reference the credentials config
 | ||||||
|  |             if (globalConfig) { | ||||||
|  |                 // Global config file is temporary
 | ||||||
|  |                 yield this.git.config('include.path', credentialsConfigPath, true // globalConfig?
 | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 // Host git directory
 | ||||||
|  |                 let gitDir = path.join(this.git.getWorkingDirectory(), '.git'); | ||||||
|  |                 gitDir = gitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||||
|  |                 // Configure host includeIf
 | ||||||
|  |                 const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`; | ||||||
|  |                 yield this.git.config(hostIncludeKey, credentialsConfigPath); | ||||||
|  |                 // Container git directory
 | ||||||
|  |                 const workingDirectory = this.git.getWorkingDirectory(); | ||||||
|  |                 const githubWorkspace = process.env['GITHUB_WORKSPACE']; | ||||||
|  |                 assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); | ||||||
|  |                 let relativePath = path.relative(githubWorkspace, workingDirectory); | ||||||
|  |                 relativePath = relativePath.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||||
|  |                 const containerGitDir = path.posix.join('/github/workspace', relativePath, '.git'); | ||||||
|  |                 // Container credentials config path
 | ||||||
|  |                 const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath)); | ||||||
|  |                 // Configure container includeIf
 | ||||||
|  |                 const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`; | ||||||
|  |                 yield this.git.config(containerIncludeKey, containerCredentialsPath); | ||||||
|  |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     /** | ||||||
|  |      * Gets or creates the path to the credentials config file in RUNNER_TEMP. | ||||||
|  |      * @returns The absolute path to the credentials config file | ||||||
|  |      */ | ||||||
|  |     getCredentialsConfigPath() { | ||||||
|  |         if (this.credentialsConfigPath) { | ||||||
|  |             return this.credentialsConfigPath; | ||||||
|  |         } | ||||||
|  |         const runnerTemp = process.env['RUNNER_TEMP'] || ''; | ||||||
|  |         assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); | ||||||
|  |         // Create a unique filename for this checkout instance
 | ||||||
|  |         const configFileName = `git-credentials-${(0, uuid_1.v4)()}.config`; | ||||||
|  |         this.credentialsConfigPath = path.join(runnerTemp, configFileName); | ||||||
|  |         core.debug(`Credentials config path: ${this.credentialsConfigPath}`); | ||||||
|  |         return this.credentialsConfigPath; | ||||||
|  |     } | ||||||
|  |     /** | ||||||
|  |      * Removes SSH authentication configuration by cleaning up SSH keys, | ||||||
|  |      * known hosts files, and SSH command configurations. | ||||||
|  |      */ | ||||||
|     removeSsh() { |     removeSsh() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             var _a; |             var _a, _b; | ||||||
|             // SSH key
 |             // SSH key
 | ||||||
|             const keyPath = this.sshKeyPath || stateHelper.SshKeyPath; |             const keyPath = this.sshKeyPath || stateHelper.SshKeyPath; | ||||||
|             if (keyPath) { |             if (keyPath) { | ||||||
|                 try { |                 try { | ||||||
|  |                     core.info(`Removing SSH key '${keyPath}'`); | ||||||
|                     yield io.rmRF(keyPath); |                     yield io.rmRF(keyPath); | ||||||
|                 } |                 } | ||||||
|                 catch (err) { |                 catch (err) { | ||||||
| @@ -399,37 +466,136 @@ class GitAuthHelper { | |||||||
|             const knownHostsPath = this.sshKnownHostsPath || stateHelper.SshKnownHostsPath; |             const knownHostsPath = this.sshKnownHostsPath || stateHelper.SshKnownHostsPath; | ||||||
|             if (knownHostsPath) { |             if (knownHostsPath) { | ||||||
|                 try { |                 try { | ||||||
|  |                     core.info(`Removing SSH known hosts '${knownHostsPath}'`); | ||||||
|                     yield io.rmRF(knownHostsPath); |                     yield io.rmRF(knownHostsPath); | ||||||
|                 } |                 } | ||||||
|                 catch (_b) { |                 catch (err) { | ||||||
|                     // Intentionally empty
 |                     core.debug(`${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`); | ||||||
|  |                     core.warning(`Failed to remove SSH known hosts '${knownHostsPath}'`); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             // SSH command
 |             // SSH command
 | ||||||
|  |             core.info('Removing SSH command configuration'); | ||||||
|             yield this.removeGitConfig(SSH_COMMAND_KEY); |             yield this.removeGitConfig(SSH_COMMAND_KEY); | ||||||
|  |             yield this.removeSubmoduleGitConfig(SSH_COMMAND_KEY); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     /** | ||||||
|  |      * Removes token-based authentication by cleaning up HTTP headers, | ||||||
|  |      * includeIf entries, and credentials config files. | ||||||
|  |      */ | ||||||
|     removeToken() { |     removeToken() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             // HTTP extra header
 |             var _a; | ||||||
|  |             // Remove HTTP extra header
 | ||||||
|  |             core.info('Removing HTTP extra header'); | ||||||
|             yield this.removeGitConfig(this.tokenConfigKey); |             yield this.removeGitConfig(this.tokenConfigKey); | ||||||
|         }); |             yield this.removeSubmoduleGitConfig(this.tokenConfigKey); | ||||||
|     } |             // Collect credentials config paths that need to be removed
 | ||||||
|     removeGitConfig(configKey_1) { |             const credentialsPaths = new Set(); | ||||||
|         return __awaiter(this, arguments, void 0, function* (configKey, submoduleOnly = false) { |             // Remove includeIf entries that point to git-credentials-*.config files
 | ||||||
|             if (!submoduleOnly) { |             core.info('Removing includeIf entries pointing to credentials config files'); | ||||||
|                 if ((yield this.git.configExists(configKey)) && |             const mainCredentialsPaths = yield this.removeIncludeIfCredentials(); | ||||||
|                     !(yield this.git.tryConfigUnset(configKey))) { |             mainCredentialsPaths.forEach(path => credentialsPaths.add(path)); | ||||||
|                     // Load the config contents
 |             // Remove submodule includeIf entries that point to git-credentials-*.config files
 | ||||||
|                     core.warning(`Failed to remove '${configKey}' from the git config`); |             const submoduleConfigPaths = yield this.git.getSubmoduleConfigPaths(true); | ||||||
|  |             for (const configPath of submoduleConfigPaths) { | ||||||
|  |                 const submoduleCredentialsPaths = yield this.removeIncludeIfCredentials(configPath); | ||||||
|  |                 submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path)); | ||||||
|  |             } | ||||||
|  |             // Remove credentials config files
 | ||||||
|  |             for (const credentialsPath of credentialsPaths) { | ||||||
|  |                 // Only remove credentials config files if they are under RUNNER_TEMP
 | ||||||
|  |                 const runnerTemp = process.env['RUNNER_TEMP']; | ||||||
|  |                 assert.ok(runnerTemp, 'RUNNER_TEMP is not defined'); | ||||||
|  |                 if (credentialsPath.startsWith(runnerTemp)) { | ||||||
|  |                     try { | ||||||
|  |                         core.info(`Removing credentials config '${credentialsPath}'`); | ||||||
|  |                         yield io.rmRF(credentialsPath); | ||||||
|  |                     } | ||||||
|  |                     catch (err) { | ||||||
|  |                         core.debug(`${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`); | ||||||
|  |                         core.warning(`Failed to remove credentials config '${credentialsPath}'`); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     core.debug(`Skipping removal of credentials config '${credentialsPath}' - not under RUNNER_TEMP`); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     /** | ||||||
|  |      * Removes a git config key from the local repository config. | ||||||
|  |      * @param configKey The git config key to remove | ||||||
|  |      */ | ||||||
|  |     removeGitConfig(configKey) { | ||||||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |             if ((yield this.git.configExists(configKey)) && | ||||||
|  |                 !(yield this.git.tryConfigUnset(configKey))) { | ||||||
|  |                 // Load the config contents
 | ||||||
|  |                 core.warning(`Failed to remove '${configKey}' from the git config`); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     /** | ||||||
|  |      * Removes a git config key from all submodule configs. | ||||||
|  |      * @param configKey The git config key to remove | ||||||
|  |      */ | ||||||
|  |     removeSubmoduleGitConfig(configKey) { | ||||||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             const pattern = regexpHelper.escape(configKey); |             const pattern = regexpHelper.escape(configKey); | ||||||
|             yield this.git.submoduleForeach( |             yield this.git.submoduleForeach( | ||||||
|             // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
 |             // Wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline.
 | ||||||
|             `sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, true); |             `sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, true); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     /** | ||||||
|  |      * Removes includeIf entries that point to git-credentials-*.config files. | ||||||
|  |      * @param configPath Optional path to a specific git config file to operate on | ||||||
|  |      * @returns Array of unique credentials config file paths that were found and removed | ||||||
|  |      */ | ||||||
|  |     removeIncludeIfCredentials(configPath) { | ||||||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |             const credentialsPaths = new Set(); | ||||||
|  |             try { | ||||||
|  |                 // Get all includeIf.gitdir keys
 | ||||||
|  |                 const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, // globalConfig?
 | ||||||
|  |                 configPath); | ||||||
|  |                 for (const key of keys) { | ||||||
|  |                     // Get all values for this key
 | ||||||
|  |                     const values = yield this.git.tryGetConfigValues(key, false, // globalConfig?
 | ||||||
|  |                     configPath); | ||||||
|  |                     if (values.length > 0) { | ||||||
|  |                         // Remove only values that match git-credentials-<uuid>.config pattern
 | ||||||
|  |                         for (const value of values) { | ||||||
|  |                             if (this.testCredentialsConfigPath(value)) { | ||||||
|  |                                 credentialsPaths.add(value); | ||||||
|  |                                 yield this.git.tryConfigUnsetValue(key, value, false, configPath); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             catch (err) { | ||||||
|  |                 // Ignore errors - this is cleanup code
 | ||||||
|  |                 if (configPath) { | ||||||
|  |                     core.debug(`Error during includeIf cleanup for ${configPath}: ${err}`); | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     core.debug(`Error during includeIf cleanup: ${err}`); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return Array.from(credentialsPaths); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     /** | ||||||
|  |      * Tests if a path matches the git-credentials-*.config pattern. | ||||||
|  |      * @param path The path to test | ||||||
|  |      * @returns True if the path matches the credentials config pattern | ||||||
|  |      */ | ||||||
|  |     testCredentialsConfigPath(path) { | ||||||
|  |         return /git-credentials-[0-9a-f-]+\.config$/i.test(path); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -627,9 +793,15 @@ class GitCommandManager { | |||||||
|             yield this.execGit(args); |             yield this.execGit(args); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|     config(configKey, configValue, globalConfig, add) { |     config(configKey, configValue, globalConfig, add, configFile) { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             const args = ['config', globalConfig ? '--global' : '--local']; |             const args = ['config']; | ||||||
|  |             if (configFile) { | ||||||
|  |                 args.push('--file', configFile); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 args.push(globalConfig ? '--global' : '--local'); | ||||||
|  |             } | ||||||
|             if (add) { |             if (add) { | ||||||
|                 args.push('--add'); |                 args.push('--add'); | ||||||
|             } |             } | ||||||
| @@ -706,6 +878,16 @@ class GitCommandManager { | |||||||
|             throw new Error('Unexpected output when retrieving default branch'); |             throw new Error('Unexpected output when retrieving default branch'); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     getSubmoduleConfigPaths(recursive) { | ||||||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |             // Get submodule config file paths.
 | ||||||
|  |             // Use `--show-origin` to get the config file path for each submodule.
 | ||||||
|  |             const output = yield this.submoduleForeach(`git config --local --show-origin --name-only --get-regexp remote.origin.url`, recursive); | ||||||
|  |             // Extract config file paths from the output (lines starting with "file:").
 | ||||||
|  |             const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []; | ||||||
|  |             return configPaths; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|     getWorkingDirectory() { |     getWorkingDirectory() { | ||||||
|         return this.workingDirectory; |         return this.workingDirectory; | ||||||
|     } |     } | ||||||
| @@ -836,6 +1018,20 @@ class GitCommandManager { | |||||||
|             return output.exitCode === 0; |             return output.exitCode === 0; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     tryConfigUnsetValue(configKey, configValue, globalConfig, configFile) { | ||||||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |             const args = ['config']; | ||||||
|  |             if (configFile) { | ||||||
|  |                 args.push('--file', configFile); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 args.push(globalConfig ? '--global' : '--local'); | ||||||
|  |             } | ||||||
|  |             args.push('--unset', configKey, configValue); | ||||||
|  |             const output = yield this.execGit(args, true); | ||||||
|  |             return output.exitCode === 0; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|     tryDisableAutomaticGarbageCollection() { |     tryDisableAutomaticGarbageCollection() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             const output = yield this.execGit(['config', '--local', 'gc.auto', '0'], true); |             const output = yield this.execGit(['config', '--local', 'gc.auto', '0'], true); | ||||||
| @@ -855,6 +1051,46 @@ class GitCommandManager { | |||||||
|             return stdout; |             return stdout; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |     tryGetConfigValues(configKey, globalConfig, configFile) { | ||||||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |             const args = ['config']; | ||||||
|  |             if (configFile) { | ||||||
|  |                 args.push('--file', configFile); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 args.push(globalConfig ? '--global' : '--local'); | ||||||
|  |             } | ||||||
|  |             args.push('--get-all', configKey); | ||||||
|  |             const output = yield this.execGit(args, true); | ||||||
|  |             if (output.exitCode !== 0) { | ||||||
|  |                 return []; | ||||||
|  |             } | ||||||
|  |             return output.stdout | ||||||
|  |                 .trim() | ||||||
|  |                 .split('\n') | ||||||
|  |                 .filter(value => value.trim()); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     tryGetConfigKeys(pattern, globalConfig, configFile) { | ||||||
|  |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|  |             const args = ['config']; | ||||||
|  |             if (configFile) { | ||||||
|  |                 args.push('--file', configFile); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 args.push(globalConfig ? '--global' : '--local'); | ||||||
|  |             } | ||||||
|  |             args.push('--name-only', '--get-regexp', pattern); | ||||||
|  |             const output = yield this.execGit(args, true); | ||||||
|  |             if (output.exitCode !== 0) { | ||||||
|  |                 return []; | ||||||
|  |             } | ||||||
|  |             return output.stdout | ||||||
|  |                 .trim() | ||||||
|  |                 .split('\n') | ||||||
|  |                 .filter(key => key.trim()); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|     tryReset() { |     tryReset() { | ||||||
|         return __awaiter(this, void 0, void 0, function* () { |         return __awaiter(this, void 0, void 0, function* () { | ||||||
|             const output = yield this.execGit(['reset', '--hard', 'HEAD'], true); |             const output = yield this.execGit(['reset', '--hard', 'HEAD'], true); | ||||||
| @@ -7802,7 +8038,7 @@ module.exports = __toCommonJS(dist_src_exports); | |||||||
| var import_universal_user_agent = __nccwpck_require__(5030); | var import_universal_user_agent = __nccwpck_require__(5030); | ||||||
| 
 | 
 | ||||||
| // pkg/dist-src/version.js
 | // pkg/dist-src/version.js
 | ||||||
| var VERSION = "9.0.5"; | var VERSION = "9.0.6"; | ||||||
| 
 | 
 | ||||||
| // pkg/dist-src/defaults.js
 | // pkg/dist-src/defaults.js
 | ||||||
| var userAgent = `octokit-endpoint.js/${VERSION} ${(0, import_universal_user_agent.getUserAgent)()}`; | var userAgent = `octokit-endpoint.js/${VERSION} ${(0, import_universal_user_agent.getUserAgent)()}`; | ||||||
| @@ -7907,9 +8143,9 @@ function addQueryParameters(url, parameters) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // pkg/dist-src/util/extract-url-variable-names.js
 | // pkg/dist-src/util/extract-url-variable-names.js
 | ||||||
| var urlVariableRegex = /\{[^}]+\}/g; | var urlVariableRegex = /\{[^{}}]+\}/g; | ||||||
| function removeNonChars(variableName) { | function removeNonChars(variableName) { | ||||||
|   return variableName.replace(/^\W+|\W+$/g, "").split(/,/); |   return variableName.replace(/(?:^\W+)|(?:(?<!\W)\W+$)/g, "").split(/,/); | ||||||
| } | } | ||||||
| function extractUrlVariableNames(url) { | function extractUrlVariableNames(url) { | ||||||
|   const matches = url.match(urlVariableRegex); |   const matches = url.match(urlVariableRegex); | ||||||
| @@ -8095,7 +8331,7 @@ function parse(options) { | |||||||
|     } |     } | ||||||
|     if (url.endsWith("/graphql")) { |     if (url.endsWith("/graphql")) { | ||||||
|       if (options.mediaType.previews?.length) { |       if (options.mediaType.previews?.length) { | ||||||
|         const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || []; |         const previewsFromAcceptHeader = headers.accept.match(/(?<![\w-])[\w-]+(?=-preview)/g) || []; | ||||||
|         headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map((preview) => { |         headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map((preview) => { | ||||||
|           const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json"; |           const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json"; | ||||||
|           return `application/vnd.github.${preview}-preview${format}`; |           return `application/vnd.github.${preview}-preview${format}`; | ||||||
| @@ -8344,7 +8580,7 @@ __export(dist_src_exports, { | |||||||
| module.exports = __toCommonJS(dist_src_exports); | module.exports = __toCommonJS(dist_src_exports); | ||||||
| 
 | 
 | ||||||
| // pkg/dist-src/version.js
 | // pkg/dist-src/version.js
 | ||||||
| var VERSION = "9.2.1"; | var VERSION = "9.2.2"; | ||||||
| 
 | 
 | ||||||
| // pkg/dist-src/normalize-paginated-list-response.js
 | // pkg/dist-src/normalize-paginated-list-response.js
 | ||||||
| function normalizePaginatedListResponse(response) { | function normalizePaginatedListResponse(response) { | ||||||
| @@ -8392,7 +8628,7 @@ function iterator(octokit, route, parameters) { | |||||||
|           const response = await requestMethod({ method, url, headers }); |           const response = await requestMethod({ method, url, headers }); | ||||||
|           const normalizedResponse = normalizePaginatedListResponse(response); |           const normalizedResponse = normalizePaginatedListResponse(response); | ||||||
|           url = ((normalizedResponse.headers.link || "").match( |           url = ((normalizedResponse.headers.link || "").match( | ||||||
|             /<([^>]+)>;\s*rel="next"/ |             /<([^<>]+)>;\s*rel="next"/ | ||||||
|           ) || [])[1]; |           ) || [])[1]; | ||||||
|           return { value: normalizedResponse }; |           return { value: normalizedResponse }; | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
| @@ -10944,7 +11180,7 @@ var RequestError = class extends Error { | |||||||
|     if (options.request.headers.authorization) { |     if (options.request.headers.authorization) { | ||||||
|       requestCopy.headers = Object.assign({}, options.request.headers, { |       requestCopy.headers = Object.assign({}, options.request.headers, { | ||||||
|         authorization: options.request.headers.authorization.replace( |         authorization: options.request.headers.authorization.replace( | ||||||
|           / .*$/, |           /(?<! ) .*$/, | ||||||
|           " [REDACTED]" |           " [REDACTED]" | ||||||
|         ) |         ) | ||||||
|       }); |       }); | ||||||
| @@ -11012,7 +11248,7 @@ var import_endpoint = __nccwpck_require__(9440); | |||||||
| var import_universal_user_agent = __nccwpck_require__(5030); | var import_universal_user_agent = __nccwpck_require__(5030); | ||||||
| 
 | 
 | ||||||
| // pkg/dist-src/version.js
 | // pkg/dist-src/version.js
 | ||||||
| var VERSION = "8.4.0"; | var VERSION = "8.4.1"; | ||||||
| 
 | 
 | ||||||
| // pkg/dist-src/is-plain-object.js
 | // pkg/dist-src/is-plain-object.js
 | ||||||
| function isPlainObject(value) { | function isPlainObject(value) { | ||||||
| @@ -11071,7 +11307,7 @@ function fetchWrapper(requestOptions) { | |||||||
|       headers[keyAndValue[0]] = keyAndValue[1]; |       headers[keyAndValue[0]] = keyAndValue[1]; | ||||||
|     } |     } | ||||||
|     if ("deprecation" in headers) { |     if ("deprecation" in headers) { | ||||||
|       const matches = headers.link && headers.link.match(/<([^>]+)>; rel="deprecation"/); |       const matches = headers.link && headers.link.match(/<([^<>]+)>; rel="deprecation"/); | ||||||
|       const deprecationLink = matches && matches.pop(); |       const deprecationLink = matches && matches.pop(); | ||||||
|       log.warn( |       log.warn( | ||||||
|         `[@octokit/request] "${requestOptions.method} ${requestOptions.url}" is deprecated. It is scheduled to be removed on ${headers.sunset}${deprecationLink ? `. See ${deprecationLink}` : ""}` |         `[@octokit/request] "${requestOptions.method} ${requestOptions.url}" is deprecated. It is scheduled to be removed on ${headers.sunset}${deprecationLink ? `. See ${deprecationLink}` : ""}` | ||||||
| @@ -18725,7 +18961,7 @@ module.exports = { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const { parseSetCookie } = __nccwpck_require__(4408) | const { parseSetCookie } = __nccwpck_require__(4408) | ||||||
| const { stringify, getHeadersList } = __nccwpck_require__(3121) | const { stringify } = __nccwpck_require__(3121) | ||||||
| const { webidl } = __nccwpck_require__(1744) | const { webidl } = __nccwpck_require__(1744) | ||||||
| const { Headers } = __nccwpck_require__(554) | const { Headers } = __nccwpck_require__(554) | ||||||
| 
 | 
 | ||||||
| @@ -18801,14 +19037,13 @@ function getSetCookies (headers) { | |||||||
| 
 | 
 | ||||||
|   webidl.brandCheck(headers, Headers, { strict: false }) |   webidl.brandCheck(headers, Headers, { strict: false }) | ||||||
| 
 | 
 | ||||||
|   const cookies = getHeadersList(headers).cookies |   const cookies = headers.getSetCookie() | ||||||
| 
 | 
 | ||||||
|   if (!cookies) { |   if (!cookies) { | ||||||
|     return [] |     return [] | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // In older versions of undici, cookies is a list of name:value.
 |   return cookies.map((pair) => parseSetCookie(pair)) | ||||||
|   return cookies.map((pair) => parseSetCookie(Array.isArray(pair) ? pair[1] : pair)) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @@ -19236,14 +19471,15 @@ module.exports = { | |||||||
| /***/ }), | /***/ }), | ||||||
| 
 | 
 | ||||||
| /***/ 3121: | /***/ 3121: | ||||||
| /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { | /***/ ((module) => { | ||||||
| 
 | 
 | ||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const assert = __nccwpck_require__(9491) | /** | ||||||
| const { kHeadersList } = __nccwpck_require__(2785) |  * @param {string} value | ||||||
| 
 |  * @returns {boolean} | ||||||
|  |  */ | ||||||
| function isCTLExcludingHtab (value) { | function isCTLExcludingHtab (value) { | ||||||
|   if (value.length === 0) { |   if (value.length === 0) { | ||||||
|     return false |     return false | ||||||
| @@ -19504,31 +19740,13 @@ function stringify (cookie) { | |||||||
|   return out.join('; ') |   return out.join('; ') | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| let kHeadersListNode |  | ||||||
| 
 |  | ||||||
| function getHeadersList (headers) { |  | ||||||
|   if (headers[kHeadersList]) { |  | ||||||
|     return headers[kHeadersList] |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (!kHeadersListNode) { |  | ||||||
|     kHeadersListNode = Object.getOwnPropertySymbols(headers).find( |  | ||||||
|       (symbol) => symbol.description === 'headers list' |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     assert(kHeadersListNode, 'Headers cannot be parsed') |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   const headersList = headers[kHeadersListNode] |  | ||||||
|   assert(headersList) |  | ||||||
| 
 |  | ||||||
|   return headersList |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   isCTLExcludingHtab, |   isCTLExcludingHtab, | ||||||
|   stringify, |   validateCookieName, | ||||||
|   getHeadersList |   validateCookiePath, | ||||||
|  |   validateCookieValue, | ||||||
|  |   toIMFDate, | ||||||
|  |   stringify | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -21457,6 +21675,14 @@ const { isUint8Array, isArrayBuffer } = __nccwpck_require__(9830) | |||||||
| const { File: UndiciFile } = __nccwpck_require__(8511) | const { File: UndiciFile } = __nccwpck_require__(8511) | ||||||
| const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(685) | const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(685) | ||||||
| 
 | 
 | ||||||
|  | let random | ||||||
|  | try { | ||||||
|  |   const crypto = __nccwpck_require__(6005) | ||||||
|  |   random = (max) => crypto.randomInt(0, max) | ||||||
|  | } catch { | ||||||
|  |   random = (max) => Math.floor(Math.random(max)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| let ReadableStream = globalThis.ReadableStream | let ReadableStream = globalThis.ReadableStream | ||||||
| 
 | 
 | ||||||
| /** @type {globalThis['File']} */ | /** @type {globalThis['File']} */ | ||||||
| @@ -21542,7 +21768,7 @@ function extractBody (object, keepalive = false) { | |||||||
|     // Set source to a copy of the bytes held by object.
 |     // Set source to a copy of the bytes held by object.
 | ||||||
|     source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength)) |     source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength)) | ||||||
|   } else if (util.isFormDataLike(object)) { |   } else if (util.isFormDataLike(object)) { | ||||||
|     const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}` |     const boundary = `----formdata-undici-0${`${random(1e11)}`.padStart(11, '0')}` | ||||||
|     const prefix = `--${boundary}\r\nContent-Disposition: form-data` |     const prefix = `--${boundary}\r\nContent-Disposition: form-data` | ||||||
| 
 | 
 | ||||||
|     /*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */ |     /*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */ | ||||||
| @@ -23524,6 +23750,7 @@ const { | |||||||
|   isValidHeaderName, |   isValidHeaderName, | ||||||
|   isValidHeaderValue |   isValidHeaderValue | ||||||
| } = __nccwpck_require__(2538) | } = __nccwpck_require__(2538) | ||||||
|  | const util = __nccwpck_require__(3837) | ||||||
| const { webidl } = __nccwpck_require__(1744) | const { webidl } = __nccwpck_require__(1744) | ||||||
| const assert = __nccwpck_require__(9491) | const assert = __nccwpck_require__(9491) | ||||||
| 
 | 
 | ||||||
| @@ -24077,6 +24304,9 @@ Object.defineProperties(Headers.prototype, { | |||||||
|   [Symbol.toStringTag]: { |   [Symbol.toStringTag]: { | ||||||
|     value: 'Headers', |     value: 'Headers', | ||||||
|     configurable: true |     configurable: true | ||||||
|  |   }, | ||||||
|  |   [util.inspect.custom]: { | ||||||
|  |     enumerable: false | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| @@ -33253,6 +33483,20 @@ class Pool extends PoolBase { | |||||||
|       ? { ...options.interceptors } |       ? { ...options.interceptors } | ||||||
|       : undefined |       : undefined | ||||||
|     this[kFactory] = factory |     this[kFactory] = factory | ||||||
|  | 
 | ||||||
|  |     this.on('connectionError', (origin, targets, error) => { | ||||||
|  |       // If a connection error occurs, we remove the client from the pool,
 | ||||||
|  |       // and emit a connectionError event. They will not be re-used.
 | ||||||
|  |       // Fixes https://github.com/nodejs/undici/issues/3895
 | ||||||
|  |       for (const target of targets) { | ||||||
|  |         // Do not use kRemoveClient here, as it will close the client,
 | ||||||
|  |         // but the client cannot be closed in this state.
 | ||||||
|  |         const idx = this[kClients].indexOf(target) | ||||||
|  |         if (idx !== -1) { | ||||||
|  |           this[kClients].splice(idx, 1) | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   [kGetDispatcher] () { |   [kGetDispatcher] () { | ||||||
| @@ -36408,6 +36652,14 @@ module.exports = require("net"); | |||||||
| 
 | 
 | ||||||
| /***/ }), | /***/ }), | ||||||
| 
 | 
 | ||||||
|  | /***/ 6005: | ||||||
|  | /***/ ((module) => { | ||||||
|  | 
 | ||||||
|  | "use strict"; | ||||||
|  | module.exports = require("node:crypto"); | ||||||
|  | 
 | ||||||
|  | /***/ }), | ||||||
|  | 
 | ||||||
| /***/ 5673: | /***/ 5673: | ||||||
| /***/ ((module) => { | /***/ ((module) => { | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										334
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										334
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,12 +1,12 @@ | |||||||
| { | { | ||||||
|   "name": "checkout", |   "name": "checkout", | ||||||
|   "version": "4.2.2", |   "version": "5.0.0", | ||||||
|   "lockfileVersion": 3, |   "lockfileVersion": 3, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "": { |     "": { | ||||||
|       "name": "checkout", |       "name": "checkout", | ||||||
|       "version": "4.2.2", |       "version": "5.0.0", | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@actions/core": "^1.10.1", |         "@actions/core": "^1.10.1", | ||||||
| @@ -18,7 +18,7 @@ | |||||||
|       }, |       }, | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
|         "@types/jest": "^29.5.12", |         "@types/jest": "^29.5.12", | ||||||
|         "@types/node": "^20.12.12", |         "@types/node": "^24.1.0", | ||||||
|         "@types/uuid": "^9.0.8", |         "@types/uuid": "^9.0.8", | ||||||
|         "@typescript-eslint/eslint-plugin": "^7.9.0", |         "@typescript-eslint/eslint-plugin": "^7.9.0", | ||||||
|         "@typescript-eslint/parser": "^7.9.0", |         "@typescript-eslint/parser": "^7.9.0", | ||||||
| @@ -129,13 +129,15 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/code-frame": { |     "node_modules/@babel/code-frame": { | ||||||
|       "version": "7.24.2", |       "version": "7.27.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", |       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", | ||||||
|       "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", |       "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@babel/highlight": "^7.24.2", |         "@babel/helper-validator-identifier": "^7.27.1", | ||||||
|         "picocolors": "^1.0.0" |         "js-tokens": "^4.0.0", | ||||||
|  |         "picocolors": "^1.1.1" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.9.0" |         "node": ">=6.9.0" | ||||||
| @@ -310,19 +312,21 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/helper-string-parser": { |     "node_modules/@babel/helper-string-parser": { | ||||||
|       "version": "7.24.1", |       "version": "7.27.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", |       "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", | ||||||
|       "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", |       "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.9.0" |         "node": ">=6.9.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/helper-validator-identifier": { |     "node_modules/@babel/helper-validator-identifier": { | ||||||
|       "version": "7.22.20", |       "version": "7.27.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", |       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", | ||||||
|       "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", |       "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.9.0" |         "node": ">=6.9.0" | ||||||
|       } |       } | ||||||
| @@ -337,110 +341,28 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/helpers": { |     "node_modules/@babel/helpers": { | ||||||
|       "version": "7.24.4", |       "version": "7.28.2", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", |       "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", | ||||||
|       "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", |       "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@babel/template": "^7.24.0", |         "@babel/template": "^7.27.2", | ||||||
|         "@babel/traverse": "^7.24.1", |         "@babel/types": "^7.28.2" | ||||||
|         "@babel/types": "^7.24.0" |  | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.9.0" |         "node": ">=6.9.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/highlight": { |  | ||||||
|       "version": "7.24.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", |  | ||||||
|       "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", |  | ||||||
|       "dev": true, |  | ||||||
|       "dependencies": { |  | ||||||
|         "@babel/helper-validator-identifier": "^7.22.20", |  | ||||||
|         "chalk": "^2.4.2", |  | ||||||
|         "js-tokens": "^4.0.0", |  | ||||||
|         "picocolors": "^1.0.0" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=6.9.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@babel/highlight/node_modules/ansi-styles": { |  | ||||||
|       "version": "3.2.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", |  | ||||||
|       "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", |  | ||||||
|       "dev": true, |  | ||||||
|       "dependencies": { |  | ||||||
|         "color-convert": "^1.9.0" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=4" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@babel/highlight/node_modules/chalk": { |  | ||||||
|       "version": "2.4.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", |  | ||||||
|       "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", |  | ||||||
|       "dev": true, |  | ||||||
|       "dependencies": { |  | ||||||
|         "ansi-styles": "^3.2.1", |  | ||||||
|         "escape-string-regexp": "^1.0.5", |  | ||||||
|         "supports-color": "^5.3.0" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=4" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@babel/highlight/node_modules/color-convert": { |  | ||||||
|       "version": "1.9.3", |  | ||||||
|       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", |  | ||||||
|       "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", |  | ||||||
|       "dev": true, |  | ||||||
|       "dependencies": { |  | ||||||
|         "color-name": "1.1.3" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@babel/highlight/node_modules/color-name": { |  | ||||||
|       "version": "1.1.3", |  | ||||||
|       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", |  | ||||||
|       "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", |  | ||||||
|       "dev": true |  | ||||||
|     }, |  | ||||||
|     "node_modules/@babel/highlight/node_modules/escape-string-regexp": { |  | ||||||
|       "version": "1.0.5", |  | ||||||
|       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", |  | ||||||
|       "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", |  | ||||||
|       "dev": true, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=0.8.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@babel/highlight/node_modules/has-flag": { |  | ||||||
|       "version": "3.0.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", |  | ||||||
|       "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", |  | ||||||
|       "dev": true, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=4" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@babel/highlight/node_modules/supports-color": { |  | ||||||
|       "version": "5.5.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", |  | ||||||
|       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", |  | ||||||
|       "dev": true, |  | ||||||
|       "dependencies": { |  | ||||||
|         "has-flag": "^3.0.0" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=4" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@babel/parser": { |     "node_modules/@babel/parser": { | ||||||
|       "version": "7.24.4", |       "version": "7.28.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", |       "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", | ||||||
|       "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", |       "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@babel/types": "^7.28.0" | ||||||
|  |       }, | ||||||
|       "bin": { |       "bin": { | ||||||
|         "parser": "bin/babel-parser.js" |         "parser": "bin/babel-parser.js" | ||||||
|       }, |       }, | ||||||
| @@ -626,26 +548,25 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/runtime": { |     "node_modules/@babel/runtime": { | ||||||
|       "version": "7.24.4", |       "version": "7.28.2", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", |       "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", | ||||||
|       "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", |       "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "dependencies": { |       "license": "MIT", | ||||||
|         "regenerator-runtime": "^0.14.0" |  | ||||||
|       }, |  | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.9.0" |         "node": ">=6.9.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/template": { |     "node_modules/@babel/template": { | ||||||
|       "version": "7.24.0", |       "version": "7.27.2", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", |       "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", | ||||||
|       "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", |       "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@babel/code-frame": "^7.23.5", |         "@babel/code-frame": "^7.27.1", | ||||||
|         "@babel/parser": "^7.24.0", |         "@babel/parser": "^7.27.2", | ||||||
|         "@babel/types": "^7.24.0" |         "@babel/types": "^7.27.1" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.9.0" |         "node": ">=6.9.0" | ||||||
| @@ -682,14 +603,14 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@babel/types": { |     "node_modules/@babel/types": { | ||||||
|       "version": "7.24.0", |       "version": "7.28.2", | ||||||
|       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", |       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", | ||||||
|       "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", |       "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@babel/helper-string-parser": "^7.23.4", |         "@babel/helper-string-parser": "^7.27.1", | ||||||
|         "@babel/helper-validator-identifier": "^7.22.20", |         "@babel/helper-validator-identifier": "^7.27.1" | ||||||
|         "to-fast-properties": "^2.0.0" |  | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=6.9.0" |         "node": ">=6.9.0" | ||||||
| @@ -749,10 +670,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { |     "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { | ||||||
|       "version": "1.1.11", |       "version": "1.1.12", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", | ||||||
|       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |       "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0", |         "balanced-match": "^1.0.0", | ||||||
|         "concat-map": "0.0.1" |         "concat-map": "0.0.1" | ||||||
| @@ -808,10 +730,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { |     "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { | ||||||
|       "version": "1.1.11", |       "version": "1.1.12", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", | ||||||
|       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |       "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0", |         "balanced-match": "^1.0.0", | ||||||
|         "concat-map": "0.0.1" |         "concat-map": "0.0.1" | ||||||
| @@ -1343,9 +1266,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@octokit/endpoint": { |     "node_modules/@octokit/endpoint": { | ||||||
|       "version": "9.0.5", |       "version": "9.0.6", | ||||||
|       "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz", |       "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", | ||||||
|       "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==", |       "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@octokit/types": "^13.1.0", |         "@octokit/types": "^13.1.0", | ||||||
|         "universal-user-agent": "^6.0.0" |         "universal-user-agent": "^6.0.0" | ||||||
| @@ -1373,9 +1297,10 @@ | |||||||
|       "integrity": "sha512-pGUdSP+eEPfZiQHNkZI0U01HLipxncisdJQB4G//OAmfeO8sqTQ9KRa0KF03TUPCziNsoXUrTg4B2Q1EX++T0Q==" |       "integrity": "sha512-pGUdSP+eEPfZiQHNkZI0U01HLipxncisdJQB4G//OAmfeO8sqTQ9KRa0KF03TUPCziNsoXUrTg4B2Q1EX++T0Q==" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@octokit/plugin-paginate-rest": { |     "node_modules/@octokit/plugin-paginate-rest": { | ||||||
|       "version": "9.2.1", |       "version": "9.2.2", | ||||||
|       "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz", |       "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", | ||||||
|       "integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==", |       "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@octokit/types": "^12.6.0" |         "@octokit/types": "^12.6.0" | ||||||
|       }, |       }, | ||||||
| @@ -1427,12 +1352,13 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@octokit/request": { |     "node_modules/@octokit/request": { | ||||||
|       "version": "8.4.0", |       "version": "8.4.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz", |       "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", | ||||||
|       "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==", |       "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@octokit/endpoint": "^9.0.1", |         "@octokit/endpoint": "^9.0.6", | ||||||
|         "@octokit/request-error": "^5.1.0", |         "@octokit/request-error": "^5.1.1", | ||||||
|         "@octokit/types": "^13.1.0", |         "@octokit/types": "^13.1.0", | ||||||
|         "universal-user-agent": "^6.0.0" |         "universal-user-agent": "^6.0.0" | ||||||
|       }, |       }, | ||||||
| @@ -1441,9 +1367,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@octokit/request-error": { |     "node_modules/@octokit/request-error": { | ||||||
|       "version": "5.1.0", |       "version": "5.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz", |       "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", | ||||||
|       "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==", |       "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@octokit/types": "^13.1.0", |         "@octokit/types": "^13.1.0", | ||||||
|         "deprecation": "^2.0.0", |         "deprecation": "^2.0.0", | ||||||
| @@ -1588,12 +1515,12 @@ | |||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/@types/node": { |     "node_modules/@types/node": { | ||||||
|       "version": "20.12.12", |       "version": "24.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", |       "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", | ||||||
|       "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", |       "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "undici-types": "~5.26.4" |         "undici-types": "~7.8.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@types/stack-utils": { |     "node_modules/@types/stack-utils": { | ||||||
| @@ -2238,10 +2165,11 @@ | |||||||
|       "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" |       "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" | ||||||
|     }, |     }, | ||||||
|     "node_modules/brace-expansion": { |     "node_modules/brace-expansion": { | ||||||
|       "version": "2.0.1", |       "version": "2.0.2", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", | ||||||
|       "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", |       "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0" |         "balanced-match": "^1.0.0" | ||||||
|       } |       } | ||||||
| @@ -2502,10 +2430,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/cross-spawn": { |     "node_modules/cross-spawn": { | ||||||
|       "version": "7.0.3", |       "version": "7.0.6", | ||||||
|       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", |       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", | ||||||
|       "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", |       "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "path-key": "^3.1.0", |         "path-key": "^3.1.0", | ||||||
|         "shebang-command": "^2.0.0", |         "shebang-command": "^2.0.0", | ||||||
| @@ -3175,10 +3104,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/eslint-plugin-import/node_modules/brace-expansion": { |     "node_modules/eslint-plugin-import/node_modules/brace-expansion": { | ||||||
|       "version": "1.1.11", |       "version": "1.1.12", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", | ||||||
|       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |       "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0", |         "balanced-match": "^1.0.0", | ||||||
|         "concat-map": "0.0.1" |         "concat-map": "0.0.1" | ||||||
| @@ -3273,10 +3203,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { |     "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { | ||||||
|       "version": "1.1.11", |       "version": "1.1.12", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", | ||||||
|       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |       "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0", |         "balanced-match": "^1.0.0", | ||||||
|         "concat-map": "0.0.1" |         "concat-map": "0.0.1" | ||||||
| @@ -3371,10 +3302,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/eslint/node_modules/brace-expansion": { |     "node_modules/eslint/node_modules/brace-expansion": { | ||||||
|       "version": "1.1.11", |       "version": "1.1.12", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", | ||||||
|       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |       "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0", |         "balanced-match": "^1.0.0", | ||||||
|         "concat-map": "0.0.1" |         "concat-map": "0.0.1" | ||||||
| @@ -3836,10 +3768,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/glob/node_modules/brace-expansion": { |     "node_modules/glob/node_modules/brace-expansion": { | ||||||
|       "version": "1.1.11", |       "version": "1.1.12", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", | ||||||
|       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |       "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0", |         "balanced-match": "^1.0.0", | ||||||
|         "concat-map": "0.0.1" |         "concat-map": "0.0.1" | ||||||
| @@ -4635,10 +4568,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/jake/node_modules/brace-expansion": { |     "node_modules/jake/node_modules/brace-expansion": { | ||||||
|       "version": "1.1.11", |       "version": "1.1.12", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", | ||||||
|       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |       "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0", |         "balanced-match": "^1.0.0", | ||||||
|         "concat-map": "0.0.1" |         "concat-map": "0.0.1" | ||||||
| @@ -5248,7 +5182,8 @@ | |||||||
|       "version": "4.0.0", |       "version": "4.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", |       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", | ||||||
|       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", |       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", | ||||||
|       "dev": true |       "dev": true, | ||||||
|  |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/js-yaml": { |     "node_modules/js-yaml": { | ||||||
|       "version": "4.1.0", |       "version": "4.1.0", | ||||||
| @@ -5528,12 +5463,13 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/micromatch": { |     "node_modules/micromatch": { | ||||||
|       "version": "4.0.5", |       "version": "4.0.8", | ||||||
|       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", |       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", | ||||||
|       "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", |       "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "braces": "^3.0.2", |         "braces": "^3.0.3", | ||||||
|         "picomatch": "^2.3.1" |         "picomatch": "^2.3.1" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
| @@ -5869,10 +5805,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/picocolors": { |     "node_modules/picocolors": { | ||||||
|       "version": "1.0.0", |       "version": "1.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", |       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", | ||||||
|       "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", |       "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", | ||||||
|       "dev": true |       "dev": true, | ||||||
|  |       "license": "ISC" | ||||||
|     }, |     }, | ||||||
|     "node_modules/picomatch": { |     "node_modules/picomatch": { | ||||||
|       "version": "2.3.1", |       "version": "2.3.1", | ||||||
| @@ -6115,12 +6052,6 @@ | |||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/regenerator-runtime": { |  | ||||||
|       "version": "0.14.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", |  | ||||||
|       "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", |  | ||||||
|       "dev": true |  | ||||||
|     }, |  | ||||||
|     "node_modules/regexp.prototype.flags": { |     "node_modules/regexp.prototype.flags": { | ||||||
|       "version": "1.5.2", |       "version": "1.5.2", | ||||||
|       "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", |       "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", | ||||||
| @@ -6622,10 +6553,11 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/test-exclude/node_modules/brace-expansion": { |     "node_modules/test-exclude/node_modules/brace-expansion": { | ||||||
|       "version": "1.1.11", |       "version": "1.1.12", | ||||||
|       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", |       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", | ||||||
|       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", |       "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "balanced-match": "^1.0.0", |         "balanced-match": "^1.0.0", | ||||||
|         "concat-map": "0.0.1" |         "concat-map": "0.0.1" | ||||||
| @@ -6655,15 +6587,6 @@ | |||||||
|       "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", |       "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", | ||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/to-fast-properties": { |  | ||||||
|       "version": "2.0.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", |  | ||||||
|       "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", |  | ||||||
|       "dev": true, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">=4" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/to-regex-range": { |     "node_modules/to-regex-range": { | ||||||
|       "version": "5.0.1", |       "version": "5.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", |       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", | ||||||
| @@ -6930,9 +6853,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/undici": { |     "node_modules/undici": { | ||||||
|       "version": "5.28.4", |       "version": "5.29.0", | ||||||
|       "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", |       "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", | ||||||
|       "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", |       "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@fastify/busboy": "^2.0.0" |         "@fastify/busboy": "^2.0.0" | ||||||
|       }, |       }, | ||||||
| @@ -6941,9 +6865,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/undici-types": { |     "node_modules/undici-types": { | ||||||
|       "version": "5.26.5", |       "version": "7.8.0", | ||||||
|       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", |       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", | ||||||
|       "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", |       "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", | ||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/universal-user-agent": { |     "node_modules/universal-user-agent": { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "checkout", |   "name": "checkout", | ||||||
|   "version": "4.2.2", |   "version": "5.0.0", | ||||||
|   "description": "checkout action", |   "description": "checkout action", | ||||||
|   "main": "lib/main.js", |   "main": "lib/main.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
| @@ -37,7 +37,7 @@ | |||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@types/jest": "^29.5.12", |     "@types/jest": "^29.5.12", | ||||||
|     "@types/node": "^20.12.12", |     "@types/node": "^24.1.0", | ||||||
|     "@types/uuid": "^9.0.8", |     "@types/uuid": "^9.0.8", | ||||||
|     "@typescript-eslint/eslint-plugin": "^7.9.0", |     "@typescript-eslint/eslint-plugin": "^7.9.0", | ||||||
|     "@typescript-eslint/parser": "^7.9.0", |     "@typescript-eslint/parser": "^7.9.0", | ||||||
|   | |||||||
| @@ -43,6 +43,7 @@ class GitAuthHelper { | |||||||
|   private sshKeyPath = '' |   private sshKeyPath = '' | ||||||
|   private sshKnownHostsPath = '' |   private sshKnownHostsPath = '' | ||||||
|   private temporaryHomePath = '' |   private temporaryHomePath = '' | ||||||
|  |   private credentialsConfigPath = '' // Path to separate credentials config file in RUNNER_TEMP | ||||||
|  |  | ||||||
|   constructor( |   constructor( | ||||||
|     gitCommandManager: IGitCommandManager, |     gitCommandManager: IGitCommandManager, | ||||||
| @@ -126,16 +127,21 @@ class GitAuthHelper { | |||||||
|  |  | ||||||
|   async configureGlobalAuth(): Promise<void> { |   async configureGlobalAuth(): Promise<void> { | ||||||
|     // 'configureTempGlobalConfig' noops if already set, just returns the path |     // 'configureTempGlobalConfig' noops if already set, just returns the path | ||||||
|     const newGitConfigPath = await this.configureTempGlobalConfig() |     await this.configureTempGlobalConfig() | ||||||
|     try { |     try { | ||||||
|       // Configure the token |       // Configure the token | ||||||
|       await this.configureToken(newGitConfigPath, true) |       await this.configureToken(true) | ||||||
|  |  | ||||||
|       // Configure HTTPS instead of SSH |       // Configure HTTPS instead of SSH | ||||||
|       await this.git.tryConfigUnset(this.insteadOfKey, true) |       await this.git.tryConfigUnset(this.insteadOfKey, true) | ||||||
|       if (!this.settings.sshKey) { |       if (!this.settings.sshKey) { | ||||||
|         for (const insteadOfValue of this.insteadOfValues) { |         for (const insteadOfValue of this.insteadOfValues) { | ||||||
|           await this.git.config(this.insteadOfKey, insteadOfValue, true, true) |           await this.git.config( | ||||||
|  |             this.insteadOfKey, | ||||||
|  |             insteadOfValue, | ||||||
|  |             true, // globalConfig? | ||||||
|  |             true // add? | ||||||
|  |           ) | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } catch (err) { |     } catch (err) { | ||||||
| @@ -150,24 +156,60 @@ class GitAuthHelper { | |||||||
|  |  | ||||||
|   async configureSubmoduleAuth(): Promise<void> { |   async configureSubmoduleAuth(): Promise<void> { | ||||||
|     // Remove possible previous HTTPS instead of SSH |     // Remove possible previous HTTPS instead of SSH | ||||||
|     await this.removeGitConfig(this.insteadOfKey, true) |     await this.removeSubmoduleGitConfig(this.insteadOfKey) | ||||||
|  |  | ||||||
|     if (this.settings.persistCredentials) { |     if (this.settings.persistCredentials) { | ||||||
|       // Configure a placeholder value. This approach avoids the credential being captured |       // Get the credentials config file path in RUNNER_TEMP | ||||||
|       // by process creation audit events, which are commonly logged. For more information, |       const credentialsConfigPath = this.getCredentialsConfigPath() | ||||||
|       // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing |  | ||||||
|       const output = await this.git.submoduleForeach( |       // Container credentials config path | ||||||
|         // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline |       const containerCredentialsPath = path.posix.join( | ||||||
|         `sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`, |         '/github/runner_temp', | ||||||
|  |         path.basename(credentialsConfigPath) | ||||||
|  |       ) | ||||||
|  |  | ||||||
|  |       // Get submodule config file paths. | ||||||
|  |       const configPaths = await this.git.getSubmoduleConfigPaths( | ||||||
|         this.settings.nestedSubmodules |         this.settings.nestedSubmodules | ||||||
|       ) |       ) | ||||||
|  |  | ||||||
|       // Replace the placeholder |       // For each submodule, configure includeIf entries pointing to the shared credentials file. | ||||||
|       const configPaths: string[] = |       // Configure both host and container paths to support Docker container actions. | ||||||
|         output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [] |  | ||||||
|       for (const configPath of configPaths) { |       for (const configPath of configPaths) { | ||||||
|         core.debug(`Replacing token placeholder in '${configPath}'`) |         // Submodule Git directory | ||||||
|         await this.replaceTokenPlaceholder(configPath) |         let submoduleGitDir = path.dirname(configPath) // The config file is at .git/modules/submodule-name/config | ||||||
|  |         submoduleGitDir = submoduleGitDir.replace(/\\/g, '/') // Use forward slashes, even on Windows | ||||||
|  |  | ||||||
|  |         // Configure host includeIf | ||||||
|  |         await this.git.config( | ||||||
|  |           `includeIf.gitdir:${submoduleGitDir}.path`, | ||||||
|  |           credentialsConfigPath, | ||||||
|  |           false, // globalConfig? | ||||||
|  |           false, // add? | ||||||
|  |           configPath | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         // Container submodule git directory | ||||||
|  |         const githubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||||
|  |         assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined') | ||||||
|  |         let relativeSubmoduleGitDir = path.relative( | ||||||
|  |           githubWorkspace, | ||||||
|  |           submoduleGitDir | ||||||
|  |         ) | ||||||
|  |         relativeSubmoduleGitDir = relativeSubmoduleGitDir.replace(/\\/g, '/') // Use forward slashes, even on Windows | ||||||
|  |         const containerSubmoduleGitDir = path.posix.join( | ||||||
|  |           '/github/workspace', | ||||||
|  |           relativeSubmoduleGitDir | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         // Configure container includeIf | ||||||
|  |         await this.git.config( | ||||||
|  |           `includeIf.gitdir:${containerSubmoduleGitDir}.path`, | ||||||
|  |           containerCredentialsPath, | ||||||
|  |           false, // globalConfig? | ||||||
|  |           false, // add? | ||||||
|  |           configPath | ||||||
|  |         ) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       if (this.settings.sshKey) { |       if (this.settings.sshKey) { | ||||||
| @@ -201,6 +243,10 @@ class GitAuthHelper { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Configures SSH authentication by writing the SSH key and known hosts, | ||||||
|  |    * and setting up the GIT_SSH_COMMAND environment variable. | ||||||
|  |    */ | ||||||
|   private async configureSsh(): Promise<void> { |   private async configureSsh(): Promise<void> { | ||||||
|     if (!this.settings.sshKey) { |     if (!this.settings.sshKey) { | ||||||
|       return |       return | ||||||
| @@ -272,57 +318,116 @@ class GitAuthHelper { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async configureToken( |   /** | ||||||
|     configPath?: string, |    * Configures token-based authentication by creating a credentials config file | ||||||
|     globalConfig?: boolean |    * and setting up includeIf entries to reference it. | ||||||
|   ): Promise<void> { |    * @param globalConfig Whether to configure global config instead of local | ||||||
|     // Validate args |    */ | ||||||
|     assert.ok( |   private async configureToken(globalConfig?: boolean): Promise<void> { | ||||||
|       (configPath && globalConfig) || (!configPath && !globalConfig), |     // Get the credentials config file path in RUNNER_TEMP | ||||||
|       'Unexpected configureToken parameter combinations' |     const credentialsConfigPath = this.getCredentialsConfigPath() | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     // Default config path |     // Write placeholder to the separate credentials config file using git config. | ||||||
|     if (!configPath && !globalConfig) { |     // This approach avoids the credential being captured by process creation audit events, | ||||||
|       configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config') |     // which are commonly logged. For more information, refer to | ||||||
|     } |     // https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing | ||||||
|  |  | ||||||
|     // Configure a placeholder value. This approach avoids the credential being captured |  | ||||||
|     // by process creation audit events, which are commonly logged. For more information, |  | ||||||
|     // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing |  | ||||||
|     await this.git.config( |     await this.git.config( | ||||||
|       this.tokenConfigKey, |       this.tokenConfigKey, | ||||||
|       this.tokenPlaceholderConfigValue, |       this.tokenPlaceholderConfigValue, | ||||||
|       globalConfig |       false, // globalConfig? | ||||||
|  |       false, // add? | ||||||
|  |       credentialsConfigPath | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     // Replace the placeholder |     // Replace the placeholder in the credentials config file | ||||||
|     await this.replaceTokenPlaceholder(configPath || '') |     let content = (await fs.promises.readFile(credentialsConfigPath)).toString() | ||||||
|   } |  | ||||||
|  |  | ||||||
|   private async replaceTokenPlaceholder(configPath: string): Promise<void> { |  | ||||||
|     assert.ok(configPath, 'configPath is not defined') |  | ||||||
|     let content = (await fs.promises.readFile(configPath)).toString() |  | ||||||
|     const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue) |     const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue) | ||||||
|     if ( |     if ( | ||||||
|       placeholderIndex < 0 || |       placeholderIndex < 0 || | ||||||
|       placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue) |       placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue) | ||||||
|     ) { |     ) { | ||||||
|       throw new Error(`Unable to replace auth placeholder in ${configPath}`) |       throw new Error( | ||||||
|  |         `Unable to replace auth placeholder in ${credentialsConfigPath}` | ||||||
|  |       ) | ||||||
|     } |     } | ||||||
|     assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined') |     assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined') | ||||||
|     content = content.replace( |     content = content.replace( | ||||||
|       this.tokenPlaceholderConfigValue, |       this.tokenPlaceholderConfigValue, | ||||||
|       this.tokenConfigValue |       this.tokenConfigValue | ||||||
|     ) |     ) | ||||||
|     await fs.promises.writeFile(configPath, content) |     await fs.promises.writeFile(credentialsConfigPath, content) | ||||||
|  |  | ||||||
|  |     // Add include or includeIf to reference the credentials config | ||||||
|  |     if (globalConfig) { | ||||||
|  |       // Global config file is temporary | ||||||
|  |       await this.git.config( | ||||||
|  |         'include.path', | ||||||
|  |         credentialsConfigPath, | ||||||
|  |         true // globalConfig? | ||||||
|  |       ) | ||||||
|  |     } else { | ||||||
|  |       // Host git directory | ||||||
|  |       let gitDir = path.join(this.git.getWorkingDirectory(), '.git') | ||||||
|  |       gitDir = gitDir.replace(/\\/g, '/') // Use forward slashes, even on Windows | ||||||
|  |  | ||||||
|  |       // Configure host includeIf | ||||||
|  |       const hostIncludeKey = `includeIf.gitdir:${gitDir}.path` | ||||||
|  |       await this.git.config(hostIncludeKey, credentialsConfigPath) | ||||||
|  |  | ||||||
|  |       // Container git directory | ||||||
|  |       const workingDirectory = this.git.getWorkingDirectory() | ||||||
|  |       const githubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||||
|  |       assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined') | ||||||
|  |       let relativePath = path.relative(githubWorkspace, workingDirectory) | ||||||
|  |       relativePath = relativePath.replace(/\\/g, '/') // Use forward slashes, even on Windows | ||||||
|  |       const containerGitDir = path.posix.join( | ||||||
|  |         '/github/workspace', | ||||||
|  |         relativePath, | ||||||
|  |         '.git' | ||||||
|  |       ) | ||||||
|  |  | ||||||
|  |       // Container credentials config path | ||||||
|  |       const containerCredentialsPath = path.posix.join( | ||||||
|  |         '/github/runner_temp', | ||||||
|  |         path.basename(credentialsConfigPath) | ||||||
|  |       ) | ||||||
|  |  | ||||||
|  |       // Configure container includeIf | ||||||
|  |       const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path` | ||||||
|  |       await this.git.config(containerIncludeKey, containerCredentialsPath) | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Gets or creates the path to the credentials config file in RUNNER_TEMP. | ||||||
|  |    * @returns The absolute path to the credentials config file | ||||||
|  |    */ | ||||||
|  |   private getCredentialsConfigPath(): string { | ||||||
|  |     if (this.credentialsConfigPath) { | ||||||
|  |       return this.credentialsConfigPath | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const runnerTemp = process.env['RUNNER_TEMP'] || '' | ||||||
|  |     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') | ||||||
|  |  | ||||||
|  |     // Create a unique filename for this checkout instance | ||||||
|  |     const configFileName = `git-credentials-${uuid()}.config` | ||||||
|  |     this.credentialsConfigPath = path.join(runnerTemp, configFileName) | ||||||
|  |  | ||||||
|  |     core.debug(`Credentials config path: ${this.credentialsConfigPath}`) | ||||||
|  |     return this.credentialsConfigPath | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Removes SSH authentication configuration by cleaning up SSH keys, | ||||||
|  |    * known hosts files, and SSH command configurations. | ||||||
|  |    */ | ||||||
|   private async removeSsh(): Promise<void> { |   private async removeSsh(): Promise<void> { | ||||||
|     // SSH key |     // SSH key | ||||||
|     const keyPath = this.sshKeyPath || stateHelper.SshKeyPath |     const keyPath = this.sshKeyPath || stateHelper.SshKeyPath | ||||||
|     if (keyPath) { |     if (keyPath) { | ||||||
|       try { |       try { | ||||||
|  |         core.info(`Removing SSH key '${keyPath}'`) | ||||||
|         await io.rmRF(keyPath) |         await io.rmRF(keyPath) | ||||||
|       } catch (err) { |       } catch (err) { | ||||||
|         core.debug(`${(err as any)?.message ?? err}`) |         core.debug(`${(err as any)?.message ?? err}`) | ||||||
| @@ -335,40 +440,149 @@ class GitAuthHelper { | |||||||
|       this.sshKnownHostsPath || stateHelper.SshKnownHostsPath |       this.sshKnownHostsPath || stateHelper.SshKnownHostsPath | ||||||
|     if (knownHostsPath) { |     if (knownHostsPath) { | ||||||
|       try { |       try { | ||||||
|  |         core.info(`Removing SSH known hosts '${knownHostsPath}'`) | ||||||
|         await io.rmRF(knownHostsPath) |         await io.rmRF(knownHostsPath) | ||||||
|       } catch { |       } catch (err) { | ||||||
|         // Intentionally empty |         core.debug(`${(err as any)?.message ?? err}`) | ||||||
|  |         core.warning(`Failed to remove SSH known hosts '${knownHostsPath}'`) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // SSH command |     // SSH command | ||||||
|  |     core.info('Removing SSH command configuration') | ||||||
|     await this.removeGitConfig(SSH_COMMAND_KEY) |     await this.removeGitConfig(SSH_COMMAND_KEY) | ||||||
|  |     await this.removeSubmoduleGitConfig(SSH_COMMAND_KEY) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Removes token-based authentication by cleaning up HTTP headers, | ||||||
|  |    * includeIf entries, and credentials config files. | ||||||
|  |    */ | ||||||
|   private async removeToken(): Promise<void> { |   private async removeToken(): Promise<void> { | ||||||
|     // HTTP extra header |     // Remove HTTP extra header | ||||||
|  |     core.info('Removing HTTP extra header') | ||||||
|     await this.removeGitConfig(this.tokenConfigKey) |     await this.removeGitConfig(this.tokenConfigKey) | ||||||
|   } |     await this.removeSubmoduleGitConfig(this.tokenConfigKey) | ||||||
|  |  | ||||||
|   private async removeGitConfig( |     // Collect credentials config paths that need to be removed | ||||||
|     configKey: string, |     const credentialsPaths = new Set<string>() | ||||||
|     submoduleOnly: boolean = false |  | ||||||
|   ): Promise<void> { |     // Remove includeIf entries that point to git-credentials-*.config files | ||||||
|     if (!submoduleOnly) { |     core.info('Removing includeIf entries pointing to credentials config files') | ||||||
|       if ( |     const mainCredentialsPaths = await this.removeIncludeIfCredentials() | ||||||
|         (await this.git.configExists(configKey)) && |     mainCredentialsPaths.forEach(path => credentialsPaths.add(path)) | ||||||
|         !(await this.git.tryConfigUnset(configKey)) |  | ||||||
|       ) { |     // Remove submodule includeIf entries that point to git-credentials-*.config files | ||||||
|         // Load the config contents |     const submoduleConfigPaths = await this.git.getSubmoduleConfigPaths(true) | ||||||
|         core.warning(`Failed to remove '${configKey}' from the git config`) |     for (const configPath of submoduleConfigPaths) { | ||||||
|       } |       const submoduleCredentialsPaths = | ||||||
|  |         await this.removeIncludeIfCredentials(configPath) | ||||||
|  |       submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Remove credentials config files | ||||||
|  |     for (const credentialsPath of credentialsPaths) { | ||||||
|  |       // Only remove credentials config files if they are under RUNNER_TEMP | ||||||
|  |       const runnerTemp = process.env['RUNNER_TEMP'] | ||||||
|  |       assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') | ||||||
|  |       if (credentialsPath.startsWith(runnerTemp)) { | ||||||
|  |         try { | ||||||
|  |           core.info(`Removing credentials config '${credentialsPath}'`) | ||||||
|  |           await io.rmRF(credentialsPath) | ||||||
|  |         } catch (err) { | ||||||
|  |           core.debug(`${(err as any)?.message ?? err}`) | ||||||
|  |           core.warning( | ||||||
|  |             `Failed to remove credentials config '${credentialsPath}'` | ||||||
|  |           ) | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         core.debug( | ||||||
|  |           `Skipping removal of credentials config '${credentialsPath}' - not under RUNNER_TEMP` | ||||||
|  |         ) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Removes a git config key from the local repository config. | ||||||
|  |    * @param configKey The git config key to remove | ||||||
|  |    */ | ||||||
|  |   private async removeGitConfig(configKey: string): Promise<void> { | ||||||
|  |     if ( | ||||||
|  |       (await this.git.configExists(configKey)) && | ||||||
|  |       !(await this.git.tryConfigUnset(configKey)) | ||||||
|  |     ) { | ||||||
|  |       // Load the config contents | ||||||
|  |       core.warning(`Failed to remove '${configKey}' from the git config`) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Removes a git config key from all submodule configs. | ||||||
|  |    * @param configKey The git config key to remove | ||||||
|  |    */ | ||||||
|  |   private async removeSubmoduleGitConfig(configKey: string): Promise<void> { | ||||||
|     const pattern = regexpHelper.escape(configKey) |     const pattern = regexpHelper.escape(configKey) | ||||||
|     await this.git.submoduleForeach( |     await this.git.submoduleForeach( | ||||||
|       // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline |       // Wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline. | ||||||
|       `sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, |       `sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, | ||||||
|       true |       true | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Removes includeIf entries that point to git-credentials-*.config files. | ||||||
|  |    * @param configPath Optional path to a specific git config file to operate on | ||||||
|  |    * @returns Array of unique credentials config file paths that were found and removed | ||||||
|  |    */ | ||||||
|  |   private async removeIncludeIfCredentials( | ||||||
|  |     configPath?: string | ||||||
|  |   ): Promise<string[]> { | ||||||
|  |     const credentialsPaths = new Set<string>() | ||||||
|  |  | ||||||
|  |     try { | ||||||
|  |       // Get all includeIf.gitdir keys | ||||||
|  |       const keys = await this.git.tryGetConfigKeys( | ||||||
|  |         '^includeIf\\.gitdir:', | ||||||
|  |         false, // globalConfig? | ||||||
|  |         configPath | ||||||
|  |       ) | ||||||
|  |  | ||||||
|  |       for (const key of keys) { | ||||||
|  |         // Get all values for this key | ||||||
|  |         const values = await this.git.tryGetConfigValues( | ||||||
|  |           key, | ||||||
|  |           false, // globalConfig? | ||||||
|  |           configPath | ||||||
|  |         ) | ||||||
|  |         if (values.length > 0) { | ||||||
|  |           // Remove only values that match git-credentials-<uuid>.config pattern | ||||||
|  |           for (const value of values) { | ||||||
|  |             if (this.testCredentialsConfigPath(value)) { | ||||||
|  |               credentialsPaths.add(value) | ||||||
|  |               await this.git.tryConfigUnsetValue(key, value, false, configPath) | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } catch (err) { | ||||||
|  |       // Ignore errors - this is cleanup code | ||||||
|  |       if (configPath) { | ||||||
|  |         core.debug(`Error during includeIf cleanup for ${configPath}: ${err}`) | ||||||
|  |       } else { | ||||||
|  |         core.debug(`Error during includeIf cleanup: ${err}`) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Array.from(credentialsPaths) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Tests if a path matches the git-credentials-*.config pattern. | ||||||
|  |    * @param path The path to test | ||||||
|  |    * @returns True if the path matches the credentials config pattern | ||||||
|  |    */ | ||||||
|  |   private testCredentialsConfigPath(path: string): boolean { | ||||||
|  |     return /git-credentials-[0-9a-f-]+\.config$/i.test(path) | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -28,7 +28,8 @@ export interface IGitCommandManager { | |||||||
|     configKey: string, |     configKey: string, | ||||||
|     configValue: string, |     configValue: string, | ||||||
|     globalConfig?: boolean, |     globalConfig?: boolean, | ||||||
|     add?: boolean |     add?: boolean, | ||||||
|  |     configFile?: string | ||||||
|   ): Promise<void> |   ): Promise<void> | ||||||
|   configExists(configKey: string, globalConfig?: boolean): Promise<boolean> |   configExists(configKey: string, globalConfig?: boolean): Promise<boolean> | ||||||
|   fetch( |   fetch( | ||||||
| @@ -41,6 +42,7 @@ export interface IGitCommandManager { | |||||||
|     } |     } | ||||||
|   ): Promise<void> |   ): Promise<void> | ||||||
|   getDefaultBranch(repositoryUrl: string): Promise<string> |   getDefaultBranch(repositoryUrl: string): Promise<string> | ||||||
|  |   getSubmoduleConfigPaths(recursive: boolean): Promise<string[]> | ||||||
|   getWorkingDirectory(): string |   getWorkingDirectory(): string | ||||||
|   init(): Promise<void> |   init(): Promise<void> | ||||||
|   isDetached(): Promise<boolean> |   isDetached(): Promise<boolean> | ||||||
| @@ -59,8 +61,24 @@ export interface IGitCommandManager { | |||||||
|   tagExists(pattern: string): Promise<boolean> |   tagExists(pattern: string): Promise<boolean> | ||||||
|   tryClean(): Promise<boolean> |   tryClean(): Promise<boolean> | ||||||
|   tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean> |   tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean> | ||||||
|  |   tryConfigUnsetValue( | ||||||
|  |     configKey: string, | ||||||
|  |     configValue: string, | ||||||
|  |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|  |   ): Promise<boolean> | ||||||
|   tryDisableAutomaticGarbageCollection(): Promise<boolean> |   tryDisableAutomaticGarbageCollection(): Promise<boolean> | ||||||
|   tryGetFetchUrl(): Promise<string> |   tryGetFetchUrl(): Promise<string> | ||||||
|  |   tryGetConfigValues( | ||||||
|  |     configKey: string, | ||||||
|  |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|  |   ): Promise<string[]> | ||||||
|  |   tryGetConfigKeys( | ||||||
|  |     pattern: string, | ||||||
|  |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|  |   ): Promise<string[]> | ||||||
|   tryReset(): Promise<boolean> |   tryReset(): Promise<boolean> | ||||||
|   version(): Promise<GitVersion> |   version(): Promise<GitVersion> | ||||||
| } | } | ||||||
| @@ -223,9 +241,15 @@ class GitCommandManager { | |||||||
|     configKey: string, |     configKey: string, | ||||||
|     configValue: string, |     configValue: string, | ||||||
|     globalConfig?: boolean, |     globalConfig?: boolean, | ||||||
|     add?: boolean |     add?: boolean, | ||||||
|  |     configFile?: string | ||||||
|   ): Promise<void> { |   ): Promise<void> { | ||||||
|     const args: string[] = ['config', globalConfig ? '--global' : '--local'] |     const args: string[] = ['config'] | ||||||
|  |     if (configFile) { | ||||||
|  |       args.push('--file', configFile) | ||||||
|  |     } else { | ||||||
|  |       args.push(globalConfig ? '--global' : '--local') | ||||||
|  |     } | ||||||
|     if (add) { |     if (add) { | ||||||
|       args.push('--add') |       args.push('--add') | ||||||
|     } |     } | ||||||
| @@ -323,6 +347,21 @@ class GitCommandManager { | |||||||
|     throw new Error('Unexpected output when retrieving default branch') |     throw new Error('Unexpected output when retrieving default branch') | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async getSubmoduleConfigPaths(recursive: boolean): Promise<string[]> { | ||||||
|  |     // Get submodule config file paths. | ||||||
|  |     // Use `--show-origin` to get the config file path for each submodule. | ||||||
|  |     const output = await this.submoduleForeach( | ||||||
|  |       `git config --local --show-origin --name-only --get-regexp remote.origin.url`, | ||||||
|  |       recursive | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     // Extract config file paths from the output (lines starting with "file:"). | ||||||
|  |     const configPaths = | ||||||
|  |       output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [] | ||||||
|  |  | ||||||
|  |     return configPaths | ||||||
|  |   } | ||||||
|  |  | ||||||
|   getWorkingDirectory(): string { |   getWorkingDirectory(): string { | ||||||
|     return this.workingDirectory |     return this.workingDirectory | ||||||
|   } |   } | ||||||
| @@ -455,6 +494,24 @@ class GitCommandManager { | |||||||
|     return output.exitCode === 0 |     return output.exitCode === 0 | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async tryConfigUnsetValue( | ||||||
|  |     configKey: string, | ||||||
|  |     configValue: string, | ||||||
|  |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|  |   ): Promise<boolean> { | ||||||
|  |     const args = ['config'] | ||||||
|  |     if (configFile) { | ||||||
|  |       args.push('--file', configFile) | ||||||
|  |     } else { | ||||||
|  |       args.push(globalConfig ? '--global' : '--local') | ||||||
|  |     } | ||||||
|  |     args.push('--unset', configKey, configValue) | ||||||
|  |  | ||||||
|  |     const output = await this.execGit(args, true) | ||||||
|  |     return output.exitCode === 0 | ||||||
|  |   } | ||||||
|  |  | ||||||
|   async tryDisableAutomaticGarbageCollection(): Promise<boolean> { |   async tryDisableAutomaticGarbageCollection(): Promise<boolean> { | ||||||
|     const output = await this.execGit( |     const output = await this.execGit( | ||||||
|       ['config', '--local', 'gc.auto', '0'], |       ['config', '--local', 'gc.auto', '0'], | ||||||
| @@ -481,6 +538,56 @@ class GitCommandManager { | |||||||
|     return stdout |     return stdout | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   async tryGetConfigValues( | ||||||
|  |     configKey: string, | ||||||
|  |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|  |   ): Promise<string[]> { | ||||||
|  |     const args = ['config'] | ||||||
|  |     if (configFile) { | ||||||
|  |       args.push('--file', configFile) | ||||||
|  |     } else { | ||||||
|  |       args.push(globalConfig ? '--global' : '--local') | ||||||
|  |     } | ||||||
|  |     args.push('--get-all', configKey) | ||||||
|  |  | ||||||
|  |     const output = await this.execGit(args, true) | ||||||
|  |  | ||||||
|  |     if (output.exitCode !== 0) { | ||||||
|  |       return [] | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return output.stdout | ||||||
|  |       .trim() | ||||||
|  |       .split('\n') | ||||||
|  |       .filter(value => value.trim()) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async tryGetConfigKeys( | ||||||
|  |     pattern: string, | ||||||
|  |     globalConfig?: boolean, | ||||||
|  |     configFile?: string | ||||||
|  |   ): Promise<string[]> { | ||||||
|  |     const args = ['config'] | ||||||
|  |     if (configFile) { | ||||||
|  |       args.push('--file', configFile) | ||||||
|  |     } else { | ||||||
|  |       args.push(globalConfig ? '--global' : '--local') | ||||||
|  |     } | ||||||
|  |     args.push('--name-only', '--get-regexp', pattern) | ||||||
|  |  | ||||||
|  |     const output = await this.execGit(args, true) | ||||||
|  |  | ||||||
|  |     if (output.exitCode !== 0) { | ||||||
|  |       return [] | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return output.stdout | ||||||
|  |       .trim() | ||||||
|  |       .split('\n') | ||||||
|  |       .filter(key => key.trim()) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   async tryReset(): Promise<boolean> { |   async tryReset(): Promise<boolean> { | ||||||
|     const output = await this.execGit(['reset', '--hard', 'HEAD'], true) |     const output = await this.execGit(['reset', '--hard', 'HEAD'], true) | ||||||
|     return output.exitCode === 0 |     return output.exitCode === 0 | ||||||
|   | |||||||
| @@ -120,7 +120,7 @@ function updateUsage( | |||||||
| } | } | ||||||
|  |  | ||||||
| updateUsage( | updateUsage( | ||||||
|   'actions/checkout@v4', |   'actions/checkout@v5', | ||||||
|   path.join(__dirname, '..', '..', 'action.yml'), |   path.join(__dirname, '..', '..', 'action.yml'), | ||||||
|   path.join(__dirname, '..', '..', 'README.md') |   path.join(__dirname, '..', '..', 'README.md') | ||||||
| ) | ) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user