Skip to content

release: restore changelog bodies and add a release dry run - #1115

Merged
Starosdev merged 4 commits into
masterfrom
develop
Nov 5, 2026
Merged

Starosdev merged 4 commits into
masterfrom
develop

Conversation

@Starosdev

Copy link
Copy Markdown
Owner

Product changes

Summary

Release notes and CHANGELOG.md record what shipped again. Since 1.69.0 every release wrote only a version heading, so twenty-eight releases left no record of their own fixes, including the replacement-risk work in 1.79.1. The conventional-commits preset had been pinned two majors ahead of the renderer that consumes it, which produced a heading and no body while the release itself still succeeded.

The Release workflow gains a dry_run input that resolves the next version and renders its notes without tagging, committing or publishing, and fails when those notes come back without commit entries. Run it from master after a promotion merges and before cutting the release.

Reason

CHANGELOG.md is the repository's own record of what each version contained, and it has been empty of content since 2026-07-30. The regression was silent because commit analysis never broke: semantic-release parsed every commit, classified it, and published the release, so only the rendered body was lost. Nothing in the pipeline looked at that body, which is why it survived twenty-eight releases.

Validation

  • CI green on both promoted pull requests: Test Backend, Lint Backend (Go), Test Frontend, and the full build matrix.
  • The preset generation mismatch was verified at source level rather than inferred. Preset 10.x returns template functions built on @conventional-changelog/template, while conventional-changelog-writer@8.4.0, which the lockfile resolves, compiles mainTemplate as a Handlebars string and has no template key. Preset 9.3.1 exports the strings that writer expects and honours the hidden and section keys .releaserc.json sets.
  • The empty-notes discriminator was checked against history rather than assumed: all 28 release commit bodies in the regression window contain zero rendered commit entries, and every release from 1.0.0 through 1.68.0 contains at least one.
  • Three review rounds on fix(release): restore changelog bodies and add a release dry run #1113, each scoped to the commit the previous round produced. Round two found the first guard tested for empty notes when the regression renders a heading, so it would not have fired against its own target; round three confirmed the corrected guard and cleared every exit path.

Not yet validated by execution: no dry run has been performed, because master has rested at its own chore(release) commit with nothing releasable after it. Running dry_run from master once this merges is the settling check, and it fails loudly if 9.3.1 is still wrong.

Follow-up

  • Run Release with dry_run from master before cutting the next release, and confirm the job summary lists the promoted fixes as bullets.
  • Backfill the empty CHANGELOG.md entry for 1.79.1 once the generator is confirmed, so the backfill is written once against a working pipeline.
  • A release whose only releasable commits are reverts would still render heading-only notes, because presetConfig.types omits revert. Pre-existing, worth its own issue.

Promoted pull requests:

RyoKusnadi and others added 3 commits November 4, 2026 23:18
…1104)

## Summary

Follow-up to #1085 and #1086, as @Starosdev asked in the approval
comment. The report's alert line now takes its ATA value from
`measurements.AttributeEvaluatedValue`, and the extra exported
`SmartAtaAttribute.ThresholdValue` is removed. No change in what the
report prints.

**Problem:** None visible to users. This is a cleanup. Until #1086
landed, the report code had its own copy of "which ATA field does the
status use", through an exported method added only for it.

**How it happens:** Not a bug. It was a stop-gap, noted in a comment in
`alertAttributeDetails`.

**Cause:** #1085 merged before `AttributeEvaluatedValue` existed, so it
called `ThresholdValue`, which gives the same result.

## Type of Change

- [x] Refactoring (no functional changes)

## Related Issues

Follow-up to #1085 and #1086. No issue.

## Changes Made

- `generator.go`: for an ATA attribute with metadata, `value` comes from
`AttributeEvaluatedValue`. The ATA-without-metadata rule (normalized
value, as the detail page shows) stays, as the helper would give the raw
value there. Comment updated.
- `smart_ata_attribute.go`: removed `ThresholdValue`. Nothing else
called it (searched the repo). The private `thresholdValue` stays.

**Scope:** The nullable threshold is a separate PR (#1103). It touches
nearby lines in `alertAttributeDetails`, so the second to merge may need
a small rebase.

## Testing

- [x] I have tested this locally
- [x] I have added/updated tests that prove my fix/feature works

Added `TestAlertAttributeDetails_ATAValueMatchesEvaluatedValue`: for
every ATA attribute with metadata, the alert value equals
`AttributeEvaluatedValue`. As this is a refactor it passes before and
after; the existing `TestAlertAttributeDetails` cases pass unchanged.

Commands: `go vet` and `go test` on `webapp/backend/pkg/reports` and
`webapp/backend/pkg/models/measurements` (pass), `gofmt -l` on the
changed files (clean). `go vet` also on `pkg/notify` and `pkg/web/...`.
I did not run the full suite.

Signed-off-by: Ryo Kusnadi <ryokusnadi@gmail.com>
## Summary

Restores `CHANGELOG.md` bodies by pinning the conventional-commits
preset back to the generation the notes renderer understands, and adds a
`dry_run` input so the next release-tooling change can be verified
before it ships.

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Refactoring (no functional changes)
- [x] CI/CD or infrastructure change

## Related Issues

Fixes #1112

## Changes Made

**The defect.** `CHANGELOG.md` has held nothing but version headings
since **1.69.0**, 28 releases ago. The `chore(release)` commit bodies
are empty for the same reason: `@semantic-release/git` writes
`${nextRelease.notes}`, and those notes were empty. The v1.79.1 entry is
two lines, while its commit range held nine `fix` commits across
replacement risk, SMART thresholds, notifications, reports and the
dashboard.

**The cause.** `50948ce6` ("ci(release): run semantic-release from
locked toolchain", 2026-08-13 22:44 UTC) replaced
`cycjimmy/semantic-release-action@v6`, which installed plugins unpinned,
with a local `npm ci` pinning
`conventional-changelog-conventionalcommits` to an exact `10.0.0`.
Version 1.69.0 was tagged at 22:55 UTC, eleven minutes later, and is the
first empty entry.

The preset is two majors ahead of the renderer.
`@semantic-release/release-notes-generator@14.1.1` depends on
`conventional-changelog-writer ^8`, `conventional-commits-parser ^6` and
`conventional-changelog-angular ^8`. Presets 8.x and 9.x belong to that
generation and depend on `compare-func`; 10.x was rewritten onto
`@conventional-changelog/template`. Pinned to **9.3.1**, the newest
release of the matching generation.

Commit analysis was never affected, which is why this stayed quiet for
three months. The v1.79.1 run logs all 17 commits parsed, each
`fix(...)` as "The release type for the commit is patch", and "Published
release 1.79.1". Only the rendered notes were empty.

This is **not** the release-notes timeout from #1100. In that same run
`@semantic-release/changelog` wrote the file at 22:11:21 and
`generate-release-notes.sh` timed out at 22:12:02, forty seconds later.
Two independent defects; #1101 fixed the binaries half.

**The guard.** New `dry_run` workflow input:

- Skips the Docker image matrix and validation, so a dry run reaches the
release job in about a minute rather than after the Buildx matrix.
- Runs semantic-release with `dryRun`, so no tag, no release commit, no
publish.
- Prints the notes it would write to the job summary.
- **Fails when a version resolves with an empty notes body**, which is
precisely the state that produced 28 empty headings.
- Reports the run as unpublished, so the build and upload jobs stay
skipped, and is rejected when combined with `rebuild_tag`.

The force-release-commit step is additionally guarded: it pushes to the
release branch and must never fire in a dry run.

## Testing

- [ ] I have tested this locally
- [x] I have added/updated tests that prove my fix/feature works
- [ ] All existing tests pass

No Go or frontend code changes, so the repository's test suites do not
cover this. The verification is the `dry_run` lane itself, which is the
point of the change. `npm install --package-lock-only` regenerated the
lock; `node --check` validates the script; the workflow YAML parses.

**Validation plan after merge**, before the next release: run Release
with `dry_run` checked against `master`. Expect a resolved version and a
notes body listing the `fix` commits since the last tag. If the body is
empty the job now fails loudly instead of publishing another bare
heading.

Backfilling the 1.79.1 changelog entry is deliberately not in this PR;
it follows once the generator is confirmed working, so the backfill is
written once rather than repeated.

## Checklist

- [x] My code follows the project's code style
- [x] I have performed a self-review of my code
- [x] I have commented my code where necessary (particularly complex
areas)
- [x] I have updated the documentation if needed
- [x] My changes generate no new warnings
- [x] No console.log, debug statements, or commented-out code

`docs/DEPLOYMENTS.md` documents the preset-generation constraint and the
`dry_run` lane.

## Additional Notes

Review: required (changes the release workflow, which takes effect on
merge for every future release and can publish or fail to publish
artifacts).
Promotion PR #1115 reports CONFLICTING: master carries #1107, which counts
warning devices from active failures, and develop carries #1104, which moves
ATA alert values onto AttributeEvaluatedValue. Both appended a test function to
the end of webapp/backend/pkg/reports/generator_test.go, so the conflict is
positional rather than semantic.

Resolution keeps both tests. The two sides shared the file's trailing brace, so
concatenating them left the first function unterminated; gofmt caught that and
the brace is restored. generator.go merged without conflict and keeps master's
warning-count change.
@Starosdev Starosdev mentioned this pull request Nov 5, 2026
8 of 15 tasks
@Starosdev
Starosdev merged commit 8c5fc00 into master Nov 5, 2026
50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants