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
51 changes: 51 additions & 0 deletions .github/workflows/_check_changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Check changelog

on:
workflow_call:
inputs:
workflows_ref:
description: 'The current reference in use for the ledger-app-workflow repository'
required: true
type: string
app_repository:
description: 'The URL of the app repository to check. Defaults to the workflow caller repository URL'
required: false
default: ${{ github.repository }}
type: string

permissions:
contents: read # Needed to clone the repositories

env:
WORKFLOWS_REF: ${{ inputs.workflows_ref }}
APP_REPOSITORY: ${{ inputs.app_repository }}

jobs:
check_changelog:
name: Check changelog
runs-on: ubuntu-22.04

steps:
- name: Clone ledger-app-workflows repository
uses: actions/checkout@v6
with:
repository: LedgerHQ/ledger-app-workflows
path: ./ledger-app-workflows
ref: ${{ inputs.workflows_ref }}

- name: Clone app repository
uses: actions/checkout@v6
with:
repository: ${{ inputs.app_repository }}
path: app-repository
submodules: recursive
# Full history is required to compute the diff between the PR base and head
fetch-depth: 0

- name: Run script
env:
# Provide the exact PR base/head so the check does not have to guess the base branch
CHANGELOG_BASE_REF: ${{ github.event.pull_request.base.sha }}
CHANGELOG_HEAD_REF: ${{ github.event.pull_request.head.sha }}
run: |
./ledger-app-workflows/scripts/check_all.sh -c changelog -a ./app-repository
10 changes: 10 additions & 0 deletions .github/workflows/reusable_guidelines_enforcer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ jobs:
app_repository: ${{ inputs.app_repository }}
workflows_ref: ${{ needs.call_get_workflow_version.outputs.version }}

call_check_changelog:
name: Dispatch check
needs: call_get_workflow_version
# Only relevant on pull requests, and skipped when the 'no_changelog' label is set
if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'no_changelog')
uses: ./.github/workflows/_check_changelog.yml
with:
app_repository: ${{ inputs.app_repository }}
workflows_ref: ${{ needs.call_get_workflow_version.outputs.version }}

call_clang_static_analyzer:
name: Dispatch check
needs: [call_get_app_metadata, call_get_workflow_version]
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.98.0] - 2026-06-22

### Added

- `reusable_guidelines_enforcer.yml` : add a `changelog` check ensuring that a repository owning a `CHANGELOG` file updates it in the PR (can be bypassed with the `no_changelog` label). The check is also available locally through `check_all.sh -c changelog`.

## [1.97.0] - 2026-06-22

### Added
Expand Down
7 changes: 7 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ In addition, the following secret can be used:
| --------- | -------- | ------------- | ------------------------------------------------- |
| git_token | ❌ | | A token used as authentication for GIT operations |

On pull requests, if the repository owns a `CHANGELOG` file, this workflow checks that it has been
updated by the PR. This check can be bypassed by adding the `no_changelog` label to the PR.

This check is also part of `check_all.sh` and can be run locally with `check_all.sh -c changelog`.
When run locally, it compares the current branch against the default branch (`origin/HEAD`, falling
back to `origin/main`/`origin/master`).

## Reusable Lint

In order to check an App, this workflow can use the following input parameters:
Expand Down
7 changes: 6 additions & 1 deletion scripts/check_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ VERBOSE=false
IS_RUST=false

# All available checks ('manifest' must be the first one)
ALL_CHECKS="manifest icons app_load_params makefile readme scan"
ALL_CHECKS="manifest icons app_load_params makefile readme changelog scan"

#===============================================================================
#
Expand Down Expand Up @@ -223,6 +223,11 @@ call_step() {
"readme")
COMMAND="${dirName}/check_readme.sh ${APP_DIR} ${REPO_NAME}"
;;
"changelog")
# CHANGELOG_BASE_REF/CHANGELOG_HEAD_REF are set by the CI (PR base/head).
# When unset (local run), check_changelog.sh guesses the base branch.
COMMAND="${dirName}/check_changelog.sh ${APP_DIR} ${CHANGELOG_BASE_REF:-} ${CHANGELOG_HEAD_REF:-}"
;;
"scan")
if [[ -n ${TARGET} ]]; then
log_bold "********* Processing target: ${TARGET}"
Expand Down
79 changes: 79 additions & 0 deletions scripts/check_changelog.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bash

set -e

# shellcheck source=/dev/null
source "$(dirname "$0")/logger.sh"

# Determine the reference to diff against (base branch).
# In CI, the caller provides the PR base SHA explicitly.
# Locally, we try to guess the default branch the current work will be merged into.
guess_base_ref() {
local repo="$1"
local candidate

# Default remote branch (e.g. 'origin/main') as set by 'git clone'
if candidate=$(git -C "${repo}" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null); then
echo "${candidate}"
return 0
fi

# Fallback to the usual default branch names
for candidate in origin/main origin/master main master; do
if git -C "${repo}" rev-parse --verify --quiet "${candidate}^{commit}" >/dev/null; then
echo "${candidate}"
return 0
fi
done

return 1
}

main() (
repo="$1"
base_ref="$2"
head_ref="${3:-HEAD}"

# Look for any tracked CHANGELOG* file (CHANGELOG, CHANGELOG.md, CHANGELOG.rst, ...)
changelog_files=$(git -C "${repo}" ls-files | grep -E '(^|/)CHANGELOG[^/]*$' || true)

if [[ -z "${changelog_files}" ]]; then
log_warning "No CHANGELOG file found in the repository, changelog check skipped"
return 0
fi

# When no base reference is provided (local run), try to guess it
if [[ -z "${base_ref}" ]]; then
if ! base_ref=$(guess_base_ref "${repo}"); then
log_warning "Could not determine the base branch, changelog check skipped"
return 0
fi
log_info "No base reference provided, comparing against '${base_ref}'"
fi

# Nothing to compare if base and head point to the same commit (e.g. run on the base branch)
if [[ "$(git -C "${repo}" rev-parse "${base_ref}")" == "$(git -C "${repo}" rev-parse "${head_ref}")" ]]; then
log_warning "Head and base references are identical, changelog check skipped"
return 0
fi

# List of files modified since the merge-base between base and head
changed_files=$(git -C "${repo}" diff --name-only "${base_ref}...${head_ref}")

while read -r changelog; do
if grep -qxF "${changelog}" <<< "${changed_files}"; then
log_success "CHANGELOG file '${changelog}' was updated"
return 0
fi
done <<< "${changelog_files}"

log_error "A CHANGELOG file exists but was not updated:"
while read -r changelog; do
log_error_no_header " - ${changelog}"
done <<< "${changelog_files}"
log_error_no_header "Please document your changes in the CHANGELOG."
log_error_no_header "If this PR does not require a CHANGELOG entry, add the 'no_changelog' label to bypass this check."
return 1
)

main "$@"
Loading