Compare commits

...

35 Commits

Author SHA1 Message Date
github-actions[bot]
b90f6f9de8 chore: bump version to 1.0.7 in README.md files (#174)
Co-authored-by: matifali <matifali@users.noreply.github.com>
2024-02-26 20:53:27 +05:00
Muhammad Atif Ali
948280600a fix(vault): fix version fetching logic (#172) 2024-02-26 20:51:45 +05:00
Muhammad Atif Ali
407738b2be feat(hcp-vault-secrets): add project_id variable to HCP provider (#173) 2024-02-26 20:50:42 +05:00
github-actions[bot]
08adb4a839 chore: bump version to 1.0.6 in README.md files (#171) 2024-02-23 23:52:36 +05:00
Muhammad Atif Ali
313ec59d46 Add terraform validation to linting (#170)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2024-02-23 23:49:47 +05:00
Muhammad Atif Ali
4b04d18f39 Add extensions support for vscode-web (#154)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2024-02-23 17:02:17 +05:00
github-actions[bot]
ee53ca0281 chore: bump version to 1.0.5 in README.md files (#168)
Co-authored-by: matifali <matifali@users.noreply.github.com>
2024-02-21 12:09:01 +05:00
Muhammad Atif Ali
8e254a3bb9 docs: elaborate instructions for setting up hcp vault module (#163)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2024-02-19 13:44:07 +04:00
Muhammad Atif Ali
1ab53139b3 ci: fix ci permissions (#166) 2024-02-18 23:45:33 +05:00
Muhammad Atif Ali
147bea9782 bump version to v1.0.4 (#160) 2024-02-16 18:50:30 +03:00
Victor Urvantsev
8d8910c52a feat(jfrog): add option to customize server id for JFrog CLI (#158)
Co-authored-by: Victor Urvantsev <victoru@jfrog.com>
2024-02-16 13:16:14 +03:00
Florian Gareis
c00b7536cb Add slug to code server (#161) 2024-02-16 12:54:05 +03:00
Muhammad Atif Ali
d66d7e994e ci: set base branch for docs update PR (#155) 2024-02-14 20:11:13 +03:00
Muhammad Atif Ali
d10ce91a64 fix: fix fetching rc versions of vault cli (#156)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2024-02-14 17:04:56 +03:00
Muhammad Atif Ali
534491613f Update module versions to v1.0.3 (#159) 2024-02-14 16:37:01 +03:00
Muhammad Atif Ali
ac64af6f02 Update Hashicorp vault modules (#140)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2024-02-14 01:43:33 +03:00
Muhammad Atif Ali
b299f98161 ci: automate version bumps in module README.md files (#139)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2024-02-13 15:23:44 +03:00
Muhammad Atif Ali
7e897a51e6 chore(vault-github): Add partner github and tests (#142) 2024-02-13 12:18:23 +03:00
Muhammad Atif Ali
ac54966f5e feat!(git-config): use full name for git configuration (#141) 2024-02-12 17:18:13 +03:00
Andrew Svoboda
aef9b3b116 Add build numbers and versions to jetbrains gateway module (#150) 2024-02-12 17:16:31 +03:00
Phorcys
a5c4d00a01 fix(git-commit-signing): fix SSH key permissions (#152) 2024-02-10 00:20:05 +03:00
Muhammad Atif Ali
3227a47044 fix(jetbrains-gateway): fix readme to include agent_name (#151) 2024-02-09 21:19:29 +03:00
Florian Gareis
cf1807dd5c Allow custom display name and slug for VS Code Web (#146) 2024-02-09 21:18:20 +03:00
Florian Gareis
4c993d342d Fix code-server docu (#147) 2024-02-09 21:17:32 +03:00
Muhammad Atif Ali
5a7e3f6ca4 Add Hashicorp Vault Secrets Integration module (#144) 2024-02-09 21:16:41 +03:00
Muhammad Atif Ali
acab6437bc chore: bump version to 1.0.2 and add script to update them automatically. (#128)
Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
2024-01-30 17:51:31 +03:00
Muhammad Atif Ali
f16d7ca3f5 docs(jfrog-oauth): fix documentation link 2024-01-30 12:41:09 +03:00
Mathias Fredriksson
a9a58bff32 chore: lint for tf/hcl blocks (#135)
Co-authored-by: Muhammad Atif Ali <atif@coder.com>
2024-01-28 00:48:49 +03:00
Muhammad Atif Ali
6b842004e6 ci: check for typos (#131) 2024-01-27 16:50:47 +03:00
Mathias Fredriksson
376c0cae31 chore: add prettier terraform formatting in markdown files (#134) 2024-01-27 15:02:40 +02:00
Muhammad Atif Ali
7d31865c94 feat!(git-clone): change path input to base_dir and return repo_dir as output (#132) 2024-01-26 16:13:03 +03:00
Muhammad Atif Ali
d3fc2d2212 docs(jfrog-oauth): improve docs (#129)
* docs(jfrog-oauth): improve docs

Adds additional step and screenshot to show creating an OAuth app in JFrog platform

* Update README.md

* Add files via upload

* fmt

* move JFrog Artifactory integration setup instructions

* Update JFrog token documentation
2024-01-26 09:20:21 +03:00
Conor Bèhard Roberts
38a2d86376 feat: enable basename of url to be added to custom path (git-clone) (#124) 2024-01-23 13:55:30 +03:00
Muhammad Atif Ali
b968a2aa22 feat: add version to module docs (#122) 2024-01-23 13:50:55 +03:00
Muhammad Atif Ali
5b3edd9bbd fix(code-server): write settings to User (#123) 2024-01-18 23:22:57 +03:00
63 changed files with 1377 additions and 681 deletions

View File

@@ -34,5 +34,7 @@ jobs:
run: bun install run: bun install
- name: Format - name: Format
run: bun fmt:ci run: bun fmt:ci
- name: typos-action
uses: crate-ci/typos@v1.17.2
- name: Lint - name: Lint
run: bun lint run: bun lint

42
.github/workflows/update-readme.yaml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Update README on Tag
on:
workflow_dispatch:
push:
tags:
- 'v*'
jobs:
update-readme:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get the latest tag
id: get-latest-tag
run: echo "TAG=$(git describe --tags --abbrev=0 | sed 's/^v//')" >> $GITHUB_OUTPUT
- name: Run update script
run: ./update-version.sh
- name: Create Pull Request
id: create-pr
uses: peter-evans/create-pull-request@v5
with:
commit-message: 'chore: bump version to ${{ env.TAG }} in README.md files'
title: 'chore: bump version to ${{ env.TAG }} in README.md files'
body: 'This is an auto-generated PR to update README.md files of all modules with the new tag ${{ env.TAG }}'
branch: 'update-readme-branch'
base: 'main'
env:
TAG: ${{ steps.get-latest-tag.outputs.TAG }}
- name: Auto-approve
uses: hmarr/auto-approve-action@v4
if: github.ref == 'refs/heads/update-readme-branch'

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

View File

@@ -11,9 +11,10 @@ tags: [helper]
<!-- Describes what this module does --> <!-- Describes what this module does -->
```hcl ```tf
module "MODULE_NAME" { module "MODULE_NAME" {
source = "https://registry.coder.com/modules/MODULE_NAME" source = "registry.coder.com/modules/MODULE_NAME/coder"
version = "1.0.2"
} }
``` ```
@@ -25,9 +26,10 @@ module "MODULE_NAME" {
Install the Dracula theme from [OpenVSX](https://open-vsx.org/): Install the Dracula theme from [OpenVSX](https://open-vsx.org/):
```hcl ```tf
module "MODULE_NAME" { module "MODULE_NAME" {
source = "https://registry.coder.com/modules/MODULE_NAME" source = "registry.coder.com/modules/MODULE_NAME/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
extensions = [ extensions = [
"dracula-theme.theme-dracula" "dracula-theme.theme-dracula"
@@ -41,9 +43,10 @@ Enter the `<author>.<name>` into the extensions array and code-server will autom
Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) file: Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) file:
```hcl ```tf
module "MODULE_NAME" { module "MODULE_NAME" {
source = "https://registry.coder.com/modules/MODULE_NAME" source = "registry.coder.com/modules/MODULE_NAME/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
extensions = [ "dracula-theme.theme-dracula" ] extensions = [ "dracula-theme.theme-dracula" ]
settings = { settings = {
@@ -56,9 +59,10 @@ module "MODULE_NAME" {
Run code-server in the background, don't fetch it from GitHub: Run code-server in the background, don't fetch it from GitHub:
```hcl ```tf
module "MODULE_NAME" { module "MODULE_NAME" {
source = "https://registry.coder.com/modules/MODULE_NAME" source = "registry.coder.com/modules/MODULE_NAME/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
offline = true offline = true
} }

View File

@@ -69,7 +69,7 @@ resource "coder_app" "MODULE_NAME" {
slug = "MODULE_NAME" slug = "MODULE_NAME"
display_name = "MODULE_NAME" display_name = "MODULE_NAME"
url = "http://localhost:${var.port}" url = "http://localhost:${var.port}"
icon = loocal.icon_url icon = local.icon_url
subdomain = false subdomain = false
share = "owner" share = "owner"

View File

@@ -3,7 +3,7 @@
To create a new module, clone this repository and run: To create a new module, clone this repository and run:
```shell ```shell
./new.sh MOUDLE_NAME ./new.sh MODULE_NAME
``` ```
## Testing a Module ## Testing a Module
@@ -19,8 +19,10 @@ $ bun test -t '<module>'
You can test a module locally by updating the source as follows You can test a module locally by updating the source as follows
```hcl ```tf
module "example" {
source = "git::https://github.com/<USERNAME>/<REPO>.git//<MODULE-NAME>?ref=<BRANCH-NAME>" source = "git::https://github.com/<USERNAME>/<REPO>.git//<MODULE-NAME>?ref=<BRANCH-NAME>"
}
``` ```
> **Note:** This is the responsibility of the module author to implement tests for their module. and test the module locally before submitting a PR. > **Note:** This is the responsibility of the module author to implement tests for their module. and test the module locally before submitting a PR.

View File

@@ -14,9 +14,10 @@ Modules extend Templates to create reusable components for your development envi
e.g. e.g.
```hcl ```tf
module "code-server" { module "code-server" {
source = "https://registry.coder.com/modules/code-server" source = "registry.coder.com/modules/code-server/coder"
version = "1.0.2"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
} }
``` ```

View File

@@ -14,9 +14,10 @@ the region closest to them.
Customize the preselected parameter value: Customize the preselected parameter value:
```hcl ```tf
module "aws-region" { module "aws-region" {
source = "https://registry.coder.com/modules/aws-region" source = "registry.coder.com/modules/aws-region/coder"
version = "1.0.2"
default = "us-east-1" default = "us-east-1"
} }
@@ -33,13 +34,16 @@ provider "aws" {
Change the display name and icon for a region using the corresponding maps: Change the display name and icon for a region using the corresponding maps:
```hcl ```tf
module "aws-region" { module "aws-region" {
source = "https://registry.coder.com/modules/aws-region" source = "registry.coder.com/modules/aws-region/coder"
version = "1.0.2"
default = "ap-south-1" default = "ap-south-1"
custom_names = { custom_names = {
"ap-south-1" : "Awesome Mumbai!" "ap-south-1" : "Awesome Mumbai!"
} }
custom_icons = { custom_icons = {
"ap-south-1" : "/emojis/1f33a.png" "ap-south-1" : "/emojis/1f33a.png"
} }
@@ -56,9 +60,10 @@ provider "aws" {
Hide the Asia Pacific regions Seoul and Osaka: Hide the Asia Pacific regions Seoul and Osaka:
```hcl ```tf
module "aws-region" { module "aws-region" {
source = "https://registry.coder.com/modules/aws-region" source = "registry.coder.com/modules/aws-region/coder"
version = "1.0.2"
exclude = ["ap-northeast-2", "ap-northeast-3"] exclude = ["ap-northeast-2", "ap-northeast-3"]
} }

View File

@@ -11,9 +11,10 @@ tags: [helper, parameter, azure, regions]
This module adds a parameter with all Azure regions, allowing developers to select the region closest to them. This module adds a parameter with all Azure regions, allowing developers to select the region closest to them.
```hcl ```tf
module "azure_region" { module "azure_region" {
source = "https://registry.coder.com/modules/azure-region" source = "registry.coder.com/modules/azure-region/coder"
version = "1.0.2"
default = "eastus" default = "eastus"
} }
@@ -30,9 +31,10 @@ resource "azurem_resource_group" "example" {
Change the display name and icon for a region using the corresponding maps: Change the display name and icon for a region using the corresponding maps:
```hcl ```tf
module "azure-region" { module "azure-region" {
source = "https://registry.coder.com/modules/azure-region" source = "registry.coder.com/modules/azure-region/coder"
version = "1.0.2"
custom_names = { custom_names = {
"australia" : "Go Australia!" "australia" : "Go Australia!"
} }
@@ -52,9 +54,10 @@ resource "azurerm_resource_group" "example" {
Hide all regions in Australia except australiacentral: Hide all regions in Australia except australiacentral:
```hcl ```tf
module "azure-region" { module "azure-region" {
source = "https://registry.coder.com/modules/azure-region" source = "registry.coder.com/modules/azure-region/coder"
version = "1.0.2"
exclude = [ exclude = [
"australia", "australia",
"australiacentral2", "australiacentral2",

BIN
bun.lockb

Binary file not shown.

View File

@@ -11,9 +11,10 @@ tags: [helper, ide, web]
Automatically install [code-server](https://github.com/coder/code-server) in a workspace, create an app to access it via the dashboard, install extensions, and pre-configure editor settings. Automatically install [code-server](https://github.com/coder/code-server) in a workspace, create an app to access it via the dashboard, install extensions, and pre-configure editor settings.
```hcl ```tf
module "code-server" { module "code-server" {
source = "https://registry.coder.com/modules/code-server" source = "registry.coder.com/modules/code-server/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```
@@ -24,9 +25,10 @@ module "code-server" {
### Pin Versions ### Pin Versions
```hcl ```tf
module "code-server" { module "code-server" {
source = "https://registry.coder.com/modules/code-server" source = "registry.coder.com/modules/code-server/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
install_version = "4.8.3" install_version = "4.8.3"
} }
@@ -36,9 +38,10 @@ module "code-server" {
Install the Dracula theme from [OpenVSX](https://open-vsx.org/): Install the Dracula theme from [OpenVSX](https://open-vsx.org/):
```hcl ```tf
module "code-server" { module "code-server" {
source = "https://registry.coder.com/modules/code-server" source = "registry.coder.com/modules/code-server/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
extensions = [ extensions = [
"dracula-theme.theme-dracula" "dracula-theme.theme-dracula"
@@ -52,9 +55,10 @@ Enter the `<author>.<name>` into the extensions array and code-server will autom
Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) file: Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) file:
```hcl ```tf
module "settings" { module "code-server" {
source = "https://registry.coder.com/modules/code-server" source = "registry.coder.com/modules/code-server/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
extensions = ["dracula-theme.theme-dracula"] extensions = ["dracula-theme.theme-dracula"]
settings = { settings = {
@@ -67,9 +71,10 @@ module "settings" {
Just run code-server in the background, don't fetch it from GitHub: Just run code-server in the background, don't fetch it from GitHub:
```hcl ```tf
module "settings" { module "code-server" {
source = "https://registry.coder.com/modules/code-server" source = "registry.coder.com/modules/code-server/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"] extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"]
} }
@@ -79,9 +84,10 @@ module "settings" {
Just run code-server in the background, don't fetch it from GitHub: Just run code-server in the background, don't fetch it from GitHub:
```hcl ```tf
module "settings" { module "code-server" {
source = "https://registry.coder.com/modules/code-server" source = "registry.coder.com/modules/code-server/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
offline = true offline = true
} }

View File

@@ -32,6 +32,12 @@ variable "display_name" {
default = "code-server" default = "code-server"
} }
variable "slug" {
type = string
description = "The slug for the code-server application."
default = "code-server"
}
variable "settings" { variable "settings" {
type = map(string) type = map(string)
description = "A map of settings to apply to code-server." description = "A map of settings to apply to code-server."
@@ -89,7 +95,7 @@ resource "coder_script" "code-server" {
resource "coder_app" "code-server" { resource "coder_app" "code-server" {
agent_id = var.agent_id agent_id = var.agent_id
slug = "code-server" slug = var.slug
display_name = var.display_name display_name = var.display_name
url = "http://localhost:${var.port}/${var.folder != "" ? "?folder=${urlencode(var.folder)}" : ""}" url = "http://localhost:${var.port}/${var.folder != "" ? "?folder=${urlencode(var.folder)}" : ""}"
icon = "/icon/code.svg" icon = "/icon/code.svg"

View File

@@ -39,10 +39,10 @@ for extension in "$${EXTENSIONLIST[@]}"; do
done done
# Check if the settings file exists... # Check if the settings file exists...
if [ ! -f ~/.local/share/code-server/Machine/settings.json ]; then if [ ! -f ~/.local/share/code-server/User/settings.json ]; then
echo "⚙️ Creating settings file..." echo "⚙️ Creating settings file..."
mkdir -p ~/.local/share/code-server/Machine mkdir -p ~/.local/share/code-server/User
echo "${SETTINGS}" > ~/.local/share/code-server/Machine/settings.json echo "${SETTINGS}" > ~/.local/share/code-server/User/settings.json
fi fi
echo "👷 Running code-server in the background..." echo "👷 Running code-server in the background..."

View File

@@ -11,9 +11,10 @@ tags: [helper]
Automatically logs the user into Coder when creating their workspace. Automatically logs the user into Coder when creating their workspace.
```hcl ```tf
module "coder-login" { module "coder-login" {
source = "https://registry.coder.com/modules/coder-login" source = "registry.coder.com/modules/coder-login/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```

View File

@@ -11,9 +11,10 @@ tags: [helper]
Allow developers to optionally bring their own [dotfiles repository](https://dotfiles.github.io)! Under the hood, this module uses the [coder dotfiles](https://coder.com/docs/v2/latest/dotfiles) command. Allow developers to optionally bring their own [dotfiles repository](https://dotfiles.github.io)! Under the hood, this module uses the [coder dotfiles](https://coder.com/docs/v2/latest/dotfiles) command.
```hcl ```tf
module "dotfiles" { module "dotfiles" {
source = "https://registry.coder.com/modules/dotfiles" source = "registry.coder.com/modules/dotfiles/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```

View File

@@ -10,19 +10,20 @@ tags: [helper, parameter, instances, exoscale]
# exoscale-instance-type # exoscale-instance-type
A parameter with all Exoscale instance types. This allows developers to select A parameter with all Exoscale instance types. This allows developers to select
their desired virtuell machine for the workspace. their desired virtual machine for the workspace.
Customize the preselected parameter value: Customize the preselected parameter value:
```hcl ```tf
module "exoscale-instance-type" { module "exoscale-instance-type" {
source = "https://registry.coder.com/modules/exoscale-instance-type" source = "registry.coder.com/modules/exoscale-instance-type/coder"
version = "1.0.2"
default = "standard.medium" default = "standard.medium"
} }
resource "exoscale_compute_instance" "instance" { resource "exoscale_compute_instance" "instance" {
type = module.exoscale-instance-type.value type = module.exoscale-instance-type.value
... # ...
} }
resource "coder_metadata" "workspace_info" { resource "coder_metadata" "workspace_info" {
@@ -41,13 +42,16 @@ resource "coder_metadata" "workspace_info" {
Change the display name a type using the corresponding maps: Change the display name a type using the corresponding maps:
```hcl ```tf
module "exoscale-instance-type" { module "exoscale-instance-type" {
source = "https://registry.coder.com/modules/exoscale-instance-type" source = "registry.coder.com/modules/exoscale-instance-type/coder"
version = "1.0.2"
default = "standard.medium" default = "standard.medium"
custom_names = { custom_names = {
"standard.medium" : "Mittlere Instanz" # German translation "standard.medium" : "Mittlere Instanz" # German translation
} }
custom_descriptions = { custom_descriptions = {
"standard.medium" : "4 GB Arbeitsspeicher, 2 Kerne, 10 - 400 GB Festplatte" # German translation "standard.medium" : "4 GB Arbeitsspeicher, 2 Kerne, 10 - 400 GB Festplatte" # German translation
} }
@@ -55,7 +59,7 @@ module "exoscale-instance-type" {
resource "exoscale_compute_instance" "instance" { resource "exoscale_compute_instance" "instance" {
type = module.exoscale-instance-type.value type = module.exoscale-instance-type.value
... # ...
} }
resource "coder_metadata" "workspace_info" { resource "coder_metadata" "workspace_info" {
@@ -68,13 +72,14 @@ resource "coder_metadata" "workspace_info" {
![Exoscale instance types Custom](../.images/exoscale-instance-custom.png) ![Exoscale instance types Custom](../.images/exoscale-instance-custom.png)
### Use category and exlude type ### Use category and exclude type
Show only gpu1 types Show only gpu1 types
```hcl ```tf
module "exoscale-instance-type" { module "exoscale-instance-type" {
source = "https://registry.coder.com/modules/exoscale-instance-type" source = "registry.coder.com/modules/exoscale-instance-type/coder"
version = "1.0.2"
default = "gpu.large" default = "gpu.large"
type_category = ["gpu"] type_category = ["gpu"]
exclude = [ exclude = [
@@ -91,7 +96,7 @@ module "exoscale-instance-type" {
resource "exoscale_compute_instance" "instance" { resource "exoscale_compute_instance" "instance" {
type = module.exoscale-instance-type.value type = module.exoscale-instance-type.value
... # ...
} }
resource "coder_metadata" "workspace_info" { resource "coder_metadata" "workspace_info" {

View File

@@ -14,9 +14,10 @@ the zone closest to them.
Customize the preselected parameter value: Customize the preselected parameter value:
```hcl ```tf
module "exoscale-zone" { module "exoscale-zone" {
source = "https://registry.coder.com/modules/exoscale-zone" source = "registry.coder.com/modules/exoscale-zone/coder"
version = "1.0.2"
default = "ch-dk-2" default = "ch-dk-2"
} }
@@ -28,7 +29,7 @@ data "exoscale_compute_template" "my_template" {
resource "exoscale_compute_instance" "instance" { resource "exoscale_compute_instance" "instance" {
zone = module.exoscale-zone.value zone = module.exoscale-zone.value
.... # ...
} }
``` ```
@@ -40,13 +41,16 @@ resource "exoscale_compute_instance" "instance" {
Change the display name and icon for a zone using the corresponding maps: Change the display name and icon for a zone using the corresponding maps:
```hcl ```tf
module "exoscale-zone" { module "exoscale-zone" {
source = "https://registry.coder.com/modules/exoscale-zone" source = "registry.coder.com/modules/exoscale-zone/coder"
version = "1.0.2"
default = "at-vie-1" default = "at-vie-1"
custom_names = { custom_names = {
"at-vie-1" : "Home Vienna" "at-vie-1" : "Home Vienna"
} }
custom_icons = { custom_icons = {
"at-vie-1" : "/emojis/1f3e0.png" "at-vie-1" : "/emojis/1f3e0.png"
} }
@@ -59,7 +63,7 @@ data "exoscale_compute_template" "my_template" {
resource "exoscale_compute_instance" "instance" { resource "exoscale_compute_instance" "instance" {
zone = module.exoscale-zone.value zone = module.exoscale-zone.value
.... # ...
} }
``` ```
@@ -69,9 +73,10 @@ resource "exoscale_compute_instance" "instance" {
Hide the Switzerland zones Geneva and Zurich Hide the Switzerland zones Geneva and Zurich
```hcl ```tf
module "exoscale-zone" { module "exoscale-zone" {
source = "https://registry.coder.com/modules/exoscale-zone" source = "registry.coder.com/modules/exoscale-zone/coder"
version = "1.0.2"
exclude = ["ch-gva-2", "ch-dk-2"] exclude = ["ch-gva-2", "ch-dk-2"]
} }
@@ -82,7 +87,7 @@ data "exoscale_compute_template" "my_template" {
resource "exoscale_compute_instance" "instance" { resource "exoscale_compute_instance" "instance" {
zone = module.exoscale-zone.value zone = module.exoscale-zone.value
.... # ...
} }
``` ```

View File

@@ -11,9 +11,10 @@ tags: [helper, filebrowser]
A file browser for your workspace. A file browser for your workspace.
```hcl ```tf
module "filebrowser" { module "filebrowser" {
source = "https://registry.coder.com/modules/filebrowser" source = "registry.coder.com/modules/filebrowser/coder"
version = "1.0.3"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```
@@ -24,9 +25,10 @@ module "filebrowser" {
### Serve a specific directory ### Serve a specific directory
```hcl ```tf
module "filebrowser" { module "filebrowser" {
source = "https://registry.coder.com/modules/filebrowser" source = "registry.coder.com/modules/filebrowser/coder"
version = "1.0.3"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
folder = "/home/coder/project" folder = "/home/coder/project"
} }
@@ -34,9 +36,10 @@ module "filebrowser" {
### Specify location of `filebrowser.db` ### Specify location of `filebrowser.db`
```hcl ```tf
module "filebrowser" { module "filebrowser" {
source = "https://registry.coder.com/modules/filebrowser" source = "registry.coder.com/modules/filebrowser/coder"
version = "1.0.3"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
database_path = ".config/filebrowser.db" database_path = ".config/filebrowser.db"
} }

View File

@@ -33,7 +33,7 @@ describe("filebrowser", async () => {
expect(output.stdout).toEqual([ expect(output.stdout).toEqual([
"\u001b[0;1mInstalling filebrowser ", "\u001b[0;1mInstalling filebrowser ",
"", "",
"🥳 Installation comlete! ", "🥳 Installation complete! ",
"", "",
"👷 Starting filebrowser in background... ", "👷 Starting filebrowser in background... ",
"", "",
@@ -55,7 +55,7 @@ describe("filebrowser", async () => {
expect(output.stdout).toEqual([ expect(output.stdout).toEqual([
"\u001b[0;1mInstalling filebrowser ", "\u001b[0;1mInstalling filebrowser ",
"", "",
"🥳 Installation comlete! ", "🥳 Installation complete! ",
"", "",
"👷 Starting filebrowser in background... ", "👷 Starting filebrowser in background... ",
"", "",
@@ -77,7 +77,7 @@ describe("filebrowser", async () => {
expect(output.stdout).toEqual([ expect(output.stdout).toEqual([
"\u001B[0;1mInstalling filebrowser ", "\u001B[0;1mInstalling filebrowser ",
"", "",
"🥳 Installation comlete! ", "🥳 Installation complete! ",
"", "",
"👷 Starting filebrowser in background... ", "👷 Starting filebrowser in background... ",
"", "",

View File

@@ -5,7 +5,7 @@ printf "$${BOLD}Installing filebrowser \n\n"
curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
printf "🥳 Installation comlete! \n\n" printf "🥳 Installation complete! \n\n"
printf "👷 Starting filebrowser in background... \n\n" printf "👷 Starting filebrowser in background... \n\n"

View File

@@ -13,9 +13,10 @@ This module adds Fly.io regions to your Coder template. Regions can be whitelist
We can use the simplest format here, only adding a default selection as the `atl` region. We can use the simplest format here, only adding a default selection as the `atl` region.
```hcl ```tf
module "fly-region" { module "fly-region" {
source = "https://registry.coder.com/modules/fly-region" source = "registry.coder.com/modules/fly-region/coder"
version = "1.0.2"
default = "atl" default = "atl"
} }
``` ```
@@ -28,9 +29,10 @@ module "fly-region" {
The regions argument can be used to display only the desired regions in the Coder parameter. The regions argument can be used to display only the desired regions in the Coder parameter.
```hcl ```tf
module "fly-region" { module "fly-region" {
source = "https://registry.coder.com/modules/fly-region" source = "registry.coder.com/modules/fly-region/coder"
version = "1.0.2"
default = "ams" default = "ams"
regions = ["ams", "arn", "atl"] regions = ["ams", "arn", "atl"]
} }
@@ -42,13 +44,16 @@ module "fly-region" {
Set custom icons and names with their respective maps. Set custom icons and names with their respective maps.
```hcl ```tf
module "fly-region" { module "fly-region" {
source = "https://registry.coder.com/modules/fly-region" source = "registry.coder.com/modules/fly-region/coder"
version = "1.0.2"
default = "ams" default = "ams"
custom_icons = { custom_icons = {
"ams" = "/emojis/1f90e.png" "ams" = "/emojis/1f90e.png"
} }
custom_names = { custom_names = {
"ams" = "We love the Netherlands!" "ams" = "We love the Netherlands!"
} }

View File

@@ -11,9 +11,10 @@ tags: [gcp, regions, parameter, helper]
This module adds Google Cloud Platform regions to your Coder template. This module adds Google Cloud Platform regions to your Coder template.
```hcl ```tf
module "gcp_region" { module "gcp_region" {
source = "https://registry.coder.com/modules/gcp-region" source = "registry.coder.com/modules/gcp-region/coder"
version = "1.0.2"
regions = ["us", "europe"] regions = ["us", "europe"]
} }
@@ -30,9 +31,10 @@ resource "google_compute_instance" "example" {
Note: setting `gpu_only = true` and using a default region without GPU support, the default will be set to `null`. Note: setting `gpu_only = true` and using a default region without GPU support, the default will be set to `null`.
```hcl ```tf
module "gcp_region" { module "gcp_region" {
source = "https://registry.coder.com/modules/gcp-region" source = "registry.coder.com/modules/gcp-region/coder"
version = "1.0.2"
default = ["us-west1-a"] default = ["us-west1-a"]
regions = ["us-west1"] regions = ["us-west1"]
gpu_only = false gpu_only = false
@@ -45,9 +47,10 @@ resource "google_compute_instance" "example" {
### Add all zones in the Europe West region ### Add all zones in the Europe West region
```hcl ```tf
module "gcp_region" { module "gcp_region" {
source = "https://registry.coder.com/modules/gcp-region" source = "registry.coder.com/modules/gcp-region/coder"
version = "1.0.2"
regions = ["europe-west"] regions = ["europe-west"]
single_zone_per_region = false single_zone_per_region = false
} }
@@ -57,11 +60,12 @@ resource "google_compute_instance" "example" {
} }
``` ```
### Add a single zone from each region in US and Europe that laos has GPUs ### Add a single zone from each region in US and Europe that has GPUs
```hcl ```tf
module "gcp_region" { module "gcp_region" {
source = "https://registry.coder.com/modules/gcp-region" source = "registry.coder.com/modules/gcp-region/coder"
version = "1.0.2"
regions = ["us", "europe"] regions = ["us", "europe"]
gpu_only = true gpu_only = true
single_zone_per_region = true single_zone_per_region = true

View File

@@ -9,33 +9,44 @@ tags: [git, helper]
# Git Clone # Git Clone
This module allows you to automatically clone a repository by URL and skip if it exists in the path provided. This module allows you to automatically clone a repository by URL and skip if it exists in the base directory provided.
```hcl ```tf
module "git-clone" { module "git-clone" {
source = "https://registry.coder.com/modules/git-clone" source = "registry.coder.com/modules/git-clone/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
url = "https://github.com/coder/coder" url = "https://github.com/coder/coder"
} }
``` ```
To use with [Git Authentication](https://coder.com/docs/v2/latest/admin/git-providers), add the provider by ID to your template:
```hcl
data "coder_git_auth" "github" {
id = "github"
}
```
## Examples ## Examples
### Custom Path ### Custom Path
```hcl ```tf
module "git-clone" { module "git-clone" {
source = "https://registry.coder.com/modules/git-clone" source = "registry.coder.com/modules/git-clone/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
url = "https://github.com/coder/coder" url = "https://github.com/coder/coder"
path = "~/projects/coder/coder" base_dir = "~/projects/coder"
}
```
### Git Authentication
To use with [Git Authentication](https://coder.com/docs/v2/latest/admin/git-providers), add the provider by ID to your template:
```tf
module "git-clone" {
source = "registry.coder.com/modules/git-clone/coder"
version = "1.0.2"
agent_id = coder_agent.example.id
url = "https://github.com/coder/coder"
}
data "coder_git_auth" "github" {
id = "github"
} }
``` ```

View File

@@ -14,9 +14,9 @@ variable "url" {
type = string type = string
} }
variable "path" { variable "base_dir" {
default = "" default = ""
description = "The path to clone the repository. Defaults to \"$HOME/<basename of url>\"." description = "The base directory to clone the repository. Defaults to \"$HOME\"."
type = string type = string
} }
@@ -25,10 +25,19 @@ variable "agent_id" {
type = string type = string
} }
locals {
clone_path = var.base_dir != "" ? join("/", [var.base_dir, replace(basename(var.url), ".git", "")]) : join("/", ["~", replace(basename(var.url), ".git", "")])
}
output "repo_dir" {
value = local.clone_path
description = "Full path of cloned repo directory"
}
resource "coder_script" "git_clone" { resource "coder_script" "git_clone" {
agent_id = var.agent_id agent_id = var.agent_id
script = templatefile("${path.module}/run.sh", { script = templatefile("${path.module}/run.sh", {
CLONE_PATH : var.path != "" ? var.path : join("/", ["~", basename(var.url)]), CLONE_PATH = local.clone_path
REPO_URL : var.url, REPO_URL : var.url,
}) })
display_name = "Git Clone" display_name = "Git Clone"

View File

@@ -16,9 +16,10 @@ Please observe that using the SSH key that's part of your Coder account for comm
This module has a chance of conflicting with the user's dotfiles / the personalize module if one of those has configuration directives that overwrite this module's / each other's git configuration. This module has a chance of conflicting with the user's dotfiles / the personalize module if one of those has configuration directives that overwrite this module's / each other's git configuration.
```hcl ```tf
module "git-commit-signing" { module "git-commit-signing" {
source = "https://registry.coder.com/modules/git-commit-signing" source = "registry.coder.com/modules/git-commit-signing/coder"
version = "1.0.3"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```

View File

@@ -31,8 +31,8 @@ jq --raw-output ".private_key" > ~/.ssh/git-commit-signing/coder << EOF
$ssh_key $ssh_key
EOF EOF
chmod -R 400 ~/.ssh/git-commit-signing/coder chmod -R 600 ~/.ssh/git-commit-signing/coder
chmod -R 400 ~/.ssh/git-commit-signing/coder.pub chmod -R 644 ~/.ssh/git-commit-signing/coder.pub
echo "Configuring git to use the SSH key" echo "Configuring git to use the SSH key"

View File

@@ -11,9 +11,10 @@ tags: [helper, git]
Runs a script that updates git credentials in the workspace to match the user's Coder credentials, optionally allowing to the developer to override the defaults. Runs a script that updates git credentials in the workspace to match the user's Coder credentials, optionally allowing to the developer to override the defaults.
```hcl ```tf
module "git-config" { module "git-config" {
source = "https://registry.coder.com/modules/git-config" source = "registry.coder.com/modules/git-config/coder"
version = "1.0.3"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```
@@ -24,9 +25,10 @@ TODO: Add screenshot
### Allow users to override both username and email ### Allow users to override both username and email
```hcl ```tf
module "git-config" { module "git-config" {
source = "https://registry.coder.com/modules/git-config" source = "registry.coder.com/modules/git-config/coder"
version = "1.0.3"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
allow_email_change = true allow_email_change = true
} }
@@ -36,13 +38,12 @@ TODO: Add screenshot
## Disallowing users from overriding both username and email ## Disallowing users from overriding both username and email
```hcl ```tf
module "git-config" { module "git-config" {
source = "https://registry.coder.com/modules/git-config" source = "registry.coder.com/modules/git-config/coder"
version = "1.0.3"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
allow_username_change = false allow_username_change = false
allow_email_change = false allow_email_change = false
} }
``` ```
TODO: Add screenshot

View File

@@ -1,43 +0,0 @@
import { describe, expect, it } from "bun:test";
import {
executeScriptInContainer,
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "../test";
describe("git-config", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "foo",
});
it("fails without git", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
});
const output = await executeScriptInContainer(state, "alpine");
expect(output.exitCode).toBe(1);
expect(output.stdout).toEqual([
"\u001B[0;1mChecking git-config!",
"Git is not installed!",
]);
});
it("runs with git", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
});
const output = await executeScriptInContainer(state, "alpine/git");
expect(output.exitCode).toBe(0);
expect(output.stdout).toEqual([
"\u001B[0;1mChecking git-config!",
"git-config: No user.email found, setting to ",
"git-config: No user.name found, setting to default",
"",
"\u001B[0;1mgit-config: using email: ",
"\u001B[0;1mgit-config: using username: default",
]);
});
});

View File

@@ -4,7 +4,7 @@ terraform {
required_providers { required_providers {
coder = { coder = {
source = "coder/coder" source = "coder/coder"
version = ">= 0.12" version = ">= 0.13"
} }
} }
} }
@@ -34,7 +34,7 @@ data "coder_parameter" "user_email" {
name = "user_email" name = "user_email"
type = "string" type = "string"
default = "" default = ""
description = "Git user.email to be used for commits. Leave empty to default to Coder username." description = "Git user.email to be used for commits. Leave empty to default to Coder user's email."
display_name = "Git config user.email" display_name = "Git config user.email"
mutable = true mutable = true
} }
@@ -44,18 +44,31 @@ data "coder_parameter" "username" {
name = "username" name = "username"
type = "string" type = "string"
default = "" default = ""
description = "Git user.name to be used for commits. Leave empty to default to Coder username." description = "Git user.name to be used for commits. Leave empty to default to Coder user's Full Name."
display_name = "Git config user.name" display_name = "Full Name for Git config"
mutable = true mutable = true
} }
resource "coder_script" "git_config" { resource "coder_env" "git_author_name" {
agent_id = var.agent_id agent_id = var.agent_id
script = templatefile("${path.module}/run.sh", { name = "GIT_AUTHOR_NAME"
GIT_USERNAME = try(data.coder_parameter.username[0].value, "") == "" ? data.coder_workspace.me.owner : try(data.coder_parameter.username[0].value, "") value = coalesce(try(data.coder_parameter.username[0].value, ""), data.coder_workspace.me.owner_name, data.coder_workspace.me.owner)
GIT_EMAIL = try(data.coder_parameter.user_email[0].value, "") == "" ? data.coder_workspace.me.owner_email : try(data.coder_parameter.user_email[0].value, "") }
})
display_name = "Git Config" resource "coder_env" "git_commmiter_name" {
icon = "/icon/git.svg" agent_id = var.agent_id
run_on_start = true name = "GIT_COMMITTER_NAME"
value = coalesce(try(data.coder_parameter.username[0].value, ""), data.coder_workspace.me.owner_name, data.coder_workspace.me.owner)
}
resource "coder_env" "git_author_email" {
agent_id = var.agent_id
name = "GIT_AUTHOR_EMAIL"
value = coalesce(try(data.coder_parameter.user_email[0].value, ""), data.coder_workspace.me.owner_email)
}
resource "coder_env" "git_commmiter_email" {
agent_id = var.agent_id
name = "GIT_COMMITTER_EMAIL"
value = coalesce(try(data.coder_parameter.user_email[0].value, ""), data.coder_workspace.me.owner_email)
} }

View File

@@ -1,24 +0,0 @@
#!/usr/bin/env sh
BOLD='\033[0;1m'
printf "$${BOLD}Checking git-config!\n"
# Check if git is installed
command -v git > /dev/null 2>&1 || {
echo "Git is not installed!"
exit 1
}
# Set git username and email if missing
if [ -z $(git config --get user.email) ]; then
printf "git-config: No user.email found, setting to ${GIT_EMAIL}\n"
git config --global user.email "${GIT_EMAIL}"
fi
if [ -z $(git config --get user.name) ]; then
printf "git-config: No user.name found, setting to ${GIT_USERNAME}\n"
git config --global user.name "${GIT_USERNAME}"
fi
printf "\n$${BOLD}git-config: using email: $(git config --get user.email)\n"
printf "$${BOLD}git-config: using username: $(git config --get user.name)\n\n"

View File

@@ -0,0 +1,80 @@
---
display_name: "HCP Vault Secrets"
description: "Fetch secrets from HCP Vault"
icon: ../.icons/vault.svg
maintainer_github: coder
partner_github: hashicorp
verified: true
tags: [helper, integration, vault, hashicorp, hvs]
---
# HCP Vault Secrets
This module lets you fetch all or selective secrets from a [HCP Vault Secrets](https://developer.hashicorp.com/hcp/docs/vault-secrets) app into your [Coder](https://coder.com) workspaces. It makes use of the [`hcp_vault_secrets_app`](https://registry.terraform.io/providers/hashicorp/hcp/latest/docs/data-sources/vault_secrets_app) data source from the [HCP provider](https://registry.terraform.io/providers/hashicorp/hcp/latest).
```tf
module "vault" {
source = "registry.coder.com/modules/hcp-vault-secrets/coder"
version = "1.0.7"
agent_id = coder_agent.example.id
app_name = "demo-app"
project_id = "aaa-bbb-ccc"
}
```
## Configuration
To configure the HCP Vault Secrets module, follow these steps,
1. [Create secrets in HCP Vault Secrets](https://developer.hashicorp.com/vault/tutorials/hcp-vault-secrets-get-started/hcp-vault-secrets-create-secret)
2. Create an HCP Service Principal from the HCP Vault Secrets app in the HCP console. This will give you the `HCP_CLIENT_ID` and `HCP_CLIENT_SECRET` that you need to authenticate with HCP Vault Secrets.
![HCP vault secrets credentials](../.images/hcp-vault-secrets-credentials.png)
3. Set `HCP_CLIENT_ID` and `HCP_CLIENT_SECRET` variables on the coder provisioner (recommended) or supply them as input to the module.
4. Set the `project_id`. This is the ID of the project where the HCP Vault Secrets app is running.
> See the [HCP Vault Secrets documentation](https://developer.hashicorp.com/hcp/docs/vault-secrets) for more information.
## Fetch All Secrets
To fetch all secrets from the HCP Vault Secrets app, skip the `secrets` input.
```tf
module "vault" {
source = "registry.coder.com/modules/hcp-vault-secrets/coder"
version = "1.0.7"
agent_id = coder_agent.example.id
app_name = "demo-app"
project_id = "aaa-bbb-ccc"
}
```
## Fetch Selective Secrets
To fetch selective secrets from the HCP Vault Secrets app, set the `secrets` input.
```tf
module "vault" {
source = "registry.coder.com/modules/hcp-vault-secrets/coder"
version = "1.0.7"
agent_id = coder_agent.example.id
app_name = "demo-app"
project_id = "aaa-bbb-ccc"
secrets = ["MY_SECRET_1", "MY_SECRET_2"]
}
```
## Set Client ID and Client Secret as Inputs
Set `client_id` and `client_secret` as module inputs.
```tf
module "vault" {
source = "registry.coder.com/modules/hcp-vault-secrets/coder"
version = "1.0.7"
agent_id = coder_agent.example.id
app_name = "demo-app"
project_id = "aaa-bbb-ccc"
client_id = "HCP_CLIENT_ID"
client_secret = "HCP_CLIENT_SECRET"
}
```

73
hcp-vault-secrets/main.tf Normal file
View File

@@ -0,0 +1,73 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.12.4"
}
hcp = {
source = "hashicorp/hcp"
version = ">= 0.82.0"
}
}
}
provider "hcp" {
client_id = var.client_id
client_secret = var.client_secret
project_id = var.project_id
}
provider "coder" {}
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "project_id" {
type = string
description = "The ID of the HCP project."
}
variable "client_id" {
type = string
description = <<-EOF
The client ID for the HCP Vault Secrets service principal. (Optional if HCP_CLIENT_ID is set as an environment variable.)
EOF
default = null
sensitive = true
}
variable "client_secret" {
type = string
description = <<-EOF
The client secret for the HCP Vault Secrets service principal. (Optional if HCP_CLIENT_SECRET is set as an environment variable.)
EOF
default = null
sensitive = true
}
variable "app_name" {
type = string
description = "The name of the secrets app in HCP Vault Secrets"
}
variable "secrets" {
type = list(string)
description = "The names of the secrets to retrieve from HCP Vault Secrets"
default = null
}
data "hcp_vault_secrets_app" "secrets" {
app_name = var.app_name
}
resource "coder_env" "hvs_secrets" {
# https://support.hashicorp.com/hc/en-us/articles/4538432032787-Variable-has-a-sensitive-value-and-cannot-be-used-as-for-each-arguments
for_each = var.secrets != null ? toset(var.secrets) : nonsensitive(toset(keys(data.hcp_vault_secrets_app.secrets.secrets)))
agent_id = var.agent_id
name = each.key
value = data.hcp_vault_secrets_app.secrets.secrets[each.key]
}

View File

@@ -11,10 +11,12 @@ tags: [ide, jetbrains, helper, parameter]
This module adds a JetBrains Gateway Button to open any workspace with a single click. This module adds a JetBrains Gateway Button to open any workspace with a single click.
```hcl ```tf
module "jetbrains_gateway" { module "jetbrains_gateway" {
source = "https://registry.coder.com/modules/jetbrains-gateway" source = "registry.coder.com/modules/jetbrains-gateway/coder"
version = "1.0.6"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
agent_name = "example"
folder = "/home/coder/example" folder = "/home/coder/example"
jetbrains_ides = ["GO", "WS", "IU", "PY", "PS", "CL", "RM"] jetbrains_ides = ["GO", "WS", "IU", "PY", "PS", "CL", "RM"]
default = "PY" default = "PY"
@@ -27,10 +29,12 @@ module "jetbrains_gateway" {
### Add GoLand and WebStorm with the default set to GoLand ### Add GoLand and WebStorm with the default set to GoLand
```hcl ```tf
module "jetbrains_gateway" { module "jetbrains_gateway" {
source = "https://registry.coder.com/modules/jetbrains-gateway" source = "registry.coder.com/modules/jetbrains-gateway/coder"
version = "1.0.6"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
agent_name = "example"
folder = "/home/coder/example" folder = "/home/coder/example"
jetbrains_ides = ["GO", "WS"] jetbrains_ides = ["GO", "WS"]
default = "GO" default = "GO"

View File

@@ -11,18 +11,16 @@ describe("jetbrains-gateway", async () => {
await testRequiredVariables(import.meta.dir, { await testRequiredVariables(import.meta.dir, {
agent_id: "foo", agent_id: "foo",
agent_name: "foo", agent_name: "foo",
folder: "/baz/", folder: "/home/foo",
}); });
it("default to first ide", async () => { it("default to first ide", async () => {
const state = await runTerraformApply(import.meta.dir, { const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo", agent_id: "foo",
agent_name: "foo", agent_name: "foo",
folder: "/baz/", folder: "/home/foo",
jetbrains_ides: '["IU", "GO", "PY"]', jetbrains_ides: '["IU", "GO", "PY"]',
}); });
expect(state.outputs.jetbrains_ides.value).toBe( expect(state.outputs.identifier.value).toBe("IU");
'["IU","232.10203.10","https://download.jetbrains.com/idea/ideaIU-2023.2.4.tar.gz"]',
);
}); });
}); });

View File

@@ -22,6 +22,10 @@ variable "agent_name" {
variable "folder" { variable "folder" {
type = string type = string
description = "The directory to open in the IDE. e.g. /home/coder/project" description = "The directory to open in the IDE. e.g. /home/coder/project"
validation {
condition = can(regex("^(?:/[^/]+)+$", var.folder))
error_message = "The folder must be a full path and must not start with a ~."
}
} }
variable "default" { variable "default" {
@@ -30,6 +34,52 @@ variable "default" {
description = "Default IDE" description = "Default IDE"
} }
variable "jetbrains_ide_versions" {
type = map(object({
build_number = string
version = string
}))
description = "The set of versions for each jetbrains IDE"
default = {
"IU" = {
build_number = "232.10203.10"
version = "2023.2.4"
}
"PS" = {
build_number = "232.10072.32"
version = "2023.2.3"
}
"WS" = {
build_number = "232.10203.14"
version = "2023.2.4"
}
"PY" = {
build_number = "232.10203.26"
version = "2023.2.4"
}
"CL" = {
build_number = "232.9921.42"
version = "2023.2.2"
}
"GO" = {
build_number = "232.10203.20"
version = "2023.2.4"
}
"RM" = {
build_number = "232.10203.15"
version = "2023.2.4"
}
}
validation {
condition = (
alltrue([
for code in keys(var.jetbrains_ide_versions) : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM"], code)
])
)
error_message = "The jetbrains_ide_versions must contain a map of valid product codes. Valid product codes are ${join(",", ["IU", "PS", "WS", "PY", "CL", "GO", "RM"])}."
}
}
variable "jetbrains_ides" { variable "jetbrains_ides" {
type = list(string) type = list(string)
description = "The list of IDE product codes." description = "The list of IDE product codes."
@@ -40,7 +90,7 @@ variable "jetbrains_ides" {
for code in var.jetbrains_ides : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM"], code) for code in var.jetbrains_ides : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM"], code)
]) ])
) )
error_message = "The jetbrains_ides must be a list of valid product codes. Valid product codes are IU, PS, WS, PY, CL, GO, RM." error_message = "The jetbrains_ides must be a list of valid product codes. Valid product codes are ${join(",", ["IU", "PS", "WS", "PY", "CL", "GO", "RM"])}."
} }
# check if the list is empty # check if the list is empty
validation { validation {
@@ -59,56 +109,69 @@ locals {
"GO" = { "GO" = {
icon = "/icon/goland.svg", icon = "/icon/goland.svg",
name = "GoLand", name = "GoLand",
value = jsonencode(["GO", "232.10203.20", "https://download.jetbrains.com/go/goland-2023.2.4.tar.gz"]) identifier = "GO",
build_number = var.jetbrains_ide_versions["GO"].build_number,
download_link = "https://download.jetbrains.com/go/goland-${var.jetbrains_ide_versions["GO"].version}.tar.gz"
}, },
"WS" = { "WS" = {
icon = "/icon/webstorm.svg", icon = "/icon/webstorm.svg",
name = "WebStorm", name = "WebStorm",
value = jsonencode(["WS", "232.10203.14", "https://download.jetbrains.com/webstorm/WebStorm-2023.2.4.tar.gz"]) identifier = "WS",
build_number = var.jetbrains_ide_versions["WS"].build_number,
download_link = "https://download.jetbrains.com/webstorm/WebStorm-${var.jetbrains_ide_versions["WS"].version}.tar.gz"
}, },
"IU" = { "IU" = {
icon = "/icon/intellij.svg", icon = "/icon/intellij.svg",
name = "IntelliJ IDEA Ultimate", name = "IntelliJ IDEA Ultimate",
value = jsonencode(["IU", "232.10203.10", "https://download.jetbrains.com/idea/ideaIU-2023.2.4.tar.gz"]) identifier = "IU",
build_number = var.jetbrains_ide_versions["IU"].build_number,
download_link = "https://download.jetbrains.com/idea/ideaIU-${var.jetbrains_ide_versions["IU"].version}.tar.gz"
}, },
"PY" = { "PY" = {
icon = "/icon/pycharm.svg", icon = "/icon/pycharm.svg",
name = "PyCharm Professional", name = "PyCharm Professional",
value = jsonencode(["PY", "232.10203.26", "https://download.jetbrains.com/python/pycharm-professional-2023.2.4.tar.gz"]) identifier = "PY",
build_number = var.jetbrains_ide_versions["PY"].build_number,
download_link = "https://download.jetbrains.com/python/pycharm-professional-${var.jetbrains_ide_versions["PY"].version}.tar.gz"
}, },
"CL" = { "CL" = {
icon = "/icon/clion.svg", icon = "/icon/clion.svg",
name = "CLion", name = "CLion",
value = jsonencode(["CL", "232.9921.42", "https://download.jetbrains.com/cpp/CLion-2023.2.2.tar.gz"]) identifier = "CL",
build_number = var.jetbrains_ide_versions["CL"].build_number,
download_link = "https://download.jetbrains.com/cpp/CLion-${var.jetbrains_ide_versions["CL"].version}.tar.gz"
}, },
"PS" = { "PS" = {
icon = "/icon/phpstorm.svg", icon = "/icon/phpstorm.svg",
name = "PhpStorm", name = "PhpStorm",
value = jsonencode(["PS", "232.10072.32", "https://download.jetbrains.com/webide/PhpStorm-2023.2.3.tar.gz"]) identifier = "PS",
build_number = var.jetbrains_ide_versions["PS"].build_number,
download_link = "https://download.jetbrains.com/webide/PhpStorm-${var.jetbrains_ide_versions["PS"].version}.tar.gz"
}, },
"RM" = { "RM" = {
icon = "/icon/rubymine.svg", icon = "/icon/rubymine.svg",
name = "RubyMine", name = "RubyMine",
value = jsonencode(["RM", "232.10203.15", "https://download.jetbrains.com/ruby/RubyMine-2023.2.4.tar.gz"]) identifier = "RM",
build_number = var.jetbrains_ide_versions["RM"].build_number,
download_link = "https://download.jetbrains.com/ruby/RubyMine-${var.jetbrains_ide_versions["RM"].version}.tar.gz"
} }
} }
} }
data "coder_parameter" "jetbrains_ide" { data "coder_parameter" "jetbrains_ide" {
type = "list(string)" type = "string"
name = "jetbrains_ide" name = "jetbrains_ide"
display_name = "JetBrains IDE" display_name = "JetBrains IDE"
icon = "/icon/gateway.svg" icon = "/icon/gateway.svg"
mutable = true mutable = true
# check if default is in the jet_brains_ides list and if it is not empty or null otherwise set it to null default = var.default == "" ? var.jetbrains_ides[0] : var.default
default = var.default != null && var.default != "" && contains(var.jetbrains_ides, var.default) ? local.jetbrains_ides[var.default].value : local.jetbrains_ides[var.jetbrains_ides[0]].value
dynamic "option" { dynamic "option" {
for_each = { for key, value in local.jetbrains_ides : key => value if contains(var.jetbrains_ides, key) } for_each = var.jetbrains_ides
content { content {
icon = option.value.icon icon = lookup(local.jetbrains_ides, option.value).icon
name = option.value.name name = lookup(local.jetbrains_ides, option.value).name
value = option.value.value value = lookup(local.jetbrains_ides, option.value).identifier
} }
} }
} }
@@ -117,9 +180,9 @@ data "coder_workspace" "me" {}
resource "coder_app" "gateway" { resource "coder_app" "gateway" {
agent_id = var.agent_id agent_id = var.agent_id
display_name = data.coder_parameter.jetbrains_ide.option[index(data.coder_parameter.jetbrains_ide.option.*.value, data.coder_parameter.jetbrains_ide.value)].name
slug = "gateway" slug = "gateway"
icon = data.coder_parameter.jetbrains_ide.option[index(data.coder_parameter.jetbrains_ide.option.*.value, data.coder_parameter.jetbrains_ide.value)].icon display_name = try(lookup(local.jetbrains_ides, data.coder_parameter.jetbrains_ide.value).name, "JetBrains IDE")
icon = try(lookup(local.jetbrains_ides, data.coder_parameter.jetbrains_ide.value).icon, "/icon/gateway.svg")
external = true external = true
url = join("", [ url = join("", [
"jetbrains-gateway://connect#type=coder&workspace=", "jetbrains-gateway://connect#type=coder&workspace=",
@@ -133,14 +196,38 @@ resource "coder_app" "gateway" {
"&token=", "&token=",
"$SESSION_TOKEN", "$SESSION_TOKEN",
"&ide_product_code=", "&ide_product_code=",
jsondecode(data.coder_parameter.jetbrains_ide.value)[0], local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].identifier,
"&ide_build_number=", "&ide_build_number=",
jsondecode(data.coder_parameter.jetbrains_ide.value)[1], local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].build_number,
"&ide_download_link=", "&ide_download_link=",
jsondecode(data.coder_parameter.jetbrains_ide.value)[2], local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].download_link
]) ])
} }
output "jetbrains_ides" { output "identifier" {
value = data.coder_parameter.jetbrains_ide.value value = data.coder_parameter.jetbrains_ide.value
} }
output "name" {
value = coder_app.gateway.display_name
}
output "icon" {
value = coder_app.gateway.icon
}
output "download_link" {
value = lookup(local.jetbrains_ides, data.coder_parameter.jetbrains_ide.value).download_link
}
output "build_number" {
value = lookup(local.jetbrains_ides, data.coder_parameter.jetbrains_ide.value).build_number
}
output "version" {
value = var.jetbrains_ide_versions[data.coder_parameter.jetbrains_ide.value].version
}
output "url" {
value = coder_app.gateway.url
}

View File

@@ -10,18 +10,18 @@ tags: [integration, jfrog]
# JFrog # JFrog
Install the JF CLI and authenticate package managers with Artifactory using OAuth configured via the Coder `external-auth` feature. Install the JF CLI and authenticate package managers with Artifactory using OAuth configured via the Coder [`external-auth`](https://coder.com/docs/v2/latest/admin/external-auth) feature.
<p align="center"> ![JFrog OAuth](../.images/jfrog-oauth.png)
<img src='../.images/jfrog-oauth.png' alt="JFrog OAuth" width='600'>
</p>
```hcl ```tf
module "jfrog" { module "jfrog" {
source = "https://registry.coder.com/modules/jfrog-oauth" source = "registry.coder.com/modules/jfrog-oauth/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://jfrog.example.com" jfrog_url = "https://example.jfrog.io"
username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username" username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username"
package_managers = { package_managers = {
"npm" : "npm", "npm" : "npm",
"go" : "go", "go" : "go",
@@ -35,61 +35,20 @@ module "jfrog" {
## Prerequisites ## Prerequisites
Coder [`external-auth`](https://coder.com/docs/v2/latest/admin/external-auth) configured with Artifactory. This requires a [custom integration](https://jfrog.com/help/r/jfrog-installation-setup-documentation/enable-new-integrations) in Artifactory with **Callback URL** set to `https://<your-coder-url>/external-auth/jfrog/callback`. This module is usable by JFrog self-hosted (on-premises) Artifactory as it requires configuring a custom integration. This integration benefits from Coder's [external-auth](https://coder.com/docs/v2/latest/admin/external-auth) feature and allows each user to authenticate with Artifactory using an OAuth flow and issues user-scoped tokens to each user. For configuration instructions, see this [guide](https://coder.com/docs/v2/latest/guides/artifactory-integration#jfrog-oauth) on the Coder documentation.
To set this up,
1. Modify your `values.yaml` for JFrog Artifactory to add,
```yaml
artifactory:
enabled: true
frontend:
extraEnvironmentVariables:
- name: JF_FRONTEND_FEATURETOGGLER_ACCESSINTEGRATION
value: "true"
access:
accessConfig:
integrations-enabled: true
integration-templates:
- id: "1"
name: "CODER"
redirect-uri: "https://CODER_URL/external-auth/jfrog/callback"
scope: "applied-permissions/user"
```
> Note
> Replace `CODER_URL` with your Coder deployment URL, e.g., <coder.example.com>
2. Add a new [external authetication](https://coder.com/docs/v2/latest/admin/external-auth) to Coder by setting these env variables,
```env
# JFrog Artifactory External Auth
CODER_EXTERNAL_AUTH_1_ID="jfrog"
CODER_EXTERNAL_AUTH_1_TYPE="jfrog"
CODER_EXTERNAL_AUTH_1_CLIENT_ID="YYYYYYYYYYYYYYY"
CODER_EXTERNAL_AUTH_1_CLIENT_SECRET="XXXXXXXXXXXXXXXXXXX"
CODER_EXTERNAL_AUTH_1_DISPLAY_NAME="JFrog Artifactory"
CODER_EXTERNAL_AUTH_1_DISPLAY_ICON="/icon/jfrog.svg"
CODER_EXTERNAL_AUTH_1_AUTH_URL="https://JFROG_URL/ui/authorization"
CODER_EXTERNAL_AUTH_1_TOKEN_URL="https://JFROG_URL/access/api/v1/integrations/YYYYYYYYYYYYYYY/token"
CODER_EXTERNAL_AUTH_1_SCOPES="applied-permissions/user"
```
> Note
> Replace `JFROG_URL` with your JFrog Artifactory base URL, e.g., <artifactory.example.com>
## Examples ## Examples
Configure the Python pip package manager to fetch packages from Artifactory while mapping the Coder email to the Artifactory username. Configure the Python pip package manager to fetch packages from Artifactory while mapping the Coder email to the Artifactory username.
```hcl ```tf
module "jfrog" { module "jfrog" {
source = "https://registry.coder.com/modules/jfrog-oauth" source = "registry.coder.com/modules/jfrog-oauth/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://jfrog.example.com" jfrog_url = "https://example.jfrog.io"
auth_method = "oauth"
username_field = "email" username_field = "email"
package_managers = { package_managers = {
"pypi" : "pypi" "pypi" : "pypi"
} }
@@ -110,11 +69,12 @@ pip install requests
The [JFrog extension](https://open-vsx.org/extension/JFrog/jfrog-vscode-extension) for VS Code allows you to interact with Artifactory from within the IDE. The [JFrog extension](https://open-vsx.org/extension/JFrog/jfrog-vscode-extension) for VS Code allows you to interact with Artifactory from within the IDE.
```hcl ```tf
module "jfrog" { module "jfrog" {
source = "https://registry.coder.com/modules/jfrog-oauth" source = "registry.coder.com/modules/jfrog-oauth/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://jfrog.example.com" jfrog_url = "https://example.jfrog.io"
username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username" username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username"
configure_code_server = true # Add JFrog extension configuration for code-server configure_code_server = true # Add JFrog extension configuration for code-server
package_managers = { package_managers = {
@@ -129,14 +89,15 @@ module "jfrog" {
JFrog Access token is also available as a terraform output. You can use it in other terraform resources. For example, you can use it to configure an [Artifactory docker registry](https://jfrog.com/help/r/jfrog-artifactory-documentation/docker-registry) with the [docker terraform provider](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs). JFrog Access token is also available as a terraform output. You can use it in other terraform resources. For example, you can use it to configure an [Artifactory docker registry](https://jfrog.com/help/r/jfrog-artifactory-documentation/docker-registry) with the [docker terraform provider](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs).
```hcl ```tf
provider "docker" { provider "docker" {
... # ...
registry_auth { registry_auth {
address = "https://YYYY.jfrog.io/artifactory/api/docker/REPO-KEY" address = "https://example.jfrog.io/artifactory/api/docker/REPO-KEY"
username = module.jfrog.username username = module.jfrog.username
password = module.jfrog.access_token password = module.jfrog.access_token
} }
} }
``` ```
> Here `REPO_KEY` is the name of docker repository in Artifactory.

View File

@@ -19,6 +19,12 @@ variable "jfrog_url" {
} }
} }
variable "jfrog_server_id" {
type = string
description = "The server ID of the JFrog instance for JFrog CLI configuration"
default = "0"
}
variable "username_field" { variable "username_field" {
type = string type = string
description = "The field to use for the artifactory username. i.e. Coder username or email." description = "The field to use for the artifactory username. i.e. Coder username or email."
@@ -79,6 +85,7 @@ resource "coder_script" "jfrog" {
script = templatefile("${path.module}/run.sh", { script = templatefile("${path.module}/run.sh", {
JFROG_URL : var.jfrog_url, JFROG_URL : var.jfrog_url,
JFROG_HOST : local.jfrog_host, JFROG_HOST : local.jfrog_host,
JFROG_SERVER_ID : var.jfrog_server_id,
ARTIFACTORY_USERNAME : local.username, ARTIFACTORY_USERNAME : local.username,
ARTIFACTORY_EMAIL : data.coder_workspace.me.owner_email, ARTIFACTORY_EMAIL : data.coder_workspace.me.owner_email,
ARTIFACTORY_ACCESS_TOKEN : data.coder_external_auth.jfrog.access_token, ARTIFACTORY_ACCESS_TOKEN : data.coder_external_auth.jfrog.access_token,

View File

@@ -15,9 +15,9 @@ fi
# flows. # flows.
export CI=true export CI=true
# Authenticate JFrog CLI with Artifactory. # Authenticate JFrog CLI with Artifactory.
echo "${ARTIFACTORY_ACCESS_TOKEN}" | jf c add --access-token-stdin --url "${JFROG_URL}" --overwrite 0 echo "${ARTIFACTORY_ACCESS_TOKEN}" | jf c add --access-token-stdin --url "${JFROG_URL}" --overwrite "${JFROG_SERVER_ID}"
# Set the configured server as the default. # Set the configured server as the default.
jf c use 0 jf c use "${JFROG_SERVER_ID}"
# Configure npm to use the Artifactory "npm" repository. # Configure npm to use the Artifactory "npm" repository.
if [ -z "${REPOSITORY_NPM}" ]; then if [ -z "${REPOSITORY_NPM}" ]; then

View File

@@ -12,9 +12,10 @@ tags: [integration, jfrog]
Install the JF CLI and authenticate package managers with Artifactory using Artifactory terraform provider. Install the JF CLI and authenticate package managers with Artifactory using Artifactory terraform provider.
```hcl ```tf
module "jfrog" { module "jfrog" {
source = "https://registry.coder.com/modules/jfrog-token" source = "registry.coder.com/modules/jfrog-token/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://XXXX.jfrog.io" jfrog_url = "https://XXXX.jfrog.io"
artifactory_access_token = var.artifactory_access_token artifactory_access_token = var.artifactory_access_token
@@ -26,14 +27,7 @@ module "jfrog" {
} }
``` ```
Get a JFrog access token from your Artifactory instance. The token must be an [admin token](https://registry.terraform.io/providers/jfrog/artifactory/latest/docs#access-token). It is recommended to store the token in a secret terraform variable. For detailed instructions, please see this [guide](https://coder.com/docs/v2/latest/guides/artifactory-integration#jfrog-token) on the Coder documentation.
```hcl
variable "artifactory_access_token" {
type = string
sensitive = true
}
```
> Note > Note
> This module does not install `npm`, `go`, `pip`, etc but only configure them. You need to handle the installation of these tools yourself. > This module does not install `npm`, `go`, `pip`, etc but only configure them. You need to handle the installation of these tools yourself.
@@ -44,9 +38,10 @@ variable "artifactory_access_token" {
### Configure npm, go, and pypi to use Artifactory local repositories ### Configure npm, go, and pypi to use Artifactory local repositories
```hcl ```tf
module "jfrog" { module "jfrog" {
source = "https://registry.coder.com/modules/jfrog-token" source = "registry.coder.com/modules/jfrog-token/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://YYYY.jfrog.io" jfrog_url = "https://YYYY.jfrog.io"
artifactory_access_token = var.artifactory_access_token # An admin access token artifactory_access_token = var.artifactory_access_token # An admin access token
@@ -76,9 +71,10 @@ pip install requests
The [JFrog extension](https://open-vsx.org/extension/JFrog/jfrog-vscode-extension) for VS Code allows you to interact with Artifactory from within the IDE. The [JFrog extension](https://open-vsx.org/extension/JFrog/jfrog-vscode-extension) for VS Code allows you to interact with Artifactory from within the IDE.
```hcl ```tf
module "jfrog" { module "jfrog" {
source = "https://registry.coder.com/modules/jfrog-token" source = "registry.coder.com/modules/jfrog-token/coder"
version = "1.0.5"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
jfrog_url = "https://XXXX.jfrog.io" jfrog_url = "https://XXXX.jfrog.io"
artifactory_access_token = var.artifactory_access_token artifactory_access_token = var.artifactory_access_token
@@ -95,10 +91,10 @@ module "jfrog" {
JFrog Access token is also available as a terraform output. You can use it in other terraform resources. For example, you can use it to configure an [Artifactory docker registry](https://jfrog.com/help/r/jfrog-artifactory-documentation/docker-registry) with the [docker terraform provider](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs). JFrog Access token is also available as a terraform output. You can use it in other terraform resources. For example, you can use it to configure an [Artifactory docker registry](https://jfrog.com/help/r/jfrog-artifactory-documentation/docker-registry) with the [docker terraform provider](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs).
```hcl ```tf
provider "docker" { provider "docker" {
... # ...
registry_auth { registry_auth {
address = "https://YYYY.jfrog.io/artifactory/api/docker/REPO-KEY" address = "https://YYYY.jfrog.io/artifactory/api/docker/REPO-KEY"
username = module.jfrog.username username = module.jfrog.username
@@ -106,3 +102,5 @@ provider "docker" {
} }
} }
``` ```
> Here `REPO_KEY` is the name of docker repository in Artifactory.

View File

@@ -23,6 +23,12 @@ variable "jfrog_url" {
} }
} }
variable "jfrog_server_id" {
type = string
description = "The server ID of the JFrog instance for JFrog CLI configuration"
default = "0"
}
variable "artifactory_access_token" { variable "artifactory_access_token" {
type = string type = string
description = "The admin-level access token to use for JFrog." description = "The admin-level access token to use for JFrog."
@@ -112,6 +118,7 @@ resource "coder_script" "jfrog" {
script = templatefile("${path.module}/run.sh", { script = templatefile("${path.module}/run.sh", {
JFROG_URL : var.jfrog_url, JFROG_URL : var.jfrog_url,
JFROG_HOST : local.jfrog_host, JFROG_HOST : local.jfrog_host,
JFROG_SERVER_ID : var.jfrog_server_id,
ARTIFACTORY_USERNAME : local.username, ARTIFACTORY_USERNAME : local.username,
ARTIFACTORY_EMAIL : data.coder_workspace.me.owner_email, ARTIFACTORY_EMAIL : data.coder_workspace.me.owner_email,
ARTIFACTORY_ACCESS_TOKEN : artifactory_scoped_token.me.access_token, ARTIFACTORY_ACCESS_TOKEN : artifactory_scoped_token.me.access_token,

View File

@@ -15,9 +15,9 @@ fi
# flows. # flows.
export CI=true export CI=true
# Authenticate JFrog CLI with Artifactory. # Authenticate JFrog CLI with Artifactory.
echo "${ARTIFACTORY_ACCESS_TOKEN}" | jf c add --access-token-stdin --url "${JFROG_URL}" --overwrite 0 echo "${ARTIFACTORY_ACCESS_TOKEN}" | jf c add --access-token-stdin --url "${JFROG_URL}" --overwrite "${JFROG_SERVER_ID}"
# Set the configured server as the default. # Set the configured server as the default.
jf c use 0 jf c use "${JFROG_SERVER_ID}"
# Configure npm to use the Artifactory "npm" repository. # Configure npm to use the Artifactory "npm" repository.
if [ -z "${REPOSITORY_NPM}" ]; then if [ -z "${REPOSITORY_NPM}" ]; then

View File

@@ -13,9 +13,10 @@ A module that adds Jupyter Notebook in your Coder template.
![Jupyter Notebook](../.images/jupyter-notebook.png) ![Jupyter Notebook](../.images/jupyter-notebook.png)
```hcl ```tf
module "jupyter-notebook" { module "jupyter-notebook" {
source = "https://registry.coder.com/modules/jupyter-notebook" source = "registry.coder.com/modules/jupyter-notebook/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```

View File

@@ -13,9 +13,10 @@ A module that adds JupyterLab in your Coder template.
![JupyterLab](../.images/jupyterlab.png) ![JupyterLab](../.images/jupyterlab.png)
```hcl ```tf
module "jupyterlab" { module "jupyterlab" {
source = "https://registry.coder.com/modules/jupyterlab" source = "registry.coder.com/modules/jupyterlab/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```

43
lint.ts
View File

@@ -15,7 +15,37 @@ let badExit = false;
const error = (...data: any[]) => { const error = (...data: any[]) => {
console.error(...data); console.error(...data);
badExit = true; badExit = true;
};
const verifyCodeBlocks = (
tokens: marked.Token[],
res = {
codeIsTF: false,
codeIsHCL: false,
} }
) => {
for (const token of tokens) {
// Check in-depth.
if (token.type === "list") {
verifyCodeBlocks(token.items, res);
continue;
}
if (token.type === "list_item") {
verifyCodeBlocks(token.tokens, res);
continue;
}
if (token.type === "code") {
if (token.lang === "tf") {
res.codeIsTF = true;
}
if (token.lang === "hcl") {
res.codeIsHCL = true;
}
}
}
return res;
};
// Ensures that each README has the proper format. // Ensures that each README has the proper format.
// Exits with 0 if all is good! // Exits with 0 if all is good!
@@ -62,6 +92,7 @@ for (const dir of dirs) {
let h1 = false; let h1 = false;
let code = false; let code = false;
let paragraph = false; let paragraph = false;
let version = true;
for (const token of tokens) { for (const token of tokens) {
if (token.type === "heading" && token.depth === 1) { if (token.type === "heading" && token.depth === 1) {
@@ -77,6 +108,10 @@ for (const dir of dirs) {
} }
if (token.type === "code") { if (token.type === "code") {
code = true; code = true;
if (token.lang === "tf" && !token.text.includes("version")) {
version = false;
error(dir.name, "missing version in tf code block");
}
continue; continue;
} }
} }
@@ -89,6 +124,14 @@ for (const dir of dirs) {
if (!code) { if (!code) {
error(dir.name, "missing example code block after paragraph"); error(dir.name, "missing example code block after paragraph");
} }
const { codeIsTF, codeIsHCL } = verifyCodeBlocks(tokens);
if (!codeIsTF) {
error(dir.name, "missing example tf code block");
}
if (codeIsHCL) {
error(dir.name, "hcl code block should be tf");
}
} }
if (badExit) { if (badExit) {

4
new.sh
View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# This scripts creates a new sample moduledir with requried files # This scripts creates a new sample moduledir with required files
# Run it like : ./new.sh my-module # Run it like : ./new.sh my-module
MODULE_NAME=$1 MODULE_NAME=$1
@@ -11,7 +11,7 @@ if [ -z "$MODULE_NAME" ]; then
exit 1 exit 1
fi fi
# Create module directory and exit if it alredy exists # Create module directory and exit if it already exists
if [ -d "$MODULE_NAME" ]; then if [ -d "$MODULE_NAME" ]; then
echo "Module with name $MODULE_NAME already exists" echo "Module with name $MODULE_NAME already exists"
echo "Please choose a different name" echo "Please choose a different name"

View File

@@ -2,17 +2,25 @@
"name": "modules", "name": "modules",
"scripts": { "scripts": {
"test": "bun test", "test": "bun test",
"fmt": "bun x prettier --plugin prettier-plugin-sh -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 **/*.ts **/*.md *.md && terraform fmt **/*.tf .sample/main.tf",
"fmt:ci": "bun x prettier --plugin prettier-plugin-sh --check **/*.sh .sample/run.sh new.sh **/*.ts **/*.md *.md && terraform fmt -check **/*.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" "lint": "bun run lint.ts && ./terraform_validate.sh",
"update-version": "./update-version.sh"
}, },
"devDependencies": { "devDependencies": {
"bun-types": "^1.0.18", "bun-types": "^1.0.18",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"marked": "^11.1.0", "marked": "^12.0.0",
"prettier-plugin-sh": "^0.13.1" "prettier-plugin-sh": "^0.13.1",
"prettier-plugin-terraform-formatter": "^1.2.1"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5.3.3" "typescript": "^5.3.3"
},
"prettier": {
"plugins": [
"prettier-plugin-sh",
"prettier-plugin-terraform-formatter"
]
} }
} }

View File

@@ -11,9 +11,10 @@ tags: [helper]
Run a script on workspace start that allows developers to run custom commands to personalize their workspace. Run a script on workspace start that allows developers to run custom commands to personalize their workspace.
```hcl ```tf
module "personalize" { module "personalize" {
source = "https://registry.coder.com/modules/personalize" source = "registry.coder.com/modules/personalize/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```

View File

@@ -12,7 +12,7 @@ tags: [helper]
Add the `slackme` command to your workspace that DMs you on Slack when your command finishes running. Add the `slackme` command to your workspace that DMs you on Slack when your command finishes running.
```bash ```bash
$ slackme npm run long-build slackme npm run long-build
``` ```
## Setup ## Setup
@@ -54,9 +54,10 @@ $ slackme npm run long-build
3. Restart your Coder deployment. Any Template can now import the Slack Me module, and `slackme` will be available on the `$PATH`: 3. Restart your Coder deployment. Any Template can now import the Slack Me module, and `slackme` will be available on the `$PATH`:
```hcl ```tf
module "slackme" { module "slackme" {
source = "https://registry.coder.com/modules/slackme" source = "registry.coder.com/modules/slackme/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
auth_provider_id = "slack" auth_provider_id = "slack"
} }
@@ -69,9 +70,10 @@ $ slackme npm run long-build
- `$COMMAND` is replaced with the command the user executed. - `$COMMAND` is replaced with the command the user executed.
- `$DURATION` is replaced with a human-readable duration the command took to execute. - `$DURATION` is replaced with a human-readable duration the command took to execute.
```hcl ```tf
module "slackme" { module "slackme" {
source = "https://registry.coder.com/modules/slackme" source = "registry.coder.com/modules/slackme/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
auth_provider_id = "slack" auth_provider_id = "slack"
slack_message = <<EOF slack_message = <<EOF

29
terraform_validate.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
set -euo pipefail
# Function to run terraform init and validate in a directory
run_terraform() {
local dir="$1"
echo "Running terraform init and validate in $dir"
pushd "$dir"
terraform init -upgrade
terraform validate
popd
}
# Main script
main() {
# Get the directory of the script
script_dir=$(dirname "$(readlink -f "$0")")
# Get all subdirectories in the repository
subdirs=$(find "$script_dir" -mindepth 1 -maxdepth 1 -type d -not -name ".*" | sort)
for dir in $subdirs; do
run_terraform "$dir"
done
}
# Run the main script
main

29
update-version.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# This script updates the version number in the README.md files of all modules
# to the latest tag in the repository. It is intended to be run from the root
# of the repository or by using the `bun update-version` command.
set -euo pipefail
current_tag=$(git describe --tags --abbrev=0)
previous_tag=$(git describe --tags --abbrev=0 $current_tag^)
mapfile -t changed_dirs < <(git diff --name-only "$previous_tag"..."$current_tag" -- ':!**/README.md' ':!**/*.test.ts' | xargs dirname | grep -v '^\.' | sort -u)
LATEST_TAG=$(git describe --abbrev=0 --tags | sed 's/^v//') || exit $?
for dir in "${changed_dirs[@]}"; do
if [[ -f "$dir/README.md" ]]; then
echo "Bumping version in $dir/README.md"
file="$dir/README.md"
tmpfile=$(mktemp /tmp/tempfile.XXXXXX)
awk -v tag="$LATEST_TAG" '{
if ($1 == "version" && $2 == "=") {
sub(/"[^"]*"/, "\"" tag "\"")
print
} else {
print
}
}' "$file" > "$tmpfile" && mv "$tmpfile" "$file"
fi
done

View File

@@ -3,6 +3,7 @@ display_name: Hashicorp Vault Integration (GitHub)
description: Authenticates with Vault using GitHub description: Authenticates with Vault using GitHub
icon: ../.icons/vault.svg icon: ../.icons/vault.svg
maintainer_github: coder maintainer_github: coder
partner_github: hashicorp
verified: true verified: true
tags: [helper, integration, vault, github] tags: [helper, integration, vault, github]
--- ---
@@ -11,9 +12,10 @@ tags: [helper, integration, vault, github]
This module lets you authenticate with [Hashicorp Vault](https://www.vaultproject.io/) in your Coder workspaces using [external auth](https://coder.com/docs/v2/latest/admin/external-auth) for GitHub. This module lets you authenticate with [Hashicorp Vault](https://www.vaultproject.io/) in your Coder workspaces using [external auth](https://coder.com/docs/v2/latest/admin/external-auth) for GitHub.
```hcl ```tf
module "vault" { module "vault" {
source = "https://registry.coder.com/modules/vault-github" source = "registry.coder.com/modules/vault-github/coder"
version = "1.0.7"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
vault_addr = "https://vault.example.com" vault_addr = "https://vault.example.com"
} }
@@ -22,13 +24,13 @@ module "vault" {
Then you can use the Vault CLI in your workspaces to fetch secrets from Vault: Then you can use the Vault CLI in your workspaces to fetch secrets from Vault:
```shell ```shell
vault kv get -mount=secret my-secret vault kv get -mount=coder my-secret
``` ```
or using the Vault API: or using the Vault API:
```shell ```shell
curl -H "X-Vault-Token: ${VAULT_TOKEN}" -X GET "${VAULT_ADDR}/v1/secret/data/my-secret" curl -H "X-Vault-Token: ${VAULT_TOKEN}" -X GET "${VAULT_ADDR}/v1/coder/data/my-secret"
``` ```
![Vault login](../.images/vault-login.png) ![Vault login](../.images/vault-login.png)
@@ -41,9 +43,10 @@ To configure the Vault module, you must set up a Vault GitHub auth method. See t
### Configure Vault integration with a different Coder GitHub external auth ID (i.e., not the default `github`) ### Configure Vault integration with a different Coder GitHub external auth ID (i.e., not the default `github`)
```hcl ```tf
module "vault" { module "vault" {
source = "https://registry.coder.com/modules/vault" source = "registry.coder.com/modules/vault-github/coder"
version = "1.0.7"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
vault_addr = "https://vault.example.com" vault_addr = "https://vault.example.com"
coder_github_auth_id = "my-github-auth-id" coder_github_auth_id = "my-github-auth-id"
@@ -52,9 +55,10 @@ module "vault" {
### Configure Vault integration with a different Coder GitHub external auth ID and a different Vault GitHub auth path ### Configure Vault integration with a different Coder GitHub external auth ID and a different Vault GitHub auth path
```hcl ```tf
module "vault" { module "vault" {
source = "https://registry.coder.com/modules/vault" source = "registry.coder.com/modules/vault-github/coder"
version = "1.0.7"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
vault_addr = "https://vault.example.com" vault_addr = "https://vault.example.com"
coder_github_auth_id = "my-github-auth-id" coder_github_auth_id = "my-github-auth-id"
@@ -64,9 +68,10 @@ module "vault" {
### Configure Vault integration and install a specific version of the Vault CLI ### Configure Vault integration and install a specific version of the Vault CLI
```hcl ```tf
module "vault" { module "vault" {
source = "https://registry.coder.com/modules/vault" source = "registry.coder.com/modules/vault-github/coder"
version = "1.0.7"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
vault_addr = "https://vault.example.com" vault_addr = "https://vault.example.com"
vault_cli_version = "1.15.0" vault_cli_version = "1.15.0"

11
vault-github/main.test.ts Normal file
View File

@@ -0,0 +1,11 @@
import { describe } from "bun:test";
import { runTerraformInit, testRequiredVariables } from "../test";
describe("vault-github", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "foo",
vault_addr: "foo",
});
});

View File

@@ -49,7 +49,6 @@ resource "coder_script" "vault" {
display_name = "Vault (GitHub)" display_name = "Vault (GitHub)"
icon = "/icon/vault.svg" icon = "/icon/vault.svg"
script = templatefile("${path.module}/run.sh", { script = templatefile("${path.module}/run.sh", {
VAULT_ADDR : var.vault_addr,
AUTH_PATH : var.vault_github_auth_path, AUTH_PATH : var.vault_github_auth_path,
GITHUB_EXTERNAL_AUTH_ID : data.coder_external_auth.github.id, GITHUB_EXTERNAL_AUTH_ID : data.coder_external_auth.github.id,
INSTALL_VERSION : var.vault_cli_version, INSTALL_VERSION : var.vault_cli_version,

View File

@@ -1,8 +1,7 @@
#!/usr/bin/env sh #!/usr/bin/env bash
# Convert all templated variables to shell variables # Convert all templated variables to shell variables
INSTALL_VERSION=${INSTALL_VERSION} INSTALL_VERSION=${INSTALL_VERSION}
VAULT_ADDR=${VAULT_ADDR}
GITHUB_EXTERNAL_AUTH_ID=${GITHUB_EXTERNAL_AUTH_ID} GITHUB_EXTERNAL_AUTH_ID=${GITHUB_EXTERNAL_AUTH_ID}
AUTH_PATH=${AUTH_PATH} AUTH_PATH=${AUTH_PATH}
@@ -21,7 +20,7 @@ fetch() {
fi fi
} }
unzip() { unzip_safe() {
if command -v unzip > /dev/null 2>&1; then if command -v unzip > /dev/null 2>&1; then
command unzip "$@" command unzip "$@"
elif command -v busybox > /dev/null 2>&1; then elif command -v busybox > /dev/null 2>&1; then
@@ -32,15 +31,26 @@ unzip() {
fi fi
} }
install() {
# Get the architecture of the system
ARCH=$(uname -m)
if [ "$${ARCH}" = "x86_64" ]; then
ARCH="amd64"
elif [ "$${ARCH}" = "aarch64" ]; then
ARCH="arm64"
else
printf "Unsupported architecture: $${ARCH}\n"
return 1
fi
# Fetch the latest version of Vault if INSTALL_VERSION is 'latest' # Fetch the latest version of Vault if INSTALL_VERSION is 'latest'
if [ "$${INSTALL_VERSION}" = "latest" ]; then if [ "$${INSTALL_VERSION}" = "latest" ]; then
LATEST_VERSION=$(curl -s https://releases.hashicorp.com/vault/ | grep -oP 'vault/\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -n 1) LATEST_VERSION=$(curl -s https://releases.hashicorp.com/vault/ | grep -v 'rc' | grep -oE 'vault/[0-9]+\.[0-9]+\.[0-9]+' | sed 's/vault\///' | sort -V | tail -n 1)
printf "Latest version of Vault is %s.\n\n" "$${LATEST_VERSION}" printf "Latest version of Vault is %s.\n\n" "$${LATEST_VERSION}"
if [ -z "$${LATEST_VERSION}" ]; then if [ -z "$${LATEST_VERSION}" ]; then
printf "Failed to determine the latest Vault version.\n" printf "Failed to determine the latest Vault version.\n"
exit 1 return 1
fi fi
VERSION=$${LATEST_VERSION} INSTALL_VERSION=$${LATEST_VERSION}
fi fi
# Check if the vault CLI is installed and has the correct version # Check if the vault CLI is installed and has the correct version
@@ -58,31 +68,41 @@ if [ $${installation_needed} -eq 1 ]; then
if [ -z "$${CURRENT_VERSION}" ]; then if [ -z "$${CURRENT_VERSION}" ]; then
printf "Installing Vault CLI ...\n\n" printf "Installing Vault CLI ...\n\n"
else else
printf "Upgrading Vault CLI from version %s to %s ...\n\n" "$${CURRENT_VERSION}" "$${VERSION}" printf "Upgrading Vault CLI from version %s to %s ...\n\n" "$${CURRENT_VERSION}" "${INSTALL_VERSION}"
fi fi
fetch vault.zip "https://releases.hashicorp.com/vault/$${VERSION}/vault_$${VERSION}_linux_amd64.zip" fetch vault.zip "https://releases.hashicorp.com/vault/$${INSTALL_VERSION}/vault_$${INSTALL_VERSION}_linux_$${ARCH}.zip"
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
printf "Failed to download Vault.\n" printf "Failed to download Vault.\n"
exit 1 return 1
fi fi
unzip vault.zip if ! unzip_safe vault.zip; then
if [ $? -ne 0 ]; then
printf "Failed to unzip Vault.\n" printf "Failed to unzip Vault.\n"
exit 1 return 1
fi fi
rm vault.zip rm vault.zip
if sudo mv vault /usr/local/bin/vault 2> /dev/null; then if sudo mv vault /usr/local/bin/vault 2> /dev/null; then
printf "Vault installed successfully!\n\n" printf "Vault installed successfully!\n\n"
else else
mkdir -p ~/.local/bin mkdir -p ~/.local/bin
mv vault ~/.local/bin/vault if ! mv vault ~/.local/bin/vault; then
if [ ! -f ~/.local/bin/vault ]; then
printf "Failed to move Vault to local bin.\n" printf "Failed to move Vault to local bin.\n"
exit 1 return 1
fi fi
printf "Please add ~/.local/bin to your PATH to use vault CLI.\n" printf "Please add ~/.local/bin to your PATH to use vault CLI.\n"
fi fi
fi fi
return 0
}
TMP=$(mktemp -d)
if ! (
cd "$TMP"
install
); then
echo "Failed to install Vault CLI."
exit 1
fi
rm -rf "$TMP"
# Authenticate with Vault # Authenticate with Vault
printf "🔑 Authenticating with Vault ...\n\n" printf "🔑 Authenticating with Vault ...\n\n"
@@ -92,8 +112,6 @@ if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi
export VAULT_ADDR="$${VAULT_ADDR}"
# Login to vault using the GitHub token # Login to vault using the GitHub token
printf "🔑 Logging in to Vault ...\n\n" printf "🔑 Logging in to Vault ...\n\n"
vault login -no-print -method=github -path=/$${AUTH_PATH} token="$${GITHUB_TOKEN}" vault login -no-print -method=github -path=/$${AUTH_PATH} token="$${GITHUB_TOKEN}"

83
vault-token/README.md Normal file
View File

@@ -0,0 +1,83 @@
---
display_name: Hashicorp Vault Integration (Token)
description: Authenticates with Vault using Token
icon: ../.icons/vault.svg
maintainer_github: coder
partner_github: hashicorp
verified: true
tags: [helper, integration, vault, token]
---
# Hashicorp Vault Integration (Token)
This module lets you authenticate with [Hashicorp Vault](https://www.vaultproject.io/) in your Coder workspaces using a [Vault token](https://developer.hashicorp.com/vault/docs/auth/token).
```tf
variable "vault_token" {
type = string
description = "The Vault token to use for authentication."
sensitive = true
}
module "vault" {
source = "registry.coder.com/modules/vault-token/coder"
version = "1.0.7"
agent_id = coder_agent.example.id
vault_token = var.token
vault_addr = "https://vault.example.com"
}
```
Then you can use the Vault CLI in your workspaces to fetch secrets from Vault:
```shell
vault kv get -namespace=coder -mount=secrets coder
```
or using the Vault API:
```shell
curl -H "X-Vault-Token: ${VAULT_TOKEN}" -X GET "${VAULT_ADDR}/v1/coder/secrets/data/coder"
```
## Configuration
To configure the Vault module, you must create a Vault token with the the required permissions and configure the module with the token and Vault address.
1. Create a vault policy with read access to the secret mount you need your developers to access.
```shell
vault policy write read-coder-secrets - <<EOF
path "coder/data/*" {
capabilities = ["read"]
}
path "coder/metadata/*" {
capabilities = ["read"]
}
EOF
```
2. Create a token using this policy.
```shell
vault token create -policy="read-coder-secrets"
```
3. Copy the generated token and use in your template.
## Examples
### Configure Vault integration and install a specific version of the Vault CLI
```tf
variable "vault_token" {
type = string
description = "The Vault token to use for authentication."
sensitive = true
}
module "vault" {
source = "registry.coder.com/modules/vault-token/coder"
version = "1.0.7"
agent_id = coder_agent.example.id
vault_addr = "https://vault.example.com"
vault_token = var.token
vault_cli_version = "1.15.0"
}
```

12
vault-token/main.test.ts Normal file
View File

@@ -0,0 +1,12 @@
import { describe } from "bun:test";
import { runTerraformInit, testRequiredVariables } from "../test";
describe("vault-token", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "foo",
vault_addr: "foo",
vault_token: "foo",
});
});

62
vault-token/main.tf Normal file
View File

@@ -0,0 +1,62 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 0.12.4"
}
}
}
# Add required variables for your modules and remove any unneeded variables
variable "agent_id" {
type = string
description = "The ID of a Coder agent."
}
variable "vault_addr" {
type = string
description = "The address of the Vault server."
}
variable "vault_token" {
type = string
description = "The Vault token to use for authentication."
sensitive = true
}
variable "vault_cli_version" {
type = string
description = "The version of Vault to install."
default = "latest"
validation {
condition = can(regex("^(latest|[0-9]+\\.[0-9]+\\.[0-9]+)$", var.vault_cli_version))
error_message = "Vault version must be in the format 0.0.0 or latest"
}
}
data "coder_workspace" "me" {}
resource "coder_script" "vault" {
agent_id = var.agent_id
display_name = "Vault (Token)"
icon = "/icon/vault.svg"
script = templatefile("${path.module}/run.sh", {
INSTALL_VERSION : var.vault_cli_version,
})
run_on_start = true
start_blocks_login = true
}
resource "coder_env" "vault_addr" {
agent_id = var.agent_id
name = "VAULT_ADDR"
value = var.vault_addr
}
resource "coder_env" "vault_token" {
agent_id = var.agent_id
name = "VAULT_TOKEN"
value = var.vault_token
}

103
vault-token/run.sh Normal file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
# Convert all templated variables to shell variables
INSTALL_VERSION=${INSTALL_VERSION}
fetch() {
dest="$1"
url="$2"
if command -v curl > /dev/null 2>&1; then
curl -sSL --fail "$${url}" -o "$${dest}"
elif command -v wget > /dev/null 2>&1; then
wget -O "$${dest}" "$${url}"
elif command -v busybox > /dev/null 2>&1; then
busybox wget -O "$${dest}" "$${url}"
else
printf "curl, wget, or busybox is not installed. Please install curl or wget in your image.\n"
return 1
fi
}
unzip_safe() {
if command -v unzip > /dev/null 2>&1; then
command unzip "$@"
elif command -v busybox > /dev/null 2>&1; then
busybox unzip "$@"
else
printf "unzip or busybox is not installed. Please install unzip in your image.\n"
return 1
fi
}
install() {
# Get the architecture of the system
ARCH=$(uname -m)
if [ "$${ARCH}" = "x86_64" ]; then
ARCH="amd64"
elif [ "$${ARCH}" = "aarch64" ]; then
ARCH="arm64"
else
printf "Unsupported architecture: $${ARCH}\n"
return 1
fi
# Fetch the latest version of Vault if INSTALL_VERSION is 'latest'
if [ "$${INSTALL_VERSION}" = "latest" ]; then
LATEST_VERSION=$(curl -s https://releases.hashicorp.com/vault/ | grep -v 'rc' | grep -oE 'vault/[0-9]+\.[0-9]+\.[0-9]+' | sed 's/vault\///' | sort -V | tail -n 1)
printf "Latest version of Vault is %s.\n\n" "$${LATEST_VERSION}"
if [ -z "$${LATEST_VERSION}" ]; then
printf "Failed to determine the latest Vault version.\n"
return 1
fi
INSTALL_VERSION=$${LATEST_VERSION}
fi
# Check if the vault CLI is installed and has the correct version
installation_needed=1
if command -v vault > /dev/null 2>&1; then
CURRENT_VERSION=$(vault version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
if [ "$${CURRENT_VERSION}" = "$${INSTALL_VERSION}" ]; then
printf "Vault version %s is already installed and up-to-date.\n\n" "$${CURRENT_VERSION}"
installation_needed=0
fi
fi
if [ $${installation_needed} -eq 1 ]; then
# Download and install Vault
if [ -z "$${CURRENT_VERSION}" ]; then
printf "Installing Vault CLI ...\n\n"
else
printf "Upgrading Vault CLI from version %s to %s ...\n\n" "$${CURRENT_VERSION}" "${INSTALL_VERSION}"
fi
fetch vault.zip "https://releases.hashicorp.com/vault/$${INSTALL_VERSION}/vault_$${INSTALL_VERSION}_linux_amd64.zip"
if [ $? -ne 0 ]; then
printf "Failed to download Vault.\n"
return 1
fi
if ! unzip_safe vault.zip; then
printf "Failed to unzip Vault.\n"
return 1
fi
rm vault.zip
if sudo mv vault /usr/local/bin/vault 2> /dev/null; then
printf "Vault installed successfully!\n\n"
else
mkdir -p ~/.local/bin
if ! mv vault ~/.local/bin/vault; then
printf "Failed to move Vault to local bin.\n"
return 1
fi
printf "Please add ~/.local/bin to your PATH to use vault CLI.\n"
fi
fi
return 0
}
TMP=$(mktemp -d)
if ! (
cd "$TMP"
install
); then
echo "Failed to install Vault CLI."
exit 1
fi
rm -rf "$TMP"

View File

@@ -13,9 +13,10 @@ Add a button to open any workspace with a single click.
Uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder). Uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder).
```hcl ```tf
module "vscode" { module "vscode" {
source = "https://registry.coder.com/modules/vscode-desktop" source = "registry.coder.com/modules/vscode-desktop/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
} }
``` ```
@@ -24,9 +25,10 @@ module "vscode" {
### Open in a specific directory ### Open in a specific directory
```hcl ```tf
module "vscode" { module "vscode" {
source = "https://registry.coder.com/modules/vscode-desktop" source = "registry.coder.com/modules/vscode-desktop/coder"
version = "1.0.2"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
folder = "/home/coder/project" folder = "/home/coder/project"
} }

View File

@@ -9,11 +9,12 @@ tags: [helper, ide, vscode, web]
# VS Code Web # VS Code Web
Automatically install [Visual Studio Code Server](https://code.visualstudio.com/docs/remote/vscode-server) in a workspace using the [VS Code CLI](https://code.visualstudio.com/docs/editor/command-line) and create an app to access it via the dashboard. Automatically install [Visual Studio Code Server](https://code.visualstudio.com/docs/remote/vscode-server) in a workspace and create an app to access it via the dashboard.
```hcl ```tf
module "vscode-web" { module "vscode-web" {
source = "https://registry.coder.com/modules/vscode-web" source = "registry.coder.com/modules/vscode-web/coder"
version = "1.0.6"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
accept_license = true accept_license = true
} }
@@ -25,12 +26,25 @@ module "vscode-web" {
### Install VS Code Web to a custom folder ### Install VS Code Web to a custom folder
```hcl ```tf
module "vscode-web" { module "vscode-web" {
source = "https://registry.coder.com/modules/vscode-web" source = "registry.coder.com/modules/vscode-web/coder"
version = "1.0.6"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
install_dir = "/home/coder/.vscode-web" install_prefix = "/home/coder/.vscode-web"
folder = "/home/coder" folder = "/home/coder"
accept_license = true accept_license = true
} }
``` ```
### Install Extensions
```tf
module "vscode-web" {
source = "registry.coder.com/modules/vscode-web/coder"
version = "1.0.6"
agent_id = coder_agent.example.id
extensions = ["github.copilot", "ms-python.python", "ms-toolsai.jupyter"]
accept_license = true
}
```

View File

@@ -1,63 +0,0 @@
import { describe, expect, it } from "bun:test";
import {
executeScriptInContainer,
runTerraformApply,
runTerraformInit,
} from "../test";
describe("vscode-web", async () => {
await runTerraformInit(import.meta.dir);
// replaces testRequiredVariables due to license variable
// may add a testRequiredVariablesWithLicense function later
it("missing agent_id", async () => {
try {
await runTerraformApply(import.meta.dir, {
accept_license: "true",
});
} catch (ex) {
expect(ex.message).toContain('input variable "agent_id" is not set');
}
});
it("invalid license_agreement", async () => {
try {
await runTerraformApply(import.meta.dir, {
agent_id: "foo",
});
} catch (ex) {
expect(ex.message).toContain(
"You must accept the VS Code license agreement by setting accept_license=true",
);
}
});
it("fails without curl", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
accept_license: "true",
});
const output = await executeScriptInContainer(state, "alpine");
expect(output.exitCode).toBe(1);
expect(output.stdout).toEqual([
"\u001b[0;1mInstalling vscode-cli!",
"Failed to install vscode-cli:", // TODO: manually test error log
]);
});
it("runs with curl", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
accept_license: "true",
});
const output = await executeScriptInContainer(state, "alpine/curl");
expect(output.exitCode).toBe(0);
expect(output.stdout).toEqual([
"\u001b[0;1mInstalling vscode-cli!",
"🥳 vscode-cli has been installed.",
"",
"👷 Running /tmp/vscode-cli/bin/code serve-web --port 13338 --without-connection-token --accept-server-license-terms in the background...",
"Check logs at /tmp/vscode-web.log!",
]);
});
});

View File

@@ -20,6 +20,18 @@ variable "port" {
default = 13338 default = 13338
} }
variable "display_name" {
type = string
description = "The display name for the VS Code Web application."
default = "VS Code Web"
}
variable "slug" {
type = string
description = "The slug for the VS Code Web application."
default = "vscode-web"
}
variable "folder" { variable "folder" {
type = string type = string
description = "The folder to open in vscode-web." description = "The folder to open in vscode-web."
@@ -41,15 +53,21 @@ variable "log_path" {
default = "/tmp/vscode-web.log" default = "/tmp/vscode-web.log"
} }
variable "install_dir" { variable "install_prefix" {
type = string type = string
description = "The directory to install VS Code CLI" description = "The prefix to install vscode-web to."
default = "/tmp/vscode-cli" default = "/tmp/vscode-web"
}
variable "extensions" {
type = list(string)
description = "A list of extensions to install."
default = []
} }
variable "accept_license" { variable "accept_license" {
type = bool type = bool
description = "Accept the VS Code license. https://code.visualstudio.com/license" description = "Accept the VS Code Server license. https://code.visualstudio.com/license/server"
default = false default = false
validation { validation {
condition = var.accept_license == true condition = var.accept_license == true
@@ -57,6 +75,16 @@ variable "accept_license" {
} }
} }
variable "telemetry_level" {
type = string
description = "Set the telemetry level for VS Code Web."
default = "error"
validation {
condition = var.telemetry_level == "off" || var.telemetry_level == "crash" || var.telemetry_level == "error" || var.telemetry_level == "all"
error_message = "Incorrect value. Please set either 'off', 'crash', 'error', or 'all'."
}
}
resource "coder_script" "vscode-web" { resource "coder_script" "vscode-web" {
agent_id = var.agent_id agent_id = var.agent_id
display_name = "VS Code Web" display_name = "VS Code Web"
@@ -64,15 +92,17 @@ resource "coder_script" "vscode-web" {
script = templatefile("${path.module}/run.sh", { script = templatefile("${path.module}/run.sh", {
PORT : var.port, PORT : var.port,
LOG_PATH : var.log_path, LOG_PATH : var.log_path,
INSTALL_DIR : var.install_dir, INSTALL_PREFIX : var.install_prefix,
EXTENSIONS : join(",", var.extensions),
TELEMETRY_LEVEL : var.telemetry_level,
}) })
run_on_start = true run_on_start = true
} }
resource "coder_app" "vscode-web" { resource "coder_app" "vscode-web" {
agent_id = var.agent_id agent_id = var.agent_id
slug = "vscode-web" slug = var.slug
display_name = "VS Code Web" display_name = var.display_name
url = var.folder == "" ? "http://localhost:${var.port}" : "http://localhost:${var.port}?folder=${var.folder}" url = var.folder == "" ? "http://localhost:${var.port}" : "http://localhost:${var.port}?folder=${var.folder}"
icon = "/icon/code.svg" icon = "/icon/code.svg"
subdomain = true subdomain = true

48
vscode-web/run.sh Normal file → Executable file
View File

@@ -1,21 +1,49 @@
#!/usr/bin/env sh #!/usr/bin/env bash
BOLD='\033[0;1m' BOLD='\033[0;1m'
EXTENSIONS=("${EXTENSIONS}")
# Create install directory if it doesn't exist # Create install prefix
mkdir -p ${INSTALL_DIR} mkdir -p ${INSTALL_PREFIX}
printf "$${BOLD}Installing vscode-cli!\n" printf "$${BOLD}Installing Microsoft Visual Studio Code Server!\n"
# Download and extract code-cli tarball # Download and extract vscode-server
output=$(curl -Lk 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64' --output vscode_cli.tar.gz && tar -xf vscode_cli.tar.gz -C ${INSTALL_DIR} && rm vscode_cli.tar.gz) ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH="x64" ;;
aarch64) ARCH="arm64" ;;
*)
echo "Unsupported architecture"
exit 1
;;
esac
HASH=$(curl https://update.code.visualstudio.com/api/commits/stable/server-linux-$ARCH-web | cut -d '"' -f 2)
output=$(curl -sL https://vscode.download.prss.microsoft.com/dbazure/download/stable/$HASH/vscode-server-linux-$ARCH-web.tar.gz | tar -xz -C ${INSTALL_PREFIX} --strip-components 1)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to install vscode-cli: $output" echo "Failed to install Microsoft Visual Studio Code Server: $output"
exit 1 exit 1
fi fi
printf "🥳 vscode-cli has been installed.\n\n" printf "$${BOLD}Microsoft Visual Studio Code Server has been installed.\n"
echo "👷 Running ${INSTALL_DIR}/bin/code serve-web --port ${PORT} --without-connection-token --accept-server-license-terms in the background..." VSCODE_SERVER="${INSTALL_PREFIX}/bin/code-server"
# Install each extension...
IFS=',' read -r -a EXTENSIONLIST <<< "$${EXTENSIONS}"
for extension in "$${EXTENSIONLIST[@]}"; do
if [ -z "$extension" ]; then
continue
fi
printf "🧩 Installing extension $${CODE}$extension$${RESET}...\n"
output=$($VSCODE_SERVER --install-extension "$extension" --force)
if [ $? -ne 0 ]; then
echo "Failed to install extension: $extension: $output"
exit 1
fi
done
echo "👷 Running ${INSTALL_PREFIX}/bin/code-server serve-local --port ${PORT} --accept-server-license-terms serve-local --without-connection-token --telemetry-level ${TELEMETRY_LEVEL} in the background..."
echo "Check logs at ${LOG_PATH}!" echo "Check logs at ${LOG_PATH}!"
${INSTALL_DIR}/code serve-web --port ${PORT} --without-connection-token --accept-server-license-terms > ${LOG_PATH} 2>&1 & "${INSTALL_PREFIX}/bin/code-server" serve-local --port "${PORT}" --accept-server-license-terms serve-local --without-connection-token --telemetry-level "${TELEMETRY_LEVEL}" > "${LOG_PATH}" 2>&1 &