Преглед изворни кода

Merge origin/main into aiqiaoy/update-error

copilot-swe-agent[bot] пре 1 недеља
родитељ
комит
29092a1639
63 измењених фајлова са 1343 додато и 2007 уклоњено
  1. 1 1
      .github/workflows/publish-immutable-actions.yml
  2. 2 1
      .licensed.yml
  3. 1 1
      .licenses/npm/@actions/core.dep.yml
  4. 1 1
      .licenses/npm/@actions/exec.dep.yml
  5. 1 1
      .licenses/npm/@actions/github.dep.yml
  6. 2 2
      .licenses/npm/@actions/http-client-3.0.2.dep.yml
  7. 32 0
      .licenses/npm/@actions/http-client-4.0.1.dep.yml
  8. 1 1
      .licenses/npm/@actions/io.dep.yml
  9. 1 1
      .licenses/npm/@actions/tool-cache.dep.yml
  10. 0 30
      .licenses/npm/@fastify/busboy.dep.yml
  11. 1 1
      .licenses/npm/@octokit/auth-token.dep.yml
  12. 1 1
      .licenses/npm/@octokit/core.dep.yml
  13. 2 2
      .licenses/npm/@octokit/endpoint.dep.yml
  14. 1 1
      .licenses/npm/@octokit/graphql.dep.yml
  15. 0 20
      .licenses/npm/@octokit/openapi-types-22.1.0.dep.yml
  16. 3 3
      .licenses/npm/@octokit/openapi-types.dep.yml
  17. 2 2
      .licenses/npm/@octokit/plugin-paginate-rest.dep.yml
  18. 1 1
      .licenses/npm/@octokit/plugin-rest-endpoint-methods.dep.yml
  19. 2 2
      .licenses/npm/@octokit/request-error.dep.yml
  20. 2 2
      .licenses/npm/@octokit/request.dep.yml
  21. 0 20
      .licenses/npm/@octokit/types-13.4.1.dep.yml
  22. 1 1
      .licenses/npm/@octokit/types.dep.yml
  23. 1 1
      .licenses/npm/before-after-hook.dep.yml
  24. 47 0
      .licenses/npm/content-type.dep.yml
  25. 0 28
      .licenses/npm/deprecation.dep.yml
  26. 8 14
      .licenses/npm/json-with-bigint.dep.yml
  27. 0 26
      .licenses/npm/once.dep.yml
  28. 1 1
      .licenses/npm/semver.dep.yml
  29. 1 1
      .licenses/npm/undici.dep.yml
  30. 3 3
      .licenses/npm/universal-user-agent.dep.yml
  31. 0 20
      .licenses/npm/uuid-8.3.2.dep.yml
  32. 0 20
      .licenses/npm/uuid-9.0.1.dep.yml
  33. 0 26
      .licenses/npm/wrappy.dep.yml
  34. 9 0
      CHANGELOG.md
  35. 26 18
      README.md
  36. 48 27
      __test__/git-auth-helper.test.ts
  37. 65 40
      __test__/git-command-manager.test.ts
  38. 41 19
      __test__/git-directory-helper.test.ts
  39. 3 2
      __test__/git-version.test.ts
  40. 29 15
      __test__/github-api-helper.test.ts
  41. 72 47
      __test__/input-helper.test.ts
  42. 57 35
      __test__/ref-helper.test.ts
  43. 24 9
      __test__/retry-helper.test.ts
  44. 36 19
      __test__/unsafe-pr-checkout-helper.test.ts
  45. 2 1
      __test__/url-helper.test.ts
  46. 363 1162
      dist/index.js
  47. 3 0
      dist/package.json
  48. 0 12
      jest.config.js
  49. 24 0
      jest.config.ts
  50. 349 294
      package-lock.json
  51. 17 15
      package.json
  52. 9 9
      src/git-auth-helper.ts
  53. 5 5
      src/git-command-manager.ts
  54. 2 2
      src/git-directory-helper.ts
  55. 10 10
      src/git-source-provider.ts
  56. 4 4
      src/github-api-helper.ts
  57. 4 4
      src/input-helper.ts
  58. 9 9
      src/main.ts
  59. 4 1
      src/misc/generate-docs.ts
  60. 2 2
      src/ref-helper.ts
  61. 1 1
      src/unsafe-pr-checkout-helper.ts
  62. 1 1
      src/url-helper.ts
  63. 5 9
      tsconfig.json

+ 1 - 1
.github/workflows/publish-immutable-actions.yml

@@ -17,4 +17,4 @@ jobs:
         uses: actions/checkout@v6
       - name: Publish
         id: publish
-        uses: actions/publish-immutable-action@0.0.3
+        uses: actions/publish-immutable-action@v0.0.4

+ 2 - 1
.licensed.yml

@@ -11,4 +11,5 @@ allowed:
   - unlicense
 
 reviewed:
-  npm:
+  npm:
+    - "@actions/http-client" # MIT

+ 1 - 1
.licenses/npm/@actions/core.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@actions/core"
-version: 1.10.1
+version: 3.0.1
 type: npm
 summary: Actions core lib
 homepage: https://github.com/actions/toolkit/tree/main/packages/core

+ 1 - 1
.licenses/npm/@actions/exec.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@actions/exec"
-version: 1.1.1
+version: 3.0.0
 type: npm
 summary: Actions exec lib
 homepage: https://github.com/actions/toolkit/tree/main/packages/exec

+ 1 - 1
.licenses/npm/@actions/github.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@actions/github"
-version: 6.0.0
+version: 9.1.1
 type: npm
 summary: Actions github lib
 homepage: https://github.com/actions/toolkit/tree/main/packages/github

+ 2 - 2
.licenses/npm/@actions/http-client.dep.yml → .licenses/npm/@actions/http-client-3.0.2.dep.yml

@@ -1,10 +1,10 @@
 ---
 name: "@actions/http-client"
-version: 2.2.1
+version: 3.0.2
 type: npm
 summary: Actions Http Client
 homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
-license: mit
+license: other
 licenses:
 - sources: LICENSE
   text: |

+ 32 - 0
.licenses/npm/@actions/http-client-4.0.1.dep.yml

@@ -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: []

+ 1 - 1
.licenses/npm/@actions/io.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@actions/io"
-version: 1.1.3
+version: 3.0.2
 type: npm
 summary: Actions io lib
 homepage: https://github.com/actions/toolkit/tree/main/packages/io

+ 1 - 1
.licenses/npm/@actions/tool-cache.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@actions/tool-cache"
-version: 2.0.1
+version: 4.0.0
 type: npm
 summary: Actions tool-cache lib
 homepage: https://github.com/actions/toolkit/tree/main/packages/tool-cache

+ 0 - 30
.licenses/npm/@fastify/busboy.dep.yml

@@ -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
.licenses/npm/@octokit/auth-token.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@octokit/auth-token"
-version: 4.0.0
+version: 6.0.0
 type: npm
 summary: GitHub API token authentication for browsers and Node.js
 homepage: 

+ 1 - 1
.licenses/npm/@octokit/core.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@octokit/core"
-version: 5.2.0
+version: 7.0.6
 type: npm
 summary: Extendable client for GitHub's REST & GraphQL APIs
 homepage: 

+ 2 - 2
.licenses/npm/@octokit/endpoint.dep.yml

@@ -1,9 +1,9 @@
 ---
 name: "@octokit/endpoint"
-version: 9.0.6
+version: 11.0.3
 type: npm
 summary: Turns REST API endpoints into generic request options
-homepage:
+homepage: 
 license: mit
 licenses:
 - sources: LICENSE

