|
| 1 | +#Contributing modules |
| 2 | + |
| 3 | +Learn how to create and contribute Terraform modules to the Coder Registry. Modules provide reusable components that extend Coder workspaces with IDEs, development tools, login tools, and other features. |
| 4 | + |
| 5 | +##What are Coder modules |
| 6 | + |
| 7 | +Coder modules are Terraform modules that integrate with Coder workspaces to provide specific functionality. They are published to the Coder Registry at[registry.coder.com](https://registry.coder.com) and can be consumed in any Coder template using standard Terraform module syntax. |
| 8 | + |
| 9 | +Examples of modules include: |
| 10 | + |
| 11 | +-**Desktop IDEs**:[`jetbrains-fleet`](https://registry.coder.com/modules/coder/jetbrains-fleet),[`cursor`](https://registry.coder.com/modules/coder/cursor),[`windsurf`](https://registry.coder.com/modules/coder/windsurf),[`zed`](https://registry.coder.com/modules/coder/zed) |
| 12 | +-**Web IDEs**:[`code-server`](https://registry.coder.com/modules/coder/code-server),[`vscode-web`](https://registry.coder.com/modules/coder/vscode-web),[`jupyter-notebook`](https://registry.coder.com/modules/coder/jupyter-notebook),[`jupyter-lab`](https://registry.coder.com/modules/coder/jupyterlab) |
| 13 | +-**Integrations**:[`devcontainers-cli`](https://registry.coder.com/modules/coder/devcontainers-cli),[`vault-github`](https://registry.coder.com/modules/coder/vault-github),[`jfrog-oauth`](https://registry.coder.com/modules/coder/jfrog-oauth),[`jfrog-token`](https://registry.coder.com/modules/coder/jfrog-token) |
| 14 | +-**Workspace utilities**:[`git-clone`](https://registry.coder.com/modules/coder/git-clone),[`dotfiles`](https://registry.coder.com/modules/coder/dotfiles),[`filebrowser`](https://registry.coder.com/modules/coder/filebrowser),[`coder-login`](https://registry.coder.com/modules/coder/coder-login),[`personalize`](https://registry.coder.com/modules/coder/personalize) |
| 15 | + |
| 16 | +##Prerequisites |
| 17 | + |
| 18 | +Before contributing modules, ensure you have: |
| 19 | + |
| 20 | +- Basic Terraform knowledge |
| 21 | +-[Terraform installed](https://developer.hashicorp.com/terraform/install) |
| 22 | +-[Docker installed](https://docs.docker.com/get-docker/) (for running tests) |
| 23 | +-[Bun installed](https://bun.sh/docs/installation) (for running tests and tooling) |
| 24 | + |
| 25 | +##Setup your development environment |
| 26 | + |
| 27 | +1.**Fork and clone the repository**: |
| 28 | + |
| 29 | +```bash |
| 30 | + git clone https://github.com/your-username/registry.git |
| 31 | +cd registry |
| 32 | +``` |
| 33 | + |
| 34 | +2.**Install dependencies**: |
| 35 | + |
| 36 | +```bash |
| 37 | + bun install |
| 38 | +``` |
| 39 | + |
| 40 | +3.**Understand the structure**: |
| 41 | + |
| 42 | +```text |
| 43 | + registry/[namespace]/ |
| 44 | + ├── modules/ # Your modules |
| 45 | + ├── .images/ # Namespace avatar and screenshots |
| 46 | + └── README.md # Namespace description |
| 47 | +``` |
| 48 | + |
| 49 | +##Create your first module |
| 50 | + |
| 51 | +###1. Set up your namespace |
| 52 | + |
| 53 | +If you're a new contributor, create your namespace directory: |
| 54 | + |
| 55 | +```bash |
| 56 | +mkdir -p registry/[your-username] |
| 57 | +mkdir -p registry/[your-username]/.images |
| 58 | +``` |
| 59 | + |
| 60 | +Add your namespace avatar by downloading your GitHub avatar and saving it as`avatar.png`: |
| 61 | + |
| 62 | +```bash |
| 63 | +curl -o registry/[your-username]/.images/avatar.png https://github.com/[your-username].png |
| 64 | +``` |
| 65 | + |
| 66 | +Create your namespace README at`registry/[your-username]/README.md`: |
| 67 | + |
| 68 | +```markdown |
| 69 | +--- |
| 70 | +display_name: "Your Name" |
| 71 | +bio: "Brief description of what you do" |
| 72 | +github: "your-username" |
| 73 | +avatar: "./.images/avatar.png" |
| 74 | +linkedin: "https://www.linkedin.com/in/your-username" |
| 75 | +website: "https://your-website.com" |
| 76 | +support_email: "support@your-domain.com" |
| 77 | +status: "community" |
| 78 | +--- |
| 79 | + |
| 80 | +# Your Name |
| 81 | + |
| 82 | +Brief description of who you are and what you do. |
| 83 | +``` |
| 84 | + |
| 85 | +>[!NOTE] |
| 86 | +>The`linkedin`,`website`, and`support_email` fields are optional and can be omitted or left empty if not applicable. |
| 87 | +
|
| 88 | +###2. Generate module scaffolding |
| 89 | + |
| 90 | +Use the provided script to generate your module structure: |
| 91 | + |
| 92 | +```bash |
| 93 | +./scripts/new_module.sh [your-username]/[module-name] |
| 94 | +cd registry/[your-username]/modules/[module-name] |
| 95 | +``` |
| 96 | + |
| 97 | +This creates: |
| 98 | + |
| 99 | +-`main.tf` - Terraform configuration template |
| 100 | +-`README.md` - Documentation template with frontmatter |
| 101 | +-`run.sh` - Optional execution script |
| 102 | + |
| 103 | +###3. Implement your module |
| 104 | + |
| 105 | +Edit`main.tf` to build your module's features. Here's an example based on the`git-clone` module structure: |
| 106 | + |
| 107 | +```terraform |
| 108 | +terraform { |
| 109 | + required_providers { |
| 110 | + coder = { |
| 111 | + source = "coder/coder" |
| 112 | + } |
| 113 | + } |
| 114 | +} |
| 115 | +
|
| 116 | +# Input variables |
| 117 | +variable "agent_id" { |
| 118 | + description = "The ID of a Coder agent" |
| 119 | + type = string |
| 120 | +} |
| 121 | +
|
| 122 | +variable "url" { |
| 123 | + description = "Git repository URL to clone" |
| 124 | + type = string |
| 125 | + validation { |
| 126 | + condition = can(regex("^(https?://|git@)", var.url)) |
| 127 | + error_message = "URL must be a valid git repository URL." |
| 128 | + } |
| 129 | +} |
| 130 | +
|
| 131 | +variable "base_dir" { |
| 132 | + description = "Directory to clone the repository into" |
| 133 | + type = string |
| 134 | + default = "~" |
| 135 | +} |
| 136 | +
|
| 137 | +# Resources |
| 138 | +resource "coder_script" "clone_repo" { |
| 139 | + agent_id = var.agent_id |
| 140 | + display_name = "Clone Repository" |
| 141 | + script = <<-EOT |
| 142 | + #!/bin/bash |
| 143 | + set -e |
| 144 | +
|
| 145 | + # Ensure git is installed |
| 146 | + if ! command -v git &> /dev/null; then |
| 147 | + echo "Installing git..." |
| 148 | + sudo apt-get update && sudo apt-get install -y git |
| 149 | + fi |
| 150 | +
|
| 151 | + # Clone repository if it doesn't exist |
| 152 | + if [ ! -d "${var.base_dir}/$(basename ${var.url} .git)" ]; then |
| 153 | + echo "Cloning ${var.url}..." |
| 154 | + git clone ${var.url} ${var.base_dir}/$(basename ${var.url} .git) |
| 155 | + fi |
| 156 | + EOT |
| 157 | + run_on_start = true |
| 158 | +} |
| 159 | +
|
| 160 | +# Outputs |
| 161 | +output "repo_dir" { |
| 162 | + description = "Path to the cloned repository" |
| 163 | + value = "${var.base_dir}/$(basename ${var.url} .git)" |
| 164 | +} |
| 165 | +``` |
| 166 | + |
| 167 | +###4. Write complete tests |
| 168 | + |
| 169 | +Create`main.test.ts` to test your module features: |
| 170 | + |
| 171 | +```typescript |
| 172 | +import {runTerraformApply,runTerraformInit,testRequiredVariables }from"~test" |
| 173 | + |
| 174 | +describe("git-clone",async ()=> { |
| 175 | +awaittestRequiredVariables("registry/[your-username]/modules/git-clone") |
| 176 | + |
| 177 | +it("should clone repository successfully",async ()=> { |
| 178 | +awaitrunTerraformInit("registry/[your-username]/modules/git-clone") |
| 179 | +awaitrunTerraformApply("registry/[your-username]/modules/git-clone", { |
| 180 | + agent_id:"test-agent-id", |
| 181 | + url:"https://github.com/coder/coder.git", |
| 182 | + base_dir:"/tmp" |
| 183 | + }) |
| 184 | + }) |
| 185 | + |
| 186 | +it("should work with SSH URLs",async ()=> { |
| 187 | +awaitrunTerraformInit("registry/[your-username]/modules/git-clone") |
| 188 | +awaitrunTerraformApply("registry/[your-username]/modules/git-clone", { |
| 189 | + agent_id:"test-agent-id", |
| 190 | + url:"git@github.com:coder/coder.git" |
| 191 | + }) |
| 192 | + }) |
| 193 | +}) |
| 194 | +``` |
| 195 | + |
| 196 | +###5. Document your module |
| 197 | + |
| 198 | +Update`README.md` with complete documentation: |
| 199 | + |
| 200 | +```markdown |
| 201 | +--- |
| 202 | +display_name: "Git Clone" |
| 203 | +description: "Clone a Git repository into your Coder workspace" |
| 204 | +icon: "../../../../.icons/git.svg" |
| 205 | +verified: false |
| 206 | +tags: ["git", "development", "vcs"] |
| 207 | +--- |
| 208 | + |
| 209 | +# Git Clone |
| 210 | + |
| 211 | +This module clones a Git repository into your Coder workspace and ensures Git is installed. |
| 212 | + |
| 213 | +## Usage |
| 214 | + |
| 215 | +```tf |
| 216 | +module "git_clone" { |
| 217 | + source = "registry.coder.com/[your-username]/git-clone/coder" |
| 218 | + version = "~> 1.0" |
| 219 | + |
| 220 | + agent_id = coder_agent.main.id |
| 221 | + url = "https://github.com/coder/coder.git" |
| 222 | + base_dir = "/home/coder/projects" |
| 223 | +} |
| 224 | +``` |
| 225 | + |
| 226 | +##Module best practices |
| 227 | + |
| 228 | +###Design principles |
| 229 | + |
| 230 | +-**Single responsibility**: Each module should have one clear purpose |
| 231 | +-**Reusability**: Design for use across different workspace types |
| 232 | +-**Flexibility**: Provide sensible defaults but allow customization |
| 233 | +-**Safe to rerun**: Ensure modules can be applied multiple times safely |
| 234 | + |
| 235 | +###Terraform conventions |
| 236 | + |
| 237 | +- Use descriptive variable names and include descriptions |
| 238 | +- Provide default values for optional variables |
| 239 | +- Include helpful outputs for working with other modules |
| 240 | +- Use proper resource dependencies |
| 241 | +- Follow[Terraform style conventions](https://developer.hashicorp.com/terraform/language/syntax/style) |
| 242 | + |
| 243 | +###Documentation standards |
| 244 | + |
| 245 | +Your module README should include: |
| 246 | + |
| 247 | +-**Frontmatter**: Required metadata for the registry |
| 248 | +-**Description**: Clear explanation of what the module does |
| 249 | +-**Usage example**: Working Terraform code snippet |
| 250 | +-**Additional context**: Setup requirements, known limitations, etc. |
| 251 | + |
| 252 | +>[!NOTE] |
| 253 | +>Do not include variables tables in your README. The registry automatically generates variable documentation from your`main.tf` file. |
| 254 | +
|
| 255 | +##Test your module |
| 256 | + |
| 257 | +Run tests to ensure your module works correctly: |
| 258 | + |
| 259 | +```bash |
| 260 | +# Test your specific module |
| 261 | +buntest -t'git-clone' |
| 262 | + |
| 263 | +# Test all modules |
| 264 | +buntest |
| 265 | + |
| 266 | +# Format code |
| 267 | +bun fmt |
| 268 | +``` |
| 269 | + |
| 270 | +>[!IMPORTANT] |
| 271 | +>Tests require Docker with`--network=host` support, which typically requires Linux. macOS users can use[Colima](https://github.com/abiosoft/colima) or[OrbStack](https://orbstack.dev/) instead of Docker Desktop. |
| 272 | +
|
| 273 | +##Contribute to existing modules |
| 274 | + |
| 275 | +###Types of contributions |
| 276 | + |
| 277 | +**Bug fixes**: |
| 278 | + |
| 279 | +- Fix installation or configuration issues |
| 280 | +- Resolve compatibility problems |
| 281 | +- Correct documentation errors |
| 282 | + |
| 283 | +**Feature additions**: |
| 284 | + |
| 285 | +- Add new configuration options |
| 286 | +- Support additional platforms or versions |
| 287 | +- Add new features |
| 288 | + |
| 289 | +**Maintenance**: |
| 290 | + |
| 291 | +- Update dependencies |
| 292 | +- Improve error handling |
| 293 | +- Optimize performance |
| 294 | + |
| 295 | +###Making changes |
| 296 | + |
| 297 | +1.**Identify the issue**: Reproduce the problem or identify the improvement needed |
| 298 | +2.**Make focused changes**: Keep modifications minimal and targeted |
| 299 | +3.**Maintain compatibility**: Ensure existing users aren't broken |
| 300 | +4.**Add tests**: Test new features and edge cases |
| 301 | +5.**Update documentation**: Reflect changes in the README |
| 302 | + |
| 303 | +###Backward compatibility |
| 304 | + |
| 305 | +When modifying existing modules: |
| 306 | + |
| 307 | +- Add new variables with sensible defaults |
| 308 | +- Don't remove existing variables without a migration path |
| 309 | +- Don't change variable types or meanings |
| 310 | +- Test that basic configurations still work |
| 311 | + |
| 312 | +##Versioning |
| 313 | + |
| 314 | +When you modify a module, update its version following semantic versioning: |
| 315 | + |
| 316 | +-**Patch** (1.0.0 → 1.0.1): Bug fixes, documentation updates |
| 317 | +-**Minor** (1.0.0 → 1.1.0): New features, new variables |
| 318 | +-**Major** (1.0.0 → 2.0.0): Breaking changes, removing variables |
| 319 | + |
| 320 | +Use the version bump script to update versions: |
| 321 | + |
| 322 | +```bash |
| 323 | +./.github/scripts/version-bump.sh patch|minor|major |
| 324 | +``` |
| 325 | + |
| 326 | +##Submit your contribution |
| 327 | + |
| 328 | +1.**Create a feature branch**: |
| 329 | + |
| 330 | +```bash |
| 331 | + git checkout -b feat/modify-git-clone-module |
| 332 | +``` |
| 333 | + |
| 334 | +2.**Test thoroughly**: |
| 335 | + |
| 336 | +```bash |
| 337 | + buntest -t'git-clone' |
| 338 | + bun fmt |
| 339 | +``` |
| 340 | + |
| 341 | +3.**Commit with clear messages**: |
| 342 | + |
| 343 | +```bash |
| 344 | + git add. |
| 345 | + git commit -m"feat(git-clone):add git-clone module" |
| 346 | +``` |
| 347 | + |
| 348 | +4.**Open a pull request**: |
| 349 | +- Use a descriptive title |
| 350 | +- Explain what the module does and why it's useful |
| 351 | +- Reference any related issues |
| 352 | + |
| 353 | +##Common issues and solutions |
| 354 | + |
| 355 | +###Testing problems |
| 356 | + |
| 357 | +**Issue**: Tests fail with network errors |
| 358 | +**Solution**: Ensure Docker is running with`--network=host` support |
| 359 | + |
| 360 | +###Module development |
| 361 | + |
| 362 | +**Issue**: Icon not displaying |
| 363 | +**Solution**: Verify icon path is correct and file exists in`.icons/` directory |
| 364 | + |
| 365 | +###Documentation |
| 366 | + |
| 367 | +**Issue**: Code blocks not syntax highlighted |
| 368 | +**Solution**: Use`tf` language identifier for Terraform code blocks |
| 369 | + |
| 370 | +##Get help |
| 371 | + |
| 372 | +-**Examples**: Review existing modules like[`code-server`](https://registry.coder.com/modules/coder/code-server),[`git-clone`](https://registry.coder.com/modules/coder/git-clone), and[`jetbrains-gateway`](https://registry.coder.com/modules/coder/jetbrains-gateway) |
| 373 | +-**Issues**: Open an issue at[github.com/coder/registry](https://github.com/coder/registry/issues) |
| 374 | +-**Community**: Join the[Coder Discord](https://discord.gg/coder) for questions |
| 375 | +-**Documentation**: Check the[Coder docs](https://coder.com/docs) for help on Coder. |
| 376 | + |
| 377 | +##Next steps |
| 378 | + |
| 379 | +After creating your first module: |
| 380 | + |
| 381 | +1.**Share with the community**: Announce your module on Discord or social media |
| 382 | +2.**Iterate based on feedback**: Improve based on user suggestions |
| 383 | +3.**Create more modules**: Build a collection of related tools |
| 384 | +4.**Contribute to existing modules**: Help maintain and improve the ecosystem |
| 385 | + |
| 386 | +Happy contributing! 🚀 |