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
1 change: 1 addition & 0 deletions .changes/PRODENG-3931.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`metadata.name` now rejects names that contain `/` or `\`, names that are exactly `.` or `..`, and names with leading or trailing whitespace. Dotted names such as `prod.eu` are unaffected. Configs using a rejected name stop applying on upgrade. Rejection was chosen over silent rewriting because a rewritten name silently relocates Launchpad's internal storage paths.
70 changes: 70 additions & 0 deletions .changes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Release note fragments

One file per change that an operator needs to know about before upgrading.
`.github/workflows/release.yml` assembles every fragment in this directory into
the GitHub release body when a tag is pushed.

## When to add one

Add a fragment when the change alters behaviour an existing configuration or
workflow depends on: a validation rule that rejects input that previously
applied, a default that changes, a flag or field that is removed or renamed, or
anything that makes a documented example stop working.

Do not add one for an internal refactor, a test change, or a fix that makes the
product match its existing documentation.

## Format

Name the file after the ticket, `PRODENG-NNNN.md`. Fragments are emitted into
the release body **verbatim**, in filename order, separated by a blank line.
The fragment owns its own markdown: no list marker or heading is added around
it. A trailing newline is not required: the workflow emits the separating
blank line whether or not the file ends with one.

A short note can be a single paragraph. A note that needs more — what breaks,
what to do about it — may run to several paragraphs; start it with a bold
one-line summary so the release body is scannable. Avoid headings (`#`), which
would compete with the `## Release notes` heading and with GitHub's generated
sections below it.

State what changed, what it breaks, and **why it was done that way**. The reason
is the part that stops an operator trying to work around the change.

```markdown
`metadata.name` now rejects names that contain `/` or `\`, names that are
exactly `.` or `..`, and names with leading or trailing whitespace. Dotted
names such as `prod.eu` are unaffected. Configs using a rejected name stop
applying on upgrade. Rejection was chosen over silent rewriting because a
rewritten name silently relocates Launchpad's internal storage paths.
```

State the rule precisely. "Names containing `.`" and "names that are exactly
`.`" are different claims, and the broader one tells operators their working
configuration is about to break when it is not.

```markdown
**Breaking: one-line summary of what changed.**

What now fails, with the exact error text an operator will see.

Why this shape was chosen over the alternative.
```

## After a release

Fragments are not deleted automatically. Remove the ones covered by a release
when cutting the next one, so the notes describe that release rather than
accumulating.

## If there are none

The release body says so explicitly:

```
_No release notes were recorded for this release._
```

That line exists so that a release nobody wrote a fragment for is
distinguishable from a release with genuinely nothing to report. An empty
section would read as the latter while meaning the former.
39 changes: 39 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,55 @@ jobs:
permissions:
contents: write
steps:
- name: Checkout code
# The release notes are assembled from .changes/ in the repository, so
# this job needs the tree as well as the built artifacts.
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false

- name: Download binaries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: launchpad-release-binaries
path: dist/

- name: Assemble release notes
run: |
set -euo pipefail
notes="$RUNNER_TEMP/release-notes.md"
printf '## Release notes\n\n' > "$notes"
count=0
for fragment in .changes/PRODENG-*.md; do
[ -e "$fragment" ] || continue
# Emitted verbatim rather than prefixed with a list marker: a note
# may run to several paragraphs, and continuation lines under a
# bullet would need indenting to stay in the list. The fragment
# owns its own markdown.
cat "$fragment" >> "$notes"
# Two newlines, not one: a fragment that does not end with a
# newline would otherwise run into the next one and Markdown would
# render the pair as a single paragraph. A fragment ending with a
# newline gains a blank line, which Markdown collapses.
printf '\n\n' >> "$notes"
count=$((count + 1))
done
if [ "$count" -eq 0 ]; then
# Stated rather than left blank. An empty section reads as "nothing
# notable changed"; this release may instead be one where nobody
# wrote a fragment. The two must not look the same.
printf '_No release notes were recorded for this release._\n' >> "$notes"
fi
echo "Assembled $count fragment(s):"
cat "$notes"

- name: Create GitHub Release
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3
with:
files: dist/*
# The assembled fragments become the body; GitHub's generated notes
# (commits and contributors) are appended below them.
body_path: ${{ runner.temp }}/release-notes.md
generate_release_notes: true
draft: true
env:
Expand Down
Loading