+ 1 - 1
.licenses/npm/@octokit/graphql.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@octokit/graphql"
-version: 7.1.0
+version: 9.0.3
 type: npm
 summary: GitHub GraphQL API client for browsers and Node
 homepage: 

+ 0 - 20
.licenses/npm/@octokit/openapi-types-22.1.0.dep.yml

@@ -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: []

+ 3 - 3
.licenses/npm/@octokit/openapi-types-20.0.0.dep.yml → .licenses/npm/@octokit/openapi-types.dep.yml

@@ -1,14 +1,14 @@
 ---
 name: "@octokit/openapi-types"
-version: 20.0.0
+version: 27.0.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
+  text: |
+    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:
 

+ 2 - 2
.licenses/npm/@octokit/plugin-paginate-rest.dep.yml

@@ -1,9 +1,9 @@
 ---
 name: "@octokit/plugin-paginate-rest"
-version: 9.2.2
+version: 14.0.0
 type: npm
 summary: Octokit plugin to paginate REST API endpoint responses
-homepage:
+homepage: 
 license: mit
 licenses:
 - sources: LICENSE

+ 1 - 1
.licenses/npm/@octokit/plugin-rest-endpoint-methods.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@octokit/plugin-rest-endpoint-methods"
-version: 10.4.1
+version: 17.0.0
 type: npm
 summary: Octokit plugin adding one method for all of api.github.com REST API endpoints
 homepage: 

+ 2 - 2
.licenses/npm/@octokit/request-error.dep.yml

@@ -1,9 +1,9 @@
 ---
 name: "@octokit/request-error"
-version: 5.1.1
+version: 7.1.0
 type: npm
 summary: Error class for Octokit request errors
-homepage:
+homepage: 
 license: mit
 licenses:
 - sources: LICENSE

+ 2 - 2
.licenses/npm/@octokit/request.dep.yml

@@ -1,10 +1,10 @@
 ---
 name: "@octokit/request"
-version: 8.4.1
+version: 10.0.10
 type: npm
 summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers
   and Node
-homepage:
+homepage: 
 license: mit
 licenses:
 - sources: LICENSE

+ 0 - 20
.licenses/npm/@octokit/types-13.4.1.dep.yml

@@ -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
.licenses/npm/@octokit/types-12.6.0.dep.yml → .licenses/npm/@octokit/types.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: "@octokit/types"
-version: 12.6.0
+version: 16.0.0
 type: npm
 summary: Shared TypeScript definitions for Octokit projects
 homepage: 

+ 1 - 1
.licenses/npm/before-after-hook.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: before-after-hook
-version: 2.2.3
+version: 4.0.0
 type: npm
 summary: asynchronous before/error/after hooks for internal functionality
 homepage: 

+ 47 - 0
.licenses/npm/content-type.dep.yml

@@ -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: []

+ 0 - 28
.licenses/npm/deprecation.dep.yml

@@ -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
.licenses/npm/uuid-3.4.0.dep.yml → .licenses/npm/json-with-bigint.dep.yml

@@ -1,16 +1,17 @@
 ---
-name: uuid
-version: 3.4.0
+name: json-with-bigint
+version: 3.5.8
 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: 
 license: mit
 licenses:
-- sources: LICENSE.md
+- sources: LICENSE
   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
     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,
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     SOFTWARE.
-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>
+notices: []

+ 0 - 26
.licenses/npm/once.dep.yml

@@ -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: []

+ 1 - 1
.licenses/npm/semver.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: semver
-version: 6.3.1
+version: 7.8.4
 type: npm
 summary: The semantic version parser used by npm.
 homepage: 

+ 1 - 1
.licenses/npm/undici.dep.yml

@@ -1,6 +1,6 @@
 ---
 name: undici
-version: 5.29.0
+version: 6.27.0
 type: npm
 summary: An HTTP/1.1 client, written from scratch for Node.js
 homepage: https://undici.nodejs.org

+ 3 - 3
.licenses/npm/universal-user-agent.dep.yml

@@ -1,8 +1,8 @@
 ---
 name: universal-user-agent
-version: 6.0.1
+version: 7.0.3
 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: 
 license: isc
 licenses:
@@ -10,7 +10,7 @@ licenses:
   text: |
     # [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.
 

+ 0 - 20
.licenses/npm/uuid-8.3.2.dep.yml

@@ -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: []

+ 0 - 20
.licenses/npm/uuid-9.0.1.dep.yml

@@ -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: []

+ 0 - 26
.licenses/npm/wrappy.dep.yml

@@ -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: []

+ 9 - 0
CHANGELOG.md

@@ -1,5 +1,14 @@
 # Changelog
 
+## 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
+* Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the minor-actions-dependencies group across 1 directory by @dependabot[bot] in https://github.com/actions/checkout/pull/2458
+* Bump flatted from 3.3.1 to 3.4.2 by @dependabot[bot] in https://github.com/actions/checkout/pull/2460
+* Bump js-yaml from 4.1.0 to 4.2.0 by @dependabot[bot] in https://github.com/actions/checkout/pull/2461
+* Bump @actions/core and @actions/tool-cache and Remove uuid by @dependabot[bot] in https://github.com/actions/checkout/pull/2459
+* upgrade module to esm and update dependencies by @aiqiaoy in https://github.com/actions/checkout/pull/2463
+* Bump the minor-npm-dependencies group across 1 directory with 3 updates by @dependabot[bot] in https://github.com/actions/checkout/pull/2462
+
 ## 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

+ 26 - 18
README.md

