oxsecurity/megalinter

prepare-release

Prepare a MegaLinter release — update CHANGELOG (prune empty sections, collapse linter versions, backfill PR numbers), run the release build, push commit and tag, and guide GitHub release creation.

First seen Aug 3, 2026

Installation

$ npx skills add oxsecurity/megalinter --skill prepare-release

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from oxsecurity/megalinter · top by installs.

npx skills add oxsecurity/megalinter

Browse all from oxsecurity/megalinter

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 2.6K
License LICENSE
Default branch main
Open issues 25
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Allowed toolsBash Read Grep Glob Edit Write AskUserQuestion
Declared agents claude-code
More metadata
internal
1

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 13,811 B
  • docs SUMMARY.md 222 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 3 installs

SKILL.md

Prepare a MegaLinter release.

Step 1 — Resolve the version

Parse $ARGUMENTS. Validate against ^v[0-9]+\.[0-9]+\.[0-9]+$. If absent or invalid:

AskUserQuestion: "What semver version should this release have? (format: vX.Y.Z)"

Call the resolved value RELEASE_VERSION (e.g. v9.6.0).

Step 1b — Major release only: bump the default major version

Skip this step unless RELEASE_VERSION is a new major (vN.0.0 with N greater than the major of the latest existing tag). For a major release, the moving major tag (vN) and every "current version" reference must be bumped from the previous major (vP) to vN before the release build. This was done for v9 in PRs #6144/#6197 and for v10 in the v10.0.0 release commit.

The authoritative checklist is the MAJOR-RELEASE-IMPACTED markers left in the sources:

grep -rn "MAJOR-RELEASE-IMPACTED" . --include='*.py' --include='*.js' --include='*.yml' --include='*.md'

