Skip to content

Epic: build reusable release automation with LibreSign as first consumer #70

Description

@vitormattos

Goal

Deliver a production-grade, reusable release automation system using LibreSign as the first consumer.

The normal maintainer journey must require human action only at two review gates:

  1. review and merge the generated release preparation PR;
  2. review and publish the generated GitHub Release draft.

Everything else must be deterministic, testable and reproducible.

LibreSign/libresign#8447 is a disposable proof of concept. It is not a merge target and is not a production dependency. It may be consulted only to extract behavior, tests and lessons.

Prototype status

LibreSign/libresign#8447 and github-workflows#77 are prototype/reference work only.

They may be used to extract useful behavior, fixtures and lessons, but:

  • neither is a production source of truth;
  • neither is a merge prerequisite for the final implementation;
  • conflicting prototype behavior is superseded by this epic and its child contracts;
  • release-specific Python prototype behavior is retired only when the corresponding PHP implementation reaches tested parity.

Repository boundaries

LibreCodeCoop/release-tool

Owns the reusable PHP release engine:

  • CLI / PHAR;
  • domain policies and contracts;
  • Git/GitHub/filesystem/process adapters;
  • consumer configuration schema;
  • release-engine tests and PHAR distribution.

LibreCodeCoop/github-workflows

Owns reusable GitHub orchestration around the PHAR:

  • composite actions/reusable workflows;
  • workflow catalog/distribution;
  • authentication/permission wiring;
  • orchestration tests;
  • no duplicated release business logic.

LibreSign/libresign

Owns consumer-specific integration only:

  • release configuration;
  • stable-branch/version mapping;
  • release-file mapping;
  • per-major changelog source files;
  • Makefile/package behavior;
  • thin workflow/catalog integration.

LibreSign/documentation

Owns the public human documentation for:

  • automated release journey;
  • equivalent manual procedure;
  • recovery/troubleshooting;
  • release-tool and consumer configuration guidance.

It does not own a copy of the changelog or release-history dataset. Canonical release history remains in LibreSign/libresign/docs/changelogs/changelog-<major>.md.

Maintainer journey

Actions → Prepare release
        ↓
read-only ReleasePlan
        ↓
generated reviewable release PR(s)
        ↓
maintainer review + authorized merge
        ↓
post-merge revalidation
        ↓
milestone transition
        ↓
GitHub Release draft
        ↓
maintainer review
        ↓
Publish release
        ↓
existing build/sign/App Store publisher
        ↓
verification

A workstation is not required for the normal path.

The same engine is available through a local CLI for dry-run, diagnostics, manual execution and recovery.

Prepare release inputs

The maintainer-facing workflow exposes:

  • branch — required;
  • commit/ref override — optional planning base; default is selected branch HEAD. It is not the final release SHA when release metadata still needs to be committed;
  • version override — optional; default is automatic calculation;
  • release channel — alpha / beta / rc / final;
  • ignore open backport — explicit boolean override;
  • create follow-up milestone — explicit boolean/choice where applicable.

Any supplied ref must resolve to an immutable SHA that is an ancestor of/equal to the selected branch HEAD. Release preparation may create a new commit on top of that base; the final GitHub Release is pinned to the merged release-preparation commit, not blindly to the input ref.

Version policy

For an existing release line:

  • at least one included merged PR whose PR title is Conventional Commit feat → next minor;
  • otherwise any releasable activity → next patch.

Releasable patch activity includes fixes, translations, dependency updates, documentation, tests, refactors, CI and maintenance.

Internal commits inside a PR never promote the release type.

Direct commits not represented by PRs may count as releasable activity, especially translation synchronization, but do not promote to minor.

A deliberately backported feature still promotes that stable line to the next minor.

Major releases are part of opening a new LibreSign/Nextcloud release line and are not inferred from Conventional Commits.

Prerelease behavior is defined in #80.

Backport blocker

A backport request/comment without an actual PR does not block.

Only an actual open backport PR for the selected stable line blocks by default.

The maintainer can explicitly override this blocker.

Detection must use selected base branch plus backport metadata/patterns; a repository-wide backport-request label query is insufficient.

Project metadata semantics

Keep release/planning dimensions separate:

  • GitHub Project Status represents workflow/planning state;
  • Milestone represents the target release train/version;
  • merged PR Conventional Commit title is the SemVer classification source;
  • labels provide auxiliary context such as bug/enhancement, feature area, platform, dependencies, security and backport metadata.

Do not use Project Status or contextual labels as the primary SemVer source.

Changelog policy

LibreSign uses one changelog source file per app major under:

docs/changelogs/changelog-<major>.md

The files follow Keep a Changelog semantics and are maintained in English only. There is no per-language changelog workflow.

The selected stable branch is authoritative for the release being prepared. main keeps the aggregate release-history archive for all supported majors and receives the exact released changelog entry after stable preparation.

Release activity is collected once and used independently by:

  • version policy;
  • changelog policy.

Rules:

  • PR titles are the primary Conventional Commit source;
  • translation activity may come from direct commits;
  • dependency-bot PRs are collapsed into one dependency entry;
  • already-published changes are not duplicated;
  • labels/context may help categorization but cannot override SemVer.

Detailed changelog strategy is #79. LibreSign integration/package behavior is #81.

Release files

For a selected LibreSign stable release, the production release PR changes only:

  • docs/changelogs/changelog-<major>.md;
  • appinfo/info.xml;
  • package.json;
  • package-lock.json.

If synchronization of the aggregate release-history archive on main requires a separate PR, that PR changes only the corresponding per-major changelog file and reuses the already-reviewed stable release entry.

No release of an older stable may change main's app/package version to the older version.

Milestones

Normal stable releases use the stable-specific release milestone.

The automation can:

  • identify the current milestone;
  • rename it to the release version;
  • create a follow-up milestone when requested;
  • move remaining open items;
  • close the released milestone.

EOL/lifecycle data may be shown as context, but support status is not inferred automatically because LibreSign may intentionally support lines beyond community lifecycle.

Prerelease milestone behavior is compatible with #80.

Draft and publication

After an authorized merge:

  • revalidate the final merged state;
  • apply/finalize milestone transition;
  • create/update one GitHub Release draft pinned to the validated SHA;
  • include curated release notes, closed milestone link and full compare link.

The maintainer explicitly clicks Publish release.

The existing publication workflow remains responsible for packaging, signing, uploading the asset and publishing to the Nextcloud App Store.

A pre-publication correction stays on the same planned version unless the maintainer intentionally starts a different release.

Multi-stable releases

Each stable release is manually initiated.

When several stables are released in one maintenance cycle:

  1. prepare as needed;
  2. publish oldest stable first;
  3. verify successful publication/App Store visibility;
  4. publish the next stable.

Do not automatically select all maintained stable branches in the first production version.

Architecture

The reusable architecture is defined in #78.

Rules:

Implementation order / dependencies

The following order is normative for autonomous implementation. Each step must leave a stable artifact/contract used by the next step.

Phase 1 — architecture and executable foundation

  1. Define the reusable release engine architecture and stage contracts #78 — architecture, repository boundaries and stage contracts.
  2. Bootstrap the production-grade PHP CLI, PHAR and quality platform #82 — bootstrap LibreCodeCoop/release-tool, PHP CLI/PHAR, QA platform and release of the tool itself.
  3. Define business-driven test architecture and conventions for release-tool #90 — business-driven test architecture, directory conventions, data providers and acceptance-test strategy.
  4. Provide a reusable action for pinned release-tool PHAR execution #87 — reusable Action that installs/verifies a pinned PHAR for GitHub workflows.
  5. Define and validate the consumer configuration contract for the release tool #83 — versioned .nextcloud-release.yml schema, parser and typed consumer configuration.

Artifacts available after Phase 1:

release-tool.phar
setup-release-tool reusable Action
test/fixture conventions
ConsumerConfig schema v1

Phase 2 — reusable domain policies

  1. Define security-release-safe domain and serialization contracts #84 — security-release-safe domain/serialization contract.
  2. Support alpha, beta, RC and final release channels #80 — Version / ReleaseChannel / prerelease state model.
  3. Support per-major Keep a Changelog storage as a reusable changelog strategy #79 — ChangelogPolicy / per-major renderer and migration support.

These extend the same PHP foundation and test conventions. They may proceed in parallel only after their declared dependencies are complete.

Phase 3 — planning and preparation

  1. Plan and validate a selected release with the reusable engine #71 — planner/discovery; produces immutable ReleasePlan v1.
  2. Prepare deterministic reviewable release PRs from a ReleasePlan #72 — preparation; consumes ReleasePlan v1 and produces ReleasePreparation v1.

Phase 4 — first real consumer integration and post-merge finalization

  1. Integrate the reusable release tooling and per-major changelog packaging in LibreSign #81 — add the LibreSign consumer config, per-major changelog layout and package/Makefile integration.
  2. Finalize the merged stable release and synchronize aggregate changelog history #73 — after the generated stable PR is merged, revalidate it and produce PreparedRelease v1; synchronize the exact released changelog entry to the aggregate history on main.
  3. Plan and apply reusable release milestone transitions #74 — consume PreparedRelease v1, apply milestone transition and produce MilestoneTransition v1.

