diff --git a/.github/workflows/validate-changenotes.yml b/.github/workflows/validate-changenotes.yml deleted file mode 100644 index b750bf578..000000000 --- a/.github/workflows/validate-changenotes.yml +++ /dev/null @@ -1,187 +0,0 @@ -name: Validate Change Notes - -on: - pull_request_target: - types: [opened, reopened, synchronize] - -permissions: - contents: read - pull-requests: write - issues: write - -jobs: - validate-changenotes: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 0 - - - name: Checkout base branch - run: | - git fetch origin ${{ github.base_ref }} - - - name: Get all changed files in PR - id: all-files - uses: tj-actions/changed-files@v47 - with: - base_sha: ${{ github.event.pull_request.base.sha }} - - - name: Get changed note files - id: changed-files - uses: tj-actions/changed-files@v47 - with: - files: | - editions/*/tiddlers/releasenotes/**/*.tid - base_sha: ${{ github.event.pull_request.base.sha }} - - - name: Check if PR needs change notes - id: check-needs-notes - run: | - chmod +x bin/changenote.sh - - if bin/changenote.sh check-needs ${{ steps.all-files.outputs.all_changed_files }}; then - echo "needs_note=true" >> $GITHUB_OUTPUT - else - echo "needs_note=false" >> $GITHUB_OUTPUT - fi - - echo "has_notes=${{ steps.changed-files.outputs.any_changed }}" >> $GITHUB_OUTPUT - shell: bash - - - name: Validate change note format - if: steps.changed-files.outputs.any_changed == 'true' - id: validate - continue-on-error: true - run: | - chmod +x bin/changenote.sh - - if bin/changenote.sh validate ${{ steps.changed-files.outputs.all_changed_files }}; then - echo "result=success" >> $GITHUB_OUTPUT - else - echo "result=failure" >> $GITHUB_OUTPUT - # Save error details if they exist - if [ -f /tmp/validation_errors.md ]; then - { - echo 'errors<> $GITHUB_OUTPUT - fi - exit 1 - fi - shell: bash - - - name: Parse change note summaries - if: steps.changed-files.outputs.any_changed == 'true' && steps.validate.outcome == 'success' - id: parse-notes - run: | - chmod +x bin/changenote.sh - - summaries=$(bin/changenote.sh parse ${{ steps.changed-files.outputs.all_changed_files }}) - - { - echo 'summaries<> $GITHUB_OUTPUT - shell: bash - - - name: Find existing bot comment - if: always() - uses: peter-evans/find-comment@v3 - id: find-comment - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: 'Change Note Status' - - - name: Create or update comment (validation passed) - if: steps.changed-files.outputs.any_changed == 'true' && steps.validate.outcome == 'success' - uses: peter-evans/create-or-update-comment@v5 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: replace - body: | - ## ✅ Change Note Status - - All change notes are properly formatted and validated! - - ${{ steps.parse-notes.outputs.summaries }} - -
- 📖 Change Note Guidelines - - Change notes help track and communicate changes effectively. See the [full documentation](https://tiddlywiki.com/prerelease/#Release%20Notes%20and%20Changes) for details. - -
- - - name: Create or update comment (validation failed) - if: steps.changed-files.outputs.any_changed == 'true' && steps.validate.outcome == 'failure' - uses: peter-evans/create-or-update-comment@v5 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: replace - body: | - ## ❌ Change Note Status - - Change note validation failed. Please fix the following issues: - - ${{ steps.validate.outputs.errors }} - - --- - - [Release Notes and Changes](https://tiddlywiki.com/prerelease/#Release%20Notes%20and%20Changes) - - - name: Create or update comment (missing change note) - if: steps.check-needs-notes.outputs.needs_note == 'true' && steps.changed-files.outputs.any_changed != 'true' - uses: peter-evans/create-or-update-comment@v5 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: replace - body: | - ## ⚠️ Change Note Status - - This PR appears to contain code changes but doesn't include a change note. - - Please add a change note by creating a `.tid` file in `editions/tw5.com/tiddlers/releasenotes//` with the following format: - - [Release Notes and Changes](https://tiddlywiki.com/prerelease/#Release%20Notes%20and%20Changes) - - Note: If this is a documentation-only change or doesn't require a change note, you can ignore this message. - - - name: Create or update comment (doc only - has no notes) - if: steps.check-needs-notes.outputs.needs_note == 'false' && steps.changed-files.outputs.any_changed != 'true' - uses: peter-evans/create-or-update-comment@v5 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: replace - body: | - ## ✅ Change Note Status - - This PR contains documentation or configuration changes that typically don't require a change note. - - - name: Remove comment if not needed - if: steps.check-needs-notes.outputs.needs_note == 'false' && steps.changed-files.outputs.any_changed == 'true' - uses: peter-evans/create-or-update-comment@v5 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: replace - body: | - ## ✅ Change Note Status - - This PR contains documentation or configuration changes that typically don't require a change note. - - - name: Fail workflow if validation failed - if: steps.validate.outcome == 'failure' - run: | - echo "::error::Change note validation failed. Please check the PR comment for details." - exit 1 diff --git a/bin/changenote.sh b/bin/changenote.sh deleted file mode 100644 index 51c8c3755..000000000 --- a/bin/changenote.sh +++ /dev/null @@ -1,307 +0,0 @@ -#!/bin/bash - -# TiddlyWiki Change Note Management Script -# Usage: -# changenote.sh check-needs - Check if files require change notes -# changenote.sh validate - Validate change note format -# changenote.sh parse - Parse and generate summary - -# Define valid values according to "Release Notes and Changes.tid" -VALID_TYPES=("bugfix" "feature" "enhancement" "deprecation" "security" "pluginisation") -VALID_CATEGORIES=("internal" "translation" "plugin" "widget" "filters" "usability" "palette" "hackability" "nodejs" "performance" "developer") - -# Type emoji mapping -declare -A TYPE_EMOJI=( - ["bugfix"]="🐛" - ["feature"]="✨" - ["enhancement"]="⚡" - ["deprecation"]="⚠️" - ["security"]="🔒" - ["pluginisation"]="🔌" -) - -# Category emoji mapping -declare -A CATEGORY_EMOJI=( - ["internal"]="🔧" - ["translation"]="🌐" - ["plugin"]="🔌" - ["widget"]="📦" - ["filters"]="🔍" - ["usability"]="👥" - ["palette"]="🎨" - ["hackability"]="🛠️" - ["nodejs"]="💻" - ["performance"]="⚡" - ["developer"]="👨‍💻" -) - -# Function: Check if files need change notes -# Returns 0 if needs change note, 1 if not needed -check_needs_changenote() { - local all_files="$@" - - if [ -z "$all_files" ]; then - echo "No files provided" - return 1 - fi - - for file in $all_files; do - # Skip GitHub workflows/configs - [[ "$file" =~ ^\.github/ ]] && continue - [[ "$file" =~ ^\.vscode/ ]] && continue - - # Skip config files - [[ "$file" =~ ^\.editorconfig$ ]] && continue - [[ "$file" =~ ^\.gitignore$ ]] && continue - [[ "$file" =~ ^LICENSE$ ]] && continue - - # Skip markdown files (except readme.md) - [[ "$file" =~ \.md$ ]] && [[ ! "$file" =~ /readme\.md$ ]] && continue - - # Skip documentation in bin folder - [[ "$file" =~ ^bin/.*\.md$ ]] && continue - - # Skip test results and reports - [[ "$file" =~ ^playwright-report/ ]] && continue - [[ "$file" =~ ^test-results/ ]] && continue - - # Skip documentation editions - [[ "$file" =~ ^editions/.*-docs?/ ]] && continue - - # Check if it's a tiddler file - if [[ "$file" =~ ^editions/.*/tiddlers/.*\.tid$ ]]; then - # Core modules, plugins should require change notes - [[ "$file" =~ /(core|plugin\.info|modules)/ ]] && echo "✓ Code file: $file" && return 0 - - # Release notes themselves don't require additional notes - [[ "$file" =~ /releasenotes/ ]] && continue - - # Other tiddlers are documentation - continue - fi - - # If we reach here, it's a code file that needs a change note - echo "✓ Code file requires change note: $file" - return 0 - done - - # All files are documentation/config - echo "✓ Only documentation/configuration changes" - return 1 -} - -# Function: Validate change note format -validate_changenotes() { - local files="$@" - local has_errors=false - local error_details="" - - if [ -z "$files" ]; then - echo "No change note files to validate." - return 0 - fi - - for file in $files; do - echo "Validating: $file" - - # Check if file exists - if [ ! -f "$file" ]; then - echo "::error file=$file::File not found" - has_errors=true - continue - fi - - # Check if it's a changenote file - if [[ ! "$file" =~ editions/.*/tiddlers/releasenotes/.* ]]; then - continue - fi - - # Extract metadata from the .tid file - title=$(grep -m 1 "^title: " "$file" | sed 's/^title: //') - tags=$(grep -m 1 "^tags: " "$file" | sed 's/^tags: //') - change_type=$(grep -m 1 "^change-type: " "$file" | sed 's/^change-type: //') - change_category=$(grep -m 1 "^change-category: " "$file" | sed 's/^change-category: //') - description=$(grep -m 1 "^description: " "$file" | sed 's/^description: //') - - # Track errors for this file - file_has_errors=false - file_errors="" - - # Validate title format - if [[ ! "$title" =~ ^\$:/changenotes/[0-9]+\.[0-9]+\.[0-9]+/(#[0-9]+|[a-f0-9]{40})$ ]]; then - echo "::error file=$file::Invalid title format" - file_errors+="- Title format: Expected \`\$:/changenotes//<#issue or commit-hash>\`, found: \`$title\`\n" - has_errors=true - file_has_errors=true - fi - - # Validate tags - if [[ -z "$tags" ]]; then - echo "::error file=$file::Missing 'tags' field" - file_errors+="- Missing field: \`tags\` field is required\n" - has_errors=true - file_has_errors=true - elif [[ ! "$tags" =~ \$:/tags/ChangeNote ]]; then - echo "::error file=$file::Tags must include '\$:/tags/ChangeNote'" - file_errors+="- Tags: Must include \`\$:/tags/ChangeNote\`, found: \`$tags\`\n" - has_errors=true - file_has_errors=true - fi - - # Validate change-type - if [[ -z "$change_type" ]]; then - echo "::error file=$file::Missing 'change-type' field" - file_errors+="- Missing field: \`change-type\` is required. Valid values: \`${VALID_TYPES[*]}\`\n" - has_errors=true - file_has_errors=true - else - valid=false - for type in "${VALID_TYPES[@]}"; do - [[ "$change_type" == "$type" ]] && valid=true && break - done - if [[ "$valid" == "false" ]]; then - echo "::error file=$file::Invalid change-type '$change_type'" - file_errors+="- Invalid change-type: \`$change_type\` is not valid. Must be one of: \`${VALID_TYPES[*]}\`\n" - has_errors=true - file_has_errors=true - fi - fi - - # Validate change-category - if [[ -z "$change_category" ]]; then - echo "::error file=$file::Missing 'change-category' field" - file_errors+="- Missing field: \`change-category\` is required. Valid values: \`${VALID_CATEGORIES[*]}\`\n" - has_errors=true - file_has_errors=true - else - valid=false - for category in "${VALID_CATEGORIES[@]}"; do - [[ "$change_category" == "$category" ]] && valid=true && break - done - if [[ "$valid" == "false" ]]; then - echo "::error file=$file::Invalid change-category '$change_category'" - file_errors+="- Invalid change-category: \`$change_category\` is not valid. Must be one of: \`${VALID_CATEGORIES[*]}\`\n" - has_errors=true - file_has_errors=true - fi - fi - - # Validate description - if [[ -z "$description" ]]; then - echo "::error file=$file::Missing 'description' field" - file_errors+="- Missing field: \`description\` is required\n" - has_errors=true - file_has_errors=true - fi - - # Collect errors - if [[ "$file_has_errors" == "true" ]]; then - error_details+="### 📄 \`$file\`\n\n$file_errors\n" - else - echo "✓ $file is valid" - fi - done - - # Output error details to file for GitHub Actions - if [[ "$has_errors" == "true" ]]; then - echo -e "$error_details" > /tmp/validation_errors.md - echo "" - echo "================================" - echo "Change note validation failed!" - echo "================================" - echo "" - echo "Please ensure your change notes follow the format specified in:" - echo "https://tiddlywiki.com/prerelease/#Release%20Notes%20and%20Changes" - return 1 - else - echo "" - echo "✓ All change notes are valid!" - return 0 - fi -} - -# Function: Parse change notes and generate summary -parse_changenotes() { - local files="$@" - - if [ -z "$files" ]; then - return 0 - fi - - for file in $files; do - [ ! -f "$file" ] && continue - - # Parse metadata - title="" - description="" - change_type="" - change_category="" - links="" - in_body=false - body_first_line="" - - while IFS= read -r line; do - # Empty line marks start of body - if [ -z "$line" ] && [ "$in_body" = false ]; then - in_body=true - continue - fi - - if [ "$in_body" = false ]; then - # Parse metadata - [[ "$line" =~ ^title:\ (.*)$ ]] && title="${BASH_REMATCH[1]}" - [[ "$line" =~ ^description:\ (.*)$ ]] && description="${BASH_REMATCH[1]}" - [[ "$line" =~ ^change-type:\ (.*)$ ]] && change_type="${BASH_REMATCH[1]}" - [[ "$line" =~ ^change-category:\ (.*)$ ]] && change_category="${BASH_REMATCH[1]}" - [[ "$line" =~ ^links:\ (.*)$ ]] && links="${BASH_REMATCH[1]}" - elif [ -z "$description" ] && [ -n "$line" ] && [ -z "$body_first_line" ]; then - # Use first non-empty body line if no description in metadata - body_first_line="$line" - fi - done < "$file" - - # Use body first line as description if needed - [ -z "$description" ] && [ -n "$body_first_line" ] && description="$body_first_line" - - # Get emojis - type_icon="${TYPE_EMOJI[$change_type]:-📝}" - cat_icon="${CATEGORY_EMOJI[$change_category]:-📋}" - - # Output markdown - echo "### ${type_icon} ${title:-$file}" - echo "" - echo "**Type:** ${change_type} | **Category:** ${change_category} ${cat_icon}" - echo "" - - [ -n "$description" ] && echo "> ${description}" && echo "" - [ -n "$links" ] && echo "🔗 [${links}](${links})" && echo "" - - echo "---" - echo "" - done -} - -# Main command dispatcher -case "${1:-}" in - check-needs) - shift - check_needs_changenote "$@" - ;; - validate) - shift - validate_changenotes "$@" - ;; - parse) - shift - parse_changenotes "$@" - ;; - *) - echo "Usage: $0 {check-needs|validate|parse} " - echo "" - echo "Commands:" - echo " check-needs Check if files require change notes" - echo " validate Validate change note format" - echo " parse Parse and generate summary" - exit 1 - ;; -esac