Fix every marked spot, then sweep for leftovers (grep -rn 'megalinter[@:]vP\b\|tag: vP\b\|DEFAULTRELEASE' excluding docs/, CHANGELOG.md, nodemodules, .claude/worktrees, megalinter-reports, linter-helps.json). Known spots (as of v10):

  • Core defaults: megalinter/constants.py (DEFAULTRELEASE), mega-linter-runner/lib/config.js (DEFAULTRELEASE).
  • Runner upgrade rules: add a new "VP to VN migration rules" block at the end of getUpgradeItems() in mega-linter-runner/lib/upgrade.js, mirroring the previous major's 8 rules (flavors @vP/@vP.x, docker flavors :vP/:vP.x, main action @vP/@vP.x, main image :vP/:vP.x). The bare oxsecurity/... regexes also match ghcr.io/-prefixed references and keep the prefix. Verify with npx mocha test/megalinter-upgrade.test.js (each rule has test/testRes self-checks).
  • Release workflows: deploy-RELEASE.yml, deploy-RELEASE-flavors.yml, deploy-RELEASE-linters.yml (moving :vP image tags, type=raw,value=vP, including the commented Docker-Hub mirror blocks), mirror-docker-image.yml (the contains(...,'vP') guard). Do not touch actions/github-script@... # v9-style pin comments — that's the github-script action's own version.
  • Docs source of truth: README.md — all @vP / :vP / tag: vP current-version references. Keep historical version mentions as-is (e.g. the Docker-Hub registry-freeze note's "since v9.5.0" / "frozen at v9.4.0"), but reword sentences that would become nonsensical with the new tag. All docs/install-*.md and docs/quick-start.md are split from README by the doc build — do not edit them, they regenerate. docs/reporters/AzureCommentReporter.md is standalone: edit it directly.
  • Other standalone files: SECURITY.md (supported versions), TEMPLATES/mega-linter.yml (uses: oxsecurity/megalinter@vP), .automation/test/yamlschema/good/actiongood_*.yml fixtures, skills/megalinter-setup/SKILL.md example tags, mega-linter-runner/lib/options.js help-text examples.
  • Config schema: megalinter/descriptors/schemas/megalinter-configuration.jsonschema.json — the MEGALINTERVERSION property's description example and examples array. Then regenerate the runner copy: python .automation/generaterunner_vars.py (also re-run automatically by the build).
  • Runner tests: tests must reference DEFAULT_RELEASE imported from ../lib/config.js, never a hardcoded major (fix any that regressed). Run cd mega-linter-runner && npx mocha "test/**/*.test.js" — the Docker-based (Module) run on own code base test may fail locally on Windows (MSYS mount mangling), rely on CI for it.

Auto-handled by the release build — never edit manually: root action.yml and flavors/*/action.yml (stamped with the exact RELEASE_TAG by build.py when --release is used), docs/ pages split from README, mega-linter-runner/lib/megalinter-vars.json.

When touching a new file that carries the moving major version, add a MAJOR-RELEASE-IMPACTED comment marker next to it so the next major release finds it.

Finally, add a CHANGELOG entry for the switch itself (mirroring past majors), e.g. under mega-linter-runner: --upgrade now migrates vP references to vN, and --install defaults to vN.

Step 2 — Rewrite CHANGELOG.md

The mechanical transformation is done by the bundled helper script .claude/skills/prepare-release/prepare_changelog.py (stdlib only, no venv needed). It:

  • converts the ## [Unreleased] block into a dated ## [RELEASE_VERSION] - YYYY-MM-DD entry;
  • prunes empty sections from the release entry;
  • collapses the linter-version bumps to one line per linter, alphabetically sorted, using the chronological first-seen from → last-seen to range with no date (this is intentional: it stays correct across renumberings, e.g. cfn-lint 3.14 → 1.52.0, where a naive semver-min/max would print a backwards range);
  • strips <!-- linter-versions-end --> from the release entry;
  • prepends a fresh empty ## [Unreleased] block that holds the sole marker and keeps the repo's (N) placeholder on the linter-versions header.

PR-number backfill is the one judgment part, so the script splits it into analyze (lists the lines that need a PR) and apply (consumes your decisions).

2a — List the lines that need a PR number

python .claude/skills/prepare-release/prepare_changelog.py analyze

This prints a JSON array of candidates {id, section, text} — every content line (- …) in a non-version section that does not already carry a MegaLinter reference. A line counts as already-referenced only if it has (#N), a bare #N after a space/paren, or an oxsecurity/megalinter URL. A line that merely links an external repo/issue (e.g. the linter's own GitHub page) is not considered referenced and will appear as a candidate.

2b — Decide the PR(s) for each candidate

Build the commit map for the release window:

PREV_TAG="$(git tag --sort=-creatordate | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)"
git log "$PREV_TAG"..HEAD --oneline --no-merges

For each candidate, extract distinctive keywords (linter name, descriptor ID, quoted identifier) and find the PR:

  • grep -i "KEYWORD" against the commit map (squash-merge subjects end in (#NNNN));
  • for terms not in commit subjects, gh pr list --state merged --search "KEYWORD in:title" --json number,title --limit 5.

Write the confident matches to a JSON file, keyed by candidate id, value = PR number(s) (comma-separated for multiple):

{ "0": "8216", "1": "8216", "5": "7907", "12": "8133,8134" }

Leave a candidate out of the JSON when no PR is found, the match is ambiguous, or the matching commit has no PR number. Unmatched lines are simply left as-is — do not report them or ask the user about them.

2c — Apply the transformation

python .claude/skills/prepare-release/prepare_changelog.py apply \
  --version RELEASE_VERSION --date "$(date +%F)" --prs /path/to/prs.json

This rewrites CHANGELOG.md in place. (For a dry run, add --out /some/tmp/path to write elsewhere and leave CHANGELOG.md untouched.)

2d — Editorial pass for end users

Reread the release entry against .claude/rules/changelog.md: the CHANGELOG is release notes for end users, not maintainers. In user-facing sections, rewrite entries that lead with implementation details so they lead with the user benefit or required action, and move internal/technical entries (refactors, test suite, repo CI, build tooling) under the Dev/CI sections — technical detail is fine there. Keep before/after tables for performance and size improvements (user-visible measures in the user-facing sections, CI job timings in the CI section). Check readability: key words/expressions in bold for scanning, and long entries split into a short lead line + sub-bullets rather than long sentences. The rewrites will appear in the Step 3 diff review.

Step 3 — Confirm the CHANGELOG

Show the diff:

git diff CHANGELOG.md

Ask:

AskUserQuestion: "Is the updated CHANGELOG correct? Proceed to create the release?"

Options: Yes, proceed / No, let me edit it first

If No: stop. The user edits CHANGELOG.md manually, then re-invokes /prepare-release RELEASE_VERSION.

Step 4 — Confirm deploy-BETA-linters was run

Ask:

AskUserQuestion: "Did you manually run the deploy-BETA-linters GitHub Actions workflow and confirm it completed successfully? The release workflow reuses the BETA linter images — the release will be incomplete if those images have not been built."

Options: Yes, it completed successfully / No / not sure

If No / not sure: stop. Guide the user:

  1. Trigger: gh workflow run deploy-BETA-linters.yml (or GitHub UI → Actions → Deploy BETA linters → Run workflow).
  2. Wait for it to finish green.
  3. Re-invoke /prepare-release RELEASE_VERSION.

Step 5 — Run the release build (manual, in a separate terminal)

Do not run this build yourself. It regenerates all documentation and Dockerfiles and can take several minutes — run it in a dedicated terminal so its output stays visible and interruptible.

Tell the user to open another command line at the repo root and run:

make megalinter-release RELEASE_VERSION=RELEASE_VERSION

The Makefile activates the venv automatically. This runs:

  • build.sh --doc --version RELEASE_VERSION — regenerates docs/Dockerfiles stamped with the release version.
  • build.sh --release RELEASEVERSION — stages all changed files, creates commit "Release MegaLinter RELEASEVERSION", and creates a git tag RELEASE_VERSION.

Do not run make megalinter-build-with-doc separately.

Then ask:

AskUserQuestion: "Has make megalinter-release RELEASEVERSION=RELEASEVERSION finished successfully in your other terminal?"

Options: Yes, it completed / No, it failed

If No, it failed: ask the user to paste the error output, then help diagnose and stop until it succeeds. Once it succeeds, continue to Step 6.

Step 6 — Confirm before pushing

Show what will be pushed:

git log -1 --oneline
git tag --sort=-creatordate | head -1

Ask:

AskUserQuestion: "Release commit and tag are ready. Push to origin now?"

Options: Yes, push / No, I'll push manually

If Yes:

BRANCH="$(git branch --show-current)"
git push origin "$BRANCH"
git push origin RELEASE_VERSION

Main-push exception: pushing to main is the one authorized exception to the global "never push to main" rule in CLAUDE.md. MegaLinter releases are always cut from main, and the release commit must land there. This skill explicitly owns this exception.

Step 7 — Guide GitHub release creation

Walk the user through the GitHub UI. Match the structure of the previous releases (look at gh release view <PREV_TAG> --repo oxsecurity/megalinter --json body if unsure):

  1. Open: https://github.com/oxsecurity/megalinter/releases/new?tag=RELEASE_VERSION
  2. Set the release title to RELEASE_VERSION.
  3. Tick "Set as the latest release".
  4. Click "Generate release notes". GitHub fills the body with ## What's Changed (an auto list of every merged PR), ## New Contributors, and a Full Changelog compare link. Keep all of this — then make the manual edits below.

Manual edits after "Generate release notes"

a. Curated summary at the top. Directly under the ## What's Changed heading (above the auto-generated PR bullet list), paste the release entry the skill just wrote to CHANGELOG.md (the ## [RELEASEVERSION] - DATE block — its section bullets and the collapsed linter-versions list), dropping the ## [RELEASEVERSION] header line itself. This puts the human-readable highlights above the raw PR dump.

b. Announcement call-to-action line. If an announcement issue exists for this release, add it as the first line under ## What's Changed, matching previous releases:

[**Take 2 mn to read MegaLinter RELEASE_VERSION announcements**](https://github.com/oxsecurity/megalinter/issues/<ANNOUNCEMENT_ISSUE>)

c. OX Security banner + GitHub-star call to action. Just above the Full Changelog: line at the very bottom, add the OX Security banner (present on every release) followed by a star CTA:

[![MegaLinter is provided by OX Security](https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/images/ox-banner.png)](https://www.ox.security/?ref=megalinter)

⭐ If MegaLinter is useful to you, please [give it a star on GitHub](https://github.com/oxsecurity/megalinter/stargazers) — it helps the project a lot!

d. (Optional) Social share. Some past releases add a [Share the news on LinkedIn :)](<post-url>) line near the bottom. Add one only if the user has a post URL to link.

  1. Review the rendered preview, then click "Publish release".

Step 8 — Remind about pending workflows

Tell the user:

  • The push and tag trigger deploy-RELEASE, deploy-RELEASE-linters, and deploy-RELEASE-flavors workflows.
  • Go to GitHub → Actions and approve any runs awaiting manual approval.
  • Monitor those three workflows until they all complete green before announcing the release publicly.