Skip to content

chore: add support for separate module versioning to CI #426

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7f130f2
Add support for separate module versioning
matifali Apr 10, 2025
b0069e2
Refactor: Combine version scripts into one versatile tool
matifali Apr 10, 2025
0611ef2
Update CI workflow to use new modules-version.sh tool
matifali Apr 10, 2025
2269e58
Improve CI version check for different workflows
matifali Apr 10, 2025
c6cffc8
Simplify CI version checking
matifali Apr 10, 2025
9bc5419
Remove check-version.sh as it's been replaced by modules-version.sh
matifali Apr 10, 2025
9b89ef4
Improve CI workflow for version checks
matifali Apr 10, 2025
038ef33
Refactor CI workflow for version checks
matifali Apr 10, 2025
0b7a602
Update CONTRIBUTING.md to clarify release process steps
matifali Apr 10, 2025
7d481e8
`fmt`
matifali Apr 10, 2025
d5ecb98
refactor: simplify modules-version script with dry-run and bump-only …
matifali Apr 14, 2025
7c95af8
chore: implement tag-first workflow with automated PRs
matifali Apr 14, 2025
324a383
feat: implement GitHub Action for automatic README updates when tags …
matifali Apr 14, 2025
96657d7
feat: add auto-approve and auto-merge for PR workflow
matifali Apr 14, 2025
1a1dd69
refactor: reuse modules-version.sh in GitHub Action
matifali Apr 14, 2025
4dbe948
feat: add --version parameter for setting exact versions
matifali Apr 14, 2025
bd1703b
refactor: remove environment variable support
matifali Apr 14, 2025
6f2fe83
docs: update release process documentation
matifali Apr 14, 2025
2998721
feat: split scripts into release.sh and update-version.sh
matifali Apr 14, 2025
d63b332
refactor: simplify scripts and workflows
matifali Apr 14, 2025
a6984fb
refactor: further simplify scripts to bare essentials
matifali Apr 14, 2025
0c4f954
feat: use GitHub CLI for PR management
matifali Apr 14, 2025
fb3ae6f
refactor: use cdrci as PR author and auto-approve action
matifali Apr 14, 2025
6d925ca
refactor: remove unnecessary comments and streamline code
matifali Apr 14, 2025
bdd8dba
docs: add helpful comments to scripts and workflow
matifali Apr 14, 2025
3f0615b
fmt
matifali Apr 14, 2025
8200f2d
Remove release script from package.json
matifali Apr 14, 2025
70e5da7
Remove trailing comma in package.json
matifali Apr 14, 2025
e4e5c73
Remove commented-out version check steps in CI
matifali Apr 14, 2025
57fc91f
Simplify and improve GitHub workflow and release scripts
matifali Apr 15, 2025
68f5396
Rename update-version.sh to update_version.sh and improve docs
matifali Apr 15, 2025
8aa9154
Review: refactor scripts
mafredri Apr 17, 2025
974f3f6
Delete .github/workflows/update-readme-version.yaml
matifali Apr 22, 2025
2086134
Update CONTRIBUTING.md
matifali Apr 22, 2025
6b1b617
Merge branch 'main' into separate-versioning
matifali Apr 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,3 @@ jobs:
uses: crate-ci/[email protected]
- name: Lint
run: bun lint
- name: Check version
shell: bash
run: |
# check for version changes
./update-version.sh
# Check if any changes were made in README.md files
if [[ -n "$(git status --porcelain -- '**/README.md')" ]]; then
echo "Version mismatch detected. Please run ./update-version.sh and commit the updated README.md files."
git diff -- '**/README.md'
exit 1
else
echo "No version mismatch detected. All versions are up to date."
fi
128 changes: 128 additions & 0 deletions .github/workflows/update-readme-version.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
name: Auto Update README Version

# This workflow runs when a new tag is pushed with the format release/module-name/vX.Y.Z
# It automatically updates the corresponding README.md file with the new version
# and creates a PR that is auto-approved and auto-merged.

on:
push:
tags:
- 'release/*/v*' # Matches tags like release/module-name/v1.0.0

jobs:
update-readme:
runs-on: ubuntu-latest
permissions:
contents: write # Needed to push branches
pull-requests: write # Needed to create and manage PRs

steps:
# Check out the repository
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # We need the full history to check tags

# Set up the Bun JavaScript runtime
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

# Install dependencies
- name: Install dependencies
run: bun install

# Extract information from the tag that triggered this workflow
- name: Extract tag information
id: tag
run: |
# Parse the tag name (e.g., "release/code-server/v1.2.3")
TAG_NAME="${GITHUB_REF#refs/tags/}"
# Extract module name (the middle part)
MODULE_NAME=$(echo $TAG_NAME | cut -d'/' -f2)
# Extract version (the last part, without the 'v' prefix)
VERSION=$(echo $TAG_NAME | cut -d'/' -f3 | sed 's/^v//')

