Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 19 additions & 24 deletions .claude/hooks/branch-guard.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
#!/usr/bin/env bash
# Branch guard — makes the target release line explicit before any code lands.
#
# DefectDojo ships from three long-lived branches (see the "Branch Check" section
# of AGENTS.md):
# bugfix -> next PATCH release (fastest timeline) <- bug fixes, regressions
# dev -> next MINOR release <- features, refactors
# master -> already released <- release / backport only
# DefectDojo has two long-lived branches (see the "Branch Check" section of
# AGENTS.md):
# dev -> next release, weekly patch or monthly minor <- all work: fixes, features
# master -> already released <- release tasks only
#
# A fix based on `dev` cannot ship until the next minor release, which is the most
# common way an urgent fix quietly misses the patch line. This hook reports the
# branch before work starts, and hard-blocks edits while on `master`.
# Every release is cut from `dev` and merged into `master`; there is no separate
# patch branch and no hotfix path off `master`. This hook reports the branch before
# work starts, and hard-blocks edits while on `master`.
#
# Three modes, all wired in .claude/settings.json:
# session SessionStart: report the branch and its release line into context.
Expand Down Expand Up @@ -54,13 +53,12 @@ g rev-parse --git-dir >/dev/null || exit 0 # not a checkout, nothing to guard

BRANCH="$(g symbolic-ref --quiet --short HEAD)"

# Release line: patch | minor | released | detached | unknown. Topic branches are
# classified by what they contain, not by their name — dev is checked first,
# because dev contains bugfix once bugfix has been merged forward.
# Release line: working | released | detached | unknown. Topic branches are
# classified by what they contain, not by their name: a branch that contains
# origin/dev is on the working line.
line_of() {
case "$BRANCH" in
bugfix) echo patch; return ;;
dev) echo minor; return ;;
dev) echo working; return ;;
master) echo released; return ;;
esac
if [ -z "$BRANCH" ]; then
Expand All @@ -71,9 +69,7 @@ line_of() {
return
fi
if g merge-base --is-ancestor origin/dev HEAD; then
echo minor
elif g merge-base --is-ancestor origin/bugfix HEAD; then
echo patch
echo working
else
echo unknown
fi
Expand All @@ -84,11 +80,10 @@ LINE="$(line_of)"
if [ "$MODE" = "session" ]; then
[ -z "$PY" ] && exit 0 # informational only; nothing to report without python3
case "$LINE" in
patch) DESC="ships in the next PATCH release (the fast line): bug fixes and regressions belong here, features do not" ;;
minor) DESC="ships in the next MINOR release: features and refactors belong here, and a BUG FIX based here will NOT ship until that minor release" ;;
released) DESC="is already-released code: nothing belongs here except a release or backport task, and edits are BLOCKED until a human confirms" ;;
working) DESC="is on the working line (dev) and ships in the next release, weekly patch or monthly minor: bug fixes and features both belong here" ;;
released) DESC="is already-released code: nothing belongs here except a release task, and edits are BLOCKED until a human confirms" ;;
detached) DESC="is a detached HEAD, so the release line is unclear" ;;
*) DESC="contains neither origin/bugfix nor origin/dev, so its base is stale or unmerged: run 'git fetch' and check the base before editing" ;;
*) DESC="does not contain origin/dev, so its base is stale or unmerged: run 'git fetch' and check the base before editing" ;;
esac
"$PY" -c '
import json, sys
Expand All @@ -98,7 +93,7 @@ print(json.dumps({"hookSpecificOutput": {
"additionalContext": (
"Branch check: this checkout is on branch " + branch + ", which " + desc + ". "
"State the branch and its release line back to the user before editing files. "
"If the task is a bug fix sitting on the minor line, say so and offer to move it onto bugfix first."
"All work targets dev. If the branch is not on dev (or a topic branch containing origin/dev), say so and offer to move the work onto origin/dev first."
),
}}))
' "${BRANCH:-detached HEAD}" "$DESC"
Expand All @@ -120,14 +115,14 @@ ACK_FILE="${ACK_DIR:-/nonexistent}/claude-branch-ack-${SESSION_ID}"