Phase 5 — draft, publication and verification

  1. Prepare the GitHub Release draft from finalized release artifacts #75 — consume finalized artifacts and create ReleaseDraft v1; stop at the human Publish gate.
  2. Validate built release artifacts with the PHP release tool #89 — reusable PHP validation of built release archives; produces ArtifactValidation v1.
  3. Verify the published release, artifact and App Store handoff #88 — after the human publishes, consume the released identity + artifact validation and produce PublicationVerification v1.

Phase 6 — end-to-end orchestration

  1. Provide the maintainer-facing Prepare Release workflow and end-to-end orchestration #76 — compose the already-proven stages into the maintainer-facing Prepare Release workflow. This issue must not invent new release policy.

Phase 7 — release documentation and single-source history

  1. Keep LibreSign release history canonical in the app repository #86 — enforce LibreSign/libresign as the single canonical changelog/release-history source and ensure no documentation synchronization duplicates that content.
  2. Document the complete LibreSign release lifecycle and manual path LibreSign/documentation#97 — document automated/manual process, recovery, security mode, configuration and the canonical changelog location using the implemented contracts.

Phase 8 — production scenario validation

  1. Validate source CLI + PHAR + GitHub Actions parity.
  2. Validate a normal final release.
  3. Validate a prerelease sequence.
  4. Validate a translation-only/maintenance-only release.
  5. Validate an open-backport blocker and explicit override.
  6. Validate a security-mode release without sensitive-data leakage.
  7. Validate failure/recovery paths, including stale state and failed publication.
  8. Only after these scenarios are proven should the reusable system be generalized to additional consumer apps.

Artifact chain

The implementation should evolve by consuming the output of the previous stage rather than rediscovering state:

ConsumerConfig v1
      +
domain policies
      ↓
ReleasePlan v1
      ↓
ReleasePreparation v1
      ↓
human merge
      ↓
PreparedRelease v1
      ↓
MilestoneTransition v1
      ↓
ReleaseDraft v1
      ↓
human Publish
      ↓
ArtifactValidation v1
      +
publisher/App Store checks
      ↓
PublicationVerification v1

An issue must not recreate a value already finalized by an upstream artifact. It may revalidate that value against current external state.

Required cross-issue contract

Every implementation issue must:

If two issues conflict, the later explicit architectural decision in #70/#78 takes precedence and the conflicting child issue must be corrected before implementation continues.

Issue closure rule

A child implementation issue is complete only when both are true:

  1. every listed Acceptance Criterion is demonstrably satisfied;
  2. every listed Definition of Done item is complete.

Closing evidence should be visible in the implementation PR/checks/artifacts, not only asserted in a comment.

For implementation issues, evidence normally includes as applicable:

  • merged or ready-for-review production code in the correct repository;
  • committed versioned schema/artifact contracts promised by the issue;
  • required Unit/Integration/Acceptance tests following Define business-driven test architecture and conventions for release-tool #90;
  • relevant CI/quality checks green;
  • source CLI and PHAR parity where the issue exposes CLI behavior;
  • no unresolved TODO/placeholders for behavior required by the issue;
  • superseded prototype/Python behavior retired only after parity is proven;
  • dependent issue can consume the produced artifact/contract without another architectural decision;
  • user/maintainer-visible documentation updated in the proper documentation location when behavior changed.

An issue must not be closed merely because a prototype exists, a happy-path test passes, or coverage increased.

Source of truth

Production source of truth is:

  1. implemented/tested engine contracts in LibreCodeCoop/release-tool;
  2. reusable orchestration contracts in LibreCodeCoop/github-workflows;
  3. consumer configuration/integration in LibreSign/libresign;
  4. published process documentation in LibreSign/documentation; changelog/release history remains canonical in LibreSign/libresign.

Chat history and LibreSign/libresign#8447 are not sources of truth.

Definition of done

A maintainer can:

  1. start a release from GitHub Actions without a local checkout;
  2. select branch and optional overrides;
  3. inspect a deterministic plan;
  4. receive a narrowly scoped, reviewable release PR;
  5. merge through an authorized maintainer path;
  6. receive a correct GitHub Release draft automatically;
  7. review and publish the draft;
  8. reuse the existing build/sign/App Store publisher;
  9. verify the published version;
  10. reproduce the planning/manual procedure locally through the CLI;
  11. follow the public LibreSign documentation without needing implementation-specific tribal knowledge.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions