Skip to content

PowerShell 7.5.4

PowerShell 7.5.4 #20

name: Update Releases Properties
# This workflow runs in individual module repositories (e.g., module-php)
# It automatically updates releases.properties file when a release is published
on:
release:
types: [prereleased, released, edited]
# Manual trigger for testing
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to process (e.g., 2025.10.31)'
required: true
jobs:
update-properties:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GH_PAT }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install requests packaging
- name: Extract module name from repository
id: extract_module
run: |
# Extract module name from repo name (e.g., "module-php" -> "php")
REPO_NAME="${{ github.event.repository.name }}"
echo "Repository name: ${REPO_NAME}"
# Remove "module-" prefix (case-insensitive)
if [[ "${REPO_NAME}" == module-* ]]; then
MODULE_NAME="${REPO_NAME#module-}"
elif [[ "${REPO_NAME}" == Module-* ]]; then
MODULE_NAME="${REPO_NAME#Module-}"
else
echo "ERROR: Repository name does not start with 'module-'"
echo "Expected format: module-MODULENAME (e.g., module-php)"
exit 1
fi
# Convert to lowercase
MODULE_NAME=$(echo "${MODULE_NAME}" | tr '[:upper:]' '[:lower:]')
echo "module_name=${MODULE_NAME}" >> $GITHUB_OUTPUT
echo "Module name: ${MODULE_NAME}"
- name: Update releases properties
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ github.event.release.tag_name || github.event.inputs.release_tag }}
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
MODULE_NAME: ${{ steps.extract_module.outputs.module_name }}
run: |
python << 'EOF'
import os
import re
import requests
from packaging import version
from collections import OrderedDict
# Get environment variables
release_tag = os.environ['RELEASE_TAG']
repo_owner = os.environ['REPO_OWNER']
repo_name = os.environ['REPO_NAME']
module_name = os.environ['MODULE_NAME']
github_token = os.environ['GITHUB_TOKEN']
# Strip module name prefix from tag if present (e.g., "postgresql-2025.11.22" -> "2025.11.22")
if release_tag.lower().startswith(f"{module_name.lower()}-"):
original_tag = release_tag
release_tag = release_tag[len(module_name)+1:]
print(f"Stripped module prefix from tag: {original_tag} -> {release_tag}")
print(f"Processing release: {release_tag}")
print(f"Module name: {module_name}")
# Construct API URL
api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases/tags/{release_tag}"
headers = {
'Authorization': f'token {github_token}',
'Accept': 'application/vnd.github.v3+json'
}
print(f"Fetching release information from: {api_url}")
# Fetch release data
response = requests.get(api_url, headers=headers)
response.raise_for_status()
release_data = response.json()
# Extract assets that end with .7z only
assets = []
for asset in release_data.get('assets', []):
filename = asset['name']
# Check if file ends with .7z
if filename.endswith('.7z'):
download_url = asset['browser_download_url']
# Extract version number after module name (e.g., "bearsampp-postgresql-18.1-2025.7.2.7z" -> "18.1")
# Pattern: module_name followed by version, then either a dash+date or .7z
# Supports: 18.1, 17.2.3, 3.2.1.0, 17.0-RC1
# The (?=-\d{4}\.|\.7z) ensures we stop before a date pattern or .7z extension
version_match = re.search(
rf'{module_name}-(\d+\.\d+(?:\.\d+)?(?:\.\d+)?(?:-(?:RC|beta|alpha|dev)\d*)?)(?=-\d{{4}}\.|-\d{{4}}\d{{2}}\.|\.\d+z|$)',
filename,
re.IGNORECASE
)
if version_match:
ver = version_match.group(1)
assets.append({
'version': ver,
'url': download_url,
'filename': filename
})
print(f"Found: {filename} -> Version: {ver}")
else:
print(f"Warning: Could not extract version from: {filename}")
if not assets:
print(f"No .7z assets found in release")
exit(0)
# Read existing properties file
properties_file = "releases.properties"
if not os.path.exists(properties_file):
print(f"Properties file not found: {properties_file}")
print(f"Creating new properties file...")
with open(properties_file, 'w', encoding='utf-8') as f:
f.write(f"# {module_name.upper()} Releases Properties\n")
f.write(f"# Auto-generated and maintained by automation\n\n")
with open(properties_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
# Parse existing properties
properties = OrderedDict()
header_lines = []
in_header = True
for line in lines:
stripped = line.strip()
if in_header and (stripped.startswith('#') or stripped == ''):
header_lines.append(line)
else:
in_header = False
if '=' in line and not stripped.startswith('#'):
key, value = line.split('=', 1)
properties[key.strip()] = value.strip()
# Fix malformed version entries by re-extracting correct versions from URLs
# This handles cases like "2025.7.2.7" or "18.1-2025" that should be "18.1"
fixed_properties = OrderedDict()
for key, url in properties.items():
# Try to extract the correct version from the URL
# URL format: .../bearsampp-postgresql-18.1-2025.7.2.7z
url_version_match = re.search(
rf'{module_name}-(\d+\.\d+(?:\.\d+)?(?:\.\d+)?(?:-(?:RC|beta|alpha|dev)\d*)?)(?=-\d{{4}}\.|-\d{{4}}\d{{2}}\.|\.\d+z|/|$)',
url,
re.IGNORECASE
)
if url_version_match:
correct_version = url_version_match.group(1)
# If the key is malformed (date-like or has trailing year), fix it
if re.match(r'^20\d{2}\.', key) or re.search(r'-20\d{2}$', key):
print(f"Fixing malformed entry: {key} -> {correct_version}")
fixed_properties[correct_version] = url
else:
# Keep valid entries as-is
fixed_properties[key] = url
else:
# If we can't extract version from URL, keep the original entry
fixed_properties[key] = url
properties = fixed_properties
# Add new versions
for asset in assets:
ver = asset['version']
url = asset['url']
# Create property key (just version number)
key = ver
properties[key] = url
print(f"Added/Updated: {key} = {url}")
# Sort properties by version (semver)
def extract_version(key):
# Parse version directly from key
try:
return version.parse(key)
except:
return version.parse("0.0.0")
sorted_properties = OrderedDict(
sorted(properties.items(), key=lambda x: extract_version(x[0]), reverse=True)
)
# Write back to file
with open(properties_file, 'w', encoding='utf-8') as f:
# Write header
for line in header_lines:
f.write(line)
# Write sorted properties with spaces around equals sign
for key, value in sorted_properties.items():
f.write(f"{key} = {value}\n")
print(f"\nSuccessfully updated {properties_file}")
print(f"Total versions: {len(sorted_properties)}")
EOF
- name: Create Pull Request
id: create_pr
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GH_PAT }}
base: main
commit-message: |
Update releases.properties
Auto-generated from release ${{ github.event.release.tag_name || github.event.inputs.release_tag }}
branch: update-releases-${{ github.event.release.tag_name || github.event.inputs.release_tag }}
delete-branch: true
title: 'Update releases.properties from release ${{ github.event.release.tag_name || github.event.inputs.release_tag }}'
body: |
## 🤖 Automated Releases Properties Update
This PR updates the `releases.properties` file with new versions from release `${{ github.event.release.tag_name || github.event.inputs.release_tag }}`.
### Changes:
- Extracted .7z assets from the release
- Added version entries with download URLs
- Maintained semver ordering (newest first)
**Release URL:** ${{ github.event.release.html_url || format('https://github.com/{0}/{1}/releases/tag/{2}', github.repository_owner, github.event.repository.name, github.event.inputs.release_tag) }}
### Next Steps:
1. ⏳ Link validation will run automatically
2. ✅ Once validation passes, this PR will auto-merge
3. ❌ If validation fails, please review and fix invalid URLs
labels: |
automated
releases-update
- name: Wait for validation checks
if: steps.create_pr.outputs.pull-request-number != ''
run: |
echo "Waiting for link validation to complete..."
sleep 10
- name: Enable auto-merge
if: steps.create_pr.outputs.pull-request-number != ''
run: |
gh pr merge ${{ steps.create_pr.outputs.pull-request-number }} --auto --squash
env:
GH_TOKEN: ${{ secrets.GH_PAT }}