Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit 856400c

Browse files
matifalimafredri
andauthored
chore: add support for separate module versioning to CI (#426)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
1 parent 5c5cf8e commit 856400c

File tree

6 files changed

+380
-97
lines changed

6 files changed

+380
-97
lines changed

.github/workflows/ci.yaml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,3 @@ jobs:
5353
config: .github/typos.toml
5454
- name: Lint
5555
run: bun lint
56-
# Disable version check until https://github.com/coder/modules/pull/426 is merged.
57-
# This will allow us to use separate versioning for each module without failing CI. The backend already supports that.
58-
# - name: Check version
59-
# shell: bash
60-
# run: |
61-
# # check for version changes
62-
# ./update-version.sh
63-
# # Check if any changes were made in README.md files
64-
# if [[ -n "$(git status --porcelain -- '**/README.md')" ]]; then
65-
# echo "Version mismatch detected. Please run ./update-version.sh and commit the updated README.md files."
66-
# git diff -- '**/README.md'
67-
# exit 1
68-
# else
69-
# echo "No version mismatch detected. All versions are up to date."
70-
# fi

CONTRIBUTING.md

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ Follow the instructions to ensure that Bun is available globally. Once Bun has b
2222

2323
## Testing a Module
2424

25-
> **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.
25+
> [!NOTE]
26+
> 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.
2627
2728
A suite of test-helpers exists to run `terraform apply` on modules with variables, and test script output against containers.
2829

@@ -53,23 +54,44 @@ module "example" {
5354

5455
## Releases
5556

56-
> [!WARNING]
57-
> 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.
57+
The release process is automated with these steps:
58+
59+
## 1. Create and Merge PR
60+
61+
- Create a PR with your module changes
62+
- Get your PR reviewed, approved, and merged to `main`
63+
64+
## 2. Prepare Release (Maintainer Task)
65+
66+
After merging to `main`, a maintainer will:
67+
68+
- View all modules and their current versions:
69+
70+
```shell
71+
./release.sh --list
72+
```
73+
74+
- Determine the next version number based on changes:
75+
76+
- **Patch version** (1.2.3 → 1.2.4): Bug fixes
77+
- **Minor version** (1.2.3 → 1.3.0): New features, adding inputs, deprecating inputs
78+
- **Major version** (1.2.3 → 2.0.0): Breaking changes (removing inputs, changing input types)
5879

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

61-
1. Navigate to [GitHub's Releases page](https://github.com/coder/modules/releases)
62-
2. Click "Draft a new release"
63-
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".
64-
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).
65-
5. Once everything looks good, click the "Publish release" button.
82+
```shell
83+
# Fetch latest changes
84+
git fetch origin
85+
86+
# Create and push tag
87+
./release.sh module-name 1.2.3 --push
88+
```
6689

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

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

71-
1. Publishing new versions to Coder's [Terraform Registry](https://registry.terraform.io/providers/coder/coder/latest)
72-
2. Publishing new data to the [Coder Registry](https://registry.coder.com)
94+
Our automated processes will handle publishing new data to [registry.coder.com](https://registry.coder.com).
7395

7496
> [!NOTE]
75-
> 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.
97+
> 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.

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
"name": "modules",
33
"scripts": {
44
"test": "bun test",
5-
"fmt": "bun x prettier -w **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt **/*.tf .sample/main.tf",
5+
"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",
66
"fmt:ci": "bun x prettier --check **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt -check **/*.tf .sample/main.tf",
7-
"lint": "bun run lint.ts && ./terraform_validate.sh",
8-
"update-version": "./update-version.sh"
7+
"lint": "bun run lint.ts && ./terraform_validate.sh"
98
},
109
"devDependencies": {
1110
"bun-types": "^1.1.23",

release.sh

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
usage() {
5+
cat <<EOF
6+
Usage: $0 [OPTIONS] [<MODULE> <VERSION>]
7+
8+
Create annotated git tags for module releases.
9+
10+
This script is used by maintainers to create annotated tags for module
11+
releases. When a tag is pushed, it triggers a GitHub workflow that
12+
updates README versions.
13+
14+
Options:
15+
-l, --list List all modules with their versions
16+
-n, --dry-run Show what would be done without making changes
17+
-p, --push Push the created tag to the remote repository
18+
-h, --help Show this help message
19+
20+
Examples:
21+
$0 --list
22+
$0 nodejs 1.2.3
23+
$0 nodejs 1.2.3 --push
24+
$0 --dry-run nodejs 1.2.3
25+
EOF
26+
exit "${1:-0}"
27+
}
28+
29+
check_getopt() {
30+
# Check if we have GNU or BSD getopt.
31+
if getopt --test >/dev/null 2>&1; then
32+
# Exit status 4 means GNU getopt is available.
33+
if [[ $? -ne 4 ]]; then
34+
echo "Error: GNU getopt is not available." >&2
35+
echo "On macOS, you can install GNU getopt and add it to your PATH:" >&2
36+
echo
37+
echo $'\tbrew install gnu-getopt' >&2
38+
echo $'\texport PATH="$(brew --prefix gnu-getopt)/bin:$PATH"' >&2
39+
exit 1
40+
fi
41+
fi
42+
}
43+
44+
maybe_dry_run() {
45+
if [[ $dry_run == true ]]; then
46+
echo "[DRY RUN] $*"
47+
return 0
48+
fi
49+
"$@"
50+
}
51+
52+
get_readme_version() {
53+
grep -o 'version *= *"[0-9]\+\.[0-9]\+\.[0-9]\+"' "$1" |
54+
head -1 |
55+
grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' ||
56+
echo "0.0.0"
57+
}
58+
59+
list_modules() {
60+
printf "\nListing all modules and their latest versions:\n"
61+
printf "%s\n" "--------------------------------------------------------------"
62+
printf "%-30s %-15s %-15s\n" "MODULE" "README VERSION" "LATEST TAG"
63+
printf "%s\n" "--------------------------------------------------------------"
64+
65+
# Process each module directory.
66+
for dir in */; do
67+
# Skip non-module directories.
68+
[[ ! -d $dir || ! -f ${dir}README.md || $dir == ".git/" ]] && continue
69+
70+
module="${dir%/}"
71+
readme_version=$(get_readme_version "${dir}README.md")
72+
latest_tag=$(git tag -l "release/${module}/v*" | sort -V | tail -n 1)
73+
tag_version="none"
74+
if [[ -n $latest_tag ]]; then
75+
tag_version="${latest_tag#"release/${module}/v"}"
76+
fi
77+
78+
printf "%-30s %-15s %-15s\n" "$module" "$readme_version" "$tag_version"
79+
done
80+
81+
printf "%s\n" "--------------------------------------------------------------"
82+
}
83+
84+
is_valid_version() {
85+
if ! [[ $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
86+
echo "Error: Version must be in format X.Y.Z (e.g., 1.2.3)" >&2
87+
return 1
88+
fi
89+
}
90+
91+
get_tag_name() {
92+
local module="$1"
93+
local version="$2"
94+
local tag_name="release/$module/v$version"
95+
local readme_path="$module/README.md"
96+
97+
if [[ ! -d $module || ! -f $readme_path ]]; then
98+
echo "Error: Module '$module' not found or missing README.md" >&2
99+
return 1
100+
fi
101+
102+
local readme_version
103+
readme_version=$(get_readme_version "$readme_path")
104+
105+
{
106+
echo "Module: $module"
107+
echo "Current README version: $readme_version"
108+
echo "New tag version: $version"
109+
echo "Tag name: $tag_name"
110+
} >&2
111+
112+
echo "$tag_name"
113+
}
114+
115+
# Ensure getopt is available.
116+
check_getopt
117+
118+
# Set defaults.
119+
list=false
120+
dry_run=false
121+
push=false
122+
module=
123+
version=
124+
125+
# Parse command-line options.
126+
if ! temp=$(getopt -o ldph --long list,dry-run,push,help -n "$0" -- "$@"); then
127+
echo "Error: Failed to parse arguments" >&2
128+
usage 1
129+
fi
130+
eval set -- "$temp"
131+
132+
while true; do
133+
case "$1" in
134+
-l | --list)
135+
list=true
136+
shift
137+
;;
138+
-d | --dry-run)
139+
dry_run=true
140+
shift
141+
;;
142+
-p | --push)
143+
push=true
144+
shift
145+
;;
146+
-h | --help)
147+
usage
148+
;;
149+
--)
150+
shift
151+
break
152+
;;
153+
*)
154+
echo "Error: Internal error!" >&2
155+
exit 1
156+
;;
157+
esac
158+
done
159+
160+
if [[ $list == true ]]; then
161+
list_modules
162+
exit 0
163+
fi
164+
165+
if [[ $# -ne 2 ]]; then
166+
echo "Error: MODULE and VERSION are required when not using --list" >&2
167+
usage 1
168+
fi
169+
170+
module="$1"
171+
version="$2"
172+
173+
if ! is_valid_version "$version"; then
174+
exit 1
175+
fi
176+
177+
if ! tag_name=$(get_tag_name "$module" "$version"); then
178+
exit 1
179+
fi
180+
181+
if git rev-parse -q --verify "refs/tags/$tag_name" >/dev/null 2>&1; then
182+
echo "Notice: Tag '$tag_name' already exists" >&2
183+
else
184+
maybe_dry_run git tag -a "$tag_name" -m "Release $module v$version"
185+
if [[ $push == true ]]; then
186+
maybe_dry_run echo "Tag '$tag_name' created."
187+
else
188+
maybe_dry_run echo "Tag '$tag_name' created locally. Use --push to push it to remote."
189+
maybe_dry_run "ℹ️ Note: Remember to push the tag when ready."
190+
fi
191+
fi
192+
193+
if [[ $push == true ]]; then
194+
maybe_dry_run git push origin "$tag_name"
195+
maybe_dry_run echo "Success! Tag '$tag_name' pushed to remote."
196+
fi

update-version.sh

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)