Skip to content

Change release process to use changie.dev#1117

Draft
carbolymer wants to merge 1 commit intomasterfrom
mgalazyn/chore/use-changie-for-changelogs
Draft

Change release process to use changie.dev#1117
carbolymer wants to merge 1 commit intomasterfrom
mgalazyn/chore/use-changie-for-changelogs

Conversation

@carbolymer
Copy link
Contributor

Changelog

- description: |
    <insert-changelog-description-here>
# uncomment types applicable to the change:
  type:
  # - feature        # introduces a new feature
  # - breaking       # the API has changed in a breaking way
  # - compatible     # the API has changed but is non-breaking
  # - optimisation   # measurable performance improvements
  # - refactoring    # QoL changes
  # - bugfix         # fixes a defect
  # - test           # fixes/modifies tests
  # - maintenance    # not directly related to the code
  # - release        # related to a new release preparation
  # - documentation  # change in code docs, haddocks...
# uncomment at least one main project this PR is associated with
  projects:
  # - cardano-api
  # - cardano-api-gen
  # - cardano-rpc
  # - cardano-wasm

Context

Additional context for the PR goes here. If the PR fixes a particular issue please provide a link to the issue.

How to trust this PR

Highlight important bits of the PR that will make the review faster. If there are commands the reviewer can run to observe the new behavior, describe them.

Checklist

  • Commit sequence broadly makes sense and commits have useful messages
  • New tests are added if needed and existing tests are updated. See Running tests for more details
  • Self-reviewed the diff

@carbolymer carbolymer self-assigned this Mar 3, 2026
Comment on lines +10 to +137
name: Changelog fragment present and valid
runs-on: ubuntu-latest
steps:
- name: Skip on merge_group
if: github.event_name == 'merge_group'
run: echo "Skipping changelog check on merge_group"

- uses: actions/checkout@v4
if: github.event_name != 'merge_group'
with:
fetch-depth: 0

- name: Fetch base branch
if: github.event_name != 'merge_group'
run: git fetch origin ${{ github.event.pull_request.base.ref }}

- name: Check for no-changelog label
if: github.event_name != 'merge_group'
id: check-label
run: |
labels='${{ toJSON(github.event.pull_request.labels.*.name) }}'
if python3 -c "
import sys, json
labels = json.loads('$labels')
sys.exit(0 if 'no-changelog' in labels else 1)
" 2>/dev/null; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "PR has 'no-changelog' label — skipping fragment check."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: Find changelog fragments added in this PR
if: github.event_name != 'merge_group' && steps.check-label.outputs.skip != 'true'
id: find-fragments
run: |
BASE="origin/${{ github.event.pull_request.base.ref }}"
FRAGMENTS=$(git diff --name-only --diff-filter=A "$BASE...HEAD" \
| grep '^\.changes/unreleased/.*\.yaml$' || true)

if [ -z "$FRAGMENTS" ]; then
echo "found=false" >> "$GITHUB_OUTPUT"
else
echo "found=true" >> "$GITHUB_OUTPUT"
# Use random EOF delimiter to safely handle arbitrary fragment paths
EOF_DELIM=$(openssl rand -hex 8)
{
echo "fragments<<${EOF_DELIM}"
echo "$FRAGMENTS"
echo "${EOF_DELIM}"
} >> "$GITHUB_OUTPUT"
fi

- name: Fail if no fragment found
if: >-
github.event_name != 'merge_group'
&& steps.check-label.outputs.skip != 'true'
&& steps.find-fragments.outputs.found == 'false'
run: |
echo "::error::No changelog fragment found in .changes/unreleased/"
echo ""
echo "Every PR that changes user-visible behaviour must include a changie fragment."
echo "To add one, run:"
echo ""
echo " changie new --project <package>"
echo ""
echo "and commit the generated file."
echo "Add the 'no-changelog' label to skip this check for maintenance / test-only PRs."
exit 1

