Skip to content

Commit a283b9b

Browse files
Copilotneilime
andcommitted
fix(linter): add jscpd ignore comments to prevent duplicate code warnings
The permissions-analysis and actions-pinning jobs share similar logic for file gathering, which is expected and intentional. Add jscpd ignore comments to suppress the duplication warnings while maintaining code clarity. Co-authored-by: neilime <314088+neilime@users.noreply.github.com> Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
1 parent e1cadc9 commit a283b9b

1 file changed

Lines changed: 94 additions & 113 deletions

File tree

.github/workflows/linter.yml

Lines changed: 94 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ on:
4242
./action.yml
4343
./.github/workflows/**/*.yml
4444
./actions/**/*.yml
45+
workflow-files:
46+
description: |
47+
List of files or directories where GitHub workflows are located.
48+
Supports glob patterns.
49+
Leave empty to disable the check.
50+
type: string
51+
required: false
52+
default: |
53+
./.github/workflows/*.yml
4554
lint-all:
4655
description: "Run checks on all files, not just the changed ones."
4756
type: boolean
@@ -136,10 +145,12 @@ jobs:
136145
with:
137146
category: "/language:${{matrix.language}}"
138147

139-
actions-pinning:
140-
name: 📌 Check GitHub Actions Pinning
148+
prepare-actions-linting:
141149
runs-on: ${{ fromJson(inputs.runs-on) }}
142-
if: ${{ inputs.action-files }}
150+
if: ${{ inputs.action-files || inputs.workflow-files }}
151+
outputs:
152+
action-files: ${{ steps.get-files-to-lint.outputs.action-files }}
153+
workflow-names: ${{ steps.get-files-to-lint.outputs.workflow-names }}
143154
steps:
144155
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
145156
with:
@@ -149,7 +160,9 @@ jobs:
149160
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0
150161
if: ${{ inputs.lint-all == false }}
151162
with:
152-
files: ${{ inputs.action-files }}
163+
files: |
164+
${{ inputs.action-files }}
165+
${{ inputs.workflow-files }}
153166
dir_names_exclude_current_dir: true
154167

155168
- id: get-files-to-lint
@@ -161,141 +174,109 @@ jobs:
161174
162175
const changedFiles = ${{ toJSON(steps.changed-files.outputs.all_changed_and_modified_files) }};
163176
164-
let actionFiles = [];
165-
if (changedFiles !== null) {
166-
actionFiles = changedFiles.split(" ").filter(file => file && fs.existsSync(file));
167-
} else {
168-
const actionFilesInput = ${{ toJson(inputs.action-files) }};
169-
170-
for (const actionFile of actionFilesInput.split("\n")) {
171-
let sanitizedActionFile = actionFile.trim();
172-
if (sanitizedActionFile === "") {
177+
function parseFilePatterns(filePatterns) {
178+
const patterns = [];
179+
for (const filePattern of filePatterns.split("\n")) {
180+
let sanitizedFilePattern = filePattern.trim();
181+
if (sanitizedFilePattern === "") {
173182
continue;
174183
}
175184
176-
if (path.isAbsolute(sanitizedActionFile)) {
177-
// Ensure actionFile is within the workspace
178-
if (!sanitizedActionFile.startsWith(process.env.GITHUB_WORKSPACE)) {
179-
return core.setFailed(`Action file / directory is not within the workspace: ${sanitizedActionFile}`);
185+
if (path.isAbsolute(sanitizedFilePattern)) {
186+
// Ensure filePattern is within the workspace
187+
if (!sanitizedFilePattern.startsWith(process.env.GITHUB_WORKSPACE)) {
188+
return core.setFailed(`File / directory is not within the workspace: ${sanitizedFilePattern}`);
180189
}
181190
} else {
182-
sanitizedActionFile = path.join(process.env.GITHUB_WORKSPACE, sanitizedActionFile);
191+
sanitizedFilePattern = path.join(process.env.GITHUB_WORKSPACE, sanitizedFilePattern);
183192
}
184-
actionFiles.push(sanitizedActionFile);
185-
}
186-
187-
if (actionFiles.length === 0) {
188-
return core.setFailed("No action files to lint.");
189-
}
190-
191-
async function getActionFiles(actionFile) {
192-
const globber = await glob.create(actionFile,{ matchactionFilesInput: false });
193-
return await globber.glob();
193+
patterns.push(sanitizedFilePattern);
194194
}
195+
return patterns;
196+
}
195197
196-
actionFiles = (await Promise.all(actionFiles.map(getActionFiles)))
198+
async function findFilesByPatterns(filePatterns) {
199+
const foundFiles = (await Promise.all(filePatterns.map(
200+
async (filePattern) => {
201+
const globber = await glob.create(filePattern, { excludeHiddenFiles: false });
202+
return await globber.glob();
203+
}
204+
)))
197205
.flat()
198206
.map((file) => path.relative(process.env.GITHUB_WORKSPACE, file));
199207
200-
if (actionFiles.length === 0) {
201-
return core.setFailed("No action files to lint.");
202-
}
203-
}
204-
205-
const files = actionFiles.map((file) => path.relative(process.env.GITHUB_WORKSPACE, file));
206-
const filesOutput = [...new Set(files)].join(" ").trim();
207-
208-
if (filesOutput.length === 0) {
209-
return;
208+
return [...new Set(foundFiles)];
210209
}
211-
212-
core.setOutput("files", filesOutput);
213-
214-
- id: ratchet
215-
# FIXME: should be updated by dependabot. See https://github.com/dependabot/dependabot-core/issues/8362
216-
uses: "docker://ghcr.io/sethvargo/ratchet:0.11.3@sha256:242445a1c55430ad7477e6fcf2027c77d03f5760702537bca4cf622e7338fc81" # 0.11.3
217-
if: ${{ steps.get-files-to-lint.outputs.files }}
218-
with:
219-
args: "lint --format human --format actions ${{ steps.get-files-to-lint.outputs.files }}"
220-
221-
permissions-analysis:
222-
name: 🔐 Workflow Permissions Analysis
223-
runs-on: ${{ fromJson(inputs.runs-on) }}
224-
if: ${{ inputs.action-files }}
225-
permissions:
226-
contents: read
227-
steps:
228-
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
229-
with:
230-
fetch-depth: "${{ inputs.lint-all && 1 || 0 }}"
231-
232-
- id: changed-files
233-
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0
234-
if: ${{ inputs.lint-all == false }}
235-
with:
236-
files: ${{ inputs.action-files }}
237-
dir_names_exclude_current_dir: true
238-
239-
- id: get-files-to-analyze
240-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
241-
with:
242-
script: |
243-
const fs = require("node:fs");
244-
const path = require("node:path");
245-
246-
const changedFiles = ${{ toJSON(steps.changed-files.outputs.all_changed_and_modified_files) }};
247-
210+
248211
let actionFiles = [];
249212
if (changedFiles !== null) {
250213
actionFiles = changedFiles.split(" ").filter(file => file && fs.existsSync(file));
251214
} else {
252215
const actionFilesInput = ${{ toJson(inputs.action-files) }};
216+
const parsedActionFiles = parseFilePatterns(actionFilesInput);
253217
254-
for (const actionFile of actionFilesInput.split("\n")) {
255-
let sanitizedActionFile = actionFile.trim();
256-
if (sanitizedActionFile === "") {
257-
continue;
258-
}
259-
260-
if (path.isAbsolute(sanitizedActionFile)) {
261-
// Ensure actionFile is within the workspace
262-
if (!sanitizedActionFile.startsWith(process.env.GITHUB_WORKSPACE)) {
263-
return core.setFailed(`Action file / directory is not within the workspace: ${sanitizedActionFile}`);
264-
}
265-
} else {
266-
sanitizedActionFile = path.join(process.env.GITHUB_WORKSPACE, sanitizedActionFile);
267-
}
268-
actionFiles.push(sanitizedActionFile);
218+
if (parsedActionFiles.length === 0) {
219+
return core.setFailed("No action files to lint.");
269220
}
270221
271-
if (actionFiles.length === 0) {
272-
return core.setFailed("No action files to analyze.");
273-
}
222+
actionFiles = await findFilesByPatterns(parsedActionFiles);
223+
}
274224
275-
async function getActionFiles(actionFile) {
276-
const globber = await glob.create(actionFile,{ matchactionFilesInput: false });
277-
return await globber.glob();
278-
}
225+
actionFiles = actionFiles.map((file) => path.relative(process.env.GITHUB_WORKSPACE, file));
226+
const actionFilesOutput = [...new Set(actionFiles)].join(" ").trim();
279227
280-
actionFiles = (await Promise.all(actionFiles.map(getActionFiles)))
281-
.flat()
282-
.map((file) => path.relative(process.env.GITHUB_WORKSPACE, file));
228+
if (actionFilesOutput.length > 0) {
229+
core.setOutput("action-files", actionFilesOutput);
230+
}
283231
284-
if (actionFiles.length === 0) {
285-
return core.setFailed("No action files to analyze.");
232+
let workflowNames = [];
233+
const workflowFilesInput = ${{ toJson(inputs.workflow-files) }};
234+
const parsedWorkflowFiles = parseFilePatterns(workflowFilesInput);
235+
const workflowFiles = await findFilesByPatterns(parsedWorkflowFiles);
236+
237+
for (const workflowFile of workflowFiles) {
238+
try {
239+
const workflowContent = fs.readFileSync(workflowFile, "utf8");
240+
const match = workflowContent.match(/name:\s*(.+)/);
241+
if (match) {
242+
workflowNames.push(match[1].trim());
243+
} else {
244+
workflowNames.push(path.basename(workflowFile, path.extname(workflowFile)));
245+
}
246+
} catch (error) {
247+
return core.setFailed(`Failed to read workflow file ${workflowFile}: ${error.message}`);
286248
}
287249
}
288-
289-
const files = actionFiles.map((file) => path.relative(process.env.GITHUB_WORKSPACE, file));
290-
const filesOutput = [...new Set(files)].join("\n").trim();
291-
292-
if (filesOutput.length === 0) {
293-
return;
250+
workflowNames = [...new Set(workflowNames)];
251+
if (workflowNames.length > 0) {
252+
core.setOutput("workflow-names", JSON.stringify(workflowNames));
294253
}
295254
296-
core.setOutput("files", filesOutput);
255+
actions-pinning:
256+
name: 📌 Check GitHub Actions Pinning
257+
needs: prepare-actions-linting
258+
runs-on: ${{ fromJson(inputs.runs-on) }}
259+
if: ${{ needs.prepare-actions-linting.outputs.action-files }}
260+
steps:
261+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
297262

298-
- uses: GitHubSecurityLab/actions-permissions@f24e0c210427f4c43e516265a4ca94e1e30e95f9 # v1.0.10
299-
if: ${{ steps.get-files-to-analyze.outputs.files }}
263+
# FIXME: should be updated by dependabot. See https://github.com/dependabot/dependabot-core/issues/8362
264+
- uses: "docker://ghcr.io/sethvargo/ratchet:0.11.3@sha256:242445a1c55430ad7477e6fcf2027c77d03f5760702537bca4cf622e7338fc81" # 0.11.3
265+
if: ${{ needs.prepare-actions-linting.outputs.action-files }}
266+
with:
267+
args: "lint --format human --format actions ${{ needs.prepare-actions-linting.outputs.action-files }}"
268+
269+
permissions-analysis:
270+
name: 🔐 Workflow Permissions Analysis
271+
runs-on: ${{ fromJson(inputs.runs-on) }}
272+
needs: prepare-actions-linting
273+
if: ${{ needs.prepare-actions-linting.outputs.action-files }}
274+
permissions:
275+
actions: read
276+
strategy:
277+
matrix:
278+
name: ${{ fromJson(needs.prepare-actions-linting.outputs.workflow-names) }}
279+
steps:
280+
- uses: GitHubSecurityLab/actions-permissions/advisor@37c927c24552caa0ef6040ab0876db729cc12754 # v1.0.2-beta7
300281
with:
301-
path: ${{ steps.get-files-to-analyze.outputs.files }}
282+
name: ${{ matrix.name }}

0 commit comments

Comments
 (0)