mirror of
https://github.com/tj-actions/changed-files
synced 2024-12-17 03:47:20 +00:00
feat: add support for complex filters (#1265)
Co-authored-by: GitHub Action <action@github.com> Co-authored-by: tj-actions[bot] <109116665+tj-actions-bot@users.noreply.github.com>
This commit is contained in:
parent
ea90b5ced9
commit
c25c77a67a
15 changed files with 737 additions and 307 deletions
70
.github/workflows/test.yml
vendored
70
.github/workflows/test.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: shellcheck
|
- name: shellcheck
|
||||||
uses: reviewdog/action-shellcheck@v1.17
|
uses: reviewdog/action-shellcheck@v1.17
|
||||||
|
@ -188,7 +188,7 @@ jobs:
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
@ -245,7 +245,7 @@ jobs:
|
||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
@ -282,7 +282,7 @@ jobs:
|
||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
@ -318,7 +318,7 @@ jobs:
|
||||||
input-fetch_depth: [1, 50]
|
input-fetch_depth: [1, 50]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
@ -348,7 +348,7 @@ jobs:
|
||||||
if: github.event_name != 'push'
|
if: github.event_name != 'push'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
@ -380,7 +380,7 @@ jobs:
|
||||||
fetch-depth: [1, 2, 0]
|
fetch-depth: [1, 2, 0]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: ${{ matrix.fetch-depth }}
|
fetch-depth: ${{ matrix.fetch-depth }}
|
||||||
|
@ -408,7 +408,7 @@ jobs:
|
||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Download build assets
|
- name: Download build assets
|
||||||
|
@ -461,7 +461,7 @@ jobs:
|
||||||
needs: build
|
needs: build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Download build assets
|
- name: Download build assets
|
||||||
|
@ -519,7 +519,7 @@ jobs:
|
||||||
fetch-depth: [0, 1, 2]
|
fetch-depth: [0, 1, 2]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout to branch
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
@ -550,6 +550,56 @@ jobs:
|
||||||
echo "${{ toJSON(steps.changed-files.outputs) }}"
|
echo "${{ toJSON(steps.changed-files.outputs) }}"
|
||||||
shell:
|
shell:
|
||||||
bash
|
bash
|
||||||
|
test-yaml:
|
||||||
|
name: Test changed-files with yaml
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 4
|
||||||
|
matrix:
|
||||||
|
fetch-depth: [0, 1, 2]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout branch
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: ${{ matrix.fetch-depth }}
|
||||||
|
|
||||||
|
- name: Download build assets
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build-assets
|
||||||
|
|
||||||
|
- name: Run changed-files with files_yaml
|
||||||
|
id: changed-files
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
files_yaml: |
|
||||||
|
test:
|
||||||
|
- test/**.txt
|
||||||
|
- test/**.md
|
||||||
|
|
||||||
|
- name: Show output
|
||||||
|
run: |
|
||||||
|
echo "${{ toJSON(steps.changed-files.outputs) }}"
|
||||||
|
shell:
|
||||||
|
bash
|
||||||
|
|
||||||
|
- name: Run changed-files with files_yaml_from_source_file
|
||||||
|
id: changed-files-from-source-file
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
files_yaml_from_source_file: |
|
||||||
|
test/changed-files.yml
|
||||||
|
|
||||||
|
- name: Show output
|
||||||
|
run: |
|
||||||
|
echo "${{ toJSON(steps.changed-files-from-source-file.outputs) }}"
|
||||||
|
shell:
|
||||||
|
bash
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test changed-files
|
name: Test changed-files
|
||||||
|
|
33
README.md
33
README.md
|
@ -62,6 +62,10 @@ Retrieve all changed files and directories relative to a target branch, precedin
|
||||||
* Using [Glob pattern](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet) matching.
|
* Using [Glob pattern](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet) matching.
|
||||||
* Globstar.
|
* Globstar.
|
||||||
* Brace expansion.
|
* Brace expansion.
|
||||||
|
* Negation.
|
||||||
|
* Using [YAML](https://yaml.org/) syntax for specifying the patterns for files and directories.
|
||||||
|
* Supports [YAML anchors & aliases](https://www.educative.io/blog/advanced-yaml-syntax-cheatsheet#anchors).
|
||||||
|
* Supports [YAML multi-line strings](https://learnxinyminutes.com/docs/yaml/).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -141,6 +145,35 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
echo "One or more .js file(s) or any file in the static folder but not in the doc folder has changed."
|
echo "One or more .js file(s) or any file in the static folder but not in the doc folder has changed."
|
||||||
echo "List all the files that have changed: ${{ steps.changed-files-excluded.outputs.all_changed_files }}"
|
echo "List all the files that have changed: ${{ steps.changed-files-excluded.outputs.all_changed_files }}"
|
||||||
|
|
||||||
|
# Example 4
|
||||||
|
- name: Get all test files, doc and src files that have changed
|
||||||
|
id: changed-files-yml
|
||||||
|
uses: tj-actions/changed-files@v36
|
||||||
|
with:
|
||||||
|
files_yaml: |
|
||||||
|
doc:
|
||||||
|
- *.md
|
||||||
|
- docs/**
|
||||||
|
- !docs/README.md
|
||||||
|
test:
|
||||||
|
- test/**
|
||||||
|
- !test/README.md
|
||||||
|
src:
|
||||||
|
- src/**
|
||||||
|
# Optionally set `files_yaml_from_source_file` to read the YAML from a file. e.g `files_yaml_from_source_file: .github/changed-files.yml`
|
||||||
|
|
||||||
|
- name: Run step if test file(s) change
|
||||||
|
if: steps.changed-files-yml.outputs.test_any_changed == 'true'
|
||||||
|
run: |
|
||||||
|
echo "One or more test file(s) has changed."
|
||||||
|
echo "List all the files that have changed: ${{ steps.changed-files-yml.outputs.test_all_changed_files }}"
|
||||||
|
|
||||||
|
- name: Run step if doc file(s) change
|
||||||
|
if: steps.changed-files-yml.outputs.doc_any_changed == 'true'
|
||||||
|
run: |
|
||||||
|
echo "One or more doc file(s) has changed."
|
||||||
|
echo "List all the files that have changed: ${{ steps.changed-files-yml.outputs.doc_all_changed_files }}"
|
||||||
```
|
```
|
||||||
|
|
||||||
To access more examples, navigate to the [Examples](#examples) section.
|
To access more examples, navigate to the [Examples](#examples) section.
|
||||||
|
|
32
action.yml
32
action.yml
|
@ -28,13 +28,37 @@ inputs:
|
||||||
default: "\n"
|
default: "\n"
|
||||||
required: false
|
required: false
|
||||||
files:
|
files:
|
||||||
description: "File and directory patterns to detect changes using only these list of file(s) (Defaults to the entire repo) **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
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
files_separator:
|
files_separator:
|
||||||
description: "Separator used to split the `files` input"
|
description: "Separator used to split the `files` input"
|
||||||
default: "\n"
|
default: "\n"
|
||||||
required: false
|
required: false
|
||||||
|
files_yaml:
|
||||||
|
description: "YAML used to define a set of file patterns to detect changes"
|
||||||
|
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)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
files_yaml_from_source_file_separator:
|
||||||
|
description: 'Separator used to split the `files_yaml_from_source_file` input'
|
||||||
|
default: "\n"
|
||||||
|
required: false
|
||||||
|
files_ignore_yaml:
|
||||||
|
description: "YAML used to define a set of file patterns to ignore changes"
|
||||||
|
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)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
files_ignore_yaml_from_source_file_separator:
|
||||||
|
description: 'Separator used to split the `files_ignore_yaml_from_source_file` input'
|
||||||
|
default: "\n"
|
||||||
|
required: false
|
||||||
files_ignore:
|
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
|
required: false
|
||||||
|
@ -70,7 +94,7 @@ inputs:
|
||||||
required: false
|
required: false
|
||||||
default: "."
|
default: "."
|
||||||
quotepath:
|
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"
|
default: "true"
|
||||||
required: false
|
required: false
|
||||||
diff_relative:
|
diff_relative:
|
||||||
|
@ -106,7 +130,7 @@ inputs:
|
||||||
required: false
|
required: false
|
||||||
default: "50"
|
default: "50"
|
||||||
since_last_remote_commit:
|
since_last_remote_commit:
|
||||||
description: "Use the last commit on the remote branch as the `base_sha`. Defaults to the last non merge commit on the target branch for pull request events and the previous remote commit of the current branch for push events."
|
description: "Use the last commit on the remote branch as the `base_sha`. Defaults to the last non-merge commit on the target branch for pull request events and the previous remote commit of the current branch for push events."
|
||||||
required: false
|
required: false
|
||||||
default: "false"
|
default: "false"
|
||||||
write_output_files:
|
write_output_files:
|
||||||
|
@ -134,7 +158,7 @@ outputs:
|
||||||
renamed_files:
|
renamed_files:
|
||||||
description: "Returns only files that are Renamed (R)."
|
description: "Returns only files that are Renamed (R)."
|
||||||
all_old_new_renamed_files:
|
all_old_new_renamed_files:
|
||||||
description: "Returns only files that are Renamed and list their old and new names. **NOTE:** This requires setting `include_all_old_new_renamed_files` to `true` (R)"
|
description: "Returns only files that are Renamed and lists their old and new names. **NOTE:** This requires setting `include_all_old_new_renamed_files` to `true`. Also, keep in mind that this output is global and wouldn't be nested in outputs generated when the `*_yaml_*` input is used. (R)"
|
||||||
type_changed_files:
|
type_changed_files:
|
||||||
description: "Returns only files that have their file type changed (T)."
|
description: "Returns only files that have their file type changed (T)."
|
||||||
unmerged_files:
|
unmerged_files:
|
||||||
|
|
BIN
dist/index.js
generated
vendored
BIN
dist/index.js
generated
vendored
Binary file not shown.
BIN
dist/index.js.map
generated
vendored
BIN
dist/index.js.map
generated
vendored
Binary file not shown.
BIN
dist/licenses.txt
generated
vendored
BIN
dist/licenses.txt
generated
vendored
Binary file not shown.
|
@ -35,7 +35,8 @@
|
||||||
"@actions/core": "1.10.0",
|
"@actions/core": "1.10.0",
|
||||||
"@actions/exec": "1.1.1",
|
"@actions/exec": "1.1.1",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"micromatch": "^4.0.5"
|
"micromatch": "^4.0.5",
|
||||||
|
"yaml": "^2.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "29.5.2",
|
"@types/jest": "29.5.2",
|
||||||
|
|
292
src/changedFilesOutput.ts
Normal file
292
src/changedFilesOutput.ts
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import {
|
||||||
|
ChangedFiles,
|
||||||
|
ChangeTypeEnum,
|
||||||
|
getAllChangeTypeFiles,
|
||||||
|
getChangeTypeFiles
|
||||||
|
} from './changedFiles'
|
||||||
|
import {Inputs} from './inputs'
|
||||||
|
import {getFilteredChangedFiles, setOutput} from './utils'
|
||||||
|
|
||||||
|
const getOutputKey = (key: string, outputPrefix: string): string => {
|
||||||
|
return outputPrefix ? `${outputPrefix}_${key}` : key
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setChangedFilesOutput = async ({
|
||||||
|
allDiffFiles,
|
||||||
|
inputs,
|
||||||
|
filePatterns = [],
|
||||||
|
outputPrefix = ''
|
||||||
|
}: {
|
||||||
|
allDiffFiles: ChangedFiles
|
||||||
|
filePatterns?: string[]
|
||||||
|
inputs: Inputs
|
||||||
|
outputPrefix?: string
|
||||||
|
}): Promise<void> => {
|
||||||
|
const allFilteredDiffFiles = await getFilteredChangedFiles({
|
||||||
|
allDiffFiles,
|
||||||
|
filePatterns
|
||||||
|
})
|
||||||
|
core.debug(`All filtered diff files: ${JSON.stringify(allFilteredDiffFiles)}`)
|
||||||
|
|
||||||
|
const addedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.Added]
|
||||||
|
})
|
||||||
|
core.debug(`Added files: ${addedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('added_files', outputPrefix),
|
||||||
|
value: addedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const copiedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.Copied]
|
||||||
|
})
|
||||||
|
core.debug(`Copied files: ${copiedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('copied_files', outputPrefix),
|
||||||
|
value: copiedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const modifiedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.Modified]
|
||||||
|
})
|
||||||
|
core.debug(`Modified files: ${modifiedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('modified_files', outputPrefix),
|
||||||
|
value: modifiedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const renamedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.Renamed]
|
||||||
|
})
|
||||||
|
core.debug(`Renamed files: ${renamedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('renamed_files', outputPrefix),
|
||||||
|
value: renamedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const typeChangedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.TypeChanged]
|
||||||
|
})
|
||||||
|
core.debug(`Type changed files: ${typeChangedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('type_changed_files', outputPrefix),
|
||||||
|
value: typeChangedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const unmergedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.Unmerged]
|
||||||
|
})
|
||||||
|
core.debug(`Unmerged files: ${unmergedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('unmerged_files', outputPrefix),
|
||||||
|
value: unmergedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const unknownFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.Unknown]
|
||||||
|
})
|
||||||
|
core.debug(`Unknown files: ${unknownFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('unknown_files', outputPrefix),
|
||||||
|
value: unknownFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const allChangedAndModifiedFiles = await getAllChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles
|
||||||
|
})
|
||||||
|
core.debug(`All changed and modified files: ${allChangedAndModifiedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('all_changed_and_modified_files', outputPrefix),
|
||||||
|
value: allChangedAndModifiedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const allChangedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [
|
||||||
|
ChangeTypeEnum.Added,
|
||||||
|
ChangeTypeEnum.Copied,
|
||||||
|
ChangeTypeEnum.Modified,
|
||||||
|
ChangeTypeEnum.Renamed
|
||||||
|
]
|
||||||
|
})
|
||||||
|
core.debug(`All changed files: ${allChangedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('all_changed_files', outputPrefix),
|
||||||
|
value: allChangedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('any_changed', outputPrefix),
|
||||||
|
value: allChangedFiles.length > 0 && filePatterns.length > 0,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const allOtherChangedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allDiffFiles,
|
||||||
|
changeTypes: [
|
||||||
|
ChangeTypeEnum.Added,
|
||||||
|
ChangeTypeEnum.Copied,
|
||||||
|
ChangeTypeEnum.Modified,
|
||||||
|
ChangeTypeEnum.Renamed
|
||||||
|
]
|
||||||
|
})
|
||||||
|
core.debug(`All other changed files: ${allOtherChangedFiles}`)
|
||||||
|
|
||||||
|
const otherChangedFiles = allOtherChangedFiles
|
||||||
|
.split(inputs.separator)
|
||||||
|
.filter(
|
||||||
|
(filePath: string) =>
|
||||||
|
!allChangedFiles.split(inputs.separator).includes(filePath)
|
||||||
|
)
|
||||||
|
|
||||||
|
const onlyChanged =
|
||||||
|
otherChangedFiles.length === 0 &&
|
||||||
|
allChangedFiles.length > 0 &&
|
||||||
|
filePatterns.length > 0
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('only_changed', outputPrefix),
|
||||||
|
value: onlyChanged,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('other_changed_files', outputPrefix),
|
||||||
|
value: otherChangedFiles.join(inputs.separator),
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const allModifiedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [
|
||||||
|
ChangeTypeEnum.Added,
|
||||||
|
ChangeTypeEnum.Copied,
|
||||||
|
ChangeTypeEnum.Modified,
|
||||||
|
ChangeTypeEnum.Renamed,
|
||||||
|
ChangeTypeEnum.Deleted
|
||||||
|
]
|
||||||
|
})
|
||||||
|
core.debug(`All modified files: ${allModifiedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('all_modified_files', outputPrefix),
|
||||||
|
value: allModifiedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('any_modified', outputPrefix),
|
||||||
|
value: allModifiedFiles.length > 0 && filePatterns.length > 0,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const allOtherModifiedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allDiffFiles,
|
||||||
|
changeTypes: [
|
||||||
|
ChangeTypeEnum.Added,
|
||||||
|
ChangeTypeEnum.Copied,
|
||||||
|
ChangeTypeEnum.Modified,
|
||||||
|
ChangeTypeEnum.Renamed,
|
||||||
|
ChangeTypeEnum.Deleted
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const otherModifiedFiles = allOtherModifiedFiles
|
||||||
|
.split(inputs.separator)
|
||||||
|
.filter(
|
||||||
|
(filePath: string) =>
|
||||||
|
!allModifiedFiles.split(inputs.separator).includes(filePath)
|
||||||
|
)
|
||||||
|
|
||||||
|
const onlyModified =
|
||||||
|
otherModifiedFiles.length === 0 &&
|
||||||
|
allModifiedFiles.length > 0 &&
|
||||||
|
filePatterns.length > 0
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('only_modified', outputPrefix),
|
||||||
|
value: onlyModified,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('other_modified_files', outputPrefix),
|
||||||
|
value: otherModifiedFiles.join(inputs.separator),
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const deletedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allFilteredDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.Deleted]
|
||||||
|
})
|
||||||
|
core.debug(`Deleted files: ${deletedFiles}`)
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('deleted_files', outputPrefix),
|
||||||
|
value: deletedFiles,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('any_deleted', outputPrefix),
|
||||||
|
value: deletedFiles.length > 0 && filePatterns.length > 0,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
const allOtherDeletedFiles = await getChangeTypeFiles({
|
||||||
|
inputs,
|
||||||
|
changedFiles: allDiffFiles,
|
||||||
|
changeTypes: [ChangeTypeEnum.Deleted]
|
||||||
|
})
|
||||||
|
|
||||||
|
const otherDeletedFiles = allOtherDeletedFiles
|
||||||
|
.split(inputs.separator)
|
||||||
|
.filter(
|
||||||
|
filePath => !deletedFiles.split(inputs.separator).includes(filePath)
|
||||||
|
)
|
||||||
|
|
||||||
|
const onlyDeleted =
|
||||||
|
otherDeletedFiles.length === 0 &&
|
||||||
|
deletedFiles.length > 0 &&
|
||||||
|
filePatterns.length > 0
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('only_deleted', outputPrefix),
|
||||||
|
value: onlyDeleted,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
|
||||||
|
await setOutput({
|
||||||
|
key: getOutputKey('other_deleted_files', outputPrefix),
|
||||||
|
value: otherDeletedFiles.join(inputs.separator),
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
}
|
|
@ -379,9 +379,18 @@ export const getSHAForPullRequestEvent = async (
|
||||||
0)
|
0)
|
||||||
) {
|
) {
|
||||||
core.warning(
|
core.warning(
|
||||||
'Unable to locate the remote branch head sha. Falling back to the pull request base sha.'
|
'Unable to locate the remote branch head sha. Falling back to the previous commit in the local history.'
|
||||||
)
|
)
|
||||||
previousSha = env.GITHUB_EVENT_PULL_REQUEST_BASE_SHA
|
previousSha = await getParentSha({
|
||||||
|
cwd: workingDirectory
|
||||||
|
})
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
previousSha = await getRemoteBranchHeadSha({
|
previousSha = await getRemoteBranchHeadSha({
|
||||||
|
@ -395,12 +404,12 @@ export const getSHAForPullRequestEvent = async (
|
||||||
|
|
||||||
if (isShallow) {
|
if (isShallow) {
|
||||||
if (
|
if (
|
||||||
await canDiffCommits({
|
!(await canDiffCommits({
|
||||||
cwd: workingDirectory,
|
cwd: workingDirectory,
|
||||||
sha1: previousSha,
|
sha1: previousSha,
|
||||||
sha2: currentSha,
|
sha2: currentSha,
|
||||||
diff
|
diff
|
||||||
})
|
}))
|
||||||
) {
|
) {
|
||||||
core.debug(
|
core.debug(
|
||||||
'Merge base is not in the local history, fetching remote target branch...'
|
'Merge base is not in the local history, fetching remote target branch...'
|
||||||
|
|
15
src/env.ts
15
src/env.ts
|
@ -2,23 +2,25 @@ import {promises as fs} from 'fs'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
export type Env = {
|
export type Env = {
|
||||||
GITHUB_EVENT_PULL_REQUEST_HEAD_REF: string
|
|
||||||
GITHUB_EVENT_PULL_REQUEST_BASE_REF: string
|
|
||||||
GITHUB_EVENT_BEFORE: string
|
|
||||||
GITHUB_REF_NAME: string
|
GITHUB_REF_NAME: string
|
||||||
GITHUB_REF: 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_BASE_REF: string
|
||||||
GITHUB_EVENT_RELEASE_TARGET_COMMITISH: string
|
GITHUB_EVENT_RELEASE_TARGET_COMMITISH: string
|
||||||
GITHUB_EVENT_HEAD_REPO_FORK: string
|
GITHUB_EVENT_HEAD_REPO_FORK: string
|
||||||
GITHUB_WORKSPACE: string
|
|
||||||
GITHUB_EVENT_FORCED: string
|
|
||||||
GITHUB_EVENT_PULL_REQUEST_NUMBER: string
|
GITHUB_EVENT_PULL_REQUEST_NUMBER: string
|
||||||
GITHUB_EVENT_PULL_REQUEST_BASE_SHA: string
|
GITHUB_EVENT_PULL_REQUEST_BASE_SHA: string
|
||||||
GITHUB_EVENT_PULL_REQUEST_HEAD_SHA: string
|
GITHUB_EVENT_PULL_REQUEST_HEAD_SHA: string
|
||||||
GITHUB_EVENT_NAME: string
|
GITHUB_EVENT_PULL_REQUEST_HEAD_REF: string
|
||||||
|
GITHUB_EVENT_PULL_REQUEST_BASE_REF: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GithubEvent = {
|
type GithubEvent = {
|
||||||
|
action?: string
|
||||||
forced?: string
|
forced?: string
|
||||||
pull_request?: {
|
pull_request?: {
|
||||||
head: {
|
head: {
|
||||||
|
@ -65,6 +67,7 @@ export const getEnv = async (): Promise<Env> => {
|
||||||
GITHUB_EVENT_PULL_REQUEST_BASE_SHA: eventJson.pull_request?.base?.sha || '',
|
GITHUB_EVENT_PULL_REQUEST_BASE_SHA: eventJson.pull_request?.base?.sha || '',
|
||||||
GITHUB_EVENT_PULL_REQUEST_HEAD_SHA: eventJson.pull_request?.head?.sha || '',
|
GITHUB_EVENT_PULL_REQUEST_HEAD_SHA: eventJson.pull_request?.head?.sha || '',
|
||||||
GITHUB_EVENT_FORCED: eventJson.forced || '',
|
GITHUB_EVENT_FORCED: eventJson.forced || '',
|
||||||
|
GITHUB_EVENT_ACTION: eventJson.action || '',
|
||||||
GITHUB_REF_NAME: process.env.GITHUB_REF_NAME || '',
|
GITHUB_REF_NAME: process.env.GITHUB_REF_NAME || '',
|
||||||
GITHUB_REF: process.env.GITHUB_REF || '',
|
GITHUB_REF: process.env.GITHUB_REF || '',
|
||||||
GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE || '',
|
GITHUB_WORKSPACE: process.env.GITHUB_WORKSPACE || '',
|
||||||
|
|
|
@ -5,10 +5,16 @@ export type Inputs = {
|
||||||
filesSeparator: string
|
filesSeparator: string
|
||||||
filesFromSourceFile: string
|
filesFromSourceFile: string
|
||||||
filesFromSourceFileSeparator: string
|
filesFromSourceFileSeparator: string
|
||||||
|
filesYaml: string
|
||||||
|
filesYamlFromSourceFile: string
|
||||||
|
filesYamlFromSourceFileSeparator: string
|
||||||
filesIgnore: string
|
filesIgnore: string
|
||||||
filesIgnoreSeparator: string
|
filesIgnoreSeparator: string
|
||||||
filesIgnoreFromSourceFile: string
|
filesIgnoreFromSourceFile: string
|
||||||
filesIgnoreFromSourceFileSeparator: string
|
filesIgnoreFromSourceFileSeparator: string
|
||||||
|
filesIgnoreYaml: string
|
||||||
|
filesIgnoreYamlFromSourceFile: string
|
||||||
|
filesIgnoreYamlFromSourceFileSeparator: string
|
||||||
separator: string
|
separator: string
|
||||||
includeAllOldNewRenamedFiles: boolean
|
includeAllOldNewRenamedFiles: boolean
|
||||||
oldNewSeparator: string
|
oldNewSeparator: string
|
||||||
|
@ -54,6 +60,17 @@ export const getInputs = (): Inputs => {
|
||||||
trimWhitespace: false
|
trimWhitespace: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
const filesYaml = core.getInput('files_yaml', {required: false})
|
||||||
|
const filesYamlFromSourceFile = core.getInput('files_yaml_from_source_file', {
|
||||||
|
required: false
|
||||||
|
})
|
||||||
|
const filesYamlFromSourceFileSeparator = core.getInput(
|
||||||
|
'files_yaml_from_source_file_separator',
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
trimWhitespace: false
|
||||||
|
}
|
||||||
|
)
|
||||||
const filesIgnoreFromSourceFile = core.getInput(
|
const filesIgnoreFromSourceFile = core.getInput(
|
||||||
'files_ignore_from_source_file',
|
'files_ignore_from_source_file',
|
||||||
{required: false}
|
{required: false}
|
||||||
|
@ -65,6 +82,18 @@ export const getInputs = (): Inputs => {
|
||||||
trimWhitespace: false
|
trimWhitespace: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
const filesIgnoreYaml = core.getInput('files_ignore_yaml', {required: false})
|
||||||
|
const filesIgnoreYamlFromSourceFile = core.getInput(
|
||||||
|
'files_ignore_yaml_from_source_file',
|
||||||
|
{required: false}
|
||||||
|
)
|
||||||
|
const filesIgnoreYamlFromSourceFileSeparator = core.getInput(
|
||||||
|
'files_ignore_yaml_from_source_file_separator',
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
trimWhitespace: false
|
||||||
|
}
|
||||||
|
)
|
||||||
const separator = core.getInput('separator', {
|
const separator = core.getInput('separator', {
|
||||||
required: true,
|
required: true,
|
||||||
trimWhitespace: false
|
trimWhitespace: false
|
||||||
|
@ -122,10 +151,16 @@ export const getInputs = (): Inputs => {
|
||||||
filesSeparator,
|
filesSeparator,
|
||||||
filesFromSourceFile,
|
filesFromSourceFile,
|
||||||
filesFromSourceFileSeparator,
|
filesFromSourceFileSeparator,
|
||||||
|
filesYaml,
|
||||||
|
filesYamlFromSourceFile,
|
||||||
|
filesYamlFromSourceFileSeparator,
|
||||||
filesIgnore,
|
filesIgnore,
|
||||||
filesIgnoreSeparator,
|
filesIgnoreSeparator,
|
||||||
filesIgnoreFromSourceFile,
|
filesIgnoreFromSourceFile,
|
||||||
filesIgnoreFromSourceFileSeparator,
|
filesIgnoreFromSourceFileSeparator,
|
||||||
|
filesIgnoreYaml,
|
||||||
|
filesIgnoreYamlFromSourceFile,
|
||||||
|
filesIgnoreYamlFromSourceFileSeparator,
|
||||||
separator,
|
separator,
|
||||||
includeAllOldNewRenamedFiles,
|
includeAllOldNewRenamedFiles,
|
||||||
oldNewSeparator,
|
oldNewSeparator,
|
||||||
|
|
328
src/main.ts
328
src/main.ts
|
@ -1,12 +1,7 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import {
|
import {getAllDiffFiles, getRenamedFiles} from './changedFiles'
|
||||||
getAllChangeTypeFiles,
|
import {setChangedFilesOutput} from './changedFilesOutput'
|
||||||
getAllDiffFiles,
|
|
||||||
getChangeTypeFiles,
|
|
||||||
getRenamedFiles,
|
|
||||||
ChangeTypeEnum
|
|
||||||
} from './changedFiles'
|
|
||||||
import {
|
import {
|
||||||
DiffResult,
|
DiffResult,
|
||||||
getSHAForPullRequestEvent,
|
getSHAForPullRequestEvent,
|
||||||
|
@ -16,8 +11,8 @@ import {getEnv} from './env'
|
||||||
import {getInputs} from './inputs'
|
import {getInputs} from './inputs'
|
||||||
import {
|
import {
|
||||||
getFilePatterns,
|
getFilePatterns,
|
||||||
getFilteredChangedFiles,
|
|
||||||
getSubmodulePath,
|
getSubmodulePath,
|
||||||
|
getYamlFilePatterns,
|
||||||
isRepoShallow,
|
isRepoShallow,
|
||||||
setOutput,
|
setOutput,
|
||||||
submoduleExists,
|
submoduleExists,
|
||||||
|
@ -88,7 +83,9 @@ export async function run(): Promise<void> {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
core.info(
|
core.info(
|
||||||
`Running on a ${env.GITHUB_EVENT_NAME || 'pull_request'} event...`
|
`Running on a ${env.GITHUB_EVENT_NAME || 'pull_request'} (${
|
||||||
|
env.GITHUB_EVENT_ACTION
|
||||||
|
}) event...`
|
||||||
)
|
)
|
||||||
diffResult = await getSHAForPullRequestEvent(
|
diffResult = await getSHAForPullRequestEvent(
|
||||||
inputs,
|
inputs,
|
||||||
|
@ -110,12 +107,6 @@ export async function run(): Promise<void> {
|
||||||
`Retrieving changes between ${diffResult.previousSha} (${diffResult.targetBranch}) → ${diffResult.currentSha} (${diffResult.currentBranch})`
|
`Retrieving changes between ${diffResult.previousSha} (${diffResult.targetBranch}) → ${diffResult.currentSha} (${diffResult.currentBranch})`
|
||||||
)
|
)
|
||||||
|
|
||||||
const filePatterns = await getFilePatterns({
|
|
||||||
inputs,
|
|
||||||
workingDirectory
|
|
||||||
})
|
|
||||||
core.debug(`File patterns: ${filePatterns}`)
|
|
||||||
|
|
||||||
const allDiffFiles = await getAllDiffFiles({
|
const allDiffFiles = await getAllDiffFiles({
|
||||||
workingDirectory,
|
workingDirectory,
|
||||||
hasSubmodule,
|
hasSubmodule,
|
||||||
|
@ -124,275 +115,58 @@ export async function run(): Promise<void> {
|
||||||
outputRenamedFilesAsDeletedAndAdded
|
outputRenamedFilesAsDeletedAndAdded
|
||||||
})
|
})
|
||||||
core.debug(`All diff files: ${JSON.stringify(allDiffFiles)}`)
|
core.debug(`All diff files: ${JSON.stringify(allDiffFiles)}`)
|
||||||
|
core.info('All Done!')
|
||||||
|
core.endGroup()
|
||||||
|
|
||||||
const allFilteredDiffFiles = await getFilteredChangedFiles({
|
const filePatterns = await getFilePatterns({
|
||||||
allDiffFiles,
|
|
||||||
filePatterns
|
|
||||||
})
|
|
||||||
core.debug(`All filtered diff files: ${JSON.stringify(allFilteredDiffFiles)}`)
|
|
||||||
|
|
||||||
const addedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
inputs,
|
||||||
changedFiles: allFilteredDiffFiles,
|
workingDirectory
|
||||||
changeTypes: [ChangeTypeEnum.Added]
|
|
||||||
})
|
|
||||||
core.debug(`Added files: ${addedFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'added_files',
|
|
||||||
value: addedFiles,
|
|
||||||
inputs
|
|
||||||
})
|
})
|
||||||
|
core.debug(`File patterns: ${filePatterns}`)
|
||||||
|
|
||||||
const copiedFiles = await getChangeTypeFiles({
|
if (filePatterns.length > 0) {
|
||||||
|
core.startGroup('changed-files-patterns')
|
||||||
|
await setChangedFilesOutput({
|
||||||
|
allDiffFiles,
|
||||||
|
filePatterns,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
core.info('All Done!')
|
||||||
|
core.endGroup()
|
||||||
|
}
|
||||||
|
|
||||||
|
const yamlFilePatterns = await getYamlFilePatterns({
|
||||||
inputs,
|
inputs,
|
||||||
changedFiles: allFilteredDiffFiles,
|
workingDirectory
|
||||||
changeTypes: [ChangeTypeEnum.Copied]
|
|
||||||
})
|
|
||||||
core.debug(`Copied files: ${copiedFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'copied_files',
|
|
||||||
value: copiedFiles,
|
|
||||||
inputs
|
|
||||||
})
|
})
|
||||||
|
core.debug(`Yaml file patterns: ${JSON.stringify(yamlFilePatterns)}`)
|
||||||
|
|
||||||
const modifiedFiles = await getChangeTypeFiles({
|
if (Object.keys(yamlFilePatterns).length > 0) {
|
||||||
inputs,
|
for (const key of Object.keys(yamlFilePatterns)) {
|
||||||
changedFiles: allFilteredDiffFiles,
|
core.startGroup(`changed-files-yaml-${key}`)
|
||||||
changeTypes: [ChangeTypeEnum.Modified]
|
await setChangedFilesOutput({
|
||||||
})
|
allDiffFiles,
|
||||||
core.debug(`Modified files: ${modifiedFiles}`)
|
filePatterns: yamlFilePatterns[key],
|
||||||
await setOutput({
|
inputs,
|
||||||
key: 'modified_files',
|
outputPrefix: key
|
||||||
value: modifiedFiles,
|
})
|
||||||
inputs
|
core.info('All Done!')
|
||||||
})
|
core.endGroup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const renamedFiles = await getChangeTypeFiles({
|
if (filePatterns.length === 0 && Object.keys(yamlFilePatterns).length === 0) {
|
||||||
inputs,
|
core.startGroup('changed-files-all')
|
||||||
changedFiles: allFilteredDiffFiles,
|
await setChangedFilesOutput({
|
||||||
changeTypes: [ChangeTypeEnum.Renamed]
|
allDiffFiles,
|
||||||
})
|
inputs
|
||||||
core.debug(`Renamed files: ${renamedFiles}`)
|
})
|
||||||
await setOutput({
|
core.info('All Done!')
|
||||||
key: 'renamed_files',
|
core.endGroup()
|
||||||
value: renamedFiles,
|
}
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const typeChangedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allFilteredDiffFiles,
|
|
||||||
changeTypes: [ChangeTypeEnum.TypeChanged]
|
|
||||||
})
|
|
||||||
core.debug(`Type changed files: ${typeChangedFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'type_changed_files',
|
|
||||||
value: typeChangedFiles,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const unmergedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allFilteredDiffFiles,
|
|
||||||
changeTypes: [ChangeTypeEnum.Unmerged]
|
|
||||||
})
|
|
||||||
core.debug(`Unmerged files: ${unmergedFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'unmerged_files',
|
|
||||||
value: unmergedFiles,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const unknownFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allFilteredDiffFiles,
|
|
||||||
changeTypes: [ChangeTypeEnum.Unknown]
|
|
||||||
})
|
|
||||||
core.debug(`Unknown files: ${unknownFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'unknown_files',
|
|
||||||
value: unknownFiles,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const allChangedAndModifiedFiles = await getAllChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allFilteredDiffFiles
|
|
||||||
})
|
|
||||||
core.debug(`All changed and modified files: ${allChangedAndModifiedFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'all_changed_and_modified_files',
|
|
||||||
value: allChangedAndModifiedFiles,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const allChangedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allFilteredDiffFiles,
|
|
||||||
changeTypes: [
|
|
||||||
ChangeTypeEnum.Added,
|
|
||||||
ChangeTypeEnum.Copied,
|
|
||||||
ChangeTypeEnum.Modified,
|
|
||||||
ChangeTypeEnum.Renamed
|
|
||||||
]
|
|
||||||
})
|
|
||||||
core.debug(`All changed files: ${allChangedFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'all_changed_files',
|
|
||||||
value: allChangedFiles,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'any_changed',
|
|
||||||
value: allChangedFiles.length > 0 && filePatterns.length > 0,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const allOtherChangedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allDiffFiles,
|
|
||||||
changeTypes: [
|
|
||||||
ChangeTypeEnum.Added,
|
|
||||||
ChangeTypeEnum.Copied,
|
|
||||||
ChangeTypeEnum.Modified,
|
|
||||||
ChangeTypeEnum.Renamed
|
|
||||||
]
|
|
||||||
})
|
|
||||||
core.debug(`All other changed files: ${allOtherChangedFiles}`)
|
|
||||||
|
|
||||||
const otherChangedFiles = allOtherChangedFiles
|
|
||||||
.split(inputs.separator)
|
|
||||||
.filter(
|
|
||||||
(filePath: string) =>
|
|
||||||
!allChangedFiles.split(inputs.separator).includes(filePath)
|
|
||||||
)
|
|
||||||
|
|
||||||
const onlyChanged =
|
|
||||||
otherChangedFiles.length === 0 &&
|
|
||||||
allChangedFiles.length > 0 &&
|
|
||||||
filePatterns.length > 0
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'only_changed',
|
|
||||||
value: onlyChanged,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'other_changed_files',
|
|
||||||
value: otherChangedFiles.join(inputs.separator),
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const allModifiedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allFilteredDiffFiles,
|
|
||||||
changeTypes: [
|
|
||||||
ChangeTypeEnum.Added,
|
|
||||||
ChangeTypeEnum.Copied,
|
|
||||||
ChangeTypeEnum.Modified,
|
|
||||||
ChangeTypeEnum.Renamed,
|
|
||||||
ChangeTypeEnum.Deleted
|
|
||||||
]
|
|
||||||
})
|
|
||||||
core.debug(`All modified files: ${allModifiedFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'all_modified_files',
|
|
||||||
value: allModifiedFiles,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'any_modified',
|
|
||||||
value: allModifiedFiles.length > 0 && filePatterns.length > 0,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const allOtherModifiedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allDiffFiles,
|
|
||||||
changeTypes: [
|
|
||||||
ChangeTypeEnum.Added,
|
|
||||||
ChangeTypeEnum.Copied,
|
|
||||||
ChangeTypeEnum.Modified,
|
|
||||||
ChangeTypeEnum.Renamed,
|
|
||||||
ChangeTypeEnum.Deleted
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const otherModifiedFiles = allOtherModifiedFiles
|
|
||||||
.split(inputs.separator)
|
|
||||||
.filter(
|
|
||||||
(filePath: string) =>
|
|
||||||
!allModifiedFiles.split(inputs.separator).includes(filePath)
|
|
||||||
)
|
|
||||||
|
|
||||||
const onlyModified =
|
|
||||||
otherModifiedFiles.length === 0 &&
|
|
||||||
allModifiedFiles.length > 0 &&
|
|
||||||
filePatterns.length > 0
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'only_modified',
|
|
||||||
value: onlyModified,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'other_modified_files',
|
|
||||||
value: otherModifiedFiles.join(inputs.separator),
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const deletedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allFilteredDiffFiles,
|
|
||||||
changeTypes: [ChangeTypeEnum.Deleted]
|
|
||||||
})
|
|
||||||
core.debug(`Deleted files: ${deletedFiles}`)
|
|
||||||
await setOutput({
|
|
||||||
key: 'deleted_files',
|
|
||||||
value: deletedFiles,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'any_deleted',
|
|
||||||
value: deletedFiles.length > 0 && filePatterns.length > 0,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
const allOtherDeletedFiles = await getChangeTypeFiles({
|
|
||||||
inputs,
|
|
||||||
changedFiles: allDiffFiles,
|
|
||||||
changeTypes: [ChangeTypeEnum.Deleted]
|
|
||||||
})
|
|
||||||
|
|
||||||
const otherDeletedFiles = allOtherDeletedFiles
|
|
||||||
.split(inputs.separator)
|
|
||||||
.filter(
|
|
||||||
filePath => !deletedFiles.split(inputs.separator).includes(filePath)
|
|
||||||
)
|
|
||||||
|
|
||||||
const onlyDeleted =
|
|
||||||
otherDeletedFiles.length === 0 &&
|
|
||||||
deletedFiles.length > 0 &&
|
|
||||||
filePatterns.length > 0
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'only_deleted',
|
|
||||||
value: onlyDeleted,
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
await setOutput({
|
|
||||||
key: 'other_deleted_files',
|
|
||||||
value: otherDeletedFiles.join(inputs.separator),
|
|
||||||
inputs
|
|
||||||
})
|
|
||||||
|
|
||||||
if (inputs.includeAllOldNewRenamedFiles) {
|
if (inputs.includeAllOldNewRenamedFiles) {
|
||||||
|
core.startGroup('changed-files-all-old-new-renamed-files')
|
||||||
const allOldNewRenamedFiles = await getRenamedFiles({
|
const allOldNewRenamedFiles = await getRenamedFiles({
|
||||||
inputs,
|
inputs,
|
||||||
workingDirectory,
|
workingDirectory,
|
||||||
|
@ -406,11 +180,9 @@ export async function run(): Promise<void> {
|
||||||
value: allOldNewRenamedFiles,
|
value: allOldNewRenamedFiles,
|
||||||
inputs
|
inputs
|
||||||
})
|
})
|
||||||
|
core.info('All Done!')
|
||||||
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info('All Done!')
|
|
||||||
|
|
||||||
core.endGroup()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
|
|
198
src/utils.ts
198
src/utils.ts
|
@ -2,9 +2,12 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
import {createReadStream, promises as fs} from 'fs'
|
import {createReadStream, promises as fs} from 'fs'
|
||||||
|
import {readFile} from 'fs/promises'
|
||||||
|
import {flattenDeep} from 'lodash'
|
||||||
import mm from 'micromatch'
|
import mm from 'micromatch'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {createInterface} from 'readline'
|
import {createInterface} from 'readline'
|
||||||
|
import {parseDocument} from 'yaml'
|
||||||
import {ChangedFiles, ChangeTypeEnum} from './changedFiles'
|
import {ChangedFiles, ChangeTypeEnum} from './changedFiles'
|
||||||
|
|
||||||
import {Inputs} from './inputs'
|
import {Inputs} from './inputs'
|
||||||
|
@ -157,7 +160,7 @@ const getFilesFromSourceFile = async ({
|
||||||
filePaths: string[]
|
filePaths: string[]
|
||||||
excludedFiles?: boolean
|
excludedFiles?: boolean
|
||||||
}): Promise<string[]> => {
|
}): Promise<string[]> => {
|
||||||
const lines = []
|
const lines: string[] = []
|
||||||
for (const filePath of filePaths) {
|
for (const filePath of filePaths) {
|
||||||
for await (const line of lineOfFileGenerator({filePath, excludedFiles})) {
|
for await (const line of lineOfFileGenerator({filePath, excludedFiles})) {
|
||||||
lines.push(line)
|
lines.push(line)
|
||||||
|
@ -808,10 +811,10 @@ export const getFilePatterns = async ({
|
||||||
if (pattern.endsWith('/')) {
|
if (pattern.endsWith('/')) {
|
||||||
return `${pattern}**`
|
return `${pattern}**`
|
||||||
} else {
|
} else {
|
||||||
const pathParts = pattern.split('/')
|
const pathParts = pattern.split(path.sep)
|
||||||
const lastPart = pathParts[pathParts.length - 1]
|
const lastPart = pathParts[pathParts.length - 1]
|
||||||
if (!lastPart.includes('.')) {
|
if (!lastPart.includes('.')) {
|
||||||
return `${pattern}/**`
|
return `${pattern}${path.sep}**`
|
||||||
} else {
|
} else {
|
||||||
return pattern
|
return pattern
|
||||||
}
|
}
|
||||||
|
@ -819,6 +822,193 @@ export const getFilePatterns = async ({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Example YAML input:
|
||||||
|
// filesYaml: |
|
||||||
|
// frontend:
|
||||||
|
// - frontend/**
|
||||||
|
// backend:
|
||||||
|
// - backend/**
|
||||||
|
// test: test/**
|
||||||
|
// shared: &shared
|
||||||
|
// - common/**
|
||||||
|
// lib:
|
||||||
|
// - *shared
|
||||||
|
// - lib/**
|
||||||
|
// Return an Object:
|
||||||
|
// {
|
||||||
|
// frontend: ['frontend/**'],
|
||||||
|
// backend: ['backend/**'],
|
||||||
|
// test: ['test/**'],
|
||||||
|
// shared: ['common/**'],
|
||||||
|
// lib: ['common/**', 'lib/**']
|
||||||
|
// }
|
||||||
|
|
||||||
|
type YamlObject = {
|
||||||
|
[key: string]: string | string[] | [string[], string]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getYamlFilePatternsFromContents = async ({
|
||||||
|
content = '',
|
||||||
|
filePath = '',
|
||||||
|
excludedFiles = false
|
||||||
|
}: {
|
||||||
|
content?: string
|
||||||
|
filePath?: string
|
||||||
|
excludedFiles?: boolean
|
||||||
|
}): Promise<Record<string, string[]>> => {
|
||||||
|
const filePatterns: Record<string, string[]> = {}
|
||||||
|
let source = ''
|
||||||
|
|
||||||
|
if (filePath) {
|
||||||
|
if (!(await exists(filePath))) {
|
||||||
|
core.error(`File does not exist: ${filePath}`)
|
||||||
|
throw new Error(`File does not exist: ${filePath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
source = await readFile(filePath, 'utf8')
|
||||||
|
} else {
|
||||||
|
source = content
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = parseDocument(source, {merge: true, schema: 'failsafe'})
|
||||||
|
|
||||||
|
if (doc.errors.length > 0) {
|
||||||
|
if (filePath) {
|
||||||
|
core.warning(`YAML errors in ${filePath}: ${doc.errors}`)
|
||||||
|
} else {
|
||||||
|
core.warning(`YAML errors: ${doc.errors}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.warnings.length > 0) {
|
||||||
|
if (filePath) {
|
||||||
|
core.warning(`YAML warnings in ${filePath}: ${doc.warnings}`)
|
||||||
|
} else {
|
||||||
|
core.warning(`YAML warnings: ${doc.warnings}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const yamlObject = doc.toJS() as YamlObject
|
||||||
|
|
||||||
|
for (const key in yamlObject) {
|
||||||
|
let value = yamlObject[key]
|
||||||
|
|
||||||
|
if (typeof value === 'string' && value.includes('\n')) {
|
||||||
|
value = value.split('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
value = value.trim()
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
filePatterns[key] = [
|
||||||
|
excludedFiles && !value.startsWith('!') ? `!${value}` : value
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
filePatterns[key] = flattenDeep(value)
|
||||||
|
.filter(v => v.trim() !== '')
|
||||||
|
.map(v => {
|
||||||
|
if (excludedFiles && !v.startsWith('!')) {
|
||||||
|
v = `!${v}`
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePatterns
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getYamlFilePatterns = async ({
|
||||||
|
inputs,
|
||||||
|
workingDirectory
|
||||||
|
}: {
|
||||||
|
inputs: Inputs
|
||||||
|
workingDirectory: string
|
||||||
|
}): Promise<Record<string, string[]>> => {
|
||||||
|
let filePatterns: Record<string, string[]> = {}
|
||||||
|
if (inputs.filesYaml) {
|
||||||
|
filePatterns = {
|
||||||
|
...(await getYamlFilePatternsFromContents({content: inputs.filesYaml}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputs.filesYamlFromSourceFile) {
|
||||||
|
const inputFilesYamlFromSourceFile = inputs.filesYamlFromSourceFile
|
||||||
|
.split(inputs.filesYamlFromSourceFileSeparator)
|
||||||
|
.filter(p => p !== '')
|
||||||
|
.map(p => path.join(workingDirectory, p))
|
||||||
|
|
||||||
|
core.debug(`files yaml from source file: ${inputFilesYamlFromSourceFile}`)
|
||||||
|
|
||||||
|
for (const filePath of inputFilesYamlFromSourceFile) {
|
||||||
|
const newFilePatterns = await getYamlFilePatternsFromContents({filePath})
|
||||||
|
for (const key in newFilePatterns) {
|
||||||
|
if (key in filePatterns) {
|
||||||
|
core.warning(
|
||||||
|
`files_yaml_from_source_file: Duplicated key ${key} detected in ${filePath}, the ${filePatterns[key]} will be overwritten by ${newFilePatterns[key]}.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filePatterns = {
|
||||||
|
...filePatterns,
|
||||||
|
...newFilePatterns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputs.filesIgnoreYaml) {
|
||||||
|
const newIgnoreFilePatterns = await getYamlFilePatternsFromContents({
|
||||||
|
content: inputs.filesIgnoreYaml,
|
||||||
|
excludedFiles: true
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const key in newIgnoreFilePatterns) {
|
||||||
|
if (key in filePatterns) {
|
||||||
|
core.warning(
|
||||||
|
`files_ignore_yaml: Duplicated key ${key} detected, the ${filePatterns[key]} will be overwritten by ${newIgnoreFilePatterns[key]}.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputs.filesIgnoreYamlFromSourceFile) {
|
||||||
|
const inputFilesIgnoreYamlFromSourceFile =
|
||||||
|
inputs.filesIgnoreYamlFromSourceFile
|
||||||
|
.split(inputs.filesIgnoreYamlFromSourceFileSeparator)
|
||||||
|
.filter(p => p !== '')
|
||||||
|
.map(p => path.join(workingDirectory, p))
|
||||||
|
|
||||||
|
core.debug(
|
||||||
|
`files ignore yaml from source file: ${inputFilesIgnoreYamlFromSourceFile}`
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const filePath of inputFilesIgnoreYamlFromSourceFile) {
|
||||||
|
const newIgnoreFilePatterns = await getYamlFilePatternsFromContents({
|
||||||
|
filePath,
|
||||||
|
excludedFiles: true
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const key in newIgnoreFilePatterns) {
|
||||||
|
if (key in filePatterns) {
|
||||||
|
core.warning(
|
||||||
|
`files_ignore_yaml_from_source_file: Duplicated key ${key} detected in ${filePath}, the ${filePatterns[key]} will be overwritten by ${newIgnoreFilePatterns[key]}.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filePatterns = {
|
||||||
|
...filePatterns,
|
||||||
|
...newIgnoreFilePatterns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePatterns
|
||||||
|
}
|
||||||
|
|
||||||
export const setOutput = async ({
|
export const setOutput = async ({
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
|
@ -832,7 +1022,7 @@ export const setOutput = async ({
|
||||||
core.setOutput(key, cleanedValue)
|
core.setOutput(key, cleanedValue)
|
||||||
|
|
||||||
if (inputs.writeOutputFiles) {
|
if (inputs.writeOutputFiles) {
|
||||||
const outputDir = inputs.outputDir || '.github/outputs'
|
const outputDir = inputs.outputDir
|
||||||
const extension = inputs.json ? 'json' : 'txt'
|
const extension = inputs.json ? 'json' : 'txt'
|
||||||
const outputFilePath = path.join(outputDir, `${key}.${extension}`)
|
const outputFilePath = path.join(outputDir, `${key}.${extension}`)
|
||||||
|
|
||||||
|
|
16
test/changed-files.yml
Normal file
16
test/changed-files.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
test:
|
||||||
|
- test/**.txt
|
||||||
|
src:
|
||||||
|
- src/*.ts
|
||||||
|
- '!src/__tests__/**'
|
||||||
|
dist:
|
||||||
|
- dist/**
|
||||||
|
shared: &shared
|
||||||
|
- .github/**
|
||||||
|
common:
|
||||||
|
- *shared
|
||||||
|
- .gitignore
|
||||||
|
multiline: |
|
||||||
|
test/**
|
||||||
|
src/*.ts
|
||||||
|
.github/**
|
|
@ -3605,6 +3605,11 @@ yallist@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||||
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
||||||
|
|
||||||
|
yaml@^2.3.1:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
|
||||||
|
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
|
||||||
|
|
||||||
yargs-parser@^21.0.1, yargs-parser@^21.1.1:
|
yargs-parser@^21.0.1, yargs-parser@^21.1.1:
|
||||||
version "21.1.1"
|
version "21.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||||
|
|
Loading…
Reference in a new issue