- name: Validate fragment files
if: >-
github.event_name != 'merge_group'
&& steps.check-label.outputs.skip != 'true'
&& steps.find-fragments.outputs.found == 'true'
env:
FRAGMENTS: ${{ steps.find-fragments.outputs.fragments }}
run: |
python3 << 'PYEOF'
import sys
import os
import yaml

VALID_KINDS = {'breaking', 'feature', 'compatible', 'bugfix', 'optimisation'}

fragments = [f for f in os.environ['FRAGMENTS'].strip().split('\n') if f]
all_ok = True

for path in fragments:
try:
with open(path) as fh:
fragment = yaml.safe_load(fh)
except Exception as exc:
print(f"::error file={path}::Failed to parse YAML: {exc}")
all_ok = False
continue

if not isinstance(fragment, dict):
print(f"::error file={path}::Fragment must be a YAML mapping, got {type(fragment).__name__}")
all_ok = False
continue

errors = []

kind = fragment.get('kind')
if not kind:
errors.append("missing required field 'kind'")
elif kind not in VALID_KINDS:
errors.append(
f"invalid kind '{kind}'; must be one of: {', '.join(sorted(VALID_KINDS))}"
)

if not fragment.get('body', '').strip():
errors.append("missing or empty required field 'body'")

custom = fragment.get('custom') or {}
if 'PR' not in custom:
errors.append("missing required field 'custom.PR' (PR number)")

if errors:
for err in errors:
print(f"::error file={path}::{err}")
all_ok = False
else:
print(f"Fragment OK: {path} (kind={kind}, PR={custom.get('PR')})")

sys.exit(0 if all_ok else 1)
PYEOF

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 11 days ago

In general, the fix is to explicitly define the GITHUB_TOKEN permissions for this workflow or job so that they are as restrictive as possible while still allowing the job to function. Since this job only checks changelog fragments and does not modify repository contents or PRs, it only needs read access to repository contents. GitHub recommends starting from contents: read as a minimal safe baseline.

The best targeted fix here is to add a permissions block for the check-fragment job, directly under the job’s name or runs-on keys. That keeps the scope narrow and avoids affecting other workflows. We will set contents: read, which is sufficient for actions/checkout and any local git operations. No other permission types (like pull-requests or issues) are required because the job only uses the event payload already provided by GitHub, not additional API calls.

Concretely: in .github/workflows/check-changelog-fragment.yml, within the jobs: check-fragment: section starting at line 9, insert:

    permissions:
      contents: read

indented to align with name: and runs-on:. No imports, methods, or additional definitions are needed because this is a pure YAML configuration change.

Suggested changeset 1
.github/workflows/check-changelog-fragment.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/check-changelog-fragment.yml b/.github/workflows/check-changelog-fragment.yml
--- a/.github/workflows/check-changelog-fragment.yml
+++ b/.github/workflows/check-changelog-fragment.yml
@@ -9,6 +9,8 @@
   check-fragment:
     name: Changelog fragment present and valid
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     steps:
       - name: Skip on merge_group
         if: github.event_name == 'merge_group'
EOF
@@ -9,6 +9,8 @@
check-fragment:
name: Changelog fragment present and valid
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Skip on merge_group
if: github.event_name == 'merge_group'
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +18 to +24
name: check-changelog (superseded — see check-changelog-fragment.yml)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
if: ${{ github.event_name != 'merge_group' }}
with:
node-version: 22

- run: npm install js-yaml@4.1.0
if: ${{ github.event_name != 'merge_group' }}

- name: Fail if PR changelog is not correct
if: ${{ github.event_name != 'merge_group' }}
uses: actions/github-script@v8
id: check-changelog
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const yaml = require('js-yaml');
const fs = require('fs');
const execSync = require('child_process').execSync;

const prDescription = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});

const changelogRegex = /# Changelog[\s\S]*?```yaml([\s\S]*?)```/;
const changelogMatch = prDescription.data.body.match(changelogRegex);
const yamlContent = changelogMatch ? changelogMatch[1].trim() : '';
yamlContent || console.error('Failed to find changelog YAML section in the "Changelog" paragraph');

