Skip to content

Commit 9d0a652

Browse files
authored
SOLR-17619 Use logchange for changelog management (#3044)
Co-authored-by: David Smiley <[email protected]>
1 parent 4cdc618 commit 9d0a652

31 files changed

+2424
-1389
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,9 @@ ij_yaml_space_before_colon = false
671671
ij_yaml_spaces_within_braces = true
672672
ij_yaml_spaces_within_brackets = true
673673

674+
[dev-tools/scripts/releaseWizard.yaml]
675+
trim_trailing_whitespace = false
676+
674677
# dos files
675678
[{*.bat,*.cmd}]
676679
end_of_line = crlf

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Before creating a pull request, please file an issue in the ASF Jira system for
77
88
* https://issues.apache.org/jira/projects/SOLR
99
10-
For something minor (i.e. that wouldn't be worth putting in release notes), you can skip JIRA.
10+
For something minor (i.e. that wouldn't be worth putting in release notes), you can skip JIRA.
1111
To create a Jira issue, you will need to create an account there first.
1212
1313
The title of the PR should reference the Jira issue number in the form:
@@ -42,3 +42,4 @@ Please review the following and check all that apply:
4242
- [ ] I have run `./gradlew check`.
4343
- [ ] I have added tests for my changes.
4444
- [ ] I have added documentation for the [Reference Guide](https://github.com/apache/solr/tree/main/solr/solr-ref-guide)
45+
- [ ] I have added a [changelog entry](https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc) for my change
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Licensed to the Apache Software Foundation (ASF) under one or more
5+
# contributor license agreements. See the NOTICE file distributed with
6+
# this work for additional information regarding copyright ownership.
7+
# The ASF licenses this file to You under the Apache License, Version 2.0
8+
# (the "License"); you may not use this file except in compliance with
9+
# the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
#
19+
20+
"""
21+
Validates changelog YAML files in changelog/unreleased/ folder.
22+
23+
Checks:
24+
- File is valid YAML
25+
- Contains required 'title' field (non-empty string)
26+
- Contains required 'type' field (one of: added, changed, fixed, deprecated, removed, dependency_update, security, other)
27+
- Contains required 'authors' field with at least one author
28+
- Each author has a 'name' field (non-empty string)
29+
"""
30+
31+
import sys
32+
import yaml
33+
34+
35+
def validate_changelog_yaml(file_path):
36+
"""Validate a changelog YAML file."""
37+
valid_types = ['added', 'changed', 'fixed', 'deprecated', 'removed', 'dependency_update', 'security', 'other']
38+
39+
try:
40+
with open(file_path, 'r', encoding='utf-8') as f:
41+
data = yaml.safe_load(f)
42+
43+
# Check if file contains a mapping (dictionary)
44+
if not isinstance(data, dict):
45+
print(f"::error file={file_path}::File must contain YAML mapping (key-value pairs)")
46+
return False
47+
48+
# Validate 'title' field
49+
if 'title' not in data or not data['title']:
50+
print(f"::error file={file_path}::Missing or empty 'title' field")
51+
return False
52+
53+
if not isinstance(data['title'], str) or not data['title'].strip():
54+
print(f"::error file={file_path}::Field 'title' must be a non-empty string")
55+
return False
56+
57+
# Validate 'type' field
58+
if 'type' not in data or not data['type']:
59+
print(f"::error file={file_path}::Missing or empty 'type' field")
60+
return False
61+
62+
if data['type'] not in valid_types:
63+
print(f"::error file={file_path}::Invalid 'type': '{data['type']}'. Must be one of: {', '.join(valid_types)}")
64+
return False
65+
66+
# Validate 'authors' field
67+
if 'authors' not in data or not data['authors']:
68+
print(f"::error file={file_path}::Missing or empty 'authors' field")
69+
return False
70+
71+
if not isinstance(data['authors'], list):
72+
print(f"::error file={file_path}::Field 'authors' must be a list")
73+
return False
74+
75+
if len(data['authors']) == 0:
76+
print(f"::error file={file_path}::Field 'authors' must contain at least one author")
77+
return False
78+
79+
# Validate each author
80+
for i, author in enumerate(data['authors']):
81+
if not isinstance(author, dict):
82+
print(f"::error file={file_path}::Author {i} must be a mapping (key-value pairs)")
83+
return False
84+
if 'name' not in author or not author['name']:
85+
print(f"::error file={file_path}::Author {i} missing or empty 'name' field")
86+
return False
87+
if not isinstance(author['name'], str) or not author['name'].strip():
88+
print(f"::error file={file_path}::Author {i} 'name' must be a non-empty string")
89+
return False
90+
91+
# All validations passed
92+
print(f"✓ {file_path} is valid")
93+
print(f" Title: {data['title']}")
94+
print(f" Type: {data['type']}")
95+
print(f" Authors: {', '.join(a['name'] for a in data['authors'])}")
96+
return True
97+
98+
except yaml.YAMLError as e:
99+
print(f"::error file={file_path}::Invalid YAML: {e}")
100+
return False
101+
except Exception as e:
102+
print(f"::error file={file_path}::Error validating file: {e}")
103+
return False
104+
105+
106+
if __name__ == '__main__':
107+
if len(sys.argv) < 2:
108+
print("Usage: validate-changelog-yaml.py <yaml-file>")
109+
sys.exit(1)
110+
111+
file_path = sys.argv[1]
112+
if not validate_changelog_yaml(file_path):
113+
sys.exit(1)
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
name: Validate Changelog
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- '*'
7+
8+
jobs:
9+
validate-changelog:
10+
name: Check changelog entry
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v5
16+
with:
17+
fetch-depth: 0
18+
19+
- name: Check for no-changelog label
20+
id: check-label
21+
run: |
22+
LABELS='${{ toJson(github.event.pull_request.labels) }}'
23+
if echo "$LABELS" | grep -q '"no-changelog"'; then
24+
echo "skip=true" >> $GITHUB_OUTPUT
25+
else
26+
echo "skip=false" >> $GITHUB_OUTPUT
27+
fi
28+
29+
- name: Check for CHANGES.txt edits
30+
if: steps.check-label.outputs.skip == 'false'
31+
run: |
32+
# Get the list of changed files
33+
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
34+
35+
if echo "$CHANGED_FILES" | grep -q "^solr/CHANGES\.txt$"; then
36+
echo "::error::Use of solr/CHANGES.txt is deprecated. Please create a changelog yaml file instead."
37+
echo ""
38+
echo "Instead of editing CHANGES.txt, please:"
39+
echo "1. Run: ./gradlew writeChangelog"
40+
echo "2. Edit the generated YAML file in changelog/unreleased/"
41+
echo "3. Commit both the code change and the YAML file"
42+
echo ""
43+
echo "For more information, see: dev-docs/changelog.adoc"
44+
echo ""
45+
echo "If this PR should not have a changelog entry (e.g., documentation-only changes),"
46+
echo "add the 'no-changelog' label to this PR."
47+
exit 1
48+
fi
49+
50+
- name: Check for changelog entry
51+
if: steps.check-label.outputs.skip == 'false'
52+
run: |
53+
# Get the list of changed files
54+
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
55+
56+
# Check if any files were added to changelog/unreleased/
57+
if echo "$CHANGED_FILES" | grep -q "^changelog/unreleased/"; then
58+
echo "✓ Changelog entry found"
59+
exit 0
60+
fi
61+
62+
# Check if only docs/tests/comments were changed (common exceptions)
63+
HAS_NON_DOCS_CHANGES=false
64+
while IFS= read -r file; do
65+
# Skip changelog, docs, tests, and certain config files
66+
if ! echo "$file" | grep -qE "(^changelog/|^solr/solr-ref-guide/|^dev-docs/|\.md$|\.adoc$|^solr/.*/test|\.gradle$|\.properties$|README|NOTICE|LICENSE)"; then
67+
HAS_NON_DOCS_CHANGES=true
68+
break
69+
fi
70+
done <<< "$CHANGED_FILES"
71+
72+
if [ "$HAS_NON_DOCS_CHANGES" = false ]; then
73+
echo "✓ No code changes detected (docs/tests only)"
74+
exit 0
75+
fi
76+
77+
echo "::error::This PR appears to contain code changes but no changelog entry was added."
78+
echo ""
79+
echo "Please add a changelog entry by:"
80+
echo "1. Running: ./gradlew writeChangelog"
81+
echo "2. Editing the generated YAML file in changelog/unreleased/"
82+
echo "3. Committing the YAML file"
83+
echo ""
84+
echo "For more information, see: dev-docs/changelog.adoc"
85+
echo ""
86+
echo "If this PR should not have a changelog entry (e.g., refactoring, internal cleanup),"
87+
echo "add the 'no-changelog' label to this PR."
88+
exit 1
89+
90+
- name: Validate changelog YAML structure
91+
if: steps.check-label.outputs.skip == 'false'
92+
run: |
93+
# Get the list of changed files
94+
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
95+
96+
# Find all YAML files added to changelog/unreleased/
97+
YAML_FILES=$(echo "$CHANGED_FILES" | grep "^changelog/unreleased/.*\.ya\?ml$" || true)
98+
99+
if [ -z "$YAML_FILES" ]; then
100+
exit 0
101+
fi
102+
103+
echo "Validating changelog YAML files..."
104+
VALIDATION_FAILED=false
105+
106+
while IFS= read -r file; do
107+
if [ -z "$file" ]; then
108+
continue
109+
fi
110+
111+
echo ""
112+
echo "Validating: $file"
113+
114+
# Validate using a Python script
115+
python3 .github/scripts/validate-changelog-yaml.py "$file"
116+
117+
if [ $? -ne 0 ]; then
118+
VALIDATION_FAILED=true
119+
fi
120+
121+
done <<< "$YAML_FILES"
122+
123+
if [ "$VALIDATION_FAILED" = true ]; then
124+
echo "Please see dev-docs/changelog.adoc for more info."
125+
126+
exit 1
127+
fi

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ gradle/wrapper/gradle-wrapper.jar
4141

4242
# WANT TO ADD MORE? You can tell Git without adding to this file:
4343
# See https://git-scm.com/docs/gitignore
44-
# In particular, if you have tools you use, add to $GIT_DIR/info/exclude or use core.excludesFile
44+
# In particular, if you have tools you use, add to $GIT_DIR/info/exclude or use core.excludesFile

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!-- @formatter:off -->
2+
<!-- noinspection -->
3+
<!-- Prevents auto format, for JetBrains IDE File > Settings > Editor > Code Style (Formatter Tab) > Turn formatter on/off with markers in code comments -->
4+
5+
<!-- This file is automatically generate by logchange tool 🌳 🪓 => 🪵 -->
6+
<!-- Visit https://github.com/logchange/logchange and leave a star 🌟 -->
7+
<!-- !!! ⚠️ DO NOT MODIFY THIS FILE, YOUR CHANGES WILL BE LOST ⚠️ !!! -->
8+
9+
10+
DUMMY changelog file.
11+
12+
We are in the process of migrating from CHANGES.txt to a structured approach to changelog generation. See [SOLR-17619](https://issues.apache.org/jira/browse/SOLR-17619) as well as [dev-docs/changelog.adoc](dev-docs/changelog.adoc) for details.
13+
14+
[unreleased]
15+
------------
16+
17+
### Added (1 change)
18+
19+
- Dummy issue [SOLR-123](https://issues.apache.org/jira/browse/SOLR-123) (janhoy)
20+
21+
[9.9.0] - 2025-07-24
22+
--------------------
23+
24+
### Added (1 change)
25+
26+
- Dummy issue for release 9.9 [SOLR-124](https://issues.apache.org/jira/browse/SOLR-124) (janhoy)

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ plugins {
3333
alias(libs.plugins.diffplug.spotless) apply false
3434
alias(libs.plugins.nodegradle.node) apply false
3535
alias(libs.plugins.openapi.generator) apply false
36+
alias(libs.plugins.logchange)
3637
}
3738

3839
// Declare default Java versions for the entire project and for SolrJ separately
@@ -216,3 +217,5 @@ apply from: file('gradle/solr/packaging.gradle')
216217
apply from: file('gradle/solr/solr-forbidden-apis.gradle')
217218

218219
apply from: file('gradle/node.gradle')
220+
221+
apply from: file('gradle/changelog.gradle')

changelog/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
# New changelog process
18+
19+
We are in the process of migrating to a new way of managing our changelog. Please see [dev-docs/changelog.adoc](../dev-docs/changelog.adoc) for details.
20+
21+
In a transition period it is still possible to merge your changelog entry to `solr/CHANGES.txt`, but then you can only use the new process.

0 commit comments

Comments
 (0)