Skip to content

Commit aa7676a

Browse files
committed
Add infrastructure to check for problems with npm configuration files
A task and GitHub Actions workflow are provided here for checking the project's npm package manager configuration. On every push and pull request that affects relevant files, and periodically: - Validate package.json against its JSON schema. - Check for forgotten package-lock.json syncs.
1 parent cf933a8 commit aa7676a

File tree

3 files changed

+237
-0
lines changed

3 files changed

+237
-0
lines changed

.github/workflows/check-npm-task.yml

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-npm-task.md
2+
name: Check npm
3+
4+
env:
5+
# See: https://github.com/actions/setup-node/#readme
6+
NODE_VERSION: 20.x
7+
8+
# See: https://docs.github.com/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows
9+
on:
10+
create:
11+
push:
12+
paths:
13+
- ".github/workflows/check-npm-task.ya?ml"
14+
- "**/package.json"
15+
- "**/package-lock.json"
16+
- "Taskfile.ya?ml"
17+
pull_request:
18+
paths:
19+
- ".github/workflows/check-npm-task.ya?ml"
20+
- "**/package.json"
21+
- "**/package-lock.json"
22+
- "Taskfile.ya?ml"
23+
schedule:
24+
# Run every Tuesday at 8 AM UTC to catch breakage resulting from changes to the JSON schema.
25+
- cron: "0 8 * * TUE"
26+
workflow_dispatch:
27+
repository_dispatch:
28+
29+
jobs:
30+
run-determination:
31+
runs-on: ubuntu-latest
32+
permissions: {}
33+
outputs:
34+
result: ${{ steps.determination.outputs.result }}
35+
steps:
36+
- name: Determine if the rest of the workflow should run
37+
id: determination
38+
run: |
39+
RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x"
40+
# The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead.
41+
if [[
42+
"${{ github.event_name }}" != "create" ||
43+
"${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX
44+
]]; then
45+
# Run the other jobs.
46+
RESULT="true"
47+
else
48+
# There is no need to run the other jobs.
49+
RESULT="false"
50+
fi
51+
52+
echo "result=$RESULT" >> $GITHUB_OUTPUT
53+
54+
validate:
55+
name: validate (${{ matrix.project.path }})
56+
needs: run-determination
57+
if: needs.run-determination.outputs.result == 'true'
58+
runs-on: ubuntu-latest
59+
permissions:
60+
contents: read
61+
62+
strategy:
63+
fail-fast: false
64+
matrix:
65+
project:
66+
- path: .
67+
68+
steps:
69+
- name: Checkout repository
70+
uses: actions/checkout@v4
71+
72+
- name: Setup Node.js
73+
uses: actions/setup-node@v4
74+
with:
75+
node-version: ${{ env.NODE_VERSION }}
76+
77+
- name: Install Task
78+
uses: arduino/setup-task@v2
79+
with:
80+
repo-token: ${{ secrets.GITHUB_TOKEN }}
81+
version: 3.x
82+
83+
- name: Validate package.json
84+
run: task --silent npm:validate PROJECT_PATH="${{ matrix.project.path }}"
85+
86+
check-sync:
87+
name: check-sync (${{ matrix.project.path }})
88+
needs: run-determination
89+
if: needs.run-determination.outputs.result == 'true'
90+
runs-on: ubuntu-latest
91+
permissions:
92+
contents: read
93+
94+
strategy:
95+
fail-fast: false
96+
matrix:
97+
project:
98+
# TODO: add paths of all npm-managed projects in the repository here.
99+
- path: .
100+
101+
steps:
102+
- name: Checkout repository
103+
uses: actions/checkout@v4
104+
105+
- name: Setup Node.js
106+
uses: actions/setup-node@v4
107+
with:
108+
node-version: ${{ env.NODE_VERSION }}
109+
110+
- name: Install Task
111+
uses: arduino/setup-task@v2
112+
with:
113+
repo-token: ${{ secrets.GITHUB_TOKEN }}
114+
version: 3.x
115+
116+
- name: Install npm dependencies
117+
run: task npm:install-deps PROJECT_PATH="${{ matrix.project.path }}"
118+
119+
- name: Check package-lock.json
120+
run: git diff --color --exit-code "${{ matrix.project.path }}/package-lock.json"

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[![Check Go Dependencies status](https://github.com/arduino/arduino-lint/actions/workflows/check-go-dependencies-task.yml/badge.svg)](https://github.com/arduino/arduino-lint/actions/workflows/check-go-dependencies-task.yml)
88
[![Publish Tester Build status](https://github.com/arduino/arduino-lint/actions/workflows/publish-go-tester-task.yml/badge.svg)](https://github.com/arduino/arduino-lint/actions/workflows/publish-go-tester-task.yml)
99
[![Publish Nightly Build status](https://github.com/arduino/arduino-lint/actions/workflows/publish-go-nightly-task.yml/badge.svg)](https://github.com/arduino/arduino-lint/actions/workflows/publish-go-nightly-task.yml)
10+
[![Check npm status](https://github.com/arduino/arduino-lint/actions/workflows/check-npm-task.yml/badge.svg)](https://github.com/arduino/arduino-lint/actions/workflows/check-npm-task.yml)
1011
[![Check Python status](https://github.com/arduino/arduino-lint/actions/workflows/check-python-task.yml/badge.svg)](https://github.com/arduino/arduino-lint/actions/workflows/check-python-task.yml)
1112
[![Check Markdown status](https://github.com/arduino/arduino-lint/actions/workflows/check-markdown-task.yml/badge.svg)](https://github.com/arduino/arduino-lint/actions/workflows/check-markdown-task.yml)
1213
[![Spell Check status](https://github.com/arduino/arduino-lint/actions/workflows/spell-check-task.yml/badge.svg)](https://github.com/arduino/arduino-lint/actions/workflows/spell-check-task.yml)

Taskfile.yml

+116
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ vars:
1818
|| \
1919
echo '"ERROR: Unable to discover Go packages"' \
2020
)
21+
# Last version of ajv-cli with support for the JSON schema "Draft 4" specification
22+
SCHEMA_DRAFT_4_AJV_CLI_VERSION: 3.3.0
2123
# build vars
2224
COMMIT:
2325
sh: echo "$(git log --no-show-signature -n 1 --format=%h)"
@@ -380,6 +382,85 @@ tasks:
380382
cmds:
381383
- npm install
382384

385+
# Parameter variables:
386+
# - PROJECT_PATH: path of the npm-managed project. Default value: "./"
387+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-npm-task/Taskfile.yml
388+
npm:validate:
389+
desc: Validate npm configuration files against their JSON schema
390+
vars:
391+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/package.json
392+
SCHEMA_URL: https://json.schemastore.org/package.json
393+
SCHEMA_PATH:
394+
sh: task utility:mktemp-file TEMPLATE="package-json-schema-XXXXXXXXXX.json"
395+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/ava.json
396+
AVA_SCHEMA_URL: https://json.schemastore.org/ava.json
397+
AVA_SCHEMA_PATH:
398+
sh: task utility:mktemp-file TEMPLATE="ava-schema-XXXXXXXXXX.json"
399+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/base.json
400+
BASE_SCHEMA_URL: https://json.schemastore.org/base.json
401+
BASE_SCHEMA_PATH:
402+
sh: task utility:mktemp-file TEMPLATE="base-schema-XXXXXXXXXX.json"
403+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/eslintrc.json
404+
ESLINTRC_SCHEMA_URL: https://json.schemastore.org/eslintrc.json
405+
ESLINTRC_SCHEMA_PATH:
406+
sh: task utility:mktemp-file TEMPLATE="eslintrc-schema-XXXXXXXXXX.json"
407+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/jscpd.json
408+
JSCPD_SCHEMA_URL: https://json.schemastore.org/jscpd.json
409+
JSCPD_SCHEMA_PATH:
410+
sh: task utility:mktemp-file TEMPLATE="jscpd-schema-XXXXXXXXXX.json"
411+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/npm-badges.json
412+
NPM_BADGES_SCHEMA_URL: https://json.schemastore.org/npm-badges.json
413+
NPM_BADGES_SCHEMA_PATH:
414+
sh: task utility:mktemp-file TEMPLATE="npm-badges-schema-XXXXXXXXXX.json"
415+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/partial-eslint-plugins.json
416+
PARTIAL_ESLINT_PLUGINS_SCHEMA_URL: https://json.schemastore.org/partial-eslint-plugins.json
417+
PARTIAL_ESLINT_PLUGINS_PATH:
418+
sh: task utility:mktemp-file TEMPLATE="partial-eslint-plugins-schema-XXXXXXXXXX.json"
419+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/prettierrc.json
420+
PRETTIERRC_SCHEMA_URL: https://json.schemastore.org/prettierrc.json
421+
PRETTIERRC_SCHEMA_PATH:
422+
sh: task utility:mktemp-file TEMPLATE="prettierrc-schema-XXXXXXXXXX.json"
423+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/semantic-release.json
424+
SEMANTIC_RELEASE_SCHEMA_URL: https://json.schemastore.org/semantic-release.json
425+
SEMANTIC_RELEASE_SCHEMA_PATH:
426+
sh: task utility:mktemp-file TEMPLATE="semantic-release-schema-XXXXXXXXXX.json"
427+
# Source: https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/stylelintrc.json
428+
STYLELINTRC_SCHEMA_URL: https://json.schemastore.org/stylelintrc.json
429+
STYLELINTRC_SCHEMA_PATH:
430+
sh: task utility:mktemp-file TEMPLATE="stylelintrc-schema-XXXXXXXXXX.json"
431+
INSTANCE_PATH: >-
432+
{{default "." .PROJECT_PATH}}/package.json
433+
PROJECT_FOLDER:
434+
sh: pwd
435+
WORKING_FOLDER:
436+
sh: task utility:mktemp-folder TEMPLATE="dependabot-validate-XXXXXXXXXX"
437+
cmds:
438+
- wget --quiet --output-document="{{.SCHEMA_PATH}}" {{.SCHEMA_URL}}
439+
- wget --quiet --output-document="{{.AVA_SCHEMA_PATH}}" {{.AVA_SCHEMA_URL}}
440+
- wget --quiet --output-document="{{.BASE_SCHEMA_PATH}}" {{.BASE_SCHEMA_URL}}
441+
- wget --quiet --output-document="{{.ESLINTRC_SCHEMA_PATH}}" {{.ESLINTRC_SCHEMA_URL}}
442+
- wget --quiet --output-document="{{.JSCPD_SCHEMA_PATH}}" {{.JSCPD_SCHEMA_URL}}
443+
- wget --quiet --output-document="{{.NPM_BADGES_SCHEMA_PATH}}" {{.NPM_BADGES_SCHEMA_URL}}
444+
- wget --quiet --output-document="{{.PARTIAL_ESLINT_PLUGINS_PATH}}" {{.PARTIAL_ESLINT_PLUGINS_SCHEMA_URL}}
445+
- wget --quiet --output-document="{{.PRETTIERRC_SCHEMA_PATH}}" {{.PRETTIERRC_SCHEMA_URL}}
446+
- wget --quiet --output-document="{{.SEMANTIC_RELEASE_SCHEMA_PATH}}" {{.SEMANTIC_RELEASE_SCHEMA_URL}}
447+
- wget --quiet --output-document="{{.STYLELINTRC_SCHEMA_PATH}}" {{.STYLELINTRC_SCHEMA_URL}}
448+
- |
449+
cd "{{.WORKING_FOLDER}}" # Workaround for https://github.com/npm/cli/issues/3210
450+
npx ajv-cli@{{.SCHEMA_DRAFT_4_AJV_CLI_VERSION}} validate \
451+
--all-errors \
452+
-s "{{.SCHEMA_PATH}}" \
453+
-r "{{.AVA_SCHEMA_PATH}}" \
454+
-r "{{.BASE_SCHEMA_PATH}}" \
455+
-r "{{.ESLINTRC_SCHEMA_PATH}}" \
456+
-r "{{.JSCPD_SCHEMA_PATH}}" \
457+
-r "{{.NPM_BADGES_SCHEMA_PATH}}" \
458+
-r "{{.PARTIAL_ESLINT_PLUGINS_PATH}}" \
459+
-r "{{.PRETTIERRC_SCHEMA_PATH}}" \
460+
-r "{{.SEMANTIC_RELEASE_SCHEMA_PATH}}" \
461+
-r "{{.STYLELINTRC_SCHEMA_PATH}}" \
462+
-d "{{.PROJECT_FOLDER}}/{{.INSTANCE_PATH}}"
463+
383464
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/poetry-task/Taskfile.yml
384465
poetry:install-deps:
385466
desc: Install dependencies managed by Poetry
@@ -465,6 +546,41 @@ tasks:
465546
fi
466547
- shfmt -w "{{.SCRIPT_PATH}}"
467548

549+
# Make a temporary file named according to the passed TEMPLATE variable and print the path passed to stdout
550+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/windows-task/Taskfile.yml
551+
utility:mktemp-file:
552+
vars:
553+
RAW_PATH:
554+
sh: mktemp --tmpdir "{{.TEMPLATE}}"
555+
cmds:
556+
- task: utility:normalize-path
557+
vars:
558+
RAW_PATH: "{{.RAW_PATH}}"
559+
560+
# Make a temporary folder named according to the passed TEMPLATE variable and print the path passed to stdout
561+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/windows-task/Taskfile.yml
562+
utility:mktemp-folder:
563+
vars:
564+
RAW_PATH:
565+
sh: mktemp --directory --tmpdir "{{.TEMPLATE}}"
566+
cmds:
567+
- task: utility:normalize-path
568+
vars:
569+
RAW_PATH: "{{.RAW_PATH}}"
570+
571+
# Print a normalized version of the path passed via the RAW_PATH variable to stdout
572+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/windows-task/Taskfile.yml
573+
utility:normalize-path:
574+
cmds:
575+
- |
576+
if [[ "{{.OS}}" == "Windows_NT" ]] && which cygpath &>/dev/null; then
577+
# Even though the shell handles POSIX format absolute paths as expected, external applications do not.
578+
# So paths passed to such applications must first be converted to Windows format.
579+
cygpath -w "{{.RAW_PATH}}"
580+
else
581+
echo "{{.RAW_PATH}}"
582+
fi
583+
468584
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-mkdocs-task/Taskfile.yml
469585
website:check:
470586
desc: Check whether the MkDocs-based website will build

0 commit comments

Comments
 (0)