-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
158 lines (149 loc) · 7.96 KB
/
Copy pathaction.yml
File metadata and controls
158 lines (149 loc) · 7.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
name: 'Changed Files with filter'
description: 'A GitHub Action that outputs a list of changed files in pull requests and commits.'
author: 'YUMEMI Inc.'
branding:
icon: 'file'
color: 'blue'
inputs:
separator:
description: 'Separator between file names in plain format.'
required: false
default: ' '
format:
description: 'Output format for list of file names. Either plain or json.'
required: false
default: 'plain'
patterns:
description: 'Path pattern to filter. See README for details.'
required: false
statuses:
description: 'Status of pull request files to include from output. Specify added, modified, renamed, and removed separately.'
required: false
exclude-statuses:
description: 'Status of pull request files to exclude from output. Specify added, modified, renamed, and removed separately.'
required: false
run:
description: 'Bash script to run if at least one file exists after filtering.'
required: false
head-ref:
description: 'Current branch, tag, or commit SHA to compare with base.'
required: false
default: ${{ github.event_name == 'pull_request_target' && format('refs/pull/{0}/merge',github.event.number) || github.sha }}
base-ref:
description: 'Branch, tag, or commit SHA as a basis for comparison.'
required: false
default: |-
${{
(github.event_name == 'pull_request' && github.base_ref) ||
(github.event_name == 'pull_request_target' && github.base_ref) ||
(github.event_name == 'push' && (startsWith(github.event.before, '0000') && github.event.repository.default_branch || github.event.before)) ||
(github.event_name == 'merge_group' && github.event.merge_group.base_sha) ||
github.event.repository.default_branch
}}
outputs:
files:
description: 'List of file names after filtering.'
value: ${{ steps.result.outputs.exists != null && (inputs.format == 'json' && steps.result.outputs.files || join(fromJSON(steps.result.outputs.files), inputs.separator)) || '' }}
exists:
description: 'Whether at least one file exists after filtering.'
value: ${{ steps.result.outputs.exists }}
additions:
description: 'Total additions after filtering.'
value: ${{ steps.result.outputs.additions }}
deletions:
description: 'Total deletions after filtering.'
value: ${{ steps.result.outputs.deletions }}
changes:
description: 'Total changes after filtering.'
value: ${{ steps.result.outputs.changes }}
action-path:
description: 'Used to access artifact files.'
value: ${{ steps.prepare.outputs.action-path }}
runs:
using: 'composite'
steps:
- name: Prepare
id: prepare
shell: bash
env:
PATTERNS: ${{ inputs.patterns }}
STATUSES: ${{ inputs.statuses }}
EXC_STATUSES: ${{ inputs.exclude-statuses }}
IS_VALID_FORMAT: ${{ inputs.format != null && contains(fromJSON('["plain","json"]'), inputs.format) }}
HEAD_REF: ${{ inputs.head-ref }}
BASE_REF: ${{ inputs.base-ref }}
GH_REPO: ${{ github.repository }} # for GitHub CLI
GH_TOKEN: ${{ github.token }} # for GitHub CLI
run: |
echo "::debug::gh version: $(gh --version | tr '\n' ' ')"
echo "::debug::jq version: $(jq --version)"
echo "action-path=$GITHUB_ACTION_PATH" >> "$GITHUB_OUTPUT"
source "$GITHUB_ACTION_PATH/raise_error.sh"
patterns="$("$GITHUB_ACTION_PATH/get_multiline_input.sh" "$PATTERNS")"
statuses=$("$GITHUB_ACTION_PATH/get_statuses.sh" "$STATUSES") > /dev/null 2>&1 || raise_error "'statuses' input can be used added, modified, renamed and removed."
exc_statuses=$("$GITHUB_ACTION_PATH/get_statuses.sh" "$EXC_STATUSES") > /dev/null 2>&1 || raise_error "'exclude-statuses' input can be used added, modified, renamed and removed."
if [ "$IS_VALID_FORMAT" != 'true' ]; then raise_error "'format' input must be plain or json."; fi
if [[ -z "$HEAD_REF" || -z "$BASE_REF" ]]; then raise_error "Specify 'head-ref' and 'base-ref' inputs."; fi
# have pemisson?
gh api repos/{owner}/{repo}/branches --silent > /dev/null 2>&1 || raise_error "May not have 'contents: read' permission."
head_sha="$(gh api "repos/{owner}/{repo}/commits/$HEAD_REF" | jq -r '.sha')" > /dev/null 2>&1 || raise_error "'head-ref' input is not valid."
base_sha="$(gh api "repos/{owner}/{repo}/commits/$BASE_REF" | jq -r '.sha')" > /dev/null 2>&1 || raise_error "'base-ref' input is not valid."
head_previous_sha="$(gh api "repos/{owner}/{repo}/commits/$head_sha" | jq -r '.parents[0].sha')"
if [ "$head_previous_sha" == 'null' ]; then raise_error "No history. 'head-ref' input reference may be the first commit."; fi
if [ "$base_sha" == "$head_sha" ]; then
files='[]'
elif [ "$base_sha" == "$head_previous_sha" ]; then
# avoid using copare, which has a limit of 300 files
files="$(gh api "repos/{owner}/{repo}/commits/$head_sha" --paginate | jq '.files[]|{ filename, status, additions, deletions, changes }' | jq -cs)"
if [ "$(echo "$files" | jq 'length')" -ge 3000 ]; then raise_error "Cannot handle more than 3000 files."; fi
else
# files do not support pagination and are limited to 300 files
files="$(gh api "repos/{owner}/{repo}/compare/${base_sha}...${head_sha}" | jq -c '[.files[]|{ filename, status, additions, deletions, changes }]')"
if [ "$(echo "$files" | jq 'length')" -ge 300 ]; then raise_error "Cannot handle more than 300 files when 'base-ref' input is specified."; fi
fi
{
echo "patterns=$patterns"; echo "statuses=$statuses"; echo "exc-statuses=$exc_statuses"
} >> "$GITHUB_OUTPUT"
echo -n "$files" > "$GITHUB_ACTION_PATH/files.json" # output large data to filles
- name: Filter files
id: filter
uses: actions/github-script@v7
env:
PATTERNS: ${{ steps.prepare.outputs.patterns }}
STATUSES: ${{ steps.prepare.outputs.statuses }}
EXC_STATUSES: ${{ steps.prepare.outputs.exc-statuses }}
with:
script: |
const { GITHUB_ACTION_PATH, PATTERNS, STATUSES, EXC_STATUSES } = process.env;
const fs = require('fs').promises, minimatch = require(`${GITHUB_ACTION_PATH}/dist/index.js`);
const files = JSON.parse(await fs.readFile(`${GITHUB_ACTION_PATH}/files.json`, 'utf-8'));
const patterns = JSON.parse(PATTERNS), statuses = JSON.parse(STATUSES), excStatuses = JSON.parse(EXC_STATUSES);
const incPatterns = patterns.filter(p => !p.startsWith('!'));
const excPatterns = patterns.filter(p => p.startsWith('!')).map(p => p.slice(1));
const option = { dot: true, nocomment: true, nonegate: true, noext: true };
const filteredFiles = files.filter(f => incPatterns.length === 0 || incPatterns.some(p => minimatch(f.filename, p, option)))
.filter(f => !excPatterns.some(p => minimatch(f.filename, p, option)))
.filter(f => statuses.length === 0 || statuses.some(s => f.status === s))
.filter(f => !excStatuses.some(s => f.status === s));
await fs.writeFile(`${GITHUB_ACTION_PATH}/filtered_files.json`, JSON.stringify(filteredFiles), 'utf-8');
- name: Output results
id: result
shell: bash
run: |
files="$(cat "$GITHUB_ACTION_PATH/filtered_files.json")"
{
if [ "$files" != '[]' ]; then
echo "files=$(echo "$files" | jq -c '[.[].filename]')"
echo 'exists=true'
echo "additions=$(echo "$files" | jq '[.[].additions]|add')"
echo "deletions=$(echo "$files" | jq '[.[].deletions]|add')"
echo "changes=$(echo "$files" | jq '[.[].changes]|add')"
else
echo 'files=[]'; echo 'exists=false'; echo 'additions=0'; echo 'deletions=0'; echo 'changes=0'
fi
} >> "$GITHUB_OUTPUT"
- name: Run script
if: steps.result.outputs.exists == 'true' && inputs.run != null
shell: bash
run: |
${{ inputs.run }}