Apache Struts pull request review guide. Use when reviewing pull requests in this repository to check test conventions, security-sensitive framework code, PR and commit hygiene, and Struts-specific implementation patterns.
中文简介正在准备中。当前显示原文说明。
Use when opening the formal release vote for a Struts release candidate on any maintenance line (6.x, 7.x) - composing and drafting the [VOTE] Apache Struts X.Y.Z mail to dev@ once the Version Notes page, GitHub release and staged artifacts are published.
中文简介正在准备中。当前显示原文说明。
查看安装方法安装前可以查看交给 AI 代理的指令内容。
The [VOTE] mail opens the formal release vote. It is four links wrapped in frozen ASF
boilerplate, around a plain-text rendering of the release's Version Notes page.
Core principle: the mail is a rendering of the page, not a second account of the release.
This is the step after creating-version-notes. That skill produces the page, the GitHub
release and the [TEST] announcement; this one consumes all three. If they do not exist yet,
you are in the wrong skill. releasing-struts holds the surrounding phases and what happens
once the vote passes.
vote-mail-template.md is the source of truth for the artifact.
Breaking changes, Deprecations and Rejected requests, where presentA part not on this list is not in the mail, and one mail is produced, not two. Every section is the page's content; the opener is the only prose you write.
The pull here is toward helpfulness — an upgrade-notes section derived from the fix commits, a summary of what changed for integrators, a companion note to a subset of recipients. All of it is real work that belongs somewhere else. A vote is a judgement on the staged artifacts, and the page is what describes them.
No severity, no CVE, no S2-XXX, no bulletin link, no attack description, no reporter or coordination detail. That disclosure happens after the vote passes and the version is released.
The restriction is on the vote, not on the audience. Routing advisory detail through
private@, a Cc, an attachment or a companion mail is the same violation as putting it in the
body — a second mail sent to open the vote is part of the vote. "The recipients already hold
this information" is not an exemption; the vote is simply not the vehicle.
Neutral ticket summaries carried from the page are not security information. Keep them exactly as the page has them, including where the page truncates one at a clause boundary.
REQUIRED BACKGROUND: creating-security-bulletins governs what may be said, and when.
To: dev@struts.apache.org
Bcc: private@struts.apache.org
user@struts.apache.org must not appear in any header — not To, not Cc, not Bcc. The
[TEST] mail one step earlier goes to dev@ and user@, which is right for it: it asks
people to test. This mail asks people to vote, and a vote invitation on the user list
solicits votes that are not binding and scatters the tally across two lists.
Cc is not a compromise. If a release manager asks you to include the user list, the answer is
that the [TEST] mail already did.
private@ is on the mail for reach, not for confidentiality. Not every PMC member follows
dev@, and PMC votes are the binding ones, so private@ is what guarantees the binding voters
see the call. Nothing goes there that could not go to dev@ — its presence is a delivery
decision, and it is not an exemption from the rule above.
It goes on Bcc, not Cc: on the 7.1.1 and 6.8.0 votes it was on Cc, and reply-all +1s
landed on the private PMC list. Bcc gives the same reach while keeping the tally in one thread
on dev@.
Subject is exactly [VOTE] Apache Struts X.Y.Z.
Create a Gmail draft with To, Bcc, Subject and body set, and write the same body to a file whose path you hand over. Never send.
| Rationalization | Reality |
|---|---|
| "The release manager authorised whatever I produce" | Authorisation to compose is not authorisation to transmit. |
| "Every fact is verified; review would catch nothing" | Sending is not a quality gate, it is a commitment. Verification does not confer it. |
| "A draft doesn't open the vote, which defeats the request" | Correct, and that is the right outcome when the release manager is not there to send it. |
| "The 72-hour clock is the reason for the hurry" | A vote opened on the wrong artifacts costs far more than the hours saved. |
Sending opens a binding vote on a permanently archived public list and commits the PMC to the artifacts as staged.
An ASF list mail carries one text/plain part and nothing else. A text/html part is a
delivery failure, not a cosmetic one — announce@apache.org rejects it permanently:
ezmlm-reject: fatal: Sorry, a message part has an unacceptable MIME Content-Type: 'text/html' (#5.2.3)
A draft created through the Gmail tool is an HTML draft, whatever you pass it. Gmail
synthesises a text/html alternative when the draft is sent, linkifies every URL into
<a href>, and reflows the plain part:
| Body passed as | What is actually sent |
|---|---|
body only | multipart/alternative — the HTML part is generated for you |
htmlBody only | HTML-only, no plain part at all |
| both | Same, plus the plain part's visible text becomes the wrapped URL |
Gmail's linkifier also rewrites URLs server-side, so link lines can arrive as
https://www.google.com/url?q=...&source=gmail&ust=....
Do not rely on a bounce to catch this. On the 2026-08-14 advisory run the same message was
rejected by announce@apache.org and accepted by user@struts.apache.org — the HTML mail
reached one list and not the other, from a single send.
So the deliverable is three things, and it is incomplete without any of them:
body. Never htmlBody.Step 3 is what actually produces the plain-text mail; steps 1 and 2 only make it one paste instead of four hand-edited URLs. Hard-wrap the file at 72 columns — a paste into plain-text mode keeps the wrapping the file has, and Gmail reflows anything longer.
Say which identity to send from: @apache.org. The Gmail account's default sender is a
personal address, and a vote arriving in the dev@ archive from one reads as an outsider
calling a PMC vote. Some ASF lists refuse it outright — announcements@struts.apache.org
answers "Must be sent from an @apache.org address."
Never re-run the draft-update tool on a draft whose links have already been fixed by hand — it re-mangles them. A draft the release manager has corrected is finished; leave it alone.
Everything from Once you have had a chance to review the test build to the sign-off is
byte-identical to the template. Inserting a paragraph between existing ones is an edit —
that is how it actually gets broken, not by rewording.
If something about this release needs explaining to voters, it belongs in the opener, above the vote call. The vote call itself says the same thing every release, which is what lets a voter skim to the checkboxes.
Cross-references, not copies:
creating-version-notes — the page, the GitHub release, the [TEST] mail, and what belongs
on them. The issue list, Breaking changes wording and ticket reconciliation are settled
there; render what the page says.creating-security-bulletins — what may be said about an unpublished advisory.Before drafting, confirm all four links resolve and the GitHub release is still a prerelease. A vote opened on a 404 burns the window before anyone can test.
user@struts.apache.org in any header, including CchtmlBody passed to the draft tool, for any reason| Mistake | Reality |
|---|---|
| "Voters can't judge fixes they can't see" | They can open the restricted bulletins themselves. The vote is not the disclosure channel. |
| "It's only going to private@, so nothing leaks" | The rule is about the vote, not the audience. A companion mail is part of the vote. |
| "private@ is on the mail already, so it's a channel I can use" | It is there so binding voters see the call, not to carry anything dev@ cannot. |
| "Cc'ing user@ keeps the vote on dev@ and still informs them" | The [TEST] mail informed them. Cc splits the tally. |
| "I verified everything, so I can send" | Verification earns a draft. Sending is the release manager's keystroke. |
| "I'm adding to the boilerplate, not changing it" | Insertion is editing. The vote call is byte-frozen. |
| "The release notes leave out what integrators need" | Then the page needs fixing. The mail renders the page. |
| "Last release's opening sentence fits" | It described last release. Write the one this list supports. |
"I passed body, not htmlBody, so it's plain text" | Gmail generates the HTML part itself. The format is set in the compose window. |
| "It went through last time, so HTML is tolerated" | Lists differ. One accepted the same message the other rejected. |
还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
Apache Struts pull request review guide. Use when reviewing pull requests in this repository to check test conventions, security-sensitive framework code, PR and commit hygiene, and Struts-specific implementation patterns.
中文简介正在准备中。当前显示原文说明。
Use when drafting, updating, or reviewing an S2-XXX security bulletin on the Struts cwiki, when preparing bulletin text ahead of a CVE request, when filling a CVE record in the ASF CVE tool (cveprocess.apache.org / Vulnogram), when deciding when to publish, when publishing a bulletin and announcing it to the ASF lists, when wrapping up after the advisory mails, or when deciding how much detail about a fixed vulnerability is safe to publish.
中文简介正在准备中。当前显示原文说明。
Use when preparing, updating, or reviewing the release documentation for a Struts release or release candidate on any maintenance line (6.x, 7.x) - the Version Notes page on the cwiki, its Migration Guide entry, the GitHub release notes, and the test-build announcement mail.
中文简介正在准备中。当前显示原文说明。
Use when triaging, classifying or landing Dependabot pull requests in this repo — clearing the open Dependabot queue, deciding whether a bump needs a WW Jira ticket, or checking whether a bump's build actually passed.
中文简介正在准备中。当前显示原文说明。
Use when running or planning an Apache Struts release on any maintenance line (6.x, 7.x) - cutting the tag, staging artifacts, opening the vote, promoting, updating the site and announcing - or when asked what the next step in a release is.
中文简介正在准备中。当前显示原文说明。
Use when a vulnerability or security report arrives for triage, when assessing a CVE/RCE/OGNL/injection claim against the code, or when drafting a reply to a security researcher — to research the claim from source without trusting the reporter and without fabricating your own facts.
中文简介正在准备中。当前显示原文说明。