# Make these values available to other steps
echo "MODULE_NAME=$MODULE_NAME" >> $GITHUB_OUTPUT
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_OUTPUT

# Check if README needs updating
- name: Check if README version needs updating
id: check
run: |
# Make the script executable
chmod +x ./update-version.sh

# Check if README version matches tag version
# The script will exit with 0 if versions match, 1 if they don't
if ./update-version.sh --check "${{ steps.tag.outputs.MODULE_NAME }}" "${{ steps.tag.outputs.VERSION }}"; then
echo "NEEDS_UPDATE=false" >> $GITHUB_OUTPUT
echo "README version already matches tag version - no update needed"
else
echo "NEEDS_UPDATE=true" >> $GITHUB_OUTPUT
echo "README version doesn't match tag version - update needed"
fi

# Update README with new version
- name: Update README version
if: steps.check.outputs.NEEDS_UPDATE == 'true'
run: |
# Set git identity for commits
git config user.name "cdrci"
git config user.email "[email protected]"

# Update the README with the new version
chmod +x ./update-version.sh
./update-version.sh "${{ steps.tag.outputs.MODULE_NAME }}" "${{ steps.tag.outputs.VERSION }}"
echo "Updated README version to ${{ steps.tag.outputs.VERSION }}"

# Create PR with the changes
- name: Create PR for version update
if: steps.check.outputs.NEEDS_UPDATE == 'true'
id: create-pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Set variables for PR creation
MODULE="${{ steps.tag.outputs.MODULE_NAME }}"
VERSION="${{ steps.tag.outputs.VERSION }}"
BRANCH="automated-update-$MODULE-$VERSION"
PR_TITLE="chore: update $MODULE version to $VERSION"

# Create a branch for the PR
git checkout -b "$BRANCH"

# Commit the changes
git add "$MODULE/README.md"
git commit -m "$PR_TITLE"

# Push the branch to the repository
git push origin "$BRANCH"

# Create a PR using GitHub CLI
PR_URL=$(gh pr create \
--title "$PR_TITLE" \
--body "Updates README version to match tag ${{ steps.tag.outputs.TAG_NAME }}" \
--base main \
--head "$BRANCH")

echo "Created PR: $PR_URL"

# Extract PR number from the URL
PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]*$')
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT

# Enable auto-merge for the PR
gh pr merge "$PR_NUMBER" --auto --squash
echo "Enabled auto-merge for PR #$PR_NUMBER"

# Auto-approve the PR
- name: Auto-approve PR
if: steps.create-pr.outputs.pr_number
uses: hmarr/auto-approve-action@v3
with:
pull-request-number: ${{ steps.create-pr.outputs.pr_number }}
github-token: ${{ secrets.GITHUB_TOKEN }}
42 changes: 32 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,42 @@ module "example" {
> [!WARNING]
> When creating a new release, make sure that your new version number is fully accurate. If a version number is incorrect or does not exist, we may end up serving incorrect/old data for our various tools and providers.

Much of our release process is automated. To cut a new release:
The release process is automated and follows these steps:

1. Navigate to [GitHub's Releases page](https://github.com/coder/modules/releases)
2. Click "Draft a new release"
3. Click the "Choose a tag" button and type a new release number in the format `v<major>.<minor>.<patch>` (e.g., `v1.18.0`). Then click "Create new tag".
4. Click the "Generate release notes" button, and clean up the resulting README. Be sure to remove any notes that would not be relevant to end-users (e.g., bumping dependencies).
5. Once everything looks good, click the "Publish release" button.
1. Create a PR with your module changes

Once the release has been cut, a script will run to check whether there are any modules that will require that the new release number be published to Terraform. If there are any, a new pull request will automatically be generated. Be sure to approve this PR and merge it into the `main` branch.
- You **do not** need to update the version number in the README.md file
- Focus on implementing your feature or fix

Following that, our automated processes will handle publishing new data for [`registry.coder.com`](https://github.com/coder/registry.coder.com/):
2. Have your PR reviewed, approved, and merged to main

1. Publishing new versions to Coder's [Terraform Registry](https://registry.terraform.io/providers/coder/coder/latest)
2. Publishing new data to the [Coder Registry](https://registry.coder.com)
3. After merging to main, a maintainer will:

- Create and push an annotated tag with an appropriate version based on your changes:

```shell
# Create an annotated tag with an exact version number
./release.sh module-name 1.2.3

# Push the tag to the repository
git push origin release/module-name/v1.2.3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue with this approach is that the tag will not include the updated README, it'll be updated after tagging meaning when you git checkout release/module-name/v1.2.3 the README will state v1.2.2.

It's perhaps a minor thing, but makes me question the release method.

Copy link
Member Author

@matifali matifali Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice observation. Do you have any suggestions on a better approach?

We would have to reverse the behavior where the user chooses a tag, commits that to README, and then pushes a tag that matches that.
That will have more responsibility on the maintainer and can resurface #229.

```

- View all modules and their current versions with:

```shell
./release.sh --list
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍🏻

```

- The tag will follow the format: `release/module-name/v1.0.0`

4. When the tag is pushed, a GitHub Action will automatically:
- Update the module's README.md version to match the tag
- Create a PR with the version update by github-actions[bot]
- Auto-approve and auto-merge the version update PR
- No manual action required - the version will be updated automatically

Following that, our automated processes will handle publishing new data to [`registry.coder.com`](https://registry.coder.com):

> [!NOTE]
> Some data in `registry.coder.com` is fetched on demand from the Module repo's main branch. This data should be updated almost immediately after a new release, but other changes will take some time to propagate.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"test": "bun test",
"fmt": "bun x prettier -w **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt **/*.tf .sample/main.tf",
"fmt:ci": "bun x prettier --check **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt -check **/*.tf .sample/main.tf",
"lint": "bun run lint.ts && ./terraform_validate.sh",
"update-version": "./update-version.sh"
"lint": "bun run lint.ts && ./terraform_validate.sh"
},
"devDependencies": {
"bun-types": "^1.1.23",
Expand Down
110 changes: 110 additions & 0 deletions release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env bash
#
# release.sh - Creates annotated tags for module releases
#
# This script is used by maintainers to create annotated tags for module releases.
# It supports two main modes:
# 1. Creating a new tag for a module: ./release.sh module-name X.Y.Z
# 2. Listing all modules with their versions: ./release.sh --list
#
# When a tag is pushed, it triggers a GitHub workflow that updates README versions.

set -euo pipefail

# Handle --list option to show all modules and their versions
if [[ "$#" -eq 1 && "$1" == "--list" ]]; then
# Function to extract version from README
extract_version() {
grep -o 'version *= *"[0-9]\+\.[0-9]\+\.[0-9]\+"' "$1" | head -1 | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' || echo "0.0.0"
}

# Display header for module listing
echo "Listing all modules and their latest versions:"
echo "--------------------------------------------------"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not enough -'s

printf "%-30s %-15s %s\n" "MODULE" "README VERSION" "LATEST TAG"
echo "--------------------------------------------------"

# Loop through all directories that look like modules
for dir in */; do
if [[ -d "$dir" && -f "${dir}README.md" && "$dir" != ".git/" ]]; then
module_name="${dir%/}"
# Get version from README
readme_version=$(extract_version "${dir}README.md")
# Get latest tag for this module
latest_tag=$(git tag -l "release/${module_name}/v*" | sort -V | tail -n 1)
tag_version=$([ -n "$latest_tag" ] && echo "$latest_tag" | sed 's|release/'"${module_name}"'/v||' || echo "none")
# Display module info
printf "%-30s %-15s %s\n" "$module_name" "$readme_version" "$tag_version"
fi
done

echo "--------------------------------------------------"
exit 0
fi

# Handle --dry-run option
DRY_RUN=false
if [[ "$1" == "--dry-run" ]]; then
DRY_RUN=true
shift
fi

# Validate arguments
if [[ "$#" -ne 2 ]]; then
echo "Usage: ./release.sh [--dry-run] module-name X.Y.Z"
echo " or: ./release.sh --list"
exit 1
fi

MODULE_NAME="$1"
VERSION="$2"

# Validate version format
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must be in format X.Y.Z"
exit 1
fi

# Function to extract version from README
extract_version() {
grep -o 'version *= *"[0-9]\+\.[0-9]\+\.[0-9]\+"' "$1" | head -1 | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' || echo "0.0.0"
}

# Check if module exists
if [[ ! -d "$MODULE_NAME" || ! -f "$MODULE_NAME/README.md" ]]; then
echo "Error: Module directory not found or missing README.md"
exit 1
fi

# Get current README version and construct tag name
README_VERSION=$(extract_version "$MODULE_NAME/README.md")
TAG_NAME="release/$MODULE_NAME/v$VERSION"

# Check if tag already exists
if git rev-parse -q --verify "refs/tags/$TAG_NAME" >/dev/null; then
echo "Error: Tag $TAG_NAME already exists"
exit 1
fi

# Display release information
echo "Module: $MODULE_NAME"
echo "Current README version: $README_VERSION"
echo "New tag version: $VERSION"
echo "Tag name: $TAG_NAME"

# Create the tag (or simulate in dry-run mode)
if [[ "$DRY_RUN" == "false" ]]; then
# Create annotated tag
git tag -a "$TAG_NAME" -m "Release $MODULE_NAME v$VERSION"
echo "Success! Tag '$TAG_NAME' created."
echo "To complete the release: git push origin $TAG_NAME"
echo ""
echo "When pushed, this will trigger the GitHub Action to:"
echo "- Update the README.md version"
echo "- Create a PR with the update"
echo "- Auto-approve and merge the PR"
else
echo "[DRY RUN] Would create tag: $TAG_NAME"
fi

exit 0
Loading