Skip to content

Conversation

@renovate
Copy link
Contributor

@renovate renovate bot commented Nov 27, 2025

This PR contains the following updates:

Package Type Update Change Pending
actions/checkout action major v5.0.1 -> v6.0.0 v6.0.1

Release Notes

actions/checkout (actions/checkout)

v6.0.0

Compare Source


Configuration

📅 Schedule: Branch creation - Tuesday through Thursday ( * * * * 2-4 ) (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot added dependencies Pull requests that update a dependency file renovate labels Nov 27, 2025
@renovate renovate bot added dependencies Pull requests that update a dependency file renovate labels Nov 27, 2025
@renovate renovate bot enabled auto-merge (squash) November 27, 2025 18:52
@github-actions
Copy link
Contributor

👋 Thanks for Submitting! This PR is available for preview at the link below.

✅ PR tip preview: https://1273.pr.nala.bravesoftware.com/
✅ Commit preview: https://1273.pr.nala.bravesoftware.com/commit-fddcfb3c9b253ae748a78a9a749e03074e04574a/

- ./tokens/css/variables-android.old.css: 7390 bytes
+ ./tokens/css/variables-android.css: 7390 bytes
---
- ./tokens/css/variables-browser.old.css: 6644 bytes
+ ./tokens/css/variables-browser.css: 6644 bytes
---
- ./tokens/css/variables-ios.old.css: 8180 bytes
+ ./tokens/css/variables-ios.css: 8180 bytes
---
- ./tokens/css/variables-marketing.old.css: 13501 bytes
+ ./tokens/css/variables-marketing.css: 13501 bytes
---
- ./tokens/css/variables-news.old.css: 526 bytes
+ ./tokens/css/variables-news.css: 526 bytes
---
- ./tokens/css/variables-newtab.old.css: 1933 bytes
+ ./tokens/css/variables-newtab.css: 1933 bytes
---
- ./tokens/css/variables-search.old.css: 17733 bytes
+ ./tokens/css/variables-search.css: 17733 bytes
---
- ./tokens/css/variables-web3.old.css: 893 bytes
+ ./tokens/css/variables-web3.css: 893 bytes
---
- ./tokens/css/variables.old.css: 125593 bytes
+ ./tokens/css/variables.css: 125593 bytes
Variables Diff: variables-android.diff
--- ./tokens/css/variables-android.old.css	2025-11-27 18:53:10.128193337 +0000
+++ ./tokens/css/variables-android.css	2025-11-27 18:52:39.040486124 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:39 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-browser.diff
--- ./tokens/css/variables-browser.old.css	2025-11-27 18:53:10.367191028 +0000
+++ ./tokens/css/variables-browser.css	2025-11-27 18:52:39.026486269 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:39 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-ios.diff
--- ./tokens/css/variables-ios.old.css	2025-11-27 18:53:10.614188646 +0000
+++ ./tokens/css/variables-ios.css	2025-11-27 18:52:39.055485969 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:39 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-marketing.diff
--- ./tokens/css/variables-marketing.old.css	2025-11-27 18:53:10.840186464 +0000
+++ ./tokens/css/variables-marketing.css	2025-11-27 18:52:39.076485753 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:39 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-news.diff
--- ./tokens/css/variables-news.old.css	2025-11-27 18:53:11.063184312 +0000
+++ ./tokens/css/variables-news.css	2025-11-27 18:52:39.113485370 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:39 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-newtab.diff
--- ./tokens/css/variables-newtab.old.css	2025-11-27 18:53:11.301182013 +0000
+++ ./tokens/css/variables-newtab.css	2025-11-27 18:52:39.121485288 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:39 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-search.diff
--- ./tokens/css/variables-search.old.css	2025-11-27 18:53:11.522179880 +0000
+++ ./tokens/css/variables-search.css	2025-11-27 18:52:39.099485515 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:39 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-web3.diff
--- ./tokens/css/variables-web3.old.css	2025-11-27 18:53:11.733177844 +0000
+++ ./tokens/css/variables-web3.css	2025-11-27 18:52:39.125485247 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:39 GMT+0000 (Coordinated Universal Time)
  */
 
 @media (prefers-color-scheme: light) {
Variables Diff: variables.diff
--- ./tokens/css/variables.old.css	2025-11-27 18:53:11.953175722 +0000
+++ ./tokens/css/variables.css	2025-11-27 18:52:38.909487477 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Nov 27 2025 02:51:26 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Nov 27 2025 18:52:38 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {

@renovate renovate bot force-pushed the renovate/actions-checkout-6-x branch from fddcfb3 to 585710c Compare December 3, 2025 21:33
@github-actions
Copy link
Contributor

github-actions bot commented Dec 3, 2025

👋 Thanks for Submitting! This PR is available for preview at the link below.

✅ PR tip preview: https://1273.pr.nala.bravesoftware.com/
✅ Commit preview: https://1273.pr.nala.bravesoftware.com/commit-585710c119407b58013c74c24fe9a8b27289e170/

- ./tokens/css/variables-android.old.css: 7390 bytes
+ ./tokens/css/variables-android.css: 7390 bytes
---
- ./tokens/css/variables-browser.old.css: 6644 bytes
+ ./tokens/css/variables-browser.css: 6644 bytes
---
- ./tokens/css/variables-ios.old.css: 8180 bytes
+ ./tokens/css/variables-ios.css: 8180 bytes
---
- ./tokens/css/variables-marketing.old.css: 13501 bytes
+ ./tokens/css/variables-marketing.css: 13501 bytes
---
- ./tokens/css/variables-news.old.css: 526 bytes
+ ./tokens/css/variables-news.css: 526 bytes
---
- ./tokens/css/variables-newtab.old.css: 1933 bytes
+ ./tokens/css/variables-newtab.css: 1933 bytes
---
- ./tokens/css/variables-search.old.css: 17733 bytes
+ ./tokens/css/variables-search.css: 17733 bytes
---
- ./tokens/css/variables-web3.old.css: 893 bytes
+ ./tokens/css/variables-web3.css: 893 bytes
---
- ./tokens/css/variables.old.css: 125593 bytes
+ ./tokens/css/variables.css: 125593 bytes
Variables Diff: variables-android.diff
--- ./tokens/css/variables-android.old.css	2025-12-03 21:34:51.501360533 +0000
+++ ./tokens/css/variables-android.css	2025-12-03 21:34:18.370149444 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-browser.diff
--- ./tokens/css/variables-browser.old.css	2025-12-03 21:34:51.650361421 +0000
+++ ./tokens/css/variables-browser.css	2025-12-03 21:34:18.355149347 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-ios.diff
--- ./tokens/css/variables-ios.old.css	2025-12-03 21:34:51.784362222 +0000
+++ ./tokens/css/variables-ios.css	2025-12-03 21:34:18.386149547 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-marketing.diff
--- ./tokens/css/variables-marketing.old.css	2025-12-03 21:34:51.918363023 +0000
+++ ./tokens/css/variables-marketing.css	2025-12-03 21:34:18.409149695 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-news.diff
--- ./tokens/css/variables-news.old.css	2025-12-03 21:34:52.007363553 +0000
+++ ./tokens/css/variables-news.css	2025-12-03 21:34:18.467150068 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-newtab.diff
--- ./tokens/css/variables-newtab.old.css	2025-12-03 21:34:52.148364395 +0000
+++ ./tokens/css/variables-newtab.css	2025-12-03 21:34:18.476150126 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-search.diff
--- ./tokens/css/variables-search.old.css	2025-12-03 21:34:52.229364878 +0000
+++ ./tokens/css/variables-search.css	2025-12-03 21:34:18.440149894 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-web3.diff
--- ./tokens/css/variables-web3.old.css	2025-12-03 21:34:52.606367129 +0000
+++ ./tokens/css/variables-web3.css	2025-12-03 21:34:18.481150158 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 @media (prefers-color-scheme: light) {
Variables Diff: variables.diff
--- ./tokens/css/variables.old.css	2025-12-03 21:34:52.787368212 +0000
+++ ./tokens/css/variables.css	2025-12-03 21:34:18.224148504 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Wed Dec 03 2025 21:33:19 GMT+0000 (Coordinated Universal Time)
+ * Generated on Wed Dec 03 2025 21:34:18 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {

@renovate renovate bot force-pushed the renovate/actions-checkout-6-x branch from 585710c to 32eef6f Compare December 4, 2025 14:53
@github-actions
Copy link
Contributor

github-actions bot commented Dec 4, 2025

[puLL-Merge] - actions/[email protected]

Diff
diff --git .github/workflows/test.yml .github/workflows/test.yml
index e62ac3ba6..7c47d7b6a 100644
--- .github/workflows/test.yml
+++ .github/workflows/test.yml
@@ -302,12 +302,15 @@ jobs:
       # Clone this repo
       - name: Checkout
         uses: actions/[email protected]
+        with:
+          path: actions-checkout
 
       # Basic checkout using git
       - name: Checkout basic
         id: checkout
-        uses: ./
+        uses: ./actions-checkout
         with:
+          path: cloned-using-local-action
           ref: test-data/v2/basic
 
       # Verify output
@@ -325,7 +328,3 @@ jobs:
             echo "Expected commit to be 82f71901cf8c021332310dcc8cdba84c4193ff5d"
             exit 1
           fi
-
-      # needed to make checkout post cleanup succeed
-      - name: Fix Checkout
-        uses: actions/[email protected]
diff --git CHANGELOG.md CHANGELOG.md
index ff8b4e7b0..25befb782 100644
--- CHANGELOG.md
+++ CHANGELOG.md
@@ -1,8 +1,17 @@
 # Changelog
 
+## V6.0.0
+* Persist creds to a separate file by @ericsciple in https://github.com/actions/checkout/pull/2286
+* Update README to include Node.js 24 support details and requirements by @salmanmkc in https://github.com/actions/checkout/pull/2248
+
+## V5.0.1
+* Port v6 cleanup to v5 by @ericsciple in https://github.com/actions/checkout/pull/2301
+
 ## V5.0.0
 * Update actions checkout to use node 24 by @salmanmkc in https://github.com/actions/checkout/pull/2226
 
+## V4.3.1
+* Port v6 cleanup to v4 by @ericsciple in https://github.com/actions/checkout/pull/2305
 
 ## V4.3.0
 * docs: update README.md by @motss in https://github.com/actions/checkout/pull/1971
diff --git README.md README.md
index f9175e995..5ad476f49 100644
--- README.md
+++ README.md
@@ -1,10 +1,21 @@
 [![Build and Test](https://github.com/actions/checkout/actions/workflows/test.yml/badge.svg)](https://github.com/actions/checkout/actions/workflows/test.yml)
 
-# Checkout V5
+# Checkout v6
 
-Checkout v5 now supports Node.js 24
+## What's new
 
-# Checkout V4
+- Updated `persist-credentials` to store the credentials under `$RUNNER_TEMP` instead of directly in the local git config.
+  - This requires a minimum Actions Runner version of [v2.329.0](https://github.com/actions/runner/releases/tag/v2.329.0) to access the persisted credentials for [Docker container action](https://docs.github.com/en/actions/tutorials/use-containerized-services/create-a-docker-container-action) scenarios.
+
+# 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
 
 This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
 
@@ -154,9 +165,10 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 # Scenarios
 
 - [Checkout V5](#checkout-v5)
+  - [What's new](#whats-new)
 - [Checkout V4](#checkout-v4)
     - [Note](#note)
-- [What's new](#whats-new)
+- [What's new](#whats-new-1)
 - [Usage](#usage)
 - [Scenarios](#scenarios)
   - [Fetch only the root files](#fetch-only-the-root-files)
diff --git __test__/git-auth-helper.test.ts __test__/git-auth-helper.test.ts
index 7633704cc..ad3566ad6 100644
--- __test__/git-auth-helper.test.ts
+++ __test__/git-auth-helper.test.ts
@@ -86,16 +86,29 @@ describe('git-auth-helper tests', () => {
     // Act
     await authHelper.configureAuth()
 
-    // Assert config
-    const configContent = (
+    // Assert config - check that .git/config contains includeIf entries
+    const localConfigContent = (
       await fs.promises.readFile(localGitConfigPath)
     ).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(
       `x-access-token:${settings.authToken}`,
       'utf8'
     ).toString('base64')
     expect(
-      configContent.indexOf(
+      credentialsContent.indexOf(
         `http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}`
       )
     ).toBeGreaterThanOrEqual(0)
@@ -120,7 +133,7 @@ describe('git-auth-helper tests', () => {
     'inject https://github.com as github server url'
   it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => {
     await testAuthHeader(
-      configureAuth_AcceptsGitHubServerUrl,
+      configureAuth_AcceptsGitHubServerUrlSetToGHEC,
       'https://github.com'
     )
   })
@@ -141,12 +154,17 @@ describe('git-auth-helper tests', () => {
       // Act
       await authHelper.configureAuth()
 
-      // Assert config
-      const configContent = (
-        await fs.promises.readFile(localGitConfigPath)
+      // Assert config - check credentials config file (not local .git/config)
+      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()
       expect(
-        configContent.indexOf(
+        credentialsContent.indexOf(
           `http.https://github.com/.extraheader AUTHORIZATION`
         )
       ).toBeGreaterThanOrEqual(0)
@@ -251,13 +269,16 @@ describe('git-auth-helper tests', () => {
       expectedSshCommand
     )
 
-    // Asserty git config
+    // Assert git config
     const gitConfigLines = (await fs.promises.readFile(localGitConfigPath))
       .toString()
       .split('\n')
       .filter(x => x)
-    expect(gitConfigLines).toHaveLength(1)
-    expect(gitConfigLines[0]).toMatch(/^http\./)
+    // Should have includeIf entries pointing to credentials file
+    expect(gitConfigLines.length).toBeGreaterThan(0)
+    expect(
+      gitConfigLines.some(line => line.indexOf('includeIf.gitdir:') >= 0)
+    ).toBeTruthy()
   })
 
   const configureAuth_setsSshCommandWhenPersistCredentialsTrue =
@@ -419,8 +440,20 @@ describe('git-auth-helper tests', () => {
     expect(
       configContent.indexOf('value-from-global-config')
     ).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(
-      configContent.indexOf(
+      credentialsContent.indexOf(
         `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
       )
     ).toBeGreaterThanOrEqual(0)
@@ -463,8 +496,20 @@ describe('git-auth-helper tests', () => {
       const configContent = (
         await fs.promises.readFile(path.join(git.env['HOME'], '.gitconfig'))
       ).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(
-        configContent.indexOf(
+        credentialsContent.indexOf(
           `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
         )
       ).toBeGreaterThanOrEqual(0)
@@ -550,15 +595,15 @@ describe('git-auth-helper tests', () => {
       await authHelper.configureSubmoduleAuth()
 
       // Assert
-      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(4)
+      // Should configure insteadOf (2 calls for two values)
+      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
         /unset-all.*insteadOf/
       )
-      expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
-      expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(
+      expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(
         /url.*insteadOf.*[email protected]:/
       )
-      expect(mockSubmoduleForeach.mock.calls[3][0]).toMatch(
+      expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(
         /url.*insteadOf.*[email protected]:/
       )
     }
@@ -589,12 +634,12 @@ describe('git-auth-helper tests', () => {
       await authHelper.configureSubmoduleAuth()
 
       // Assert
-      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
+      // Should configure sshCommand (1 call)
+      expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2)
       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
         /unset-all.*insteadOf/
       )
-      expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
-      expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/core\.sshCommand/)
+      expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/core\.sshCommand/)
     }
   )
 
@@ -660,19 +705,201 @@ describe('git-auth-helper tests', () => {
     await setup(removeAuth_removesToken)
     const authHelper = gitAuthHelper.createAuthHelper(git, settings)
     await authHelper.configureAuth()
-    let gitConfigContent = (
+
+    // Verify includeIf entries exist in local config
+    let localConfigContent = (
       await fs.promises.readFile(localGitConfigPath)
     ).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
     await authHelper.removeAuth()
 
-    // Assert git config
-    gitConfigContent = (
+    // Assert all includeIf entries removed from local git config
+    localConfigContent = (
       await fs.promises.readFile(localGitConfigPath)
     ).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 =
@@ -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> {
@@ -715,6 +988,7 @@ async function setup(testName: string): Promise<void> {
   await fs.promises.mkdir(tempHomedir, {recursive: true})
   process.env['RUNNER_TEMP'] = runnerTemp
   process.env['HOME'] = tempHomedir
+  process.env['GITHUB_WORKSPACE'] = workspace
 
   // Create git config
   globalGitConfigPath = path.join(tempHomedir, '.gitconfig')
@@ -733,10 +1007,20 @@ async function setup(testName: string): Promise<void> {
     checkout: jest.fn(),
     checkoutDetach: jest.fn(),
     config: jest.fn(
-      async (key: string, value: string, globalConfig?: boolean) => {
-        const configPath = globalConfig
-          ? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
-          : localGitConfigPath
+      async (
+        key: string,
+        value: string,
+        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}`)
       }
     ),
@@ -756,6 +1040,7 @@ async function setup(testName: string): Promise<void> {
     env: {},
     fetch: jest.fn(),
     getDefaultBranch: jest.fn(),
+    getSubmoduleConfigPaths: jest.fn(async () => []),
     getWorkingDirectory: jest.fn(() => workspace),
     init: jest.fn(),
     isDetached: jest.fn(),
@@ -794,8 +1079,72 @@ async function setup(testName: string): Promise<void> {
         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(),
     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(),
     version: jest.fn()
   }
@@ -830,6 +1179,7 @@ async function setup(testName: string): Promise<void> {
 
 async function getActualSshKeyPath(): Promise<string> {
   let actualTempFiles = (await fs.promises.readdir(runnerTemp))
+    .filter(x => !x.startsWith('git-credentials-')) // Exclude credentials config file
     .sort()
     .map(x => path.join(runnerTemp, x))
   if (actualTempFiles.length === 0) {
@@ -843,6 +1193,7 @@ async function getActualSshKeyPath(): Promise<string> {
 
 async function getActualSshKnownHostsPath(): Promise<string> {
   let actualTempFiles = (await fs.promises.readdir(runnerTemp))
+    .filter(x => !x.startsWith('git-credentials-')) // Exclude credentials config file
     .sort()
     .map(x => path.join(runnerTemp, x))
   if (actualTempFiles.length === 0) {
diff --git __test__/git-directory-helper.test.ts __test__/git-directory-helper.test.ts
index 22e9ae6d4..de79dc890 100644
--- __test__/git-directory-helper.test.ts
+++ __test__/git-directory-helper.test.ts
@@ -471,6 +471,7 @@ async function setup(testName: string): Promise<void> {
     configExists: jest.fn(),
     fetch: jest.fn(),
     getDefaultBranch: jest.fn(),
+    getSubmoduleConfigPaths: jest.fn(async () => []),
     getWorkingDirectory: jest.fn(() => repositoryPath),
     init: jest.fn(),
     isDetached: jest.fn(),
@@ -493,12 +494,15 @@ async function setup(testName: string): Promise<void> {
       return true
     }),
     tryConfigUnset: jest.fn(),
+    tryConfigUnsetValue: jest.fn(),
     tryDisableAutomaticGarbageCollection: jest.fn(),
     tryGetFetchUrl: jest.fn(async () => {
       // Sanity check - this function shouldn't be called when the .git directory doesn't exist
       await fs.promises.stat(path.join(repositoryPath, '.git'))
       return repositoryUrl
     }),
+    tryGetConfigValues: jest.fn(),
+    tryGetConfigKeys: jest.fn(),
     tryReset: jest.fn(async () => {
       return true
     }),
diff --git __test__/verify-submodules-recursive.sh __test__/verify-submodules-recursive.sh
index 1b68f9b97..5ecbb42d0 100755
--- __test__/verify-submodules-recursive.sh
+++ __test__/verify-submodules-recursive.sh
@@ -17,7 +17,7 @@ fi
 
 echo "Testing persisted credential"
 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
     echo "Failed to validate persisted credential"
     popd
diff --git __test__/verify-submodules-true.sh __test__/verify-submodules-true.sh
index 43769fe06..4c311f846 100755
--- __test__/verify-submodules-true.sh
+++ __test__/verify-submodules-true.sh
@@ -17,7 +17,7 @@ fi
 
 echo "Testing persisted credential"
 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
     echo "Failed to validate persisted credential"
     popd
diff --git dist/index.js dist/index.js
index f3ae6f3ea..a251a1966 100644
--- dist/index.js
+++ dist/index.js
@@ -162,6 +162,7 @@ class GitAuthHelper {
         this.sshKeyPath = '';
         this.sshKnownHostsPath = '';
         this.temporaryHomePath = '';
+        this.credentialsConfigPath = ''; // Path to separate credentials config file in RUNNER_TEMP
         this.git = gitCommandManager;
         this.settings = gitSourceSettings || {};
         // Token auth header
@@ -229,15 +230,17 @@ class GitAuthHelper {
     configureGlobalAuth() {
         return __awaiter(this, void 0, void 0, function* () {
             // 'configureTempGlobalConfig' noops if already set, just returns the path
-            const newGitConfigPath = yield this.configureTempGlobalConfig();
+            yield this.configureTempGlobalConfig();
             try {
                 // Configure the token
-                yield this.configureToken(newGitConfigPath, true);
+                yield this.configureToken(true);
                 // Configure HTTPS instead of SSH
                 yield this.git.tryConfigUnset(this.insteadOfKey, true);
                 if (!this.settings.sshKey) {
                     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() {
         return __awaiter(this, void 0, void 0, function* () {
             // Remove possible previous HTTPS instead of SSH
-            yield this.removeGitConfig(this.insteadOfKey, true);
+            yield this.removeSubmoduleGitConfig(this.insteadOfKey);
             if (this.settings.persistCredentials) {
-                // 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
-                const output = 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
-                `sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`, this.settings.nestedSubmodules);
-                // Replace the placeholder
-                const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [];
+                // Get the credentials config file path in RUNNER_TEMP
+                const credentialsConfigPath = this.getCredentialsConfigPath();
+                // Container credentials config path
+                const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath));
+                // Get submodule config file paths.
+                const configPaths = yield this.git.getSubmoduleConfigPaths(this.settings.nestedSubmodules);
+                // For each submodule, configure includeIf entries pointing to the shared credentials file.
+                // Configure both host and container paths to support Docker container actions.
                 for (const configPath of configPaths) {
-                    core.debug(`Replacing token placeholder in '${configPath}'`);
-                    yield this.replaceTokenPlaceholder(configPath);
+                    // Submodule Git directory
+                    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) {
                     // 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() {
         return __awaiter(this, void 0, void 0, function* () {
             if (!this.settings.sshKey) {
@@ -351,43 +373,88 @@ class GitAuthHelper {
             }
         });
     }
-    configureToken(configPath, globalConfig) {
-        return __awaiter(this, void 0, void 0, function* () {
-            // Validate args
-            assert.ok((configPath && globalConfig) || (!configPath && !globalConfig), 'Unexpected configureToken parameter combinations');
-            // Default config path
-            if (!configPath && !globalConfig) {
-                configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config');
-            }
-            // 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
-            yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, globalConfig);
-            // Replace the placeholder
-            yield this.replaceTokenPlaceholder(configPath || '');
-        });
-    }
-    replaceTokenPlaceholder(configPath) {
+    /**
+     * 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* () {
-            assert.ok(configPath, 'configPath is not defined');
-            let content = (yield fs.promises.readFile(configPath)).toString();
+            // Get the credentials config file path in RUNNER_TEMP
+            const credentialsConfigPath = this.getCredentialsConfigPath();
+            // Write placeholder to the separate credentials config file using git config.
+            // 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
+            yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, false, // globalConfig?
+            false, // add?
+            credentialsConfigPath);
+            // Replace the placeholder in the credentials config file
+            let content = (yield fs.promises.readFile(credentialsConfigPath)).toString();
             const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue);
             if (placeholderIndex < 0 ||
                 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');
             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() {
         return __awaiter(this, void 0, void 0, function* () {
-            var _a;
+            var _a, _b;
             // SSH key
             const keyPath = this.sshKeyPath || stateHelper.SshKeyPath;
             if (keyPath) {
                 try {
+                    core.info(`Removing SSH key '${keyPath}'`);
                     yield io.rmRF(keyPath);
                 }
                 catch (err) {
@@ -399,37 +466,136 @@ class GitAuthHelper {
             const knownHostsPath = this.sshKnownHostsPath || stateHelper.SshKnownHostsPath;
             if (knownHostsPath) {
                 try {
+                    core.info(`Removing SSH known hosts '${knownHostsPath}'`);
                     yield io.rmRF(knownHostsPath);
                 }
-                catch (_b) {
-                    // Intentionally empty
+                catch (err) {
+                    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
+            core.info('Removing SSH command configuration');
             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() {
         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.removeSubmoduleGitConfig(this.tokenConfigKey);
+            // Collect credentials config paths that need to be removed
+            const credentialsPaths = new Set();
+            // Remove includeIf entries that point to git-credentials-*.config files
+            core.info('Removing includeIf entries pointing to credentials config files');
+            const mainCredentialsPaths = yield this.removeIncludeIfCredentials();
+            mainCredentialsPaths.forEach(path => credentialsPaths.add(path));
+            // Remove submodule includeIf entries that point to git-credentials-*.config files
+            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`);
+                }
+            }
         });
     }
-    removeGitConfig(configKey_1) {
-        return __awaiter(this, arguments, void 0, function* (configKey, submoduleOnly = false) {
-            if (!submoduleOnly) {
-                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 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);
             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);
         });
     }
+    /**
+     * 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);
         });
     }
-    config(configKey, configValue, globalConfig, add) {
+    config(configKey, configValue, globalConfig, add, configFile) {
         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) {
                 args.push('--add');
             }
@@ -706,6 +878,16 @@ class GitCommandManager {
             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() {
         return this.workingDirectory;
     }
@@ -836,6 +1018,20 @@ class GitCommandManager {
             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() {
         return __awaiter(this, void 0, void 0, function* () {
             const output = yield this.execGit(['config', '--local', 'gc.auto', '0'], true);
@@ -855,6 +1051,46 @@ class GitCommandManager {
             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() {
         return __awaiter(this, void 0, void 0, function* () {
             const output = yield this.execGit(['reset', '--hard', 'HEAD'], true);
diff --git src/git-auth-helper.ts src/git-auth-helper.ts
index 126e8e5ee..a1950a60c 100644
--- src/git-auth-helper.ts
+++ src/git-auth-helper.ts
@@ -43,6 +43,7 @@ class GitAuthHelper {
   private sshKeyPath = ''
   private sshKnownHostsPath = ''
   private temporaryHomePath = ''
+  private credentialsConfigPath = '' // Path to separate credentials config file in RUNNER_TEMP
 
   constructor(
     gitCommandManager: IGitCommandManager,
@@ -126,16 +127,21 @@ class GitAuthHelper {
 
   async configureGlobalAuth(): Promise<void> {
     // 'configureTempGlobalConfig' noops if already set, just returns the path
-    const newGitConfigPath = await this.configureTempGlobalConfig()
+    await this.configureTempGlobalConfig()
     try {
       // Configure the token
-      await this.configureToken(newGitConfigPath, true)
+      await this.configureToken(true)
 
       // Configure HTTPS instead of SSH
       await this.git.tryConfigUnset(this.insteadOfKey, true)
       if (!this.settings.sshKey) {
         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) {
@@ -150,24 +156,60 @@ class GitAuthHelper {
 
   async configureSubmoduleAuth(): Promise<void> {
     // Remove possible previous HTTPS instead of SSH
-    await this.removeGitConfig(this.insteadOfKey, true)
+    await this.removeSubmoduleGitConfig(this.insteadOfKey)
 
     if (this.settings.persistCredentials) {
-      // 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
-      const output = 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
-        `sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`,
+      // Get the credentials config file path in RUNNER_TEMP
+      const credentialsConfigPath = this.getCredentialsConfigPath()
+
+      // Container credentials config path
+      const containerCredentialsPath = path.posix.join(
+        '/github/runner_temp',
+        path.basename(credentialsConfigPath)
+      )
+
+      // Get submodule config file paths.
+      const configPaths = await this.git.getSubmoduleConfigPaths(
         this.settings.nestedSubmodules
       )
 
-      // Replace the placeholder
-      const configPaths: string[] =
-        output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []
+      // For each submodule, configure includeIf entries pointing to the shared credentials file.
+      // Configure both host and container paths to support Docker container actions.
       for (const configPath of configPaths) {
-        core.debug(`Replacing token placeholder in '${configPath}'`)
-        await this.replaceTokenPlaceholder(configPath)
+        // Submodule Git directory
+        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) {
@@ -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> {
     if (!this.settings.sshKey) {
       return
@@ -272,57 +318,116 @@ class GitAuthHelper {
     }
   }
 
-  private async configureToken(
-    configPath?: string,
-    globalConfig?: boolean
-  ): Promise<void> {
-    // Validate args
-    assert.ok(
-      (configPath && globalConfig) || (!configPath && !globalConfig),
-      'Unexpected configureToken parameter combinations'
-    )
-
-    // Default config path
-    if (!configPath && !globalConfig) {
-      configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config')
-    }
-
-    // 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
+  /**
+   * 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
+   */
+  private async configureToken(globalConfig?: boolean): Promise<void> {
+    // Get the credentials config file path in RUNNER_TEMP
+    const credentialsConfigPath = this.getCredentialsConfigPath()
+
+    // Write placeholder to the separate credentials config file using git config.
+    // 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(
       this.tokenConfigKey,
       this.tokenPlaceholderConfigValue,
-      globalConfig
+      false, // globalConfig?
+      false, // add?
+      credentialsConfigPath
     )
 
-    // Replace the placeholder
-    await this.replaceTokenPlaceholder(configPath || '')
-  }
-
-  private async replaceTokenPlaceholder(configPath: string): Promise<void> {
-    assert.ok(configPath, 'configPath is not defined')
-    let content = (await fs.promises.readFile(configPath)).toString()
+    // Replace the placeholder in the credentials config file
+    let content = (await fs.promises.readFile(credentialsConfigPath)).toString()
     const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue)
     if (
       placeholderIndex < 0 ||
       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')
     content = content.replace(
       this.tokenPlaceholderConfigValue,
       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> {
     // SSH key
     const keyPath = this.sshKeyPath || stateHelper.SshKeyPath
     if (keyPath) {
       try {
+        core.info(`Removing SSH key '${keyPath}'`)
         await io.rmRF(keyPath)
       } catch (err) {
         core.debug(`${(err as any)?.message ?? err}`)
@@ -335,40 +440,149 @@ class GitAuthHelper {
       this.sshKnownHostsPath || stateHelper.SshKnownHostsPath
     if (knownHostsPath) {
       try {
+        core.info(`Removing SSH known hosts '${knownHostsPath}'`)
         await io.rmRF(knownHostsPath)
-      } catch {
-        // Intentionally empty
+      } catch (err) {
+        core.debug(`${(err as any)?.message ?? err}`)
+        core.warning(`Failed to remove SSH known hosts '${knownHostsPath}'`)
       }
     }
 
     // SSH command
+    core.info('Removing SSH command configuration')
     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> {
-    // HTTP extra header
+    // Remove HTTP extra header
+    core.info('Removing HTTP extra header')
     await this.removeGitConfig(this.tokenConfigKey)
-  }
+    await this.removeSubmoduleGitConfig(this.tokenConfigKey)
+
+    // Collect credentials config paths that need to be removed
+    const credentialsPaths = new Set<string>()
+
+    // Remove includeIf entries that point to git-credentials-*.config files
+    core.info('Removing includeIf entries pointing to credentials config files')
+    const mainCredentialsPaths = await this.removeIncludeIfCredentials()
+    mainCredentialsPaths.forEach(path => credentialsPaths.add(path))
+
+    // Remove submodule includeIf entries that point to git-credentials-*.config files
+    const submoduleConfigPaths = await this.git.getSubmoduleConfigPaths(true)
+    for (const configPath of submoduleConfigPaths) {
+      const submoduleCredentialsPaths =
+        await this.removeIncludeIfCredentials(configPath)
+      submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path))
+    }
 
-  private async removeGitConfig(
-    configKey: string,
-    submoduleOnly: boolean = false
-  ): Promise<void> {
-    if (!submoduleOnly) {
-      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`)
+    // 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)
     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}' || :"`,
       true
     )
   }
+
+  /**
+   * Removes includeIf entries t,hat 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)
+  }
 }
diff --git src/git-command-manager.ts src/git-command-manager.ts
index 8e42a387f..a45e15a86 100644
--- src/git-command-manager.ts
+++ src/git-command-manager.ts
@@ -28,7 +28,8 @@ export interface IGitCommandManager {
     configKey: string,
     configValue: string,
     globalConfig?: boolean,
-    add?: boolean
+    add?: boolean,
+    configFile?: string
   ): Promise<void>
   configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
   fetch(
@@ -41,6 +42,7 @@ export interface IGitCommandManager {
     }
   ): Promise<void>
   getDefaultBranch(repositoryUrl: string): Promise<string>
+  getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
   getWorkingDirectory(): string
   init(): Promise<void>
   isDetached(): Promise<boolean>
@@ -59,8 +61,24 @@ export interface IGitCommandManager {
   tagExists(pattern: string): Promise<boolean>
   tryClean(): Promise<boolean>
   tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean>
+  tryConfigUnsetValue(
+    configKey: string,
+    configValue: string,
+    globalConfig?: boolean,
+    configFile?: string
+  ): Promise<boolean>
   tryDisableAutomaticGarbageCollection(): Promise<boolean>
   tryGetFetchUrl(): Promise<string>
+  tryGetConfigValues(
+    configKey: string,
+    globalConfig?: boolean,
+    configFile?: string
+  ): Promise<string[]>
+  tryGetConfigKeys(
+    pattern: string,
+    globalConfig?: boolean,
+    configFile?: string
+  ): Promise<string[]>
   tryReset(): Promise<boolean>
   version(): Promise<GitVersion>
 }
@@ -223,9 +241,15 @@ class GitCommandManager {
     configKey: string,
     configValue: string,
     globalConfig?: boolean,
-    add?: boolean
+    add?: boolean,
+    configFile?: string
   ): 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) {
       args.push('--add')
     }
@@ -323,6 +347,21 @@ class GitCommandManager {
     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 {
     return this.workingDirectory
   }
@@ -455,6 +494,24 @@ class GitCommandManager {
     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> {
     const output = await this.execGit(
       ['config', '--local', 'gc.auto', '0'],
@@ -481,6 +538,56 @@ class GitCommandManager {
     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> {
     const output = await this.execGit(['reset', '--hard', 'HEAD'], true)
     return output.exitCode === 0

Description

This PR implements a major security enhancement for the checkout action (v6.0.0) by changing how Git credentials are persisted. Instead of storing credentials directly in the local .git/config file, credentials are now stored in a separate config file under $RUNNER_TEMP with a unique UUID filename (git-credentials-<uuid>.config). The main Git config then uses includeIf directives to conditionally include these credentials based on the Git directory path. This change supports both host and Docker container scenarios by configuring separate includeIf entries for each environment.

Possible Issues

  1. Minimum Runner Version Requirement: The changes require Actions Runner v2.329.0 for Docker container scenarios. Workflows running on older runners may fail, but this is documented in the README.

  2. Test Mock Complexity: The test suite adds several new mocked Git methods (tryConfigUnsetValue, tryGetConfigValues, tryGetConfigKeys, getSubmoduleConfigPaths) which increases test maintenance burden.

  3. Backwards Compatibility: The cleanup logic attempts to remove old-style credentials, but there's no explicit migration path documented for workflows that might have mixed v5 and v6 checkouts.

  4. Path Handling: The code relies on GITHUB_WORKSPACE environment variable being set correctly, which could fail in non-standard GitHub Actions environments.

Security Hotspots

  1. Credentials File Cleanup (Medium Risk): The removeToken() method only removes credentials files that start with runnerTemp. If RUNNER_TEMP is compromised or misconfigured, credentials files might not be properly cleaned up:

    if (credentialsPath.startsWith(runnerTemp)) {
      await io.rmRF(credentialsPath)
    }
  2. Regex Pattern Matching (Low Risk): The testCredentialsConfigPath() uses a regex that could potentially match unintended files:

    return /git-credentials-[0-9a-f-]+\.config$/i.test(path)

    The case-insensitive flag might allow matching .CONFIG files that weren't created by this action.

  3. Error Handling in Cleanup (Low Risk): The cleanup methods use broad try-catch blocks that could silently fail, potentially leaving credentials on disk:

    } catch (err) {
      core.debug(`${(err as any)?.message ?? err}`)
      core.warning(`Failed to remove credentials config '${credentialsPath}'`)
    }

Privacy Hotspots

  1. Credentials File Naming (Low Risk): While credentials are stored with UUID filenames, the pattern git-credentials-*.config makes them easily identifiable in the temp directory if an attacker gains filesystem access.

  2. Debug Logging: The code logs the credentials config path in debug mode:

    core.debug(`Credentials config path: ${this.credentialsConfigPath}`)

    While this doesn't expose credentials directly, it reveals the exact location where sensitive data is stored.

Changes

Changes

.github/workflows/test.yml

  • Updated test workflow to use explicit path parameter for checkout actions
  • Removed workaround for cleanup issues (no longer needed with new credentials approach)

CHANGELOG.md & README.md

  • Added v6.0.0 release notes documenting the credentials storage change
  • Added v5.0.1 and v4.3.1 backport notes
  • Updated documentation to note minimum runner version requirements

__test__/git-auth-helper.test.ts

  • Refactored tests to verify credentials are stored in separate config files
  • Added tests for includeIf entries in both host and container paths
  • Added comprehensive cleanup tests for both main repo and submodules
  • Added test for testCredentialsConfigPath pattern matching
  • Updated mock implementations to support new Git command methods
  • Fixed test name typo (configureAuth_AcceptsGitHubServerUrlSetToGHEC)

__test__/git-directory-helper.test.ts

  • Added mocks for new Git methods to prevent test failures

__test__/verify-submodules-*.sh

  • Updated to use --includes flag when checking for credentials in submodules

dist/index.js & src/git-auth-helper.ts

  • Added credentialsConfigPath field to track the separate credentials file
  • Implemented getCredentialsConfigPath() to generate unique credentials file paths using UUID
  • Refactored configureToken() to write credentials to separate file and set up includeIf entries
  • Added testCredentialsConfigPath() to validate credentials file patterns
  • Implemented comprehensive cleanup in removeToken() and removeAuth() including submodule cleanup
  • Added removeIncludeIfCredentials() to remove includeIf entries pointing to credentials files
  • Split removeGitConfig() into separate methods for main repo and submodules
  • Updated configureSubmoduleAuth() to use includeIf approach instead of direct credential writes
  • Enhanced cleanup methods with better logging and error handling

src/git-command-manager.ts

  • Added optional configFile parameter to config() method
  • Implemented tryConfigUnsetValue() to remove specific config key-value pairs
  • Implemented tryGetConfigValues() to retrieve all values for a config key
  • Implemented tryGetConfigKeys() to retrieve config keys matching a pattern
  • Implemented getSubmoduleConfigPaths() to get list of submodule config file paths
sequenceDiagram
    participant Workflow
    participant GitAuthHelper
    participant GitCommandManager
    participant FileSystem
    participant GitConfig

    Workflow->>GitAuthHelper: configureAuth()
    GitAuthHelper->>GitAuthHelper: getCredentialsConfigPath()
    GitAuthHelper->>FileSystem: Generate UUID filename
    FileSystem-->>GitAuthHelper: git-credentials-<uuid>.config path
    
    GitAuthHelper->>GitCommandManager: config(tokenKey, placeholder, configFile)
    GitCommandManager->>FileSystem: Write placeholder to credentials file
    
    GitAuthHelper->>FileSystem: Read credentials file
    GitAuthHelper->>FileSystem: Replace placeholder with actual token
    
    GitAuthHelper->>GitCommandManager: config(includeIf.gitdir:<host-path>.path, credentials-path)
    GitCommandManager->>GitConfig: Add includeIf for host
    
    GitAuthHelper->>GitCommandManager: config(includeIf.gitdir:<container-path>.path, credentials-path)
    GitCommandManager->>GitConfig: Add includeIf for container
    
    Note over GitAuthHelper,GitConfig: For submodules
    GitAuthHelper->>GitCommandManager: getSubmoduleConfigPaths()
    GitCommandManager-->>GitAuthHelper: List of submodule configs
    
    loop For each submodule
        GitAuthHelper->>GitCommandManager: config(includeIf..., credentials-path, submodule-config)
        GitCommandManager->>FileSystem: Add includeIf to submodule config
    end
    
    Note over Workflow,GitConfig: Cleanup phase
    Workflow->>GitAuthHelper: removeAuth()
    GitAuthHelper->>GitAuthHelper: removeIncludeIfCredentials()
    GitAuthHelper->>GitCommandManager: tryGetConfigKeys(includeIf.gitdir)
    GitCommandManager-->>GitAuthHelper: List of includeIf keys
    
    loop For each includeIf key
        GitAuthHelper->>GitCommandManager: tryGetConfigValues(key)
        GitCommandManager-->>GitAuthHelper: Credentials file paths
        GitAuthHelper->>GitCommandManager: tryConfigUnsetValue(key, value)
    end
    
    loop For each credentials file
        GitAuthHelper->>FileSystem: Delete credentials file
    end
Loading

@github-actions
Copy link
Contributor

github-actions bot commented Dec 4, 2025

👋 Thanks for Submitting! This PR is available for preview at the link below.

✅ PR tip preview: https://1273.pr.nala.bravesoftware.com/
✅ Commit preview: https://1273.pr.nala.bravesoftware.com/commit-32eef6f1429ce5e0427ed61239ad692c1046007f/

- ./tokens/css/variables-android.old.css: 7390 bytes
+ ./tokens/css/variables-android.css: 7390 bytes
---
- ./tokens/css/variables-browser.old.css: 6644 bytes
+ ./tokens/css/variables-browser.css: 6644 bytes
---
- ./tokens/css/variables-ios.old.css: 8180 bytes
+ ./tokens/css/variables-ios.css: 8180 bytes
---
- ./tokens/css/variables-marketing.old.css: 13501 bytes
+ ./tokens/css/variables-marketing.css: 13501 bytes
---
- ./tokens/css/variables-news.old.css: 526 bytes
+ ./tokens/css/variables-news.css: 526 bytes
---
- ./tokens/css/variables-newtab.old.css: 1933 bytes
+ ./tokens/css/variables-newtab.css: 1933 bytes
---
- ./tokens/css/variables-search.old.css: 17733 bytes
+ ./tokens/css/variables-search.css: 17733 bytes
---
- ./tokens/css/variables-web3.old.css: 893 bytes
+ ./tokens/css/variables-web3.css: 893 bytes
---
- ./tokens/css/variables.old.css: 125593 bytes
+ ./tokens/css/variables.css: 125593 bytes
Variables Diff: variables-android.diff
--- ./tokens/css/variables-android.old.css	2025-12-04 14:54:02.545516911 +0000
+++ ./tokens/css/variables-android.css	2025-12-04 14:53:30.193514340 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-browser.diff
--- ./tokens/css/variables-browser.old.css	2025-12-04 14:54:02.819516868 +0000
+++ ./tokens/css/variables-browser.css	2025-12-04 14:53:30.178514376 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-ios.diff
--- ./tokens/css/variables-ios.old.css	2025-12-04 14:54:03.090516822 +0000
+++ ./tokens/css/variables-ios.css	2025-12-04 14:53:30.208514305 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-marketing.diff
--- ./tokens/css/variables-marketing.old.css	2025-12-04 14:54:03.359516777 +0000
+++ ./tokens/css/variables-marketing.css	2025-12-04 14:53:30.234514243 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-news.diff
--- ./tokens/css/variables-news.old.css	2025-12-04 14:54:03.491516754 +0000
+++ ./tokens/css/variables-news.css	2025-12-04 14:53:30.272514152 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-newtab.diff
--- ./tokens/css/variables-newtab.old.css	2025-12-04 14:54:03.774516709 +0000
+++ ./tokens/css/variables-newtab.css	2025-12-04 14:53:30.286514119 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-search.diff
--- ./tokens/css/variables-search.old.css	2025-12-04 14:54:04.049516663 +0000
+++ ./tokens/css/variables-search.css	2025-12-04 14:53:30.256514191 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-web3.diff
--- ./tokens/css/variables-web3.old.css	2025-12-04 14:54:04.325516620 +0000
+++ ./tokens/css/variables-web3.css	2025-12-04 14:53:30.292514105 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 @media (prefers-color-scheme: light) {
Variables Diff: variables.diff
--- ./tokens/css/variables.old.css	2025-12-04 14:54:04.734516527 +0000
+++ ./tokens/css/variables.css	2025-12-04 14:53:30.065514644 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 09:32:04 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 14:53:30 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {

@renovate renovate bot force-pushed the renovate/actions-checkout-6-x branch from 32eef6f to 557d367 Compare December 4, 2025 18:12
@github-actions
Copy link
Contributor

github-actions bot commented Dec 4, 2025

👋 Thanks for Submitting! This PR is available for preview at the link below.

✅ PR tip preview: https://1273.pr.nala.bravesoftware.com/
✅ Commit preview: https://1273.pr.nala.bravesoftware.com/commit-557d3678a73b9af73bbf10e0e42a6378046ea7e5/

- ./tokens/css/variables-android.old.css: 7390 bytes
+ ./tokens/css/variables-android.css: 7390 bytes
---
- ./tokens/css/variables-browser.old.css: 6644 bytes
+ ./tokens/css/variables-browser.css: 6644 bytes
---
- ./tokens/css/variables-ios.old.css: 8180 bytes
+ ./tokens/css/variables-ios.css: 8180 bytes
---
- ./tokens/css/variables-marketing.old.css: 13501 bytes
+ ./tokens/css/variables-marketing.css: 13501 bytes
---
- ./tokens/css/variables-news.old.css: 526 bytes
+ ./tokens/css/variables-news.css: 526 bytes
---
- ./tokens/css/variables-newtab.old.css: 1933 bytes
+ ./tokens/css/variables-newtab.css: 1933 bytes
---
- ./tokens/css/variables-search.old.css: 17733 bytes
+ ./tokens/css/variables-search.css: 17733 bytes
---
- ./tokens/css/variables-web3.old.css: 893 bytes
+ ./tokens/css/variables-web3.css: 893 bytes
---
- ./tokens/css/variables.old.css: 125593 bytes
+ ./tokens/css/variables.css: 125593 bytes
Variables Diff: variables-android.diff
--- ./tokens/css/variables-android.old.css	2025-12-04 18:13:35.425343108 +0000
+++ ./tokens/css/variables-android.css	2025-12-04 18:13:01.999595750 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:58 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:02 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-browser.diff
--- ./tokens/css/variables-browser.old.css	2025-12-04 18:13:35.603341756 +0000
+++ ./tokens/css/variables-browser.css	2025-12-04 18:13:01.984595860 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:58 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:01 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-ios.diff
--- ./tokens/css/variables-ios.old.css	2025-12-04 18:13:35.767340511 +0000
+++ ./tokens/css/variables-ios.css	2025-12-04 18:13:02.015595631 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:58 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:02 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-marketing.diff
--- ./tokens/css/variables-marketing.old.css	2025-12-04 18:13:35.938339211 +0000
+++ ./tokens/css/variables-marketing.css	2025-12-04 18:13:02.040595447 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:58 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:02 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-news.diff
--- ./tokens/css/variables-news.old.css	2025-12-04 18:13:36.121337821 +0000
+++ ./tokens/css/variables-news.css	2025-12-04 18:13:02.083595129 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:58 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:02 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-newtab.diff
--- ./tokens/css/variables-newtab.old.css	2025-12-04 18:13:36.286336570 +0000
+++ ./tokens/css/variables-newtab.css	2025-12-04 18:13:02.091595070 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:58 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:02 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-search.diff
--- ./tokens/css/variables-search.old.css	2025-12-04 18:13:36.492335004 +0000
+++ ./tokens/css/variables-search.css	2025-12-04 18:13:02.064595270 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:58 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:02 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {
Variables Diff: variables-web3.diff
--- ./tokens/css/variables-web3.old.css	2025-12-04 18:13:36.638333896 +0000
+++ ./tokens/css/variables-web3.css	2025-12-04 18:13:02.096595033 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:58 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:02 GMT+0000 (Coordinated Universal Time)
  */
 
 @media (prefers-color-scheme: light) {
Variables Diff: variables.diff
--- ./tokens/css/variables.old.css	2025-12-04 18:13:36.831332431 +0000
+++ ./tokens/css/variables.css	2025-12-04 18:13:01.859596783 +0000
@@ -1,6 +1,6 @@
 /**
  * Do not edit directly
- * Generated on Thu Dec 04 2025 14:54:57 GMT+0000 (Coordinated Universal Time)
+ * Generated on Thu Dec 04 2025 18:13:01 GMT+0000 (Coordinated Universal Time)
  */
 
 :root {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file puLL-Merge renovate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant