Skip to content

chore(release): prepare the package for a rosdistro release - #73

Open
benaliabderrahmane wants to merge 3 commits into
mainfrom
chore/rosdistro-release-prep
Open

benaliabderrahmane wants to merge 3 commits into
mainfrom
chore/rosdistro-release-prep

Conversation

@benaliabderrahmane

Copy link
Copy Markdown
Owner

Makes the package releasable through bloom into ros/rosdistro, so users can
apt install ros-<distro>-rmw-unix-socket-cpp. Release metadata only — no
source files are touched.

What was blocking

CHANGELOG.rst in the wrong place and the wrong format. bloom and
catkin_pkg read CHANGELOG.rst from next to package.xml and nowhere else,
so the root CHANGELOG.md was invisible to catkin_generate_changelog and the
release would have shipped an empty debian changelog. Moved to
rmw_unix_socket_cpp/CHANGELOG.rst and converted — one changelog, not two to
drift apart. All 64 bullets and 17 category headings carried over.

Two rst constraints shaped the conversion, both found by parsing the result
rather than by eyeballing it:

  • REP-132 allows only paragraphs, lists and transitions under a version
    heading. catkin_pkg drops sub-sections silently — written with Added
    / Fixed as rst sub-sections, 0.4.0 and 0.4.1 parsed to a completely
    empty body and 0.5.0 kept only its two intro paragraphs. They are bold
    paragraphs instead.
  • rst has no nested inline markup, so **``rmw_wait`` timeout contract**
    rendered the backticks literally in 7 bold spans. docutils emits no warning
    for this.

No <url> tags. Added repository, bugtracker and website. ROS Index
renders them and rosdistro reviewers look for them.

Release tag format. Our tags are v0.5.0; catkin_prepare_release writes
0.5.0. The bloom track needs v:{version} in its release tag field or it
looks for a tag that does not exist. Recorded in RELEASING.md — it is asked
once, on --new-track, and is easy to get wrong.

Distro scope. jazzy, kilted, rolling, lyrical. Not Humble:
serialization.cpp uses eprosima::fastcdr::CdrVersion::DDS_CDR, which is
Fast CDR 2.x, and Humble ships 1.0.x where the enum does not exist.

Also here

  • RELEASING.md — the track answers, the first-release command, and the
    four-command loop for every release after it.
  • release.yml now builds lyrical, matching ci.yml and the set of distros
    we intend to open tracks for.
  • A release-metadata CI job re-checks the above on every push.

Beyond the strict ask

Two things go slightly past "fix the four items" — easy to drop if you disagree:

  1. The release-metadata CI job. Not speculative: the empty-body failure
    above is invisible to a human reading the file and only surfaces at bloom
    time, after you have already tagged. One job, no new files.
  2. lyrical in release.yml. ci.yml already tested it; the release
    workflow did not build it.

Deliberately skipped: the README apt install note. The package does not exist
in the apt repo yet, so documenting it now would be wrong until the rosdistro
PR merges — worth adding then.

Verification

catkin_pkg — the parser bloom itself uses — was run against the result:

changelog OK: 0.1.0 (2026-05-11), 0.2.0 (2026-05-11), 0.3.0 (2026-06-17),
              0.4.0 (2026-07-16), 0.4.1 (2026-07-23), 0.5.0 (2026-08-27)
package.xml OK: rmw_unix_socket_cpp 0.5.0, Apache-2.0,
                urls=['bugtracker', 'repository', 'website']

Every version parses with a date and a non-empty body, package.xml validates
and its version matches the newest changelog entry, docutils reports zero system
messages and zero literal backticks leaking into rendered text, and xmllint
accepts package.xml. The embedded CI check was executed locally exactly as the
runner will feed it to python3.

Next step (not in this PR)

sudo apt install python3-bloom python3-catkin-pkg
bloom-release --new-track --rosdistro jazzy --track jazzy rmw_unix_socket_cpp

Then repeat per distro. RELEASING.md has the rest.

Copilot AI lite review requested due to automatic review settings September 11, 2026 15:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The CI validation job has a critical changelog-path error and incomplete checks, and the release instructions omit pushing the release tag.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Prepares the package for Bloom/ROS distribution release by updating metadata, changelog format, release documentation, and CI coverage.

Changes:

  • Adds package URLs and a Bloom-compatible changelog.
  • Documents release procedures and distro scope.
  • Adds Lyrical release support and metadata validation CI.
File summaries
File Description
rmw_unix_socket_cpp/package.xml Adds repository, bugtracker, and website URLs.
rmw_unix_socket_cpp/CHANGELOG.rst Provides the package-local Bloom-compatible changelog.
RELEASING.md Documents Bloom release procedures.
CHANGELOG.md Removes the superseded Markdown changelog.
.github/workflows/release.yml Adds Lyrical to release builds.
.github/workflows/ci.yml Adds release metadata validation.
Review details

Suppressed comments (3)

.github/workflows/ci.yml:130

  • This only verifies that the package version appears somewhere in the changelog history. If package.xml is left at an older version, that old entry still satisfies the assertion and a release can proceed with a newer changelog entry; compare the package version with the newest parsed version instead.
          assert pkg.version in versions, f'package.xml {pkg.version} has no changelog entry'

.github/workflows/ci.yml:123

  • The release requirement is for repository, bugtracker, and website URLs, but assert pkg.urls passes with any single URL. A future removal of one of the required links would still make this guard green; assert those URL types explicitly.
          assert pkg.urls, 'package.xml has no <url> tags'

RELEASING.md:51

  • catkin_prepare_release creates the release commit and tag locally; it does not push them to the remote. Following this sequence therefore reaches bloom-release without a remote tag, so the preceding release.yml check cannot run; add an explicit push command using the actual remote/branch before bloom-release, or remove “pushes” and document where that step occurs.
catkin_prepare_release        # bumps package.xml, commits, tags, pushes
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/ci.yml Outdated
Copilot AI review requested due to automatic review settings September 11, 2026 15:53
@benaliabderrahmane

Copy link
Copy Markdown
Owner Author

Thanks — two of the four points were real and are fixed in 0a8e3b8. Two are not
correct; evidence below.

Accepted

assert pkg.version in versions was too weak. Right: a stale package.xml
matching an old entry stayed green. Now asserts equality with the newest parsed
version.

assert pkg.urls was too weak. Right: it passed with any single URL. Now
asserts repository, bugtracker and website explicitly and names whichever
is missing.

Both were weakened by hand when the check was inlined into the workflow; the
version it was derived from asserted the stronger form.

Not correct

get_changelog_from_path(PKG, PKG) is not a path error. The second
positional argument is package_name, not a filename:

def get_changelog_from_path(path, package_name=None):
    changelog = Changelog(package_name)
    if os.path.isdir(path):
        path = os.path.join(path, CHANGELOG_FILENAME)

path is a directory, so the function appends CHANGELOG.rst itself, and
PKG is both the directory name and the package name, so both arguments are
correct. The job passed in 28s on the run that produced this review rather than
"failing on every run".

catkin_prepare_release does push. push_changes() runs git push and
then git push <remote> <tag>; the opt-out is --no-push, and pushability is
dry-run checked up front via try_repo_push().

The real bug in that line

The tag prefix was wrong, which the review did not flag:

tag_name = args.tag_prefix + new_version   # --tag-prefix defaults to ''

So the documented command produced 0.6.0, which the v:{version} bloom track
cannot find and which release.yml ignores because it only triggers on v*.
RELEASING.md now specifies catkin_prepare_release --tag-prefix v and says
why.

Guard now has negative tests

The tightened assertions were checked against mutated copies of the metadata to
confirm they actually trip:

PASS  unmodified metadata passes
PASS  package.xml behind newest changelog entry
PASS  a required <url> type removed
PASS  a version body written as an rst sub-section

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new CI metadata validation currently fails due to incorrect changelog handling and version selection, and does not fully validate changelog content.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

.github/workflows/ci.yml:129

  • The second argument is the changelog filename, so passing PKG makes catkin_pkg look for rmw_unix_socket_cpp/rmw_unix_socket_cpp instead of rmw_unix_socket_cpp/CHANGELOG.rst. This new job will fail before validating the changelog; use the default filename or pass filename='CHANGELOG.rst'.
          changelog = get_changelog_from_path(PKG, PKG)

.github/workflows/ci.yml:138

  • This only asserts that some parsed content remains; it does not enforce the no-subsections rule described above. A version with an intro paragraph plus Added/Fixed subsections would still pass because catkin_pkg silently drops the subsection content, allowing an incomplete Debian changelog to ship. Please validate the raw RST structure (or otherwise reject subsection headings) in addition to checking non-empty parsed content.
          for version, date, content in changelog.foreach_version():
              assert date, f'{version} has no parseable date'
              assert content, f'{version} body is empty (sub-sections are dropped)'
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread .github/workflows/ci.yml
changelog = get_changelog_from_path(PKG, PKG)
assert changelog is not None, f'no CHANGELOG.rst in {PKG}/'
versions = [v for v, _, _ in changelog.foreach_version()]
assert pkg.version == versions[-1], \
Copilot AI review requested due to automatic review settings September 11, 2026 16:23
@benaliabderrahmane

Copy link
Copy Markdown
Owner Author

Second review: one valid, one repeat. Fixed in 750282c.

Valid, and a real hole. The non-empty-body assertion did not enforce the
no-sub-sections rule it claimed to. A version with an intro paragraph plus
sub-sections parsed non-empty while every bullet under them was dropped — the
same empty-debian-changelog failure, just partial rather than total. The check
now inspects the raw rst: no version section may contain a nested section, and
no bold span may contain literal double backticks (rst has no nested inline
markup, and docutils reports neither).

Proven by mutation, asserting on which check fires rather than merely that
something failed:

PASS  unmodified metadata passes
PASS  package.xml behind newest changelog entry      -> is not the newest changelog entry
PASS  a required <url> type removed                  -> missing <url> types: ['bugtracker']
PASS  a version body written entirely as sub-sections-> 0.4.1 body is empty
PASS  intro paragraph PLUS sub-sections              -> 0.3.0 (2026-06-17) has rst sub-sections
                                                        ['Added', 'Fixed', 'Performance']
PASS  ``code`` nested inside **bold**                -> literal backticks in bold

The fifth case demotes headings only inside 0.3.0, which keeps its intro
paragraph, so every version still parses non-empty and only the new structural
check can catch it. That case failed to fail before this commit.

Repeat: get_changelog_from_path is still not a path bug. The second
parameter is package_name, not a filename — def get_changelog_from_path(path, package_name=None), and the body does if os.path.isdir(path): path = os.path.join(path, CHANGELOG_FILENAME). Both previous runs of this job passed
in 28s and 15s. That said, the argument only names the Changelog object, which
this check never reads, so it is now called with the directory alone — the value
was noise either way.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Correct the release-tag template from v:{version} to v{version}.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread RELEASING.md
| Upstream repository uri | `https://github.com/benaliabderrahmane/rmw_unix_socket_cpp.git` |
| Upstream devel branch | the branch for that distro (`main` until per-distro branches exist) |
| Version | `:{auto}` |
| Release tag | `v:{version}` |
@benaliabderrahmane

Copy link
Copy Markdown
Owner Author

Third review: v:{version} is correct and v{version} would break the track.
No change made — evidence below.

Bloom substitutes track templates with a string.Template subclass whose
delimiter is a colon, so the placeholder is :{name}, not {name}
(bloom/config.py, 0.14.3):

class ConfigTemplate(string.Template):
    delimiter = ':'
'release_tag': PromptEntry('Release Tag', default=':{version}', spec=config_spec['release_tag']),

Running bloom's own class over both candidates:

'v:{version}'    -> 'v0.6.0'
'v{version}'     -> 'v{version}'      # no substitution at all
':{version}'     -> '0.6.0'

v{version} contains no : delimiter, so nothing is substituted and bloom
would look for a tag named literally v{version} — which will never exist. The
prefix works precisely because the literal v sits outside the :{...}
placeholder, which is the same reason bloom's own default is :{version}.


Running tally across the three reviews: two findings were correct and are fixed
(the weak version/url assertions in 0a8e3b8, the sub-section hole in 750282c).
Four were not — the get_changelog_from_path filename claim twice, the
"catkin_prepare_release does not push" claim, and this one. The genuine tag
bug in that area was the missing --tag-prefix v, found by reading
catkin_pkg/cli/prepare_release.py while checking claim three:

tag_name = args.tag_prefix + new_version   # --tag-prefix defaults to ''

Copilot AI review requested due to automatic review settings September 17, 2026 23:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new CI release-metadata check sets docutils warning_stream to an invalid value (False), which can cause unexpected workflow failures.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

.github/workflows/ci.yml:149

  • In the docutils call, warning_stream is set to False, but docutils expects a file-like object (with .write()). If docutils ever emits a system message despite report_level, this can raise AttributeError and fail the CI job for the wrong reason. Prefer omitting warning_stream or using a real sink (e.g. io.StringIO()).
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread RELEASING.md

| Prompt | Answer |
| --- | --- |
| Release repository url | `https://github.com/benaliabderrahmane/rmw_unix_socket_cpp-release.git` |
benaliabderrahmane and others added 3 commits September 18, 2026 14:35
bloom reads CHANGELOG.rst from next to package.xml and nowhere else, so the
root CHANGELOG.md was invisible to catkin_generate_changelog and the release
would have shipped an empty debian changelog. Move it into the package and
convert it to rst.

Two rst constraints shaped the conversion. REP-132 allows only paragraphs and
lists under a version heading and catkin_pkg drops sub-sections silently, so
the per-release Added/Changed/Fixed headings are bold paragraphs, not rst
sub-sections; written as sub-sections, 0.4.0 and 0.4.1 parsed to an empty body.
rst also has no nested inline markup, so ``code`` inside **bold** rendered the
backticks literally.

Also:

- package.xml gains repository/bugtracker/website <url> tags, which ROS Index
  renders and rosdistro reviewers look for.
- release.yml builds lyrical, matching ci.yml and the set of distros we intend
  to open bloom tracks for. Humble is excluded deliberately: serialization.cpp
  uses fastcdr's CdrVersion::DDS_CDR, which is Fast CDR 2.x and absent from the
  1.0.x Humble ships.
- RELEASING.md records the bloom track answers, notably the v:{version} release
  tag our v-prefixed tags need and the per-release command sequence.
- A release-metadata CI job re-checks all of the above, because a changelog
  that parses to an empty body fails silently rather than loudly.

No source files are touched; this is release metadata only.
The release-metadata job re-parses CHANGELOG.rst the way catkin_pkg does
and fails on a dropped sub-section instead of shipping an empty debian
changelog, and checks the version/tag pairing; release.yml uses the v
tag prefix that catkin_prepare_release --tag-prefix v produces.

Squashed from:
- chore(release): tighten the metadata guard and fix the release tag prefix
- chore(release): reject dropped changelog sub-sections structurally
…e the release repo

- "First release" ran bloom-release straight away. With Version :{auto} that
  resolves to package.xml's 0.5.0, i.e. the existing v0.5.0 tag - which
  predates CHANGELOG.rst and the <url> tags this PR adds. Tag first.
- bloom's "Release repository url" prompt wants the URL of an existing
  repository; it cannot create one. Rolling-derived distros additionally
  require it to live in the ros2-gbp organisation.
- The token needs the workflow scope as well as public_repo.
- catkin_prepare_release bumps the patch version by default; the 0.6.0 in
  the examples needs --bump minor.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUNQNo26cKRPrVXcaHZnje
@benaliabderrahmane
benaliabderrahmane force-pushed the chore/rosdistro-release-prep branch from 26d2600 to a8ad796 Compare September 18, 2026 12:44
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