Melon Colly
Feeling a little unfocused at the start of the week as it was too hot. Finished the week much more focused and with a nice cycle ride.
I finally seem to have resolved the challenges using a custom Megalinter flavor from the Practicalli GitHub Org. It came down to Package permissions set to private by default in the Org.
I'll continue testing with a Clojure and Zensical repository and if all goes well will migrate all the Practicalli repositories to use the new custom flavors.
Entertainment this weekλ︎
Finished watching the Rookie, up to season 4. The next 4 seasons are not free on Prime currently. Nathon Filion is excellent at the eponymous character.
Battlestar Galactica 2009, all 4 seasons.
Terry Pratchett's Jingo Audio book is centered around Samuel Vimes and the rest of the City Watch.
Doom Patrol is a quirky series with some excellent actors, including Alan Tudyk. I enjoy the episodes with Alan Tudyk, but haven't enjoyed the rest of it as much.
Prime films watched
- The Day After Tomorrow
- Killing them softly
Prime Films to watch:
- Meet Dave
- My Name Is Bruce
- Olympus Has Fallen
- London Has Fallen
- Whitehouse Down
Prime Series to watch:
- Bladerunner Black Lotus (via trial)
- Discontinued - Bruce Campbell from the future talking about all the things we no longer have
- Persons Of Interest
- The Last Enemy - Benedict Cumberbach
- 12 Monkeys (TV Show)
- The Water Margin - classic kung fu
- Warehouse 13
HBO Max:
- Stuart Fails To Save The Universe
- Rick n Morty series 9 ?
Netflix:
- Resident Alien (Season 3 and 4)
Conventional Commitsλ︎
A specification and convention for writing version control commit messages.
Provides an easy to use form that supports creating an explicit change history, as well as enabling tool automation for that history.
Using a consistent set of change types flattens the learning curve when contributing to the project.
The Conventional Commits specification includes the following elements:
- Type: Indicates the nature of the change (e.g., feat, fix, docs).
- Scope (optional): Specifies the area of the codebase affected.
- Description: A brief summary of the change.
- Body (optional): Additional details about the change.
- Footer (optional): References to issues or breaking changes.
General form of a conventional commit message
! after the type/scope is a short-cut to indicate a breaking change.
Change Typesλ︎
buildchanges affecting the build system or external dependencieschorecicontinuous integration change, e.g. Git workflows and pipelinesdocsadd or update documentation to the projectfeatrelates to a feature of the projectfixresolves a specific bug or issue in the source codeperfperformance related changerefactorchange to the design, architecture, language, library or toolingstylechanges not affecting the meaning of the code (blank-space, formatting, etc)revertreverts a previous commit. Use the description from the reverted commit, the body it should state:This reverts commit <hash>, where hash is the SHA of the commit being reverted.testtesting the project, e.g. unit & integration testing
A project could introduce more types, if there is significant value and their purpose is documented in the projects contribution guide.
Bodyλ︎
The body should include the motivation for the change and contrast this with previous behavior.
Use the imperative, present tense: "change" not "changed" nor "changes".
Footersλ︎
The footer should contain any information about Breaking Changes.
Include a reference to an issue that this commit is Relate to or Resolve, where relevant.
BREAKING CHANGE: is when a commit introduces a breaking API change or any impactful change to users of the project.
Breaking Changes should start with the word BREAKING CHANGE: with a space or two newlines. The rest of the commit message is then used for this.
Other footers can follow a convention similar to git trailer format.
Tool supportλ︎
Megalinter Custom Flavorλ︎
Megalinter is an extensive collection of lint, format and security tools.
There are several 'flavours' that include a subset of these tools relevant to a specific programming language.
As a flavor has fewer tools, the docker image is smaller and takes less time to download (either locally or in a CI workflow).
Each flavor may still include more tools that are actually needed. The Megalinter configuration can define that these tools are not run, however, they are still part of the docker image that is downloaded.
By defining a custom image, only the specified tools are included in the docker image. This approach can make a very small docker image as well as simplfying the Megalinter config (no need to exclude tools that dont need to run).
Megalinter Custom Flavor - step by step guide
Custom Clojure flavorλ︎
Create a new GitHub repository in the Practicalli Org, with the name starting megalinter-custom-flavor-
Change into the cloned directory (see error example when the command is run outside of a git controlled directory).
In the cloned repository, use npx mega-linter-runner@9.6.0 --custom-flavor-setup to create the files for a custom flavor. Select the tools to include via the interactive wizard.
The --custom-flavor-linters option is used to specify the tools to include in the custom flavor, which are pre-selected in the tool wizard.
Currently, Practicalli uses the Megalinter Java flavor for all Clojure projects, with a few exclusions defined in the megalinter-config.yaml file.
The Megalinter Action log includes details of the tools that would be suitable for a custom clojure flavor.
Command: npx mega-linter-runner@9.6.0 --custom-flavor-setup --custom-flavor-linters CLOJURE_CLJ_KONDO,CLOJURE_CLJSTYLE,MARKDOWN_MARKDOWNLINT,MARKDOWN_MARKDOWN_TABLE_FORMATTER,REPOSITORY_GITLEAKS,REPOSITORY_BETTERLEAKS,REPOSITORY_GRYPE,REPOSITORY_OSV_SCANNER,REPOSITORY_SYFT,REPOSITORY_TRIVY,REPOSITORY_TRIVY_SBOM,REPOSITORY_TRUFFLEHOG,SPELL_LYCHEE,YAML_V8R
There is some cross-over in the suggested tools, especially in the REPOSITORY category. I also quickly reviewed the tools that are part of the Java flavor.
I simplified the repository group tools to just use REPOSITORY_BETTERLEAKS, the more efficient MARKDOWN_RUMDL and both Clojure related tools.
npx mega-linter-runner@9.6.0 --custom-flavor-setup --custom-flavor-linters BASH_SHELLCHECK,CLOJURE_CLJ_KONDO,CLOJURE_CLJSTYLE,DOCKERFILE_HADOLINT,MAKEFILE_CHECKMAKE,MARKDOWN_MARKDOWN_TABLE_FORMATTER,MARKDOWN_RUMDL,REPOSITORY_BETTERLEAKS,SPELL_LYCHEE,YAML_V8R
Creating files for Custom Clojure flavor
cd megalinter-custom-flavor-clojure
❯ npx mega-linter-runner@9.6.0 --custom-flavor-setup --custom-flavor-linters BASH_SHELLCHECK,CLOJURE_CLJ_KONDO,CLOJURE_CLJSTYLE,DOCKERFILE_HADOLINT,MAKEFILE_CHECKMAKE,MARKDOWN_MARKDOWN_TABLE_FORMATTER,MARKDOWN_RUMDL,REPOSITORY_BETTERLEAKS,SPELL_LYCHEE,YAML_V8R
Yeoman generator used: /home/practicalli/.npm/_npx/e592646a0575050f/node_modules/mega-linter-runner/generators/mega-linter-custom-flavor
.:oool' ,looo;
.xNXNXl .dXNNXo.
lXXXX0c. 'oKXXN0;
.oKNXNX0kxdddddddoc,. .;lodddddddxk0XXXX0c
.:kKXXXXXXXXXXXXNXX0dllx0XXXXXXXXXXXXXXXKd,
.,cdkOOOOOOOO0KXXXXXXXXXXK0OOOOOOOkxo:'
'ckKXNNNXkc'
':::::;. .c0XX0l. .;::::;.
'xXXXXXx' :kx: ;OXXXXKd.
.dKNNXXO; .. :0XXXXKl.
.lKXXXX0: .lKXXXX0:
:0XXXXKl. .dXXXXXk,
;kXXXXKd:cxXXXXXx'
'xXNXXXXXXXXXKo.
.oKXXXXNXXX0l.
.lKNNXNNXO:
,looool'
==========================================================
============= MegaLinter, by OX Security =============
========= https://ox.security?ref=megalinter ===========
==========================================================
Welcome to the MegaLinter Custom Flavor generator !
When you don't know what option to select, please use default values
Fetching MegaLinter configuration schema...
✔ What is the label of your custom flavor? PracticalliClojure
✔ Please select the linters you want to include in your custom flavor: BASH_SHELLCHECK, CLOJURE_CLJSTYLE, CLOJURE_CLJ_KONDO, DOCKERFILE_HADOLINT, MAKEFILE_CHECKMAKE, MARKDOWN_MARK
DOWN_TABLE_FORMATTER, MARKDOWN_RUMDL, REPOSITORY_BETTERLEAKS, SPELL_LYCHEE, YAML_V8R
create megalinter-custom-flavor.yml
create .github/workflows/megalinter-custom-flavor-builder.yml
create .github/workflows/check-new-megalinter-version.yml
create action.yml
conflict README.md
✔ Overwrite README.md? overwrite
force README.md
No change to package.json was detected. No package manager install will be executed.
You're all set !
Now commit, push then create a GitHub Release to generate your custom flavor !
Error running this command outside of git managed directory
❯ npx mega-linter-runner@9.6.0 --custom-flavor-setup --custom-flavor-linters BASH_SHELLCHECK,CLOJURE_CLJ_KONDO,CLOJURE_CLJSTYLE,DOCKERFILE_HADOLINT,MAKEFILE_CHECKMAKE,MARKDOWN_MARKDOWN_TABLE_FORMATTER,MARKDOWN_RUMDL,REPOSITORY_BETTERLEAKS,SPELL_LYCHEE,YAML_V8R
Need to install the following packages:
mega-linter-runner@9.6.0
Ok to proceed? (y) y
npm warn EBADENGINE Unsupported engine {
npm warn EBADENGINE package: 'which@7.0.0',
npm warn EBADENGINE required: { node: '^22.22.2 || ^24.15.0 || >=26.0.0' },
npm warn EBADENGINE current: { node: 'v24.13.1', npm: '11.8.0' }
npm warn EBADENGINE }
Yeoman generator used: /home/practicalli/.npm/_npx/e592646a0575050f/node_modules/mega-linter-runner/generators/mega-linter-custom-flavor
.:oool' ,looo;
.xNXNXl .dXNNXo.
lXXXX0c. 'oKXXN0;
.oKNXNX0kxdddddddoc,. .;lodddddddxk0XXXX0c
.:kKXXXXXXXXXXXXNXX0dllx0XXXXXXXXXXXXXXXKd,
.,cdkOOOOOOOO0KXXXXXXXXXXK0OOOOOOOkxo:'
'ckKXNNNXkc'
':::::;. .c0XX0l. .;::::;.
'xXXXXXx' :kx: ;OXXXXKd.
.dKNNXXO; .. :0XXXXKl.
.lKXXXX0: .lKXXXX0:
:0XXXXKl. .dXXXXXk,
;kXXXXKd:cxXXXXXx'
'xXNXXXXXXXXXKo.
.oKXXXXNXXX0l.
.lKNNXNNXO:
,looool'
==========================================================
============= MegaLinter, by OX Security =============
========= https://ox.security?ref=megalinter ===========
==========================================================
Welcome to the MegaLinter Custom Flavor generator !
When you don't know what option to select, please use default values
✖ An error occured while running mega-linter-runner:mega-linter-custom-flavor#prompting
file:///home/practicalli/.npm/_npx/e592646a0575050f/node_modules/simple-git/dist/esm/index.js:4399
return { error: new GitError(void 0, error.toString("utf-8")) };
^
GitError: fatal: not a git repository (or any of the parent directories): .git
at Object.action (file:///home/practicalli/.npm/_npx/e592646a0575050f/node_modules/simple-git/dist/esm/index.js:4399:25)
at PluginStore.exec (file:///home/practicalli/.npm/_npx/e592646a0575050f/node_modules/simple-git/dist/esm/index.js:4438:25)
at file:///home/practicalli/.npm/_npx/e592646a0575050f/node_modules/simple-git/dist/esm/index.js:1323:43
at new Promise (<anonymous>)
at GitExecutorChain.handleTaskData (file:///home/practicalli/.npm/_npx/e592646a0575050f/node_modules/simple-git/dist/esm/index.js:1321:16)
at GitExecutorChain.attemptRemoteTask (file:///home/practicalli/.npm/_npx/e592646a0575050f/node_modules/simple-git/dist/esm/index.js:1308:42)
at async GitExecutorChain.attemptTask (file:///home/practicalli/.npm/_npx/e592646a0575050f/node_modules/simple-git/dist/esm/index.js:1281:18) {
task: {
commands: [ 'remote', '-v' ],
format: 'utf-8',
parser: [Function: parseGetRemotesVerbose]
}
}
Node.js v24.13.1
Create a fine grained token in practicalli-johnny user account.
Use the token value to create an Org secret for actions, for use with specific repositories only (Megalinter custom flavor repositories)
Unauthorized Accessλ︎
A GitHub Org packages are set to private by default, so members can only create a private package.
Visit the Org Settings > Packages ... and allow members to create a public package: https://github.com/organizations/practicalli/settings/packages
New packages will use the visibility of the repository the package is generated from.
Copilot assistance
GitHub Copilot Chat Assistant — Solution
Failure summary
The job fails pulling the container image: "Error response from daemon: ... ghcr.io/.../manifests/latest: unauthorized".
Cause: the MegaLinter action references a GHCR image (ghcr.io/practicalli/megalinter-custom-flavor-clojure/megalinter-custom-flavor:latest) that the runner cannot pull anonymously (private or protected container).
Fix options (pick one)
Recommended — authenticate the runner to GHCR
Add packages: read permission so the GITHUB_TOKEN can access packages.
Add a Docker login step (docker/login-action) before the MegaLinter step so the runner can pull the private image.
Patch to .github/workflows/mega-linter.yml (insert these changes in the megalinter job):
Update the job permissions (replace the existing permissions block in the megalinter job): permissions: contents: write issues: write pull-requests: write packages: read
Add a login step immediately before the MegaLinter step (place it above the step that uses practicalli/megalinter-custom-flavor-clojure@main):
name: Log in to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
No other changes to the MegaLinter step are required.
Notes:
Using GITHUB_TOKEN requires the packages: read permission (as shown). If the image is in a different owner/org and cross-repo package access is restricted, GITHUB_TOKEN may not work — in that case create a Personal Access Token (with read:packages), store it as a secret (e.g. secrets.GHCR_PAT) and use that as the password in the login step (username: your-actor, password: ${{ secrets.GHCR_PAT }}). Prefer minimal scopes and be cautious about PAT use.
If you prefer not to authenticate, make the container image public in GHCR (Package settings → change visibility to public). That also resolves the unauthorized pull.
Alternative quick workaround
Use the standard published MegaLinter action instead of a private/custom GHCR image: uses: oxsecurity/megalinter@v9 This avoids needing GHCR access if the custom flavor image isn't required.
Where to edit
Edit .github/workflows/mega-linter.yml in this repository (commit/branch shown in the job logs: ref 8cfbe66c09260308b27187e8535c6735f1fb6e91). The workflow file is here: https://github.com/practicalli/clojure-through-code/blob/8cfbe66c09260308b27187e8535c6735f1fb6e91/.github/workflows/mega-linter.yml
If you want, I can produce a ready pull request patch (diff) that:
adds packages: read to the job permissions, and
inserts the docker/login-action step before the MegaLinter step.
Thank you.
