diff --git a/.github/scripts/version-bump.sh b/.github/scripts/version-bump.sh new file mode 100755 index 0000000..b074583 --- /dev/null +++ b/.github/scripts/version-bump.sh @@ -0,0 +1,229 @@ +#!/bin/bash + +# Version Bump Script +# Usage: ./version-bump.sh [base_ref] +# bump_type: patch, minor, or major +# base_ref: base reference for diff (default: origin/main) + +set -euo pipefail + +usage() { + echo "Usage: $0 [base_ref]" + echo " bump_type: patch, minor, or major" + echo " base_ref: base reference for diff (default: origin/main)" + echo "" + echo "Examples:" + echo " $0 patch # Update versions with patch bump" + echo " $0 minor # Update versions with minor bump" + echo " $0 major # Update versions with major bump" + exit 1 +} + +validate_version() { + local version="$1" + if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Invalid version format: '$version'. Expected X.Y.Z format." >&2 + return 1 + fi + return 0 +} + +bump_version() { + local current_version="$1" + local bump_type="$2" + + IFS='.' read -r major minor patch <<< "$current_version" + + if ! [[ "$major" =~ ^[0-9]+$ ]] || ! [[ "$minor" =~ ^[0-9]+$ ]] || ! [[ "$patch" =~ ^[0-9]+$ ]]; then + echo "❌ Version components must be numeric: major='$major' minor='$minor' patch='$patch'" >&2 + return 1 + fi + + case "$bump_type" in + "patch") + echo "$major.$minor.$((patch + 1))" + ;; + "minor") + echo "$major.$((minor + 1)).0" + ;; + "major") + echo "$((major + 1)).0.0" + ;; + *) + echo "❌ Invalid bump type: '$bump_type'. Expected patch, minor, or major." >&2 + return 1 + ;; + esac +} + +update_readme_version() { + local readme_path="$1" + local namespace="$2" + local module_name="$3" + local new_version="$4" + + if [ ! -f "$readme_path" ]; then + return 1 + fi + + local module_source="registry.coder.com/${namespace}/${module_name}/coder" + if grep -q "source.*${module_source}" "$readme_path"; then + echo "Updating version references for $namespace/$module_name in $readme_path" + awk -v module_source="$module_source" -v new_version="$new_version" ' + /source.*=.*/ { + if ($0 ~ module_source) { + in_target_module = 1 + } else { + in_target_module = 0 + } + } + /version.*=.*"/ { + if (in_target_module) { + gsub(/version[[:space:]]*=[[:space:]]*"[^"]*"/, "version = \"" new_version "\"") + in_target_module = 0 + } + } + { print } + ' "$readme_path" > "${readme_path}.tmp" && mv "${readme_path}.tmp" "$readme_path" + return 0 + elif grep -q 'version\s*=\s*"' "$readme_path"; then + echo "⚠️ Found version references but no module source match for $namespace/$module_name" + return 1 + fi + + return 1 +} + +main() { + if [ $# -lt 1 ] || [ $# -gt 2 ]; then + usage + fi + + local bump_type="$1" + local base_ref="${2:-origin/main}" + + case "$bump_type" in + "patch" | "minor" | "major") ;; + + *) + echo "❌ Invalid bump type: '$bump_type'. Expected patch, minor, or major." >&2 + exit 1 + ;; + esac + + echo "🔍 Detecting modified modules..." + + local changed_files + changed_files=$(git diff --name-only "${base_ref}"...HEAD) + local modules + modules=$(echo "$changed_files" | grep -E '^registry/[^/]+/modules/[^/]+/' | cut -d'/' -f1-4 | sort -u) + + if [ -z "$modules" ]; then + echo "❌ No modules detected in changes" + exit 1 + fi + + echo "Found modules:" + echo "$modules" + echo "" + + local bumped_modules="" + local updated_readmes="" + local untagged_modules="" + local has_changes=false + + while IFS= read -r module_path; do + if [ -z "$module_path" ]; then continue; fi + + local namespace + namespace=$(echo "$module_path" | cut -d'/' -f2) + local module_name + module_name=$(echo "$module_path" | cut -d'/' -f4) + + echo "📦 Processing: $namespace/$module_name" + + local latest_tag + latest_tag=$(git tag -l "release/${namespace}/${module_name}/v*" | sort -V | tail -1) + local readme_path="$module_path/README.md" + local current_version + + if [ -z "$latest_tag" ]; then + if [ -f "$readme_path" ] && grep -q 'version\s*=\s*"' "$readme_path"; then + local readme_version + readme_version=$(grep 'version\s*=\s*"' "$readme_path" | head -1 | sed 's/.*version\s*=\s*"\([^"]*\)".*/\1/') + echo "No git tag found, but README shows version: $readme_version" + + if ! validate_version "$readme_version"; then + echo "Starting from v1.0.0 instead" + current_version="1.0.0" + else + current_version="$readme_version" + untagged_modules="$untagged_modules\n- $namespace/$module_name (README: v$readme_version)" + fi + else + echo "No existing tags or version references found for $namespace/$module_name, starting from v1.0.0" + current_version="1.0.0" + fi + else + current_version=$(echo "$latest_tag" | sed 's/.*\/v//') + echo "Found git tag: $latest_tag (v$current_version)" + fi + + echo "Current version: $current_version" + + if ! validate_version "$current_version"; then + exit 1 + fi + + local new_version + new_version=$(bump_version "$current_version" "$bump_type") + + echo "New version: $new_version" + + if update_readme_version "$readme_path" "$namespace" "$module_name" "$new_version"; then + updated_readmes="$updated_readmes\n- $namespace/$module_name" + has_changes=true + fi + + bumped_modules="$bumped_modules\n- $namespace/$module_name: v$current_version → v$new_version" + echo "" + + done <<< "$modules" + + echo "📋 Summary:" + echo "Bump Type: $bump_type" + echo "" + echo "Modules Updated:" + echo -e "$bumped_modules" + echo "" + + if [ -n "$updated_readmes" ]; then + echo "READMEs Updated:" + echo -e "$updated_readmes" + echo "" + fi + + if [ -n "$untagged_modules" ]; then + echo "⚠️ Modules Without Git Tags:" + echo -e "$untagged_modules" + echo "These modules were versioned based on README content. Consider creating proper release tags after merging." + echo "" + fi + + if [ "$has_changes" = true ]; then + echo "✅ Version bump completed successfully!" + echo "📝 README files have been updated with new versions." + echo "" + echo "Next steps:" + echo "1. Review the changes: git diff" + echo "2. Commit the changes: git add . && git commit -m 'chore: bump module versions ($bump_type)'" + echo "3. Push the changes: git push" + exit 0 + else + echo "ℹ️ No README files were updated (no version references found matching module sources)." + echo "Version calculations completed, but no files were modified." + exit 0 + fi +} + +main "$@" diff --git a/.github/workflows/version-bump.yaml b/.github/workflows/version-bump.yaml new file mode 100644 index 0000000..537830a --- /dev/null +++ b/.github/workflows/version-bump.yaml @@ -0,0 +1,107 @@ +name: Version Bump + +on: + pull_request: + types: [labeled] + paths: + - "registry/**/modules/**" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + version-bump: + if: github.event.label.name == 'version:patch' || github.event.label.name == 'version:minor' || github.event.label.name == 'version:major' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract bump type from label + id: bump-type + run: | + case "${{ github.event.label.name }}" in + "version:patch") + echo "type=patch" >> $GITHUB_OUTPUT + ;; + "version:minor") + echo "type=minor" >> $GITHUB_OUTPUT + ;; + "version:major") + echo "type=major" >> $GITHUB_OUTPUT + ;; + *) + echo "Invalid version label: ${{ github.event.label.name }}" + exit 1 + ;; + esac + + - name: Check version bump requirements + id: version-check + run: | + # Run the script to check what versions should be + output_file=$(mktemp) + if ./.github/scripts/version-bump.sh "${{ steps.bump-type.outputs.type }}" origin/main > "$output_file" 2>&1; then + echo "Script completed successfully" + else + echo "Script failed" + cat "$output_file" + exit 1 + fi + + # Store output for PR comment + { + echo "output<> $GITHUB_OUTPUT + + # Show output + cat "$output_file" + + # Check if any files would be modified by the script + if git diff --quiet; then + echo "versions_up_to_date=true" >> $GITHUB_OUTPUT + echo "✅ All module versions are already up to date" + else + echo "versions_up_to_date=false" >> $GITHUB_OUTPUT + echo "❌ Module versions need to be updated" + echo "Files that would be changed:" + git diff --name-only + echo "" + echo "Diff preview:" + git diff + exit 1 + fi + + - name: Comment on PR - Failure + if: failure() && steps.version-check.outputs.versions_up_to_date == 'false' + uses: actions/github-script@v7 + with: + script: | + const output = `${{ steps.version-check.outputs.output }}`; + const bumpType = `${{ steps.bump-type.outputs.type }}`; + + let comment = `## ❌ Version Bump Validation Failed\n\n`; + comment += `**Bump Type:** \`${bumpType}\`\n\n`; + comment += `Module versions need to be updated but haven't been bumped yet.\n\n`; + comment += `**Required Actions:**\n`; + comment += `1. Run the version bump script locally: \`./.github/scripts/version-bump.sh ${bumpType}\`\n`; + comment += `2. Commit the changes: \`git add . && git commit -m "chore: bump module versions (${bumpType})"\`\n`; + comment += `3. Push the changes: \`git push\`\n\n`; + comment += `### Script Output:\n\`\`\`\n${output}\n\`\`\`\n\n`; + comment += `> Please update the module versions and push the changes to continue.`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a0e8f63..8d63b42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -258,13 +258,35 @@ All README files must follow these rules: ## Versioning Guidelines -After your PR is merged, maintainers will handle the release. Understanding version numbers helps you describe the impact of your changes: +When you modify a module, you need to update its version number in the README. Understanding version numbers helps you describe the impact of your changes: - **Patch** (1.2.3 → 1.2.4): Bug fixes - **Minor** (1.2.3 → 1.3.0): New features, adding inputs - **Major** (1.2.3 → 2.0.0): Breaking changes (removing inputs, changing types) -**Important**: Always specify the version change in your PR (e.g., `v1.2.3 → v1.2.4`). This helps maintainers create the correct release tag. +### Updating Module Versions + +If your changes require a version bump, use the version bump script: + +```bash +# For bug fixes +./.github/scripts/version-bump.sh patch + +# For new features +./.github/scripts/version-bump.sh minor + +# For breaking changes +./.github/scripts/version-bump.sh major +``` + +The script will: + +1. Detect which modules you've modified +2. Calculate the new version number +3. Update all version references in the module's README +4. Show you a summary of changes + +**Important**: Only run the version bump script if your changes require a new release. Documentation-only changes don't need version updates. ---