Some of the assets use tools sourced from the npm software registry.
Previously, the version of the tools used was not controlled. This was problematic because:
- A different version of the tool may be used on the contributor's machine than on the CI runner, resulting in confusing
failures.
- The project is immediately subject to disruption or breakage resulting from a release of the tool.
---
These tools were installed via either of the following methods:
`npx <pkg>`
This approach has the following behaviors of interest:
https://docs.npmjs.com/cli/v8/commands/npx#description
> If any requested packages are not present in the local project dependencies, then they are installed to a folder in the npm cache, which is added to the PATH environment variable in the executed process.
> Package names provided without a specifier will be matched with whatever version exists in the local project. Package names with a specifier will only be considered a match if they have the exact same name and version as the local dependency.
This means that the version used was:
1. Whatever happens to be present in the local cache
2. The latest available version if it is not already present
`npm install --global <pkg>`
The latest available version of the package is used.
---
The new approach is to specify the version of the tools via the standard npm metadata files (package.json +
package-lock.json). This approach was chosen over the `npx <pkg>@<version>` alternative for the following reasons:
- Enables automated updates via Dependabot PRs
- Enables automated vulnerability alerts
- Separates dependency management from the asset contents (i.e., no need to mess with the taskfile or workflow on every
update)
- Matches how we are already managing Python dependencies (pyproject.toml + poetry.lock)
The "Sync Labels" workflow was originally developed with Arduino firmware repositories in mind. Those projects don't
have a lot of existing infrastructure and, since they are intended to be very approachable, the impact of adding
additional non-firmware files or folders (especially in the root) must be carefully considered.
In that context, a self-contained workflow is desirable. However, the situation is different in the inherently more
complex and infrastructure rich tooling projects, which are typically consumed only as a binary by users.
For this reason, an alternative standardized version of the "Sync Labels" workflow was produced, which utilizes npm to
manage its tool dependencies.
The code dependencies of this project and the code infrastructure are already managed via npm, which means the switch to
this superior version of the workflow can be made without the need to add any additional infrastructure.
This provides some significant benefits:
- Controlled updates via Dependabot PRs instead of being subject to immediate breakage resulting from a new tool release
- Enables automated vulnerability alerts
This is especially important for the github-label-sync tool, since it is making irreversible writes to the GitHub
repository.
In addition to helping others use Task in their GitHub Actions workflows, this project uses Task for its own development.
Most of the repository's GitHub Actions workflows use the approach of executing a task. This approach allows maintaining
a single set of commands that can easily be used locally by contributors in addition to their use by the CI system.
The "Check npm" workflow was an exception to this pattern, using commands directly coded into the workflow file. This
meant the contributor would need to study the workflow and run the multiple equivalent complex commands locally if they
wanted to validate changes to the npm configuration in advance of pushing, or troubleshoot a problem flagged by the
workflow.
We already have a Task-based version of the workflow and accompanying tasks, which are generally preferred for Arduino
tooling projects and especially appropriate for this specific project which is devoted to Task:
https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-npm-task.md
Those standardized assets are hereby used in this repository, moving the commands from the workflow to an npm:validate
task and switching the workflow to using that task.
This project uses Node.js 16. Previously, although the workflows that were more closely related to the code were
configured to use this version, other workflows which also use Node.js/npm were using whichever version happened to be
pre-installed on the GitHub Actions runner.
Since the contributor will be using Node.js 16 on their local machine for all development and maintenance operations,
use of a different version by the GitHub Actions runner might result in confusing differences in results between the
local machine and the CI system.
This is avoided by setting the Node.js version in all applicable workflows. Following the existing convention, this is
done via an environment variable at the top of the workflow in order to facilitate the process of updating to a new
standard version of Node.js in the project.
In addition to helping others use Task in their GitHub Actions workflows, this project uses Task for its own development.
Most of the repository's GitHub Actions workflows use the approach of executing a task. This approach allows maintaining
a single set of commands that can easily be used locally by contributors in addition to their use by the CI system.
The "Check TypeScript Configuration" workflow was an exception to this pattern, using commands directly coded into the
workflow file. This meant the contributor would need to study the workflow and run the multiple equivalent complex
commands locally if they wanted to validate changes to the TypeScript configuration in advance of pushing, or
troubleshoot a problem flagged by the workflow.
These commands are hereby moved to a ts:validate task and the workflow switched to using that task.
This is intended to be a straight across transfer of the existing commands (or their equivalents) to the task.
A task and GitHub Actions workflow are provided here for checking the license types of npm-managed project dependencies.
On every push and pull request that affects relevant files, the CI workflow will check:
- If the dependency licenses cache is up to date
- If any of the project's dependencies have an unapproved license type.
Approval can be based on:
- Universally allowed license type
- Individual dependency
The "Check npm" GitHub Actions workflow validates the repository's `package.json` npm manifest file against its JSON
schema to catch any problems with its data format.
In order to avoid duplication of content, JSON schemas may reference other schemas via the `$ref` keyword. The
`package.json` schema was recently updated to share resources with the jscpd configuration schema, which caused the
validation to start failing:
schema /home/runner/work/_temp/json-schema/package-json-schema.json is invalid
error: can't resolve reference https://json.schemastore.org/jscpd.json from id #
The solution is to configure the workflow to download that schema as well and also to provide its path to the avj-cli
validator via an `-r` flag.
GitHub Actions actions are used by the workflows to set up development tools in the runner workspace.
In order to facilitate updates to new versions of these tools, we set the version to be set up via environment variables
at the top of the workflow.
Since this variable definition is separate from the step using the action, it might not be immediately apparent to the
maintainer which version syntaxes are supported. For this reason, comments were added with the URL to the relevant
section of the consuming action's documentation. Previously, these URLs were made to point to the version of the
documentation that matched the version of the action in use by the workflow. Since we only use a major version ref, the
expectation was that this would only need to be updated rarely. However, it turned out that the major version bump cycle
is significantly shorter than expected. In addition, it is easy to forget the update because action version update PRs
are provided by Dependabot, which obviously won't update the URLs in the comments.
So it will be best to use a URL that points to the documentation at the tip of the default branch of the action
repository. The likelihood of the documentation provided by this URL not matching the behavior of the release version of
the action in use is likely less than it is for an outdated URL.
The "Check npm" GitHub Actions workflow validates the repository's `package.json` npm manifest file against its JSON
schema to catch any problems with its data format.
In order to avoid duplication of content, JSON schemas may reference other schemas via the `$ref` keyword. The
`package.json` schema was recently updated to share resources with the `.stylelintrc` and semantic-release configuration
schemas, which caused the validation to
start failing:
schema /home/runner/work/_temp/json-schema/package-json-schema.json is invalid
error: can't resolve reference https://json.schemastore.org/stylelintrc.json from id #
The solution is to configure the workflow to download that schema as well and also to provide its path to the avj-cli
validator via an `-r` flag.
The "Check npm" GitHub Actions workflow validates the repository's `package.json` npm manifest file against its JSON
schema to catch any problems with its data format.
In order to avoid duplication of content, JSON schemas may reference other schemas via the `$ref` keyword. The
`package.json` schema was recently updated to share resources with the `ava.json` schema, which caused the validation to
start failing:
schema /home/runner/work/_temp/json-schema/package-json-schema.json is invalid
error: can't resolve reference https://json.schemastore.org/ava.json from id #
The solution is to configure the workflow to download that schema as well and also to provide its path to the avj-cli
validator via an `-r` flag.
The "Check npm" GitHub Actions workflow validates the repository's `package.json` npm manifest file against its JSON
schema to catch any problems with its data format.
In order to avoid duplication of content, JSON schemas may reference other schemas via the `$ref` keyword. The
`package.json` schema was recently updated to share resources with the `prettierrc.json` schema, which caused the
validation to start failing:
schema /home/runner/work/_temp/json-schema/package-json-schema.json is invalid
error: can't resolve reference https://json.schemastore.org/prettierrc.json from id #
The solution is to configure the workflow to download that schema as well and also to provide its path to the avj-cli
validator via an `-r` flag.
The "Check npm" GitHub Actions workflow validates the repository's `package.json` npm manifest file against its JSON
schema to catch any problems with its data format.
In order to avoid duplication of content, JSON schemas may reference other schemas via the `$ref` keyword. The
`package.json` schema was recently updated to share resources with the `eslintrc.json` schema, which caused the
validation to start failing:
error: can't resolve reference https://json.schemastore.org/eslintrc.json from id #
The solution is to configure the workflow to download that schema as well and also to provide its path to the avj-cli
validator via an `-r` flag.
`GITHUB_TOKEN` is an access token provided automatically by GitHub Actions. By default, this token has write permissions
except when the workflow is triggered by events generated by a PR from a fork.
This level of privilege is not needed by the workflow, which violates the security principle of least privilege.
For this reason, GitHub Actions now allows fine grained control of the permissions provided to the token, which are used
here to configure the workflows for only the permissions they require. The automatic permissions downgrade from write to
read for workflows triggered by events generated by a PR from a fork is unaffected.
Read permissions are required in the "contents" scope in order to checkout private repositories. Even though those
permissions are not required for public repositories, the workflow templates are intended to be applicable in public and
private repositories both and so a small excess in permissions was chosen over a large increase in maintenance effort to
have an extra version of each of the workflows for use in private repositories.
The test runner CI workflow was using Node.js 12.x, which reaches EOL 2022-04-30.
Other workflows did not specify a Node.js version, meaning they used whatever was installed on the GitHub Actions runner.
It will be best to define a specific version series for use and use that throughout the infrastructure and development.
The `carlosperate/download-file-action` action is used in the GitHub Actions workflows as a convenient way to download
external resources.
A major version ref has been added to that repository. It will always point to the latest release of the "1" major
version series. This means it is no longer necessary to do a full pin of the action version in use as before.
Use of the major version ref will cause the workflow to use a stable version of the action, while also benefiting from
ongoing development to the action up until such time as a new major release of an action is made. At that time we would
need to evaluate whether any changes to the workflow are required by the breaking change that triggered the major
release before manually updating the major ref (e.g., uses: `carlosperate/download-file-action@v2`). I think this
approach strikes the right balance between stability and maintainability for these workflows.
On every push that changes relevant files, and periodically, configure the repository's issue and pull request labels
according to the universal, shared, and local label configuration files.
This will make it easier for the maintainers to sync fixes and improvements in either direction between the upstream
"template" assets and their installation in this repository.
The use of the `error` workflow command will cause the important error message output to be surfaced prominently in the
workflow run summary and log. The workflow run logs can be somewhat labyrinthine to those who don't work with them
regularly, so finding the previous output to determine what caused the failure might have been challenging.
Even if it works as intended, it is not clear what the effect is of the escaped quote at the end of the environment
variables in the shell commands used to check the license detection results. Wrapping the variable names in braces
ensures they are as expected and also makes the working of the code clear.
Placement of this information at the top of the file, where it is easy to find and edit, facilitates updates to the
workflows as the tool versions used for project development are bumped periodically.
The paths filter is used to make GitHub Actions workflows more efficient and contributor friendly by preventing pointless
workflow runs from happening when only irrelevant files were modified. However, careful consideration must be given to
which files are relevant. In some cases, this can lead to a very lengthy list of path patterns. These can be compressed
down through the use of advanced patterns.
There are two file extensions in common use for YAML files: `.yaml` and `.yml`. Although this project uses `.yml`
exclusively for YAML files, these are standardized workflows which might be applied to projects that have established the
use of the other extension. It will be most flexible if it supports both.
This useful action is an integral part of the CI system. Now that it has a dedicated repository and a new name, the CI
workflows must be updated to use the latest release version of the action.
The default behavior is to cancel all the other matrix jobs if one fails. However, it can often be valuable to see what
the results are of the other jobs. In this case where the matrix is operating systems, it can be useful to see whether
the problem is specific to one OS, or if it affects all.
The `workflow_dispatch` event trigger allows the workflow to be run manually via the GitHub web interface. This can be
useful to trigger the workflow run in cases where the standard triggers won't, such as testing how external changes
(e.g., actions used in the workflow) affected it.
The `repository_dispatch` event serves a similar purpose, except triggered via the GitHub API.