mirror of
https://github.com/actions/checkout.git
synced 2026-07-11 19:23:49 +08:00
Compare commits
12 Commits
ce8654db7e
...
v7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c091bb21b | ||
|
|
1044a6dea9 | ||
|
|
f0282184c7 | ||
|
|
d914b262ff | ||
|
|
537c7ef99c | ||
|
|
130a169078 | ||
|
|
7d09575332 | ||
|
|
0f9f3aa320 | ||
|
|
f9e715a95f | ||
|
|
df4cb1c069 | ||
|
|
1cce3390c2 | ||
|
|
900f2210b1 |
121
.github/copilot-instructions.md
vendored
121
.github/copilot-instructions.md
vendored
@@ -1,121 +0,0 @@
|
|||||||
# GitHub Actions Checkout - AI Coding Instructions
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
This is the official `actions/checkout` GitHub Action for checking out repositories in workflows. It's a TypeScript project that compiles to a single bundled JavaScript file (`dist/index.js`) and supports both git-based and REST API-based repository downloads.
|
|
||||||
|
|
||||||
## Architecture & Key Components
|
|
||||||
|
|
||||||
### Core Entry Points
|
|
||||||
- **`src/main.ts`**: Main entry point with `run()` and `cleanup()` functions, determined by `stateHelper.IsPost`
|
|
||||||
- **`src/git-source-provider.ts`**: Primary orchestrator for repository acquisition (git vs REST API fallback)
|
|
||||||
- **`src/input-helper.ts`**: Input validation and GitHub Actions input processing
|
|
||||||
- **`action.yml`**: Defines the action interface with comprehensive input/output schema
|
|
||||||
|
|
||||||
### Critical Data Flow
|
|
||||||
1. `main.ts` → `inputHelper.getInputs()` → validates and transforms action inputs
|
|
||||||
2. `main.ts` → `gitSourceProvider.getSource(settings)` → orchestrates repository download
|
|
||||||
3. `git-source-provider.ts` decides: use Git CLI or fallback to GitHub REST API
|
|
||||||
4. State management via `state-helper.ts` for POST action cleanup
|
|
||||||
|
|
||||||
### Authentication & Security Patterns
|
|
||||||
- **Token-based auth**: Uses `@actions/core` to handle GitHub tokens securely
|
|
||||||
- **SSH key management**: Configures temporary SSH keys in `git-auth-helper.ts`
|
|
||||||
- **Safe directory**: Automatically configures `git config safe.directory` for container compatibility
|
|
||||||
|
|
||||||
## Development Workflow
|
|
||||||
|
|
||||||
### Essential Commands
|
|
||||||
```bash
|
|
||||||
npm ci # Install dependencies
|
|
||||||
npm run build # TypeScript → JavaScript + bundle with ncc + generate docs
|
|
||||||
npm run format # Prettier formatting
|
|
||||||
npm run lint # ESLint validation
|
|
||||||
npm test # Jest test suite
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build Process (Critical!)
|
|
||||||
- **`npm run build`** runs: `tsc && ncc build && node lib/misc/generate-docs.js`
|
|
||||||
- **Documentation sync**: `src/misc/generate-docs.ts` auto-updates README.md usage section from `action.yml`
|
|
||||||
- **Bundling**: Uses `@vercel/ncc` to create single `dist/index.js` file
|
|
||||||
- **Always run `npm run build` before commits** - the `dist/` directory must be up-to-date
|
|
||||||
|
|
||||||
### Testing Strategy
|
|
||||||
- **Unit tests**: Jest tests in `__test__/` for all core modules
|
|
||||||
- **Integration tests**: Shell scripts (`__test__/verify-*.sh`) test real git operations
|
|
||||||
- **E2E tests**: `.github/workflows/test.yml` tests across OS matrix with actual GitHub repos
|
|
||||||
|
|
||||||
## Project-Specific Conventions
|
|
||||||
|
|
||||||
### TypeScript Patterns
|
|
||||||
- **Interface-driven**: `IGitSourceSettings` centralizes all configuration
|
|
||||||
- **Async/await**: All I/O operations use async patterns, not promises
|
|
||||||
- **Error handling**: Use `core.setFailed()` for action failures, `core.warning()` for non-critical issues
|
|
||||||
|
|
||||||
### Git Operation Patterns
|
|
||||||
```typescript
|
|
||||||
// Check Git version and fallback pattern
|
|
||||||
const git = await getGitCommandManager(settings)
|
|
||||||
if (git) {
|
|
||||||
// Use Git CLI
|
|
||||||
await git.fetch(refSpec, fetchDepth)
|
|
||||||
} else {
|
|
||||||
// Fallback to REST API
|
|
||||||
await githubApiHelper.downloadRepository(...)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### State Management (Unique Pattern!)
|
|
||||||
- **Dual-phase execution**: Same script runs twice (MAIN + POST) determined by `stateHelper.IsPost`
|
|
||||||
- **State persistence**: Use `core.saveState()` / `core.getState()` to pass data between phases
|
|
||||||
- **Cleanup responsibility**: POST phase cleans up auth tokens, SSH keys, etc.
|
|
||||||
|
|
||||||
### Input Validation Approach
|
|
||||||
- **GitHub context integration**: Defaults repository from `github.context.repo`
|
|
||||||
- **Path safety**: Validates paths are within `GITHUB_WORKSPACE`
|
|
||||||
- **Flexible refs**: Handles branches, tags, SHAs, and PR refs uniformly
|
|
||||||
|
|
||||||
## Integration Points
|
|
||||||
|
|
||||||
### GitHub Actions SDK Usage
|
|
||||||
- **`@actions/core`**: Input/output, logging, state management
|
|
||||||
- **`@actions/github`**: GitHub context and API access
|
|
||||||
- **`@actions/exec`**: Git command execution
|
|
||||||
- **`@actions/io`**: File system operations
|
|
||||||
|
|
||||||
### Git Integration
|
|
||||||
- **Version compatibility**: Minimum Git 2.18, with feature detection for sparse-checkout
|
|
||||||
- **Authentication modes**: Token-based (default) or SSH key-based
|
|
||||||
- **Advanced features**: LFS, submodules, sparse-checkout, partial clones
|
|
||||||
|
|
||||||
### Container Support
|
|
||||||
- **Safe directory**: Critical for container workflows - auto-configures git safe.directory
|
|
||||||
- **Credential persistence**: Configures git credential helper for authenticated operations
|
|
||||||
|
|
||||||
## Common Debugging Patterns
|
|
||||||
|
|
||||||
### Enable Debug Logging
|
|
||||||
```yaml
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v5
|
|
||||||
env:
|
|
||||||
ACTIONS_STEP_DEBUG: true
|
|
||||||
```
|
|
||||||
|
|
||||||
### REST API Fallback Testing
|
|
||||||
```bash
|
|
||||||
# Force REST API mode by overriding Git version
|
|
||||||
__test__/override-git-version.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### Authentication Issues
|
|
||||||
- Check `GITHUB_TOKEN` permissions: needs `contents: read`
|
|
||||||
- For private repos: requires PAT with repo access
|
|
||||||
- Container issues: verify safe.directory configuration
|
|
||||||
|
|
||||||
## Key Files for Understanding
|
|
||||||
- `src/git-source-provider.ts` - Main orchestration logic
|
|
||||||
- `src/input-helper.ts` - Action interface and validation
|
|
||||||
- `src/git-auth-helper.ts` - Authentication and credential management
|
|
||||||
- `action.yml` - Complete input/output specification
|
|
||||||
- `.github/workflows/test.yml` - Comprehensive test scenarios
|
|
||||||
41
.github/workflows/deno.yml
vendored
41
.github/workflows/deno.yml
vendored
@@ -1,41 +0,0 @@
|
|||||||
# This workflow uses actions that are not certified by GitHub.
|
|
||||||
# They are provided by a third-party and are governed by
|
|
||||||
# separate terms of service, privacy policy, and support
|
|
||||||
# documentation.
|
|
||||||
|
|
||||||
# This workflow will install Deno then run `deno lint` and `deno test`.
|
|
||||||
# For more information see: https://github.com/denoland/setup-deno
|
|
||||||
|
|
||||||
name: Deno
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["main"]
|
|
||||||
pull_request:
|
|
||||||
branches: ["main"]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Setup repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Deno
|
|
||||||
uses: denoland/setup-deno@v1.1.2
|
|
||||||
with:
|
|
||||||
deno-version: v1.x
|
|
||||||
|
|
||||||
# Uncomment this step to verify the use of 'deno fmt' on each commit.
|
|
||||||
# - name: Verify formatting
|
|
||||||
# run: deno fmt --check
|
|
||||||
|
|
||||||
- name: Run linter
|
|
||||||
run: deno lint
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: deno test -A
|
|
||||||
@@ -17,4 +17,4 @@ jobs:
|
|||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
- name: Publish
|
- name: Publish
|
||||||
id: publish
|
id: publish
|
||||||
uses: actions/publish-immutable-action@0.0.3
|
uses: actions/publish-immutable-action@v0.0.4
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ allowed:
|
|||||||
- unlicense
|
- unlicense
|
||||||
|
|
||||||
reviewed:
|
reviewed:
|
||||||
npm:
|
npm:
|
||||||
|
- "@actions/http-client" # MIT
|
||||||
|
|||||||
2
.licenses/npm/@actions/core.dep.yml
generated
2
.licenses/npm/@actions/core.dep.yml
generated
@@ -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
|
||||||
|
|||||||
2
.licenses/npm/@actions/exec.dep.yml
generated
2
.licenses/npm/@actions/exec.dep.yml
generated
@@ -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
|
||||||
|
|||||||
2
.licenses/npm/@actions/github.dep.yml
generated
2
.licenses/npm/@actions/github.dep.yml
generated
@@ -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
|
||||||
|
|||||||
@@ -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
.licenses/npm/@actions/http-client-4.0.1.dep.yml
generated
Normal file
32
.licenses/npm/@actions/http-client-4.0.1.dep.yml
generated
Normal file
@@ -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: []
|
||||||
2
.licenses/npm/@actions/io.dep.yml
generated
2
.licenses/npm/@actions/io.dep.yml
generated
@@ -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
|
||||||
|
|||||||
2
.licenses/npm/@actions/tool-cache.dep.yml
generated
2
.licenses/npm/@actions/tool-cache.dep.yml
generated
@@ -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
|
||||||
|
|||||||
30
.licenses/npm/@fastify/busboy.dep.yml
generated
30
.licenses/npm/@fastify/busboy.dep.yml
generated
@@ -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: []
|
|
||||||
2
.licenses/npm/@octokit/auth-token.dep.yml
generated
2
.licenses/npm/@octokit/auth-token.dep.yml
generated
@@ -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:
|
||||||
|
|||||||
2
.licenses/npm/@octokit/core.dep.yml
generated
2
.licenses/npm/@octokit/core.dep.yml
generated
@@ -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:
|
||||||
|
|||||||
4
.licenses/npm/@octokit/endpoint.dep.yml
generated
4
.licenses/npm/@octokit/endpoint.dep.yml
generated
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/endpoint"
|
name: "@octokit/endpoint"
|
||||||
version: 9.0.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:
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
|
|||||||
2
.licenses/npm/@octokit/graphql.dep.yml
generated
2
.licenses/npm/@octokit/graphql.dep.yml
generated
@@ -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
.licenses/npm/@octokit/openapi-types-22.1.0.dep.yml
generated
20
.licenses/npm/@octokit/openapi-types-22.1.0.dep.yml
generated
@@ -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: []
|
|
||||||
@@ -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,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
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:
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
4
.licenses/npm/@octokit/request-error.dep.yml
generated
4
.licenses/npm/@octokit/request-error.dep.yml
generated
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/request-error"
|
name: "@octokit/request-error"
|
||||||
version: 5.1.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:
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
|
|||||||
4
.licenses/npm/@octokit/request.dep.yml
generated
4
.licenses/npm/@octokit/request.dep.yml
generated
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
name: "@octokit/request"
|
name: "@octokit/request"
|
||||||
version: 8.4.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
|
||||||
homepage:
|
homepage:
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE
|
- sources: LICENSE
|
||||||
|
|||||||
20
.licenses/npm/@octokit/types-13.4.1.dep.yml
generated
20
.licenses/npm/@octokit/types-13.4.1.dep.yml
generated
@@ -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,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:
|
||||||
2
.licenses/npm/before-after-hook.dep.yml
generated
2
.licenses/npm/before-after-hook.dep.yml
generated
@@ -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:
|
||||||
|
|||||||
47
.licenses/npm/content-type.dep.yml
generated
Normal file
47
.licenses/npm/content-type.dep.yml
generated
Normal file
@@ -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: []
|
||||||
28
.licenses/npm/deprecation.dep.yml
generated
28
.licenses/npm/deprecation.dep.yml
generated
@@ -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: []
|
|
||||||
@@ -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>
|
|
||||||
26
.licenses/npm/once.dep.yml
generated
26
.licenses/npm/once.dep.yml
generated
@@ -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: []
|
|
||||||
2
.licenses/npm/semver.dep.yml
generated
2
.licenses/npm/semver.dep.yml
generated
@@ -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:
|
||||||
|
|||||||
2
.licenses/npm/undici.dep.yml
generated
2
.licenses/npm/undici.dep.yml
generated
@@ -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
|
||||||
|
|||||||
6
.licenses/npm/universal-user-agent.dep.yml
generated
6
.licenses/npm/universal-user-agent.dep.yml
generated
@@ -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.
|
||||||
|
|
||||||
|
|||||||
20
.licenses/npm/uuid-8.3.2.dep.yml
generated
20
.licenses/npm/uuid-8.3.2.dep.yml
generated
@@ -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: []
|
|
||||||
20
.licenses/npm/uuid-9.0.1.dep.yml
generated
20
.licenses/npm/uuid-9.0.1.dep.yml
generated
@@ -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: []
|
|
||||||
26
.licenses/npm/wrappy.dep.yml
generated
26
.licenses/npm/wrappy.dep.yml
generated
@@ -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: []
|
|
||||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,5 +1,18 @@
|
|||||||
# Changelog
|
# 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
|
||||||
|
|
||||||
## v6.0.2
|
## v6.0.2
|
||||||
* Fix tag handling: preserve annotations and explicit fetch-tags by @ericsciple in https://github.com/actions/checkout/pull/2356
|
* Fix tag handling: preserve annotations and explicit fetch-tags by @ericsciple in https://github.com/actions/checkout/pull/2356
|
||||||
|
|
||||||
|
|||||||
213
LICENSE
213
LICENSE
@@ -1,201 +1,22 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
The MIT License (MIT)
|
||||||
|
|
||||||
1. Definitions.
|
Copyright (c) 2018 GitHub, Inc. and contributors
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
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:
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
The above copyright notice and this permission notice shall be included in
|
||||||
the copyright owner that is granting the License.
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
other entities that control, are controlled by, or are under common
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
control with that entity. For the purposes of this definition,
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
direction or management of such entity, whether by contract or
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
THE SOFTWARE.
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|||||||
53
README.md
53
README.md
@@ -1,5 +1,14 @@
|
|||||||
[](https://github.com/actions/checkout/actions/workflows/test.yml)
|
[](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
|
# Checkout v6
|
||||||
|
|
||||||
## What's new
|
## What's new
|
||||||
@@ -15,7 +24,6 @@
|
|||||||
- Updated to the node24 runtime
|
- 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.
|
- This requires a minimum Actions Runner version of [v2.327.1](https://github.com/actions/runner/releases/tag/v2.327.1) to run.
|
||||||
|
|
||||||
|
|
||||||
# Checkout v4
|
# Checkout v4
|
||||||
|
|
||||||
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
|
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
|
||||||
@@ -52,7 +60,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
<!-- start usage -->
|
<!-- start usage -->
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v6
|
- 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 }}
|
||||||
@@ -160,6 +168,15 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
# running from unless specified. Example URLs are https://github.com or
|
# running from unless specified. Example URLs are https://github.com or
|
||||||
# https://my-ghes-server.example.com
|
# https://my-ghes-server.example.com
|
||||||
github-server-url: ''
|
github-server-url: ''
|
||||||
|
|
||||||
|
# Required to check out fork pull request code from a workflow triggered by
|
||||||
|
# `pull_request_target` or `workflow_run`. These workflows run with the base
|
||||||
|
# repository's GITHUB_TOKEN, secrets, default-branch cache scope, and runner
|
||||||
|
# access; fetching and executing a fork's code in that trusted context commonly
|
||||||
|
# leads to "pwn request" vulnerabilities. Set to `true` only after reviewing the
|
||||||
|
# risks at https://gh.io/securely-using-pull_request_target.
|
||||||
|
# Default: false
|
||||||
|
allow-unsafe-pr-checkout: ''
|
||||||
```
|
```
|
||||||
<!-- end usage -->
|
<!-- end usage -->
|
||||||
|
|
||||||
@@ -191,7 +208,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
## Fetch only the root files
|
## Fetch only the root files
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
sparse-checkout: .
|
sparse-checkout: .
|
||||||
```
|
```
|
||||||
@@ -199,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@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
.github
|
.github
|
||||||
@@ -209,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@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
README.md
|
README.md
|
||||||
@@ -219,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@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
```
|
```
|
||||||
@@ -227,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@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
ref: my-branch
|
ref: my-branch
|
||||||
```
|
```
|
||||||
@@ -235,7 +252,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
## Checkout HEAD^
|
## Checkout HEAD^
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- run: git checkout HEAD^
|
- run: git checkout HEAD^
|
||||||
@@ -245,12 +262,12 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
|
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-tools
|
repository: my-org/my-tools
|
||||||
path: my-tools
|
path: my-tools
|
||||||
@@ -261,10 +278,10 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-tools
|
repository: my-org/my-tools
|
||||||
path: my-tools
|
path: my-tools
|
||||||
@@ -275,12 +292,12 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
|
|
||||||
- name: Checkout private tools
|
- name: Checkout private tools
|
||||||
uses: actions/checkout@v6
|
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
|
||||||
@@ -293,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@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
```
|
```
|
||||||
@@ -309,7 +326,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
```
|
```
|
||||||
|
|
||||||
## Push a commit using the built-in token
|
## Push a commit using the built-in token
|
||||||
@@ -320,7 +337,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- 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
|
||||||
@@ -342,7 +359,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.head_ref }}
|
ref: ${{ github.head_ref }}
|
||||||
- run: |
|
- run: |
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -229,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}`,
|
||||||
@@ -529,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
|
||||||
@@ -562,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
|
||||||
@@ -588,7 +609,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
|
||||||
@@ -627,7 +648,7 @@ 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
|
||||||
@@ -809,7 +830,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
|
|
||||||
// Mock getSubmoduleConfigPaths to return our fake submodules (for both configure and remove)
|
// Mock getSubmoduleConfigPaths to return our fake submodules (for both configure and remove)
|
||||||
const mockGetSubmoduleConfigPaths =
|
const mockGetSubmoduleConfigPaths =
|
||||||
git.getSubmoduleConfigPaths as jest.Mock<any, any>
|
git.getSubmoduleConfigPaths as jest.Mock<any>
|
||||||
mockGetSubmoduleConfigPaths.mockResolvedValue([
|
mockGetSubmoduleConfigPaths.mockResolvedValue([
|
||||||
submodule1ConfigPath,
|
submodule1ConfigPath,
|
||||||
submodule2ConfigPath
|
submodule2ConfigPath
|
||||||
@@ -1147,7 +1168,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
),
|
),
|
||||||
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',
|
||||||
@@ -1173,7 +1194,8 @@ async function setup(testName: string): Promise<void> {
|
|||||||
sshUser: '',
|
sshUser: '',
|
||||||
workflowOrganizationId: 123456,
|
workflowOrganizationId: 123456,
|
||||||
setSafeDirectory: true,
|
setSafeDirectory: true,
|
||||||
githubServerUrl: githubServerUrl
|
githubServerUrl: githubServerUrl,
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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', 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
|
||||||
@@ -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 () => {
|
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 workingDirectory = 'test'
|
||||||
const lfs = false
|
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 () => {
|
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 workingDirectory = 'test'
|
||||||
const lfs = false
|
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 () => {
|
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
|
||||||
@@ -261,7 +286,7 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
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
|
||||||
@@ -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 () => {
|
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
|
||||||
@@ -378,14 +403,67 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('git user-agent with orchestration ID', () => {
|
describe('repository initialization object format', () => {
|
||||||
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()
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
// Clean up environment variable to prevent test pollution
|
||||||
delete process.env['ACTIONS_ORCHESTRATION_ID']
|
delete process.env['ACTIONS_ORCHESTRATION_ID']
|
||||||
})
|
})
|
||||||
@@ -395,7 +473,7 @@ describe('git user-agent with orchestration ID', () => {
|
|||||||
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
|
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
|
||||||
|
|
||||||
let capturedEnv: any = null
|
let capturedEnv: any = null
|
||||||
mockExec.mockImplementation((path, args, options) => {
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
if (args.includes('version')) {
|
if (args.includes('version')) {
|
||||||
options.listeners.stdout(Buffer.from('2.18'))
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
}
|
}
|
||||||
@@ -403,7 +481,7 @@ describe('git user-agent with orchestration ID', () => {
|
|||||||
capturedEnv = options.env
|
capturedEnv = options.env
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
@@ -430,7 +508,7 @@ describe('git user-agent with orchestration ID', () => {
|
|||||||
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
|
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
|
||||||
|
|
||||||
let capturedEnv: any = null
|
let capturedEnv: any = null
|
||||||
mockExec.mockImplementation((path, args, options) => {
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
if (args.includes('version')) {
|
if (args.includes('version')) {
|
||||||
options.listeners.stdout(Buffer.from('2.18'))
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
}
|
}
|
||||||
@@ -438,7 +516,7 @@ describe('git user-agent with orchestration ID', () => {
|
|||||||
capturedEnv = options.env
|
capturedEnv = options.env
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
@@ -464,7 +542,7 @@ describe('git user-agent with orchestration ID', () => {
|
|||||||
delete process.env['ACTIONS_ORCHESTRATION_ID']
|
delete process.env['ACTIONS_ORCHESTRATION_ID']
|
||||||
|
|
||||||
let capturedEnv: any = null
|
let capturedEnv: any = null
|
||||||
mockExec.mockImplementation((path, args, options) => {
|
mockExec.mockImplementation((path: any, args: any, options: any) => {
|
||||||
if (args.includes('version')) {
|
if (args.includes('version')) {
|
||||||
options.listeners.stdout(Buffer.from('2.18'))
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
}
|
}
|
||||||
@@ -472,7 +550,7 @@ describe('git user-agent with orchestration ID', () => {
|
|||||||
capturedEnv = options.env
|
capturedEnv = options.env
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
// exec.exec is already mockExec
|
||||||
|
|
||||||
const workingDirectory = 'test'
|
const workingDirectory = 'test'
|
||||||
const lfs = false
|
const lfs = false
|
||||||
|
|||||||
@@ -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']
|
||||||
@@ -507,5 +529,5 @@ async function setup(testName: string): Promise<void> {
|
|||||||
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 () => {
|
||||||
|
|||||||
112
__test__/github-api-helper.test.ts
Normal file
112
__test__/github-api-helper.test.ts
Normal file
@@ -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'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -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,42 +15,58 @@ 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
|
||||||
|
|
||||||
// Shallow clone original @actions/github context
|
// Mutable mock github context
|
||||||
let originalContext = {...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', () => {
|
describe('input-helper tests', () => {
|
||||||
beforeAll(() => {
|
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
|
// GitHub workspace
|
||||||
process.env['GITHUB_WORKSPACE'] = gitHubWorkspace
|
process.env['GITHUB_WORKSPACE'] = gitHubWorkspace
|
||||||
})
|
})
|
||||||
@@ -55,6 +74,15 @@ 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(
|
||||||
|
(name: string) => inputs[name]
|
||||||
|
)
|
||||||
|
mockDirectoryExistsSync.mockImplementation(
|
||||||
|
(p: string) => p === gitHubWorkspace
|
||||||
|
)
|
||||||
|
mockGetOrganizationId.mockResolvedValue(123456)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
@@ -65,11 +93,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 () => {
|
||||||
@@ -91,18 +116,19 @@ describe('input-helper tests', () => {
|
|||||||
expect(settings.repositoryOwner).toBe('some-owner')
|
expect(settings.repositoryOwner).toBe('some-owner')
|
||||||
expect(settings.repositoryPath).toBe(gitHubWorkspace)
|
expect(settings.repositoryPath).toBe(gitHubWorkspace)
|
||||||
expect(settings.setSafeDirectory).toBe(true)
|
expect(settings.setSafeDirectory).toBe(true)
|
||||||
|
expect(settings.allowUnsafePrCheckout).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -133,6 +159,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()
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -156,14 +195,12 @@ describe('ref-helper tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('getRefSpec sha + refs/tags/ with fetchTags', async () => {
|
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)
|
const refSpec = refHelper.getRefSpec('refs/tags/my-tag', commit, true)
|
||||||
expect(refSpec.length).toBe(1)
|
expect(refSpec.length).toBe(1)
|
||||||
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('getRefSpec sha + refs/heads/ with fetchTags', async () => {
|
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)
|
const refSpec = refHelper.getRefSpec('refs/heads/my/branch', commit, true)
|
||||||
expect(refSpec.length).toBe(2)
|
expect(refSpec.length).toBe(2)
|
||||||
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
@@ -184,7 +221,6 @@ describe('ref-helper tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('getRefSpec unqualified ref only with fetchTags', async () => {
|
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)
|
const refSpec = refHelper.getRefSpec('my-ref', '', true)
|
||||||
expect(refSpec.length).toBe(2)
|
expect(refSpec.length).toBe(2)
|
||||||
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
@@ -212,14 +248,12 @@ describe('ref-helper tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('getRefSpec refs/tags/ only with fetchTags', async () => {
|
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)
|
const refSpec = refHelper.getRefSpec('refs/tags/my-tag', '', true)
|
||||||
expect(refSpec.length).toBe(1)
|
expect(refSpec.length).toBe(1)
|
||||||
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('getRefSpec refs/heads/ only with fetchTags', async () => {
|
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)
|
const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '', true)
|
||||||
expect(refSpec.length).toBe(2)
|
expect(refSpec.length).toBe(2)
|
||||||
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
|
||||||
@@ -227,4 +261,140 @@ describe('ref-helper tests', () => {
|
|||||||
'+refs/heads/my/branch:refs/remotes/origin/my/branch'
|
'+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()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
284
__test__/unsafe-pr-checkout-helper.test.ts
Normal file
284
__test__/unsafe-pr-checkout-helper.test.ts
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
import {
|
||||||
|
jest,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals'
|
||||||
|
|
||||||
|
const BASE_REPO_ID = 100
|
||||||
|
const FORK_REPO_ID = 200
|
||||||
|
const PR_HEAD_SHA = '1111111111111111111111111111111111111111'
|
||||||
|
const PR_MERGE_SHA = '2222222222222222222222222222222222222222'
|
||||||
|
const SAFE_BASE_SHA = '3333333333333333333333333333333333333333'
|
||||||
|
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 {
|
||||||
|
mockContext.eventName = eventName
|
||||||
|
mockContext.payload = payload
|
||||||
|
}
|
||||||
|
|
||||||
|
function forkPullRequestTargetPayload(): object {
|
||||||
|
return {
|
||||||
|
repository: {id: BASE_REPO_ID},
|
||||||
|
pull_request: {
|
||||||
|
head: {
|
||||||
|
sha: PR_HEAD_SHA,
|
||||||
|
repo: {id: FORK_REPO_ID, full_name: FORK_QUALIFIED_REPO}
|
||||||
|
},
|
||||||
|
merge_commit_sha: PR_MERGE_SHA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sameRepoPullRequestTargetPayload(): object {
|
||||||
|
return {
|
||||||
|
repository: {id: BASE_REPO_ID},
|
||||||
|
pull_request: {
|
||||||
|
head: {
|
||||||
|
sha: PR_HEAD_SHA,
|
||||||
|
repo: {id: BASE_REPO_ID, full_name: BASE_QUALIFIED_REPO}
|
||||||
|
},
|
||||||
|
merge_commit_sha: PR_MERGE_SHA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function forkWorkflowRunPayload(): object {
|
||||||
|
return {
|
||||||
|
repository: {id: BASE_REPO_ID},
|
||||||
|
workflow_run: {
|
||||||
|
event: 'pull_request',
|
||||||
|
head_commit: {id: WORKFLOW_RUN_HEAD_COMMIT_SHA},
|
||||||
|
head_repository: {id: FORK_REPO_ID, full_name: FORK_QUALIFIED_REPO}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('unsafe-pr-checkout-helper', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
mockContext.repo = {owner: 'some-owner', repo: 'some-repo'}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mockContext.eventName = originalEventName
|
||||||
|
mockContext.payload = originalPayload
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mockContext.eventName = originalEventName
|
||||||
|
mockContext.payload = originalPayload
|
||||||
|
})
|
||||||
|
|
||||||
|
it('allows pull_request events untouched', () => {
|
||||||
|
setContext('pull_request', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: 'attacker/fork',
|
||||||
|
ref: 'refs/pull/1/merge',
|
||||||
|
commit: '',
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('allows pull_request_target default checkout (base branch)', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: 'refs/heads/main',
|
||||||
|
commit: SAFE_BASE_SHA,
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('allows same-repo pull_request_target checkout of PR head', () => {
|
||||||
|
setContext('pull_request_target', sameRepoPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: '',
|
||||||
|
commit: PR_HEAD_SHA,
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses pull_request_target fork PR head SHA checkout', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: '',
|
||||||
|
commit: PR_HEAD_SHA,
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow(/Refusing to check out fork pull request code/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses pull_request_target fork PR merge_commit_sha checkout', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: '',
|
||||||
|
commit: PR_MERGE_SHA,
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow(/allow-unsafe-pr-checkout/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses pull_request_target fork PR ref pattern (head)', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: 'refs/pull/42/head',
|
||||||
|
commit: '',
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses pull_request_target fork PR ref pattern (merge)', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: 'refs/pull/42/merge',
|
||||||
|
commit: '',
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses pull_request_target when repository points at the fork', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: FORK_QUALIFIED_REPO,
|
||||||
|
ref: 'refs/heads/main',
|
||||||
|
commit: '',
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('allows pull_request_target checkout of an unrelated third-party repo', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: 'some-other/unrelated',
|
||||||
|
ref: 'refs/heads/main',
|
||||||
|
commit: '',
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses pull_request_target ignoring repository case differences', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: FORK_QUALIFIED_REPO.toUpperCase(),
|
||||||
|
ref: '',
|
||||||
|
commit: '',
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses pull_request_target ignoring commit SHA case differences', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: '',
|
||||||
|
commit: PR_HEAD_SHA.toUpperCase(),
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('allows pull_request_target fork PR checkout when opted in', () => {
|
||||||
|
setContext('pull_request_target', forkPullRequestTargetPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: 'refs/pull/42/merge',
|
||||||
|
commit: '',
|
||||||
|
allowUnsafePrCheckout: true
|
||||||
|
})
|
||||||
|
).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses workflow_run fork PR head_commit.id checkout', () => {
|
||||||
|
setContext('workflow_run', forkWorkflowRunPayload())
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: '',
|
||||||
|
commit: WORKFLOW_RUN_HEAD_COMMIT_SHA,
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('refuses workflow_run with pull_request_target underlying event', () => {
|
||||||
|
const payload = forkWorkflowRunPayload() as {
|
||||||
|
workflow_run: {event: string}
|
||||||
|
}
|
||||||
|
payload.workflow_run.event = 'pull_request_target'
|
||||||
|
setContext('workflow_run', payload)
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: '',
|
||||||
|
commit: WORKFLOW_RUN_HEAD_COMMIT_SHA,
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('allows workflow_run same-repo PR (head_repository.id matches base)', () => {
|
||||||
|
const payload = forkWorkflowRunPayload() as {
|
||||||
|
workflow_run: {head_repository: {id: number}}
|
||||||
|
}
|
||||||
|
payload.workflow_run.head_repository.id = BASE_REPO_ID
|
||||||
|
setContext('workflow_run', payload)
|
||||||
|
expect(() =>
|
||||||
|
assertSafePrCheckout({
|
||||||
|
qualifiedRepository: BASE_QUALIFIED_REPO,
|
||||||
|
ref: '',
|
||||||
|
commit: WORKFLOW_RUN_HEAD_COMMIT_SHA,
|
||||||
|
allowUnsafePrCheckout: false
|
||||||
|
})
|
||||||
|
).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -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', () => {
|
describe('getServerUrl tests', () => {
|
||||||
it('basics', async () => {
|
it('basics', async () => {
|
||||||
|
|||||||
@@ -98,6 +98,15 @@ inputs:
|
|||||||
github-server-url:
|
github-server-url:
|
||||||
description: The base URL for the GitHub instance that you are trying to clone from, will use environment defaults to fetch from the same instance that the workflow is running from unless specified. Example URLs are https://github.com or https://my-ghes-server.example.com
|
description: The base URL for the GitHub instance that you are trying to clone from, will use environment defaults to fetch from the same instance that the workflow is running from unless specified. Example URLs are https://github.com or https://my-ghes-server.example.com
|
||||||
required: false
|
required: false
|
||||||
|
allow-unsafe-pr-checkout:
|
||||||
|
description: >
|
||||||
|
Required to check out fork pull request code from a workflow triggered by
|
||||||
|
`pull_request_target` or `workflow_run`. These workflows run with the
|
||||||
|
base repository's GITHUB_TOKEN, secrets, default-branch cache scope, and
|
||||||
|
runner access; fetching and executing a fork's code in that trusted
|
||||||
|
context commonly leads to "pwn request" vulnerabilities. Set to `true`
|
||||||
|
only after reviewing the risks at https://gh.io/securely-using-pull_request_target.
|
||||||
|
default: false
|
||||||
outputs:
|
outputs:
|
||||||
ref:
|
ref:
|
||||||
description: 'The branch, tag or SHA that was checked out'
|
description: 'The branch, tag or SHA that was checked out'
|
||||||
|
|||||||
52802
dist/index.js
vendored
52802
dist/index.js
vendored
File diff suppressed because one or more lines are too long
3
dist/package.json
vendored
Normal file
3
dist/package.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
@@ -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
jest.config.ts
Normal file
24
jest.config.ts
Normal file
@@ -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
|
||||||
|
}
|
||||||
804
package-lock.json
generated
804
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@@ -1,14 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "checkout",
|
"name": "checkout",
|
||||||
"version": "5.0.0",
|
"version": "7.0.0",
|
||||||
"description": "checkout action",
|
"description": "checkout action",
|
||||||
|
"type": "module",
|
||||||
"main": "lib/main.js",
|
"main": "lib/main.js",
|
||||||
"scripts": {
|
"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": "prettier --write '**/*.ts'",
|
||||||
"format-check": "prettier --check '**/*.ts'",
|
"format-check": "prettier --check '**/*.ts'",
|
||||||
"lint": "eslint src/**/*.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-check": "src/misc/licensed-check.sh",
|
||||||
"licensed-generate": "src/misc/licensed-generate.sh"
|
"licensed-generate": "src/misc/licensed-generate.sh"
|
||||||
},
|
},
|
||||||
@@ -27,29 +28,30 @@
|
|||||||
"url": "https://github.com/actions/checkout/issues"
|
"url": "https://github.com/actions/checkout/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/actions/checkout#readme",
|
"homepage": "https://github.com/actions/checkout#readme",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=24"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1",
|
"@actions/core": "^3.0.1",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^3.0.0",
|
||||||
"@actions/github": "^6.0.0",
|
"@actions/github": "^9.1.1",
|
||||||
"@actions/io": "^1.1.3",
|
"@actions/io": "^3.0.2",
|
||||||
"@actions/tool-cache": "^2.0.1",
|
"@actions/tool-cache": "^4.0.0"
|
||||||
"uuid": "^9.0.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^24.1.0",
|
"@types/node": "^24.1.0",
|
||||||
"@types/uuid": "^9.0.8",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
||||||
"@typescript-eslint/parser": "^7.9.0",
|
"@typescript-eslint/parser": "^7.9.0",
|
||||||
"@vercel/ncc": "^0.38.1",
|
"@vercel/ncc": "^0.44.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-github": "^4.10.2",
|
"eslint-plugin-github": "^4.10.2",
|
||||||
"eslint-plugin-jest": "^28.8.2",
|
"eslint-plugin-jest": "^28.8.2",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-circus": "^29.7.0",
|
"js-yaml": "^4.2.0",
|
||||||
"js-yaml": "^4.1.0",
|
"prettier": "^3.8.4",
|
||||||
"prettier": "^3.3.3",
|
"ts-jest": "^29.4.11",
|
||||||
"ts-jest": "^29.2.5",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import * as fs from 'fs'
|
|||||||
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 regexpHelper from './regexp-helper'
|
import * as regexpHelper from './regexp-helper.js'
|
||||||
import * as stateHelper from './state-helper'
|
import * as stateHelper from './state-helper.js'
|
||||||
import * as urlHelper from './url-helper'
|
import * as urlHelper from './url-helper.js'
|
||||||
import {v4 as uuid} from 'uuid'
|
import {randomUUID} from 'crypto'
|
||||||
import {IGitCommandManager} from './git-command-manager'
|
import {IGitCommandManager} from './git-command-manager.js'
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
import {IGitSourceSettings} from './git-source-settings.js'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
const SSH_COMMAND_KEY = 'core.sshCommand'
|
const SSH_COMMAND_KEY = 'core.sshCommand'
|
||||||
@@ -90,7 +90,7 @@ class GitAuthHelper {
|
|||||||
// Create a temp home directory
|
// Create a temp home directory
|
||||||
const runnerTemp = process.env['RUNNER_TEMP'] || ''
|
const runnerTemp = process.env['RUNNER_TEMP'] || ''
|
||||||
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
|
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
|
||||||
const uniqueId = uuid()
|
const uniqueId = randomUUID()
|
||||||
this.temporaryHomePath = path.join(runnerTemp, uniqueId)
|
this.temporaryHomePath = path.join(runnerTemp, uniqueId)
|
||||||
await fs.promises.mkdir(this.temporaryHomePath, {recursive: true})
|
await fs.promises.mkdir(this.temporaryHomePath, {recursive: true})
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ class GitAuthHelper {
|
|||||||
// Write key
|
// Write key
|
||||||
const runnerTemp = process.env['RUNNER_TEMP'] || ''
|
const runnerTemp = process.env['RUNNER_TEMP'] || ''
|
||||||
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
|
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
|
||||||
const uniqueId = uuid()
|
const uniqueId = randomUUID()
|
||||||
this.sshKeyPath = path.join(runnerTemp, uniqueId)
|
this.sshKeyPath = path.join(runnerTemp, uniqueId)
|
||||||
stateHelper.setSshKeyPath(this.sshKeyPath)
|
stateHelper.setSshKeyPath(this.sshKeyPath)
|
||||||
await fs.promises.mkdir(runnerTemp, {recursive: true})
|
await fs.promises.mkdir(runnerTemp, {recursive: true})
|
||||||
@@ -422,7 +422,7 @@ class GitAuthHelper {
|
|||||||
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
|
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
|
||||||
|
|
||||||
// Create a unique filename for this checkout instance
|
// 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)
|
this.credentialsConfigPath = path.join(runnerTemp, configFileName)
|
||||||
|
|
||||||
core.debug(`Credentials config path: ${this.credentialsConfigPath}`)
|
core.debug(`Credentials config path: ${this.credentialsConfigPath}`)
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
import * as fs from 'fs'
|
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 io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as refHelper from './ref-helper'
|
import * as refHelper from './ref-helper.js'
|
||||||
import * as regexpHelper from './regexp-helper'
|
import * as regexpHelper from './regexp-helper.js'
|
||||||
import * as retryHelper from './retry-helper'
|
import * as retryHelper from './retry-helper.js'
|
||||||
import {GitVersion} from './git-version'
|
import {GitVersion} from './git-version.js'
|
||||||
|
|
||||||
// Auth header not supported before 2.9
|
// Auth header not supported before 2.9
|
||||||
// Wire protocol v2 not supported before 2.18
|
// Wire protocol v2 not supported before 2.18
|
||||||
@@ -43,7 +43,7 @@ export interface IGitCommandManager {
|
|||||||
getDefaultBranch(repositoryUrl: string): Promise<string>
|
getDefaultBranch(repositoryUrl: string): Promise<string>
|
||||||
getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
|
getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
|
||||||
getWorkingDirectory(): string
|
getWorkingDirectory(): string
|
||||||
init(): Promise<void>
|
init(objectFormat?: string): Promise<void>
|
||||||
isDetached(): Promise<boolean>
|
isDetached(): Promise<boolean>
|
||||||
lfsFetch(ref: string): Promise<void>
|
lfsFetch(ref: string): Promise<void>
|
||||||
lfsInstall(): Promise<void>
|
lfsInstall(): Promise<void>
|
||||||
@@ -364,8 +364,14 @@ class GitCommandManager {
|
|||||||
return this.workingDirectory
|
return this.workingDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(objectFormat?: string): Promise<void> {
|
||||||
await this.execGit(['init', this.workingDirectory])
|
const args = ['init']
|
||||||
|
if (objectFormat === 'sha256') {
|
||||||
|
args.push('--object-format=sha256')
|
||||||
|
}
|
||||||
|
args.push(this.workingDirectory)
|
||||||
|
|
||||||
|
await this.execGit(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
async isDetached(): Promise<boolean> {
|
async isDetached(): Promise<boolean> {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as assert from 'assert'
|
import * as assert from 'assert'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fs from 'fs'
|
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 io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {IGitCommandManager} from './git-command-manager'
|
import {IGitCommandManager} from './git-command-manager.js'
|
||||||
|
|
||||||
export async function prepareExistingDirectory(
|
export async function prepareExistingDirectory(
|
||||||
git: IGitCommandManager | undefined,
|
git: IGitCommandManager | undefined,
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fsHelper from './fs-helper'
|
import * as fsHelper from './fs-helper.js'
|
||||||
import * as gitAuthHelper from './git-auth-helper'
|
import * as gitAuthHelper from './git-auth-helper.js'
|
||||||
import * as gitCommandManager from './git-command-manager'
|
import * as gitCommandManager from './git-command-manager.js'
|
||||||
import * as gitDirectoryHelper from './git-directory-helper'
|
import * as gitDirectoryHelper from './git-directory-helper.js'
|
||||||
import * as githubApiHelper from './github-api-helper'
|
import * as githubApiHelper from './github-api-helper.js'
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as refHelper from './ref-helper'
|
import * as refHelper from './ref-helper.js'
|
||||||
import * as stateHelper from './state-helper'
|
import * as stateHelper from './state-helper.js'
|
||||||
import * as urlHelper from './url-helper'
|
import * as urlHelper from './url-helper.js'
|
||||||
import {
|
import {
|
||||||
MinimumGitSparseCheckoutVersion,
|
MinimumGitSparseCheckoutVersion,
|
||||||
IGitCommandManager
|
IGitCommandManager
|
||||||
} from './git-command-manager'
|
} from './git-command-manager.js'
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
import {IGitSourceSettings} from './git-source-settings.js'
|
||||||
|
|
||||||
export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
||||||
// Repository URL
|
// Repository URL
|
||||||
@@ -109,8 +109,25 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
if (
|
if (
|
||||||
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
|
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
|
||||||
) {
|
) {
|
||||||
|
core.startGroup('Determining repository object format')
|
||||||
|
const objectFormatResult =
|
||||||
|
await githubApiHelper.tryGetRepositoryObjectFormat(
|
||||||
|
settings.authToken,
|
||||||
|
settings.repositoryOwner,
|
||||||
|
settings.repositoryName,
|
||||||
|
settings.githubServerUrl,
|
||||||
|
settings.commit
|
||||||
|
)
|
||||||
|
const objectFormat = objectFormatResult.succeeded
|
||||||
|
? objectFormatResult.format
|
||||||
|
: ''
|
||||||
|
if (objectFormat === 'sha256') {
|
||||||
|
core.info('Detected SHA-256 repository object format')
|
||||||
|
}
|
||||||
|
core.endGroup()
|
||||||
|
|
||||||
core.startGroup('Initializing the repository')
|
core.startGroup('Initializing the repository')
|
||||||
await git.init()
|
await git.init(objectFormat)
|
||||||
await git.remoteAdd('origin', repositoryUrl)
|
await git.remoteAdd('origin', repositoryUrl)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,4 +118,10 @@ export interface IGitSourceSettings {
|
|||||||
* User override on the GitHub Server/Host URL that hosts the repository to be cloned
|
* User override on the GitHub Server/Host URL that hosts the repository to be cloned
|
||||||
*/
|
*/
|
||||||
githubServerUrl: string | undefined
|
githubServerUrl: string | undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opt-in to allow checking out fork pull request code from a workflow
|
||||||
|
* triggered by pull_request_target or workflow_run.
|
||||||
|
*/
|
||||||
|
allowUnsafePrCheckout: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,18 @@ import * as fs from 'fs'
|
|||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as path from 'path'
|
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 * as toolCache from '@actions/tool-cache'
|
||||||
import {v4 as uuid} from 'uuid'
|
import {randomUUID} from 'crypto'
|
||||||
import {getServerApiUrl} from './url-helper'
|
import {getServerApiUrl} from './url-helper.js'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
|
||||||
|
export interface RepositoryObjectFormatResult {
|
||||||
|
format: string
|
||||||
|
succeeded: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export async function downloadRepository(
|
export async function downloadRepository(
|
||||||
authToken: string,
|
authToken: string,
|
||||||
owner: string,
|
owner: string,
|
||||||
@@ -34,7 +39,7 @@ export async function downloadRepository(
|
|||||||
|
|
||||||
// Write archive to disk
|
// Write archive to disk
|
||||||
core.info('Writing archive to disk')
|
core.info('Writing archive to disk')
|
||||||
const uniqueId = uuid()
|
const uniqueId = randomUUID()
|
||||||
const archivePath = IS_WINDOWS
|
const archivePath = IS_WINDOWS
|
||||||
? path.join(repositoryPath, `${uniqueId}.zip`)
|
? path.join(repositoryPath, `${uniqueId}.zip`)
|
||||||
: path.join(repositoryPath, `${uniqueId}.tar.gz`)
|
: path.join(repositoryPath, `${uniqueId}.tar.gz`)
|
||||||
@@ -122,6 +127,53 @@ export async function getDefaultBranch(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function tryGetRepositoryObjectFormat(
|
||||||
|
authToken: string,
|
||||||
|
owner: string,
|
||||||
|
repo: string,
|
||||||
|
baseUrl?: string,
|
||||||
|
commit?: string
|
||||||
|
): Promise<RepositoryObjectFormatResult> {
|
||||||
|
const commitFormat = getObjectFormat(commit)
|
||||||
|
if (commitFormat) {
|
||||||
|
return {format: commitFormat, succeeded: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const octokit = github.getOctokit(authToken, {
|
||||||
|
baseUrl: getServerApiUrl(baseUrl)
|
||||||
|
})
|
||||||
|
const response = await octokit.request(
|
||||||
|
'GET /repos/{owner}/{repo}/hash-algorithm',
|
||||||
|
{owner, repo}
|
||||||
|
)
|
||||||
|
const hashAlgorithm = response.data.hash_algorithm
|
||||||
|
if (hashAlgorithm === 'sha256' || hashAlgorithm === 'sha1') {
|
||||||
|
return {format: hashAlgorithm, succeeded: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.debug(
|
||||||
|
'Unable to determine repository object format from hash-algorithm endpoint'
|
||||||
|
)
|
||||||
|
return {format: '', succeeded: false}
|
||||||
|
} catch (err) {
|
||||||
|
core.debug(
|
||||||
|
`Unable to determine repository object format from hash-algorithm endpoint: ${(err as any)?.message ?? err}`
|
||||||
|
)
|
||||||
|
return {format: '', succeeded: false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObjectFormat(sha?: string): string {
|
||||||
|
if (/^[0-9a-fA-F]{64}$/.test(sha || '')) {
|
||||||
|
return 'sha256'
|
||||||
|
}
|
||||||
|
if (/^[0-9a-fA-F]{40}$/.test(sha || '')) {
|
||||||
|
return 'sha1'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
async function downloadArchive(
|
async function downloadArchive(
|
||||||
authToken: string,
|
authToken: string,
|
||||||
owner: string,
|
owner: string,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import * as core from '@actions/core'
|
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 github from '@actions/github'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as workflowContextHelper from './workflow-context-helper'
|
import * as unsafePrCheckoutHelper from './unsafe-pr-checkout-helper.js'
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
import * as workflowContextHelper from './workflow-context-helper.js'
|
||||||
|
import {IGitSourceSettings} from './git-source-settings.js'
|
||||||
|
|
||||||
export async function getInputs(): Promise<IGitSourceSettings> {
|
export async function getInputs(): Promise<IGitSourceSettings> {
|
||||||
const result = {} as unknown as IGitSourceSettings
|
const result = {} as unknown as IGitSourceSettings
|
||||||
@@ -71,7 +72,7 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SHA?
|
// SHA?
|
||||||
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) {
|
else if (result.ref.match(/^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$/)) {
|
||||||
result.commit = result.ref
|
result.commit = result.ref
|
||||||
result.ref = ''
|
result.ref = ''
|
||||||
}
|
}
|
||||||
@@ -161,5 +162,18 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
|||||||
result.githubServerUrl = core.getInput('github-server-url')
|
result.githubServerUrl = core.getInput('github-server-url')
|
||||||
core.debug(`GitHub Host URL = ${result.githubServerUrl}`)
|
core.debug(`GitHub Host URL = ${result.githubServerUrl}`)
|
||||||
|
|
||||||
|
// Allow unsafe PR checkout (opt-in for pull_request_target / workflow_run fork PRs)
|
||||||
|
result.allowUnsafePrCheckout =
|
||||||
|
(core.getInput('allow-unsafe-pr-checkout') || 'false').toUpperCase() ===
|
||||||
|
'TRUE'
|
||||||
|
core.debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`)
|
||||||
|
|
||||||
|
unsafePrCheckoutHelper.assertSafePrCheckout({
|
||||||
|
qualifiedRepository,
|
||||||
|
ref: result.ref,
|
||||||
|
commit: result.commit,
|
||||||
|
allowUnsafePrCheckout: result.allowUnsafePrCheckout
|
||||||
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/main.ts
18
src/main.ts
@@ -1,9 +1,11 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as coreCommand from '@actions/core/lib/command'
|
import * as gitSourceProvider from './git-source-provider.js'
|
||||||
import * as gitSourceProvider from './git-source-provider'
|
import * as inputHelper from './input-helper.js'
|
||||||
import * as inputHelper from './input-helper'
|
|
||||||
import * as path from 'path'
|
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> {
|
async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@@ -11,10 +13,8 @@ async function run(): Promise<void> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Register problem matcher
|
// Register problem matcher
|
||||||
coreCommand.issueCommand(
|
core.info(
|
||||||
'add-matcher',
|
`::add-matcher::${path.join(__dirname, 'problem-matcher.json')}`
|
||||||
{},
|
|
||||||
path.join(__dirname, 'problem-matcher.json')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get sources
|
// Get sources
|
||||||
@@ -22,7 +22,7 @@ async function run(): Promise<void> {
|
|||||||
core.setOutput('ref', sourceSettings.ref)
|
core.setOutput('ref', sourceSettings.ref)
|
||||||
} finally {
|
} finally {
|
||||||
// Unregister problem matcher
|
// Unregister problem matcher
|
||||||
coreCommand.issueCommand('remove-matcher', {owner: 'checkout-git'}, '')
|
core.info('::remove-matcher owner=checkout-git::')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(`${(error as any)?.message ?? error}`)
|
core.setFailed(`${(error as any)?.message ?? error}`)
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import * as fs from 'fs'
|
|||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as yaml from 'js-yaml'
|
import * as yaml from 'js-yaml'
|
||||||
|
import {fileURLToPath} from 'url'
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
//
|
//
|
||||||
// SUMMARY
|
// SUMMARY
|
||||||
@@ -120,7 +123,7 @@ function updateUsage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateUsage(
|
updateUsage(
|
||||||
'actions/checkout@v6',
|
'actions/checkout@v7',
|
||||||
path.join(__dirname, '..', '..', 'action.yml'),
|
path.join(__dirname, '..', '..', 'action.yml'),
|
||||||
path.join(__dirname, '..', '..', 'README.md')
|
path.join(__dirname, '..', '..', 'README.md')
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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 core from '@actions/core'
|
||||||
import * as github from '@actions/github'
|
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/*'
|
export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
|
||||||
|
|
||||||
@@ -258,7 +258,9 @@ export async function checkCommitInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract details from message
|
// Extract details from message
|
||||||
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/)
|
const match = commitInfo.match(
|
||||||
|
/Merge ([0-9a-f]{40}|[0-9a-f]{64}) into ([0-9a-f]{40}|[0-9a-f]{64})/
|
||||||
|
)
|
||||||
if (!match) {
|
if (!match) {
|
||||||
core.debug('Unexpected message format')
|
core.debug('Unexpected message format')
|
||||||
return
|
return
|
||||||
@@ -290,7 +292,7 @@ export async function checkCommitInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromPayload(path: string): any {
|
export function fromPayload(path: string): any {
|
||||||
return select(github.context.payload, path)
|
return select(github.context.payload, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
88
src/unsafe-pr-checkout-helper.ts
Normal file
88
src/unsafe-pr-checkout-helper.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import * as github from '@actions/github'
|
||||||
|
import {fromPayload} from './ref-helper.js'
|
||||||
|
|
||||||
|
const PR_REF_PATTERN = /^refs\/pull\/[0-9]+\/(?:head|merge)$/
|
||||||
|
|
||||||
|
export interface IUnsafePrCheckoutInput {
|
||||||
|
qualifiedRepository: string
|
||||||
|
ref: string
|
||||||
|
commit: string | undefined
|
||||||
|
allowUnsafePrCheckout: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertSafePrCheckout(input: IUnsafePrCheckoutInput): void {
|
||||||
|
if (input.allowUnsafePrCheckout) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventName = github.context.eventName
|
||||||
|
if (eventName !== 'pull_request_target' && eventName !== 'workflow_run') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseRepoId = fromPayload('repository.id')
|
||||||
|
if (typeof baseRepoId !== 'number') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let prHeadRepoId: unknown
|
||||||
|
let prHeadRepoFullName: unknown
|
||||||
|
const prShas: string[] = []
|
||||||
|
|
||||||
|
if (eventName === 'pull_request_target') {
|
||||||
|
prHeadRepoId = fromPayload('pull_request.head.repo.id')
|
||||||
|
prHeadRepoFullName = fromPayload('pull_request.head.repo.full_name')
|
||||||
|
pushIfSha(prShas, fromPayload('pull_request.head.sha'))
|
||||||
|
pushIfSha(prShas, fromPayload('pull_request.merge_commit_sha'))
|
||||||
|
} else {
|
||||||
|
const wrEvent = fromPayload('workflow_run.event')
|
||||||
|
if (typeof wrEvent !== 'string' || !wrEvent.startsWith('pull_request')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prHeadRepoId = fromPayload('workflow_run.head_repository.id')
|
||||||
|
prHeadRepoFullName = fromPayload('workflow_run.head_repository.full_name')
|
||||||
|
pushIfSha(prShas, fromPayload('workflow_run.head_commit.id'))
|
||||||
|
// For `pull_request_target`-triggered workflow_run, `head_sha` is the base
|
||||||
|
// default branch SHA (not the PR head)
|
||||||
|
if (wrEvent !== 'pull_request_target') {
|
||||||
|
pushIfSha(prShas, fromPayload('workflow_run.head_sha'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (A) Fork PR?
|
||||||
|
if (typeof prHeadRepoId !== 'number' || prHeadRepoId === baseRepoId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// (B) We cannot check for all fork PR refs so check to see
|
||||||
|
// if the resolved input points to the fork PR sha we have in the payload
|
||||||
|
const repositoryMatchesPrHead =
|
||||||
|
typeof prHeadRepoFullName === 'string' &&
|
||||||
|
input.qualifiedRepository.toLowerCase() === prHeadRepoFullName.toLowerCase()
|
||||||
|
const refMatchesPullPattern = PR_REF_PATTERN.test(input.ref)
|
||||||
|
const commitMatchesPrHeadSha =
|
||||||
|
!!input.commit && prShas.includes(input.commit.toLowerCase())
|
||||||
|
|
||||||
|
if (
|
||||||
|
!repositoryMatchesPrHead &&
|
||||||
|
!refMatchesPullPattern &&
|
||||||
|
!commitMatchesPrHeadSha
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Refusing to check out fork pull request code from a '${eventName}' workflow. ` +
|
||||||
|
`This workflow runs with the base repository's GITHUB_TOKEN, secrets, default-branch ` +
|
||||||
|
`cache scope, and runner access. Fetching and executing a fork's code in that trusted ` +
|
||||||
|
`context commonly leads to "pwn request" vulnerabilities. To opt in, review the risks ` +
|
||||||
|
`at https://gh.io/securely-using-pull_request_target and set 'allow-unsafe-pr-checkout: true' ` +
|
||||||
|
`on the actions/checkout step.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushIfSha(target: string[], value: unknown): void {
|
||||||
|
if (typeof value === 'string' && value.length > 0) {
|
||||||
|
target.push(value.toLowerCase())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as assert from 'assert'
|
import * as assert from 'assert'
|
||||||
import {URL} from 'url'
|
import {URL} from 'url'
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
import {IGitSourceSettings} from './git-source-settings.js'
|
||||||
|
|
||||||
export function getFetchUrl(settings: IGitSourceSettings): string {
|
export function getFetchUrl(settings: IGitSourceSettings): string {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es6",
|
"target": "ES2022",
|
||||||
"module": "commonjs",
|
"module": "NodeNext",
|
||||||
"lib": [
|
"moduleResolution": "NodeNext",
|
||||||
"es6"
|
|
||||||
],
|
|
||||||
"outDir": "./lib",
|
"outDir": "./lib",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"declaration": true,
|
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true
|
||||||
"skipLibCheck": true
|
|
||||||
},
|
},
|
||||||
"exclude": ["__test__", "lib", "node_modules"]
|
"exclude": ["__test__", "lib", "node_modules", "jest.config.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user