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 trunk-based development strategy is used by some tooling projects. Their release branches may contain a subset of
the history of the default branch.
The status of the GitHub Actions workflows should be evaluated before making a release. However, this is not so simple
as checking the status of the commit at the tip of the branch in the project using that strategy. The reason is that,
for the sake of efficiency, the workflow is configured to run only when the processes are relevant to the trigger event
(e.g., no need to run it for a change to the readme).
In the case of the default branch, you can simply set the workflow runs filter to that branch and then check the result
of the latest run of each workflow of interest. However, that was not possible to do with the release branch since it
might be that the workflow was never run in that branch. The status of the latest run of the workflow in the default
branch might not match the status for the release branch if the release branch does not contain the full history.
For this reason, it will be helpful to trigger the workflow on the creation of a release branch. This will ensure that
the workflow will always have at least one run in the release branch. Subsequent commits pushed to the branch can run
based on their usual trigger filters and the status of the latest run of the workflow in the branch will provide an
accurate indication of the state of that branch.
Branches are created for purposes other than releases, most notably feature branches to stage work for a pull request.
Because the collection of workflows in a Tooling project are often very comprehensive, it would not be convenient or
efficient to run them on the creation of every feature branch.
Unfortunately, GitHub Actions does not support filters on the `create` event of branch creation like it does for the
`push` and `pull_request` events. There is support for a `branches` filter of the `push` event, but that filter is an AND
to the `paths` filter and this application requires an OR. For this reason, the workflows must be triggered by the
creation of any branch. The unwanted job runs are prevented by adding a `run-determination` job with the branch filter
handled by Bash commands. The other jobs of the workflow use this `run-determination` job as a dependency, only running
when it indicates they should via a job output. Because this minimal `run-determination` job runs very quickly, it is
roughly equivalent to the workflow having been skipped entirely for non-release branch creations. This approach has been
in use for some time already in other workflows.
The workflow is configured to run whenever any relevant file in the repository is changed. However, the results of the
workflow run are also dependent on the external environment it runs in, which include:
- The software running on the GitHub hosted GitHub Actions runner machines
- The GitHub Actions actions used by the workflow
- The dependencies that are installed by the workflow directly or via the GitHub Actions actions it uses
The workflow does not fully pin to a specific version of external tools. This was done in the interest of reducing the
maintenance burden of keeping the systems up to date. However, it also means that a new release of one of those tools
can cause the workflow runs to start failing (which might happen through an enhancement to that resource resolving a
false negative, or a defect causing a false negative).
When the repository file path trigger is used by itself, this sort of external breakage is only revealed when an
unrelated change triggers the workflow. That can be distracting even to a dedicated member of the project development
team, as well as confusing and discouraging to any contributor.
This type of change can be caught by adding a `schedule` event trigger that causes the workflow to run periodically in
addition to the other on-demand triggers. This allows the problem to be identified and resolved at the maintainer's
convenience, separate from the unrelated development work.
The "Check Go Dependencies" GitHub Actions workflow checks for dependencies with incompatible or unapproved license
types.
The dependency license metadata consumed by the "Licensed" tool is cached in the project repository, in a dedicated file
for each dependency.
The `check-cache` job of the workflow checks whether that cache is in sync with the project's current dependencies. It
does this by using the "Licensed" tool to update the cache and then a `git diff` command to check whether that resulted
in any changes (which would indicate it is out of sync).
Out of sync states could result from any of three distinct conditions:
- Missing metadata file
- Incorrect metadata file contents
- Superfluous metadata file
An incorrectly configured `git diff` command previously caused the last of these to be missed.
My first take at this system was simply using `git diff --exit-code` alone. That detects the last two, but misses the
first. I added the `git add --intent-to-add .` command to detect added files, but didn't realize that it caused the last
to be missed.
Superfluous files in the dependency license metadata cache won't actually interfere with its intended functionality, but
it is still important to avoid an accumulation of unused files.
The new commands will catch all three of the possible out of sync conditions by staging all changes that result from
the metadata cache update to the repository and then comparing those against the `HEAD` commit.
I considered an alternative approach which works just as well as the chosen one:
```
git add .
git diff --exit-code HEAD
```
However, I feel that the `--cached` flag makes the `git diff` command more self-explanatory.
- Use correct `actions/setup-go` reference for version in use
- Update redirecting GitHub docs URL to new location
- Configure GitHub docs URL to allow localization where available
At the time the dependency on `github.com/arduino/libraries-repository-engine` was added to the project, there was not a
release version available with the correct "v"-prefixed tag name, so it was necessary to use a "pseudo-version" instead.
A new release of `github.com/arduino/libraries-repository-engine` has now been made with the standard tag format
required by the Go module system. This offers the opportunity for more effective management of the dependency through
automated update offers generated by Dependabot.
Keeping this dependency updated is especially important because the latest release of
`github.com/arduino/libraries-repository-engine` is always used by the backend process, and the registry validator must
match its requirements.
The `carlosperate/download-file-action` action is used by 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.
A task and GitHub Actions workflow are provided here for checking the license types of Go 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
I neglected to document the licensing of the tool used to validate the registry file. This is now remedied and the
"Check License" workflow has been configured to also verify its license file is machine readable as AGPL 3.0.
Since it is not a primary component of the project, I puth the registry validator Go module in a subfolder of the
repository. The standardized Arduino tooling project "assets" at that time were designed for the more common project
structure of the module in the repository root. This meant some small modifications to the assets were required in order
to make them applicable to this repository's structure.
Since that time, the standardized assets have been improved so they can support arbitrary Go module locations, as is
needed here. A different approach was taken in order to also support any number of modules. Although this particular
repository is not likely to gain multiple modules, that is needed by other projects, and so it is a better approach in
general.
It looks like the formatting of the license header comments in Python files was corrupted at some point by the file being
formatted as a different language (likely Go).
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 version used for project development are bumped periodically.
These are the naming conventions established in the standardized template workflow.
The application's name is "Task", with "taskfile" being its configuration file. So the previous "Install Taskfile" step
name was inaccurate.
1.16 is now the preferred Go version for all Arduino tooling projects.
The update from Go 1.14 to 1.16 broke the task that runs golint. The good news is that the new `go install` command
eliminates the need for the workaround of running the `go get golang.org/x/lint/golint` command from outside the project
path.
The bad news is the `go list` command used to get the path of the golint installation does not work in the "module-aware
mode" that is now the default. In the end, I gave up on making the task work as before. I think it's better to require
the user to install golint and put the installation in the system `PATH`, displaying a helpful message when this has not
been done.
The registry data file must be manually edited when a library name change request is received. Each library must have a
unique name, so it's important to verify that no duplicates are introduced through these edits.
GitHub Actions workflows can be configured to run only when files under specific paths are modified. This makes the CI
system more efficient by avoiding pointless workflow runs. The path filter must be carefully configured to cover all
relevant files. In this case, the file that is the whole point of the workflow's existence was missing from the list!
Whenever one of the recognized license file names are modified in the repository, the workflow runs to check whether the
license can be recognized and whether it is of the expected type.
GitHub has a useful automated license detection system that determines the license type used by a repository, and
surfaces that information in the repository home page, the search web interface, and the GitHub API. This license
detection system requires that the license be defined by a dedicated file with one of several standardized filenames and
paths.
GitHub's license detection system uses the popular licensee tool, so this file also serves to define the license type
for any other usages of licensee, as well as to human readers of the file.
For this reason, and to ensure it remains a valid legal instrument, it's important that there be no non-standard
modifications to the license file or collisions with other supported licence files. This workflow ensures that any
changes which would change the license type or which license file is used by the detection are caught automatically.
On every push and pull request that affects relevant files, run yamllint to check the YAML files of
the repository for issues.
The .yamllint.yml file is used to configure yamllint:
https://yamllint.readthedocs.io/en/stable/configuration.html
On every push and pull request that affects relevant files, check the Go module for:
- Common detectable errors in the code.
- Use of outdated APIs
- Code style violations
- Code formatting inconsistency
- Misconfiguration
On every push and pull request that affects relevant files, run flake8 to check the Python files of
the repository for issues and black to check formatting.
The .flake8 file is used to configure flake8:
https://flake8.pycqa.org/en/latest/user/configuration.html