Update releases.properties from release 2025.11.23 #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Validate Properties Links | |
| # This workflow validates all URLs in modified .properties files when a PR is created or edited | |
| # Works for both modules/*.properties and releases.properties files | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, edited, reopened] | |
| paths: | |
| - 'modules/*.properties' | |
| - 'releases.properties' | |
| jobs: | |
| validate-links: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout PR branch | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dependencies | |
| run: | | |
| pip install requests | |
| - name: Get changed properties files | |
| id: changed_files | |
| run: | | |
| # Get list of changed .properties files | |
| git fetch origin ${{ github.event.pull_request.base.ref }} | |
| CHANGED_FILES=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }}...HEAD | grep '\.properties$' || true) | |
| if [ -z "$CHANGED_FILES" ]; then | |
| echo "No properties files changed" | |
| echo "files=" >> $GITHUB_OUTPUT | |
| else | |
| echo "Changed files:" | |
| echo "$CHANGED_FILES" | |
| # Convert to comma-separated list | |
| FILES_LIST=$(echo "$CHANGED_FILES" | tr '\n' ',' | sed 's/,$//') | |
| echo "files=$FILES_LIST" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Validate all links in properties files | |
| if: steps.changed_files.outputs.files != '' | |
| env: | |
| CHANGED_FILES: ${{ steps.changed_files.outputs.files }} | |
| run: | | |
| python << 'EOF' | |
| import os | |
| import sys | |
| import requests | |
| from urllib.parse import urlparse | |
| import time | |
| # Get changed files | |
| changed_files = os.environ.get('CHANGED_FILES', '').split(',') | |
| changed_files = [f.strip() for f in changed_files if f.strip()] | |
| if not changed_files: | |
| print("No properties files to validate") | |
| sys.exit(0) | |
| print(f"Validating {len(changed_files)} properties file(s)...\n") | |
| all_valid = True | |
| total_urls = 0 | |
| valid_urls = 0 | |
| invalid_urls = [] | |
| for properties_file in changed_files: | |
| if not os.path.exists(properties_file): | |
| print(f"β οΈ File not found: {properties_file}") | |
| continue | |
| print(f"π Checking: {properties_file}") | |
| print("-" * 80) | |
| with open(properties_file, 'r', encoding='utf-8') as f: | |
| lines = f.readlines() | |
| file_urls = [] | |
| for line_num, line in enumerate(lines, 1): | |
| line = line.strip() | |
| # Skip comments and empty lines | |
| if not line or line.startswith('#'): | |
| continue | |
| # Parse property line | |
| if '=' in line: | |
| key, value = line.split('=', 1) | |
| url = value.strip() | |
| # Check if it's a URL | |
| if url.startswith('http://') or url.startswith('https://'): | |
| file_urls.append({ | |
| 'line': line_num, | |
| 'key': key.strip(), | |
| 'url': url | |
| }) | |
| print(f"Found {len(file_urls)} URL(s) to validate\n") | |
| # Validate each URL | |
| for item in file_urls: | |
| total_urls += 1 | |
| url = item['url'] | |
| key = item['key'] | |
| line = item['line'] | |
| try: | |
| # Send HEAD request first (faster) | |
| response = requests.head(url, timeout=10, allow_redirects=True) | |
| # If HEAD fails, try GET | |
| if response.status_code >= 400: | |
| response = requests.get(url, timeout=10, allow_redirects=True, stream=True) | |
| if response.status_code == 200: | |
| print(f"β Line {line}: {key}") | |
| print(f" URL: {url}") | |
| print(f" Status: {response.status_code} OK") | |
| valid_urls += 1 | |
| else: | |
| print(f"β Line {line}: {key}") | |
| print(f" URL: {url}") | |
| print(f" Status: {response.status_code} {response.reason}") | |
| invalid_urls.append({ | |
| 'file': properties_file, | |
| 'line': line, | |
| 'key': key, | |
| 'url': url, | |
| 'status': response.status_code, | |
| 'reason': response.reason | |
| }) | |
| all_valid = False | |
| except requests.exceptions.Timeout: | |
| print(f"β±οΈ Line {line}: {key}") | |
| print(f" URL: {url}") | |
| print(f" Error: Request timeout (>10s)") | |
| invalid_urls.append({ | |
| 'file': properties_file, | |
| 'line': line, | |
| 'key': key, | |
| 'url': url, | |
| 'status': 'TIMEOUT', | |
| 'reason': 'Request timeout' | |
| }) | |
| all_valid = False | |
| except requests.exceptions.RequestException as e: | |
| print(f"β Line {line}: {key}") | |
| print(f" URL: {url}") | |
| print(f" Error: {str(e)}") | |
| invalid_urls.append({ | |
| 'file': properties_file, | |
| 'line': line, | |
| 'key': key, | |
| 'url': url, | |
| 'status': 'ERROR', | |
| 'reason': str(e) | |
| }) | |
| all_valid = False | |
| print() | |
| # Small delay to avoid rate limiting | |
| time.sleep(0.5) | |
| print() | |
| # Summary | |
| print("=" * 80) | |
| print("VALIDATION SUMMARY") | |
| print("=" * 80) | |
| print(f"Total URLs checked: {total_urls}") | |
| print(f"Valid URLs: {valid_urls}") | |
| print(f"Invalid URLs: {len(invalid_urls)}") | |
| print() | |
| if invalid_urls: | |
| print("β INVALID URLS FOUND:") | |
| print("-" * 80) | |
| for item in invalid_urls: | |
| print(f"\nFile: {item['file']}") | |
| print(f"Line: {item['line']}") | |
| print(f"Key: {item['key']}") | |
| print(f"URL: {item['url']}") | |
| print(f"Status: {item['status']} - {item['reason']}") | |
| print() | |
| print("=" * 80) | |
| print("β VALIDATION FAILED - Please fix the invalid URLs above") | |
| print("=" * 80) | |
| sys.exit(1) | |
| else: | |
| print("=" * 80) | |
| print("β ALL URLS ARE VALID") | |
| print("=" * 80) | |
| sys.exit(0) | |
| EOF | |
| - name: Comment on PR with validation results | |
| if: failure() | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| // Create a comment on the PR | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: `## β Link Validation Failed | |
| Some URLs in the modified properties files are not accessible. Please check the workflow logs for details. | |
| **Action Required:** | |
| - Review the invalid URLs listed in the [workflow logs](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) | |
| - Fix or remove the invalid URLs | |
| - Push the changes to trigger a new validation | |
| The PR cannot be merged until all URLs are valid.` | |
| }); | |
| - name: Comment on PR with success | |
| if: success() && steps.changed_files.outputs.files != '' | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: `## β Link Validation Passed | |
| All URLs in the modified properties files have been validated and are accessible. | |
| This PR is ready for review and merge.` | |
| }); |