if [ "$MODE" = "commit" ]; then ACTION="Committing"; else ACTION="Editing files"; fi

REASON="BLOCKED: ${ACTION} is not allowed right now, because this checkout is on ${WHERE}, which holds already-released code. Bug fixes belong on \`bugfix\` (next patch release) and features on \`dev\` (next minor).
REASON="BLOCKED: ${ACTION} is not allowed right now, because this checkout is on ${WHERE}, which holds already-released code. All work, bug fixes and features alike, belongs on \`dev\` (the next release).

Do NOT retry, and do NOT work around this. Instead:
1. Tell the user the checkout is on master and the change is blocked.
2. Ask them to confirm this work is genuinely intended for master (a release or a backport), and WAIT for their reply.
2. Ask them to confirm this work is genuinely intended for master (a release task), and WAIT for their reply.
3. If they confirm, record it with: touch '${ACK_FILE}'
(that command prompts them for approval, which IS the confirmation) then continue.
4. If they do not confirm, move the work first: git switch -c <branch-name> origin/bugfix
4. If they do not confirm, move the work first: git switch -c <branch-name> origin/dev

The ack lasts for this session only."

Expand Down
6 changes: 3 additions & 3 deletions .claude/skills/defectdojo-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ templates, not a SPA) run via Docker Compose, with a Postgres DB and a Valkey br
the loop below against a **running local stack** — do not reason about behavior from the
code alone when you can exercise it.

Read `AGENTS.md` first for the branch/release-line policy: bug fixes target `bugfix`,
features target `dev`, and `master` is off-limits without explicit confirmation (the
Read `AGENTS.md` first for the branch/release-line policy: bug fixes and features both
target `dev`, and `master` is off-limits without explicit confirmation (the
`.claude/hooks/branch-guard.sh` hook enforces this). Put the work on the right branch
before editing.

Expand Down Expand Up @@ -153,7 +153,7 @@ A recurring PR category touches the Helm chart (`helm/defectdojo/`), nginx confi
still apply (security defaults, backward compatibility), but the checks are different:

- **The branch/release-line policy applies to chart and docker PRs too** — they are not
exempt. A fix still targets `bugfix`, a feature `dev`, never `master`. Defer to `AGENTS.md`.
exempt. Fixes and features both target `dev`, never `master`. Defer to `AGENTS.md`.
- **Know the three Helm CI jobs** (`.github/workflows/test-helm-chart.yml`) — each is an
automatic blocker when it fails:
- **`Lint chart (version)`** includes an **`artifacthub.io/changes` annotation check**: it
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/defectdojo-parser/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ table** — they drift and get rejected in review.

## Notes

- **New parser = a feature → targets `dev`**; a parser bugfix targets `bugfix`. Label the PR
- **New parsers and parser fixes both target `dev`.** Label the PR
`Import Scans`. Defer to `AGENTS.md` for the branch/milestone policy.
- **New API parsers from the community are currently not accepted** (supportability) — flag
this in review of an inbound API parser.
Expand Down
3 changes: 1 addition & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ Please update any documentation when needed in the [documentation folder](https:
This checklist is for your information.

- [ ] Make sure to rebase your PR against the very latest `dev`.
- [ ] Features/Changes should be submitted against the `dev`.
- [ ] Bugfixes should be submitted against the `bugfix` branch.
- [ ] Submit all PRs, features and bug fixes alike, against the `dev` branch.
- [ ] Give a meaningful name to your PR, as it may end up being used in the release notes.
- [ ] Your code is Ruff compliant (see [ruff.toml](../ruff.toml)).
- [ ] Your code is python 3.13 compliant.
Expand Down
9 changes: 4 additions & 5 deletions .github/workflows/ci-warm-caches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ name: "CI: Warm Caches"
# Without a workflow like this one, caching the migrated database would therefore
# do nothing at all for the first push of a new branch, which is most branches
# most of the time. Something has to save an entry into a scope pull requests are
# allowed to read, and only a run triggered from a release line can do that.
# allowed to read, and only a run triggered from the dev branch can do that.
#
# Note what is deliberately absent: a `schedule:` trigger. Scheduled workflows
# only ever run against the default branch, so a cron here would run with ref
# refs/heads/master and warm the master scope -- not the release-line scopes this
# is for. Eviction is handled instead by reads: GitHub drops an entry it has not
# seen used for 7 days, and every pull request based on a release line reads this
# refs/heads/master and warm the master scope -- not the dev scope this is
# for. Eviction is handled instead by reads: GitHub drops an entry it has not
# seen used for 7 days, and every pull request based on dev reads this
# one, which keeps it alive without a timer.
#
# Warming the default-branch scope is worth doing too, since every run can read
Expand All @@ -27,7 +27,6 @@ name: "CI: Warm Caches"
on:
push:
branches:
- bugfix
- dev
# Exactly the paths the snapshot key hashes -- anything else leaves the key
# unchanged, so there would be nothing to warm.
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/detect-merge-conflicts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ on:
branches:
- dev
- master
- bugfix
- release/*

pull_request_target:
types: [synchronize]

jobs:
main:
# Release PRs and merge-backs are merged without waiting for CI. Skip when the
# head is release/merge-* AND the PR has release-management.
if: ${{ !(startsWith(github.head_ref, 'release/merge-') && contains(github.event.pull_request.labels.*.name, 'release-management')) }}
runs-on: ubuntu-latest
steps:
- name: check if prs are conflicted
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ on:
- 'docs/**'
branches:
- master
- bugfix
- dev

# A master deploy and a dev deploy both publish to the gh-pages branch.
# Queue them instead of letting them race.
concurrency:
group: gh-pages-deploy
cancel-in-progress: false

# Taken from https://github.com/marketplace/actions/hugo-setup#%EF%B8%8F-workflow-for-autoprefixer-and-postcss-cli
# Both builds have to be one worflow as otherwise one publish will overwrite the other
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/migration-graph.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,21 @@ on:
branches:
- master
- dev
- bugfix
- release/**
- hotfix/**
pull_request:

jobs:
migration-graph:
# Release PRs and merge-backs are merged without waiting for CI. PR: a
# release/merge-* head AND the release-management label.
# Push: such a branch, or GitHub's merge commit for such a PR (a push has
# no labels, so the branch name is the only signal).
if: >-
${{ !(
(startsWith(github.event_name, 'pull_request') && startsWith(github.head_ref, 'release/merge-') && contains(github.event.pull_request.labels.*.name, 'release-management'))
|| (github.event_name == 'push' && (startsWith(github.ref_name, 'release/merge-') || (startsWith(github.event.head_commit.message, 'Merge pull request') && contains(github.event.head_commit.message, '/release/merge-'))))
) }}
name: Migration Graph Check
runs-on: ubuntu-latest
steps:
Expand Down
30 changes: 11 additions & 19 deletions .github/workflows/release-1-create-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ env:
on:
workflow_dispatch:
inputs:
# the actual branch that can be chosen on the UI is made irrelevant by further steps
# because someone will forget one day to change it.
# Every release (weekly patch and monthly minor) is cut from dev. The input is kept
# so existing callers that pass `-f from_branch=dev` keep working.
from_branch:
description: "Select branch to release from. Dev branch releases happen the first monday of the month. Otherwise, use bugfix."
description: "Branch to release from. All releases, weekly patches and monthly minors, come from dev."
required: true
type: choice
default: 'bugfix'
default: 'dev'
options:
- bugfix
- dev
release_number:
description: "Release version (x.y.z format)"
Expand All @@ -24,17 +23,11 @@ jobs:
create_pr:
runs-on: ubuntu-latest
steps:
- name: Validate proper bugfix branch release_number format is being used
if: ${{ inputs.from_branch == 'bugfix' }}
- name: Validate release_number format
run: |
# Expect a valid x.y.z release_number with a 1-3 digit last octet
echo "${{ inputs.release_number }}" | grep "^[0-9]*\.[0-9]*\.[0-9]\{1,3\}$"

- name: Validate proper dev branch release_number format is being used
if: ${{ inputs.from_branch == 'dev' }}
run: |
# Expect the last octet in release_number to not be 1-9
echo "${{ inputs.release_number }}" | grep "^[0-9]*\.[0-9]*\.0$"
# Expect x.y.z with a 1-3 digit last octet: x.y.0 for a monthly minor,
# x.y.100 / x.y.200 / ... for a weekly patch
echo "${{ inputs.release_number }}" | grep -E '^[0-9]+\.[0-9]+\.[0-9]{1,3}$'

- id: Set-GitHub-org
run: echo "GITHUB_ORG=${GITHUB_REPOSITORY%%/*}" >> $GITHUB_ENV
Expand All @@ -46,15 +39,14 @@ jobs:

- name: Create release branch
run: |
echo "NEW_BRANCH=release/${{ inputs.release_number }}" >> $GITHUB_ENV
echo "NEW_BRANCH=release/merge-dev-into-master-${{ inputs.release_number }}" >> $GITHUB_ENV

- name: Configure git
run: |
git config --global user.name "${{ env.GIT_USERNAME }}"
git config --global user.email "${{ env.GIT_EMAIL }}"

- name: Push branch
if: "!startsWith('${{ inputs.from_branch }}', 'release/')"
run: git push origin HEAD:${NEW_BRANCH}

- name: Checkout release branch
Expand All @@ -69,11 +61,11 @@ jobs:
sed -ri 's/appVersion: ".*"/appVersion: "${{ inputs.release_number }}"/' helm/defectdojo/Chart.yaml

if grep "\-dev" helm/defectdojo/Chart.yaml; then
echo "x.y.z-dev found in Chart.yaml, probably releasing a new minor version"
echo "x.y.z-dev found in Chart.yaml (the normal case for a release from dev)"
echo "removing the -dev suffix"
sed -e "s/\-dev//" -i helm/defectdojo/Chart.yaml
else
echo "x.y.z without -dev found in Chart.yaml, probably releasing a new bug fix version"
echo "x.y.z without -dev found in Chart.yaml: the chart was already stripped, probably a manual re-release"
CURRENT_CHART_VERSION=$(grep -oP 'version: (\K\S*)?' helm/defectdojo/Chart.yaml | head -1)
NEW_CHART_VERSION=$(echo "version: $CURRENT_CHART_VERSION" | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{$NF=sprintf("%0*d", length($NF), ($NF+1)); print}')
echo "bumping the chart version from $CURRENT_CHART_VERSION to $NEW_CHART_VERSION"
Expand Down
84 changes: 1 addition & 83 deletions .github/workflows/release-3-master-into-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

- name: Create merge back branch
run: |
echo "NEW_BRANCH=master-into-dev/${{ inputs.release_number_new }}-${{ inputs.release_number_dev }}" >> $GITHUB_ENV
echo "NEW_BRANCH=release/merge-master-into-dev-${{ inputs.release_number_new }}" >> $GITHUB_ENV

- name: Configure git
run: |
Expand Down Expand Up @@ -113,85 +113,3 @@ jobs:
issue_number: pr.data.number,
labels: ['release-management']
})

create_pr_for_merge_back_into_bugfix:
runs-on: ubuntu-latest
steps:
- id: Set-GitHub-org
run: echo "GITHUB_ORG=${GITHUB_REPOSITORY%%/*}" >> $GITHUB_ENV

- name: Checkout master
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: master

- name: Create merge back branch
run: |
echo "NEW_BRANCH=master-into-bugfix/${{ inputs.release_number_new }}-${{ inputs.release_number_dev }}" >> $GITHUB_ENV

- name: Configure git
run: |
git config --global user.name "${{ env.GIT_USERNAME }}"
git config --global user.email "${{ env.GIT_EMAIL }}"

- name: Push new branch
run: git push origin HEAD:${NEW_BRANCH}

- name: Checkout new branch
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ env.NEW_BRANCH }}

- name: Update version numbers in key files
run: |
sed -ri "s/__version__ = '.*'/__version__ = '${{ inputs.release_number_dev }}'/" dojo/__init__.py
sed -ri "s/appVersion: \".*\"/appVersion: \"${{ inputs.release_number_dev }}\"/" helm/defectdojo/Chart.yaml
sed -ri "s/\"version\": \".*\"/\"version\": \"${{ inputs.release_number_dev }}\"/" components/package.json
CURRENT_CHART_VERSION=$(grep -oP 'version: (\K\S*)?' helm/defectdojo/Chart.yaml | head -1)
sed -ri "0,/version/s/version: \S+/$(echo "version: $CURRENT_CHART_VERSION" | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{$NF=sprintf("%0*d", length($NF), ($NF+1)); print}')-dev/" helm/defectdojo/Chart.yaml

- name: Check numbers
run: |
grep version dojo/__init__.py
grep appVersion helm/defectdojo/Chart.yaml
grep version components/package.json

- name: Update values in HELM chart
run: |
yq -i '.annotations = {}' helm/defectdojo/Chart.yaml
yq -i '.annotations."artifacthub.io/prerelease" = "true"' helm/defectdojo/Chart.yaml
yq -i '.annotations."artifacthub.io/changes" = ""' helm/defectdojo/Chart.yaml

- name: Run helm-docs
uses: losisin/helm-docs-github-action@9e0787c426fdec38be6288bb5c0559dcb388412c # v2
with:
chart-search-root: "helm/defectdojo"

- name: Push version changes
uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0
with:
commit_user_name: "${{ env.GIT_USERNAME }}"
commit_user_email: "${{ env.GIT_EMAIL }}"
commit_author: "${{ env.GIT_USERNAME }} <${{ env.GIT_EMAIL }}>"
commit_message: "Update versions in application files"
branch: ${{ env.NEW_BRANCH }}

- name: Create Pull Request
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const pr = await github.rest.pulls.create({
owner: '${{ env.GITHUB_ORG }}',
repo: 'django-DefectDojo',
title: 'Release: Merge back ${{ inputs.release_number_new }} into bugfix from: ${{ env.NEW_BRANCH }}',
body: `Release triggered by \`${ process.env.GITHUB_ACTOR }\``,
head: '${{ env.NEW_BRANCH }}',
base: 'bugfix'
})
await github.rest.issues.addLabels({
owner: '${{ env.GITHUB_ORG }}',
repo: 'django-DefectDojo',
issue_number: pr.data.number,
labels: ['release-management']
})
6 changes: 3 additions & 3 deletions .github/workflows/release_drafter_valentijn.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ on:
description: |
Semver range limiting which past releases may be picked as the previous release,
i.e. where the changeset starts. Evaluated per tag with node's semver.satisfies().
For a minor-to-minor changeset, pass the previous minor tag exactly, e.g. '3.1.0'.
For changes since the last patch of the previous minor, pass a range, e.g. '>=3.1.0 <3.2.0'.
Leave empty to start from the most recent release.
Every release, patch or minor, is cut from dev, so the previous release tag is
the natural start: leave this empty for the normal case.
Set it only to start somewhere else, e.g. '3.1.0' for a changeset since 3.1.0.
required: false
dry-run:
description: |
Expand Down
Loading
Loading