@@ -1,5 +1,14 @@
 [![Build and Test](https://github.com/actions/checkout/actions/workflows/test.yml/badge.svg)](https://github.com/actions/checkout/actions/workflows/test.yml)
 
+# 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
@@ -15,7 +24,6 @@
 - 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.
@@ -52,7 +60,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 
 <!-- start usage -->
 ```yaml
-- uses: actions/checkout@v6
+- uses: actions/checkout@v7
   with:
     # Repository name with owner. For example, actions/checkout
     # Default: ${{ github.repository }}
@@ -200,7 +208,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 ## Fetch only the root files
 
 ```yaml
-- uses: actions/checkout@v6
+- uses: actions/checkout@v7
   with:
     sparse-checkout: .
 ```
@@ -208,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
 
 ```yaml
-- uses: actions/checkout@v6
+- uses: actions/checkout@v7
   with:
     sparse-checkout: |
       .github
@@ -218,7 +226,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 ## Fetch only a single file
 
 ```yaml
-- uses: actions/checkout@v6
+- uses: actions/checkout@v7
   with:
     sparse-checkout: |
       README.md
@@ -228,7 +236,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 ## Fetch all history for all tags and branches
 
 ```yaml
-- uses: actions/checkout@v6
+- uses: actions/checkout@v7
   with:
     fetch-depth: 0
 ```
@@ -236,7 +244,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 ## Checkout a different branch
 
 ```yaml
-- uses: actions/checkout@v6
+- uses: actions/checkout@v7
   with:
     ref: my-branch
 ```
@@ -244,7 +252,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 ## Checkout HEAD^
 
 ```yaml
-- uses: actions/checkout@v6
+- uses: actions/checkout@v7
   with:
     fetch-depth: 2
 - run: git checkout HEAD^
@@ -254,12 +262,12 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 
 ```yaml
 - name: Checkout
-  uses: actions/checkout@v6
+  uses: actions/checkout@v7
   with:
     path: main
 
 - name: Checkout tools repo
-  uses: actions/checkout@v6
+  uses: actions/checkout@v7
   with:
     repository: my-org/my-tools
     path: my-tools
@@ -270,10 +278,10 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 
 ```yaml
 - name: Checkout
-  uses: actions/checkout@v6
+  uses: actions/checkout@v7
 
 - name: Checkout tools repo
-  uses: actions/checkout@v6
+  uses: actions/checkout@v7
   with:
     repository: my-org/my-tools
     path: my-tools
@@ -284,12 +292,12 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 
 ```yaml
 - name: Checkout
-  uses: actions/checkout@v6
+  uses: actions/checkout@v7
   with:
     path: main
 
 - name: Checkout private tools
-  uses: actions/checkout@v6
+  uses: actions/checkout@v7
   with:
     repository: my-org/my-private-tools
     token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
@@ -302,7 +310,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
 ## Checkout pull request HEAD commit instead of merge commit
 
 ```yaml
-- uses: actions/checkout@v6
+- uses: actions/checkout@v7
   with:
     ref: ${{ github.event.pull_request.head.sha }}
 ```
@@ -318,7 +326,7 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v6
+      - uses: actions/checkout@v7
 ```
 
 ## Push a commit using the built-in token
@@ -329,7 +337,7 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v6
+      - uses: actions/checkout@v7
       - run: |
           date > generated.txt
           # Note: the following account information will not work on GHES
@@ -351,7 +359,7 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v6
+      - uses: actions/checkout@v7
         with:
           ref: ${{ github.head_ref }}
       - run: |

+ 48 - 27
__test__/git-auth-helper.test.ts

@@ -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 gitAuthHelper from '../lib/git-auth-helper'
 import * as io from '@actions/io'
 import * as os from 'os'
 import * as path from 'path'
-import * as stateHelper from '../lib/state-helper'
-import {IGitCommandManager} from '../lib/git-command-manager'
-import {IGitSourceSettings} from '../lib/git-source-settings'
+import {fileURLToPath} from 'url'
+
+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 testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper')
@@ -32,25 +66,12 @@ describe('git-auth-helper tests', () => {
   })
 
   beforeEach(() => {
-    // Mock setSecret
-    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())
+    jest.clearAllMocks()
   })
 
   afterEach(() => {
     // Unregister mocks
-    jest.restoreAllMocks()
+    jest.clearAllMocks()
 
     // Restore HOME
     if (originalHome) {
@@ -229,7 +250,7 @@ describe('git-auth-helper tests', () => {
     await authHelper.configureAuth()
 
     // Assert secret
-    const setSecretSpy = core.setSecret as jest.Mock<any, any>
+    const setSecretSpy = core.setSecret as jest.Mock<any>
     expect(setSecretSpy).toHaveBeenCalledTimes(1)
     const expectedSecret = Buffer.from(
       `x-access-token:${settings.authToken}`,
@@ -529,7 +550,7 @@ describe('git-auth-helper tests', () => {
       settings.sshKey = ''
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       await authHelper.configureAuth()
-      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
+      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any>
       mockSubmoduleForeach.mockClear() // reset calls
 
       // Act
@@ -562,7 +583,7 @@ describe('git-auth-helper tests', () => {
       settings.persistCredentials = false
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       await authHelper.configureAuth()
-      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
+      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any>
       mockSubmoduleForeach.mockClear() // reset calls
 
       // Act
@@ -588,7 +609,7 @@ describe('git-auth-helper tests', () => {
       settings.sshKey = ''
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       await authHelper.configureAuth()
-      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
+      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any>
       mockSubmoduleForeach.mockClear() // reset calls
 
       // Act
@@ -627,7 +648,7 @@ describe('git-auth-helper tests', () => {
       )
       const authHelper = gitAuthHelper.createAuthHelper(git, settings)
       await authHelper.configureAuth()
-      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
+      const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any>
       mockSubmoduleForeach.mockClear() // reset calls
 
       // Act
@@ -809,7 +830,7 @@ describe('git-auth-helper tests', () => {
 
     // Mock getSubmoduleConfigPaths to return our fake submodules (for both configure and remove)
     const mockGetSubmoduleConfigPaths =
-      git.getSubmoduleConfigPaths as jest.Mock<any, any>
+      git.getSubmoduleConfigPaths as jest.Mock<any>
     mockGetSubmoduleConfigPaths.mockResolvedValue([
       submodule1ConfigPath,
       submodule2ConfigPath
@@ -1147,7 +1168,7 @@ async function setup(testName: string): Promise<void> {
     ),
     tryReset: jest.fn(),
     version: jest.fn()
-  }
+  } as unknown as IGitCommandManager & {env: {[key: string]: string}}
 
   settings = {
     authToken: 'some auth token',

+ 65 - 40
__test__/git-command-manager.test.ts

@@ -1,26 +1,51 @@
-import * as exec from '@actions/exec'
-import * as fshelper from '../lib/fs-helper'
-import * as commandManager from '../lib/git-command-manager'
-
-let git: commandManager.IGitCommandManager
-let mockExec = jest.fn()
+import {
+  jest,
+  describe,
+  it,
+  expect,
+  beforeAll,
+  beforeEach,
+  afterEach,
+  afterAll
+} from '@jest/globals'
+
+// Mock @actions/exec
+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', () => {
   beforeAll(async () => {})
 
   beforeEach(async () => {
-    jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
-    jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
+    mockFileExistsSync.mockReset()
+    mockDirectoryExistsSync.mockReset()
   })
 
   afterEach(() => {
-    jest.restoreAllMocks()
+    jest.clearAllMocks()
   })
 
   afterAll(() => {})
 
   it('branch list matches', async () => {
-    mockExec.mockImplementation((path, args, options) => {
+    mockExec.mockImplementation((path: any, args: any, options: any) => {
       console.log(args, options.listeners.stdout)
 
       if (args.includes('version')) {
@@ -36,7 +61,7 @@ describe('git-auth-helper tests', () => {
 
       return 1
     })
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
     const workingDirectory = 'test'
     const lfs = false
     const doSparseCheckout = false
@@ -53,7 +78,7 @@ describe('git-auth-helper tests', () => {
   })
 
   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)
 
       if (args.includes('version')) {
@@ -72,7 +97,7 @@ describe('git-auth-helper tests', () => {
 
       return 1
     })
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
     const workingDirectory = 'test'
     const lfs = false
     const doSparseCheckout = false
@@ -91,9 +116,9 @@ describe('git-auth-helper tests', () => {
 
 describe('Test fetchDepth and fetchTags options', () => {
   beforeEach(async () => {
-    jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
-    jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
-    mockExec.mockImplementation((path, args, options) => {
+    mockFileExistsSync.mockReset()
+    mockDirectoryExistsSync.mockReset()
+    mockExec.mockImplementation((path: any, args: any, options: any) => {
       console.log(args, options.listeners.stdout)
 
       if (args.includes('version')) {
@@ -105,11 +130,11 @@ describe('Test fetchDepth and fetchTags options', () => {
   })
 
   afterEach(() => {
-    jest.restoreAllMocks()
+    jest.clearAllMocks()
   })
 
   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 lfs = false
     const doSparseCheckout = false
@@ -146,7 +171,7 @@ describe('Test fetchDepth and fetchTags options', () => {
   })
 
   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
@@ -184,7 +209,7 @@ describe('Test fetchDepth and fetchTags options', () => {
   })
 
   it('should call execGit with the correct arguments when fetchDepth is 1', async () => {
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
 
     const workingDirectory = 'test'
     const lfs = false
@@ -222,7 +247,7 @@ describe('Test fetchDepth and fetchTags options', () => {
   })
 
   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 lfs = false
@@ -261,7 +286,7 @@ describe('Test fetchDepth and fetchTags options', () => {
   })
 
   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 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 () => {
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
 
     const workingDirectory = 'test'
     const lfs = false
@@ -339,7 +364,7 @@ describe('Test fetchDepth and fetchTags options', () => {
   })
 
   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 lfs = false
@@ -380,23 +405,23 @@ describe('Test fetchDepth and fetchTags options', () => {
 
 describe('repository initialization object format', () => {
   beforeEach(async () => {
-    jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
-    jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
+    mockFileExistsSync.mockReset()
+    mockDirectoryExistsSync.mockReset()
   })
 
   afterEach(() => {
-    jest.restoreAllMocks()
+    jest.clearAllMocks()
   })
 
   it('initializes SHA-256 repositories with the matching object format', async () => {
-    mockExec.mockImplementation((path, args, options) => {
+    mockExec.mockImplementation((path: any, args: any, options: any) => {
       if (args.includes('version')) {
         options.listeners.stdout(Buffer.from('git version 2.50.1'))
       }
 
       return 0
     })
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
 
     git = await commandManager.createCommandManager('test', false, false)
 
@@ -410,14 +435,14 @@ describe('repository initialization object format', () => {
   })
 
   it('initializes SHA-1 repositories with existing default arguments', async () => {
-    mockExec.mockImplementation((path, args, options) => {
+    mockExec.mockImplementation((path: any, args: any, options: any) => {
       if (args.includes('version')) {
         options.listeners.stdout(Buffer.from('git version 2.50.1'))
       }
 
       return 0
     })
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
 
     git = await commandManager.createCommandManager('test', false, false)
 
@@ -433,12 +458,12 @@ describe('repository initialization object format', () => {
 
 describe('git user-agent with orchestration ID', () => {
   beforeEach(async () => {
-    jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
-    jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
+    mockFileExistsSync.mockReset()
+    mockDirectoryExistsSync.mockReset()
   })
 
   afterEach(() => {
-    jest.restoreAllMocks()
+    jest.clearAllMocks()
     // Clean up environment variable to prevent test pollution
     delete process.env['ACTIONS_ORCHESTRATION_ID']
   })
@@ -448,7 +473,7 @@ describe('git user-agent with orchestration ID', () => {
     process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
 
     let capturedEnv: any = null
-    mockExec.mockImplementation((path, args, options) => {
+    mockExec.mockImplementation((path: any, args: any, options: any) => {
       if (args.includes('version')) {
         options.listeners.stdout(Buffer.from('2.18'))
       }
@@ -456,7 +481,7 @@ describe('git user-agent with orchestration ID', () => {
       capturedEnv = options.env
       return 0
     })
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
 
     const workingDirectory = 'test'
     const lfs = false
@@ -483,7 +508,7 @@ describe('git user-agent with orchestration ID', () => {
     process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
 
     let capturedEnv: any = null
-    mockExec.mockImplementation((path, args, options) => {
+    mockExec.mockImplementation((path: any, args: any, options: any) => {
       if (args.includes('version')) {
         options.listeners.stdout(Buffer.from('2.18'))
       }
@@ -491,7 +516,7 @@ describe('git user-agent with orchestration ID', () => {
       capturedEnv = options.env
       return 0
     })
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
 
     const workingDirectory = 'test'
     const lfs = false
@@ -517,7 +542,7 @@ describe('git user-agent with orchestration ID', () => {
     delete process.env['ACTIONS_ORCHESTRATION_ID']
 
     let capturedEnv: any = null
-    mockExec.mockImplementation((path, args, options) => {
+    mockExec.mockImplementation((path: any, args: any, options: any) => {
       if (args.includes('version')) {
         options.listeners.stdout(Buffer.from('2.18'))
       }
@@ -525,7 +550,7 @@ describe('git user-agent with orchestration ID', () => {
       capturedEnv = options.env
       return 0
     })
-    jest.spyOn(exec, 'exec').mockImplementation(mockExec)
+    // exec.exec is already mockExec
 
     const workingDirectory = 'test'
     const lfs = false

+ 41 - 19
__test__/git-directory-helper.test.ts

@@ -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 gitDirectoryHelper from '../lib/git-directory-helper'
 import * as io from '@actions/io'
 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')
 let repositoryPath: string
@@ -19,16 +46,11 @@ describe('git-directory-helper tests', () => {
   })
 
   beforeEach(() => {
-    // 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())
+    jest.clearAllMocks()
   })
 
   afterEach(() => {
-    // Unregister mocks
-    jest.restoreAllMocks()
+    jest.clearAllMocks()
   })
 
   const cleansWhenCleanTrue = 'cleans when clean true'
@@ -81,7 +103,7 @@ describe('git-directory-helper tests', () => {
     // Arrange
     await setup(doesNotCheckoutDetachWhenNotAlreadyDetached)
     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 () => {
       return true
     })
@@ -132,7 +154,7 @@ describe('git-directory-helper tests', () => {
     // Arrange
     await setup(removesContentsWhenCleanFails)
     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 () => {
       return false
     })
@@ -210,7 +232,7 @@ describe('git-directory-helper tests', () => {
     // Arrange
     await setup(removesContentsWhenResetFails)
     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 () => {
       return false
     })
@@ -260,7 +282,7 @@ describe('git-directory-helper tests', () => {
     // Arrange
     await setup(removesLocalBranches)
     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) => {
       return remote ? [] : ['local-branch-1', 'local-branch-2']
     })
@@ -291,7 +313,7 @@ describe('git-directory-helper tests', () => {
 
     //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) => {
       return false
     })
@@ -319,7 +341,7 @@ describe('git-directory-helper tests', () => {
     await setup(doesNotCleanWhenSubmoduleStatusIsTrue)
     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) => {
       return true
     })
@@ -381,7 +403,7 @@ describe('git-directory-helper tests', () => {
     // Arrange
     await setup(removesAncestorRemoteBranch)
     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) => {
       return remote ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : []
     })
@@ -411,7 +433,7 @@ describe('git-directory-helper tests', () => {
     // Arrange
     await setup(removesDescendantRemoteBranches)
     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) => {
       return remote
         ? ['origin/remote-branch-1/conflict', 'origin/remote-branch-2']
@@ -507,5 +529,5 @@ async function setup(testName: string): Promise<void> {
       return true
     }),
     version: jest.fn()
-  }
+  } as unknown as IGitCommandManager
 }

+ 3 - 2
__test__/git-version.test.ts

@@ -1,5 +1,6 @@
-import {GitVersion} from '../src/git-version'
-import {MinimumGitSparseCheckoutVersion} from '../src/git-command-manager'
+import {describe, it, expect} from '@jest/globals'
+import {GitVersion} from '../src/git-version.js'
+import {MinimumGitSparseCheckoutVersion} from '../src/git-command-manager.js'
 
 describe('git-version tests', () => {
   it('basics', async () => {

+ 29 - 15
__test__/github-api-helper.test.ts

@@ -1,11 +1,25 @@
-import * as core from '@actions/core'
-import * as github from '@actions/github'
-import * as githubApiHelper from '../lib/github-api-helper'
+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 getOctokitSpy: jest.SpyInstance
-  let debugSpy: jest.SpyInstance
-  let request: jest.Mock
+  let request: jest.Mock<any>
 
   function mockHashAlgorithmApi(hashAlgorithm: string): void {
     request = jest.fn(async () => ({
@@ -13,17 +27,18 @@ describe('github-api-helper object format', () => {
         hash_algorithm: hashAlgorithm
       }
     }))
-    getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({
+    mockGetOctokit.mockReturnValue({
       request
     } as any)
   }
 
   beforeEach(() => {
-    debugSpy = jest.spyOn(core, 'debug').mockImplementation(jest.fn())
+    mockDebug.mockClear()
+    mockGetOctokit.mockClear()
   })
 
   afterEach(() => {
-    jest.restoreAllMocks()
+    jest.clearAllMocks()
   })
 
   it('detects SHA-256 from the repository hash algorithm endpoint', async () => {
@@ -33,7 +48,7 @@ describe('github-api-helper object format', () => {
       githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
     ).resolves.toEqual({format: 'sha256', succeeded: true})
 
-    expect(getOctokitSpy).toHaveBeenCalledWith(
+    expect(mockGetOctokit).toHaveBeenCalledWith(
       'token',
       expect.objectContaining({baseUrl: 'https://api.github.com'})
     )
@@ -54,7 +69,6 @@ describe('github-api-helper object format', () => {
   it('detects object format from an existing commit without API calls', async () => {
     const commitSha =
       '9422233ca7ee1b17f1e905d0e141faf0c401556c41cdc6acd71c6bd685da2e92'
-    getOctokitSpy = jest.spyOn(github, 'getOctokit')
 
     await expect(
       githubApiHelper.tryGetRepositoryObjectFormat(
@@ -66,7 +80,7 @@ describe('github-api-helper object format', () => {
       )
     ).resolves.toEqual({format: 'sha256', succeeded: true})
 
-    expect(getOctokitSpy).not.toHaveBeenCalled()
+    expect(mockGetOctokit).not.toHaveBeenCalled()
   })
 
   it('returns unsuccessful when the hash algorithm endpoint value is not recognized', async () => {
@@ -75,7 +89,7 @@ describe('github-api-helper object format', () => {
     await expect(
       githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
     ).resolves.toEqual({format: '', succeeded: false})
-    expect(debugSpy).toHaveBeenCalledWith(
+    expect(mockDebug).toHaveBeenCalledWith(
       'Unable to determine repository object format from hash-algorithm endpoint'
     )
   })
@@ -84,14 +98,14 @@ describe('github-api-helper object format', () => {
     request = jest.fn(async () => {
       throw new Error('not found')
     })
-    jest.spyOn(github, 'getOctokit').mockReturnValue({
+    mockGetOctokit.mockReturnValue({
       request
     } as any)
 
     await expect(
       githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
     ).resolves.toEqual({format: '', succeeded: false})
-    expect(debugSpy).toHaveBeenCalledWith(
+    expect(mockDebug).toHaveBeenCalledWith(
       'Unable to determine repository object format from hash-algorithm endpoint: not found'
     )
   })

+ 72 - 47
__test__/input-helper.test.ts

@@ -1,10 +1,13 @@
-import * as core from '@actions/core'
-import * as fsHelper from '../lib/fs-helper'
-import * as github from '@actions/github'
-import * as inputHelper from '../lib/input-helper'
+import {
+  jest,
+  describe,
+  it,
+  expect,
+  beforeAll,
+  beforeEach,
+  afterAll
+} from '@jest/globals'
 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 gitHubWorkspace = path.resolve('/checkout-tests/workspace')
@@ -12,42 +15,58 @@ const gitHubWorkspace = path.resolve('/checkout-tests/workspace')
 // Inputs for mock @actions/core
 let inputs = {} as any
 
-// Shallow clone original @actions/github context
-let originalContext = {...github.context}
+// Mutable mock github context
+const mockGithubContext: any = {
+  ref: 'refs/heads/some-ref',
+  sha: '1234567890123456789012345678901234567890',
+  repo: {owner: 'some-owner', repo: 'some-repo'},
+  eventName: '',
+  payload: {}
+}
+
+// Mock @actions/core before loading input-helper
+jest.unstable_mockModule('@actions/core', () => ({
+  getInput: jest.fn((name: string) => inputs[name]),
+  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', () => {
   beforeAll(() => {
-    // Mock getInput
-    jest.spyOn(core, 'getInput').mockImplementation((name: string) => {
-      return inputs[name]
-    })
-
-    // 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
     process.env['GITHUB_WORKSPACE'] = gitHubWorkspace
   })
@@ -55,6 +74,15 @@ describe('input-helper tests', () => {
   beforeEach(() => {
     // Reset inputs
     inputs = {}
+    jest.clearAllMocks()
+    // Re-apply default mocks
+    ;(core.getInput as jest.Mock<any>).mockImplementation(
+      (name: string) => inputs[name]
+    )
+    mockDirectoryExistsSync.mockImplementation(
+      (p: string) => p === gitHubWorkspace
+    )
+    mockGetOrganizationId.mockResolvedValue(123456)
   })
 
   afterAll(() => {
@@ -65,11 +93,8 @@ describe('input-helper tests', () => {
     }
 
     // Restore @actions/github context
-    github.context.ref = originalContext.ref
-    github.context.sha = originalContext.sha
-
-    // Restore
-    jest.restoreAllMocks()
+    mockGithubContext.ref = 'refs/heads/some-ref'
+    mockGithubContext.sha = '1234567890123456789012345678901234567890'
   })
 
   it('sets defaults', async () => {
@@ -95,15 +120,15 @@ describe('input-helper tests', () => {
   })
 
   it('qualifies ref', async () => {
-    let originalRef = github.context.ref
+    let originalRef = mockGithubContext.ref
     try {
-      github.context.ref = 'some-unqualified-ref'
+      mockGithubContext.ref = 'some-unqualified-ref'
       const settings: IGitSourceSettings = await inputHelper.getInputs()
       expect(settings).toBeTruthy()
       expect(settings.commit).toBe('1234567890123456789012345678901234567890')
       expect(settings.ref).toBe('refs/heads/some-unqualified-ref')
     } finally {
-      github.context.ref = originalRef
+      mockGithubContext.ref = originalRef
     }
   })
 

+ 57 - 35
__test__/ref-helper.test.ts

@@ -1,8 +1,36 @@
+import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals'
 import * as assert from 'assert'
-import * as core from '@actions/core'
-import * as github from '@actions/github'
-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 sha256Commit =
@@ -12,6 +40,7 @@ let git: IGitCommandManager
 describe('ref-helper tests', () => {
   beforeEach(() => {
     git = {} as unknown as IGitCommandManager
+    jest.clearAllMocks()
   })
 
   it('getCheckoutInfo requires git', async () => {
@@ -166,14 +195,12 @@ describe('ref-helper tests', () => {
   })
 
   it('getRefSpec sha + refs/tags/ with fetchTags', async () => {
-    // When fetchTags is true, only include tags wildcard (specific tag is redundant)
     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 () => {
-    // When fetchTags is true, include both the branch refspec and tags wildcard
     const refSpec = refHelper.getRefSpec('refs/heads/my/branch', commit, true)
     expect(refSpec.length).toBe(2)
     expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
@@ -194,7 +221,6 @@ describe('ref-helper tests', () => {
   })
 
   it('getRefSpec unqualified ref only with fetchTags', async () => {
-    // When fetchTags is true, skip specific tag pattern since wildcard covers all
     const refSpec = refHelper.getRefSpec('my-ref', '', true)
     expect(refSpec.length).toBe(2)
     expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
@@ -222,14 +248,12 @@ describe('ref-helper tests', () => {
   })
 
   it('getRefSpec refs/tags/ only with fetchTags', async () => {
-    // When fetchTags is true, only include tags wildcard (specific tag is redundant)
     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 () => {
-    // When fetchTags is true, include both the branch refspec and tags wildcard
     const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '', true)
     expect(refSpec.length).toBe(2)
     expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
@@ -248,9 +272,7 @@ describe('ref-helper tests', () => {
       '1111111111222222222233333333334444444444555555555566666666667777'
     const sha256Base =
       'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff0000'
-    let debugSpy: jest.SpyInstance
-    let getOctokitSpy: jest.SpyInstance
-    let repoGetSpy: jest.Mock
+    let repoGetSpy: jest.Mock<any>
     let originalEventName: string
     let originalPayload: unknown
     let originalRef: string
@@ -261,10 +283,10 @@ describe('ref-helper tests', () => {
       expectedBaseSha: string,
       mergeCommit: string
     ): void {
-      ;(github.context as any).eventName = 'pull_request'
-      github.context.ref = ref
-      github.context.sha = mergeCommit
-      ;(github.context as any).payload = {
+      mockGithubContext.eventName = 'pull_request'
+      mockGithubContext.ref = ref
+      mockGithubContext.sha = mergeCommit
+      mockGithubContext.payload = {
         action: 'synchronize',
         after: expectedHeadSha,
         number: 123,
@@ -280,18 +302,18 @@ describe('ref-helper tests', () => {
     }
 
     beforeEach(() => {
-      originalEventName = github.context.eventName
-      originalPayload = github.context.payload
-      originalRef = github.context.ref
-      originalSha = github.context.sha
+      originalEventName = mockGithubContext.eventName
+      originalPayload = mockGithubContext.payload
+      originalRef = mockGithubContext.ref
+      originalSha = mockGithubContext.sha
 
-      jest.spyOn(github.context, 'repo', 'get').mockReturnValue({
+      mockGithubContext.repo = {
         owner: repositoryOwner,
         repo: repositoryName
-      })
-      debugSpy = jest.spyOn(core, 'debug').mockImplementation(jest.fn())
+      }
+
       repoGetSpy = jest.fn(async () => ({}))
-      getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({
+      mockGetOctokit.mockReturnValue({
         rest: {
           repos: {
             get: repoGetSpy
@@ -301,11 +323,11 @@ describe('ref-helper tests', () => {
     })
 
     afterEach(() => {
-      ;(github.context as any).eventName = originalEventName
-      ;(github.context as any).payload = originalPayload
-      github.context.ref = originalRef
-      github.context.sha = originalSha
-      jest.restoreAllMocks()
+      mockGithubContext.eventName = originalEventName
+      mockGithubContext.payload = originalPayload
+      mockGithubContext.ref = originalRef
+      mockGithubContext.sha = originalSha
+      jest.clearAllMocks()
     })
 
     it('returns early for SHA-1 merge commit', async () => {
@@ -320,7 +342,7 @@ describe('ref-helper tests', () => {
         commit
       )
 
-      expect(getOctokitSpy).not.toHaveBeenCalled()
+      expect(mockGetOctokit).not.toHaveBeenCalled()
       expect(repoGetSpy).not.toHaveBeenCalled()
     })
 
@@ -338,7 +360,7 @@ describe('ref-helper tests', () => {
         sha256Commit
       )
 
-      expect(getOctokitSpy).toHaveBeenCalledWith(
+      expect(mockGetOctokit).toHaveBeenCalledWith(
         'token',
         expect.objectContaining({
           userAgent: expect.stringContaining(
@@ -350,10 +372,10 @@ describe('ref-helper tests', () => {
         owner: repositoryOwner,
         repo: repositoryName
       })
-      expect(debugSpy).toHaveBeenCalledWith(
+      expect(mockDebug).toHaveBeenCalledWith(
         `Expected head sha ${sha256Head}; actual head sha ${actualHeadSha}`
       )
-      expect(debugSpy).not.toHaveBeenCalledWith('Unexpected message format')
+      expect(mockDebug).not.toHaveBeenCalledWith('Unexpected message format')
     })
 
     it('does not match 50-char hex as a valid merge', async () => {
@@ -370,9 +392,9 @@ describe('ref-helper tests', () => {
         commit
       )
 
-      expect(getOctokitSpy).not.toHaveBeenCalled()
+      expect(mockGetOctokit).not.toHaveBeenCalled()
       expect(repoGetSpy).not.toHaveBeenCalled()
-      expect(debugSpy).toHaveBeenCalledWith('Unexpected message format')
+      expect(mockDebug).toHaveBeenCalledWith('Unexpected message format')
     })
   })
 })

+ 24 - 9
__test__/retry-helper.test.ts

@@ -1,16 +1,32 @@
-import * as core from '@actions/core'
-import {RetryHelper} from '../lib/retry-helper'
+import {
+  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
 
 describe('retry-helper tests', () => {
   beforeAll(() => {
-    // Mock @actions/core info()
-    jest.spyOn(core, 'info').mockImplementation((message: string) => {
-      info.push(message)
-    })
-
     retryHelper = new RetryHelper(3, 0, 0)
   })
 
@@ -20,7 +36,6 @@ describe('retry-helper tests', () => {
   })
 
   afterAll(() => {
-    // Restore
     jest.restoreAllMocks()
   })
 

+ 36 - 19
__test__/unsafe-pr-checkout-helper.test.ts

@@ -1,10 +1,12 @@
-import * as github from '@actions/github'
-import {assertSafePrCheckout} from '../lib/unsafe-pr-checkout-helper'
-
-// Shallow clone original @actions/github context
-const originalContext = {...github.context}
-const originalEventName = github.context.eventName
-const originalPayload = github.context.payload
+import {
+  jest,
+  describe,
+  it,
+  expect,
+  beforeAll,
+  afterEach,
+  afterAll
+} from '@jest/globals'
 
 const BASE_REPO_ID = 100
 const FORK_REPO_ID = 200
@@ -15,9 +17,29 @@ const WORKFLOW_RUN_HEAD_COMMIT_SHA = '4444444444444444444444444444444444444444'
 const BASE_QUALIFIED_REPO = 'some-owner/some-repo'
 const FORK_QUALIFIED_REPO = 'another-repo/fork'
 
+// Mutable mock context
+const mockContext: any = {
+  eventName: '',
+  payload: {},
+  repo: {owner: 'some-owner', repo: 'some-repo'},
+  ref: '',
+  sha: ''
+}
+
+jest.unstable_mockModule('@actions/github', () => ({
+  context: mockContext
+}))
+
+// Dynamic imports after mocking
+const {assertSafePrCheckout} =
+  await import('../src/unsafe-pr-checkout-helper.js')
+
+const originalEventName = mockContext.eventName
+const originalPayload = mockContext.payload
+
 function setContext(eventName: string, payload: object): void {
-  ;(github.context as {eventName: string}).eventName = eventName
-  ;(github.context as {payload: object}).payload = payload
+  mockContext.eventName = eventName
+  mockContext.payload = payload
 }
 
 function forkPullRequestTargetPayload(): object {
@@ -59,22 +81,17 @@ function forkWorkflowRunPayload(): object {
 
 describe('unsafe-pr-checkout-helper', () => {
   beforeAll(() => {
-    jest.spyOn(github.context, 'repo', 'get').mockReturnValue({
-      owner: 'some-owner',
-      repo: 'some-repo'
-    })
+    mockContext.repo = {owner: 'some-owner', repo: 'some-repo'}
   })
 
   afterEach(() => {
-    ;(github.context as {eventName: string}).eventName = originalEventName
-    ;(github.context as {payload: object}).payload = originalPayload
+    mockContext.eventName = originalEventName
+    mockContext.payload = originalPayload
   })
 
   afterAll(() => {
-    ;(github.context as {eventName: string}).eventName =
-      originalContext.eventName
-    ;(github.context as {payload: object}).payload = originalContext.payload
-    jest.restoreAllMocks()
+    mockContext.eventName = originalEventName
+    mockContext.payload = originalPayload
   })
 
   it('allows pull_request events untouched', () => {

+ 2 - 1
__test__/url-helper.test.ts

@@ -1,4 +1,5 @@
-import * as urlHelper from '../src/url-helper'
+import {jest, describe, it, expect, beforeEach, afterAll} from '@jest/globals'
+import * as urlHelper from '../src/url-helper.js'
 
 describe('getServerUrl tests', () => {
   it('basics', async () => {

Разлика између датотеке није приказан због своје велике величине
+ 363 - 1162
dist/index.js


+ 3 - 0
dist/package.json

@@ -0,0 +1,3 @@
+{
+  "type": "module"
+}

+ 0 - 12
jest.config.js

@@ -1,12 +0,0 @@
-module.exports = {
-  clearMocks: true,
-  fakeTimers: {},
-  moduleFileExtensions: ['js', 'ts'],
-  testEnvironment: 'node',
-  testMatch: ['**/*.test.ts'],
-  testRunner: 'jest-circus/runner',
-  transform: {
-    '^.+\\.ts$': 'ts-jest'
-  },
-  verbose: true
-}

+ 24 - 0
jest.config.ts

@@ -0,0 +1,24 @@
+export default {
+  clearMocks: true,
+  moduleFileExtensions: ['js', 'ts'],
+  roots: ['<rootDir>'],
+  testEnvironment: 'node',
+  testMatch: ['**/*.test.ts'],
+  transform: {
+    '^.+\\.ts$': [
+      'ts-jest',
+      {
+        useESM: true,
+        diagnostics: {
+          ignoreCodes: [151002]
+        }
+      }
+    ]
+  },
+  extensionsToTreatAsEsm: ['.ts'],
+  transformIgnorePatterns: ['node_modules/(?!(@actions)/)'],
+  moduleNameMapper: {
+    '^(\\.{1,2}/.*)\\.js$': '$1'
+  },
+  verbose: true
+}

Разлика између датотеке није приказан због своје велике величине
+ 349 - 294
package-lock.json


+ 17 - 15
package.json

@@ -1,14 +1,15 @@
 {
   "name": "checkout",
-  "version": "5.0.0",
+  "version": "7.0.0",
   "description": "checkout action",
+  "type": "module",
   "main": "lib/main.js",
   "scripts": {
-    "build": "tsc && ncc build && node lib/misc/generate-docs.js",
+    "build": "tsc && ncc build src/main.ts -o dist && node lib/misc/generate-docs.js",
     "format": "prettier --write '**/*.ts'",
     "format-check": "prettier --check '**/*.ts'",
     "lint": "eslint src/**/*.ts",
-    "test": "jest",
+    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
     "licensed-check": "src/misc/licensed-check.sh",
     "licensed-generate": "src/misc/licensed-generate.sh"
   },
@@ -27,29 +28,30 @@
     "url": "https://github.com/actions/checkout/issues"
   },
   "homepage": "https://github.com/actions/checkout#readme",
+  "engines": {
+    "node": ">=24"
+  },
   "dependencies": {
-    "@actions/core": "^1.10.1",
-    "@actions/exec": "^1.1.1",
-    "@actions/github": "^6.0.0",
-    "@actions/io": "^1.1.3",
-    "@actions/tool-cache": "^2.0.1",
-    "uuid": "^9.0.1"
+    "@actions/core": "^3.0.1",
+    "@actions/exec": "^3.0.0",
+    "@actions/github": "^9.1.1",
+    "@actions/io": "^3.0.2",
+    "@actions/tool-cache": "^4.0.0"
   },
   "devDependencies": {
     "@types/jest": "^29.5.12",
     "@types/node": "^24.1.0",
-    "@types/uuid": "^9.0.8",
     "@typescript-eslint/eslint-plugin": "^7.9.0",
     "@typescript-eslint/parser": "^7.9.0",
-    "@vercel/ncc": "^0.38.1",
+    "@vercel/ncc": "^0.44.0",
     "eslint": "^8.57.0",
     "eslint-plugin-github": "^4.10.2",
     "eslint-plugin-jest": "^28.8.2",
     "jest": "^29.7.0",
-    "jest-circus": "^29.7.0",
-    "js-yaml": "^4.1.0",
-    "prettier": "^3.3.3",
-    "ts-jest": "^29.2.5",
+    "js-yaml": "^4.2.0",
+    "prettier": "^3.8.4",
+    "ts-jest": "^29.4.11",
+    "ts-node": "^10.9.2",
     "typescript": "^5.5.4"
   }
 }

+ 9 - 9
src/git-auth-helper.ts

@@ -5,12 +5,12 @@ import * as fs from 'fs'
 import * as io from '@actions/io'
 import * as os from 'os'
 import * as path from 'path'
-import * as regexpHelper from './regexp-helper'
-import * as stateHelper from './state-helper'
-import * as urlHelper from './url-helper'
-import {v4 as uuid} from 'uuid'
-import {IGitCommandManager} from './git-command-manager'
-import {IGitSourceSettings} from './git-source-settings'
+import * as regexpHelper from './regexp-helper.js'
+import * as stateHelper from './state-helper.js'
+import * as urlHelper from './url-helper.js'
+import {randomUUID} from 'crypto'
+import {IGitCommandManager} from './git-command-manager.js'
+import {IGitSourceSettings} from './git-source-settings.js'
 
 const IS_WINDOWS = process.platform === 'win32'
 const SSH_COMMAND_KEY = 'core.sshCommand'
@@ -90,7 +90,7 @@ class GitAuthHelper {
     // Create a temp home directory
     const runnerTemp = process.env['RUNNER_TEMP'] || ''
     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
-    const uniqueId = uuid()
+    const uniqueId = randomUUID()
     this.temporaryHomePath = path.join(runnerTemp, uniqueId)
     await fs.promises.mkdir(this.temporaryHomePath, {recursive: true})
 
@@ -255,7 +255,7 @@ class GitAuthHelper {
     // Write key
     const runnerTemp = process.env['RUNNER_TEMP'] || ''
     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
-    const uniqueId = uuid()
+    const uniqueId = randomUUID()
     this.sshKeyPath = path.join(runnerTemp, uniqueId)
     stateHelper.setSshKeyPath(this.sshKeyPath)
     await fs.promises.mkdir(runnerTemp, {recursive: true})
@@ -422,7 +422,7 @@ class GitAuthHelper {
     assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
 
     // Create a unique filename for this checkout instance
-    const configFileName = `git-credentials-${uuid()}.config`
+    const configFileName = `git-credentials-${randomUUID()}.config`
     this.credentialsConfigPath = path.join(runnerTemp, configFileName)
 
     core.debug(`Credentials config path: ${this.credentialsConfigPath}`)

+ 5 - 5
src/git-command-manager.ts

@@ -1,13 +1,13 @@
 import * as core from '@actions/core'
 import * as exec from '@actions/exec'
 import * as fs from 'fs'
-import * as fshelper from './fs-helper'
+import * as fshelper from './fs-helper.js'
 import * as io from '@actions/io'
 import * as path from 'path'
-import * as refHelper from './ref-helper'
-import * as regexpHelper from './regexp-helper'
-import * as retryHelper from './retry-helper'
-import {GitVersion} from './git-version'
+import * as refHelper from './ref-helper.js'
+import * as regexpHelper from './regexp-helper.js'
+import * as retryHelper from './retry-helper.js'
+import {GitVersion} from './git-version.js'
 
 // Auth header not supported before 2.9
 // Wire protocol v2 not supported before 2.18

+ 2 - 2
src/git-directory-helper.ts

@@ -1,10 +1,10 @@
 import * as assert from 'assert'
 import * as core from '@actions/core'
 import * as fs from 'fs'
-import * as fsHelper from './fs-helper'
+import * as fsHelper from './fs-helper.js'
 import * as io from '@actions/io'
 import * as path from 'path'
-import {IGitCommandManager} from './git-command-manager'
+import {IGitCommandManager} from './git-command-manager.js'
 
 export async function prepareExistingDirectory(
   git: IGitCommandManager | undefined,

+ 10 - 10
src/git-source-provider.ts

@@ -1,19 +1,19 @@
 import * as core from '@actions/core'
-import * as fsHelper from './fs-helper'
-import * as gitAuthHelper from './git-auth-helper'
-import * as gitCommandManager from './git-command-manager'
-import * as gitDirectoryHelper from './git-directory-helper'
-import * as githubApiHelper from './github-api-helper'
+import * as fsHelper from './fs-helper.js'
+import * as gitAuthHelper from './git-auth-helper.js'
+import * as gitCommandManager from './git-command-manager.js'
+import * as gitDirectoryHelper from './git-directory-helper.js'
+import * as githubApiHelper from './github-api-helper.js'
 import * as io from '@actions/io'
 import * as path from 'path'
-import * as refHelper from './ref-helper'
-import * as stateHelper from './state-helper'
-import * as urlHelper from './url-helper'
+import * as refHelper from './ref-helper.js'
+import * as stateHelper from './state-helper.js'
+import * as urlHelper from './url-helper.js'
 import {
   MinimumGitSparseCheckoutVersion,
   IGitCommandManager
-} from './git-command-manager'
-import {IGitSourceSettings} from './git-source-settings'
+} from './git-command-manager.js'
+import {IGitSourceSettings} from './git-source-settings.js'
 
 export async function getSource(settings: IGitSourceSettings): Promise<void> {
   // Repository URL

+ 4 - 4
src/github-api-helper.ts

@@ -4,10 +4,10 @@ import * as fs from 'fs'
 import * as github from '@actions/github'
 import * as io from '@actions/io'
 import * as path from 'path'
-import * as retryHelper from './retry-helper'
+import * as retryHelper from './retry-helper.js'
 import * as toolCache from '@actions/tool-cache'
-import {v4 as uuid} from 'uuid'
-import {getServerApiUrl} from './url-helper'
+import {randomUUID} from 'crypto'
+import {getServerApiUrl} from './url-helper.js'
 
 const IS_WINDOWS = process.platform === 'win32'
 
@@ -39,7 +39,7 @@ export async function downloadRepository(
 
   // Write archive to disk
   core.info('Writing archive to disk')
-  const uniqueId = uuid()
+  const uniqueId = randomUUID()
   const archivePath = IS_WINDOWS
     ? path.join(repositoryPath, `${uniqueId}.zip`)
     : path.join(repositoryPath, `${uniqueId}.tar.gz`)

+ 4 - 4
src/input-helper.ts

@@ -1,10 +1,10 @@
 import * as core from '@actions/core'
-import * as fsHelper from './fs-helper'
+import * as fsHelper from './fs-helper.js'
 import * as github from '@actions/github'
 import * as path from 'path'
-import * as unsafePrCheckoutHelper from './unsafe-pr-checkout-helper'
-import * as workflowContextHelper from './workflow-context-helper'
-import {IGitSourceSettings} from './git-source-settings'
+import * as unsafePrCheckoutHelper from './unsafe-pr-checkout-helper.js'
+import * as workflowContextHelper from './workflow-context-helper.js'
+import {IGitSourceSettings} from './git-source-settings.js'
 
 export async function getInputs(): Promise<IGitSourceSettings> {
   const result = {} as unknown as IGitSourceSettings

+ 9 - 9
src/main.ts

@@ -1,9 +1,11 @@
 import * as core from '@actions/core'
-import * as coreCommand from '@actions/core/lib/command'
-import * as gitSourceProvider from './git-source-provider'
-import * as inputHelper from './input-helper'
+import * as gitSourceProvider from './git-source-provider.js'
+import * as inputHelper from './input-helper.js'
 import * as path from 'path'
-import * as stateHelper from './state-helper'
+import * as stateHelper from './state-helper.js'
+import {fileURLToPath} from 'url'
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
 
 async function run(): Promise<void> {
   try {
@@ -11,10 +13,8 @@ async function run(): Promise<void> {
 
     try {
       // Register problem matcher
-      coreCommand.issueCommand(
-        'add-matcher',
-        {},
-        path.join(__dirname, 'problem-matcher.json')
+      core.info(
+        `::add-matcher::${path.join(__dirname, 'problem-matcher.json')}`
       )
 
       // Get sources
@@ -22,7 +22,7 @@ async function run(): Promise<void> {
       core.setOutput('ref', sourceSettings.ref)
     } finally {
       // Unregister problem matcher
-      coreCommand.issueCommand('remove-matcher', {owner: 'checkout-git'}, '')
+      core.info('::remove-matcher owner=checkout-git::')
     }
   } catch (error) {
     core.setFailed(`${(error as any)?.message ?? error}`)

+ 4 - 1
src/misc/generate-docs.ts

@@ -2,6 +2,9 @@ import * as fs from 'fs'
 import * as os from 'os'
 import * as path from 'path'
 import * as yaml from 'js-yaml'
+import {fileURLToPath} from 'url'
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
 
 //
 // SUMMARY
@@ -120,7 +123,7 @@ function updateUsage(
 }
 
 updateUsage(
-  'actions/checkout@v6',
+  'actions/checkout@v7',
   path.join(__dirname, '..', '..', 'action.yml'),
   path.join(__dirname, '..', '..', 'README.md')
 )

+ 2 - 2
src/ref-helper.ts

@@ -1,7 +1,7 @@
-import {IGitCommandManager} from './git-command-manager'
+import {IGitCommandManager} from './git-command-manager.js'
 import * as core from '@actions/core'
 import * as github from '@actions/github'
-import {getServerApiUrl, isGhes} from './url-helper'
+import {getServerApiUrl, isGhes} from './url-helper.js'
 
 export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
 

+ 1 - 1
src/unsafe-pr-checkout-helper.ts

@@ -1,5 +1,5 @@
 import * as github from '@actions/github'
-import {fromPayload} from './ref-helper'
+import {fromPayload} from './ref-helper.js'
 
 const PR_REF_PATTERN = /^refs\/pull\/[0-9]+\/(?:head|merge)$/
 

+ 1 - 1
src/url-helper.ts

@@ -1,6 +1,6 @@
 import * as assert from 'assert'
 import {URL} from 'url'
-import {IGitSourceSettings} from './git-source-settings'
+import {IGitSourceSettings} from './git-source-settings.js'
 
 export function getFetchUrl(settings: IGitSourceSettings): string {
   assert.ok(

+ 5 - 9
tsconfig.json

@@ -1,17 +1,13 @@
 {
   "compilerOptions": {
-    "target": "es6",
-    "module": "commonjs",
-    "lib": [
-      "es6"
-    ],
+    "target": "ES2022",
+    "module": "NodeNext",
+    "moduleResolution": "NodeNext",
     "outDir": "./lib",
     "rootDir": "./src",
-    "declaration": true,
     "strict": true,
     "noImplicitAny": false,
-    "esModuleInterop": true,
-    "skipLibCheck": true
+    "esModuleInterop": true
   },
-  "exclude": ["__test__", "lib", "node_modules"]
+  "exclude": ["__test__", "lib", "node_modules", "jest.config.ts"]
 }

Неке датотеке нису приказане због велике количине промена