try {
changelog = yaml.load(yamlContent)[0];
} catch (e) {
console.error('Failed to parse YAML changelog as array:', yamlContent);
process.exit(1);
}

try {
config = yaml.load(fs.readFileSync('.cardano-dev.yaml', 'utf8'));
} catch (e) {
console.error('Failed to load .cardano-dev.yaml config:', e);
process.exit(1);
}

let isCompatibilityValid = false;
if (!changelog.compatibility) {
isCompatibilityValid = true;
}
if (!isCompatibilityValid) {
console.error('Changelog field "compatibility" is deprecated and no longer used. Please remove it.');
}

let isTypeValid = false;
const validTypeValues = Object.keys(config.changelog.options.type);
if (Array.isArray(changelog.type) && !!changelog.type) {
isTypeValid = changelog.type.every(value => validTypeValues.includes(value));
} else {
isTypeValid = validTypeValues.includes(changelog.type);
}
if (!isTypeValid) {
console.error(`PR changelog has invalid type: ${changelog.type}\nExpected one, or more of: ${validTypeValues}`)
}

let isProjectsValid = false;
// .filter(Boolean) is a trick that removes empty values from the array (see https://michaeluloth.com/javascript-filter-boolean/)
const validProjectsValues = execSync("ls */CHANGELOG* | cut -d/ -f1").toString().split('\n').filter(Boolean)
if (Array.isArray(changelog.projects) && !!changelog.projects) {
isProjectsValid = changelog.projects.every(value => validProjectsValues.includes(value));
} else {
isProjectsValid = validProjectsValue.includes(changelog.projects);
}
if (!isProjectsValid) {
console.error(`PR changelog has invalid project: ${changelog.projects}\nExpected one, or more of: ${validProjectsValues}`)
}

let isDescriptionValid = true;
if (changelog.description.trim() === '<insert-changelog-description-here>') {
console.error('PR changelog description has not been updated!')
isDescriptionValid = false;
} else if (!changelog.description.trim()) {
console.error('PR changelog description field is missing!')
isDescriptionValid = false;
}

if (!isCompatibilityValid || !isTypeValid || !isProjectsValid || !isDescriptionValid) {
console.error('Failed PR changelog checks!');
process.exit(1);
}

- name: Pass (no-op)
run: |
echo "This check is a no-op. Changelog validation is now performed by"
echo "the 'check-changelog-fragment' workflow (check-changelog-fragment.yml)."

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}

Copilot Autofix

AI 11 days ago

To fix the problem, explicitly restrict the GITHUB_TOKEN permissions for this workflow to the minimum required. Since this job is a pure no-op that only runs echo commands and does not interact with the repository or GitHub APIs, it can safely run with contents: read (or even contents: none if supported). Setting the permissions at the workflow root applies them to all jobs in this file and best matches the CodeQL recommendation.

The single best fix without changing functionality is to add a permissions block at the top level of .github/workflows/check-pr-changelog.yml, alongside name and on. Insert this block after the name: declaration (around line 11), for example:

permissions:
  contents: read

No imports or additional methods are needed, since this is a YAML workflow file, not application code.

Suggested changeset 1
.github/workflows/check-pr-changelog.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/check-pr-changelog.yml b/.github/workflows/check-pr-changelog.yml
--- a/.github/workflows/check-pr-changelog.yml
+++ b/.github/workflows/check-pr-changelog.yml
@@ -8,6 +8,9 @@
 
 name: Check if PR changelog was filled correctly (legacy — no-op)
 
+permissions:
+  contents: read
+
 on:
   merge_group:
   pull_request:
EOF
@@ -8,6 +8,9 @@

name: Check if PR changelog was filled correctly (legacy — no-op)

permissions:
contents: read

on:
merge_group:
pull_request:
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant