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 all 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
15 changes: 0 additions & 15 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,3 @@ jobs:
config: .github/typos.toml
- name: Lint
run: bun lint
# Disable version check until https://github.com/coder/modules/pull/426 is merged.
# This will allow us to use separate versioning for each module without failing CI. The backend already supports that.
# - 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
50 changes: 36 additions & 14 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ Follow the instructions to ensure that Bun is available globally. Once Bun has b

## Testing a Module

> **Note:** It is the responsibility of the module author to implement tests for their module. The author must test the module locally before submitting a PR.
> [!NOTE]
> It is the responsibility of the module author to implement tests for their module. The author must test the module locally before submitting a PR.

A suite of test-helpers exists to run `terraform apply` on modules with variables, and test script output against containers.

Expand Down Expand Up @@ -53,23 +54,44 @@ module "example" {

## Releases

> [!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.
The release process is automated with these steps:

## 1. Create and Merge PR

- Create a PR with your module changes
- Get your PR reviewed, approved, and merged to `main`

## 2. Prepare Release (Maintainer Task)

After merging to `main`, a maintainer will:

- View all modules and their current versions:

```shell
./release.sh --list
```

- Determine the next version number based on changes:

- **Patch version** (1.2.3 → 1.2.4): Bug fixes
- **Minor version** (1.2.3 → 1.3.0): New features, adding inputs, deprecating inputs
- **Major version** (1.2.3 → 2.0.0): Breaking changes (removing inputs, changing input types)

Much of our release process is automated. To cut a new release:
- Create and push an annotated tag:

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.
```shell
# Fetch latest changes
git fetch origin

# Create and push tag
./release.sh module-name 1.2.3 --push
```

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.
The tag format will be: `release/module-name/v1.2.3`

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

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)
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.
> Some data in registry.coder.com is fetched on demand from the [coder/modules](https://github.com/coder/modules) repo's `main` branch. This data should update almost immediately after a release, while other changes will take some time to propagate.
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
"name": "modules",
"scripts": {
"test": "bun test",
"fmt": "bun x prettier -w **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt **/*.tf .sample/main.tf",
"fmt": "bun x prettier -w **/*.sh .sample/run.sh new.sh terraform_validate.sh release.sh update_version.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
196 changes: 196 additions & 0 deletions release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#!/usr/bin/env bash
set -euo pipefail

usage() {
cat <<EOF
Usage: $0 [OPTIONS] [<MODULE> <VERSION>]

Create annotated git tags for module releases.

This script is used by maintainers to create annotated tags for module
releases. When a tag is pushed, it triggers a GitHub workflow that
updates README versions.

Options:
-l, --list List all modules with their versions
-n, --dry-run Show what would be done without making changes
-p, --push Push the created tag to the remote repository
-h, --help Show this help message

Examples:
$0 --list
$0 nodejs 1.2.3
$0 nodejs 1.2.3 --push
$0 --dry-run nodejs 1.2.3
EOF
exit "${1:-0}"
}

check_getopt() {
# Check if we have GNU or BSD getopt.
if getopt --test >/dev/null 2>&1; then
# Exit status 4 means GNU getopt is available.
if [[ $? -ne 4 ]]; then
echo "Error: GNU getopt is not available." >&2
echo "On macOS, you can install GNU getopt and add it to your PATH:" >&2
echo
echo $'\tbrew install gnu-getopt' >&2
echo $'\texport PATH="$(brew --prefix gnu-getopt)/bin:$PATH"' >&2
exit 1
fi
fi
}

maybe_dry_run() {
if [[ $dry_run == true ]]; then
echo "[DRY RUN] $*"
return 0
fi
"$@"
}

get_readme_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"
}

list_modules() {
printf "\nListing all modules and their latest versions:\n"
printf "%s\n" "--------------------------------------------------------------"
printf "%-30s %-15s %-15s\n" "MODULE" "README VERSION" "LATEST TAG"
printf "%s\n" "--------------------------------------------------------------"

# Process each module directory.
for dir in */; do
# Skip non-module directories.
[[ ! -d $dir || ! -f ${dir}README.md || $dir == ".git/" ]] && continue

module="${dir%/}"
readme_version=$(get_readme_version "${dir}README.md")
latest_tag=$(git tag -l "release/${module}/v*" | sort -V | tail -n 1)
tag_version="none"
if [[ -n $latest_tag ]]; then
tag_version="${latest_tag#"release/${module}/v"}"
fi

printf "%-30s %-15s %-15s\n" "$module" "$readme_version" "$tag_version"
done

printf "%s\n" "--------------------------------------------------------------"
}

is_valid_version() {
if ! [[ $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must be in format X.Y.Z (e.g., 1.2.3)" >&2
return 1
fi
}

get_tag_name() {
local module="$1"
local version="$2"
local tag_name="release/$module/v$version"
local readme_path="$module/README.md"

if [[ ! -d $module || ! -f $readme_path ]]; then
echo "Error: Module '$module' not found or missing README.md" >&2
return 1
fi

local readme_version
readme_version=$(get_readme_version "$readme_path")

{
echo "Module: $module"
echo "Current README version: $readme_version"
echo "New tag version: $version"
echo "Tag name: $tag_name"
} >&2

echo "$tag_name"
}

# Ensure getopt is available.
check_getopt

# Set defaults.
list=false
dry_run=false
push=false
module=
version=

# Parse command-line options.
if ! temp=$(getopt -o ldph --long list,dry-run,push,help -n "$0" -- "$@"); then
echo "Error: Failed to parse arguments" >&2
usage 1
fi
eval set -- "$temp"

while true; do
case "$1" in
-l | --list)
list=true
shift
;;
-d | --dry-run)
dry_run=true
shift
;;
-p | --push)
push=true
shift
;;
-h | --help)
usage
;;
--)
shift
break
;;
*)
echo "Error: Internal error!" >&2
exit 1
;;
esac
done

if [[ $list == true ]]; then
list_modules
exit 0
fi

if [[ $# -ne 2 ]]; then
echo "Error: MODULE and VERSION are required when not using --list" >&2
usage 1
fi

module="$1"
version="$2"

if ! is_valid_version "$version"; then
exit 1
fi

if ! tag_name=$(get_tag_name "$module" "$version"); then
exit 1
fi

if git rev-parse -q --verify "refs/tags/$tag_name" >/dev/null 2>&1; then
echo "Notice: Tag '$tag_name' already exists" >&2
else
maybe_dry_run git tag -a "$tag_name" -m "Release $module v$version"
if [[ $push == true ]]; then
maybe_dry_run echo "Tag '$tag_name' created."
else
maybe_dry_run echo "Tag '$tag_name' created locally. Use --push to push it to remote."
maybe_dry_run "ℹ️ Note: Remember to push the tag when ready."
fi
fi

if [[ $push == true ]]; then
maybe_dry_run git push origin "$tag_name"
maybe_dry_run echo "Success! Tag '$tag_name' pushed to remote."
fi
65 changes: 0 additions & 65 deletions update-version.sh

This file was deleted.

Loading