mirror of
https://github.com/actions/checkout.git
synced 2026-08-10 03:14:49 +07:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ff9e6206e | |||
| f548e57e54 | |||
| 3d3c42e5aa | |||
| 28802689a1 | |||
| 12cd2235ef | |||
| 62661c4e71 | |||
| e8d4307400 | |||
| 631c942040 | |||
| 4f1f4aec02 | |||
| ba097532fb | |||
| b9e0990d21 | |||
| e8cb398be4 | |||
| 5de26ee9b1 | |||
| 79102f2503 | |||
| 9c091bb21b | |||
| 1044a6dea9 | |||
| f0282184c7 | |||
| d914b262ff | |||
| 537c7ef99c | |||
| 130a169078 | |||
| 7d09575332 | |||
| 0f9f3aa320 | |||
| f9e715a95f | |||
| df4cb1c069 | |||
| 1cce3390c2 | |||
| 900f2210b1 | |||
| 0c366fd6a8 | |||
| de0fac2e45 | |||
| 064fe7f331 | |||
| 8e8c483db8 | |||
| 033fa0dc0b | |||
| c2d88d3ecc | |||
| 1af3b93b68 | |||
| 71cf2267d8 | |||
| 069c695914 | |||
| ff7abcd0c3 | |||
| 08c6903cd8 | |||
| 9f265659d3 |
@@ -1,3 +0,0 @@
|
|||||||
dist/
|
|
||||||
lib/
|
|
||||||
node_modules/
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"plugins": ["jest", "@typescript-eslint"],
|
|
||||||
"extends": ["plugin:github/recommended"],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 9,
|
|
||||||
"sourceType": "module",
|
|
||||||
"project": "./tsconfig.json"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"eslint-comments/no-use": "off",
|
|
||||||
"import/no-namespace": "off",
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": "error",
|
|
||||||
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
|
|
||||||
"@typescript-eslint/no-require-imports": "error",
|
|
||||||
"@typescript-eslint/array-type": "error",
|
|
||||||
"@typescript-eslint/await-thenable": "error",
|
|
||||||
"camelcase": "off",
|
|
||||||
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
|
|
||||||
"@typescript-eslint/func-call-spacing": ["error", "never"],
|
|
||||||
"@typescript-eslint/no-array-constructor": "error",
|
|
||||||
"@typescript-eslint/no-empty-interface": "error",
|
|
||||||
"@typescript-eslint/no-explicit-any": "error",
|
|
||||||
"@typescript-eslint/no-extraneous-class": "error",
|
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
|
||||||
"@typescript-eslint/no-for-in-array": "error",
|
|
||||||
"@typescript-eslint/no-inferrable-types": "error",
|
|
||||||
"@typescript-eslint/no-misused-new": "error",
|
|
||||||
"@typescript-eslint/no-namespace": "error",
|
|
||||||
"@typescript-eslint/no-non-null-assertion": "warn",
|
|
||||||
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
|
||||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
|
||||||
"@typescript-eslint/no-useless-constructor": "error",
|
|
||||||
"@typescript-eslint/no-var-requires": "error",
|
|
||||||
"@typescript-eslint/prefer-for-of": "warn",
|
|
||||||
"@typescript-eslint/prefer-function-type": "warn",
|
|
||||||
"@typescript-eslint/prefer-includes": "error",
|
|
||||||
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
|
||||||
"@typescript-eslint/promise-function-async": "error",
|
|
||||||
"@typescript-eslint/require-array-sort-compare": "error",
|
|
||||||
"@typescript-eslint/restrict-plus-operands": "error",
|
|
||||||
"semi": "off",
|
|
||||||
"@typescript-eslint/semi": ["error", "never"],
|
|
||||||
"@typescript-eslint/type-annotation-spacing": "error",
|
|
||||||
"@typescript-eslint/unbound-method": "error"
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"es6": true,
|
|
||||||
"jest/globals": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,12 +22,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Set Node.js 20.x
|
- name: Set Node.js 24.x
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 24.x
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# If dist/ was different than expected, upload the expected version as an artifact
|
# If dist/ was different than expected, upload the expected version as an artifact
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v7
|
||||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v4
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -55,4 +55,4 @@ jobs:
|
|||||||
- run: rm -rf dist # We want code scanning to analyze lib instead (individual .js files)
|
- run: rm -rf dist # We want code scanning to analyze lib instead (individual .js files)
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v4
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Check licenses
|
name: Check licenses
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v7
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run licensed-check
|
- run: npm run licensed-check
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checking out
|
- name: Checking out
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
- name: Publish
|
- name: Publish
|
||||||
id: publish
|
id: publish
|
||||||
uses: actions/publish-immutable-action@0.0.3
|
uses: actions/publish-immutable-action@v0.0.4
|
||||||
|
|||||||
+41
-15
@@ -16,10 +16,10 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 24.x
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v7
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run format-check
|
- run: npm run format-check
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
# Basic checkout
|
# Basic checkout
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
@@ -87,6 +87,17 @@ jobs:
|
|||||||
- name: Verify fetch filter
|
- name: Verify fetch filter
|
||||||
run: __test__/verify-fetch-filter.sh
|
run: __test__/verify-fetch-filter.sh
|
||||||
|
|
||||||
|
# Fetch tags
|
||||||
|
- name: Checkout with fetch-tags
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
ref: test-data/v2/basic
|
||||||
|
path: fetch-tags-test
|
||||||
|
fetch-tags: true
|
||||||
|
- name: Verify fetch-tags
|
||||||
|
shell: bash
|
||||||
|
run: __test__/verify-fetch-tags.sh
|
||||||
|
|
||||||
# Sparse checkout
|
# Sparse checkout
|
||||||
- name: Sparse checkout
|
- name: Sparse checkout
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -165,6 +176,22 @@ jobs:
|
|||||||
- name: Verify submodules recursive
|
- name: Verify submodules recursive
|
||||||
run: __test__/verify-submodules-recursive.sh
|
run: __test__/verify-submodules-recursive.sh
|
||||||
|
|
||||||
|
# Worktree credentials
|
||||||
|
- name: Checkout for worktree test
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
path: worktree-test
|
||||||
|
- name: Verify worktree credentials
|
||||||
|
shell: bash
|
||||||
|
run: __test__/verify-worktree.sh worktree-test worktree-branch
|
||||||
|
|
||||||
|
# Worktree credentials in container step
|
||||||
|
- name: Verify worktree credentials in container step
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
uses: docker://bitnami/git:latest
|
||||||
|
with:
|
||||||
|
args: bash __test__/verify-worktree.sh worktree-test container-worktree-branch
|
||||||
|
|
||||||
# Basic checkout using REST API
|
# Basic checkout using REST API
|
||||||
- name: Remove basic
|
- name: Remove basic
|
||||||
if: runner.os != 'windows'
|
if: runner.os != 'windows'
|
||||||
@@ -202,7 +229,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
# Basic checkout using git
|
# Basic checkout using git
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
@@ -234,7 +261,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
# Basic checkout using git
|
# Basic checkout using git
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
@@ -264,7 +291,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
path: localClone
|
path: localClone
|
||||||
|
|
||||||
@@ -291,8 +318,8 @@ jobs:
|
|||||||
git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main
|
git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main
|
||||||
|
|
||||||
# needed to make checkout post cleanup succeed
|
# needed to make checkout post cleanup succeed
|
||||||
- name: Fix Checkout v4
|
- name: Fix Checkout v6
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
path: localClone
|
path: localClone
|
||||||
|
|
||||||
@@ -301,13 +328,16 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
path: actions-checkout
|
||||||
|
|
||||||
# Basic checkout using git
|
# Basic checkout using git
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
id: checkout
|
id: checkout
|
||||||
uses: ./
|
uses: ./actions-checkout
|
||||||
with:
|
with:
|
||||||
|
path: cloned-using-local-action
|
||||||
ref: test-data/v2/basic
|
ref: test-data/v2/basic
|
||||||
|
|
||||||
# Verify output
|
# Verify output
|
||||||
@@ -325,7 +355,3 @@ jobs:
|
|||||||
echo "Expected commit to be 82f71901cf8c021332310dcc8cdba84c4193ff5d"
|
echo "Expected commit to be 82f71901cf8c021332310dcc8cdba84c4193ff5d"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# needed to make checkout post cleanup succeed
|
|
||||||
- name: Fix Checkout
|
|
||||||
uses: actions/checkout@v4.1.6
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ on:
|
|||||||
type: choice
|
type: choice
|
||||||
description: The major version to update
|
description: The major version to update
|
||||||
options:
|
options:
|
||||||
|
- v5
|
||||||
- v4
|
- v4
|
||||||
- v3
|
- v3
|
||||||
- v2
|
- v2
|
||||||
@@ -22,7 +23,7 @@ jobs:
|
|||||||
# Note this update workflow can also be used as a rollback tool.
|
# Note this update workflow can also be used as a rollback tool.
|
||||||
# For that reason, it's best to pin `actions/checkout` to a known, stable version
|
# For that reason, it's best to pin `actions/checkout` to a known, stable version
|
||||||
# (typically, about two releases back).
|
# (typically, about two releases back).
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Git config
|
- name: Git config
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
# Use `docker/login-action` to log in to GHCR.io.
|
# Use `docker/login-action` to log in to GHCR.io.
|
||||||
# Once published, the packages are scoped to the account defined here.
|
# Once published, the packages are scoped to the account defined here.
|
||||||
- name: Log in to the ghcr.io container registry
|
- name: Log in to the ghcr.io container registry
|
||||||
uses: docker/login-action@v3.3.0
|
uses: docker/login-action@v4.4.0
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
|
|
||||||
# Use `docker/build-push-action` to build (and optionally publish) the image.
|
# Use `docker/build-push-action` to build (and optionally publish) the image.
|
||||||
- name: Build Docker Image (with optional Push)
|
- name: Build Docker Image (with optional Push)
|
||||||
uses: docker/build-push-action@v6.5.0
|
uses: docker/build-push-action@v7.3.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: images/test-ubuntu-git.Dockerfile
|
file: images/test-ubuntu-git.Dockerfile
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ allowed:
|
|||||||
|
|
||||||
reviewed:
|
reviewed:
|
||||||
npm:
|
npm:
|
||||||
|
- "@actions/http-client" # MIT
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/core"
|
name: "@actions/core"
|
||||||
version: 1.10.1
|
version: 3.0.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions core lib
|
summary: Actions core lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/core
|
homepage: https://github.com/actions/toolkit/tree/main/packages/core
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/exec"
|
name: "@actions/exec"
|
||||||
version: 1.1.1
|
version: 3.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions exec lib
|
summary: Actions exec lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/exec
|
homepage: https://github.com/actions/toolkit/tree/main/packages/exec
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/github"
|
name: "@actions/github"
|
||||||
version: 6.0.0
|
version: 9.1.1
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions github lib
|
summary: Actions github lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/github
|
homepage: https://github.com/actions/toolkit/tree/main/packages/github
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/http-client"
|
name: "@actions/http-client"
|
||||||
version: 2.2.1
|
version: 3.0.2
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions Http Client
|
summary: Actions Http Client
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
|
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
|
||||||
license: mit
|
license: other
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
text: |
|
text: |
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: "@actions/http-client"
|
||||||
|
version: 4.0.1
|
||||||
|
type: npm
|
||||||
|
summary: Actions Http Client
|
||||||
|
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
|
||||||
|
license: other
|
||||||
|
licenses:
|
||||||
|
- sources: LICENSE
|
||||||
|
text: |
|
||||||
|
Actions Http Client for Node.js
|
||||||
|
|
||||||
|
Copyright (c) GitHub, Inc.
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||||
|
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
|
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||||
|
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||||
|
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
notices: []
|
||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/io"
|
name: "@actions/io"
|
||||||
version: 1.1.3
|
version: 3.0.2
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions io lib
|
summary: Actions io lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/io
|
homepage: https://github.com/actions/toolkit/tree/main/packages/io
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/tool-cache"
|
name: "@actions/tool-cache"
|
||||||
version: 2.0.1
|
version: 4.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions tool-cache lib
|
summary: Actions tool-cache lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/tool-cache
|
homepage: https://github.com/actions/toolkit/tree/main/packages/tool-cache
|
||||||
|
|||||||
Generated
-30
@@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
name: "@fastify/busboy"
|
|
||||||
version: 2.1.1
|
|
||||||
type: npm
|
|
||||||
summary: A streaming parser for HTML form data for node.js
|
|
||||||
homepage:
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |-
|
|
||||||
Copyright Brian White. All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to
|
|
||||||
deal in the Software without restriction, including without limitation the
|
|
||||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
IN THE SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/auth-token"
|
name: "@octokit/auth-token"
|
||||||
version: 4.0.0
|
version: 6.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: GitHub API token authentication for browsers and Node.js
|
summary: GitHub API token authentication for browsers and Node.js
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/core"
|
name: "@octokit/core"
|
||||||
version: 5.2.0
|
version: 7.0.6
|
||||||
type: npm
|
type: npm
|
||||||
summary: Extendable client for GitHub's REST & GraphQL APIs
|
summary: Extendable client for GitHub's REST & GraphQL APIs
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/endpoint"
|
name: "@octokit/endpoint"
|
||||||
version: 9.0.6
|
version: 11.0.3
|
||||||
type: npm
|
type: npm
|
||||||
summary: Turns REST API endpoints into generic request options
|
summary: Turns REST API endpoints into generic request options
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/graphql"
|
name: "@octokit/graphql"
|
||||||
version: 7.1.0
|
version: 9.0.3
|
||||||
type: npm
|
type: npm
|
||||||
summary: GitHub GraphQL API client for browsers and Node
|
summary: GitHub GraphQL API client for browsers and Node
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: "@octokit/openapi-types"
|
|
||||||
version: 22.1.0
|
|
||||||
type: npm
|
|
||||||
summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com
|
|
||||||
homepage:
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |-
|
|
||||||
Copyright 2020 Gregor Martynus
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
- sources: README.md
|
|
||||||
text: "[MIT](LICENSE)"
|
|
||||||
notices: []
|
|
||||||
.licenses/npm/@octokit/openapi-types-20.0.0.dep.yml → .licenses/npm/@octokit/openapi-types.dep.yml
Generated
+3
-3
@@ -1,14 +1,14 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/openapi-types"
|
name: "@octokit/openapi-types"
|
||||||
version: 20.0.0
|
version: 27.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com
|
summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com
|
||||||
homepage:
|
homepage:
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
text: |-
|
text: |
|
||||||
Copyright 2020 Gregor Martynus
|
Copyright (c) GitHub 2025 - Licensed as MIT.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/plugin-paginate-rest"
|
name: "@octokit/plugin-paginate-rest"
|
||||||
version: 9.2.2
|
version: 14.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Octokit plugin to paginate REST API endpoint responses
|
summary: Octokit plugin to paginate REST API endpoint responses
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/plugin-rest-endpoint-methods"
|
name: "@octokit/plugin-rest-endpoint-methods"
|
||||||
version: 10.4.1
|
version: 17.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Octokit plugin adding one method for all of api.github.com REST API endpoints
|
summary: Octokit plugin adding one method for all of api.github.com REST API endpoints
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/request-error"
|
name: "@octokit/request-error"
|
||||||
version: 5.1.1
|
version: 7.1.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Error class for Octokit request errors
|
summary: Error class for Octokit request errors
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/request"
|
name: "@octokit/request"
|
||||||
version: 8.4.1
|
version: 10.0.10
|
||||||
type: npm
|
type: npm
|
||||||
summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers
|
summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers
|
||||||
and Node
|
and Node
|
||||||
|
|||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: "@octokit/types"
|
|
||||||
version: 13.4.1
|
|
||||||
type: npm
|
|
||||||
summary: Shared TypeScript definitions for Octokit projects
|
|
||||||
homepage:
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
MIT License Copyright (c) 2019 Octokit contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
- sources: README.md
|
|
||||||
text: "[MIT](LICENSE)"
|
|
||||||
notices: []
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/types"
|
name: "@octokit/types"
|
||||||
version: 12.6.0
|
version: 16.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: Shared TypeScript definitions for Octokit projects
|
summary: Shared TypeScript definitions for Octokit projects
|
||||||
homepage:
|
homepage:
|
||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: before-after-hook
|
name: before-after-hook
|
||||||
version: 2.2.3
|
version: 4.0.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: asynchronous before/error/after hooks for internal functionality
|
summary: asynchronous before/error/after hooks for internal functionality
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
Generated
+47
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
name: content-type
|
||||||
|
version: 2.0.0
|
||||||
|
type: npm
|
||||||
|
summary: Create and parse HTTP Content-Type header
|
||||||
|
homepage:
|
||||||
|
license: mit
|
||||||
|
licenses:
|
||||||
|
- sources: LICENSE
|
||||||
|
text: |
|
||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Douglas Christopher Wilson
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
'Software'), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
- sources: README.md
|
||||||
|
text: |-
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/content-type
|
||||||
|
[npm-url]: https://npmjs.org/package/content-type
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/content-type
|
||||||
|
[downloads-url]: https://npmjs.org/package/content-type
|
||||||
|
[build-image]: https://img.shields.io/github/actions/workflow/status/jshttp/content-type/ci.yml?branch=master
|
||||||
|
[build-url]: https://github.com/jshttp/content-type/actions/workflows/ci.yml?query=branch%3Amaster
|
||||||
|
[coverage-image]: https://img.shields.io/codecov/c/gh/jshttp/content-type
|
||||||
|
[coverage-url]: https://codecov.io/gh/jshttp/content-type
|
||||||
|
[license-image]: http://img.shields.io/npm/l/content-type.svg?style=flat
|
||||||
|
[license-url]: LICENSE
|
||||||
|
notices: []
|
||||||
Generated
-28
@@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
name: deprecation
|
|
||||||
version: 2.3.1
|
|
||||||
type: npm
|
|
||||||
summary: Log a deprecation message with stack
|
|
||||||
homepage: https://github.com/gr2m/deprecation#readme
|
|
||||||
license: isc
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
The ISC License
|
|
||||||
|
|
||||||
Copyright (c) Gregor Martynus and contributors
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
||||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
- sources: README.md
|
|
||||||
text: "[ISC](LICENSE)"
|
|
||||||
notices: []
|
|
||||||
+8
-14
@@ -1,16 +1,17 @@
|
|||||||
---
|
---
|
||||||
name: uuid
|
name: json-with-bigint
|
||||||
version: 3.4.0
|
version: 3.5.8
|
||||||
type: npm
|
type: npm
|
||||||
summary: RFC4122 (v1, v4, and v5) UUIDs
|
summary: JS library that allows you to easily serialize and deserialize data with
|
||||||
|
BigInt values
|
||||||
homepage:
|
homepage:
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE.md
|
- sources: LICENSE
|
||||||
text: |
|
text: |
|
||||||
The MIT License (MIT)
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2010-2016 Robert Kieffer and other contributors
|
Copyright (c) 2023 Ivan Korolenko
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -29,11 +30,4 @@ licenses:
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
notices:
|
notices: []
|
||||||
- sources: AUTHORS
|
|
||||||
text: |-
|
|
||||||
Robert Kieffer <robert@broofa.com>
|
|
||||||
Christoph Tavan <dev@tavan.de>
|
|
||||||
AJ ONeal <coolaj86@gmail.com>
|
|
||||||
Vincent Voyer <vincent@zeroload.net>
|
|
||||||
Roman Shtylman <shtylman@gmail.com>
|
|
||||||
Generated
-26
@@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
name: once
|
|
||||||
version: 1.4.0
|
|
||||||
type: npm
|
|
||||||
summary: Run a function exactly one time
|
|
||||||
homepage: https://github.com/isaacs/once#readme
|
|
||||||
license: isc
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
The ISC License
|
|
||||||
|
|
||||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
||||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: semver
|
name: semver
|
||||||
version: 6.3.1
|
version: 7.8.4
|
||||||
type: npm
|
type: npm
|
||||||
summary: The semantic version parser used by npm.
|
summary: The semantic version parser used by npm.
|
||||||
homepage:
|
homepage:
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: undici
|
name: undici
|
||||||
version: 5.29.0
|
version: 6.27.0
|
||||||
type: npm
|
type: npm
|
||||||
summary: An HTTP/1.1 client, written from scratch for Node.js
|
summary: An HTTP/1.1 client, written from scratch for Node.js
|
||||||
homepage: https://undici.nodejs.org
|
homepage: https://undici.nodejs.org
|
||||||
|
|||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
name: universal-user-agent
|
name: universal-user-agent
|
||||||
version: 6.0.1
|
version: 7.0.3
|
||||||
type: npm
|
type: npm
|
||||||
summary: Get a user agent string in both browser and node
|
summary: Get a user agent string across all JavaScript Runtime Environments
|
||||||
homepage:
|
homepage:
|
||||||
license: isc
|
license: isc
|
||||||
licenses:
|
licenses:
|
||||||
@@ -10,7 +10,7 @@ licenses:
|
|||||||
text: |
|
text: |
|
||||||
# [ISC License](https://spdx.org/licenses/ISC)
|
# [ISC License](https://spdx.org/licenses/ISC)
|
||||||
|
|
||||||
Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m)
|
Copyright (c) 2018-2021, Gregor Martynus (https://github.com/gr2m)
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
|||||||
Generated
-20
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: uuid
|
|
||||||
version: 8.3.2
|
|
||||||
type: npm
|
|
||||||
summary: RFC4122 (v1, v4, and v5) UUIDs
|
|
||||||
homepage: https://github.com/uuidjs/uuid#readme
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE.md
|
|
||||||
text: |
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2010-2020 Robert Kieffer and other contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
Generated
-20
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: uuid
|
|
||||||
version: 9.0.1
|
|
||||||
type: npm
|
|
||||||
summary: RFC4122 (v1, v4, and v5) UUIDs
|
|
||||||
homepage:
|
|
||||||
license: mit
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE.md
|
|
||||||
text: |
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2010-2020 Robert Kieffer and other contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
Generated
-26
@@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
name: wrappy
|
|
||||||
version: 1.0.2
|
|
||||||
type: npm
|
|
||||||
summary: Callback wrapping utility
|
|
||||||
homepage: https://github.com/npm/wrappy
|
|
||||||
license: isc
|
|
||||||
licenses:
|
|
||||||
- sources: LICENSE
|
|
||||||
text: |
|
|
||||||
The ISC License
|
|
||||||
|
|
||||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
||||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
notices: []
|
|
||||||
+34
-1
@@ -1,6 +1,39 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## V4.3.0
|
## v7.0.1
|
||||||
|
* Skip running unsafe pr check if input is default by @aiqiaoy in https://github.com/actions/checkout/pull/2518
|
||||||
|
* Trim only ascii whitespace for branch by @aiqiaoy in https://github.com/actions/checkout/pull/2521
|
||||||
|
* Escape values passed to --unset by @aiqiaoy in https://github.com/actions/checkout/pull/2530
|
||||||
|
* Various dependency updates
|
||||||
|
|
||||||
|
## v7.0.0
|
||||||
|
* Block checking out fork PR for pull_request_target and workflow_run by @aiqiaoy in https://github.com/actions/checkout/pull/2454
|
||||||
|
* Various dependency updates
|
||||||
|
|
||||||
|
## v6.0.3
|
||||||
|
* Fix checkout init for SHA-256 repositories by @yaananth in https://github.com/actions/checkout/pull/2439
|
||||||
|
* fix: expand merge commit SHA regex and add SHA-256 test cases by @yaananth in https://github.com/actions/checkout/pull/2414
|
||||||
|
|
||||||
|
## v6.0.2
|
||||||
|
* Fix tag handling: preserve annotations and explicit fetch-tags by @ericsciple in https://github.com/actions/checkout/pull/2356
|
||||||
|
|
||||||
|
## v6.0.1
|
||||||
|
* Add worktree support for persist-credentials includeIf by @ericsciple in https://github.com/actions/checkout/pull/2327
|
||||||
|
|
||||||
|
## 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
|
* docs: update README.md by @motss in https://github.com/actions/checkout/pull/1971
|
||||||
* Add internal repos for checking out multiple repositories by @mouismail in https://github.com/actions/checkout/pull/1977
|
* Add internal repos for checking out multiple repositories by @mouismail in https://github.com/actions/checkout/pull/1977
|
||||||
* Documentation update - add recommended permissions to Readme by @benwells in https://github.com/actions/checkout/pull/2043
|
* Documentation update - add recommended permissions to Readme by @benwells in https://github.com/actions/checkout/pull/2043
|
||||||
|
|||||||
@@ -1,6 +1,30 @@
|
|||||||
[](https://github.com/actions/checkout/actions/workflows/test.yml)
|
[](https://github.com/actions/checkout/actions/workflows/test.yml)
|
||||||
|
|
||||||
# Checkout V4
|
# Checkout v7
|
||||||
|
|
||||||
|
## What's new
|
||||||
|
|
||||||
|
- Safer fork pull request handling: checkout now refuses to check out fork pull request code by default when the workflow is triggered by `pull_request_target` or `workflow_run`. These triggers run with the base repository's `GITHUB_TOKEN`, secrets, and runner access, where executing a fork's code commonly leads to "pwn request" vulnerabilities.
|
||||||
|
- To opt in after [reviewing the risks](https://gh.io/securely-using-pull_request_target), set the new `allow-unsafe-pr-checkout: true` input.
|
||||||
|
- Migrated `actions/checkout` to ESM to support new versions of the `@actions/*` packages.
|
||||||
|
- Updated direct and transitive dependencies, including security fixes for known vulnerabilities.
|
||||||
|
|
||||||
|
# Checkout v6
|
||||||
|
|
||||||
|
## What's new
|
||||||
|
|
||||||
|
- Improved credential security: `persist-credentials` now stores credentials in a separate file under `$RUNNER_TEMP` instead of directly in `.git/config`
|
||||||
|
- No workflow changes required — `git fetch`, `git push`, etc. continue to work automatically
|
||||||
|
- Running authenticated git commands from a [Docker container action](https://docs.github.com/actions/sharing-automations/creating-actions/creating-a-docker-container-action) requires Actions Runner [v2.329.0](https://github.com/actions/runner/releases/tag/v2.329.0) or later
|
||||||
|
|
||||||
|
# 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.
|
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
|
||||||
|
|
||||||
@@ -36,7 +60,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
<!-- start usage -->
|
<!-- start usage -->
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
# Repository name with owner. For example, actions/checkout
|
# Repository name with owner. For example, actions/checkout
|
||||||
# Default: ${{ github.repository }}
|
# Default: ${{ github.repository }}
|
||||||
@@ -158,24 +182,33 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
# Scenarios
|
# Scenarios
|
||||||
|
|
||||||
- [Fetch only the root files](#Fetch-only-the-root-files)
|
- [Checkout V5](#checkout-v5)
|
||||||
- [Fetch only the root files and `.github` and `src` folder](#Fetch-only-the-root-files-and-github-and-src-folder)
|
- [What's new](#whats-new)
|
||||||
- [Fetch only a single file](#Fetch-only-a-single-file)
|
- [Checkout V4](#checkout-v4)
|
||||||
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
|
- [Note](#note)
|
||||||
- [Checkout a different branch](#Checkout-a-different-branch)
|
- [What's new](#whats-new-1)
|
||||||
- [Checkout HEAD^](#Checkout-HEAD)
|
- [Usage](#usage)
|
||||||
- [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side)
|
- [Scenarios](#scenarios)
|
||||||
- [Checkout multiple repos (nested)](#Checkout-multiple-repos-nested)
|
- [Fetch only the root files](#fetch-only-the-root-files)
|
||||||
- [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
|
- [Fetch only the root files and `.github` and `src` folder](#fetch-only-the-root-files-and-github-and-src-folder)
|
||||||
- [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit)
|
- [Fetch only a single file](#fetch-only-a-single-file)
|
||||||
- [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
|
- [Fetch all history for all tags and branches](#fetch-all-history-for-all-tags-and-branches)
|
||||||
- [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token)
|
- [Checkout a different branch](#checkout-a-different-branch)
|
||||||
- [Push a commit to a PR using the built-in token](#Push-a-commit-to-a-PR-using-the-built-in-token)
|
- [Checkout HEAD^](#checkout-head)
|
||||||
|
- [Checkout multiple repos (side by side)](#checkout-multiple-repos-side-by-side)
|
||||||
|
- [Checkout multiple repos (nested)](#checkout-multiple-repos-nested)
|
||||||
|
- [Checkout multiple repos (private)](#checkout-multiple-repos-private)
|
||||||
|
- [Checkout pull request HEAD commit instead of merge commit](#checkout-pull-request-head-commit-instead-of-merge-commit)
|
||||||
|
- [Checkout pull request on closed event](#checkout-pull-request-on-closed-event)
|
||||||
|
- [Push a commit using the built-in token](#push-a-commit-using-the-built-in-token)
|
||||||
|
- [Push a commit to a PR using the built-in token](#push-a-commit-to-a-pr-using-the-built-in-token)
|
||||||
|
- [Recommended permissions](#recommended-permissions)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
## Fetch only the root files
|
## Fetch only the root files
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
sparse-checkout: .
|
sparse-checkout: .
|
||||||
```
|
```
|
||||||
@@ -183,7 +216,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
## Fetch only the root files and `.github` and `src` folder
|
## Fetch only the root files and `.github` and `src` folder
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
.github
|
.github
|
||||||
@@ -193,7 +226,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
## Fetch only a single file
|
## Fetch only a single file
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
README.md
|
README.md
|
||||||
@@ -203,7 +236,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
## Fetch all history for all tags and branches
|
## Fetch all history for all tags and branches
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
```
|
```
|
||||||
@@ -211,7 +244,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
## Checkout a different branch
|
## Checkout a different branch
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
ref: my-branch
|
ref: my-branch
|
||||||
```
|
```
|
||||||
@@ -219,7 +252,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
## Checkout HEAD^
|
## Checkout HEAD^
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- run: git checkout HEAD^
|
- run: git checkout HEAD^
|
||||||
@@ -229,12 +262,12 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
|
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-tools
|
repository: my-org/my-tools
|
||||||
path: my-tools
|
path: my-tools
|
||||||
@@ -245,10 +278,10 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-tools
|
repository: my-org/my-tools
|
||||||
path: my-tools
|
path: my-tools
|
||||||
@@ -259,12 +292,12 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
|
|
||||||
- name: Checkout private tools
|
- name: Checkout private tools
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-private-tools
|
repository: my-org/my-private-tools
|
||||||
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
|
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
|
||||||
@@ -277,7 +310,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
## Checkout pull request HEAD commit instead of merge commit
|
## Checkout pull request HEAD commit instead of merge commit
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
```
|
```
|
||||||
@@ -293,7 +326,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
```
|
```
|
||||||
|
|
||||||
## Push a commit using the built-in token
|
## Push a commit using the built-in token
|
||||||
@@ -304,7 +337,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- run: |
|
- run: |
|
||||||
date > generated.txt
|
date > generated.txt
|
||||||
# Note: the following account information will not work on GHES
|
# Note: the following account information will not work on GHES
|
||||||
@@ -326,7 +359,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.head_ref }}
|
ref: ${{ github.head_ref }}
|
||||||
- run: |
|
- run: |
|
||||||
|
|||||||
+370
-287
@@ -1,12 +1,46 @@
|
|||||||
import * as core from '@actions/core'
|
import {
|
||||||
|
jest,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as gitAuthHelper from '../lib/git-auth-helper'
|
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as stateHelper from '../lib/state-helper'
|
import {fileURLToPath} from 'url'
|
||||||
import {IGitCommandManager} from '../lib/git-command-manager'
|
|
||||||
import {IGitSourceSettings} from '../lib/git-source-settings'
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
// Mock @actions/core before loading git-auth-helper
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
setFailed: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Mock state-helper
|
||||||
|
jest.unstable_mockModule('../src/state-helper.js', () => ({
|
||||||
|
setSshKeyPath: jest.fn(),
|
||||||
|
setSshKnownHostsPath: jest.fn(),
|
||||||
|
IsPost: false,
|
||||||
|
RepositoryPath: ''
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core')
|
||||||
|
const gitAuthHelper = await import('../src/git-auth-helper.js')
|
||||||
|
type IGitCommandManager =
|
||||||
|
import('../src/git-command-manager.js').IGitCommandManager
|
||||||
|
type IGitSourceSettings =
|
||||||
|
import('../src/git-source-settings.js').IGitSourceSettings
|
||||||
|
|
||||||
const isWindows = process.platform === 'win32'
|
const isWindows = process.platform === 'win32'
|
||||||
const testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper')
|
const testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper')
|
||||||
@@ -32,25 +66,12 @@ describe('git-auth-helper tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Mock setSecret
|
jest.clearAllMocks()
|
||||||
jest.spyOn(core, 'setSecret').mockImplementation((secret: string) => {})
|
|
||||||
|
|
||||||
// Mock error/warning/info/debug
|
|
||||||
jest.spyOn(core, 'error').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'info').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
|
|
||||||
|
|
||||||
// Mock state helper
|
|
||||||
jest.spyOn(stateHelper, 'setSshKeyPath').mockImplementation(jest.fn())
|
|
||||||
jest
|
|
||||||
.spyOn(stateHelper, 'setSshKnownHostsPath')
|
|
||||||
.mockImplementation(jest.fn())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// Unregister mocks
|
// Unregister mocks
|
||||||
jest.restoreAllMocks()
|
jest.clearAllMocks()
|
||||||
|
|
||||||
// Restore HOME
|
// Restore HOME
|
||||||
if (originalHome) {
|
if (originalHome) {
|
||||||
@@ -86,16 +107,29 @@ describe('git-auth-helper tests', () => {
|
|||||||
// Act
|
// Act
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
|
|
||||||
// Assert config
|
// Assert config - check that .git/config contains includeIf entries
|
||||||
const configContent = (
|
const localConfigContent = (
|
||||||
await fs.promises.readFile(localGitConfigPath)
|
await fs.promises.readFile(localGitConfigPath)
|
||||||
).toString()
|
).toString()
|
||||||
|
expect(
|
||||||
|
localConfigContent.indexOf('includeIf.gitdir:')
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
|
// Assert credentials config file contains the actual credentials
|
||||||
|
const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter(
|
||||||
|
f => f.startsWith('git-credentials-') && f.endsWith('.config')
|
||||||
|
)
|
||||||
|
expect(credentialsFiles.length).toBe(1)
|
||||||
|
const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0])
|
||||||
|
const credentialsContent = (
|
||||||
|
await fs.promises.readFile(credentialsConfigPath)
|
||||||
|
).toString()
|
||||||
const basicCredential = Buffer.from(
|
const basicCredential = Buffer.from(
|
||||||
`x-access-token:${settings.authToken}`,
|
`x-access-token:${settings.authToken}`,
|
||||||
'utf8'
|
'utf8'
|
||||||
).toString('base64')
|
).toString('base64')
|
||||||
expect(
|
expect(
|
||||||
configContent.indexOf(
|
credentialsContent.indexOf(
|
||||||
`http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
`http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
||||||
)
|
)
|
||||||
).toBeGreaterThanOrEqual(0)
|
).toBeGreaterThanOrEqual(0)
|
||||||
@@ -120,7 +154,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
'inject https://github.com as github server url'
|
'inject https://github.com as github server url'
|
||||||
it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => {
|
it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => {
|
||||||
await testAuthHeader(
|
await testAuthHeader(
|
||||||
configureAuth_AcceptsGitHubServerUrl,
|
configureAuth_AcceptsGitHubServerUrlSetToGHEC,
|
||||||
'https://github.com'
|
'https://github.com'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -141,12 +175,17 @@ describe('git-auth-helper tests', () => {
|
|||||||
// Act
|
// Act
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
|
|
||||||
// Assert config
|
// Assert config - check credentials config file (not local .git/config)
|
||||||
const configContent = (
|
const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter(
|
||||||
await fs.promises.readFile(localGitConfigPath)
|
f => f.startsWith('git-credentials-') && f.endsWith('.config')
|
||||||
|
)
|
||||||
|
expect(credentialsFiles.length).toBe(1)
|
||||||
|
const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0])
|
||||||
|
const credentialsContent = (
|
||||||
|
await fs.promises.readFile(credentialsConfigPath)
|
||||||
).toString()
|
).toString()
|
||||||
expect(
|
expect(
|
||||||
configContent.indexOf(
|
credentialsContent.indexOf(
|
||||||
`http.https://github.com/.extraheader AUTHORIZATION`
|
`http.https://github.com/.extraheader AUTHORIZATION`
|
||||||
)
|
)
|
||||||
).toBeGreaterThanOrEqual(0)
|
).toBeGreaterThanOrEqual(0)
|
||||||
@@ -211,7 +250,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
|
|
||||||
// Assert secret
|
// Assert secret
|
||||||
const setSecretSpy = core.setSecret as jest.Mock<any, any>
|
const setSecretSpy = core.setSecret as jest.Mock<any>
|
||||||
expect(setSecretSpy).toHaveBeenCalledTimes(1)
|
expect(setSecretSpy).toHaveBeenCalledTimes(1)
|
||||||
const expectedSecret = Buffer.from(
|
const expectedSecret = Buffer.from(
|
||||||
`x-access-token:${settings.authToken}`,
|
`x-access-token:${settings.authToken}`,
|
||||||
@@ -251,13 +290,16 @@ describe('git-auth-helper tests', () => {
|
|||||||
expectedSshCommand
|
expectedSshCommand
|
||||||
)
|
)
|
||||||
|
|
||||||
// Asserty git config
|
// Assert git config
|
||||||
const gitConfigLines = (await fs.promises.readFile(localGitConfigPath))
|
const gitConfigLines = (await fs.promises.readFile(localGitConfigPath))
|
||||||
.toString()
|
.toString()
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter(x => x)
|
.filter(x => x)
|
||||||
expect(gitConfigLines).toHaveLength(1)
|
// Should have includeIf entries pointing to credentials file
|
||||||
expect(gitConfigLines[0]).toMatch(/^http\./)
|
expect(gitConfigLines.length).toBeGreaterThan(0)
|
||||||
|
expect(
|
||||||
|
gitConfigLines.some(line => line.indexOf('includeIf.gitdir:') >= 0)
|
||||||
|
).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
const configureAuth_setsSshCommandWhenPersistCredentialsTrue =
|
const configureAuth_setsSshCommandWhenPersistCredentialsTrue =
|
||||||
@@ -419,8 +461,20 @@ describe('git-auth-helper tests', () => {
|
|||||||
expect(
|
expect(
|
||||||
configContent.indexOf('value-from-global-config')
|
configContent.indexOf('value-from-global-config')
|
||||||
).toBeGreaterThanOrEqual(0)
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
// Global config should have include.path pointing to credentials file
|
||||||
|
expect(configContent.indexOf('include.path')).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
|
// Check credentials in the separate config file
|
||||||
|
const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter(
|
||||||
|
f => f.startsWith('git-credentials-') && f.endsWith('.config')
|
||||||
|
)
|
||||||
|
expect(credentialsFiles.length).toBeGreaterThan(0)
|
||||||
|
const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0])
|
||||||
|
const credentialsContent = (
|
||||||
|
await fs.promises.readFile(credentialsConfigPath)
|
||||||
|
).toString()
|
||||||
expect(
|
expect(
|
||||||
configContent.indexOf(
|
credentialsContent.indexOf(
|
||||||
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
||||||
)
|
)
|
||||||
).toBeGreaterThanOrEqual(0)
|
).toBeGreaterThanOrEqual(0)
|
||||||
@@ -463,8 +517,20 @@ describe('git-auth-helper tests', () => {
|
|||||||
const configContent = (
|
const configContent = (
|
||||||
await fs.promises.readFile(path.join(git.env['HOME'], '.gitconfig'))
|
await fs.promises.readFile(path.join(git.env['HOME'], '.gitconfig'))
|
||||||
).toString()
|
).toString()
|
||||||
|
// Global config should have include.path pointing to credentials file
|
||||||
|
expect(configContent.indexOf('include.path')).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
|
// Check credentials in the separate config file
|
||||||
|
const credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter(
|
||||||
|
f => f.startsWith('git-credentials-') && f.endsWith('.config')
|
||||||
|
)
|
||||||
|
expect(credentialsFiles.length).toBeGreaterThan(0)
|
||||||
|
const credentialsConfigPath = path.join(runnerTemp, credentialsFiles[0])
|
||||||
|
const credentialsContent = (
|
||||||
|
await fs.promises.readFile(credentialsConfigPath)
|
||||||
|
).toString()
|
||||||
expect(
|
expect(
|
||||||
configContent.indexOf(
|
credentialsContent.indexOf(
|
||||||
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
||||||
)
|
)
|
||||||
).toBeGreaterThanOrEqual(0)
|
).toBeGreaterThanOrEqual(0)
|
||||||
@@ -484,7 +550,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
settings.sshKey = ''
|
settings.sshKey = ''
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
|
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any>
|
||||||
mockSubmoduleForeach.mockClear() // reset calls
|
mockSubmoduleForeach.mockClear() // reset calls
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -517,7 +583,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
settings.persistCredentials = false
|
settings.persistCredentials = false
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
|
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any>
|
||||||
mockSubmoduleForeach.mockClear() // reset calls
|
mockSubmoduleForeach.mockClear() // reset calls
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@@ -543,22 +609,22 @@ describe('git-auth-helper tests', () => {
|
|||||||
settings.sshKey = ''
|
settings.sshKey = ''
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
|
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any>
|
||||||
mockSubmoduleForeach.mockClear() // reset calls
|
mockSubmoduleForeach.mockClear() // reset calls
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await authHelper.configureSubmoduleAuth()
|
await authHelper.configureSubmoduleAuth()
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(4)
|
// Should configure insteadOf (2 calls for two values)
|
||||||
|
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
|
||||||
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
|
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
|
||||||
/unset-all.*insteadOf/
|
/unset-all.*insteadOf/
|
||||||
)
|
)
|
||||||
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
|
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(
|
||||||
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(
|
|
||||||
/url.*insteadOf.*git@github.com:/
|
/url.*insteadOf.*git@github.com:/
|
||||||
)
|
)
|
||||||
expect(mockSubmoduleForeach.mock.calls[3][0]).toMatch(
|
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(
|
||||||
/url.*insteadOf.*org-123456@github.com:/
|
/url.*insteadOf.*org-123456@github.com:/
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -582,19 +648,19 @@ describe('git-auth-helper tests', () => {
|
|||||||
)
|
)
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
|
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any>
|
||||||
mockSubmoduleForeach.mockClear() // reset calls
|
mockSubmoduleForeach.mockClear() // reset calls
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await authHelper.configureSubmoduleAuth()
|
await authHelper.configureSubmoduleAuth()
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
|
// Should configure sshCommand (1 call)
|
||||||
|
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2)
|
||||||
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
|
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
|
||||||
/unset-all.*insteadOf/
|
/unset-all.*insteadOf/
|
||||||
)
|
)
|
||||||
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
|
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/core\.sshCommand/)
|
||||||
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/core\.sshCommand/)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -660,112 +726,81 @@ describe('git-auth-helper tests', () => {
|
|||||||
await setup(removeAuth_removesToken)
|
await setup(removeAuth_removesToken)
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
let gitConfigContent = (
|
|
||||||
|
// Verify includeIf entries exist in local config
|
||||||
|
let localConfigContent = (
|
||||||
await fs.promises.readFile(localGitConfigPath)
|
await fs.promises.readFile(localGitConfigPath)
|
||||||
).toString()
|
).toString()
|
||||||
expect(gitConfigContent.indexOf('http.')).toBeGreaterThanOrEqual(0) // sanity check
|
expect(
|
||||||
|
localConfigContent.indexOf('includeIf.gitdir:')
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
// Act
|
// Verify both host and container includeIf entries are present
|
||||||
await authHelper.removeAuth()
|
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)
|
||||||
|
|
||||||
// Assert git config
|
// Verify credentials file exists
|
||||||
gitConfigContent = (
|
let credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter(
|
||||||
await fs.promises.readFile(localGitConfigPath)
|
f => f.startsWith('git-credentials-') && f.endsWith('.config')
|
||||||
|
)
|
||||||
|
expect(credentialsFiles.length).toBe(1)
|
||||||
|
const credentialsFilePath = path.join(runnerTemp, credentialsFiles[0])
|
||||||
|
|
||||||
|
// Verify credentials file contains the auth token
|
||||||
|
let credentialsContent = (
|
||||||
|
await fs.promises.readFile(credentialsFilePath)
|
||||||
).toString()
|
).toString()
|
||||||
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
const removeAuth_removesV6StyleCredentials =
|
|
||||||
'removeAuth removes v6 style credentials'
|
|
||||||
it(removeAuth_removesV6StyleCredentials, async () => {
|
|
||||||
// Arrange
|
|
||||||
await setup(removeAuth_removesV6StyleCredentials)
|
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
|
||||||
await authHelper.configureAuth()
|
|
||||||
|
|
||||||
// Manually create v6-style credentials that would be left by v6
|
|
||||||
const credentialsFileName =
|
|
||||||
'git-credentials-12345678-1234-1234-1234-123456789abc.config'
|
|
||||||
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
|
||||||
const basicCredential = Buffer.from(
|
const basicCredential = Buffer.from(
|
||||||
`x-access-token:${settings.authToken}`,
|
`x-access-token:${settings.authToken}`,
|
||||||
'utf8'
|
'utf8'
|
||||||
).toString('base64')
|
).toString('base64')
|
||||||
const credentialsContent = `[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic ${basicCredential}\n`
|
|
||||||
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
|
||||||
|
|
||||||
// Add includeIf entries to local git config (simulating v6 configuration)
|
|
||||||
const hostGitDir = path.join(workspace, '.git').replace(/\\/g, '/')
|
|
||||||
await fs.promises.appendFile(
|
|
||||||
localGitConfigPath,
|
|
||||||
`[includeIf "gitdir:${hostGitDir}/"]\n\tpath = ${credentialsFilePath}\n`
|
|
||||||
)
|
|
||||||
await fs.promises.appendFile(
|
|
||||||
localGitConfigPath,
|
|
||||||
`[includeIf "gitdir:/github/workspace/.git/"]\n\tpath = /github/runner_temp/${credentialsFileName}\n`
|
|
||||||
)
|
|
||||||
|
|
||||||
// Verify v6 style config exists
|
|
||||||
let gitConfigContent = (
|
|
||||||
await fs.promises.readFile(localGitConfigPath)
|
|
||||||
).toString()
|
|
||||||
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
|
||||||
expect(
|
expect(
|
||||||
gitConfigContent.indexOf(credentialsFilePath)
|
credentialsContent.indexOf(
|
||||||
|
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
||||||
|
)
|
||||||
).toBeGreaterThanOrEqual(0)
|
).toBeGreaterThanOrEqual(0)
|
||||||
await fs.promises.stat(credentialsFilePath) // Verify file exists
|
|
||||||
|
|
||||||
// Mock the git methods to handle v6 cleanup
|
// Verify the includeIf entries point to the credentials file
|
||||||
const mockTryGetConfigKeys = git.tryGetConfigKeys as jest.Mock<any, any>
|
const containerCredentialsPath = path.posix.join(
|
||||||
mockTryGetConfigKeys.mockResolvedValue([
|
'/github/runner_temp',
|
||||||
`includeIf.gitdir:${hostGitDir}/.path`,
|
path.basename(credentialsFilePath)
|
||||||
'includeIf.gitdir:/github/workspace/.git/.path'
|
|
||||||
])
|
|
||||||
|
|
||||||
const mockTryGetConfigValues = git.tryGetConfigValues as jest.Mock<any, any>
|
|
||||||
mockTryGetConfigValues.mockImplementation(async (key: string) => {
|
|
||||||
if (key === `includeIf.gitdir:${hostGitDir}/.path`) {
|
|
||||||
return [credentialsFilePath]
|
|
||||||
}
|
|
||||||
if (key === 'includeIf.gitdir:/github/workspace/.git/.path') {
|
|
||||||
return [`/github/runner_temp/${credentialsFileName}`]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
|
|
||||||
const mockTryConfigUnsetValue = git.tryConfigUnsetValue as jest.Mock<
|
|
||||||
any,
|
|
||||||
any
|
|
||||||
>
|
|
||||||
mockTryConfigUnsetValue.mockImplementation(
|
|
||||||
async (
|
|
||||||
key: string,
|
|
||||||
value: string,
|
|
||||||
globalConfig?: boolean,
|
|
||||||
configPath?: string
|
|
||||||
) => {
|
|
||||||
const targetPath = configPath || localGitConfigPath
|
|
||||||
let content = await fs.promises.readFile(targetPath, 'utf8')
|
|
||||||
// Remove the includeIf section
|
|
||||||
const lines = content
|
|
||||||
.split('\n')
|
|
||||||
.filter(line => !line.includes('includeIf') && !line.includes(value))
|
|
||||||
await fs.promises.writeFile(targetPath, lines.join('\n'))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
expect(
|
||||||
|
localConfigContent.indexOf(credentialsFilePath)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
expect(
|
||||||
|
localConfigContent.indexOf(containerCredentialsPath)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await authHelper.removeAuth()
|
await authHelper.removeAuth()
|
||||||
|
|
||||||
// Assert includeIf entries removed from local git config
|
// Assert all includeIf entries removed from local git config
|
||||||
gitConfigContent = (
|
localConfigContent = (
|
||||||
await fs.promises.readFile(localGitConfigPath)
|
await fs.promises.readFile(localGitConfigPath)
|
||||||
).toString()
|
).toString()
|
||||||
expect(gitConfigContent.indexOf('includeIf')).toBeLessThan(0)
|
expect(localConfigContent.indexOf('includeIf.gitdir:')).toBeLessThan(0)
|
||||||
expect(gitConfigContent.indexOf(credentialsFilePath)).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
|
// 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 {
|
try {
|
||||||
await fs.promises.stat(credentialsFilePath)
|
await fs.promises.stat(credentialsFilePath)
|
||||||
throw new Error('Credentials file should have been deleted')
|
throw new Error('Credentials file should have been deleted')
|
||||||
@@ -776,113 +811,108 @@ describe('git-auth-helper tests', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const removeAuth_removesV6StyleCredentialsFromSubmodules =
|
const removeAuth_removesTokenFromSubmodules =
|
||||||
'removeAuth removes v6 style credentials from submodules'
|
'removeAuth removes token from submodules'
|
||||||
it(removeAuth_removesV6StyleCredentialsFromSubmodules, async () => {
|
it(removeAuth_removesTokenFromSubmodules, async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
await setup(removeAuth_removesV6StyleCredentialsFromSubmodules)
|
await setup(removeAuth_removesTokenFromSubmodules)
|
||||||
|
|
||||||
// Create fake submodule config paths
|
// Create fake submodule config paths
|
||||||
const submodule1Dir = path.join(workspace, '.git', 'modules', 'submodule-1')
|
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 submodule1ConfigPath = path.join(submodule1Dir, 'config')
|
||||||
|
const submodule2ConfigPath = path.join(submodule2Dir, 'config')
|
||||||
|
|
||||||
await fs.promises.mkdir(submodule1Dir, {recursive: true})
|
await fs.promises.mkdir(submodule1Dir, {recursive: true})
|
||||||
|
await fs.promises.mkdir(submodule2Dir, {recursive: true})
|
||||||
await fs.promises.writeFile(submodule1ConfigPath, '')
|
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>
|
||||||
|
mockGetSubmoduleConfigPaths.mockResolvedValue([
|
||||||
|
submodule1ConfigPath,
|
||||||
|
submodule2ConfigPath
|
||||||
|
])
|
||||||
|
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
|
await authHelper.configureSubmoduleAuth()
|
||||||
|
|
||||||
// Create v6-style credentials file
|
// Verify credentials file exists
|
||||||
const credentialsFileName =
|
let credentialsFiles = (await fs.promises.readdir(runnerTemp)).filter(
|
||||||
'git-credentials-abcdef12-3456-7890-abcd-ef1234567890.config'
|
f => f.startsWith('git-credentials-') && f.endsWith('.config')
|
||||||
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
|
||||||
const basicCredential = Buffer.from(
|
|
||||||
`x-access-token:${settings.authToken}`,
|
|
||||||
'utf8'
|
|
||||||
).toString('base64')
|
|
||||||
const credentialsContent = `[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic ${basicCredential}\n`
|
|
||||||
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
|
||||||
|
|
||||||
// Add includeIf entries to submodule config
|
|
||||||
const submodule1GitDir = submodule1Dir.replace(/\\/g, '/')
|
|
||||||
await fs.promises.appendFile(
|
|
||||||
submodule1ConfigPath,
|
|
||||||
`[includeIf "gitdir:${submodule1GitDir}/"]\n\tpath = ${credentialsFilePath}\n`
|
|
||||||
)
|
)
|
||||||
|
expect(credentialsFiles.length).toBe(1)
|
||||||
|
const credentialsFilePath = path.join(runnerTemp, credentialsFiles[0])
|
||||||
|
|
||||||
// Verify submodule config has includeIf entry
|
// Verify submodule 1 config has includeIf entries
|
||||||
let submoduleConfigContent = (
|
let submodule1Content = (
|
||||||
await fs.promises.readFile(submodule1ConfigPath)
|
await fs.promises.readFile(submodule1ConfigPath)
|
||||||
).toString()
|
).toString()
|
||||||
expect(submoduleConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(
|
const submodule1GitDir = submodule1Dir.replace(/\\/g, '/')
|
||||||
0
|
|
||||||
)
|
|
||||||
expect(
|
expect(
|
||||||
submoduleConfigContent.indexOf(credentialsFilePath)
|
submodule1Content.indexOf(`includeIf.gitdir:${submodule1GitDir}.path`)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
expect(
|
||||||
|
submodule1Content.indexOf(credentialsFilePath)
|
||||||
).toBeGreaterThanOrEqual(0)
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
// Mock getSubmoduleConfigPaths
|
// Verify submodule 2 config has includeIf entries
|
||||||
const mockGetSubmoduleConfigPaths =
|
let submodule2Content = (
|
||||||
git.getSubmoduleConfigPaths as jest.Mock<any, any>
|
await fs.promises.readFile(submodule2ConfigPath)
|
||||||
mockGetSubmoduleConfigPaths.mockResolvedValue([submodule1ConfigPath])
|
).toString()
|
||||||
|
const submodule2GitDir = submodule2Dir.replace(/\\/g, '/')
|
||||||
|
expect(
|
||||||
|
submodule2Content.indexOf(`includeIf.gitdir:${submodule2GitDir}.path`)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
expect(
|
||||||
|
submodule2Content.indexOf(credentialsFilePath)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
// Mock tryGetConfigKeys for submodule
|
// Verify both host and container paths are in each submodule config
|
||||||
const mockTryGetConfigKeys = git.tryGetConfigKeys as jest.Mock<any, any>
|
const containerCredentialsPath = path.posix.join(
|
||||||
mockTryGetConfigKeys.mockImplementation(
|
'/github/runner_temp',
|
||||||
async (pattern: string, globalConfig?: boolean, configPath?: string) => {
|
path.basename(credentialsFilePath)
|
||||||
if (configPath === submodule1ConfigPath) {
|
|
||||||
return [`includeIf.gitdir:${submodule1GitDir}/.path`]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
expect(
|
||||||
|
submodule1Content.indexOf(containerCredentialsPath)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
expect(
|
||||||
|
submodule2Content.indexOf(containerCredentialsPath)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
// Mock tryGetConfigValues for submodule
|
// Act - ensure mock persists for removeAuth
|
||||||
const mockTryGetConfigValues = git.tryGetConfigValues as jest.Mock<any, any>
|
mockGetSubmoduleConfigPaths.mockResolvedValue([
|
||||||
mockTryGetConfigValues.mockImplementation(
|
submodule1ConfigPath,
|
||||||
async (key: string, globalConfig?: boolean, configPath?: string) => {
|
submodule2ConfigPath
|
||||||
if (
|
])
|
||||||
configPath === submodule1ConfigPath &&
|
|
||||||
key === `includeIf.gitdir:${submodule1GitDir}/.path`
|
|
||||||
) {
|
|
||||||
return [credentialsFilePath]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mock tryConfigUnsetValue for submodule
|
|
||||||
const mockTryConfigUnsetValue = git.tryConfigUnsetValue as jest.Mock<
|
|
||||||
any,
|
|
||||||
any
|
|
||||||
>
|
|
||||||
mockTryConfigUnsetValue.mockImplementation(
|
|
||||||
async (
|
|
||||||
key: string,
|
|
||||||
value: string,
|
|
||||||
globalConfig?: boolean,
|
|
||||||
configPath?: string
|
|
||||||
) => {
|
|
||||||
const targetPath = configPath || localGitConfigPath
|
|
||||||
let content = await fs.promises.readFile(targetPath, 'utf8')
|
|
||||||
const lines = content
|
|
||||||
.split('\n')
|
|
||||||
.filter(line => !line.includes('includeIf') && !line.includes(value))
|
|
||||||
await fs.promises.writeFile(targetPath, lines.join('\n'))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await authHelper.removeAuth()
|
await authHelper.removeAuth()
|
||||||
|
|
||||||
// Assert submodule includeIf entries removed
|
// Assert submodule 1 includeIf entries removed
|
||||||
submoduleConfigContent = (
|
submodule1Content = (
|
||||||
await fs.promises.readFile(submodule1ConfigPath)
|
await fs.promises.readFile(submodule1ConfigPath)
|
||||||
).toString()
|
).toString()
|
||||||
expect(submoduleConfigContent.indexOf('includeIf')).toBeLessThan(0)
|
expect(submodule1Content.indexOf('includeIf.gitdir:')).toBeLessThan(0)
|
||||||
expect(submoduleConfigContent.indexOf(credentialsFilePath)).toBeLessThan(0)
|
expect(submodule1Content.indexOf(credentialsFilePath)).toBeLessThan(0)
|
||||||
|
expect(submodule1Content.indexOf(containerCredentialsPath)).toBeLessThan(0)
|
||||||
|
|
||||||
// Assert credentials file deleted
|
// 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 {
|
try {
|
||||||
await fs.promises.stat(credentialsFilePath)
|
await fs.promises.stat(credentialsFilePath)
|
||||||
throw new Error('Credentials file should have been deleted')
|
throw new Error('Credentials file should have been deleted')
|
||||||
@@ -893,65 +923,6 @@ describe('git-auth-helper tests', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const removeAuth_skipsV6CleanupWhenEnvVarSet =
|
|
||||||
'removeAuth skips v6 cleanup when ACTIONS_CHECKOUT_SKIP_V6_CLEANUP is set'
|
|
||||||
it(removeAuth_skipsV6CleanupWhenEnvVarSet, async () => {
|
|
||||||
// Arrange
|
|
||||||
await setup(removeAuth_skipsV6CleanupWhenEnvVarSet)
|
|
||||||
|
|
||||||
// Set the skip environment variable
|
|
||||||
process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'] = '1'
|
|
||||||
|
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
|
||||||
await authHelper.configureAuth()
|
|
||||||
|
|
||||||
// Create v6-style credentials file in RUNNER_TEMP
|
|
||||||
const credentialsFileName = 'git-credentials-test-uuid-1234-5678.config'
|
|
||||||
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
|
||||||
const credentialsContent =
|
|
||||||
'[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic token\n'
|
|
||||||
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
|
||||||
|
|
||||||
// Add includeIf section to local git config (separate from http.* config)
|
|
||||||
const includeIfSection = `\n[includeIf "gitdir:/some/path/.git/"]\n\tpath = ${credentialsFilePath}\n`
|
|
||||||
await fs.promises.appendFile(localGitConfigPath, includeIfSection)
|
|
||||||
|
|
||||||
// Verify v6 style config exists
|
|
||||||
let gitConfigContent = (
|
|
||||||
await fs.promises.readFile(localGitConfigPath)
|
|
||||||
).toString()
|
|
||||||
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
|
||||||
await fs.promises.stat(credentialsFilePath) // Verify file exists
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await authHelper.removeAuth()
|
|
||||||
|
|
||||||
// Assert v5 cleanup still happened (http.* removed)
|
|
||||||
gitConfigContent = (
|
|
||||||
await fs.promises.readFile(localGitConfigPath)
|
|
||||||
).toString()
|
|
||||||
expect(
|
|
||||||
gitConfigContent.indexOf('http.https://github.com/.extraheader')
|
|
||||||
).toBeLessThan(0)
|
|
||||||
|
|
||||||
// Assert v6 cleanup was skipped - includeIf should still be present
|
|
||||||
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
|
||||||
expect(
|
|
||||||
gitConfigContent.indexOf(credentialsFilePath)
|
|
||||||
).toBeGreaterThanOrEqual(0)
|
|
||||||
|
|
||||||
// Assert credentials file still exists (wasn't deleted)
|
|
||||||
await fs.promises.stat(credentialsFilePath) // File should still exist
|
|
||||||
|
|
||||||
// Assert debug message was logged
|
|
||||||
expect(core.debug).toHaveBeenCalledWith(
|
|
||||||
'Skipping v6 style cleanup due to ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
delete process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP']
|
|
||||||
})
|
|
||||||
|
|
||||||
const removeGlobalConfig_removesOverride =
|
const removeGlobalConfig_removesOverride =
|
||||||
'removeGlobalConfig removes override'
|
'removeGlobalConfig removes override'
|
||||||
it(removeGlobalConfig_removesOverride, async () => {
|
it(removeGlobalConfig_removesOverride, async () => {
|
||||||
@@ -978,6 +949,52 @@ describe('git-auth-helper tests', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const testCredentialsConfigPath_matchesCredentialsConfigPaths =
|
||||||
|
'testCredentialsConfigPath matches credentials config paths'
|
||||||
|
it(testCredentialsConfigPath_matchesCredentialsConfigPaths, async () => {
|
||||||
|
// Arrange
|
||||||
|
await setup(testCredentialsConfigPath_matchesCredentialsConfigPaths)
|
||||||
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
|
|
||||||
|
// Get a real credentials config path
|
||||||
|
const credentialsConfigPath = await (
|
||||||
|
authHelper as any
|
||||||
|
).getCredentialsConfigPath()
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
expect(
|
||||||
|
(authHelper as any).testCredentialsConfigPath(credentialsConfigPath)
|
||||||
|
).toBe(true)
|
||||||
|
expect(
|
||||||
|
(authHelper as any).testCredentialsConfigPath(
|
||||||
|
'/some/path/git-credentials-12345678-abcd-1234-5678-123456789012.config'
|
||||||
|
)
|
||||||
|
).toBe(true)
|
||||||
|
expect(
|
||||||
|
(authHelper as any).testCredentialsConfigPath(
|
||||||
|
'/some/path/git-credentials-abcdef12-3456-7890-abcd-ef1234567890.config'
|
||||||
|
)
|
||||||
|
).toBe(true)
|
||||||
|
|
||||||
|
// Test invalid paths
|
||||||
|
expect(
|
||||||
|
(authHelper as any).testCredentialsConfigPath(
|
||||||
|
'/some/path/other-config.config'
|
||||||
|
)
|
||||||
|
).toBe(false)
|
||||||
|
expect(
|
||||||
|
(authHelper as any).testCredentialsConfigPath(
|
||||||
|
'/some/path/git-credentials-invalid.config'
|
||||||
|
)
|
||||||
|
).toBe(false)
|
||||||
|
expect(
|
||||||
|
(authHelper as any).testCredentialsConfigPath(
|
||||||
|
'/some/path/git-credentials-.config'
|
||||||
|
)
|
||||||
|
).toBe(false)
|
||||||
|
expect((authHelper as any).testCredentialsConfigPath('')).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function setup(testName: string): Promise<void> {
|
async function setup(testName: string): Promise<void> {
|
||||||
@@ -992,6 +1009,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
await fs.promises.mkdir(tempHomedir, {recursive: true})
|
await fs.promises.mkdir(tempHomedir, {recursive: true})
|
||||||
process.env['RUNNER_TEMP'] = runnerTemp
|
process.env['RUNNER_TEMP'] = runnerTemp
|
||||||
process.env['HOME'] = tempHomedir
|
process.env['HOME'] = tempHomedir
|
||||||
|
process.env['GITHUB_WORKSPACE'] = workspace
|
||||||
|
|
||||||
// Create git config
|
// Create git config
|
||||||
globalGitConfigPath = path.join(tempHomedir, '.gitconfig')
|
globalGitConfigPath = path.join(tempHomedir, '.gitconfig')
|
||||||
@@ -1010,10 +1028,20 @@ async function setup(testName: string): Promise<void> {
|
|||||||
checkout: jest.fn(),
|
checkout: jest.fn(),
|
||||||
checkoutDetach: jest.fn(),
|
checkoutDetach: jest.fn(),
|
||||||
config: jest.fn(
|
config: jest.fn(
|
||||||
async (key: string, value: string, globalConfig?: boolean) => {
|
async (
|
||||||
const configPath = globalConfig
|
key: string,
|
||||||
? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
|
value: string,
|
||||||
: localGitConfigPath
|
globalConfig?: boolean,
|
||||||
|
add?: boolean,
|
||||||
|
configFile?: string
|
||||||
|
) => {
|
||||||
|
const configPath =
|
||||||
|
configFile ||
|
||||||
|
(globalConfig
|
||||||
|
? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
|
||||||
|
: localGitConfigPath)
|
||||||
|
// Ensure directory exists
|
||||||
|
await fs.promises.mkdir(path.dirname(configPath), {recursive: true})
|
||||||
await fs.promises.appendFile(configPath, `\n${key} ${value}`)
|
await fs.promises.appendFile(configPath, `\n${key} ${value}`)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -1033,6 +1061,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
env: {},
|
env: {},
|
||||||
fetch: jest.fn(),
|
fetch: jest.fn(),
|
||||||
getDefaultBranch: jest.fn(),
|
getDefaultBranch: jest.fn(),
|
||||||
|
getSubmoduleConfigPaths: jest.fn(async () => []),
|
||||||
getWorkingDirectory: jest.fn(() => workspace),
|
getWorkingDirectory: jest.fn(() => workspace),
|
||||||
init: jest.fn(),
|
init: jest.fn(),
|
||||||
isDetached: jest.fn(),
|
isDetached: jest.fn(),
|
||||||
@@ -1071,23 +1100,75 @@ async function setup(testName: string): Promise<void> {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
tryConfigUnsetValue: jest.fn(
|
||||||
|
async (
|
||||||
|
key: string,
|
||||||
|
value: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configPath?: string
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const targetConfigPath =
|
||||||
|
configPath ||
|
||||||
|
(globalConfig
|
||||||
|
? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
|
||||||
|
: localGitConfigPath)
|
||||||
|
let content = await fs.promises.readFile(targetConfigPath)
|
||||||
|
let lines = content
|
||||||
|
.toString()
|
||||||
|
.split('\n')
|
||||||
|
.filter(x => x)
|
||||||
|
.filter(x => !(x.startsWith(key) && x.includes(value)))
|
||||||
|
await fs.promises.writeFile(targetConfigPath, lines.join('\n'))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
),
|
||||||
tryDisableAutomaticGarbageCollection: jest.fn(),
|
tryDisableAutomaticGarbageCollection: jest.fn(),
|
||||||
tryGetFetchUrl: jest.fn(),
|
tryGetFetchUrl: jest.fn(),
|
||||||
getSubmoduleConfigPaths: jest.fn(async () => {
|
tryGetConfigValues: jest.fn(
|
||||||
return []
|
async (
|
||||||
}),
|
key: string,
|
||||||
tryConfigUnsetValue: jest.fn(async () => {
|
globalConfig?: boolean,
|
||||||
return true
|
configPath?: string
|
||||||
}),
|
): Promise<string[]> => {
|
||||||
tryGetConfigValues: jest.fn(async () => {
|
const targetConfigPath =
|
||||||
return []
|
configPath ||
|
||||||
}),
|
(globalConfig
|
||||||
tryGetConfigKeys: jest.fn(async () => {
|
? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
|
||||||
return []
|
: localGitConfigPath)
|
||||||
}),
|
const content = await fs.promises.readFile(targetConfigPath)
|
||||||
|
const lines = content
|
||||||
|
.toString()
|
||||||
|
.split('\n')
|
||||||
|
.filter(x => x && x.startsWith(key))
|
||||||
|
.map(x => x.substring(key.length).trim())
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
),
|
||||||
|
tryGetConfigKeys: jest.fn(
|
||||||
|
async (
|
||||||
|
pattern: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configPath?: string
|
||||||
|
): Promise<string[]> => {
|
||||||
|
const targetConfigPath =
|
||||||
|
configPath ||
|
||||||
|
(globalConfig
|
||||||
|
? path.join(git.env['HOME'] || tempHomedir, '.gitconfig')
|
||||||
|
: localGitConfigPath)
|
||||||
|
const content = await fs.promises.readFile(targetConfigPath)
|
||||||
|
const lines = content
|
||||||
|
.toString()
|
||||||
|
.split('\n')
|
||||||
|
.filter(x => x)
|
||||||
|
const keys = lines
|
||||||
|
.filter(x => new RegExp(pattern).test(x.split(' ')[0]))
|
||||||
|
.map(x => x.split(' ')[0])
|
||||||
|
return [...new Set(keys)] // Remove duplicates
|
||||||
|
}
|
||||||
|
),
|
||||||
tryReset: jest.fn(),
|
tryReset: jest.fn(),
|
||||||
version: jest.fn()
|
version: jest.fn()
|
||||||
}
|
} as unknown as IGitCommandManager & {env: {[key: string]: string}}
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
authToken: 'some auth token',
|
authToken: 'some auth token',
|
||||||
@@ -1120,6 +1201,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
|
|
||||||
async function getActualSshKeyPath(): Promise<string> {
|
async function getActualSshKeyPath(): Promise<string> {
|
||||||
let actualTempFiles = (await fs.promises.readdir(runnerTemp))
|
let actualTempFiles = (await fs.promises.readdir(runnerTemp))
|
||||||
|
.filter(x => !x.startsWith('git-credentials-')) // Exclude credentials config file
|
||||||
.sort()
|
.sort()
|
||||||
.map(x => path.join(runnerTemp, x))
|
.map(x => path.join(runnerTemp, x))
|
||||||
if (actualTempFiles.length === 0) {
|
if (actualTempFiles.length === 0) {
|
||||||
@@ -1133,6 +1215,7 @@ async function getActualSshKeyPath(): Promise<string> {
|
|||||||
|
|
||||||
async function getActualSshKnownHostsPath(): Promise<string> {
|
async function getActualSshKnownHostsPath(): Promise<string> {
|
||||||
let actualTempFiles = (await fs.promises.readdir(runnerTemp))
|
let actualTempFiles = (await fs.promises.readdir(runnerTemp))
|
||||||
|
.filter(x => !x.startsWith('git-credentials-')) // Exclude credentials config file
|
||||||
.sort()
|
.sort()
|
||||||
.map(x => path.join(runnerTemp, x))
|
.map(x => path.join(runnerTemp, x))
|
||||||
if (actualTempFiles.length === 0) {
|
if (actualTempFiles.length === 0) {
|
||||||
|
|||||||
@@ -1,26 +1,51 @@
|
|||||||
import * as exec from '@actions/exec'
|
import {
|
||||||
import * as fshelper from '../lib/fs-helper'
|
jest,
|
||||||
import * as commandManager from '../lib/git-command-manager'
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals'
|
||||||
|
|
||||||
let git: commandManager.IGitCommandManager
|
// Mock @actions/exec
|
||||||
let mockExec = jest.fn()
|
const mockExec = jest.fn()
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: mockExec
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Mock fs-helper
|
||||||
|
const mockFileExistsSync = jest.fn()
|
||||||
|
const mockDirectoryExistsSync = jest.fn()
|
||||||
|
jest.unstable_mockModule('../src/fs-helper.js', () => ({
|
||||||
|
fileExistsSync: mockFileExistsSync,
|
||||||
|
directoryExistsSync: mockDirectoryExistsSync
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const commandManager = await import('../src/git-command-manager.js')
|
||||||
|
type IGitCommandManager =
|
||||||
|
import('../src/git-command-manager.js').IGitCommandManager
|
||||||
|
|
||||||
|
let git: IGitCommandManager
|
||||||
|
|
||||||
describe('git-auth-helper tests', () => {
|
describe('git-auth-helper tests', () => {
|
||||||
beforeAll(async () => {})
|
beforeAll(async () => {})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
|
mockFileExistsSync.mockReset()
|
||||||
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
|
mockDirectoryExistsSync.mockReset()
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.restoreAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(() => {})
|
afterAll(() => {})
|
||||||
|
|
||||||
it('branch list matches', async () => {
|
it('branch list matches', async () => {
|
||||||
mockExec.mockImplementation((path, args, options) => {
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
console.log(args, options.listeners.stdout)
|
console.log(args, options.listeners.stdout)
|
||||||
|
|
||||||
if (args.includes('version')) {
|
if (args.includes('version')) {
|
||||||
@@ -36,7 +61,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
})
|
})
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
const doSparseCheckout = false
|
const doSparseCheckout = false
|
||||||
@@ -53,7 +78,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('ambiguous ref name output is captured', async () => {
|
it('ambiguous ref name output is captured', async () => {
|
||||||
mockExec.mockImplementation((path, args, options) => {
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
console.log(args, options.listeners.stdout)
|
console.log(args, options.listeners.stdout)
|
||||||
|
|
||||||
if (args.includes('version')) {
|
if (args.includes('version')) {
|
||||||
@@ -72,7 +97,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
})
|
})
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
const doSparseCheckout = false
|
const doSparseCheckout = false
|
||||||
@@ -91,9 +116,9 @@ describe('git-auth-helper tests', () => {
|
|||||||
|
|
||||||
describe('Test fetchDepth and fetchTags options', () => {
|
describe('Test fetchDepth and fetchTags options', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
|
mockFileExistsSync.mockReset()
|
||||||
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
|
mockDirectoryExistsSync.mockReset()
|
||||||
mockExec.mockImplementation((path, args, options) => {
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
console.log(args, options.listeners.stdout)
|
console.log(args, options.listeners.stdout)
|
||||||
|
|
||||||
if (args.includes('version')) {
|
if (args.includes('version')) {
|
||||||
@@ -105,11 +130,11 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.restoreAllMocks()
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when fetchDepth is 0 and fetchTags is true', async () => {
|
it('should call execGit with the correct arguments when fetchDepth is 0', async () => {
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
const doSparseCheckout = false
|
const doSparseCheckout = false
|
||||||
@@ -122,45 +147,7 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
const refSpec = ['refspec1', 'refspec2']
|
const refSpec = ['refspec1', 'refspec2']
|
||||||
const options = {
|
const options = {
|
||||||
filter: 'filterValue',
|
filter: 'filterValue',
|
||||||
fetchDepth: 0,
|
fetchDepth: 0
|
||||||
fetchTags: true
|
|
||||||
}
|
|
||||||
|
|
||||||
await git.fetch(refSpec, options)
|
|
||||||
|
|
||||||
expect(mockExec).toHaveBeenCalledWith(
|
|
||||||
expect.any(String),
|
|
||||||
[
|
|
||||||
'-c',
|
|
||||||
'protocol.version=2',
|
|
||||||
'fetch',
|
|
||||||
'--prune',
|
|
||||||
'--no-recurse-submodules',
|
|
||||||
'--filter=filterValue',
|
|
||||||
'origin',
|
|
||||||
'refspec1',
|
|
||||||
'refspec2'
|
|
||||||
],
|
|
||||||
expect.any(Object)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when fetchDepth is 0 and fetchTags is false', async () => {
|
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
|
||||||
const lfs = false
|
|
||||||
const doSparseCheckout = false
|
|
||||||
git = await commandManager.createCommandManager(
|
|
||||||
workingDirectory,
|
|
||||||
lfs,
|
|
||||||
doSparseCheckout
|
|
||||||
)
|
|
||||||
const refSpec = ['refspec1', 'refspec2']
|
|
||||||
const options = {
|
|
||||||
filter: 'filterValue',
|
|
||||||
fetchDepth: 0,
|
|
||||||
fetchTags: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await git.fetch(refSpec, options)
|
await git.fetch(refSpec, options)
|
||||||
@@ -183,8 +170,46 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when fetchDepth is 1 and fetchTags is false', async () => {
|
it('should call execGit with the correct arguments when fetchDepth is 0 and refSpec includes tags', async () => {
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
const refSpec = ['refspec1', 'refspec2', '+refs/tags/*:refs/tags/*']
|
||||||
|
const options = {
|
||||||
|
filter: 'filterValue',
|
||||||
|
fetchDepth: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
await git.fetch(refSpec, options)
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
[
|
||||||
|
'-c',
|
||||||
|
'protocol.version=2',
|
||||||
|
'fetch',
|
||||||
|
'--no-tags',
|
||||||
|
'--prune',
|
||||||
|
'--no-recurse-submodules',
|
||||||
|
'--filter=filterValue',
|
||||||
|
'origin',
|
||||||
|
'refspec1',
|
||||||
|
'refspec2',
|
||||||
|
'+refs/tags/*:refs/tags/*'
|
||||||
|
],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call execGit with the correct arguments when fetchDepth is 1', async () => {
|
||||||
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
@@ -197,8 +222,7 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
const refSpec = ['refspec1', 'refspec2']
|
const refSpec = ['refspec1', 'refspec2']
|
||||||
const options = {
|
const options = {
|
||||||
filter: 'filterValue',
|
filter: 'filterValue',
|
||||||
fetchDepth: 1,
|
fetchDepth: 1
|
||||||
fetchTags: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await git.fetch(refSpec, options)
|
await git.fetch(refSpec, options)
|
||||||
@@ -222,8 +246,8 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when fetchDepth is 1 and fetchTags is true', async () => {
|
it('should call execGit with the correct arguments when fetchDepth is 1 and refSpec includes tags', async () => {
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
@@ -233,11 +257,10 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
lfs,
|
lfs,
|
||||||
doSparseCheckout
|
doSparseCheckout
|
||||||
)
|
)
|
||||||
const refSpec = ['refspec1', 'refspec2']
|
const refSpec = ['refspec1', 'refspec2', '+refs/tags/*:refs/tags/*']
|
||||||
const options = {
|
const options = {
|
||||||
filter: 'filterValue',
|
filter: 'filterValue',
|
||||||
fetchDepth: 1,
|
fetchDepth: 1
|
||||||
fetchTags: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await git.fetch(refSpec, options)
|
await git.fetch(refSpec, options)
|
||||||
@@ -248,20 +271,22 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
'-c',
|
'-c',
|
||||||
'protocol.version=2',
|
'protocol.version=2',
|
||||||
'fetch',
|
'fetch',
|
||||||
|
'--no-tags',
|
||||||
'--prune',
|
'--prune',
|
||||||
'--no-recurse-submodules',
|
'--no-recurse-submodules',
|
||||||
'--filter=filterValue',
|
'--filter=filterValue',
|
||||||
'--depth=1',
|
'--depth=1',
|
||||||
'origin',
|
'origin',
|
||||||
'refspec1',
|
'refspec1',
|
||||||
'refspec2'
|
'refspec2',
|
||||||
|
'+refs/tags/*:refs/tags/*'
|
||||||
],
|
],
|
||||||
expect.any(Object)
|
expect.any(Object)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when showProgress is true', async () => {
|
it('should call execGit with the correct arguments when showProgress is true', async () => {
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
@@ -299,7 +324,7 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when fetchDepth is 42 and showProgress is true', async () => {
|
it('should call execGit with the correct arguments when fetchDepth is 42 and showProgress is true', async () => {
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
@@ -338,8 +363,8 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when fetchTags is true and showProgress is true', async () => {
|
it('should call execGit with the correct arguments when showProgress is true and refSpec includes tags', async () => {
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
@@ -349,10 +374,9 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
lfs,
|
lfs,
|
||||||
doSparseCheckout
|
doSparseCheckout
|
||||||
)
|
)
|
||||||
const refSpec = ['refspec1', 'refspec2']
|
const refSpec = ['refspec1', 'refspec2', '+refs/tags/*:refs/tags/*']
|
||||||
const options = {
|
const options = {
|
||||||
filter: 'filterValue',
|
filter: 'filterValue',
|
||||||
fetchTags: true,
|
|
||||||
showProgress: true
|
showProgress: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,15 +388,187 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
'-c',
|
'-c',
|
||||||
'protocol.version=2',
|
'protocol.version=2',
|
||||||
'fetch',
|
'fetch',
|
||||||
|
'--no-tags',
|
||||||
'--prune',
|
'--prune',
|
||||||
'--no-recurse-submodules',
|
'--no-recurse-submodules',
|
||||||
'--progress',
|
'--progress',
|
||||||
'--filter=filterValue',
|
'--filter=filterValue',
|
||||||
'origin',
|
'origin',
|
||||||
'refspec1',
|
'refspec1',
|
||||||
'refspec2'
|
'refspec2',
|
||||||
|
'+refs/tags/*:refs/tags/*'
|
||||||
],
|
],
|
||||||
expect.any(Object)
|
expect.any(Object)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('repository initialization object format', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
mockFileExistsSync.mockReset()
|
||||||
|
mockDirectoryExistsSync.mockReset()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('initializes SHA-256 repositories with the matching object format', async () => {
|
||||||
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('git version 2.50.1'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
|
git = await commandManager.createCommandManager('test', false, false)
|
||||||
|
|
||||||
|
await git.init('sha256')
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
['init', '--object-format=sha256', 'test'],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('initializes SHA-1 repositories with existing default arguments', async () => {
|
||||||
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('git version 2.50.1'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
|
git = await commandManager.createCommandManager('test', false, false)
|
||||||
|
|
||||||
|
await git.init('sha1')
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
['init', 'test'],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('git user-agent with orchestration ID', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
mockFileExistsSync.mockReset()
|
||||||
|
mockDirectoryExistsSync.mockReset()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
// Clean up environment variable to prevent test pollution
|
||||||
|
delete process.env['ACTIONS_ORCHESTRATION_ID']
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should include orchestration ID in user-agent when ACTIONS_ORCHESTRATION_ID is set', async () => {
|
||||||
|
const orchId = 'test-orch-id-12345'
|
||||||
|
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
|
||||||
|
|
||||||
|
let capturedEnv: any = null
|
||||||
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
|
}
|
||||||
|
// Capture env on any command
|
||||||
|
capturedEnv = options.env
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call a git command to trigger env capture after user-agent is set
|
||||||
|
await git.init()
|
||||||
|
|
||||||
|
// Verify the user agent includes the orchestration ID
|
||||||
|
expect(git).toBeDefined()
|
||||||
|
expect(capturedEnv).toBeDefined()
|
||||||
|
expect(capturedEnv['GIT_HTTP_USER_AGENT']).toBe(
|
||||||
|
`git/2.18 (github-actions-checkout) actions_orchestration_id/${orchId}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should sanitize invalid characters in orchestration ID', async () => {
|
||||||
|
const orchId = 'test (with) special/chars'
|
||||||
|
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
|
||||||
|
|
||||||
|
let capturedEnv: any = null
|
||||||
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
|
}
|
||||||
|
// Capture env on any command
|
||||||
|
capturedEnv = options.env
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call a git command to trigger env capture after user-agent is set
|
||||||
|
await git.init()
|
||||||
|
|
||||||
|
// Verify the user agent has sanitized orchestration ID (spaces, parentheses, slash replaced)
|
||||||
|
expect(git).toBeDefined()
|
||||||
|
expect(capturedEnv).toBeDefined()
|
||||||
|
expect(capturedEnv['GIT_HTTP_USER_AGENT']).toBe(
|
||||||
|
'git/2.18 (github-actions-checkout) actions_orchestration_id/test__with__special_chars'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not modify user-agent when ACTIONS_ORCHESTRATION_ID is not set', async () => {
|
||||||
|
delete process.env['ACTIONS_ORCHESTRATION_ID']
|
||||||
|
|
||||||
|
let capturedEnv: any = null
|
||||||
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
|
}
|
||||||
|
// Capture env on any command
|
||||||
|
capturedEnv = options.env
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call a git command to trigger env capture after user-agent is set
|
||||||
|
await git.init()
|
||||||
|
|
||||||
|
// Verify the user agent does NOT contain orchestration ID
|
||||||
|
expect(git).toBeDefined()
|
||||||
|
expect(capturedEnv).toBeDefined()
|
||||||
|
expect(capturedEnv['GIT_HTTP_USER_AGENT']).toBe(
|
||||||
|
'git/2.18 (github-actions-checkout)'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,9 +1,36 @@
|
|||||||
import * as core from '@actions/core'
|
import {
|
||||||
|
jest,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
afterEach
|
||||||
|
} from '@jest/globals'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as gitDirectoryHelper from '../lib/git-directory-helper'
|
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {IGitCommandManager} from '../lib/git-command-manager'
|
import {fileURLToPath} from 'url'
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
// Mock @actions/core before loading git-directory-helper
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
error: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core')
|
||||||
|
const gitDirectoryHelper = await import('../src/git-directory-helper.js')
|
||||||
|
|
||||||
|
type IGitCommandManager =
|
||||||
|
import('../src/git-command-manager.js').IGitCommandManager
|
||||||
|
|
||||||
const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
|
const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
|
||||||
let repositoryPath: string
|
let repositoryPath: string
|
||||||
@@ -19,16 +46,11 @@ describe('git-directory-helper tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Mock error/warning/info/debug
|
jest.clearAllMocks()
|
||||||
jest.spyOn(core, 'error').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'info').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// Unregister mocks
|
jest.clearAllMocks()
|
||||||
jest.restoreAllMocks()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const cleansWhenCleanTrue = 'cleans when clean true'
|
const cleansWhenCleanTrue = 'cleans when clean true'
|
||||||
@@ -81,7 +103,7 @@ describe('git-directory-helper tests', () => {
|
|||||||
// Arrange
|
// Arrange
|
||||||
await setup(doesNotCheckoutDetachWhenNotAlreadyDetached)
|
await setup(doesNotCheckoutDetachWhenNotAlreadyDetached)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
const mockIsDetached = git.isDetached as jest.Mock<any, any>
|
const mockIsDetached = git.isDetached as jest.Mock<any>
|
||||||
mockIsDetached.mockImplementation(async () => {
|
mockIsDetached.mockImplementation(async () => {
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@@ -132,7 +154,7 @@ describe('git-directory-helper tests', () => {
|
|||||||
// Arrange
|
// Arrange
|
||||||
await setup(removesContentsWhenCleanFails)
|
await setup(removesContentsWhenCleanFails)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
let mockTryClean = git.tryClean as jest.Mock<any, any>
|
let mockTryClean = git.tryClean as jest.Mock<any>
|
||||||
mockTryClean.mockImplementation(async () => {
|
mockTryClean.mockImplementation(async () => {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@@ -210,7 +232,7 @@ describe('git-directory-helper tests', () => {
|
|||||||
// Arrange
|
// Arrange
|
||||||
await setup(removesContentsWhenResetFails)
|
await setup(removesContentsWhenResetFails)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
let mockTryReset = git.tryReset as jest.Mock<any, any>
|
let mockTryReset = git.tryReset as jest.Mock<any>
|
||||||
mockTryReset.mockImplementation(async () => {
|
mockTryReset.mockImplementation(async () => {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@@ -260,7 +282,7 @@ describe('git-directory-helper tests', () => {
|
|||||||
// Arrange
|
// Arrange
|
||||||
await setup(removesLocalBranches)
|
await setup(removesLocalBranches)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
const mockBranchList = git.branchList as jest.Mock<any, any>
|
const mockBranchList = git.branchList as jest.Mock<any>
|
||||||
mockBranchList.mockImplementation(async (remote: boolean) => {
|
mockBranchList.mockImplementation(async (remote: boolean) => {
|
||||||
return remote ? [] : ['local-branch-1', 'local-branch-2']
|
return remote ? [] : ['local-branch-1', 'local-branch-2']
|
||||||
})
|
})
|
||||||
@@ -291,7 +313,7 @@ describe('git-directory-helper tests', () => {
|
|||||||
|
|
||||||
//mock bad submodule
|
//mock bad submodule
|
||||||
|
|
||||||
const submoduleStatus = git.submoduleStatus as jest.Mock<any, any>
|
const submoduleStatus = git.submoduleStatus as jest.Mock<any>
|
||||||
submoduleStatus.mockImplementation(async (remote: boolean) => {
|
submoduleStatus.mockImplementation(async (remote: boolean) => {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@@ -319,7 +341,7 @@ describe('git-directory-helper tests', () => {
|
|||||||
await setup(doesNotCleanWhenSubmoduleStatusIsTrue)
|
await setup(doesNotCleanWhenSubmoduleStatusIsTrue)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
|
|
||||||
const submoduleStatus = git.submoduleStatus as jest.Mock<any, any>
|
const submoduleStatus = git.submoduleStatus as jest.Mock<any>
|
||||||
submoduleStatus.mockImplementation(async (remote: boolean) => {
|
submoduleStatus.mockImplementation(async (remote: boolean) => {
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@@ -381,7 +403,7 @@ describe('git-directory-helper tests', () => {
|
|||||||
// Arrange
|
// Arrange
|
||||||
await setup(removesAncestorRemoteBranch)
|
await setup(removesAncestorRemoteBranch)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
const mockBranchList = git.branchList as jest.Mock<any, any>
|
const mockBranchList = git.branchList as jest.Mock<any>
|
||||||
mockBranchList.mockImplementation(async (remote: boolean) => {
|
mockBranchList.mockImplementation(async (remote: boolean) => {
|
||||||
return remote ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : []
|
return remote ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : []
|
||||||
})
|
})
|
||||||
@@ -411,7 +433,7 @@ describe('git-directory-helper tests', () => {
|
|||||||
// Arrange
|
// Arrange
|
||||||
await setup(removesDescendantRemoteBranches)
|
await setup(removesDescendantRemoteBranches)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
const mockBranchList = git.branchList as jest.Mock<any, any>
|
const mockBranchList = git.branchList as jest.Mock<any>
|
||||||
mockBranchList.mockImplementation(async (remote: boolean) => {
|
mockBranchList.mockImplementation(async (remote: boolean) => {
|
||||||
return remote
|
return remote
|
||||||
? ['origin/remote-branch-1/conflict', 'origin/remote-branch-2']
|
? ['origin/remote-branch-1/conflict', 'origin/remote-branch-2']
|
||||||
@@ -471,6 +493,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
configExists: jest.fn(),
|
configExists: jest.fn(),
|
||||||
fetch: jest.fn(),
|
fetch: jest.fn(),
|
||||||
getDefaultBranch: jest.fn(),
|
getDefaultBranch: jest.fn(),
|
||||||
|
getSubmoduleConfigPaths: jest.fn(async () => []),
|
||||||
getWorkingDirectory: jest.fn(() => repositoryPath),
|
getWorkingDirectory: jest.fn(() => repositoryPath),
|
||||||
init: jest.fn(),
|
init: jest.fn(),
|
||||||
isDetached: jest.fn(),
|
isDetached: jest.fn(),
|
||||||
@@ -493,27 +516,18 @@ async function setup(testName: string): Promise<void> {
|
|||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
tryConfigUnset: jest.fn(),
|
tryConfigUnset: jest.fn(),
|
||||||
|
tryConfigUnsetValue: jest.fn(),
|
||||||
tryDisableAutomaticGarbageCollection: jest.fn(),
|
tryDisableAutomaticGarbageCollection: jest.fn(),
|
||||||
tryGetFetchUrl: jest.fn(async () => {
|
tryGetFetchUrl: jest.fn(async () => {
|
||||||
// Sanity check - this function shouldn't be called when the .git directory doesn't exist
|
// Sanity check - this function shouldn't be called when the .git directory doesn't exist
|
||||||
await fs.promises.stat(path.join(repositoryPath, '.git'))
|
await fs.promises.stat(path.join(repositoryPath, '.git'))
|
||||||
return repositoryUrl
|
return repositoryUrl
|
||||||
}),
|
}),
|
||||||
getSubmoduleConfigPaths: jest.fn(async () => {
|
tryGetConfigValues: jest.fn(),
|
||||||
return []
|
tryGetConfigKeys: jest.fn(),
|
||||||
}),
|
|
||||||
tryConfigUnsetValue: jest.fn(async () => {
|
|
||||||
return true
|
|
||||||
}),
|
|
||||||
tryGetConfigValues: jest.fn(async () => {
|
|
||||||
return []
|
|
||||||
}),
|
|
||||||
tryGetConfigKeys: jest.fn(async () => {
|
|
||||||
return []
|
|
||||||
}),
|
|
||||||
tryReset: jest.fn(async () => {
|
tryReset: jest.fn(async () => {
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
version: jest.fn()
|
version: jest.fn()
|
||||||
}
|
} as unknown as IGitCommandManager
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {GitVersion} from '../src/git-version'
|
import {describe, it, expect} from '@jest/globals'
|
||||||
import {MinimumGitSparseCheckoutVersion} from '../src/git-command-manager'
|
import {GitVersion} from '../src/git-version.js'
|
||||||
|
import {MinimumGitSparseCheckoutVersion} from '../src/git-command-manager.js'
|
||||||
|
|
||||||
describe('git-version tests', () => {
|
describe('git-version tests', () => {
|
||||||
it('basics', async () => {
|
it('basics', async () => {
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals'
|
||||||
|
|
||||||
|
// Mock @actions/core
|
||||||
|
const mockDebug = jest.fn()
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
debug: mockDebug,
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
error: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Mock @actions/github
|
||||||
|
const mockGetOctokit = jest.fn()
|
||||||
|
jest.unstable_mockModule('@actions/github', () => ({
|
||||||
|
getOctokit: mockGetOctokit
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const githubApiHelper = await import('../src/github-api-helper.js')
|
||||||
|
|
||||||
|
describe('github-api-helper object format', () => {
|
||||||
|
let request: jest.Mock<any>
|
||||||
|
|
||||||
|
function mockHashAlgorithmApi(hashAlgorithm: string): void {
|
||||||
|
request = jest.fn(async () => ({
|
||||||
|
data: {
|
||||||
|
hash_algorithm: hashAlgorithm
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
mockGetOctokit.mockReturnValue({
|
||||||
|
request
|
||||||
|
} as any)
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockDebug.mockClear()
|
||||||
|
mockGetOctokit.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('detects SHA-256 from the repository hash algorithm endpoint', async () => {
|
||||||
|
mockHashAlgorithmApi('sha256')
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
|
||||||
|
).resolves.toEqual({format: 'sha256', succeeded: true})
|
||||||
|
|
||||||
|
expect(mockGetOctokit).toHaveBeenCalledWith(
|
||||||
|
'token',
|
||||||
|
expect.objectContaining({baseUrl: 'https://api.github.com'})
|
||||||
|
)
|
||||||
|
expect(request).toHaveBeenCalledWith(
|
||||||
|
'GET /repos/{owner}/{repo}/hash-algorithm',
|
||||||
|
{owner: 'owner', repo: 'repo'}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('detects SHA-1 from the repository hash algorithm endpoint', async () => {
|
||||||
|
mockHashAlgorithmApi('sha1')
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
|
||||||
|
).resolves.toEqual({format: 'sha1', succeeded: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('detects object format from an existing commit without API calls', async () => {
|
||||||
|
const commitSha =
|
||||||
|
'9422233ca7ee1b17f1e905d0e141faf0c401556c41cdc6acd71c6bd685da2e92'
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
githubApiHelper.tryGetRepositoryObjectFormat(
|
||||||
|
'token',
|
||||||
|
'owner',
|
||||||
|
'repo',
|
||||||
|
undefined,
|
||||||
|
commitSha
|
||||||
|
)
|
||||||
|
).resolves.toEqual({format: 'sha256', succeeded: true})
|
||||||
|
|
||||||
|
expect(mockGetOctokit).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns unsuccessful when the hash algorithm endpoint value is not recognized', async () => {
|
||||||
|
mockHashAlgorithmApi('unknown')
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
|
||||||
|
).resolves.toEqual({format: '', succeeded: false})
|
||||||
|
expect(mockDebug).toHaveBeenCalledWith(
|
||||||
|
'Unable to determine repository object format from hash-algorithm endpoint'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns unsuccessful when the hash algorithm API lookup fails', async () => {
|
||||||
|
request = jest.fn(async () => {
|
||||||
|
throw new Error('not found')
|
||||||
|
})
|
||||||
|
mockGetOctokit.mockReturnValue({
|
||||||
|
request
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
|
||||||
|
).resolves.toEqual({format: '', succeeded: false})
|
||||||
|
expect(mockDebug).toHaveBeenCalledWith(
|
||||||
|
'Unable to determine repository object format from hash-algorithm endpoint: not found'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
+103
-60
@@ -1,10 +1,13 @@
|
|||||||
import * as core from '@actions/core'
|
import {
|
||||||
import * as fsHelper from '../lib/fs-helper'
|
jest,
|
||||||
import * as github from '@actions/github'
|
describe,
|
||||||
import * as inputHelper from '../lib/input-helper'
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as workflowContextHelper from '../lib/workflow-context-helper'
|
|
||||||
import {IGitSourceSettings} from '../lib/git-source-settings'
|
|
||||||
|
|
||||||
const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE']
|
const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE']
|
||||||
const gitHubWorkspace = path.resolve('/checkout-tests/workspace')
|
const gitHubWorkspace = path.resolve('/checkout-tests/workspace')
|
||||||
@@ -12,6 +15,15 @@ const gitHubWorkspace = path.resolve('/checkout-tests/workspace')
|
|||||||
// Inputs for mock @actions/core
|
// Inputs for mock @actions/core
|
||||||
let inputs = {} as any
|
let inputs = {} as any
|
||||||
|
|
||||||
|
// Mutable mock github context
|
||||||
|
const mockGithubContext: any = {
|
||||||
|
ref: 'refs/heads/some-ref',
|
||||||
|
sha: '1234567890123456789012345678901234567890',
|
||||||
|
repo: {owner: 'some-owner', repo: 'some-repo'},
|
||||||
|
eventName: '',
|
||||||
|
payload: {}
|
||||||
|
}
|
||||||
|
|
||||||
// Replicate @actions/core getInput behavior: it trims whitespace by default
|
// Replicate @actions/core getInput behavior: it trims whitespace by default
|
||||||
// (String.prototype.trim(), which strips characters such as a leading U+FEFF BOM)
|
// (String.prototype.trim(), which strips characters such as a leading U+FEFF BOM)
|
||||||
// unless trimWhitespace is explicitly set to false.
|
// unless trimWhitespace is explicitly set to false.
|
||||||
@@ -23,40 +35,49 @@ const getInputImpl = (name: string, options?: {trimWhitespace?: boolean}) => {
|
|||||||
return typeof val === 'string' ? val.trim() : val
|
return typeof val === 'string' ? val.trim() : val
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shallow clone original @actions/github context
|
// Mock @actions/core before loading input-helper
|
||||||
let originalContext = {...github.context}
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
getInput: jest.fn(getInputImpl),
|
||||||
|
getBooleanInput: jest.fn((name: string) => inputs[name]),
|
||||||
|
getMultilineInput: jest.fn((name: string) =>
|
||||||
|
inputs[name] ? String(inputs[name]).split('\n').filter(Boolean) : []
|
||||||
|
),
|
||||||
|
error: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
setSecret: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Mock @actions/github before loading input-helper
|
||||||
|
jest.unstable_mockModule('@actions/github', () => ({
|
||||||
|
context: mockGithubContext,
|
||||||
|
getOctokit: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Mock fs-helper
|
||||||
|
const mockDirectoryExistsSync = jest.fn((p: string) => p === gitHubWorkspace)
|
||||||
|
jest.unstable_mockModule('../src/fs-helper.js', () => ({
|
||||||
|
directoryExistsSync: mockDirectoryExistsSync,
|
||||||
|
fileExistsSync: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Mock workflow-context-helper
|
||||||
|
const mockGetOrganizationId = jest.fn(async () => 123456)
|
||||||
|
jest.unstable_mockModule('../src/workflow-context-helper.js', () => ({
|
||||||
|
getOrganizationId: mockGetOrganizationId
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core')
|
||||||
|
const inputHelper = await import('../src/input-helper.js')
|
||||||
|
type IGitSourceSettings =
|
||||||
|
import('../src/git-source-settings.js').IGitSourceSettings
|
||||||
|
|
||||||
describe('input-helper tests', () => {
|
describe('input-helper tests', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
// Mock getInput
|
|
||||||
jest.spyOn(core, 'getInput').mockImplementation(getInputImpl as any)
|
|
||||||
|
|
||||||
// Mock error/warning/info/debug
|
|
||||||
jest.spyOn(core, 'error').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'info').mockImplementation(jest.fn())
|
|
||||||
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
|
|
||||||
|
|
||||||
// Mock github context
|
|
||||||
jest.spyOn(github.context, 'repo', 'get').mockImplementation(() => {
|
|
||||||
return {
|
|
||||||
owner: 'some-owner',
|
|
||||||
repo: 'some-repo'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
github.context.ref = 'refs/heads/some-ref'
|
|
||||||
github.context.sha = '1234567890123456789012345678901234567890'
|
|
||||||
|
|
||||||
// Mock ./fs-helper directoryExistsSync()
|
|
||||||
jest
|
|
||||||
.spyOn(fsHelper, 'directoryExistsSync')
|
|
||||||
.mockImplementation((path: string) => path == gitHubWorkspace)
|
|
||||||
|
|
||||||
// Mock ./workflowContextHelper getOrganizationId()
|
|
||||||
jest
|
|
||||||
.spyOn(workflowContextHelper, 'getOrganizationId')
|
|
||||||
.mockImplementation(() => Promise.resolve(123456))
|
|
||||||
|
|
||||||
// GitHub workspace
|
// GitHub workspace
|
||||||
process.env['GITHUB_WORKSPACE'] = gitHubWorkspace
|
process.env['GITHUB_WORKSPACE'] = gitHubWorkspace
|
||||||
})
|
})
|
||||||
@@ -64,6 +85,13 @@ describe('input-helper tests', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Reset inputs
|
// Reset inputs
|
||||||
inputs = {}
|
inputs = {}
|
||||||
|
jest.clearAllMocks()
|
||||||
|
// Re-apply default mocks
|
||||||
|
;(core.getInput as jest.Mock<any>).mockImplementation(getInputImpl as any)
|
||||||
|
mockDirectoryExistsSync.mockImplementation(
|
||||||
|
(p: string) => p === gitHubWorkspace
|
||||||
|
)
|
||||||
|
mockGetOrganizationId.mockResolvedValue(123456)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
@@ -74,11 +102,8 @@ describe('input-helper tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restore @actions/github context
|
// Restore @actions/github context
|
||||||
github.context.ref = originalContext.ref
|
mockGithubContext.ref = 'refs/heads/some-ref'
|
||||||
github.context.sha = originalContext.sha
|
mockGithubContext.sha = '1234567890123456789012345678901234567890'
|
||||||
|
|
||||||
// Restore
|
|
||||||
jest.restoreAllMocks()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets defaults', async () => {
|
it('sets defaults', async () => {
|
||||||
@@ -104,15 +129,15 @@ describe('input-helper tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('qualifies ref', async () => {
|
it('qualifies ref', async () => {
|
||||||
let originalRef = github.context.ref
|
let originalRef = mockGithubContext.ref
|
||||||
try {
|
try {
|
||||||
github.context.ref = 'some-unqualified-ref'
|
mockGithubContext.ref = 'some-unqualified-ref'
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
||||||
expect(settings).toBeTruthy()
|
expect(settings).toBeTruthy()
|
||||||
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
|
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
|
||||||
expect(settings.ref).toBe('refs/heads/some-unqualified-ref')
|
expect(settings.ref).toBe('refs/heads/some-unqualified-ref')
|
||||||
} finally {
|
} finally {
|
||||||
github.context.ref = originalRef
|
mockGithubContext.ref = originalRef
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -143,6 +168,16 @@ describe('input-helper tests', () => {
|
|||||||
expect(settings.commit).toBe('1111111111222222222233333333334444444444')
|
expect(settings.commit).toBe('1111111111222222222233333333334444444444')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('sets ref to empty when explicit sha-256', async () => {
|
||||||
|
inputs.ref =
|
||||||
|
'1111111111222222222233333333334444444444555555555566666666667777'
|
||||||
|
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
||||||
|
expect(settings.ref).toBeFalsy()
|
||||||
|
expect(settings.commit).toBe(
|
||||||
|
'1111111111222222222233333333334444444444555555555566666666667777'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('sets sha to empty when explicit ref', async () => {
|
it('sets sha to empty when explicit ref', async () => {
|
||||||
inputs.ref = 'refs/heads/some-other-ref'
|
inputs.ref = 'refs/heads/some-other-ref'
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
||||||
@@ -160,6 +195,16 @@ describe('input-helper tests', () => {
|
|||||||
expect(settings.ref).toBe('522d932fae5296da51fdf431934425ecf891c6a2')
|
expect(settings.ref).toBe('522d932fae5296da51fdf431934425ecf891c6a2')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('does not reclassify a sha-256 ref as sha when a BOM is prefixed', async () => {
|
||||||
|
inputs.ref =
|
||||||
|
'\uFEFF1111111111222222222233333333334444444444555555555566666666667777'
|
||||||
|
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
||||||
|
expect(settings.commit).toBeFalsy()
|
||||||
|
expect(settings.ref).toBe(
|
||||||
|
'1111111111222222222233333333334444444444555555555566666666667777'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('treats a sha surrounded by ascii whitespace as a commit', async () => {
|
it('treats a sha surrounded by ascii whitespace as a commit', async () => {
|
||||||
// ASCII whitespace can only come from the workflow author's YAML (git ref
|
// ASCII whitespace can only come from the workflow author's YAML (git ref
|
||||||
// names cannot contain it), so trimming it and treating the value as a
|
// names cannot contain it), so trimming it and treating the value as a
|
||||||
@@ -188,37 +233,35 @@ describe('input-helper tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it('allows the default self-checkout on a fork pull_request_target', async () => {
|
it('allows the default self-checkout on a fork pull_request_target', async () => {
|
||||||
const originalEvent = github.context.eventName
|
const originalEvent = mockGithubContext.eventName
|
||||||
const originalPayload = github.context.payload
|
const originalPayload = mockGithubContext.payload
|
||||||
const originalSha = github.context.sha
|
|
||||||
try {
|
try {
|
||||||
github.context.eventName = 'pull_request_target'
|
mockGithubContext.eventName = 'pull_request_target'
|
||||||
github.context.payload = forkPayload as any
|
mockGithubContext.payload = forkPayload
|
||||||
// Simulate a rebase/fast-forward merge where the base tip (event SHA)
|
// Simulate a rebase/fast-forward merge where the base tip (event SHA)
|
||||||
// equals the PR head SHA. The default self-checkout must still succeed.
|
// equals the PR head SHA. The default self-checkout must still succeed.
|
||||||
github.context.sha = '1234567890123456789012345678901234567890'
|
mockGithubContext.sha = '1234567890123456789012345678901234567890'
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
||||||
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
|
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
|
||||||
} finally {
|
} finally {
|
||||||
github.context.eventName = originalEvent
|
mockGithubContext.eventName = originalEvent
|
||||||
github.context.payload = originalPayload
|
mockGithubContext.payload = originalPayload
|
||||||
github.context.sha = originalSha
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('refuses an explicit fork repository on pull_request_target', async () => {
|
it('refuses an explicit fork repository on pull_request_target', async () => {
|
||||||
const originalEvent = github.context.eventName
|
const originalEvent = mockGithubContext.eventName
|
||||||
const originalPayload = github.context.payload
|
const originalPayload = mockGithubContext.payload
|
||||||
try {
|
try {
|
||||||
github.context.eventName = 'pull_request_target'
|
mockGithubContext.eventName = 'pull_request_target'
|
||||||
github.context.payload = forkPayload as any
|
mockGithubContext.payload = forkPayload
|
||||||
inputs.repository = 'attacker/fork'
|
inputs.repository = 'attacker/fork'
|
||||||
await expect(inputHelper.getInputs()).rejects.toThrow(
|
await expect(inputHelper.getInputs()).rejects.toThrow(
|
||||||
/Refusing to check out fork pull request code/
|
/Refusing to check out fork pull request code/
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
github.context.eventName = originalEvent
|
mockGithubContext.eventName = originalEvent
|
||||||
github.context.payload = originalPayload
|
mockGithubContext.payload = originalPayload
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+213
-3
@@ -1,13 +1,46 @@
|
|||||||
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals'
|
||||||
import * as assert from 'assert'
|
import * as assert from 'assert'
|
||||||
import * as refHelper from '../lib/ref-helper'
|
|
||||||
import {IGitCommandManager} from '../lib/git-command-manager'
|
// Mutable mock github context
|
||||||
|
const mockGithubContext: any = {
|
||||||
|
eventName: '',
|
||||||
|
payload: {},
|
||||||
|
repo: {owner: 'some-owner', repo: 'some-repo'},
|
||||||
|
ref: '',
|
||||||
|
sha: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock @actions/core
|
||||||
|
const mockDebug = jest.fn()
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
debug: mockDebug,
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
setFailed: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Mock @actions/github
|
||||||
|
const mockGetOctokit = jest.fn()
|
||||||
|
jest.unstable_mockModule('@actions/github', () => ({
|
||||||
|
context: mockGithubContext,
|
||||||
|
getOctokit: mockGetOctokit
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const refHelper = await import('../src/ref-helper.js')
|
||||||
|
type IGitCommandManager =
|
||||||
|
import('../src/git-command-manager.js').IGitCommandManager
|
||||||
|
|
||||||
const commit = '1234567890123456789012345678901234567890'
|
const commit = '1234567890123456789012345678901234567890'
|
||||||
|
const sha256Commit =
|
||||||
|
'1234567890123456789012345678901234567890123456789012345678901234'
|
||||||
let git: IGitCommandManager
|
let git: IGitCommandManager
|
||||||
|
|
||||||
describe('ref-helper tests', () => {
|
describe('ref-helper tests', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
git = {} as unknown as IGitCommandManager
|
git = {} as unknown as IGitCommandManager
|
||||||
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('getCheckoutInfo requires git', async () => {
|
it('getCheckoutInfo requires git', async () => {
|
||||||
@@ -37,6 +70,12 @@ describe('ref-helper tests', () => {
|
|||||||
expect(checkoutInfo.startPoint).toBeFalsy()
|
expect(checkoutInfo.startPoint).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('getCheckoutInfo sha-256 only', async () => {
|
||||||
|
const checkoutInfo = await refHelper.getCheckoutInfo(git, '', sha256Commit)
|
||||||
|
expect(checkoutInfo.ref).toBe(sha256Commit)
|
||||||
|
expect(checkoutInfo.startPoint).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
it('getCheckoutInfo refs/heads/', async () => {
|
it('getCheckoutInfo refs/heads/', async () => {
|
||||||
const checkoutInfo = await refHelper.getCheckoutInfo(
|
const checkoutInfo = await refHelper.getCheckoutInfo(
|
||||||
git,
|
git,
|
||||||
@@ -152,7 +191,20 @@ describe('ref-helper tests', () => {
|
|||||||
it('getRefSpec sha + refs/tags/', async () => {
|
it('getRefSpec sha + refs/tags/', async () => {
|
||||||
const refSpec = refHelper.getRefSpec('refs/tags/my-tag', commit)
|
const refSpec = refHelper.getRefSpec('refs/tags/my-tag', commit)
|
||||||
expect(refSpec.length).toBe(1)
|
expect(refSpec.length).toBe(1)
|
||||||
expect(refSpec[0]).toBe(`+${commit}:refs/tags/my-tag`)
|
expect(refSpec[0]).toBe(`+refs/tags/my-tag:refs/tags/my-tag`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getRefSpec sha + refs/tags/ with fetchTags', async () => {
|
||||||
|
const refSpec = refHelper.getRefSpec('refs/tags/my-tag', commit, true)
|
||||||
|
expect(refSpec.length).toBe(1)
|
||||||
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getRefSpec sha + refs/heads/ with fetchTags', async () => {
|
||||||
|
const refSpec = refHelper.getRefSpec('refs/heads/my/branch', commit, true)
|
||||||
|
expect(refSpec.length).toBe(2)
|
||||||
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
|
expect(refSpec[1]).toBe(`+${commit}:refs/remotes/origin/my/branch`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('getRefSpec sha only', async () => {
|
it('getRefSpec sha only', async () => {
|
||||||
@@ -168,6 +220,13 @@ describe('ref-helper tests', () => {
|
|||||||
expect(refSpec[1]).toBe('+refs/tags/my-ref*:refs/tags/my-ref*')
|
expect(refSpec[1]).toBe('+refs/tags/my-ref*:refs/tags/my-ref*')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('getRefSpec unqualified ref only with fetchTags', async () => {
|
||||||
|
const refSpec = refHelper.getRefSpec('my-ref', '', true)
|
||||||
|
expect(refSpec.length).toBe(2)
|
||||||
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
|
expect(refSpec[1]).toBe('+refs/heads/my-ref*:refs/remotes/origin/my-ref*')
|
||||||
|
})
|
||||||
|
|
||||||
it('getRefSpec refs/heads/ only', async () => {
|
it('getRefSpec refs/heads/ only', async () => {
|
||||||
const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '')
|
const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '')
|
||||||
expect(refSpec.length).toBe(1)
|
expect(refSpec.length).toBe(1)
|
||||||
@@ -187,4 +246,155 @@ describe('ref-helper tests', () => {
|
|||||||
expect(refSpec.length).toBe(1)
|
expect(refSpec.length).toBe(1)
|
||||||
expect(refSpec[0]).toBe('+refs/tags/my-tag:refs/tags/my-tag')
|
expect(refSpec[0]).toBe('+refs/tags/my-tag:refs/tags/my-tag')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('getRefSpec refs/tags/ only with fetchTags', async () => {
|
||||||
|
const refSpec = refHelper.getRefSpec('refs/tags/my-tag', '', true)
|
||||||
|
expect(refSpec.length).toBe(1)
|
||||||
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getRefSpec refs/heads/ only with fetchTags', async () => {
|
||||||
|
const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '', true)
|
||||||
|
expect(refSpec.length).toBe(2)
|
||||||
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
|
expect(refSpec[1]).toBe(
|
||||||
|
'+refs/heads/my/branch:refs/remotes/origin/my/branch'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('checkCommitInfo', () => {
|
||||||
|
const repositoryOwner = 'some-owner'
|
||||||
|
const repositoryName = 'some-repo'
|
||||||
|
const ref = 'refs/pull/123/merge'
|
||||||
|
const sha1Head = '1111111111222222222233333333334444444444'
|
||||||
|
const sha1Base = 'aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd'
|
||||||
|
const sha256Head =
|
||||||
|
'1111111111222222222233333333334444444444555555555566666666667777'
|
||||||
|
const sha256Base =
|
||||||
|
'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff0000'
|
||||||
|
let repoGetSpy: jest.Mock<any>
|
||||||
|
let originalEventName: string
|
||||||
|
let originalPayload: unknown
|
||||||
|
let originalRef: string
|
||||||
|
let originalSha: string
|
||||||
|
|
||||||
|
function setPullRequestContext(
|
||||||
|
expectedHeadSha: string,
|
||||||
|
expectedBaseSha: string,
|
||||||
|
mergeCommit: string
|
||||||
|
): void {
|
||||||
|
mockGithubContext.eventName = 'pull_request'
|
||||||
|
mockGithubContext.ref = ref
|
||||||
|
mockGithubContext.sha = mergeCommit
|
||||||
|
mockGithubContext.payload = {
|
||||||
|
action: 'synchronize',
|
||||||
|
after: expectedHeadSha,
|
||||||
|
number: 123,
|
||||||
|
pull_request: {
|
||||||
|
base: {
|
||||||
|
sha: expectedBaseSha
|
||||||
|
}
|
||||||
|
},
|
||||||
|
repository: {
|
||||||
|
private: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalEventName = mockGithubContext.eventName
|
||||||
|
originalPayload = mockGithubContext.payload
|
||||||
|
originalRef = mockGithubContext.ref
|
||||||
|
originalSha = mockGithubContext.sha
|
||||||
|
|
||||||
|
mockGithubContext.repo = {
|
||||||
|
owner: repositoryOwner,
|
||||||
|
repo: repositoryName
|
||||||
|
}
|
||||||
|
|
||||||
|
repoGetSpy = jest.fn(async () => ({}))
|
||||||
|
mockGetOctokit.mockReturnValue({
|
||||||
|
rest: {
|
||||||
|
repos: {
|
||||||
|
get: repoGetSpy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as any)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mockGithubContext.eventName = originalEventName
|
||||||
|
mockGithubContext.payload = originalPayload
|
||||||
|
mockGithubContext.ref = originalRef
|
||||||
|
mockGithubContext.sha = originalSha
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns early for SHA-1 merge commit', async () => {
|
||||||
|
setPullRequestContext(sha1Head, sha1Base, commit)
|
||||||
|
|
||||||
|
await refHelper.checkCommitInfo(
|
||||||
|
'token',
|
||||||
|
`Merge ${sha1Head} into ${sha1Base}`,
|
||||||
|
repositoryOwner,
|
||||||
|
repositoryName,
|
||||||
|
ref,
|
||||||
|
commit
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(mockGetOctokit).not.toHaveBeenCalled()
|
||||||
|
expect(repoGetSpy).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('matches SHA-256 merge commit info', async () => {
|
||||||
|
const actualHeadSha =
|
||||||
|
'9999999999888888888877777777776666666666555555555544444444443333'
|
||||||
|
setPullRequestContext(sha256Head, sha256Base, sha256Commit)
|
||||||
|
|
||||||
|
await refHelper.checkCommitInfo(
|
||||||
|
'token',
|
||||||
|
`Merge ${actualHeadSha} into ${sha256Base}`,
|
||||||
|
repositoryOwner,
|
||||||
|
repositoryName,
|
||||||
|
ref,
|
||||||
|
sha256Commit
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(mockGetOctokit).toHaveBeenCalledWith(
|
||||||
|
'token',
|
||||||
|
expect.objectContaining({
|
||||||
|
userAgent: expect.stringContaining(
|
||||||
|
`expected_head_sha=${sha256Head};actual_head_sha=${actualHeadSha}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(repoGetSpy).toHaveBeenCalledWith({
|
||||||
|
owner: repositoryOwner,
|
||||||
|
repo: repositoryName
|
||||||
|
})
|
||||||
|
expect(mockDebug).toHaveBeenCalledWith(
|
||||||
|
`Expected head sha ${sha256Head}; actual head sha ${actualHeadSha}`
|
||||||
|
)
|
||||||
|
expect(mockDebug).not.toHaveBeenCalledWith('Unexpected message format')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not match 50-char hex as a valid merge', async () => {
|
||||||
|
const invalidHeadSha =
|
||||||
|
'99999999998888888888777777777766666666665555555555'
|
||||||
|
setPullRequestContext(sha1Head, sha1Base, commit)
|
||||||
|
|
||||||
|
await refHelper.checkCommitInfo(
|
||||||
|
'token',
|
||||||
|
`Merge ${invalidHeadSha} into ${sha1Base}`,
|
||||||
|
repositoryOwner,
|
||||||
|
repositoryName,
|
||||||
|
ref,
|
||||||
|
commit
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(mockGetOctokit).not.toHaveBeenCalled()
|
||||||
|
expect(repoGetSpy).not.toHaveBeenCalled()
|
||||||
|
expect(mockDebug).toHaveBeenCalledWith('Unexpected message format')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,16 +1,32 @@
|
|||||||
import * as core from '@actions/core'
|
import {
|
||||||
import {RetryHelper} from '../lib/retry-helper'
|
jest,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals'
|
||||||
|
|
||||||
|
let info: string[] = []
|
||||||
|
|
||||||
|
// Mock @actions/core before loading retry-helper
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn((message: string) => {
|
||||||
|
info.push(message)
|
||||||
|
}),
|
||||||
|
debug: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
error: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const {RetryHelper} = await import('../src/retry-helper.js')
|
||||||
|
|
||||||
let info: string[]
|
|
||||||
let retryHelper: any
|
let retryHelper: any
|
||||||
|
|
||||||
describe('retry-helper tests', () => {
|
describe('retry-helper tests', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
// Mock @actions/core info()
|
|
||||||
jest.spyOn(core, 'info').mockImplementation((message: string) => {
|
|
||||||
info.push(message)
|
|
||||||
})
|
|
||||||
|
|
||||||
retryHelper = new RetryHelper(3, 0, 0)
|
retryHelper = new RetryHelper(3, 0, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -20,7 +36,6 @@ describe('retry-helper tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
// Restore
|
|
||||||
jest.restoreAllMocks()
|
jest.restoreAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import * as github from '@actions/github'
|
import {
|
||||||
import {assertSafePrCheckout} from '../lib/unsafe-pr-checkout-helper'
|
jest,
|
||||||
|
describe,
|
||||||
// Shallow clone original @actions/github context
|
it,
|
||||||
const originalContext = {...github.context}
|
expect,
|
||||||
const originalEventName = github.context.eventName
|
beforeAll,
|
||||||
const originalPayload = github.context.payload
|
|||||||