Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/actionlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ jobs:
with:
version: 1.7.12
shellcheck: true
# actionlint does not support GitHub's $/ self-repository syntax yet.
# Remove this ignore when https://github.com/rhysd/actionlint/issues/711 is fixed.
flags: >-
-ignore
specifying.action.*\$/.*invalid.format.because.ref.is.missing
63 changes: 63 additions & 0 deletions .github/workflows/release-plan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

name: Nextcloud release plan

on:
workflow_call:
inputs:
version:
description: Release version in MAJOR.MINOR.PATCH form
required: true
type: string
stable_branch:
description: Stable branch that is allowed to release
required: true
type: string
milestone:
description: Milestone title that must be closed with no open issues
required: false
type: string
default: ''
blocker_queries:
description: JSON array of GitHub issue search fragments that must return zero open items
required: false
type: string
default: '[]'
appinfo_path:
description: Path to the Nextcloud app info.xml
required: false
type: string
default: appinfo/info.xml
changelog_path:
description: Path to the changelog
required: false
type: string
default: CHANGELOG.md

permissions:
contents: read
issues: read
pull-requests: read

jobs:
plan:
name: Release plan
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout caller
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Build release plan
uses: $/actions/release-plan
with:
version: ${{ inputs.version }}
stable-branch: ${{ inputs.stable_branch }}
milestone: ${{ inputs.milestone }}
blocker-queries: ${{ inputs.blocker_queries }}
appinfo-path: ${{ inputs.appinfo_path }}
changelog-path: ${{ inputs.changelog_path }}
github-token: ${{ github.token }}
47 changes: 47 additions & 0 deletions actions/release-plan/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

name: Nextcloud release plan
description: Validate whether a Nextcloud app release is ready

inputs:
version:
description: Release version in MAJOR.MINOR.PATCH form
required: true
stable-branch:
description: Stable branch that is allowed to release
required: true
milestone:
description: Milestone title that must be closed with no open issues
required: false
default: ''
blocker-queries:
description: JSON array of GitHub issue search fragments that must return zero open items
required: false
default: '[]'
appinfo-path:
description: Path to the Nextcloud app info.xml
required: false
default: appinfo/info.xml
changelog-path:
description: Path to the changelog
required: false
default: CHANGELOG.md
github-token:
description: GitHub token used for milestone and blocker checks
required: true

runs:
using: composite
steps:
- name: Build release plan
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github-token }}
RELEASE_PLAN_VERSION: ${{ inputs.version }}
RELEASE_PLAN_STABLE_BRANCH: ${{ inputs.stable-branch }}
RELEASE_PLAN_MILESTONE: ${{ inputs.milestone }}
RELEASE_PLAN_BLOCKER_QUERIES: ${{ inputs.blocker-queries }}
RELEASE_PLAN_APPINFO_PATH: ${{ inputs.appinfo-path }}
RELEASE_PLAN_CHANGELOG_PATH: ${{ inputs.changelog-path }}
run: python3 "$GITHUB_ACTION_PATH/../../scripts/release_plan.py"
61 changes: 61 additions & 0 deletions docs/nextcloud-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<!--
SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-->

# Nextcloud release planning

The reusable release-plan workflow is intentionally non-mutating. It validates
release prerequisites before any tag, GitHub Release, signing or App Store
publication occurs.

## Architecture

The reusable workflow owns orchestration concerns: permissions, runner selection
and checking out the caller plus the workflow tooling repository.

The release-plan operation itself is exposed as the local composite action
`actions/release-plan`. The action maps its declared inputs to a small,
namespaced environment contract and invokes `scripts/release_plan.py`.

Business rules, input parsing, GitHub API checks, exit status and step-summary
rendering live in the Python script and are covered by unit tests. The workflow
does not contain release decision logic.

This follows GitHub's distinction between reusable workflows, which reuse whole
workflow/job structures, and composite actions, which encapsulate a reusable
sequence of steps within a job.

## Checks

The first implementation validates:

- semantic release version in `MAJOR.MINOR.PATCH` form;
- execution from the declared stable branch;
- `appinfo/info.xml` version matches the requested release;
- changelog contains a level-2 section for the requested version;
- optional milestone exists, is closed and has zero open issues;
- optional GitHub blocker queries return zero open issues or pull requests.

Blocker queries are caller-owned. This keeps project conventions out of the
shared workflow. A caller can model pending backports with a label query without
making that label part of the reusable workflow contract.

## Example caller

```yaml
jobs:
release-plan:
uses: LibreCodeCoop/github-workflows/.github/workflows/release-plan.yml@<full-release-sha> # v0.1.0
with:
version: 16.0.0
stable_branch: stable36
milestone: 16.0.0
blocker_queries: '["label:\"backport pending\""]'
```

The workflow only needs read permissions. Signing keys and App Store tokens are
deliberately not accepted by the planning stage.

Publication will be implemented as a separate privileged workflow after the
planning contract is proven with LibreSign and at least one additional app.
Loading
Loading