Compare commits

..

No commits in common. "main" and "v37.1.1" have entirely different histories.

50 changed files with 11549 additions and 50488 deletions

View File

@ -187,73 +187,6 @@
"contributions": [
"doc"
]
},
{
"login": "V0lantis",
"name": "Arthur",
"avatar_url": "https://avatars.githubusercontent.com/u/37664438?v=4",
"profile": "https://arthurvolant.com",
"contributions": [
"bug",
"code"
]
},
{
"login": "rodrigorfk",
"name": "Rodrigo Fior Kuntzer",
"avatar_url": "https://avatars.githubusercontent.com/u/1995033?v=4",
"profile": "https://github.com/rodrigorfk",
"contributions": [
"code",
"test",
"bug"
]
},
{
"login": "levenleven",
"name": "Aleksey Levenstein",
"avatar_url": "https://avatars.githubusercontent.com/u/6463364?v=4",
"profile": "https://github.com/levenleven",
"contributions": [
"doc"
]
},
{
"login": "dan-hill2802",
"name": "Daniel Hill",
"avatar_url": "https://avatars.githubusercontent.com/u/5046322?v=4",
"profile": "https://github.com/dan-hill2802",
"contributions": [
"doc"
]
},
{
"login": "KeisukeYamashita",
"name": "KeisukeYamashita",
"avatar_url": "https://avatars.githubusercontent.com/u/23056537?v=4",
"profile": "https://keisukeyamashita.com",
"contributions": [
"doc"
]
},
{
"login": "codesculpture",
"name": "Aravind",
"avatar_url": "https://avatars.githubusercontent.com/u/63452117?v=4",
"profile": "https://github.com/codesculpture",
"contributions": [
"code",
"bug"
]
},
{
"login": "Whadup",
"name": "Lukas Pfahler",
"avatar_url": "https://avatars.githubusercontent.com/u/2308119?v=4",
"profile": "https://lukaspfahler.de",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
@ -262,6 +195,5 @@
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true,
"commitConvention": "angular",
"commitType": "docs"
"commitConvention": "angular"
}

View File

@ -1,4 +0,0 @@
---
exclude_paths:
- "*.md"
- "dist/**"

View File

@ -1,5 +1,4 @@
dist/
lib/
node_modules/
jest.config.js
coverage/
jest.config.js

View File

@ -5,8 +5,7 @@
"github"
],
"extends": [
"plugin:github/recommended",
"plugin:prettier/recommended"
"plugin:github/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: jackton1
patreon: # Replace with a single Patreon username
open_collective: tj-actions
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: []

98
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@ -0,0 +1,98 @@
name: 🐞 Bug
description: Create a report to help us improve
title: "[BUG] <title>"
labels: [bug, needs triage]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: checkboxes
attributes:
label: Does this issue exist in the latest version?
description: Please view all releases to confirm that this issue hasn't already been fixed.
options:
- label: I'm using the latest release
required: true
- type: textarea
id: what-happened
attributes:
label: Describe the bug?
description: A clear and concise description of what the bug is
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: To Reproduce
description: Steps to reproduce the behavior?
placeholder: |
1. In this environment...
2. With this config...
3. Run '...'
4. See error...
validations:
required: true
- type: dropdown
id: os
attributes:
label: What OS are you seeing the problem on?
multiple: true
options:
- all
- ubuntu-latest or ubuntu-20.04
- ubuntu-18.04
- macos-latest or macos-10.15
- macos-11
- windows-latest or windows-2019
- windows-2016
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior?
description: A clear and concise description of what you expected to happen.
placeholder: Tell us what you expected!
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output which is obtained after enabling debug logging. This will be automatically formatted into code, so no need for backticks.
placeholder: |
1. Re-running the workflow with debug logging enabled.
2. Copy or download the log archive.
3. Paste the contents here or upload the file in a subsequent comment.
render: shell
validations:
required: true
- type: textarea
attributes:
label: Anything else?
description: |
Links? or References?
Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](../blob/main/CODE_OF_CONDUCT.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@ -0,0 +1,59 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature] <title>"
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request!
- type: checkboxes
attributes:
label: Is this feature missing in the latest version?
description: Please upgrade to the latest version to verify that this feature is still missing.
options:
- label: I'm using the latest release
required: true
- type: textarea
id: what-happened
attributes:
label: Is your feature request related to a problem? Please describe.
description: |
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: requests
attributes:
label: Describe the solution you'd like?
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternative
attributes:
label: Describe alternatives you've considered?
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: textarea
attributes:
label: Anything else?
description: |
Links? or References?
Add any other context or screenshots about the feature request here.
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](../blob/main/CODE_OF_CONDUCT.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@ -15,10 +15,3 @@ updates:
open-pull-requests-limit: 10
labels:
- "merge when passing"
- package-ecosystem: gitsubmodule
directory: /
schedule:
interval: daily
open-pull-requests-limit: 10
labels:
- "merge when passing"

33
.github/workflows/auto-approve.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: Auto approve
on:
pull_request_target
jobs:
auto-approve:
runs-on: ubuntu-latest
steps:
- uses: hmarr/auto-approve-action@v3
if: |
(
github.event.pull_request.user.login == 'dependabot[bot]' ||
github.event.pull_request.user.login == 'dependabot' ||
github.event.pull_request.user.login == 'dependabot-preview[bot]' ||
github.event.pull_request.user.login == 'dependabot-preview' ||
github.event.pull_request.user.login == 'renovate[bot]' ||
github.event.pull_request.user.login == 'renovate' ||
github.event.pull_request.user.login == 'github-actions[bot]'
)
&&
(
github.actor == 'dependabot[bot]' ||
github.actor == 'dependabot' ||
github.actor == 'dependabot-preview[bot]' ||
github.actor == 'dependabot-preview' ||
github.actor == 'renovate[bot]' ||
github.actor == 'renovate' ||
github.actor == 'github-actions[bot]'
)
with:
github-token: ${{ secrets.PAT_TOKEN }}

View File

@ -17,24 +17,24 @@ on:
schedule:
- cron: '15 16 * * 2'
permissions:
actions: read
contents: read
security-events: write
jobs:
codacy-security-scan:
# Cancel other workflows that are running for the same branch
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
name: Codacy Security Scan
runs-on: ubuntu-latest
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
continue-on-error: true
uses: codacy/codacy-analysis-cli-action@v4.4.5
uses: codacy/codacy-analysis-cli-action@v4.3.0
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations
@ -51,6 +51,6 @@ jobs:
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
continue-on-error: true
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarif

View File

@ -20,11 +20,6 @@ on:
schedule:
- cron: '44 20 * * 0'
permissions:
actions: read
contents: read
security-events: write
jobs:
analyze:
name: Analyze
@ -43,11 +38,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -61,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@ -74,6 +69,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@ -2,17 +2,12 @@ name: Greetings
on: [pull_request_target, issues]
permissions:
pull-requests: write
issues: write
jobs:
greeting:
runs-on: ubuntu-latest
steps:
- uses: actions/first-interaction@v1
continue-on-error: true
with:
repo-token: ${{ secrets.PAT_TOKEN }}
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: "Thanks for reporting this issue, don't forget to star this project if you haven't already to help us reach a wider audience."
pr-message: "Thanks for implementing a fix, could you ensure that the test covers your changes if applicable."

View File

@ -1,178 +0,0 @@
name: Issue Comment Job Example
permissions:
contents: read
on:
issue_comment:
jobs:
pr_commented:
# This job only runs for pull request comments
name: PR comment
if: ${{ github.event.issue.pull_request }}
runs-on: ubuntu-latest
steps:
- run: |
echo A comment on PR $NUMBER
env:
NUMBER: ${{ github.event.issue.number }}
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Run changed-files with defaults
id: changed-files
uses: ./
- name: Show output
run: |
echo '${{ toJSON(steps.changed-files.outputs) }}'
shell:
bash
- name: Run changed-files for old new filenames test rename
id: changed-files-all-old-new-renamed-files
uses: ./
with:
base_sha: d1c0ee4
sha: 4d04215
fetch_depth: 60000
include_all_old_new_renamed_files: true
- name: Show output
run: |
echo '${{ toJSON(steps.changed-files-all-old-new-renamed-files.outputs) }}'
shell:
bash
- name: Show output
run: |
echo '${{ toJSON(steps.changed-files-all-old-new-renamed-files.outputs) }}'
shell:
bash
- name: Check all_old_new_renamed_files output on non windows platform
if: "!contains(steps.changed-files-all-old-new-renamed-files.outputs.all_old_new_renamed_files, 'test/test rename 1.txt,test/test rename-1.txt') && runner.os != 'Windows'"
run: |
echo "Invalid output: Expected to include (test/test rename 1.txt,test/test rename-1.txt) got (${{ steps.changed-files-all-old-new-renamed-files.outputs.all_old_new_renamed_files }})"
exit 1
shell:
bash
- name: Check all_old_new_renamed_files output on windows platform
if: "!contains(steps.changed-files-all-old-new-renamed-files.outputs.all_old_new_renamed_files, 'test\\test rename 1.txt,test\\test rename-1.txt') && runner.os == 'Windows'"
run: |
echo "Invalid output: Expected to not include (test\\test rename 1.txt,test\\test rename-1.txt) got (${{ steps.changed-files-all-old-new-renamed-files.outputs.all_old_new_renamed_files }})"
exit 1
shell:
bash
- name: Check the renamed_files output on non windows platform
if: "!contains(steps.changed-files-all-old-new-renamed-files.outputs.renamed_files, 'test/test rename-1.txt') && runner.os != 'Windows'"
run: |
echo "Invalid output: Expected to include (test/test rename-1.txt) got (${{ steps.changed-files-all-old-new-renamed-files.outputs.renamed_files }})"
exit 1
shell:
bash
- name: Check the renamed_files output on windows platform
if: "!contains(steps.changed-files-all-old-new-renamed-files.outputs.renamed_files, 'test\\test rename-1.txt') && runner.os == 'Windows'"
run: |
echo "Invalid output: Expected to not include (test\\test rename-1.txt) got (${{ steps.changed-files-all-old-new-renamed-files.outputs.renamed_files }})"
exit 1
shell:
bash
issue_commented:
# This job only runs for issue comments
name: Issue comment
if: ${{ !github.event.issue.pull_request }}
runs-on: ubuntu-latest
steps:
- run: |
echo A comment on issue $NUMBER
env:
NUMBER: ${{ github.event.issue.number }}
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Run changed-files with defaults
id: changed-files
uses: ./
- name: Show output
run: |
echo '${{ toJSON(steps.changed-files.outputs) }}'
shell:
bash
- name: Run changed-files for old new filenames test rename
id: changed-files-all-old-new-renamed-files
uses: ./
with:
base_sha: d1c0ee4
sha: 4d04215
fetch_depth: 60000
include_all_old_new_renamed_files: true
- name: Show output
run: |
echo '${{ toJSON(steps.changed-files-all-old-new-renamed-files.outputs) }}'
shell:
bash
- name: Show output
run: |
echo '${{ toJSON(steps.changed-files-all-old-new-renamed-files.outputs) }}'
shell:
bash
- name: Check all_old_new_renamed_files output on non windows platform
if: "!contains(steps.changed-files-all-old-new-renamed-files.outputs.all_old_new_renamed_files, 'test/test rename 1.txt,test/test rename-1.txt') && runner.os != 'Windows'"
run: |
echo "Invalid output: Expected to include (test/test rename 1.txt,test/test rename-1.txt) got (${{ steps.changed-files-all-old-new-renamed-files.outputs.all_old_new_renamed_files }})"
exit 1
shell:
bash
- name: Check all_old_new_renamed_files output on windows platform
if: "!contains(steps.changed-files-all-old-new-renamed-files.outputs.all_old_new_renamed_files, 'test\\test rename 1.txt,test\\test rename-1.txt') && runner.os == 'Windows'"
run: |
echo "Invalid output: Expected to not include (test\\test rename 1.txt,test\\test rename-1.txt) got (${{ steps.changed-files-all-old-new-renamed-files.outputs.all_old_new_renamed_files }})"
exit 1
shell:
bash
- name: Check the renamed_files output on non windows platform
if: "!contains(steps.changed-files-all-old-new-renamed-files.outputs.renamed_files, 'test/test rename-1.txt') && runner.os != 'Windows'"
run: |
echo "Invalid output: Expected to include (test/test rename-1.txt) got (${{ steps.changed-files-all-old-new-renamed-files.outputs.renamed_files }})"
exit 1
shell:
bash
- name: Check the renamed_files output on windows platform
if: "!contains(steps.changed-files-all-old-new-renamed-files.outputs.renamed_files, 'test\\test rename-1.txt') && runner.os == 'Windows'"
run: |
echo "Invalid output: Expected to not include (test\\test rename-1.txt) got (${{ steps.changed-files-all-old-new-renamed-files.outputs.renamed_files }})"
exit 1
shell:
bash

View File

@ -1,11 +1,9 @@
name: Manual Triggered Job Example
permissions:
contents: read
name: Manual Test
on:
workflow_dispatch:
jobs:
test:
name: Test changed-files
@ -18,7 +16,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
submodules: true
fetch-depth: 0

View File

@ -1,7 +1,4 @@
name: Matrix Example
permissions:
contents: read
name: Matrix Test
on:
workflow_dispatch:
@ -11,35 +8,37 @@ on:
jobs:
changed-files:
name: Get changed files
name: Get changes
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.changed-files.outputs.all_changed_files }}
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: ./
with:
matrix: true
json: true
quotepath: false
- name: List all changed files
run: echo '${{ steps.changed-files.outputs.all_changed_files }}'
- id: set-matrix
run: echo "matrix={\"files\":${{ steps.changed-files.outputs.all_changed_files }}}" >> "$GITHUB_OUTPUT"
matrix-job:
name: Run Matrix Job
runs-on: ubuntu-latest
needs: [changed-files]
strategy:
matrix:
files: ${{ fromJSON(needs.changed-files.outputs.matrix) }}
matrix: ${{ fromJSON(needs.changed-files.outputs.matrix) }}
max-parallel: 4
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Test
run: |
echo ${{ matrix.files }}

View File

@ -1,67 +0,0 @@
name: Multi Job Example
permissions:
contents: read
on:
push:
branches:
- "**"
pull_request:
branches:
- "**"
jobs:
changed-files:
name: Get changed files
runs-on: ubuntu-latest
outputs:
all_changed_files: ${{ steps.changed-files.outputs.all_changed_files }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: ./
- name: List all changed files
run: echo '${{ steps.changed-files.outputs.all_changed_files }}'
view-changed-files:
name: View all changed files
runs-on: ubuntu-latest
needs: [changed-files]
steps:
- name: List all changed files
run: |
echo '${{ needs.changed-files.outputs.all_changed_files }}'
changed-files-rest-api:
name: Get changed files using REST API
runs-on: ubuntu-latest
outputs:
all_changed_files: ${{ steps.changed-files.outputs.all_changed_files }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
continue-on-error: ${{ github.event_name == 'push' }}
uses: ./
with:
use_rest_api: true
- name: List all changed files
run: echo '${{ steps.changed-files.outputs.all_changed_files }}'
view-changed-files-rest-api:
name: View all changed files using REST API
runs-on: ubuntu-latest
needs: [changed-files-rest-api]
steps:
- name: List all changed files
run: |
echo '${{ needs.changed-files-rest-api.outputs.all_changed_files }}'

29
.github/workflows/submodule-sync.yml vendored Normal file
View File

@ -0,0 +1,29 @@
on:
workflow_dispatch:
jobs:
sync:
name: Submodule Sync
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- name: Git Sumbodule Update
run: |
git pull --recurse-submodules
git submodule update --remote --recursive
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5.0.2
with:
title: "Updated submodule"
labels: "merge when passing"
branch: "chore/update-submodule"
commit-message: "Updated submodule"
body: "Updated submodule"
token: ${{ secrets.PAT_TOKEN }}

View File

@ -1,9 +1,4 @@
name: Update release version
permissions:
contents: write
pull-requests: write
name: Update release version.
on:
release:
types: [published]
@ -13,11 +8,11 @@ jobs:
update-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Run release-tagger
uses: tj-actions/release-tagger@v4
uses: tj-actions/release-tagger@v3
- name: Sync release version.
uses: tj-actions/sync-release-version@v13
id: sync-release-version
@ -26,18 +21,10 @@ jobs:
only_major: true
paths: |
README.md
- name: Sync release package version.
uses: tj-actions/sync-release-version@v13
id: sync-release-package-version
with:
pattern: '"version": "'
strip_prefix: "v"
paths: |
package.json
- name: Run git-cliff
uses: tj-actions/git-cliff@v1
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7.0.5
uses: peter-evans/create-pull-request@v5.0.2
with:
base: "main"
labels: "merge when passing"

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,5 @@
name: Format README.md
permissions:
contents: read
pull-requests: write
on:
push:
branches:
@ -13,21 +9,18 @@ jobs:
sync-assets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Run auto-doc
uses: tj-actions/auto-doc@v3
with:
use_code_blocks: true
use_major_version: true
uses: tj-actions/auto-doc@v2
- name: Run remark
uses: tj-actions/remark@v3
- name: Verify Changed files
uses: tj-actions/verify-changed-files@v20
uses: tj-actions/verify-changed-files@v16
id: verify_changed_files
with:
files: |
@ -41,7 +34,7 @@ jobs:
- name: Create Pull Request
if: failure()
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v5
with:
base: "main"
labels: "merge when passing"

View File

@ -1,41 +0,0 @@
name: Workflow Run Example
on:
workflow_run:
workflows: [Matrix Example]
types: [completed]
permissions:
contents: read
jobs:
on-success:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: ./
- name: Echo list of changed files on success
run: |
echo "Changed files on success:"
echo "${{ steps.changed-files.outputs.all_changed_files }}"
on-failure:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get changed files
id: changed-files
uses: ./
- name: Echo list of changed files on failure
run: |
echo "Changed files on failure:"
echo "${{ steps.changed-files.outputs.all_changed_files }}"

2
.nvmrc
View File

@ -1 +1 @@
20
16

2808
HISTORY.md

File diff suppressed because it is too large Load Diff

964
README.md

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +0,0 @@
# Security Policy
## Proactive Security Measures
To proactively detect and address security vulnerabilities, we utilize several robust tools and processes:
- **Dependency Updates:** We use [Renovate](https://renovatebot.com) and [Dependabot](https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates) to keep our dependencies updated and promptly patch detected vulnerabilities through automated PRs.
- **[GitHub's Security Features](https://github.com/features/security):** Our repository and dependencies are continuously monitored via GitHub's security features, which include:
- **Code Scanning:** Using GitHub's CodeQL, all pull requests are scanned to identify potential vulnerabilities in our source code.
- **Automated Alerts:** Dependabot identifies vulnerabilities based on the GitHub Advisory Database and opens PRs with patches, while automated [secret scanning](https://docs.github.com/en/enterprise-cloud@latest/code-security/secret-scanning/about-secret-scanning#about-secret-scanning-for-partner-patterns) provides alerts for detected secrets.
- **[GitGuardian Security Checks](https://www.gitguardian.com/):** We employ GitGuardian to ensure security checks are performed on the codebase, enhancing the overall security of our project.
- **Code Analysis and Security Scanning:** With the help of [Codacy Static Code Analysis](https://www.codacy.com/) and [Codacy Security Scan](https://security.codacy.com/), we conduct thorough analyses and scans of our code for potential security risks.
## Reporting Security Vulnerabilities
Despite our best efforts to deliver secure software, we acknowledge the invaluable role of the community in identifying security breaches.
### Private Vulnerability Disclosures
We request all suspected vulnerabilities to be responsibly and privately disclosed by sending an email to [support@tj-actions.online](mailto:support@tj-actions.online).
### Public Vulnerability Disclosures
For publicly disclosed security vulnerabilities, please **IMMEDIATELY** email [support@tj-actions.online](mailto:support@tj-actions.online) with the details for prompt action.
Upon confirmation of a breach, reporters will receive full credit and recognition for their contribution. Please note, that we do not offer monetary compensation for reporting vulnerabilities.
## Communication of Security Breaches
We will utilize the [GitHub Security Advisory](https://github.com/tj-actions/changed-files/security/advisories) to communicate any security breaches. The advisory will be made public once a patch has been released to rectify the issue.
We appreciate your cooperation and contribution to maintaining the security of our software. Remember, a secure community is a strong community.

View File

@ -4,11 +4,11 @@ author: tj-actions
inputs:
separator:
description: "Split character for output strings."
description: "Split character for output strings"
required: false
default: " "
include_all_old_new_renamed_files:
description: "Include `all_old_new_renamed_files` output. Note this can generate a large output See: #501."
description: "Include `all_old_new_renamed_files` output. Note this can generate a large output See: [#501](https://github.com/tj-actions/changed-files/issues/501)."
required: false
default: "false"
old_new_separator:
@ -24,13 +24,11 @@ inputs:
required: false
default: ""
files_from_source_file_separator:
description: "Separator used to split the `files_from_source_file` input."
description: 'Separator used to split the `files_from_source_file` input'
default: "\n"
required: false
files:
description: |
File and directory patterns used to detect changes (Defaults to the entire repo if unset).
NOTE: Multiline file/directory patterns should not include quotes.
description: "File and directory patterns used to detect changes (Defaults to the entire repo if unset) **NOTE:** Multiline file/directory patterns should not include quotes."
required: false
default: ""
files_separator:
@ -42,7 +40,7 @@ inputs:
required: false
default: ""
files_yaml_from_source_file:
description: "Source file(s) used to populate the `files_yaml` input. Example: https://github.com/tj-actions/changed-files/blob/main/test/changed-files.yml"
description: "Source file(s) used to populate the `files_yaml` input. [Example](https://github.com/tj-actions/changed-files/blob/main/test/changed-files.yml)"
required: false
default: ""
files_yaml_from_source_file_separator:
@ -54,7 +52,7 @@ inputs:
required: false
default: ""
files_ignore_yaml_from_source_file:
description: "Source file(s) used to populate the `files_ignore_yaml` input. Example: https://github.com/tj-actions/changed-files/blob/main/test/changed-files.yml"
description: "Source file(s) used to populate the `files_ignore_yaml` input. [Example](https://github.com/tj-actions/changed-files/blob/main/test/changed-files.yml)"
required: false
default: ""
files_ignore_yaml_from_source_file_separator:
@ -62,7 +60,7 @@ inputs:
default: "\n"
required: false
files_ignore:
description: "Ignore changes to these file(s). NOTE: Multiline file/directory patterns should not include quotes."
description: "Ignore changes to these file(s) **NOTE:** Multiline file/directory patterns should not include quotes."
required: false
default: ""
files_ignore_separator:
@ -78,10 +76,10 @@ inputs:
default: "\n"
required: false
sha:
description: "Specify a different commit SHA or branch used for comparing changes"
description: "Specify a different commit SHA used for comparing changes"
required: false
base_sha:
description: "Specify a different base commit SHA or branch used for comparing changes"
description: "Specify a different base commit SHA used for comparing changes"
required: false
since:
description: "Get changed files for commits whose timestamp is older than the given time."
@ -96,16 +94,16 @@ inputs:
required: false
default: "."
quotepath:
description: "Use non-ASCII characters to match files and output the filenames completely verbatim by setting this to `false`"
description: "Use non-ascii characters to match files and output the filenames completely verbatim by setting this to `false`"
default: "true"
required: false
diff_relative:
description: "Exclude changes outside the current directory and show path names relative to it. NOTE: This requires you to specify the top-level directory via the `path` input."
description: "Exclude changes outside the current directory and show path names relative to it. **NOTE:** This requires you to specify the top level directory via the `path` input."
required: false
default: "true"
dir_names:
default: "false"
description: "Output unique changed directories instead of filenames. NOTE: This returns `.` for changed files located in the current working directory which defaults to `$GITHUB_WORKSPACE`."
description: "Output unique changed directories instead of filenames. **NOTE:** This returns `.` for changed files located in the current working directory which defaults to `$GITHUB_WORKSPACE`."
required: false
dir_names_max_depth:
description: "Limit the directory output to a maximum depth e.g `test/test1/test2` with max depth of `2` returns `test/test1`."
@ -114,42 +112,20 @@ inputs:
description: "Exclude the current directory represented by `.` from the output when `dir_names` is set to `true`."
required: false
default: "false"
dir_names_include_files:
description: "File and directory patterns to include in the output when `dir_names` is set to `true`. NOTE: This returns only the matching files and also the directory names."
required: false
default: ""
dir_names_include_files_separator:
description: "Separator used to split the `dir_names_include_files` input"
default: "\n"
required: false
dir_names_deleted_files_include_only_deleted_dirs:
description: "Include only directories that have been deleted as opposed to directory names of files that have been deleted in the `deleted_files` output when `dir_names` is set to `true`."
required: false
default: "false"
json:
description: "Output list of changed files in a JSON formatted string which can be used for matrix jobs. Example: https://github.com/tj-actions/changed-files/blob/main/.github/workflows/matrix-example.yml"
description: "Output list of changed files in a JSON formatted string which can be used for matrix jobs."
required: false
default: "false"
escape_json:
description: "Escape JSON output."
required: false
default: "true"
safe_output:
description: "Apply sanitization to output filenames before being set as output."
required: false
default: "true"
fetch_depth:
description: "Depth of additional branch history fetched. NOTE: This can be adjusted to resolve errors with insufficient history."
description: "Depth of additional branch history fetched. **NOTE**: This can be adjusted to resolve errors with insufficient history."
required: false
default: "25"
default: "50"
skip_initial_fetch:
description: |
Skip initially fetching additional history to improve performance for shallow repositories.
NOTE: This could lead to errors with missing history. It's intended to be used when you've fetched all necessary history to perform the diff.
required: false
default: "false"
fetch_additional_submodule_history:
description: "Fetch additional history for submodules."
description: "Skip the initial fetch to improve performance."
required: false
default: "false"
since_last_remote_commit:
@ -157,7 +133,7 @@ inputs:
required: false
default: "false"
write_output_files:
description: "Write outputs to the `output_dir` defaults to `.github/outputs` folder. NOTE: This creates a `.txt` file by default and a `.json` file if `json` is set to `true`."
description: "Write outputs to the `output_dir` defaults to `.github/outputs` folder. **NOTE:** This creates a `.txt` file by default and a `.json` file if `json` is set to `true`."
required: false
default: "false"
output_dir:
@ -176,74 +152,14 @@ inputs:
description: "Recover deleted files to a new destination directory, defaults to the original location."
required: false
default: ""
recover_files:
description: |
File and directory patterns used to recover deleted files,
defaults to the patterns provided via the `files`, `files_from_source_file`, `files_ignore` and `files_ignore_from_source_file` inputs
or all deleted files if no patterns are provided.
required: false
default: ""
recover_files_separator:
description: "Separator used to split the `recover_files` input"
default: "\n"
required: false
recover_files_ignore:
description: "File and directory patterns to ignore when recovering deleted files."
required: false
default: ""
recover_files_ignore_separator:
description: "Separator used to split the `recover_files_ignore` input"
default: "\n"
required: false
token:
description: "GitHub token used to fetch changed files from Github's API."
description: "Github token used to fetch changed files from Github's API."
required: false
default: ${{ github.token }}
api_url:
description: "Github API URL."
required: false
default: ${{ github.api_url }}
use_rest_api:
description: "Force the use of Github's REST API even when a local copy of the repository exists"
required: false
default: "false"
fail_on_initial_diff_error:
description: "Fail when the initial diff fails."
required: false
default: "false"
fail_on_submodule_diff_error:
description: "Fail when the submodule diff fails."
required: false
default: "false"
negation_patterns_first:
description: "Apply the negation patterns first. NOTE: This affects how changed files are matched."
required: false
default: "false"
matrix:
description: "Output changed files in a format that can be used for matrix jobs. Alias for setting inputs `json` to `true` and `escape_json` to `false`."
required: false
default: "false"
exclude_submodules:
description: "Exclude changes to submodules."
required: false
default: "false"
fetch_missing_history_max_retries:
description: "Maximum number of retries to fetch missing history."
required: false
default: "20"
use_posix_path_separator:
description: "Use POSIX path separator `/` for output file paths on Windows."
required: false
default: "false"
tags_pattern:
description: "Tags pattern to include."
required: false
default: "*"
tags_ignore_pattern:
description: "Tags pattern to ignore."
required: false
default: ""
outputs:
added_files:
@ -283,48 +199,44 @@ outputs:
unknown_files_count:
description: "Returns the number of `unknown_files`"
all_changed_and_modified_files:
description: "Returns all changed and modified files i.e. a combination of (ACMRDTUX)"
description: "Returns all changed and modified files i.e. *a combination of (ACMRDTUX)*"
all_changed_and_modified_files_count:
description: "Returns the number of `all_changed_and_modified_files`"
all_changed_files:
description: "Returns all changed files i.e. a combination of all added, copied, modified and renamed files (ACMR)"
description: "Returns all changed files i.e. *a combination of all added, copied, modified and renamed files (ACMR)*"
all_changed_files_count:
description: "Returns the number of `all_changed_files`"
any_changed:
description: "Returns `true` when any of the filenames provided using the `files*` or `files_ignore*` inputs have changed. This defaults to `true` when no patterns are specified. i.e. *includes a combination of all added, copied, modified and renamed files (ACMR)*."
description: "Returns `true` when any of the filenames provided using the `files*` or `files_ignore*` inputs has changed. i.e. *using a combination of all added, copied, modified and renamed files (ACMR)*."
only_changed:
description: "Returns `true` when only files provided using the `files*` or `files_ignore*` inputs have changed. i.e. *includes a combination of all added, copied, modified and renamed files (ACMR)*."
description: "Returns `true` when only files provided using the `files*` or `files_ignore*` inputs has changed. i.e. *using a combination of all added, copied, modified and renamed files (ACMR)*."
other_changed_files:
description: "Returns all other changed files not listed in the files input i.e. includes a combination of all added, copied, modified and renamed files (ACMR)."
description: "Returns all other changed files not listed in the files input i.e. *using a combination of all added, copied, modified and renamed files (ACMR)*."
other_changed_files_count:
description: "Returns the number of `other_changed_files`"
all_modified_files:
description: "Returns all changed files i.e. a combination of all added, copied, modified, renamed and deleted files (ACMRD)."
description: "Returns all changed files i.e. *a combination of all added, copied, modified, renamed and deleted files (ACMRD)*."
all_modified_files_count:
description: "Returns the number of `all_modified_files`"
any_modified:
description: "Returns `true` when any of the filenames provided using the `files*` or `files_ignore*` inputs have been modified. This defaults to `true` when no patterns are specified. i.e. *includes a combination of all added, copied, modified, renamed, and deleted files (ACMRD)*."
description: "Returns `true` when any of the filenames provided using the `files*` or `files_ignore*` inputs has been modified. i.e. *using a combination of all added, copied, modified, renamed, and deleted files (ACMRD)*."
only_modified:
description: "Returns `true` when only files provided using the `files*` or `files_ignore*` inputs have been modified. (ACMRD)."
description: "Returns `true` when only files provided using the `files*` or `files_ignore*` inputs has been modified. (ACMRD)."
other_modified_files:
description: "Returns all other modified files not listed in the files input i.e. a combination of all added, copied, modified, and deleted files (ACMRD)"
description: "Returns all other modified files not listed in the files input i.e. *a combination of all added, copied, modified, and deleted files (ACMRD)*"
other_modified_files_count:
description: "Returns the number of `other_modified_files`"
any_deleted:
description: "Returns `true` when any of the filenames provided using the `files*` or `files_ignore*` inputs have been deleted. This defaults to `true` when no patterns are specified. (D)"
description: "Returns `true` when any of the filenames provided using the `files*` or `files_ignore*` inputs has been deleted. (D)"
only_deleted:
description: "Returns `true` when only files provided using the `files*` or `files_ignore*` inputs have been deleted. (D)"
description: "Returns `true` when only files provided using the `files*` or `files_ignore*` inputs has been deleted. (D)"
other_deleted_files:
description: "Returns all other deleted files not listed in the files input i.e. a combination of all deleted files (D)"
description: "Returns all other deleted files not listed in the files input i.e. *a combination of all deleted files (D)*"
other_deleted_files_count:
description: "Returns the number of `other_deleted_files`"
modified_keys:
description: "Returns all modified YAML keys when the `files_yaml` input is used. i.e. key that contains any path that has either been added, copied, modified, and deleted (ACMRD)"
changed_keys:
description: "Returns all changed YAML keys when the `files_yaml` input is used. i.e. key that contains any path that has either been added, copied, modified, and renamed (ACMR)"
runs:
using: 'node20'
using: 'node16'
main: 'dist/index.js'
branding:

43223
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

5408
dist/licenses.txt generated vendored

File diff suppressed because it is too large Load Diff

2
dist/sourcemap-register.js generated vendored

File diff suppressed because one or more lines are too long

View File

@ -10,4 +10,4 @@ module.exports = {
setupFiles: [
"<rootDir>/jest/setupEnv.cjs"
]
};
}

View File

@ -1,5 +1,6 @@
const path = require('path')
process.env.TESTING = "1"
process.env.GITHUB_WORKSPACE = path.join(
path.resolve(__dirname, '..'), '.'
)

View File

@ -1,25 +1,24 @@
{
"name": "@tj-actions/changed-files",
"version": "45.0.4",
"description": "Github action to retrieve all (added, copied, modified, deleted, renamed, type changed, unmerged, unknown) files and directories.",
"name": "@tj-actions/glob",
"version": "17.2.5",
"description": "Glob pattern matching github action",
"main": "lib/main.js",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
},
"scripts": {
"build": "tsc",
"format": "prettier --write src/*.ts src/**/*.ts",
"format-check": "prettier --check src/*.ts src/**/*.ts",
"lint": "eslint src/*.ts src/**/*.ts --max-warnings 0",
"lint:fix": "eslint --fix src/*.ts src/**/*.ts",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts",
"lint:fix": "eslint --fix src/**/*.ts",
"package": "ncc build lib/main.js --source-map --license licenses.txt",
"test": "jest --coverage",
"update-snapshot": "jest -u",
"all": "yarn build && yarn format && yarn lint && yarn package && yarn test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tj-actions/changed-files.git"
"url": "git+https://github.com/tj-actions/glob.git"
},
"keywords": [
"actions",
@ -29,15 +28,14 @@
"author": "Tonye Jack",
"license": "MIT",
"bugs": {
"url": "https://github.com/tj-actions/changed-files/issues"
"url": "https://github.com/tj-actions/glob/issues"
},
"homepage": "https://github.com/tj-actions/changed-files#readme",
"homepage": "https://github.com/tj-actions/glob#readme",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"@octokit/rest": "^21.0.0",
"@stdlib/utils-convert-path": "^0.2.1",
"@actions/github": "^5.1.1",
"@octokit/rest": "^19.0.13",
"lodash": "^4.17.21",
"micromatch": "^4.0.5",
"yaml": "^2.3.1"
@ -46,15 +44,14 @@
"@types/jest": "^29.5.2",
"@types/lodash": "^4.14.195",
"@types/micromatch": "^4.0.2",
"@types/node": "^22.0.0",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"@vercel/ncc": "^0.38.0",
"@types/node": "^20.3.2",
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vercel/ncc": "^0.36.1",
"eslint": "^8.43.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-github": "^5.0.0",
"eslint-plugin-jest": "^28.0.0",
"eslint-plugin-github": "^4.8.0",
"eslint-plugin-jest": "^27.2.2",
"eslint-plugin-prettier": "^5.0.0-alpha.2",
"jest": "^29.5.0",
"prettier": "^3.0.0",

View File

@ -1,373 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`getInputs should correctly parse boolean inputs 1`] = `
{
"apiUrl": "",
"baseSha": "",
"diffRelative": "false",
"dirNames": "false",
"dirNamesDeletedFilesIncludeOnlyDeletedDirs": "false",
"dirNamesExcludeCurrentDir": "false",
"dirNamesIncludeFiles": "",
"dirNamesIncludeFilesSeparator": "",
"escapeJson": false,
"excludeSubmodules": "false",
"failOnInitialDiffError": "false",
"failOnSubmoduleDiffError": "false",
"fetchAdditionalSubmoduleHistory": "false",
"fetchMissingHistoryMaxRetries": 20,
"files": "",
"filesFromSourceFile": "",
"filesFromSourceFileSeparator": "",
"filesIgnore": "",
"filesIgnoreFromSourceFile": "",
"filesIgnoreFromSourceFileSeparator": "",
"filesIgnoreSeparator": "",
"filesIgnoreYaml": "",
"filesIgnoreYamlFromSourceFile": "",
"filesIgnoreYamlFromSourceFileSeparator": "",
"filesSeparator": "",
"filesYaml": "",
"filesYamlFromSourceFile": "",
"filesYamlFromSourceFileSeparator": "",
"includeAllOldNewRenamedFiles": "false",
"json": true,
"negationPatternsFirst": "false",
"oldNewFilesSeparator": " ",
"oldNewSeparator": ",",
"outputDir": "",
"outputRenamedFilesAsDeletedAndAdded": "false",
"path": ".",
"quotepath": "false",
"recoverDeletedFiles": "false",
"recoverDeletedFilesToDestination": "",
"recoverFiles": "",
"recoverFilesIgnore": "",
"recoverFilesIgnoreSeparator": "
",
"recoverFilesSeparator": "
",
"safeOutput": "false",
"separator": "",
"sha": "",
"since": "",
"sinceLastRemoteCommit": "false",
"skipInitialFetch": "true",
"tagsIgnorePattern": "",
"tagsPattern": "*",
"token": "",
"until": "",
"usePosixPathSeparator": "false",
"useRestApi": "false",
"writeOutputFiles": "false",
}
`;
exports[`getInputs should correctly parse numeric inputs 1`] = `
{
"apiUrl": "",
"baseSha": "",
"diffRelative": true,
"dirNames": false,
"dirNamesDeletedFilesIncludeOnlyDeletedDirs": false,
"dirNamesExcludeCurrentDir": false,
"dirNamesIncludeFiles": "",
"dirNamesIncludeFilesSeparator": "",
"dirNamesMaxDepth": 2,
"escapeJson": false,
"excludeSubmodules": false,
"failOnInitialDiffError": false,
"failOnSubmoduleDiffError": false,
"fetchAdditionalSubmoduleHistory": false,
"fetchDepth": 5,
"files": "",
"filesFromSourceFile": "",
"filesFromSourceFileSeparator": "",
"filesIgnore": "",
"filesIgnoreFromSourceFile": "",
"filesIgnoreFromSourceFileSeparator": "",
"filesIgnoreSeparator": "",
"filesIgnoreYaml": "",
"filesIgnoreYamlFromSourceFile": "",
"filesIgnoreYamlFromSourceFileSeparator": "",
"filesSeparator": "",
"filesYaml": "",
"filesYamlFromSourceFile": "",
"filesYamlFromSourceFileSeparator": "",
"includeAllOldNewRenamedFiles": false,
"json": false,
"negationPatternsFirst": false,
"oldNewFilesSeparator": "",
"oldNewSeparator": "",
"outputDir": "",
"outputRenamedFilesAsDeletedAndAdded": false,
"path": "",
"quotepath": true,
"recoverDeletedFiles": false,
"recoverDeletedFilesToDestination": "",
"recoverFiles": "",
"recoverFilesIgnore": "",
"recoverFilesIgnoreSeparator": "",
"recoverFilesSeparator": "",
"safeOutput": false,
"separator": "",
"sha": "",
"since": "",
"sinceLastRemoteCommit": false,
"skipInitialFetch": false,
"tagsIgnorePattern": "",
"tagsPattern": "",
"token": "",
"until": "",
"usePosixPathSeparator": false,
"useRestApi": false,
"writeOutputFiles": false,
}
`;
exports[`getInputs should correctly parse string inputs 1`] = `
{
"apiUrl": "https://api.github.com",
"baseSha": "",
"diffRelative": true,
"dirNames": false,
"dirNamesDeletedFilesIncludeOnlyDeletedDirs": false,
"dirNamesExcludeCurrentDir": false,
"dirNamesIncludeFiles": "",
"dirNamesIncludeFilesSeparator": "",
"escapeJson": false,
"excludeSubmodules": false,
"failOnInitialDiffError": false,
"failOnSubmoduleDiffError": false,
"fetchAdditionalSubmoduleHistory": false,
"files": "",
"filesFromSourceFile": "",
"filesFromSourceFileSeparator": "",
"filesIgnore": "",
"filesIgnoreFromSourceFile": "",
"filesIgnoreFromSourceFileSeparator": "",
"filesIgnoreSeparator": "",
"filesIgnoreYaml": "",
"filesIgnoreYamlFromSourceFile": "",
"filesIgnoreYamlFromSourceFileSeparator": "",
"filesSeparator": "",
"filesYaml": "",
"filesYamlFromSourceFile": "",
"filesYamlFromSourceFileSeparator": "",
"includeAllOldNewRenamedFiles": false,
"json": false,
"negationPatternsFirst": false,
"oldNewFilesSeparator": "",
"oldNewSeparator": "",
"outputDir": "",
"outputRenamedFilesAsDeletedAndAdded": false,
"path": "",
"quotepath": true,
"recoverDeletedFiles": false,
"recoverDeletedFilesToDestination": "",
"recoverFiles": "",
"recoverFilesIgnore": "",
"recoverFilesIgnoreSeparator": "",
"recoverFilesSeparator": "",
"safeOutput": false,
"separator": "",
"sha": "",
"since": "",
"sinceLastRemoteCommit": false,
"skipInitialFetch": false,
"tagsIgnorePattern": "",
"tagsPattern": "",
"token": "token",
"until": "",
"usePosixPathSeparator": false,
"useRestApi": false,
"writeOutputFiles": false,
}
`;
exports[`getInputs should handle invalid numeric inputs correctly 1`] = `
{
"apiUrl": "",
"baseSha": "",
"diffRelative": true,
"dirNames": false,
"dirNamesDeletedFilesIncludeOnlyDeletedDirs": false,
"dirNamesExcludeCurrentDir": false,
"dirNamesIncludeFiles": "",
"dirNamesIncludeFilesSeparator": "",
"dirNamesMaxDepth": 2,
"escapeJson": false,
"excludeSubmodules": false,
"failOnInitialDiffError": false,
"failOnSubmoduleDiffError": false,
"fetchAdditionalSubmoduleHistory": false,
"fetchDepth": NaN,
"files": "",
"filesFromSourceFile": "",
"filesFromSourceFileSeparator": "",
"filesIgnore": "",
"filesIgnoreFromSourceFile": "",
"filesIgnoreFromSourceFileSeparator": "",
"filesIgnoreSeparator": "",
"filesIgnoreYaml": "",
"filesIgnoreYamlFromSourceFile": "",
"filesIgnoreYamlFromSourceFileSeparator": "",
"filesSeparator": "",
"filesYaml": "",
"filesYamlFromSourceFile": "",
"filesYamlFromSourceFileSeparator": "",
"includeAllOldNewRenamedFiles": false,
"json": false,
"negationPatternsFirst": false,
"oldNewFilesSeparator": "",
"oldNewSeparator": "",
"outputDir": "",
"outputRenamedFilesAsDeletedAndAdded": false,
"path": "",
"quotepath": true,
"recoverDeletedFiles": false,
"recoverDeletedFilesToDestination": "",
"recoverFiles": "",
"recoverFilesIgnore": "",
"recoverFilesIgnoreSeparator": "",
"recoverFilesSeparator": "",
"safeOutput": false,
"separator": "",
"sha": "",
"since": "",
"sinceLastRemoteCommit": false,
"skipInitialFetch": false,
"tagsIgnorePattern": "",
"tagsPattern": "",
"token": "",
"until": "",
"usePosixPathSeparator": false,
"useRestApi": false,
"writeOutputFiles": false,
}
`;
exports[`getInputs should handle negative numeric inputs correctly 1`] = `
{
"apiUrl": "",
"baseSha": "",
"diffRelative": true,
"dirNames": false,
"dirNamesDeletedFilesIncludeOnlyDeletedDirs": false,
"dirNamesExcludeCurrentDir": false,
"dirNamesIncludeFiles": "",
"dirNamesIncludeFilesSeparator": "",
"dirNamesMaxDepth": -2,
"escapeJson": false,
"excludeSubmodules": false,
"failOnInitialDiffError": false,
"failOnSubmoduleDiffError": false,
"fetchAdditionalSubmoduleHistory": false,
"fetchDepth": 2,
"files": "",
"filesFromSourceFile": "",
"filesFromSourceFileSeparator": "",
"filesIgnore": "",
"filesIgnoreFromSourceFile": "",
"filesIgnoreFromSourceFileSeparator": "",
"filesIgnoreSeparator": "",
"filesIgnoreYaml": "",
"filesIgnoreYamlFromSourceFile": "",
"filesIgnoreYamlFromSourceFileSeparator": "",
"filesSeparator": "",
"filesYaml": "",
"filesYamlFromSourceFile": "",
"filesYamlFromSourceFileSeparator": "",
"includeAllOldNewRenamedFiles": false,
"json": false,
"negationPatternsFirst": false,
"oldNewFilesSeparator": "",
"oldNewSeparator": "",
"outputDir": "",
"outputRenamedFilesAsDeletedAndAdded": false,
"path": "",
"quotepath": true,
"recoverDeletedFiles": false,
"recoverDeletedFilesToDestination": "",
"recoverFiles": "",
"recoverFilesIgnore": "",
"recoverFilesIgnoreSeparator": "",
"recoverFilesSeparator": "",
"safeOutput": false,
"separator": "",
"sha": "",
"since": "",
"sinceLastRemoteCommit": false,
"skipInitialFetch": false,
"tagsIgnorePattern": "",
"tagsPattern": "",
"token": "",
"until": "",
"usePosixPathSeparator": false,
"useRestApi": false,
"writeOutputFiles": false,
}
`;
exports[`getInputs should return default values when no inputs are provided 1`] = `
{
"apiUrl": "",
"baseSha": "",
"diffRelative": true,
"dirNames": false,
"dirNamesDeletedFilesIncludeOnlyDeletedDirs": false,
"dirNamesExcludeCurrentDir": false,
"dirNamesIncludeFiles": "",
"dirNamesIncludeFilesSeparator": "",
"escapeJson": false,
"excludeSubmodules": false,
"failOnInitialDiffError": false,
"failOnSubmoduleDiffError": false,
"fetchAdditionalSubmoduleHistory": false,
"fetchMissingHistoryMaxRetries": 20,
"files": "",
"filesFromSourceFile": "",
"filesFromSourceFileSeparator": "",
"filesIgnore": "",
"filesIgnoreFromSourceFile": "",
"filesIgnoreFromSourceFileSeparator": "",
"filesIgnoreSeparator": "",
"filesIgnoreYaml": "",
"filesIgnoreYamlFromSourceFile": "",
"filesIgnoreYamlFromSourceFileSeparator": "",
"filesSeparator": "",
"filesYaml": "",
"filesYamlFromSourceFile": "",
"filesYamlFromSourceFileSeparator": "",
"includeAllOldNewRenamedFiles": false,
"json": false,
"negationPatternsFirst": false,
"oldNewFilesSeparator": " ",
"oldNewSeparator": ",",
"outputDir": "",
"outputRenamedFilesAsDeletedAndAdded": false,
"path": ".",
"quotepath": true,
"recoverDeletedFiles": false,
"recoverDeletedFilesToDestination": "",
"recoverFiles": "",
"recoverFilesIgnore": "",
"recoverFilesIgnoreSeparator": "
",
"recoverFilesSeparator": "
",
"safeOutput": false,
"separator": "",
"sha": "",
"since": "",
"sinceLastRemoteCommit": false,
"skipInitialFetch": false,
"tagsIgnorePattern": "",
"tagsPattern": "*",
"token": "",
"until": "",
"usePosixPathSeparator": false,
"useRestApi": false,
"writeOutputFiles": false,
}
`;

View File

@ -1,153 +0,0 @@
import * as core from '@actions/core'
import {getInputs, Inputs} from '../inputs'
import {DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS} from '../constant'
jest.mock('@actions/core')
describe('getInputs', () => {
afterEach(() => {
jest.clearAllMocks()
})
test('should return default values when no inputs are provided', () => {
;(core.getInput as jest.Mock).mockImplementation(name => {
const camelCaseName = name.replace(/_([a-z])/g, (g: string[]) => {
return g[1].toUpperCase()
}) as keyof Inputs
return (DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS[camelCaseName] ||
'') as string
})
;(core.getBooleanInput as jest.Mock).mockImplementation(name => {
const camelCaseName = name.replace(/_([a-z])/g, (g: string[]) => {
return g[1].toUpperCase()
}) as keyof Inputs
return (DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS[camelCaseName] ||
false) as boolean
})
expect(getInputs()).toMatchSnapshot()
})
test('should correctly parse boolean inputs', () => {
;(core.getInput as jest.Mock).mockImplementation(name => {
const camelCaseName = name.replace(/_([a-z])/g, (g: string[]) => {
return g[1].toUpperCase()
}) as keyof Inputs
return (DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS[camelCaseName] ||
'') as string
})
;(core.getBooleanInput as jest.Mock).mockImplementation(name => {
switch (name) {
case 'matrix':
return 'true'
case 'skip_initial_fetch':
return 'true'
default:
return 'false'
}
})
expect(getInputs()).toMatchSnapshot()
})
test('should handle matrix alias correctly', () => {
;(core.getBooleanInput as jest.Mock).mockImplementation(name => {
return name === 'matrix' ? 'true' : 'false'
})
const inputs = getInputs()
expect(inputs).toHaveProperty('json', true)
expect(inputs).toHaveProperty('escapeJson', false)
})
test('should correctly parse string inputs', () => {
;(core.getInput as jest.Mock).mockImplementation(name => {
switch (name) {
case 'token':
return 'token'
case 'api_url':
return 'https://api.github.com'
default:
return ''
}
})
;(core.getBooleanInput as jest.Mock).mockImplementation(name => {
const camelCaseName = name.replace(/_([a-z])/g, (g: string[]) => {
return g[1].toUpperCase()
}) as keyof Inputs
return (DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS[camelCaseName] ||
false) as boolean
})
expect(getInputs()).toMatchSnapshot()
})
test('should correctly parse numeric inputs', () => {
;(core.getInput as jest.Mock).mockImplementation(name => {
switch (name) {
case 'fetch_depth':
return '5'
case 'dir_names_max_depth':
return '2'
default:
return ''
}
})
;(core.getBooleanInput as jest.Mock).mockImplementation(name => {
const camelCaseName = name.replace(/_([a-z])/g, (g: string[]) => {
return g[1].toUpperCase()
}) as keyof Inputs
return (DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS[camelCaseName] ||
false) as boolean
})
expect(getInputs()).toMatchSnapshot()
})
test('should handle invalid numeric inputs correctly', () => {
;(core.getInput as jest.Mock).mockImplementation(name => {
// TODO: Add validation for invalid numbers which should result in an error instead of NaN
switch (name) {
case 'fetch_depth':
return 'invalid'
case 'dir_names_max_depth':
return '2'
default:
return ''
}
})
;(core.getBooleanInput as jest.Mock).mockImplementation(name => {
const camelCaseName = name.replace(/_([a-z])/g, (g: string[]) => {
return g[1].toUpperCase()
}) as keyof Inputs
return (DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS[camelCaseName] ||
false) as boolean
})
expect(getInputs()).toMatchSnapshot()
})
test('should handle negative numeric inputs correctly', () => {
;(core.getInput as jest.Mock).mockImplementation(name => {
// TODO: Add validation for negative numbers which should result in an error
switch (name) {
case 'fetch_depth':
return '-5'
case 'dir_names_max_depth':
return '-2'
default:
return ''
}
})
;(core.getBooleanInput as jest.Mock).mockImplementation(name => {
const camelCaseName = name.replace(/_([a-z])/g, (g: string[]) => {
return g[1].toUpperCase()
}) as keyof Inputs
return (DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS[camelCaseName] ||
false) as boolean
})
expect(getInputs()).toMatchSnapshot()
})
})

View File

@ -0,0 +1,5 @@
describe('main test', () => {
it('adds two numbers', async () => {
expect(1 + 1).toEqual(2)
})
})

View File

@ -1,761 +0,0 @@
import * as core from '@actions/core'
import * as exec from '@actions/exec'
import {ChangeTypeEnum} from '../changedFiles'
import {Inputs} from '../inputs'
import {
getDirname,
getDirnameMaxDepth,
getFilteredChangedFiles,
getPreviousGitTag,
normalizeSeparators,
warnUnsupportedRESTAPIInputs
} from '../utils'
const originalPlatform = process.platform
function mockedPlatform(platform: string): void {
Object.defineProperty(process, 'platform', {
value: platform
})
}
describe('utils test', () => {
afterEach(() => {
Object.defineProperty(process, 'platform', {
value: originalPlatform
})
})
describe('getDirnameMaxDepth_function', () => {
// Tests that the function returns the correct dirname when the relative path has multiple directories
it('test_multiple_directories', () => {
const result = getDirnameMaxDepth({
relativePath: 'path/to/some/file',
dirNamesMaxDepth: 2,
excludeCurrentDir: false
})
expect(result).toEqual('path/to')
})
// Tests that the function returns the correct dirname when the relative path has only one directory
it('test_single_directory', () => {
const result = getDirnameMaxDepth({
relativePath: 'path/to',
dirNamesMaxDepth: 1,
excludeCurrentDir: false
})
expect(result).toEqual('path')
})
// Tests that the function returns the correct dirname when the relative path has no directories
it('test_no_directories', () => {
const result = getDirnameMaxDepth({
relativePath: 'file.txt',
dirNamesMaxDepth: 1,
excludeCurrentDir: false
})
expect(result).toEqual('.')
})
// Tests that the function returns the correct dirname when dirNamesMaxDepth is set to a value less than the number of directories in the relative path
it('test_dirnames_max_depth_less_than_num_directories', () => {
const result = getDirnameMaxDepth({
relativePath: 'path/to/some/file',
dirNamesMaxDepth: 1,
excludeCurrentDir: false
})
expect(result).toEqual('path')
})
// Tests that the function returns an empty string when excludeCurrentDir is true and the output is '.'
it('test_exclude_current_dir_is_true_and_output_is_dot', () => {
const result = getDirnameMaxDepth({
relativePath: '.',
dirNamesMaxDepth: 1,
excludeCurrentDir: true
})
expect(result).toEqual('')
})
// Tests that the function returns the correct dirname when the relative path is a Windows drive root and excludeCurrentDir is true
it('test_windows_drive_root_and_exclude_current_dir_is_true', () => {
mockedPlatform('win32')
const result = getDirnameMaxDepth({
relativePath: 'C:\\',
dirNamesMaxDepth: 1,
excludeCurrentDir: true
})
expect(result).toEqual('')
})
// Tests that getDirnameMaxDepth handles a relative path with a trailing separator correctly
it('test_trailing_separator', () => {
const input = {
relativePath: 'path/to/dir/',
dirNamesMaxDepth: 2,
excludeCurrentDir: true
}
const expectedOutput = 'path/to'
const actualOutput = getDirnameMaxDepth(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that getDirnameMaxDepth returns an empty string when excludeCurrentDir is true and the output is '.'
it('test_trailing_separator_exclude_current_dir', () => {
const input = {
relativePath: 'file',
excludeCurrentDir: true
}
const expectedOutput = ''
const actualOutput = getDirnameMaxDepth(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that getDirnameMaxDepth returns the correct output for a Windows UNC root path
it('test_windows_unc_root', () => {
mockedPlatform('win32')
const input = {
relativePath: '\\hello',
dirNamesMaxDepth: 2,
excludeCurrentDir: true
}
const expectedOutput = ''
expect(getDirnameMaxDepth(input)).toEqual(expectedOutput)
})
// Tests that getDirnameMaxDepth returns an empty string when given a Windows UNC root and excludeCurrentDir is true
it('test_windows_unc_root_exclude_current_dir', () => {
mockedPlatform('win32')
const relativePath = '\\hello'
const result = getDirnameMaxDepth({
relativePath,
excludeCurrentDir: true
})
expect(result).toEqual('')
})
// Tests that getDirnameMaxDepth returns the correct dirname with a relative path that contains both forward and backward slashes
it('test_relative_path_with_slashes', () => {
const relativePath = 'path/to\file'
const expectedOutput = 'path'
const actualOutput = getDirnameMaxDepth({relativePath})
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that getDirnameMaxDepth returns the correct dirname for a relative path that contains special characters
it('test_special_characters', () => {
const relativePath =
'path/with/special/characters/!@#$%^&*()_+{}|:<>?[];,./'
const expectedDirname = 'path/with/special/characters'
const actualDirname = getDirnameMaxDepth({relativePath})
expect(actualDirname).toEqual(expectedDirname)
})
})
describe('getDirname_function', () => {
// Tests that the function returns the correct dirname for a valid path
it('test valid path', () => {
expect(getDirname('/path/to/file')).toEqual('/path/to')
})
// Tests that the function returns the correct dirname for a valid Windows UNC root path
it('test windows unc root path', () => {
mockedPlatform('win32')
expect(getDirname('\\helloworld')).toEqual('.')
})
// Tests that the function returns the correct dirname for a path with a trailing slash
it('test path with trailing slash', () => {
expect(getDirname('/path/to/file/')).toEqual('/path/to')
})
// Tests that the function returns the correct dirname for a Windows UNC root path with a trailing slash
it('test windows unc root path with trailing slash', () => {
mockedPlatform('win32')
expect(getDirname('\\hello\\world\\')).toEqual('.')
})
// Tests that the function returns the correct dirname for a path with multiple slashes
it('test path with multiple slashes', () => {
expect(getDirname('/path//to/file')).toEqual('/path/to')
})
// Tests that the function returns the correct dirname for a Windows UNC root path with multiple slashes
it('test windows unc root path with multiple slashes', () => {
mockedPlatform('win32')
expect(getDirname('\\hello\\world')).toEqual('.')
})
})
describe('normalizeSeparators_function', () => {
// Tests that forward slashes are normalized on Linux
it('test forward slashes linux', () => {
const input = 'path/to/file'
const expectedOutput = 'path/to/file'
const actualOutput = normalizeSeparators(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that backslashes are normalized on Windows
it('test backslashes windows', () => {
mockedPlatform('win32')
const input = 'path\\to\\file'
const expectedOutput = 'path\\to\\file'
const actualOutput = normalizeSeparators(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that forward slashes are normalized on Windows
it('test mixed slashes windows', () => {
mockedPlatform('win32')
const input = 'path/to/file'
const expectedOutput = 'path\\to\\file'
const actualOutput = normalizeSeparators(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that mixed slashes are normalized on Windows
it('test mixed slashes windows', () => {
mockedPlatform('win32')
const input = 'path\\to/file'
const expectedOutput = 'path\\to\\file'
const actualOutput = normalizeSeparators(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that an empty string returns an empty string
it('test empty string', () => {
const input = ''
const expectedOutput = ''
const actualOutput = normalizeSeparators(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that multiple consecutive slashes are removed
it('test multiple consecutive slashes', () => {
const input = 'path//to//file'
const expectedOutput = 'path/to/file'
const actualOutput = normalizeSeparators(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that UNC format is preserved on Windows
it('test unc format windows', () => {
mockedPlatform('win32')
const input = '\\\\hello\\world'
const expectedOutput = '\\\\hello\\world'
const actualOutput = normalizeSeparators(input)
expect(actualOutput).toEqual(expectedOutput)
})
// Tests that a drive root is preserved on Windows
it('test drive root windows', () => {
mockedPlatform('win32')
const input = 'C:\\'
const expectedOutput = 'C:\\'
const actualOutput = normalizeSeparators(input)
expect(actualOutput).toEqual(expectedOutput)
})
})
describe('getFilteredChangedFiles', () => {
// Tests that the function returns an empty object when allDiffFiles and filePatterns are empty
it('should return an empty object when allDiffFiles and filePatterns are empty', async () => {
const result = await getFilteredChangedFiles({
allDiffFiles: {
[ChangeTypeEnum.Added]: [],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
},
filePatterns: []
})
expect(result).toEqual({
[ChangeTypeEnum.Added]: [],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
})
})
// Tests that the function returns allDiffFiles when filePatterns is empty
it('should return allDiffFiles when filePatterns is empty', async () => {
const allDiffFiles = {
[ChangeTypeEnum.Added]: ['file1.txt'],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const result = await getFilteredChangedFiles({
allDiffFiles,
filePatterns: []
})
expect(result).toEqual(allDiffFiles)
})
// Tests that the function returns an empty object when allDiffFiles is empty
it('should return an empty object when allDiffFiles is empty', async () => {
const result = await getFilteredChangedFiles({
allDiffFiles: {
[ChangeTypeEnum.Added]: [],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
},
filePatterns: ['*.txt']
})
expect(result).toEqual({
[ChangeTypeEnum.Added]: [],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
})
})
// Tests that the function returns only the files that match the file patterns on non windows platforms
it('should return only the files that match the file patterns', async () => {
const allDiffFiles = {
[ChangeTypeEnum.Added]: [
'file1.txt',
'file2.md',
'file3.txt',
'test/dir/file4.txt',
'test/dir/file5.txt',
'dir/file6.md'
],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const result = await getFilteredChangedFiles({
allDiffFiles,
filePatterns: ['*.txt']
})
expect(result).toEqual({
[ChangeTypeEnum.Added]: ['file1.txt', 'file3.txt'],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
})
})
// Tests that the function returns only the files that match the file patterns on windows
it('should return only the files that match the file patterns on windows', async () => {
mockedPlatform('win32')
const allDiffFiles = {
[ChangeTypeEnum.Added]: [
'file1.txt',
'file2.md',
'file3.txt',
'test\\dir\\file4.txt',
'test\\dir\\file5.txt',
'dir\\file6.md'
],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const result = await getFilteredChangedFiles({
allDiffFiles,
filePatterns: ['*.txt']
})
expect(result).toEqual({
[ChangeTypeEnum.Added]: ['file1.txt', 'file3.txt'],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
})
})
// Tests that the function returns only the files that match the file patterns with globstar on non windows platforms
it('should return only the files that match the file patterns with globstar', async () => {
const allDiffFiles = {
[ChangeTypeEnum.Added]: [
'file1.txt',
'file2.md',
'file3.txt',
'test/dir/file4.txt',
'test/dir/file5.txt',
'dir/file6.md'
],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const result = await getFilteredChangedFiles({
allDiffFiles,
filePatterns: ['**.txt']
})
expect(result).toEqual({
[ChangeTypeEnum.Added]: [
'file1.txt',
'file3.txt',
'test/dir/file4.txt',
'test/dir/file5.txt'
],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
})
})
// Tests that the function returns only the files that match the file patterns with globstar on windows
it('should return only the files that match the file patterns with globstar on windows', async () => {
mockedPlatform('win32')
const allDiffFiles = {
[ChangeTypeEnum.Added]: ['test\\test rename-1.txt'],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const result = await getFilteredChangedFiles({
allDiffFiles,
filePatterns: ['test/**']
})
expect(result).toEqual({
[ChangeTypeEnum.Added]: ['test\\test rename-1.txt'],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
})
})
// Tests that the function returns an empty object when there are no files that match the file patterns
it('should return an empty object when there are no files that match the file patterns', async () => {
const allDiffFiles = {
[ChangeTypeEnum.Added]: ['file1.md', 'file2.md', 'file3.md'],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const result = await getFilteredChangedFiles({
allDiffFiles,
filePatterns: ['*.txt']
})
expect(result).toEqual({
[ChangeTypeEnum.Added]: [],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
})
})
// Tests that the function can handle file names with special characters
it('should handle file names with special characters', async () => {
const allDiffFiles = {
[ChangeTypeEnum.Added]: [
'file1.txt',
'file2 with spaces.txt',
'file3$$.txt'
],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const result = await getFilteredChangedFiles({
allDiffFiles,
filePatterns: ['file2*.txt']
})
expect(result).toEqual({
[ChangeTypeEnum.Added]: ['file2 with spaces.txt'],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
})
})
// Tests that getFilteredChangedFiles correctly filters files using glob patterns
it('should filter files using glob patterns', async () => {
const allDiffFiles = {
[ChangeTypeEnum.Added]: ['test/migrations/test.sql'],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const filePatterns = ['test/migrations/**']
const filteredFiles = await getFilteredChangedFiles({
allDiffFiles,
filePatterns
})
expect(filteredFiles[ChangeTypeEnum.Added]).toEqual([
'test/migrations/test.sql'
])
})
// Tests that getFilteredChangedFiles correctly filters files using ignore glob patterns
it('should filter files using ignore glob patterns', async () => {
const allDiffFiles = {
[ChangeTypeEnum.Added]: [],
[ChangeTypeEnum.Copied]: [],
[ChangeTypeEnum.Deleted]: [],
[ChangeTypeEnum.Modified]: [
'assets/scripts/configure-minikube-linux.sh'
],
[ChangeTypeEnum.Renamed]: [],
[ChangeTypeEnum.TypeChanged]: [],
[ChangeTypeEnum.Unmerged]: [],
[ChangeTypeEnum.Unknown]: []
}
const filePatterns = [
'assets/scripts/**.sh',
'!assets/scripts/configure-minikube-linux.sh'
]
const filteredFiles = await getFilteredChangedFiles({
allDiffFiles,
filePatterns
})
expect(filteredFiles[ChangeTypeEnum.Modified]).toEqual([])
})
})
describe('warnUnsupportedRESTAPIInputs', () => {
// Warns about unsupported inputs when using the REST API.
it('should warn about unsupported inputs when all inputs are supported', async () => {
const inputs: Inputs = {
files: '',
filesSeparator: '\n',
filesFromSourceFile: '',
filesFromSourceFileSeparator: '\n',
filesYaml: '',
filesYamlFromSourceFile: '',
filesYamlFromSourceFileSeparator: '\n',
filesIgnore: '',
filesIgnoreSeparator: '\n',
filesIgnoreFromSourceFile: '',
filesIgnoreFromSourceFileSeparator: '\n',
filesIgnoreYaml: '',
filesIgnoreYamlFromSourceFile: '',
filesIgnoreYamlFromSourceFileSeparator: '\n',
separator: ' ',
includeAllOldNewRenamedFiles: false,
oldNewSeparator: ',',
oldNewFilesSeparator: ' ',
sha: '1313123',
baseSha: '',
since: '',
until: '',
path: '.',
quotepath: true,
diffRelative: true,
dirNames: false,
dirNamesMaxDepth: undefined,
dirNamesExcludeCurrentDir: false,
dirNamesIncludeFiles: '',
dirNamesIncludeFilesSeparator: '\n',
dirNamesDeletedFilesIncludeOnlyDeletedDirs: false,
json: false,
escapeJson: true,
safeOutput: true,
fetchDepth: 50,
fetchAdditionalSubmoduleHistory: false,
sinceLastRemoteCommit: false,
writeOutputFiles: false,
outputDir: '.github/outputs',
outputRenamedFilesAsDeletedAndAdded: false,
recoverDeletedFiles: false,
recoverDeletedFilesToDestination: '',
recoverFiles: '',
recoverFilesSeparator: '\n',
recoverFilesIgnore: '',
recoverFilesIgnoreSeparator: '\n',
token: '${{ github.token }}',
apiUrl: '${{ github.api_url }}',
skipInitialFetch: false,
failOnInitialDiffError: false,
failOnSubmoduleDiffError: false,
negationPatternsFirst: false,
useRestApi: false,
excludeSubmodules: false,
fetchMissingHistoryMaxRetries: 20,
usePosixPathSeparator: false,
tagsPattern: '*',
tagsIgnorePattern: ''
}
const coreWarningSpy = jest.spyOn(core, 'warning')
await warnUnsupportedRESTAPIInputs({
inputs
})
expect(coreWarningSpy).toHaveBeenCalledWith(
'Input "sha" is not supported when using GitHub\'s REST API to get changed files'
)
expect(coreWarningSpy).toHaveBeenCalledTimes(1)
})
})
describe('getPreviousGitTag', () => {
// Check if the environment variable GITHUB_REPOSITORY_OWNER is 'tj-actions'
const shouldSkip = !!process.env.GITHUB_EVENT_PULL_REQUEST_HEAD_REPO_FORK
// Function returns the second-latest tag and its SHA
it('should return the second latest tag and its SHA when multiple tags are present', async () => {
if (shouldSkip) {
return
}
const result = await getPreviousGitTag({
cwd: '.',
tagsPattern: '*',
tagsIgnorePattern: '',
currentBranch: 'v1.0.1'
})
expect(result).toEqual({
tag: 'v1.0.0',
sha: 'f0751de6af436d4e79016e2041cf6400e0833653'
})
})
// Tags are filtered by a specified pattern when 'tagsPattern' is provided
it('should filter tags by the specified pattern', async () => {
if (shouldSkip) {
return
}
const result = await getPreviousGitTag({
cwd: '.',
tagsPattern: 'v1.*',
tagsIgnorePattern: '',
currentBranch: 'v1.0.1'
})
expect(result).toEqual({
tag: 'v1.0.0',
sha: 'f0751de6af436d4e79016e2041cf6400e0833653'
})
})
// Tags are excluded by a specified ignore pattern when 'tagsIgnorePattern' is provided
it('should exclude tags by the specified ignore pattern', async () => {
if (shouldSkip) {
return
}
const result = await getPreviousGitTag({
cwd: '.',
tagsPattern: '*',
tagsIgnorePattern: 'v0.*.*',
currentBranch: 'v1.0.1'
})
expect(result).toEqual({
tag: 'v1.0.0',
sha: 'f0751de6af436d4e79016e2041cf6400e0833653'
})
})
// No tags are available in the repository
it('should return empty values when no tags are available in the repository', async () => {
jest.spyOn(exec, 'getExecOutput').mockResolvedValueOnce({
stdout: '',
stderr: '',
exitCode: 0
})
const result = await getPreviousGitTag({
cwd: '.',
tagsPattern: '*',
tagsIgnorePattern: '',
currentBranch: ''
})
expect(result).toEqual({tag: '', sha: ''})
})
// Only one tag is available, making it impossible to find a previous tag
it('should return empty values when only one tag is available', async () => {
jest.spyOn(exec, 'getExecOutput').mockResolvedValueOnce({
stdout:
'v1.0.1|f0751de6af436d4e79016e2041cf6400e0833653|2021-01-01T00:00:00Z',
stderr: '',
exitCode: 0
})
const result = await getPreviousGitTag({
cwd: '.',
tagsPattern: '*',
tagsIgnorePattern: '',
currentBranch: 'v1.0.1'
})
expect(result).toEqual({tag: '', sha: ''})
})
// Git commands fail and throw errors
it('should throw an error when git commands fail', async () => {
jest
.spyOn(exec, 'getExecOutput')
.mockRejectedValue(new Error('git command failed'))
await expect(
getPreviousGitTag({
cwd: '.',
tagsPattern: '*',
tagsIgnorePattern: '',
currentBranch: 'v1.0.1'
})
).rejects.toThrow('git command failed')
})
})
})

View File

@ -1,134 +1,30 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import type {RestEndpointMethodTypes} from '@octokit/rest'
import flatten from 'lodash/flatten'
import convertPath from '@stdlib/utils-convert-path'
import mm from 'micromatch'
import * as path from 'path'
import {setOutputsAndGetModifiedAndChangedFilesStatus} from './changedFilesOutput'
import {DiffResult} from './commitSha'
import {Env} from './env'
import {Inputs} from './inputs'
import {
canDiffCommits,
getAllChangedFiles,
getDirnameMaxDepth,
getDirNamesIncludeFilesPattern,
getFilteredChangedFiles,
gitRenamedFiles,
gitSubmoduleDiffSHA,
isWindows,
jsonOutput,
setArrayOutput
getAllChangedFiles
} from './utils'
export const processChangedFiles = async ({
filePatterns,
allDiffFiles,
inputs,
yamlFilePatterns,
workingDirectory
}: {
filePatterns: string[]
allDiffFiles: ChangedFiles
inputs: Inputs
yamlFilePatterns: Record<string, string[]>
workingDirectory?: string
}): Promise<void> => {
if (filePatterns.length > 0) {
core.startGroup('changed-files-patterns')
const allFilteredDiffFiles = await getFilteredChangedFiles({
allDiffFiles,
filePatterns
})
core.debug(
`All filtered diff files: ${JSON.stringify(allFilteredDiffFiles)}`
)
await setOutputsAndGetModifiedAndChangedFilesStatus({
allDiffFiles,
allFilteredDiffFiles,
inputs,
filePatterns,
workingDirectory
})
core.info('All Done!')
core.endGroup()
}
if (Object.keys(yamlFilePatterns).length > 0) {
const modifiedKeys: string[] = []
const changedKeys: string[] = []
for (const key of Object.keys(yamlFilePatterns)) {
core.startGroup(`changed-files-yaml-${key}`)
const allFilteredDiffFiles = await getFilteredChangedFiles({
allDiffFiles,
filePatterns: yamlFilePatterns[key]
})
core.debug(
`All filtered diff files for ${key}: ${JSON.stringify(
allFilteredDiffFiles
)}`
)
const {anyChanged, anyModified} =
await setOutputsAndGetModifiedAndChangedFilesStatus({
allDiffFiles,
allFilteredDiffFiles,
inputs,
filePatterns: yamlFilePatterns[key],
outputPrefix: key,
workingDirectory
})
if (anyModified) {
modifiedKeys.push(key)
}
if (anyChanged) {
changedKeys.push(key)
}
core.info('All Done!')
core.endGroup()
}
if (modifiedKeys.length > 0) {
await setArrayOutput({
key: 'modified_keys',
inputs,
value: modifiedKeys
})
}
if (changedKeys.length > 0) {
await setArrayOutput({
key: 'changed_keys',
inputs,
value: changedKeys
})
}
}
if (filePatterns.length === 0 && Object.keys(yamlFilePatterns).length === 0) {
core.startGroup('changed-files-all')
await setOutputsAndGetModifiedAndChangedFilesStatus({
allDiffFiles,
allFilteredDiffFiles: allDiffFiles,
inputs,
workingDirectory
})
core.info('All Done!')
core.endGroup()
}
}
import flatten from 'lodash/flatten'
export const getRenamedFiles = async ({
inputs,
workingDirectory,
diffSubmodule,
hasSubmodule,
diffResult,
submodulePaths
}: {
inputs: Inputs
workingDirectory: string
diffSubmodule: boolean
hasSubmodule: boolean
diffResult: DiffResult
submodulePaths: string[]
}): Promise<{paths: string; count: string}> => {
@ -140,7 +36,7 @@ export const getRenamedFiles = async ({
oldNewSeparator: inputs.oldNewSeparator
})
if (diffSubmodule) {
if (hasSubmodule) {
for (const submodulePath of submodulePaths) {
const submoduleShaResult = await gitSubmoduleDiffSHA({
cwd: workingDirectory,
@ -156,29 +52,11 @@ export const getRenamedFiles = async ({
)
if (submoduleShaResult.currentSha && submoduleShaResult.previousSha) {
let diff = '...'
if (
!(await canDiffCommits({
cwd: submoduleWorkingDirectory,
sha1: submoduleShaResult.previousSha,
sha2: submoduleShaResult.currentSha,
diff
}))
) {
let message = `Unable to use three dot diff for: ${submodulePath} submodule. Falling back to two dot diff. You can set 'fetch_additional_submodule_history: true' to fetch additional submodule history in order to use three dot diff`
if (inputs.fetchAdditionalSubmoduleHistory) {
message = `To fetch additional submodule history for: ${submodulePath} you can increase history depth using 'fetch_depth' input`
}
core.info(message)
diff = '..'
}
const submoduleRenamedFiles = await gitRenamedFiles({
cwd: submoduleWorkingDirectory,
sha1: submoduleShaResult.previousSha,
sha2: submoduleShaResult.currentSha,
diff,
diff: diffResult.diff,
oldNewSeparator: inputs.oldNewSeparator,
isSubmodule: true,
parentDir: submodulePath
@ -218,33 +96,26 @@ export type ChangedFiles = {
export const getAllDiffFiles = async ({
workingDirectory,
diffSubmodule,
hasSubmodule,
diffResult,
submodulePaths,
outputRenamedFilesAsDeletedAndAdded,
fetchAdditionalSubmoduleHistory,
failOnInitialDiffError,
failOnSubmoduleDiffError
outputRenamedFilesAsDeletedAndAdded
}: {
workingDirectory: string
diffSubmodule: boolean
hasSubmodule: boolean
diffResult: DiffResult
submodulePaths: string[]
outputRenamedFilesAsDeletedAndAdded: boolean
fetchAdditionalSubmoduleHistory: boolean
failOnInitialDiffError: boolean
failOnSubmoduleDiffError: boolean
}): Promise<ChangedFiles> => {
const files = await getAllChangedFiles({
cwd: workingDirectory,
sha1: diffResult.previousSha,
sha2: diffResult.currentSha,
diff: diffResult.diff,
outputRenamedFilesAsDeletedAndAdded,
failOnInitialDiffError
outputRenamedFilesAsDeletedAndAdded
})
if (diffSubmodule) {
if (hasSubmodule) {
for (const submodulePath of submodulePaths) {
const submoduleShaResult = await gitSubmoduleDiffSHA({
cwd: workingDirectory,
@ -260,33 +131,14 @@ export const getAllDiffFiles = async ({
)
if (submoduleShaResult.currentSha && submoduleShaResult.previousSha) {
let diff = '...'
if (
!(await canDiffCommits({
cwd: submoduleWorkingDirectory,
sha1: submoduleShaResult.previousSha,
sha2: submoduleShaResult.currentSha,
diff
}))
) {
let message = `Set 'fetch_additional_submodule_history: true' to fetch additional submodule history for: ${submodulePath}`
if (fetchAdditionalSubmoduleHistory) {
message = `To fetch additional submodule history for: ${submodulePath} you can increase history depth using 'fetch_depth' input`
}
core.warning(message)
diff = '..'
}
const submoduleFiles = await getAllChangedFiles({
cwd: submoduleWorkingDirectory,
sha1: submoduleShaResult.previousSha,
sha2: submoduleShaResult.currentSha,
diff,
diff: diffResult.diff,
isSubmodule: true,
parentDir: submodulePath,
outputRenamedFilesAsDeletedAndAdded,
failOnSubmoduleDiffError
outputRenamedFilesAsDeletedAndAdded
})
for (const changeType of Object.keys(
@ -304,35 +156,6 @@ export const getAllDiffFiles = async ({
return files
}
function* getFilePaths({
inputs,
filePaths,
dirNamesIncludeFilePatterns
}: {
inputs: Inputs
filePaths: string[]
dirNamesIncludeFilePatterns: string[]
}): Generator<string> {
for (const filePath of filePaths) {
if (inputs.dirNames) {
if (dirNamesIncludeFilePatterns.length > 0) {
const isWin = isWindows()
const matchOptions = {dot: true, windows: isWin, noext: true}
if (mm.isMatch(filePath, dirNamesIncludeFilePatterns, matchOptions)) {
yield filePath
}
}
yield getDirnameMaxDepth({
relativePath: filePath,
dirNamesMaxDepth: inputs.dirNamesMaxDepth,
excludeCurrentDir: inputs.dirNamesExcludeCurrentDir
})
} else {
yield filePath
}
}
}
function* getChangeTypeFilesGenerator({
inputs,
changedFiles,
@ -342,24 +165,17 @@ function* getChangeTypeFilesGenerator({
changedFiles: ChangedFiles
changeTypes: ChangeTypeEnum[]
}): Generator<string> {
const dirNamesIncludeFilePatterns = getDirNamesIncludeFilesPattern({inputs})
core.debug(
`Dir names include file patterns: ${JSON.stringify(
dirNamesIncludeFilePatterns
)}`
)
for (const changeType of changeTypes) {
const filePaths = changedFiles[changeType] || []
for (const filePath of getFilePaths({
inputs,
filePaths,
dirNamesIncludeFilePatterns
})) {
if (isWindows() && inputs.usePosixPathSeparator) {
yield convertPath(filePath, 'mixed')
const files = changedFiles[changeType] || []
for (const file of files) {
if (inputs.dirNames) {
yield getDirnameMaxDepth({
pathStr: file,
dirNamesMaxDepth: inputs.dirNamesMaxDepth,
excludeCurrentDir: inputs.dirNamesExcludeCurrentDir
})
} else {
yield filePath
yield file
}
}
}
@ -373,15 +189,20 @@ export const getChangeTypeFiles = async ({
inputs: Inputs
changedFiles: ChangedFiles
changeTypes: ChangeTypeEnum[]
}): Promise<{paths: string[] | string; count: string}> => {
}): Promise<{paths: string; count: string}> => {
const files = [
...new Set(getChangeTypeFilesGenerator({inputs, changedFiles, changeTypes}))
].filter(Boolean)
]
const paths = inputs.json ? files : files.join(inputs.separator)
if (inputs.json) {
return {
paths: jsonOutput({value: files, shouldEscape: inputs.escapeJson}),
count: files.length.toString()
}
}
return {
paths,
paths: files.join(inputs.separator),
count: files.length.toString()
}
}
@ -393,24 +214,15 @@ function* getAllChangeTypeFilesGenerator({
inputs: Inputs
changedFiles: ChangedFiles
}): Generator<string> {
const dirNamesIncludeFilePatterns = getDirNamesIncludeFilesPattern({inputs})
core.debug(
`Dir names include file patterns: ${JSON.stringify(
dirNamesIncludeFilePatterns
)}`
)
const filePaths = flatten(Object.values(changedFiles))
for (const filePath of getFilePaths({
inputs,
filePaths,
dirNamesIncludeFilePatterns
})) {
if (isWindows() && inputs.usePosixPathSeparator) {
yield convertPath(filePath, 'mixed')
for (const file of flatten(Object.values(changedFiles))) {
if (inputs.dirNames) {
yield getDirnameMaxDepth({
pathStr: file,
dirNamesMaxDepth: inputs.dirNamesMaxDepth,
excludeCurrentDir: inputs.dirNamesExcludeCurrentDir
})
} else {
yield filePath
yield file
}
}
}
@ -421,23 +233,30 @@ export const getAllChangeTypeFiles = async ({
}: {
inputs: Inputs
changedFiles: ChangedFiles
}): Promise<{paths: string[] | string; count: string}> => {
}): Promise<{paths: string; count: string}> => {
const files = [
...new Set(getAllChangeTypeFilesGenerator({inputs, changedFiles}))
].filter(Boolean)
]
const paths = inputs.json ? files : files.join(inputs.separator)
if (inputs.json) {
return {
paths: jsonOutput({value: files, shouldEscape: inputs.escapeJson}),
count: files.length.toString()
}
}
return {
paths,
paths: files.join(inputs.separator),
count: files.length.toString()
}
}
export const getChangedFilesFromGithubAPI = async ({
inputs
inputs,
env
}: {
inputs: Inputs
env: Env
}): Promise<ChangedFiles> => {
const octokit = github.getOctokit(inputs.token, {
baseUrl: inputs.apiUrl
@ -458,14 +277,13 @@ export const getChangedFilesFromGithubAPI = async ({
const options = octokit.rest.pulls.listFiles.endpoint.merge({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
pull_number: github.context.payload.pull_request?.number,
pull_number: env.GITHUB_EVENT_PULL_REQUEST_NUMBER,
per_page: 100
})
const paginatedResponse =
await octokit.paginate<
RestEndpointMethodTypes['pulls']['listFiles']['response']['data'][0]
>(options)
const paginatedResponse = await octokit.paginate<
RestEndpointMethodTypes['pulls']['listFiles']['response']['data'][0]
>(options)
core.info(`Found ${paginatedResponse.length} changed files from GitHub API`)
const statusMap: Record<string, ChangeTypeEnum> = {
@ -484,7 +302,7 @@ export const getChangedFilesFromGithubAPI = async ({
if (changeType === ChangeTypeEnum.Renamed) {
if (inputs.outputRenamedFilesAsDeletedAndAdded) {
changedFiles[ChangeTypeEnum.Deleted].push(item.previous_filename || '')
changedFiles[ChangeTypeEnum.Deleted].push(item.filename)
changedFiles[ChangeTypeEnum.Added].push(item.filename)
} else {
changedFiles[ChangeTypeEnum.Renamed].push(item.filename)

View File

@ -1,56 +1,63 @@
import * as core from '@actions/core'
import path from 'path'
import {
ChangedFiles,
ChangeTypeEnum,
getAllChangeTypeFiles,
getChangeTypeFiles
} from './changedFiles'
import {DiffResult} from './commitSha'
import {Inputs} from './inputs'
import {getOutputKey, setArrayOutput, setOutput, exists} from './utils'
import {getFilteredChangedFiles, recoverDeletedFiles, setOutput} from './utils'
const getArrayFromPaths = (
paths: string | string[],
inputs: Inputs
): string[] => {
return Array.isArray(paths) ? paths : paths.split(inputs.separator)
const getOutputKey = (key: string, outputPrefix: string): string => {
return outputPrefix ? `${outputPrefix}_${key}` : key
}
export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
export const setChangedFilesOutput = async ({
allDiffFiles,
allFilteredDiffFiles,
inputs,
workingDirectory,
diffResult,
filePatterns = [],
outputPrefix = '',
workingDirectory
outputPrefix = ''
}: {
allDiffFiles: ChangedFiles
allFilteredDiffFiles: ChangedFiles
inputs: Inputs
workingDirectory: string
diffResult?: DiffResult
filePatterns?: string[]
outputPrefix?: string
workingDirectory?: string
}): Promise<{anyModified: boolean; anyChanged: boolean}> => {
}): Promise<void> => {
const allFilteredDiffFiles = await getFilteredChangedFiles({
allDiffFiles,
filePatterns
})
core.debug(`All filtered diff files: ${JSON.stringify(allFilteredDiffFiles)}`)
if (diffResult) {
await recoverDeletedFiles({
inputs,
workingDirectory,
deletedFiles: allFilteredDiffFiles[ChangeTypeEnum.Deleted],
sha: diffResult.previousSha
})
}
const addedFiles = await getChangeTypeFiles({
inputs,
changedFiles: allFilteredDiffFiles,
changeTypes: [ChangeTypeEnum.Added]
})
core.debug(`Added files: ${JSON.stringify(addedFiles)}`)
core.debug(`Added files: ${addedFiles}`)
await setOutput({
key: getOutputKey('added_files', outputPrefix),
value: addedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('added_files_count', outputPrefix),
value: addedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const copiedFiles = await getChangeTypeFiles({
@ -58,22 +65,17 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
changedFiles: allFilteredDiffFiles,
changeTypes: [ChangeTypeEnum.Copied]
})
core.debug(`Copied files: ${JSON.stringify(copiedFiles)}`)
core.debug(`Copied files: ${copiedFiles}`)
await setOutput({
key: getOutputKey('copied_files', outputPrefix),
value: copiedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('copied_files_count', outputPrefix),
value: copiedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const modifiedFiles = await getChangeTypeFiles({
@ -81,22 +83,17 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
changedFiles: allFilteredDiffFiles,
changeTypes: [ChangeTypeEnum.Modified]
})
core.debug(`Modified files: ${JSON.stringify(modifiedFiles)}`)
core.debug(`Modified files: ${modifiedFiles}`)
await setOutput({
key: getOutputKey('modified_files', outputPrefix),
value: modifiedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('modified_files_count', outputPrefix),
value: modifiedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const renamedFiles = await getChangeTypeFiles({
@ -104,22 +101,17 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
changedFiles: allFilteredDiffFiles,
changeTypes: [ChangeTypeEnum.Renamed]
})
core.debug(`Renamed files: ${JSON.stringify(renamedFiles)}`)
core.debug(`Renamed files: ${renamedFiles}`)
await setOutput({
key: getOutputKey('renamed_files', outputPrefix),
value: renamedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('renamed_files_count', outputPrefix),
value: renamedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const typeChangedFiles = await getChangeTypeFiles({
@ -127,22 +119,17 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
changedFiles: allFilteredDiffFiles,
changeTypes: [ChangeTypeEnum.TypeChanged]
})
core.debug(`Type changed files: ${JSON.stringify(typeChangedFiles)}`)
core.debug(`Type changed files: ${typeChangedFiles}`)
await setOutput({
key: getOutputKey('type_changed_files', outputPrefix),
value: typeChangedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('type_changed_files_count', outputPrefix),
value: typeChangedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const unmergedFiles = await getChangeTypeFiles({
@ -150,22 +137,17 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
changedFiles: allFilteredDiffFiles,
changeTypes: [ChangeTypeEnum.Unmerged]
})
core.debug(`Unmerged files: ${JSON.stringify(unmergedFiles)}`)
core.debug(`Unmerged files: ${unmergedFiles}`)
await setOutput({
key: getOutputKey('unmerged_files', outputPrefix),
value: unmergedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('unmerged_files_count', outputPrefix),
value: unmergedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const unknownFiles = await getChangeTypeFiles({
@ -173,48 +155,34 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
changedFiles: allFilteredDiffFiles,
changeTypes: [ChangeTypeEnum.Unknown]
})
core.debug(`Unknown files: ${JSON.stringify(unknownFiles)}`)
core.debug(`Unknown files: ${unknownFiles}`)
await setOutput({
key: getOutputKey('unknown_files', outputPrefix),
value: unknownFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('unknown_files_count', outputPrefix),
value: unknownFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const allChangedAndModifiedFiles = await getAllChangeTypeFiles({
inputs,
changedFiles: allFilteredDiffFiles
})
core.debug(
`All changed and modified files: ${JSON.stringify(
allChangedAndModifiedFiles
)}`
)
core.debug(`All changed and modified files: ${allChangedAndModifiedFiles}`)
await setOutput({
key: getOutputKey('all_changed_and_modified_files', outputPrefix),
value: allChangedAndModifiedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('all_changed_and_modified_files_count', outputPrefix),
value: allChangedAndModifiedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const allChangedFiles = await getChangeTypeFiles({
@ -227,30 +195,23 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
ChangeTypeEnum.Renamed
]
})
core.debug(`All changed files: ${JSON.stringify(allChangedFiles)}`)
core.debug(`All changed files: ${allChangedFiles}`)
await setOutput({
key: getOutputKey('all_changed_files', outputPrefix),
value: allChangedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('all_changed_files_count', outputPrefix),
value: allChangedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
await setOutput({
key: getOutputKey('any_changed', outputPrefix),
value: allChangedFiles.paths.length > 0,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json
value: allChangedFiles.paths.length > 0 && filePatterns.length > 0,
inputs
})
const allOtherChangedFiles = await getChangeTypeFiles({
@ -263,20 +224,14 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
ChangeTypeEnum.Renamed
]
})
core.debug(`All other changed files: ${JSON.stringify(allOtherChangedFiles)}`)
core.debug(`All other changed files: ${allOtherChangedFiles}`)
const allOtherChangedFilesPaths: string[] = getArrayFromPaths(
allOtherChangedFiles.paths,
inputs
)
const allChangedFilesPaths: string[] = getArrayFromPaths(
allChangedFiles.paths,
inputs
)
const otherChangedFiles = allOtherChangedFilesPaths.filter(
(filePath: string) => !allChangedFilesPaths.includes(filePath)
)
const otherChangedFiles = allOtherChangedFiles.paths
.split(inputs.separator)
.filter(
(filePath: string) =>
!allChangedFiles.paths.split(inputs.separator).includes(filePath)
)
const onlyChanged =
otherChangedFiles.length === 0 &&
@ -286,23 +241,19 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
await setOutput({
key: getOutputKey('only_changed', outputPrefix),
value: onlyChanged,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json
inputs
})
await setArrayOutput({
key: 'other_changed_files',
inputs,
value: otherChangedFiles,
outputPrefix
await setOutput({
key: getOutputKey('other_changed_files', outputPrefix),
value: otherChangedFiles.join(inputs.separator),
inputs
})
await setOutput({
key: getOutputKey('other_changed_files_count', outputPrefix),
value: otherChangedFiles.length.toString(),
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const allModifiedFiles = await getChangeTypeFiles({
@ -316,30 +267,23 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
ChangeTypeEnum.Deleted
]
})
core.debug(`All modified files: ${JSON.stringify(allModifiedFiles)}`)
core.debug(`All modified files: ${allModifiedFiles}`)
await setOutput({
key: getOutputKey('all_modified_files', outputPrefix),
value: allModifiedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('all_modified_files_count', outputPrefix),
value: allModifiedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
await setOutput({
key: getOutputKey('any_modified', outputPrefix),
value: allModifiedFiles.paths.length > 0,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json
value: allModifiedFiles.paths.length > 0 && filePatterns.length > 0,
inputs
})
const allOtherModifiedFiles = await getChangeTypeFiles({
@ -354,19 +298,12 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
]
})
const allOtherModifiedFilesPaths: string[] = getArrayFromPaths(
allOtherModifiedFiles.paths,
inputs
)
const allModifiedFilesPaths: string[] = getArrayFromPaths(
allModifiedFiles.paths,
inputs
)
const otherModifiedFiles = allOtherModifiedFilesPaths.filter(
(filePath: string) => !allModifiedFilesPaths.includes(filePath)
)
const otherModifiedFiles = allOtherModifiedFiles.paths
.split(inputs.separator)
.filter(
(filePath: string) =>
!allModifiedFiles.paths.split(inputs.separator).includes(filePath)
)
const onlyModified =
otherModifiedFiles.length === 0 &&
@ -376,23 +313,19 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
await setOutput({
key: getOutputKey('only_modified', outputPrefix),
value: onlyModified,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json
inputs
})
await setArrayOutput({
key: 'other_modified_files',
inputs,
value: otherModifiedFiles,
outputPrefix
await setOutput({
key: getOutputKey('other_modified_files', outputPrefix),
value: otherModifiedFiles.join(inputs.separator),
inputs
})
await setOutput({
key: getOutputKey('other_modified_files_count', outputPrefix),
value: otherModifiedFiles.length.toString(),
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
const deletedFiles = await getChangeTypeFiles({
@ -400,52 +333,23 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
changedFiles: allFilteredDiffFiles,
changeTypes: [ChangeTypeEnum.Deleted]
})
core.debug(`Deleted files: ${JSON.stringify(deletedFiles)}`)
if (
inputs.dirNamesDeletedFilesIncludeOnlyDeletedDirs &&
inputs.dirNames &&
workingDirectory
) {
const newDeletedFilesPaths: string[] = []
for (const deletedPath of getArrayFromPaths(deletedFiles.paths, inputs)) {
const dirPath = path.join(workingDirectory, deletedPath)
core.debug(`Checking if directory exists: ${dirPath}`)
if (!(await exists(dirPath))) {
core.debug(`Directory not found: ${dirPath}`)
newDeletedFilesPaths.push(deletedPath)
}
}
deletedFiles.paths = inputs.json
? newDeletedFilesPaths
: newDeletedFilesPaths.join(inputs.separator)
deletedFiles.count = newDeletedFilesPaths.length.toString()
core.debug(`New deleted files: ${JSON.stringify(deletedFiles)}`)
}
core.debug(`Deleted files: ${deletedFiles}`)
await setOutput({
key: getOutputKey('deleted_files', outputPrefix),
value: deletedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
shouldEscape: inputs.escapeJson,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: getOutputKey('deleted_files_count', outputPrefix),
value: deletedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
await setOutput({
key: getOutputKey('any_deleted', outputPrefix),
value: deletedFiles.paths.length > 0,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json
value: deletedFiles.paths.length > 0 && filePatterns.length > 0,
inputs
})
const allOtherDeletedFiles = await getChangeTypeFiles({
@ -454,19 +358,11 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
changeTypes: [ChangeTypeEnum.Deleted]
})
const allOtherDeletedFilesPaths: string[] = getArrayFromPaths(
allOtherDeletedFiles.paths,
inputs
)
const deletedFilesPaths: string[] = getArrayFromPaths(
deletedFiles.paths,
inputs
)
const otherDeletedFiles = allOtherDeletedFilesPaths.filter(
filePath => !deletedFilesPaths.includes(filePath)
)
const otherDeletedFiles = allOtherDeletedFiles.paths
.split(inputs.separator)
.filter(
filePath => !deletedFiles.paths.split(inputs.separator).includes(filePath)
)
const onlyDeleted =
otherDeletedFiles.length === 0 &&
@ -476,27 +372,18 @@ export const setOutputsAndGetModifiedAndChangedFilesStatus = async ({
await setOutput({
key: getOutputKey('only_deleted', outputPrefix),
value: onlyDeleted,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json
inputs
})
await setArrayOutput({
key: 'other_deleted_files',
inputs,
value: otherDeletedFiles,
outputPrefix
await setOutput({
key: getOutputKey('other_deleted_files', outputPrefix),
value: otherDeletedFiles.join(inputs.separator),
inputs
})
await setOutput({
key: getOutputKey('other_deleted_files_count', outputPrefix),
value: otherDeletedFiles.length.toString(),
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir
inputs
})
return {
anyModified: allModifiedFiles.paths.length > 0,
anyChanged: allChangedFiles.paths.length > 0
}
}

View File

@ -1,12 +1,9 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import {Env} from './env'
import {Inputs} from './inputs'
import {
canDiffCommits,
cleanShaInput,
getCurrentBranchName,
getHeadSha,
getParentSha,
getPreviousGitTag,
@ -18,17 +15,15 @@ import {
} from './utils'
const getCurrentSHA = async ({
env,
inputs,
workingDirectory
}: {
env: Env
inputs: Inputs
workingDirectory: string
}): Promise<string> => {
let currentSha = await cleanShaInput({
sha: inputs.sha,
cwd: workingDirectory,
token: inputs.token
})
let currentSha = inputs.sha
core.debug('Getting current SHA...')
if (inputs.until) {
@ -55,16 +50,14 @@ const getCurrentSHA = async ({
} else {
if (!currentSha) {
if (
github.context.payload.pull_request?.head?.sha &&
env.GITHUB_EVENT_PULL_REQUEST_HEAD_SHA &&
(await verifyCommitSha({
sha: github.context.payload.pull_request?.head?.sha,
sha: env.GITHUB_EVENT_PULL_REQUEST_HEAD_SHA,
cwd: workingDirectory,
showAsErrorMessage: false
})) === 0
) {
currentSha = github.context.payload.pull_request?.head?.sha
} else if (github.context.eventName === 'merge_group') {
currentSha = github.context.payload.merge_group?.head_sha
currentSha = env.GITHUB_EVENT_PULL_REQUEST_HEAD_SHA
} else {
currentSha = await getHeadSha({cwd: workingDirectory})
}
@ -86,117 +79,69 @@ export interface DiffResult {
initialCommit?: boolean
}
interface SHAForNonPullRequestEvent {
inputs: Inputs
env: Env
workingDirectory: string
isShallow: boolean
diffSubmodule: boolean
gitFetchExtraArgs: string[]
export const getSHAForPushEvent = async (
inputs: Inputs,
env: Env,
workingDirectory: string,
isShallow: boolean,
hasSubmodule: boolean,
gitFetchExtraArgs: string[],
isTag: boolean
remoteName: string
}
export const getSHAForNonPullRequestEvent = async ({
inputs,
env,
workingDirectory,
isShallow,
diffSubmodule,
gitFetchExtraArgs,
isTag,
remoteName
}: SHAForNonPullRequestEvent): Promise<DiffResult> => {
): Promise<DiffResult> => {
let targetBranch = env.GITHUB_REF_NAME
let currentBranch = targetBranch
const currentBranch = targetBranch
let initialCommit = false
if (!inputs.skipInitialFetch) {
if (isShallow) {
core.info('Repository is shallow, fetching more history...')
if (isShallow && !inputs.skipInitialFetch) {
core.info('Repository is shallow, fetching more history...')
if (isTag) {
let sourceBranch = ''
if (github.context.payload.base_ref) {
sourceBranch = github.context.payload.base_ref.replace(
'refs/heads/',
''
)
} else if (github.context.payload.release?.target_commitish) {
sourceBranch = github.context.payload.release?.target_commitish
}
await gitFetch({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`,
remoteName,
`+refs/heads/${sourceBranch}:refs/remotes/${remoteName}/${sourceBranch}`
]
})
} else {
await gitFetch({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`,
remoteName,
`+refs/heads/${targetBranch}:refs/remotes/${remoteName}/${targetBranch}`
]
})
}
if (diffSubmodule) {
await gitFetchSubmodules({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`
]
})
}
if (isTag) {
const sourceBranch =
env.GITHUB_EVENT_BASE_REF.replace('refs/heads/', '') ||
env.GITHUB_EVENT_RELEASE_TARGET_COMMITISH
await gitFetch({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`,
'origin',
`+refs/heads/${sourceBranch}:refs/remotes/origin/${sourceBranch}`
]
})
} else {
if (diffSubmodule && inputs.fetchAdditionalSubmoduleHistory) {
await gitFetchSubmodules({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`
]
})
}
await gitFetch({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`,
'origin',
`+refs/heads/${targetBranch}:refs/remotes/origin/${targetBranch}`
]
})
}
if (hasSubmodule) {
await gitFetchSubmodules({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`
]
})
}
}
const currentSha = await getCurrentSHA({inputs, workingDirectory})
let previousSha = await cleanShaInput({
sha: inputs.baseSha,
cwd: workingDirectory,
token: inputs.token
})
const currentSha = await getCurrentSHA({env, inputs, workingDirectory})
let previousSha = inputs.baseSha
const diff = '..'
const currentBranchName = await getCurrentBranchName({cwd: workingDirectory})
if (
currentBranchName &&
currentBranchName !== 'HEAD' &&
(currentBranchName !== targetBranch || currentBranchName !== currentBranch)
) {
targetBranch = currentBranchName
currentBranch = currentBranchName
}
if (inputs.baseSha && inputs.sha && currentBranch && targetBranch) {
if (previousSha && currentSha && currentBranch && targetBranch) {
if (previousSha === currentSha) {
core.error(
`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`
@ -207,6 +152,7 @@ export const getSHAForNonPullRequestEvent = async ({
throw new Error('Similar commit hashes detected.')
}
await verifyCommitSha({sha: previousSha, cwd: workingDirectory})
core.debug(`Previous SHA: ${previousSha}`)
return {
@ -218,7 +164,7 @@ export const getSHAForNonPullRequestEvent = async ({
}
}
if (!previousSha || previousSha === currentSha) {
if (!previousSha) {
core.debug('Getting previous SHA...')
if (inputs.since) {
core.debug(`Getting base SHA for '${inputs.since}'...`)
@ -240,26 +186,13 @@ export const getSHAForNonPullRequestEvent = async ({
}
} else if (isTag) {
core.debug('Getting previous SHA for tag...')
const {sha, tag} = await getPreviousGitTag({
cwd: workingDirectory,
tagsPattern: inputs.tagsPattern,
tagsIgnorePattern: inputs.tagsIgnorePattern,
currentBranch
})
const {sha, tag} = await getPreviousGitTag({cwd: workingDirectory})
previousSha = sha
targetBranch = tag
} else {
if (github.context.eventName === 'merge_group') {
core.debug('Getting previous SHA for merge group...')
previousSha = github.context.payload.merge_group?.base_sha
} else {
core.debug('Getting previous SHA for last remote commit...')
if (
github.context.payload.forced === 'false' ||
!github.context.payload.forced
) {
previousSha = github.context.payload.before
}
core.debug('Getting previous SHA for last remote commit...')
if (env.GITHUB_EVENT_FORCED === 'false' || !env.GITHUB_EVENT_FORCED) {
previousSha = env.GITHUB_EVENT_BEFORE
}
if (
@ -324,62 +257,55 @@ export const getSHAForNonPullRequestEvent = async ({
}
}
interface SHAForPullRequestEvent {
inputs: Inputs
workingDirectory: string
isShallow: boolean
diffSubmodule: boolean
export const getSHAForPullRequestEvent = async (
inputs: Inputs,
env: Env,
workingDirectory: string,
isShallow: boolean,
hasSubmodule: boolean,
gitFetchExtraArgs: string[]
remoteName: string
}
export const getSHAForPullRequestEvent = async ({
inputs,
workingDirectory,
isShallow,
diffSubmodule,
gitFetchExtraArgs,
remoteName
}: SHAForPullRequestEvent): Promise<DiffResult> => {
let targetBranch = github.context.payload.pull_request?.base?.ref
const currentBranch = github.context.payload.pull_request?.head?.ref
): Promise<DiffResult> => {
let targetBranch = env.GITHUB_EVENT_PULL_REQUEST_BASE_REF
const currentBranch = env.GITHUB_EVENT_PULL_REQUEST_HEAD_REF
if (inputs.sinceLastRemoteCommit) {
targetBranch = currentBranch
}
if (!inputs.skipInitialFetch) {
if (isShallow && !inputs.skipInitialFetch) {
core.info('Repository is shallow, fetching more history...')
if (isShallow) {
let prFetchExitCode = await gitFetch({
let prFetchExitCode = await gitFetch({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
'origin',
`pull/${env.GITHUB_EVENT_PULL_REQUEST_NUMBER}/head:${currentBranch}`
]
})
if (prFetchExitCode !== 0) {
prFetchExitCode = await gitFetch({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
remoteName,
`pull/${github.context.payload.pull_request?.number}/head:${currentBranch}`
`--deepen=${inputs.fetchDepth}`,
'origin',
`+refs/heads/${currentBranch}*:refs/remotes/origin/${currentBranch}*`
]
})
}
if (prFetchExitCode !== 0) {
prFetchExitCode = await gitFetch({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`,
remoteName,
`+refs/heads/${currentBranch}*:refs/remotes/${remoteName}/${currentBranch}*`
]
})
}
if (prFetchExitCode !== 0) {
throw new Error(
'Failed to fetch pull request branch. Please ensure "persist-credentials" is set to "true" when checking out the repository. See: https://github.com/actions/checkout#usage'
)
}
if (prFetchExitCode !== 0) {
throw new Error(
'Failed to fetch pull request branch. Please ensure "persist-credentials" is set to "true" when checking out the repository. See: https://github.com/actions/checkout#usage'
)
}
if (!inputs.sinceLastRemoteCommit) {
core.debug('Fetching target branch...')
await gitFetch({
cwd: workingDirectory,
@ -388,24 +314,12 @@ export const getSHAForPullRequestEvent = async ({
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`,
remoteName,
`+refs/heads/${github.context.payload.pull_request?.base?.ref}:refs/remotes/${remoteName}/${github.context.payload.pull_request?.base?.ref}`
'origin',
`+refs/heads/${targetBranch}:refs/remotes/origin/${targetBranch}`
]
})
if (diffSubmodule) {
await gitFetchSubmodules({
cwd: workingDirectory,
args: [
...gitFetchExtraArgs,
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`
]
})
}
} else {
if (diffSubmodule && inputs.fetchAdditionalSubmoduleHistory) {
if (hasSubmodule) {
await gitFetchSubmodules({
cwd: workingDirectory,
args: [
@ -420,15 +334,11 @@ export const getSHAForPullRequestEvent = async ({
core.info('Completed fetching more history.')
}
const currentSha = await getCurrentSHA({inputs, workingDirectory})
let previousSha = await cleanShaInput({
sha: inputs.baseSha,
cwd: workingDirectory,
token: inputs.token
})
const currentSha = await getCurrentSHA({env, inputs, workingDirectory})
let previousSha = inputs.baseSha
let diff = '...'
if (inputs.baseSha && inputs.sha && currentBranch && targetBranch) {
if (previousSha && currentSha && currentBranch && targetBranch) {
if (previousSha === currentSha) {
core.error(
`Similar commit hashes detected: previous sha: ${previousSha} is equivalent to the current sha: ${currentSha}.`
@ -439,6 +349,7 @@ export const getSHAForPullRequestEvent = async ({
throw new Error('Similar commit hashes detected.')
}
await verifyCommitSha({sha: previousSha, cwd: workingDirectory})
core.debug(`Previous SHA: ${previousSha}`)
return {
@ -450,71 +361,45 @@ export const getSHAForPullRequestEvent = async ({
}
}
if (!github.context.payload.pull_request?.base?.ref) {
if (
!env.GITHUB_EVENT_PULL_REQUEST_BASE_REF ||
env.GITHUB_EVENT_HEAD_REPO_FORK === 'true'
) {
diff = '..'
}
if (!previousSha || previousSha === currentSha) {
if (!previousSha) {
if (inputs.sinceLastRemoteCommit) {
previousSha = github.context.payload.before
previousSha = env.GITHUB_EVENT_BEFORE
if (
!previousSha ||
(previousSha &&
(await verifyCommitSha({
sha: previousSha,
cwd: workingDirectory,
showAsErrorMessage: false
})) !== 0)
(await verifyCommitSha({sha: previousSha, cwd: workingDirectory})) !==
0)
) {
core.info(
`Unable to locate the previous commit in the local history for ${github.context.eventName} (${github.context.payload.action}) event. Falling back to the previous commit in the local history.`
core.warning(
'Unable to locate the remote branch head sha. Falling back to the previous commit in the local history.'
)
previousSha = await getParentSha({
cwd: workingDirectory
})
if (
github.context.payload.action &&
github.context.payload.action === 'synchronize' &&
previousSha &&
(!previousSha ||
(previousSha &&
(await verifyCommitSha({
sha: previousSha,
cwd: workingDirectory,
showAsErrorMessage: false
})) !== 0))
) {
throw new Error(
'Unable to locate the previous commit in the local history. Please ensure to checkout pull request HEAD commit instead of the merge commit. See: https://github.com/actions/checkout/blob/main/README.md#checkout-pull-request-head-commit-instead-of-merge-commit'
)
}
if (
!previousSha ||
(previousSha &&
(await verifyCommitSha({
sha: previousSha,
cwd: workingDirectory,
showAsErrorMessage: false
})) !== 0)
) {
throw new Error(
'Unable to locate the previous commit in the local history. Please ensure to checkout pull request HEAD commit instead of the merge commit. See: https://github.com/actions/checkout/blob/main/README.md#checkout-pull-request-head-commit-instead-of-merge-commit'
if (!previousSha) {
core.warning(
'Unable to locate the previous commit in the local history. Falling back to the pull request base sha.'
)
previousSha = env.GITHUB_EVENT_PULL_REQUEST_BASE_SHA
}
}
} else {
previousSha = github.context.payload.pull_request?.base?.sha
previousSha = await getRemoteBranchHeadSha({
cwd: workingDirectory,
branch: targetBranch
})
if (!previousSha) {
previousSha = await getRemoteBranchHeadSha({
cwd: workingDirectory,
remoteName,
branch: targetBranch
})
previousSha = env.GITHUB_EVENT_PULL_REQUEST_BASE_SHA
}
if (isShallow) {
@ -530,11 +415,7 @@ export const getSHAForPullRequestEvent = async ({
'Merge base is not in the local history, fetching remote target branch...'
)
for (
let i = 1;
i <= (inputs.fetchMissingHistoryMaxRetries || 10);
i++
) {
for (let i = 1; i <= 10; i++) {
await gitFetch({
cwd: workingDirectory,
args: [
@ -542,8 +423,8 @@ export const getSHAForPullRequestEvent = async ({
'-u',
'--progress',
`--deepen=${inputs.fetchDepth}`,
remoteName,
`+refs/heads/${targetBranch}:refs/remotes/${remoteName}/${targetBranch}`
'origin',
`+refs/heads/${targetBranch}:refs/remotes/origin/${targetBranch}`
]
})
@ -568,7 +449,7 @@ export const getSHAForPullRequestEvent = async ({
}
if (!previousSha || previousSha === currentSha) {
previousSha = github.context.payload.pull_request?.base?.sha
previousSha = env.GITHUB_EVENT_PULL_REQUEST_BASE_SHA
}
}
@ -594,12 +475,6 @@ export const getSHAForPullRequestEvent = async ({
diff
}))
) {
core.warning(
'If this pull request is from a forked repository, please set the checkout action `repository` input to the same repository as the pull request.'
)
core.warning(
'This can be done by setting actions/checkout `repository` to ${{ github.event.pull_request.head.repo.full_name }}'
)
throw new Error(
`Unable to determine a difference between ${previousSha}${diff}${currentSha}`
)
@ -619,7 +494,7 @@ export const getSHAForPullRequestEvent = async ({
// uses: actions/checkout@v3
// with:
// repository: ${{ github.event.pull_request.head.repo.full_name }}
if (github.context.eventName === 'pull_request_target') {
if (env.GITHUB_EVENT_NAME === 'pull_request_target') {
core.warning(
'If this pull request is from a forked repository, please set the checkout action `repository` input to the same repository as the pull request.'
)

View File

@ -1,29 +0,0 @@
import {Inputs} from './inputs'
export const DEFAULT_VALUES_OF_UNSUPPORTED_API_INPUTS: Partial<Inputs> = {
sha: '',
baseSha: '',
since: '',
until: '',
path: '.',
quotepath: true,
diffRelative: true,
sinceLastRemoteCommit: false,
recoverDeletedFiles: false,
recoverDeletedFilesToDestination: '',
recoverFiles: '',
recoverFilesSeparator: '\n',
recoverFilesIgnore: '',
recoverFilesIgnoreSeparator: '\n',
includeAllOldNewRenamedFiles: false,
oldNewSeparator: ',',
oldNewFilesSeparator: ' ',
skipInitialFetch: false,
fetchAdditionalSubmoduleHistory: false,
dirNamesDeletedFilesIncludeOnlyDeletedDirs: false,
excludeSubmodules: false,
fetchMissingHistoryMaxRetries: 20,
usePosixPathSeparator: false,
tagsPattern: '*',
tagsIgnorePattern: ''
}

View File

@ -1,13 +1,80 @@
import {promises as fs} from 'fs'
import * as core from '@actions/core'
export type Env = {
GITHUB_REF_NAME: string
GITHUB_REF: string
GITHUB_WORKSPACE: string
GITHUB_EVENT_ACTION: string
GITHUB_EVENT_NAME: string
GITHUB_EVENT_FORCED: string
GITHUB_EVENT_BEFORE: string
GITHUB_EVENT_BASE_REF: string
GITHUB_EVENT_RELEASE_TARGET_COMMITISH: string
GITHUB_EVENT_HEAD_REPO_FORK: string
GITHUB_EVENT_PULL_REQUEST_NUMBER: string
GITHUB_EVENT_PULL_REQUEST_BASE_SHA: string
GITHUB_EVENT_PULL_REQUEST_HEAD_SHA: string
GITHUB_EVENT_PULL_REQUEST_HEAD_REF: string
GITHUB_EVENT_PULL_REQUEST_BASE_REF: string
GITHUB_REPOSITORY_OWNER: string
GITHUB_REPOSITORY: string
}
type GithubEvent = {
action?: string
forced?: string
pull_request?: {
head: {
ref: string
sha: string
}
base: {
ref: string
sha: string
}
number: string
}
release?: {
target_commitish: string
}
before?: string
base_ref?: string
head?: {
repo?: {
fork: string
}
}
}
export const getEnv = async (): Promise<Env> => {
const eventPath = process.env.GITHUB_EVENT_PATH
let eventJson: GithubEvent = {}
if (eventPath) {
eventJson = JSON.parse(await fs.readFile(eventPath, {encoding: 'utf8'}))
}
core.debug(`Env: ${JSON.stringify(process.env, null, 2)}`)
core.debug(`Event: ${JSON.stringify(eventJson, null, 2)}`)
return {
GITHUB_EVENT_PULL_REQUEST_HEAD_REF: eventJson.pull_request?.head?.ref || '',
GITHUB_EVENT_PULL_REQUEST_BASE_REF: eventJson.pull_request?.base?.ref || '',
GITHUB_EVENT_BEFORE: eventJson.before || '',
GITHUB_EVENT_BASE_REF: eventJson.base_ref || '',
GITHUB_EVENT_RELEASE_TARGET_COMMITISH:
eventJson.release?.target_commitish || '',
GITHUB_EVENT_HEAD_REPO_FORK: eventJson.head?.repo?.fork || '',
GITHUB_EVENT_PULL_REQUEST_NUMBER: eventJson.pull_request?.number || '',
GITHUB_EVENT_PULL_REQUEST_BASE_SHA: eventJson.pull_request?.base?.sha || '',
GITHUB_EVENT_PULL_REQUEST_HEAD_SHA: eventJson.pull_request?.head?.sha || '',
GITHUB_EVENT_FORCED: eventJson.forced || '',
GITHUB_EVENT_ACTION: eventJson.action || '',
GITHUB_REF_NAME: process.env.GITHUB_REF_NAME || '',
GITHUB_REF: process.env.GITHUB_REF || '',
GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE || ''
GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE || '',
GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME || '',
GITHUB_REPOSITORY_OWNER: process.env.GITHUB_REPOSITORY_OWNER || '',
GITHUB_REPOSITORY: process.env.GITHUB_REPOSITORY || ''
}
}

View File

@ -24,41 +24,23 @@ export type Inputs = {
since: string
until: string
path: string
quotepath: boolean
quotePath: boolean
diffRelative: boolean
dirNames: boolean
dirNamesMaxDepth?: number
dirNamesExcludeCurrentDir: boolean
dirNamesIncludeFiles: string
dirNamesIncludeFilesSeparator: string
dirNamesDeletedFilesIncludeOnlyDeletedDirs: boolean
json: boolean
escapeJson: boolean
safeOutput: boolean
fetchDepth?: number
fetchAdditionalSubmoduleHistory: boolean
sinceLastRemoteCommit: boolean
writeOutputFiles: boolean
outputDir: string
outputRenamedFilesAsDeletedAndAdded: boolean
recoverDeletedFiles: boolean
recoverDeletedFilesToDestination: string
recoverFiles: string
recoverFilesSeparator: string
recoverFilesIgnore: string
recoverFilesIgnoreSeparator: string
token: string
apiUrl: string
skipInitialFetch: boolean
failOnInitialDiffError: boolean
failOnSubmoduleDiffError: boolean
negationPatternsFirst: boolean
useRestApi: boolean
excludeSubmodules: boolean
fetchMissingHistoryMaxRetries?: number
usePosixPathSeparator: boolean
tagsPattern: string
tagsIgnorePattern?: string
}
export const getInputs = (): Inputs => {
@ -137,7 +119,7 @@ export const getInputs = (): Inputs => {
const since = core.getInput('since', {required: false})
const until = core.getInput('until', {required: false})
const path = core.getInput('path', {required: false})
const quotepath = core.getBooleanInput('quotepath', {required: false})
const quotePath = core.getBooleanInput('quotepath', {required: false})
const diffRelative = core.getBooleanInput('diff_relative', {required: false})
const dirNames = core.getBooleanInput('dir_names', {required: false})
const dirNamesMaxDepth = core.getInput('dir_names_max_depth', {
@ -149,26 +131,8 @@ export const getInputs = (): Inputs => {
required: false
}
)
const dirNamesIncludeFiles = core.getInput('dir_names_include_files', {
required: false
})
const dirNamesIncludeFilesSeparator = core.getInput(
'dir_names_include_files_separator',
{
required: false,
trimWhitespace: false
}
)
let json = core.getBooleanInput('json', {required: false})
let escapeJson = core.getBooleanInput('escape_json', {required: false})
const matrix = core.getBooleanInput('matrix', {required: false})
if (matrix) {
json = true
escapeJson = false
}
const safeOutput = core.getBooleanInput('safe_output', {required: false})
const json = core.getBooleanInput('json', {required: false})
const escapeJson = core.getBooleanInput('escape_json', {required: false})
const fetchDepth = core.getInput('fetch_depth', {required: false})
const sinceLastRemoteCommit = core.getBooleanInput(
'since_last_remote_commit',
@ -189,86 +153,11 @@ export const getInputs = (): Inputs => {
'recover_deleted_files_to_destination',
{required: false}
)
const recoverFiles = core.getInput('recover_files', {required: false})
const recoverFilesSeparator = core.getInput('recover_files_separator', {
required: false,
trimWhitespace: false
})
const recoverFilesIgnore = core.getInput('recover_files_ignore', {
required: false
})
const recoverFilesIgnoreSeparator = core.getInput(
'recover_files_ignore_separator',
{
required: false,
trimWhitespace: false
}
)
const token = core.getInput('token', {required: false})
const apiUrl = core.getInput('api_url', {required: false})
const skipInitialFetch = core.getBooleanInput('skip_initial_fetch', {
required: false
})
const fetchAdditionalSubmoduleHistory = core.getBooleanInput(
'fetch_additional_submodule_history',
{
required: false
}
)
const failOnInitialDiffError = core.getBooleanInput(
'fail_on_initial_diff_error',
{
required: false
}
)
const failOnSubmoduleDiffError = core.getBooleanInput(
'fail_on_submodule_diff_error',
{
required: false
}
)
const dirNamesDeletedFilesIncludeOnlyDeletedDirs = core.getBooleanInput(
'dir_names_deleted_files_include_only_deleted_dirs',
{
required: false
}
)
const negationPatternsFirst = core.getBooleanInput(
'negation_patterns_first',
{
required: false
}
)
const useRestApi = core.getBooleanInput('use_rest_api', {
required: false
})
const excludeSubmodules = core.getBooleanInput('exclude_submodules', {
required: false
})
const fetchMissingHistoryMaxRetries = core.getInput(
'fetch_missing_history_max_retries',
{required: false}
)
const usePosixPathSeparator = core.getBooleanInput(
'use_posix_path_separator',
{
required: false
}
)
const tagsPattern = core.getInput('tags_pattern', {
required: false,
trimWhitespace: false
})
const tagsIgnorePattern = core.getInput('tags_ignore_pattern', {
required: false,
trimWhitespace: false
})
const inputs: Inputs = {
files,
@ -285,8 +174,6 @@ export const getInputs = (): Inputs => {
filesIgnoreYaml,
filesIgnoreYamlFromSourceFile,
filesIgnoreYamlFromSourceFileSeparator,
failOnInitialDiffError,
failOnSubmoduleDiffError,
separator,
// Not Supported via REST API
sha,
@ -294,44 +181,28 @@ export const getInputs = (): Inputs => {
since,
until,
path,
quotepath,
quotePath,
diffRelative,
sinceLastRemoteCommit,
recoverDeletedFiles,
recoverDeletedFilesToDestination,
recoverFiles,
recoverFilesSeparator,
recoverFilesIgnore,
recoverFilesIgnoreSeparator,
includeAllOldNewRenamedFiles,
oldNewSeparator,
oldNewFilesSeparator,
skipInitialFetch,
fetchAdditionalSubmoduleHistory,
dirNamesDeletedFilesIncludeOnlyDeletedDirs,
excludeSubmodules,
usePosixPathSeparator,
tagsPattern,
tagsIgnorePattern,
// End Not Supported via REST API
dirNames,
dirNamesExcludeCurrentDir,
dirNamesIncludeFiles,
dirNamesIncludeFilesSeparator,
json,
escapeJson,
safeOutput,
writeOutputFiles,
outputDir,
outputRenamedFilesAsDeletedAndAdded,
token,
apiUrl,
negationPatternsFirst,
useRestApi
skipInitialFetch
}
if (fetchDepth) {
// Fallback to at least 2 if the fetch_depth is less than 2
inputs.fetchDepth = Math.max(parseInt(fetchDepth, 10), 2)
}
@ -339,13 +210,5 @@ export const getInputs = (): Inputs => {
inputs.dirNamesMaxDepth = parseInt(dirNamesMaxDepth, 10)
}
if (fetchMissingHistoryMaxRetries) {
// Fallback to at least 1 if the fetch_missing_history_max_retries is less than 1
inputs.fetchMissingHistoryMaxRetries = Math.max(
parseInt(fetchMissingHistoryMaxRetries, 10),
1
)
}
return inputs
}

View File

@ -1,36 +1,31 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import path from 'path'
import {
processChangedFiles,
ChangeTypeEnum,
getAllDiffFiles,
getChangedFilesFromGithubAPI,
getRenamedFiles
} from './changedFiles'
import {setChangedFilesOutput} from './changedFilesOutput'
import {
DiffResult,
getSHAForNonPullRequestEvent,
getSHAForPullRequestEvent
getSHAForPullRequestEvent,
getSHAForPushEvent
} from './commitSha'
import {Env, getEnv} from './env'
import {getInputs, Inputs} from './inputs'
import {
getFilePatterns,
getRecoverFilePatterns,
getSubmodulePath,
getYamlFilePatterns,
hasLocalGitDirectory,
isRepoShallow,
recoverDeletedFiles,
setOutput,
submoduleExists,
updateGitGlobalConfig,
verifyMinimumGitVersion,
warnUnsupportedRESTAPIInputs
verifyMinimumGitVersion
} from './utils'
const getChangedFilesFromLocalGitHistory = async ({
const getChangedFilesFromLocalGit = async ({
inputs,
env,
workingDirectory,
@ -45,15 +40,15 @@ const getChangedFilesFromLocalGitHistory = async ({
}): Promise<void> => {
await verifyMinimumGitVersion()
let quotepathValue = 'on'
let quotePathValue = 'on'
if (!inputs.quotepath) {
quotepathValue = 'off'
if (!inputs.quotePath) {
quotePathValue = 'off'
}
await updateGitGlobalConfig({
name: 'core.quotepath',
value: quotepathValue
value: quotePathValue
})
if (inputs.diffRelative) {
@ -64,26 +59,14 @@ const getChangedFilesFromLocalGitHistory = async ({
}
const isShallow = await isRepoShallow({cwd: workingDirectory})
let diffSubmodule = false
let gitFetchExtraArgs = ['--no-tags', '--prune']
if (inputs.excludeSubmodules) {
core.info('Excluding submodules from the diff')
} else {
diffSubmodule = await submoduleExists({cwd: workingDirectory})
}
if (diffSubmodule) {
gitFetchExtraArgs.push('--recurse-submodules')
}
const hasSubmodule = await submoduleExists({cwd: workingDirectory})
let gitFetchExtraArgs = ['--no-tags', '--prune', '--recurse-submodules']
const isTag = env.GITHUB_REF?.startsWith('refs/tags/')
const remoteName = 'origin'
const outputRenamedFilesAsDeletedAndAdded =
inputs.outputRenamedFilesAsDeletedAndAdded
let submodulePaths: string[] = []
if (diffSubmodule) {
if (hasSubmodule) {
submodulePaths = await getSubmodulePath({cwd: workingDirectory})
}
@ -93,32 +76,31 @@ const getChangedFilesFromLocalGitHistory = async ({
let diffResult: DiffResult
if (!github.context.payload.pull_request?.base?.ref) {
core.info(`Running on a ${github.context.eventName || 'push'} event...`)
diffResult = await getSHAForNonPullRequestEvent({
if (!env.GITHUB_EVENT_PULL_REQUEST_BASE_REF) {
core.info(`Running on a ${env.GITHUB_EVENT_NAME || 'push'} event...`)
diffResult = await getSHAForPushEvent(
inputs,
env,
workingDirectory,
isShallow,
diffSubmodule,
hasSubmodule,
gitFetchExtraArgs,
isTag,
remoteName
})
isTag
)
} else {
core.info(
`Running on a ${github.context.eventName || 'pull_request'} (${
github.context.payload.action
`Running on a ${env.GITHUB_EVENT_NAME || 'pull_request'} (${
env.GITHUB_EVENT_ACTION
}) event...`
)
diffResult = await getSHAForPullRequestEvent({
diffResult = await getSHAForPullRequestEvent(
inputs,
env,
workingDirectory,
isShallow,
diffSubmodule,
gitFetchExtraArgs,
remoteName
})
hasSubmodule,
gitFetchExtraArgs
)
}
if (diffResult.initialCommit) {
@ -133,51 +115,62 @@ const getChangedFilesFromLocalGitHistory = async ({
const allDiffFiles = await getAllDiffFiles({
workingDirectory,
diffSubmodule,
hasSubmodule,
diffResult,
submodulePaths,
outputRenamedFilesAsDeletedAndAdded,
fetchAdditionalSubmoduleHistory: inputs.fetchAdditionalSubmoduleHistory,
failOnInitialDiffError: inputs.failOnInitialDiffError,
failOnSubmoduleDiffError: inputs.failOnSubmoduleDiffError
outputRenamedFilesAsDeletedAndAdded
})
core.debug(`All diff files: ${JSON.stringify(allDiffFiles)}`)
core.info('All Done!')
core.endGroup()
if (inputs.recoverDeletedFiles) {
let recoverPatterns = getRecoverFilePatterns({inputs})
if (recoverPatterns.length > 0 && filePatterns.length > 0) {
core.info('No recover patterns found; defaulting to file patterns')
recoverPatterns = filePatterns
}
await recoverDeletedFiles({
if (filePatterns.length > 0) {
core.startGroup('changed-files-patterns')
await setChangedFilesOutput({
allDiffFiles,
filePatterns,
inputs,
workingDirectory,
deletedFiles: allDiffFiles[ChangeTypeEnum.Deleted],
recoverPatterns,
diffResult,
diffSubmodule,
submodulePaths
diffResult
})
core.info('All Done!')
core.endGroup()
}
await processChangedFiles({
filePatterns,
allDiffFiles,
inputs,
yamlFilePatterns,
workingDirectory
})
if (Object.keys(yamlFilePatterns).length > 0) {
for (const key of Object.keys(yamlFilePatterns)) {
core.startGroup(`changed-files-yaml-${key}`)
await setChangedFilesOutput({
allDiffFiles,
filePatterns: yamlFilePatterns[key],
outputPrefix: key,
inputs,
workingDirectory,
diffResult
})
core.info('All Done!')
core.endGroup()
}
}
if (filePatterns.length === 0 && Object.keys(yamlFilePatterns).length === 0) {
core.startGroup('changed-files-all')
await setChangedFilesOutput({
allDiffFiles,
inputs,
workingDirectory,
diffResult
})
core.info('All Done!')
core.endGroup()
}
if (inputs.includeAllOldNewRenamedFiles) {
core.startGroup('changed-files-all-old-new-renamed-files')
const allOldNewRenamedFiles = await getRenamedFiles({
inputs,
workingDirectory,
diffSubmodule,
hasSubmodule,
diffResult,
submodulePaths
})
@ -185,17 +178,12 @@ const getChangedFilesFromLocalGitHistory = async ({
await setOutput({
key: 'all_old_new_renamed_files',
value: allOldNewRenamedFiles.paths,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json,
safeOutput: inputs.safeOutput
inputs
})
await setOutput({
key: 'all_old_new_renamed_files_count',
value: allOldNewRenamedFiles.count,
writeOutputFiles: inputs.writeOutputFiles,
outputDir: inputs.outputDir,
json: inputs.json
inputs
})
core.info('All Done!')
core.endGroup()
@ -204,25 +192,61 @@ const getChangedFilesFromLocalGitHistory = async ({
const getChangedFilesFromRESTAPI = async ({
inputs,
env,
workingDirectory,
filePatterns,
yamlFilePatterns
}: {
inputs: Inputs
env: Env
workingDirectory: string
filePatterns: string[]
yamlFilePatterns: Record<string, string[]>
}): Promise<void> => {
const allDiffFiles = await getChangedFilesFromGithubAPI({
inputs
inputs,
env
})
core.debug(`All diff files: ${JSON.stringify(allDiffFiles)}`)
core.info('All Done!')
await processChangedFiles({
filePatterns,
allDiffFiles,
inputs,
yamlFilePatterns
})
if (filePatterns.length > 0) {
core.startGroup('changed-files-patterns')
await setChangedFilesOutput({
allDiffFiles,
filePatterns,
inputs,
workingDirectory
})
core.info('All Done!')
core.endGroup()
}
if (Object.keys(yamlFilePatterns).length > 0) {
for (const key of Object.keys(yamlFilePatterns)) {
core.startGroup(`changed-files-yaml-${key}`)
await setChangedFilesOutput({
allDiffFiles,
filePatterns: yamlFilePatterns[key],
outputPrefix: key,
inputs,
workingDirectory
})
core.info('All Done!')
core.endGroup()
}
}
if (filePatterns.length === 0 && Object.keys(yamlFilePatterns).length === 0) {
core.startGroup('changed-files-all')
await setChangedFilesOutput({
allDiffFiles,
inputs,
workingDirectory
})
core.info('All Done!')
core.endGroup()
}
}
export async function run(): Promise<void> {
@ -236,7 +260,7 @@ export async function run(): Promise<void> {
const workingDirectory = path.resolve(
env.GITHUB_WORKSPACE || process.cwd(),
inputs.useRestApi ? '.' : inputs.path
inputs.path
)
core.debug(`Working directory: ${workingDirectory}`)
@ -255,33 +279,47 @@ export async function run(): Promise<void> {
})
core.debug(`Yaml file patterns: ${JSON.stringify(yamlFilePatterns)}`)
if (inputs.useRestApi && !github.context.payload.pull_request?.number) {
throw new Error(
"Only pull_request* events are supported when using GitHub's REST API."
)
}
if (
inputs.token &&
github.context.payload.pull_request?.number &&
(!hasGitDirectory || inputs.useRestApi)
env.GITHUB_EVENT_PULL_REQUEST_NUMBER &&
!hasGitDirectory
) {
core.info("Using GitHub's REST API to get changed files")
await warnUnsupportedRESTAPIInputs({inputs})
const unsupportedInputs: (keyof Inputs)[] = [
'sha',
'baseSha',
'since',
'until',
'sinceLastRemoteCommit',
'recoverDeletedFiles',
'recoverDeletedFilesToDestination',
'includeAllOldNewRenamedFiles'
]
for (const input of unsupportedInputs) {
if (inputs[input]) {
core.warning(
`Input "${input}" is not supported when using GitHub's REST API to get changed files`
)
}
}
await getChangedFilesFromRESTAPI({
inputs,
env,
workingDirectory,
filePatterns,
yamlFilePatterns
})
} else {
if (!hasGitDirectory) {
throw new Error(
`Unable to locate the git repository in the given path: ${workingDirectory}.\n Please run actions/checkout before this action (Make sure the 'path' input is correct).\n If you intend to use Github's REST API note that only pull_request* events are supported. Current event is "${github.context.eventName}".`
core.setFailed(
"Can't find local .git directory. Please run actions/checkout before this action"
)
return
}
core.info('Using local .git directory')
await getChangedFilesFromLocalGitHistory({
await getChangedFilesFromLocalGit({
inputs,
env,
workingDirectory,
@ -291,8 +329,10 @@ export async function run(): Promise<void> {
}
}
// eslint-disable-next-line github/no-then
run().catch(e => {
core.setFailed(e.message || e)
process.exit(1)
})
/* istanbul ignore if */
if (!process.env.TESTING) {
// eslint-disable-next-line github/no-then
run().catch(e => {
core.setFailed(e.message || e)
})
}

File diff suppressed because it is too large Load Diff

@ -1 +1 @@
Subproject commit 5dfac2e9a7dc53ca33bc4682355193672c97437c
Subproject commit e168fac86c58062a1054c3a5540cd81577c4e2ba

View File

@ -1 +0,0 @@
This is a test markdown file

View File

@ -1 +1 @@
This is a test file...
This is a test file.

View File

@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */

4167
yarn.lock

File diff suppressed because it is too large Load Diff