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
35 changes: 35 additions & 0 deletions .github/workflows/workflow-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

name: Workflow policy

on:
pull_request:
paths:
- '.github/workflows/**'
- 'workflow-templates/**'
- 'scripts/check_workflow_policy.py'
- 'tests/test_workflow_policy.py'
push:
branches:
- main
paths:
- '.github/workflows/**'
- 'workflow-templates/**'
- 'scripts/check_workflow_policy.py'
- 'tests/test_workflow_policy.py'

permissions:
contents: read

jobs:
policy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Check workflow policy
run: python3 scripts/check_workflow_policy.py
61 changes: 37 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,49 @@ SPDX-License-Identifier: AGPL-3.0-or-later

# GitHub Workflows

Reusable, testable GitHub workflows for organizations that want consistent CI
and release automation without copying opaque YAML between repositories.
Managed, testable GitHub workflow templates and supporting Actions for organizations
that want consistent CI without copying opaque YAML between repositories.

This project keeps reusable workflow logic independent from
This project keeps workflow distribution independent from
[GitHub Governance](https://github.com/LibreCodeCoop/github-governance):
governance manages repository rulesets, while this repository manages reusable
workflows and reproducible upstream workflow adaptations.
governance manages repository rulesets, while this repository manages workflow
sources, adaptations, tests and publication.

## Why use it

- **Reusable automation:** consume shared workflows instead of maintaining copies.
- **Reproducible upstream imports:** source files are tied to immutable upstream commits and SHA-256 hashes.
- **Reviewable downstream changes:** local adaptations are explicit and testable.
- **Security-first defaults:** third-party Actions are pinned to immutable commit SHAs.
- **Versioned consumption:** releases are referenced by immutable SHA with a human-readable version comment.
- **Reviewable downstream changes:** LibreCode adaptations are explicit patches.
- **Materialized consumer workflows:** repositories keep normal local GitHub workflows instead of opaque remote callers.
- **Automated updates:** consumers receive reviewable pull requests from the organization catalog.
- **Local customization:** consumer-specific differences live in `.github/workflows/<workflow>.patch`.
- **Security-first defaults:** external Actions are pinned and checked by policy CI.

## Current scope
## Distribution model

The first target is reusable automation for Nextcloud applications, with
LibreSign as the first production consumer.
`LibreCodeCoop/github-workflows` is the source of truth.

The repository is intentionally product-agnostic. LibreSign and Nextcloud are
reference consumers and upstream sources, not hard-coded engine concepts.
`LibreCodeCoop/.github` is the organization catalog used by GitHub's
**Actions → New workflow** UI.

## Repository layout
Consumer repositories install full workflow files. Their local
`sync-workflow-templates.yml` periodically invokes
`actions/sync-workflows`, which:

- updates workflows already installed in the repository;
- applies local workflow patches;
- records catalog versions in `.github/actions-lock.txt`;
- refuses to overwrite unexplained local divergence;
- opens reviewable update pull requests through the caller workflow.

- `workflow-templates/` — generated GitHub-native organization workflow templates ready for catalog publication.
- `upstream/` — immutable source manifests.
- `patches/` — explicit downstream adaptations.
- `scripts/` — deterministic synchronization/check tooling.
- `tests/` — tests for synchronization and template behavior.
- `docs/` — architecture, adoption and security guidance.
## Repository layout

`LibreCodeCoop/.github` is the organization catalog used by GitHub's **Actions → New workflow** UI. This repository remains the source of truth; catalog publication should mirror generated templates rather than make `.github` a second editing source.
- `workflow-templates/` — generated organization workflow templates.
- `actions/` — tested Actions used by the workflow platform.
- `upstream/` — immutable source manifests and vendored upstream files.
- `patches/` — explicit organization-level adaptations.
- `scripts/` — deterministic synchronization and policy tooling.
- `tests/` — tests for synchronization, rendering and policy behavior.
- `docs/` — architecture, security and adoption decisions.

## Development

Expand All @@ -47,10 +56,14 @@ Run:
```bash
python3 -m unittest discover -s tests -p 'test_*.py'
python3 scripts/sync_upstream.py check upstream/sources.json
python3 scripts/render_upstream.py check upstream/templates.json
python3 scripts/check_workflow_policy.py
```

See [Architecture](docs/architecture.md) and
[Upstream workflow model](docs/upstream-workflows.md).
See [Architecture](docs/architecture.md),
[Upstream workflow model](docs/upstream-workflows.md),
[GitHub Actions security policy](docs/security-policy.md) and
[Dependency update policy](docs/dependency-update-policy.md).

## Security

Expand Down
100 changes: 67 additions & 33 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,69 +7,103 @@ SPDX-License-Identifier: AGPL-3.0-or-later

## Responsibility boundary

`github-workflows` is the source of truth for reusable CI, imported workflow adaptations, generated workflow templates and release automation.
`github-workflows` is the source of truth for imported workflow adaptations,
generated organization templates and tested helper Actions.

`LibreCodeCoop/.github` is the organization-facing catalog. Generated workflow templates can be published there so developers can discover them through GitHub's **Actions → New workflow** experience. The catalog is a distribution target, not the editing source.
`LibreCodeCoop/.github` is the organization-facing catalog. It is a distribution
target, not an editing source.

Repository rulesets remain the responsibility of `LibreCodeCoop/github-governance`.
Repository rulesets remain the responsibility of
`LibreCodeCoop/github-governance`.

Consumer repositories own:

- which catalog workflows they install;
- consumer-specific workflow patches;
- credentials and protected environments;
- product-specific configuration;
- the decision to invoke a mutating workflow;
- immutable pins to released workflow revisions.
- branch policy;
- the final review and merge of workflow-update pull requests.

## Upstream workflow pipeline

An imported workflow follows this pipeline:

```text
immutable upstream commit
↓
source URL + SHA-256 in manifest
↓
deterministic fetch
↓
hash verification
source URL + SHA-256
↓
explicit downstream patches
vendored upstream file
↓
generated `workflow-templates/` artifact
explicit LibreCode patch
↓
tests + actionlint + zizmor
generated workflow template
↓
publish catalog copy to `LibreCodeCoop/.github`
tests + actionlint + zizmor + workflow policy
↓
versioned release / consumer update
LibreCodeCoop/.github catalog
```

The source manifest is authoritative. A network response that does not match the
recorded SHA-256 fails closed.

## Generated files
When a refresh resolves a newer upstream commit but the file bytes are unchanged,
the existing immutable pin is preserved to avoid meaningless pin-only pull requests.

## Consumer update pipeline

```text
LibreCodeCoop/.github catalog
↓
consumer sync-workflow-templates.yml
↓
LibreCodeCoop/github-workflows/actions/sync-workflows
↓
compare .github/actions-lock.txt
↓
copy changed catalog workflow
↓
apply optional consumer-local <workflow>.patch
↓
reviewable consumer pull request
```

Only workflows already installed in the consumer are managed. The sync Action does
not maintain a central consumer registry.

Generated templates must not be edited directly. Changes should come from:
The lock records the catalog version before consumer-local patching. If a local file
cannot be explained by the catalog plus its local patch, synchronization stops rather
than overwriting the divergence.

1. an upstream source revision change; or
2. an explicit downstream patch.
A local patch that no longer applies is surfaced for human intervention.

CI should detect when regenerated output differs from committed output.
## Organization patches vs consumer patches

## Release automation
Organization-level differences from Nextcloud belong in
`patches/nextcloud/*.patch` and should remain minimal.

Consumer-specific differences belong beside the installed workflow:

```text
.github/workflows/example.yml
.github/workflows/example.yml.patch
```

Release automation is split into two stages:
Do not move a consumer-only branch list, product dependency or credential assumption
into the organization template.

- **plan:** non-mutating validation and release proposal;
- **apply:** explicit mutation and publication.
## Distribution decision

Credentials remain in the consumer repository or protected environment.
The default model is a materialized workflow template because it remains visible,
reviewable and native to the consumer repository.

## Developer experience
A custom Action is appropriate when substantial deterministic logic can be extracted
from YAML and tested independently, as with `actions/sync-workflows`.

The distribution model has two complementary entry points:
Reusable workflows are not the default distribution model. Introduce one only when
GitHub Actions semantics clearly benefit from centralized execution and the consumer
still retains an explicit, reviewable interface.

1. **Discovery / first install:** `LibreCodeCoop/.github/workflow-templates/` provides the GitHub-native template cards, metadata and optional icons.
2. **Ongoing updates:** consumer repositories receive reviewable update pull requests generated from the tested templates in this repository.
## Credential-sensitive automation

When a workflow can be expressed as a thin caller of a reusable workflow, prefer that model because fixes remain centralized. When GitHub Actions semantics require a full installed workflow, publish the generated workflow template and keep its downstream differences as explicit patches here.
Catalog publication does not imply that every workflow is safe to install everywhere.
Dependency approval, auto-merge and release workflows follow the documented security
and credential policies and may intentionally remain repository-local.
67 changes: 67 additions & 0 deletions docs/dependency-update-policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!--
SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-->

# Dependency update and auto-merge policy

Dependency automation is more privileged than lint or test workflows because it can
create, approve or merge pull requests. It is therefore not automatically copied from
Nextcloud into the LibreCode catalog.

## Allowed actors

Automatic approval or merge may only act on pull requests created by explicitly
recognized dependency bots:

- GitHub Dependabot;
- other bots only after an explicit organization-level decision and equivalent actor
verification.

Do not auto-approve arbitrary pull requests based only on branch naming or labels.

## Merge policy

Default policy:

- patch and minor dependency updates may be eligible for auto-merge after all required
checks pass;
- major updates require human review unless a repository documents a narrower exception;
- security remediation may create a pull request automatically but must not bypass
required checks;
- approval and merge are separate operations and should remain independently auditable.

## Credentials

Prefer the `LibreCode Workflow Automation` GitHub App for cross-repository or
workflow-file mutations.

Do not require a maintainer's personal access token in a shared template. A PAT-based
workflow stays repository-local until it can be replaced with an organization-owned
credential model.

## pull_request_target

A workflow using `pull_request_target` must:

- verify the pull request actor before any privileged action;
- avoid checking out or executing untrusted pull-request code with write credentials;
- grant only the permissions required for metadata, approval or merge operations;
- pin every external Action to a full commit SHA.

## Distribution decision

Dependency/update workflows are cataloged only when their credential and actor model is
generic across LibreCode consumers.

Repository-specific combinations of Dependabot, Renovate, labels, branch naming or PATs
remain local workflows. The organization catalog should not centralize them merely to
reduce YAML duplication.

## Current decisions

- `dependabot-approve-merge.yml`: do not migrate the existing Extract workflow until it
uses the organization policy above and an organization-owned credential model.
- `npm-audit-fix.yml`: keep repository-local; it may create a remediation PR but should
not imply automatic approval/merge.
- obsolete Nextcloud OCP auto-merge workflows are not migrated.
52 changes: 52 additions & 0 deletions docs/security-policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!--
SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-->

# GitHub Actions security policy

This repository publishes workflow templates that execute inside consumer repositories.
The baseline policy is intentionally small, objective and testable.

## Mandatory rules

Published templates and this repository's own workflows must:

- pin external GitHub Actions to a full 40-character commit SHA;
- keep a human-readable version comment next to the pin when a stable release is known;
- configure `actions/checkout` with `persist-credentials: false`;
- avoid `permissions: write-all`;
- use explicit least-privilege workflow or job permissions.

The first three mechanically enforceable rules are checked by
`scripts/check_workflow_policy.py`. Existing actionlint and zizmor checks remain
responsible for syntax, expression and broader workflow security analysis.

## Exceptions

An exception must be explicit in the pull request that introduces it and must explain:

1. why the workflow cannot use the mandatory rule;
2. the smallest additional permission or credential needed;
3. how the risk is constrained;
4. how the exception will be tested.

Do not encode permanent organization-name bypasses when a generic permission or
capability check can express the same requirement.

## Credentials

Consumer repositories own runtime credentials and protected environments.
Organization-level GitHub App credentials may be used for public consumer repositories
when the organization plan allows them.

Mutating automation must prefer a GitHub App over personal access tokens. Personal or
bot PATs are a last resort and require an explicit documented exception.

## Governance

`github-workflows` validates workflow content. Repository rulesets and required-check
enforcement belong in `LibreCodeCoop/github-governance`.

A workflow policy check becomes a candidate required check only after it is stable on
the default branch and does not produce false positives on the published catalog.
Loading
Loading