Skip to content
Draft
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
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,11 @@ jobs:
uses: ./go-linter
with:
working-directory: test/go-linter

isms-change-management:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6

- name: Run ISMS change management action tests
run: bash isms-change-management/tests/run.sh
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
.DS_Store
.foreman

# Python
__pycache__/
*.pyc

# Ignore tests binaries
*.test
# But take CI env file
Expand Down
2 changes: 1 addition & 1 deletion .sclng/metadata.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dependencies = []
description = "Repository of GitHub Actions of the organisation"
flags = ["tools"]
languages = ["GitHub Action", "shell", "YAML"]
languages = ["GitHub Action", "Ruby", "shell", "YAML"]
owner = "etienne@scalingo.com"
team = "IST"
version = "1.1.2"
260 changes: 260 additions & 0 deletions isms-change-management/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
# ISMS Change Management Compliance Action

GitHub Action that validates changes to ISMS documents against the [ISMS Change Management Policy](https://github.com/Scalingo/specifications/blob/main/isms/Change-Management-Policy/ISMS-Change-Management-Policy-Fr.md).

It checks that:

1. **Author ≠ Validator** — no one can validate their own change
2. **Version is bumped** — the document version must be strictly increased
3. **Double validation** — minor or major version bumps require at least 2 validators
4. **RSSI role** *(optional)* — the RSSI must be a validator, with a specific exception when the RSSI is the author
5. **Commit authors** — every git commit author touching the file must be declared in the document's `authors` field
6. **PR reviewer coherence** *(optional)* — the `validators` field must exactly match the set of GitHub PR approvers
7. **Version matches content change** — the version bump level (patch/minor/major) must reflect the actual nature of the content change

## Usage

```yaml
jobs:
isms-compliance:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Check ISMS change management compliance
uses: Scalingo/actions/isms-change-management@main
with:
base-ref: main
rssi: "Yannick Jost"
cto: "Léo Unbekandt"
ceo: "Frédéric Harper"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wut?

github-token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ github.event.pull_request.number }}
```

## Inputs

| Input | Required | Default | Description |
|-----------------|----------|----------------|-------------|
| `base-ref` | no | merge-base with `origin/main` | Base branch ref to compare against (e.g. `main`) |
| `files-pattern` | no | `isms/**/*.md` | Glob pattern selecting which markdown files are ISMS documents |
| `rssi` | no | `""` | Comma-separated full name(s) of the RSSI holder(s). When set, enables the RSSI role check. |
| `cto` | no | `""` | Comma-separated full name(s) of the CTO(s). Used when `rssi` is set. |
| `ceo` | no | `""` | Comma-separated full name(s) of the CEO(s). Used when `rssi` is set. |
| `github-token` | no | `""` | GitHub token. When set together with `pr-number`, enables the PR reviewer coherence check and posts a summary comment on the PR. |
| `pr-number` | no | `""` | Pull request number. Required when `github-token` is provided. |

> `fetch-depth: 0` is required in the `actions/checkout` step so that git history is available for the base-ref comparison.

## Validation Checks

The action reads the YAML front matter of each changed ISMS document. The expected front matter shape is:

```yaml
---
version: 1.2.0
authors:
- Alice Martin
validators:
- Bob Dupont
---
```

### 1. Author ≠ Validator

The person who authors a change cannot also validate it. This enforces the separation of duties required by the ISMS Change Management Policy.

**Triggers an error when** any name appears in both the `authors` list and the `validators` list of the same document.

```yaml
# ❌ Error: Alice authored and validated the same change
authors:
- Alice Martin
validators:
- Alice Martin
- Bob Dupont
```

### 2. Version Bump Required

Every approved ISMS change must produce a new version of the document. The version uses [semver](https://semver.org/) (`MAJOR.MINOR.PATCH`).

**Triggers an error when** the `version` field in the changed document is equal to or lower than the version on the base branch.

```yaml
# Base branch: version: 1.1.0
# ❌ Error: version unchanged
version: 1.1.0

# ✅ OK: version increased
version: 1.1.1
```

New documents (not present on the base branch) are exempt from this check.

### 3. Double Validation for Minor and Major Bumps

The policy requires two validators for changes that materially affect the content of a document:

- **Minor bump** (`x.Y.z` increments): an article or step was added or removed
- **Major bump** (`X.y.z` increments): more than 50% of the content was rewritten

A **patch bump** (`x.y.Z` increments, cosmetic corrections) only requires 1 validator.

**Triggers an error when** a minor or major bump has fewer than 2 validators.

```yaml
# Base: version: 1.0.0 → new: 1.1.0 (minor bump)
# ❌ Error: only 1 validator for a minor bump
validators:
- Bob Dupont

# ✅ OK: 2 validators
validators:
- Bob Dupont
- Carol Lefèvre
```

### 4. RSSI Role Check *(enabled when `rssi` input is set)*

All ISMS documents must be validated by the RSSI. However, the RSSI cannot validate their own changes. When the RSSI is the author, the CTO or CEO must validate instead.

**Triggers an error when:**
- The RSSI is **not** the author **and** is **not** among the validators, or
- The RSSI **is** the author and neither the CTO nor the CEO is among the validators.

```yaml
# RSSI = "Yannick Jost", CTO = "Léo Unbekandt"

# ✅ Normal case: RSSI validates
authors:
- Alice Martin
validators:
- Yannick Jost

# ✅ Exception: RSSI is author, CTO validates
authors:
- Yannick Jost
validators:
- Léo Unbekandt

# ❌ Error: RSSI is not author and not validator
authors:
- Alice Martin
validators:
- Bob Dupont
```

### 5. Commit Authors Coherence

Every person who authors a git commit touching an ISMS document must be listed in that document's `authors` field. This ensures the declared authorship in the front matter reflects reality — you cannot silently modify a document without declaring yourself as an author.

**Triggers an error when** a commit author name (from `git log`) is not found in the document's `authors` list. Name matching is case-insensitive.

```yaml
# git log shows: Alice Martin committed on this file
# ❌ Error: Alice is not in authors
authors:
- Bob Dupont
validators:
- Yannick Jost

# ✅ OK
authors:
- Alice Martin
validators:
- Yannick Jost
```

### 6. PR Reviewer Coherence *(enabled when `github-token` + `pr-number` are set)*

The `validators` field must match the set of GitHub users who have actually approved the pull request. This check is **bidirectional**:

- Every YAML `validator` must have approved the PR on GitHub — you cannot claim someone validated your change if they did not.
- Every GitHub approver must be listed as a `validator` — all actual approvals must be documented.

Name matching is case-insensitive and uses the GitHub user's display name (falling back to their login if no display name is set).

**Triggers an error when:**
- A name in `validators` has not approved the PR, or
- A GitHub approver is not listed in `validators`.

```yaml
# PR approved by: Bob Dupont

# ❌ Error: Carol listed but did not approve; Bob approved but not listed
validators:
- Carol Lefèvre

# ✅ OK
validators:
- Bob Dupont
```

### 7. Version Matches Content Change

The version bump level must reflect the actual nature of the content change, as defined by the security policy:

| Content change | Required bump | Detection rule |
|----------------|---------------|----------------|
| Cosmetic / wording corrections | patch | fewer than 50 % of body lines changed, no heading change |
| Article or heading added / removed | **at least minor** | a heading line (`#…`) was added or removed |
| Substantial rewrite | **major** | more than 50 % of non-empty body lines changed |

A higher bump than required is always acceptable (e.g. bumping major for what is technically a minor change is fine). Only bumping *lower* than the content warrants is an error.

**Triggers an error when** the content change qualifies as minor or major but the version bump does not reach that level.

```yaml
# Body diff: 60 % of lines changed → classified as major
# ❌ Error: only a patch bump for a major content change
version: 1.0.1 # was 1.0.0

# ✅ OK: major bump matches major content change
version: 2.0.0 # was 1.0.0
```

## PR Comment

When `github-token` and `pr-number` are provided, the action posts (or updates) a single comment on the pull request summarising the result of every check for every changed ISMS document.

### All checks passed

> ## ISMS Change Management Compliance
>
> ### ✅ `isms/access-control/ISMS-Access-Control-Policy.md` — patch bump (1.3.0 → 1.3.1) — content change: **patch**
>
> - ✅ Author ≠ Validator
> - ✅ Version bumped
> - ✅ Double validation
> - ✅ Version matches content change
> - ✅ RSSI role
> - ✅ Commit authors coherence
> - ✅ PR reviewer coherence
>
> ---
>
> **Overall result: ✅ All checks passed.**

### Some checks failed

> ## ISMS Change Management Compliance
>
> ### ❌ `isms/change-management/ISMS-Change-Management-Policy.md` — patch bump (2.1.0 → 2.1.1) — content change: **minor**
>
> - ✅ Author ≠ Validator
> - ✅ Version bumped
> - ✅ Double validation
> - ❌ **Version matches content change**: Content change classified as **minor** (an article or heading was added or removed), but the version was only bumped as patch (2.1.0 → 2.1.1). A minor bump is required by the security policy.
> - ✅ RSSI role
> - ✅ Commit authors coherence
> - ❌ **PR reviewer coherence**: 'Carol Lefèvre' is listed as a validator but has not approved the PR on GitHub. Actual approvers: Bob Dupont
>
> ---
>
> **Overall result: ❌ Some checks failed. See details above.**

The comment is updated in place on each new push, so the PR always shows the latest status without accumulating duplicate comments.

47 changes: 47 additions & 0 deletions isms-change-management/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: "ISMS Change Management Compliance"
description: "Validate ISMS document changes: author/validator coherence, version bump, and role-based validation rules"

inputs:
base-ref:
description: "Base branch ref to compare against (e.g. 'main'). Defaults to the merge-base of HEAD and origin/main."
required: false
default: ""
files-pattern:
description: "Glob pattern for ISMS markdown documents to check"
required: false
default: "isms/**/*.md"
rssi:
description: "Comma-separated full name(s) of the RSSI holder(s). When set, enables the RSSI role check."
required: false
default: ""
cto:
description: "Comma-separated full name(s) of the CTO(s). Used when rssi is set."
required: false
default: ""
ceo:
description: "Comma-separated full name(s) of the CEO(s). Used when rssi is set."
required: false
default: ""
github-token:
description: "GitHub token for API access. When set together with pr-number, enables PR reviewer coherence check (Check 6)."
required: false
default: ""
pr-number:
description: "Pull request number. Required when github-token is provided."
required: false
default: ""

runs:
using: "composite"
steps:
- name: Check ISMS change management compliance
shell: bash
env:
BASE_REF: ${{ inputs.base-ref }}
FILES_PATTERN: ${{ inputs.files-pattern }}
RSSI: ${{ inputs.rssi }}
CTO: ${{ inputs.cto }}
CEO: ${{ inputs.ceo }}
GITHUB_TOKEN: ${{ inputs.github-token }}
PR_NUMBER: ${{ inputs.pr-number }}
run: ruby "${GITHUB_ACTION_PATH}/scripts/check_isms_change.rb"
Loading
Loading