Repository navigation
Plan and validate a selected release with the reusable engine #71
Description
Parent: #70
Depends on: #78, #79, #80, #82, #83, #84, #87
Blocks: #72, #74, #76
Goal
Implement the read-only planning stage for one manually selected release line using the reusable engine.
The same planner must work from GitHub Actions and the local CLI.
Inputs
branch— required;ref/ commit SHA — optional planning base, not necessarily the final release commit;version— optional explicit override;channel— alpha / beta / rc / final;ignore_open_backport— boolean;create_follow_up_milestone— boolean/choice where applicable.
If ref is omitted, resolve selected branch HEAD.
If ref is supplied:
- resolve it to an immutable SHA;
- require it to belong to the selected release line and be an ancestor of/equal to the selected branch HEAD;
- use it as the analysis/preparation base;
- do not assume it will be the final release SHA if changelog/version files still need a release-preparation commit.
Persist the planning base SHA in ReleasePlan. The final merged release SHA does not exist at this stage and must not be predicted. It is produced later by #73 after the release preparation PR is merged and revalidated.
Consumer discovery contract
Use consumer configuration/adapters to discover:
- version source and current app version;
- selected branch/ref;
- previous relevant release/tag;
- merged PRs in the release range;
- direct commits not represented by PRs;
- translation activity;
- dependency activity;
- configured changelog strategy/target;
- current milestone;
- open PRs against selected stable.
Do not hardcode LibreSign file paths in domain logic.
For LibreSign, appinfo/info.xml is the primary version/Nextcloud-compatibility source configured through #83; package mirrors are consistency checks, not independent version authorities.
Previous release / comparison range
Do not use GitHub's repository-wide "latest release" as the previous release. In a multi-stable repository it can belong to another release line.
Determine the previous release from Git ancestry:
- list tags matching the consumer-configured tag prefix + supported app version syntax;
- resolve tag commits;
- keep only tags whose commits are ancestors of/equal to the planning base SHA;
- choose the closest reachable release tag in commit history;
- if multiple release tags resolve to the same commit, choose the highest valid app version;
- use the range
previous-tag..planning-basefor release-activity discovery.
Ignore unrelated/non-version tags.
If no previous reachable release tag exists, fail unless the consumer config provides an explicit initial-history baseline. Do not silently scan the whole repository history.
Persist previous tag and previous tag SHA in ReleasePlan.
Version proposal
Use merged PR titles as the Conventional Commit classification source.
For an existing release line:
- any included
featPR → minor; - otherwise releasable activity → patch.
Direct translation/maintenance commits count as release activity but do not promote to minor.
Major is not inferred from commits. A Conventional Commit ! / BREAKING CHANGE marker is surfaced as a warning requiring maintainer attention, but it does not automatically create a new LibreSign major because major lines are opened explicitly with the Nextcloud/framework release line.
Prerelease transitions use #80.
For the first release on a new line, do not invent a major from commit history. Use the configured app base version/release-line metadata. Development/prerelease suffixes such as -dev, alpha, beta or RC are normalized only according to the explicitly selected channel/final transition from #80.
Explicit version override is accepted only after validation against current version, branch/line, channel and existing tags.
The CLI/workflow accepts either 15.0.4 or v15.0.4 when the configured tag prefix is v. Normalize internally to app version 15.0.4 and derive tag v15.0.4; never write the tag prefix into appinfo/info.xml.
Backport blocker
Do not block on a backport request/comment without a PR.
Block only when an actual open backport PR targets the selected stable, unless ignore_open_backport=true.
Detection considers:
- PR base branch == selected stable;
- backport/stable title/body/metadata conventions;
- optional consumer-configured labels.
A repository-wide label:backport-request query is not sufficient.
Other open PRs directly targeting the stable may be surfaced as warnings without becoming hard blockers unless consumer policy says otherwise.
Validation
Fail or warn clearly for:
- missing/invalid branch/ref;
- ref not valid for selected release line;
- inconsistent configured release files;
- unexpected existing target tag/release;
- missing expected milestone;
- invalid version regression;
- no releasable activity;
- missing configured changelog target/strategy;
- stale branch/ref movement.
Translation-only and dependency-only releases are valid.
Output artifact: ReleasePlan v1
This issue's stable handoff artifact is ReleasePlan v1.
It is immutable planning data. Later stages may reference its id/hash but must not mutate it in place.
Produce machine-readable JSON plus human GitHub Step Summary.
The JSON contract is versioned (for example schema: 1) and must be validated by tests so workflows are not coupled to undocumented fields.
It contains:
- repository/consumer id;
- branch;
- planning base SHA;
- previous tag + tag SHA;
- current/proposed version;
- explicit override, if any;
- channel;
- bump reason;
- release activity summary;
- changelog target/candidates;
- milestone;
- open backport blockers;
- warnings;
- override state;
- readiness.
This stage performs no mutation.
Acceptance criteria
- deterministic for same repository/GitHub state;
- no write permission required;
- CLI/workflow plans are equivalent;
- translation-only/dependency-only release accepted;
- internal
featcommit insidefixPR does not cause minor; - stable
featPR causes minor; - open backport on another stable does not block;
- matching open backport blocks unless explicitly overridden;
- consumer configuration is used for paths/conventions;
- the versioned
ReleasePlan v1JSON schema/fixture is committed and becomes the input contract for Prepare deterministic reviewable release PRs from a ReleasePlan #72 and the planning input for later stages.
Definition of done
- production planner implementation exists in
LibreCodeCoop/release-tool; ReleasePlan v1schema and representative fixtures are committed;- required Unit/Integration/Acceptance tests for this issue pass under the Define business-driven test architecture and conventions for release-tool #90 conventions;
- source CLI and PHAR produce equivalent planning results for representative fixtures;
- the transitional Python planner is retired or clearly marked superseded only after behavior parity is proven;
- Prepare deterministic reviewable release PRs from a ReleasePlan #72 can consume the committed
ReleasePlan v1contract without additional design decisions.
Activity
[-]Discover and validate a LibreSign release train[/-][+]Plan and validate a selected LibreSign release[/+][-]Plan and validate a selected LibreSign release[/-][+]Plan and validate a selected release with the reusable engine[/+]vitormattos commentedon Sep 21, 2026
Completed by release-tool v0.2.0 and github-workflows#93. ReleasePlan v1 is produced by the PHP engine from local Git + GitHub API state, with reachable-tag ancestry, PR-title Conventional Commit classification, translation/dependency activity, branch/ref validation, backport blockers, milestone state, security mode and immutable planning-base data. Source CLI and PHAR planning are contract-equivalent in deterministic integration smoke coverage. The reusable workflow now installs exact release-tool v0.2.0 with checksum verification, checks out full history, exposes plan/ready/tool-version outputs, and the transitional Python planner has been removed.