Compare commits
2 Commits
v1.0.1
...
slackme/ex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd3d42d54e | ||
|
|
66f1c3b97c |
6
.github/workflows/ci.yaml
vendored
@@ -20,8 +20,6 @@ jobs:
|
|||||||
- uses: oven-sh/setup-bun@v1
|
- uses: oven-sh/setup-bun@v1
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
- name: Setup
|
|
||||||
run: bun install
|
|
||||||
- run: bun test
|
- run: bun test
|
||||||
pretty:
|
pretty:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -30,9 +28,7 @@ jobs:
|
|||||||
- uses: oven-sh/setup-bun@v1
|
- uses: oven-sh/setup-bun@v1
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
- name: Setup
|
|
||||||
run: bun install
|
|
||||||
- name: Format
|
- name: Format
|
||||||
run: bun fmt:ci
|
run: bun fmt:ci
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: bun lint
|
run: bun install && bun lint
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68.03 68.03"><defs><style>.cls-1{fill:#da291c;}</style></defs><title>Artboard 1</title><polygon class="cls-1" points="34.02 13.31 11.27 52.72 14.52 52.72 34.02 18.94 34.02 24.57 17.77 52.72 21.02 52.72 34.02 30.2 34.02 35.83 24.27 52.72 27.52 52.72 34.02 41.46 34.02 47.09 30.77 52.72 34.02 52.72 34.02 52.72 56.77 52.72 34.02 13.31"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 427 B |
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
||||||
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="none"><path fill="#FFD814" d="M0 0l7.971 15.516L16 0H0zm6.732 6.16h-1.27V4.89h1.27v1.27zm0-1.906h-1.27V2.985h1.27v1.269zm1.904 3.81h-1.27v-1.27h1.27v1.27zm0-1.905h-1.27V4.89h1.27v1.27zm0-1.905h-1.27V2.985h1.27v1.269zm1.894 1.905H9.26V4.89h1.27v1.27zM9.26 4.254V2.985h1.27v1.269H9.26z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 506 B |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 205 KiB |
@@ -13,8 +13,7 @@ tags: [helper]
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "MODULE_NAME" {
|
module "MODULE_NAME" {
|
||||||
source = "registry.coder.com/modules/MODULE_NAME/coder"
|
source = "https://registry.coder.com/modules/MODULE_NAME"
|
||||||
version = "1.0.0"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -28,12 +27,11 @@ Install the Dracula theme from [OpenVSX](https://open-vsx.org/):
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "MODULE_NAME" {
|
module "MODULE_NAME" {
|
||||||
source = "registry.coder.com/modules/MODULE_NAME/coder"
|
source = "https://registry.coder.com/modules/MODULE_NAME"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
extensions = [
|
||||||
extensions = [
|
"dracula-theme.theme-dracula"
|
||||||
"dracula-theme.theme-dracula"
|
]
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -45,13 +43,12 @@ Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarte
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "MODULE_NAME" {
|
module "MODULE_NAME" {
|
||||||
source = "registry.coder.com/modules/MODULE_NAME/coder"
|
source = "https://registry.coder.com/modules/MODULE_NAME"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
extensions = [ "dracula-theme.theme-dracula" ]
|
||||||
extensions = [ "dracula-theme.theme-dracula" ]
|
settings = {
|
||||||
settings = {
|
"workbench.colorTheme" = "Dracula"
|
||||||
"workbench.colorTheme" = "Dracula"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -61,9 +58,8 @@ Run code-server in the background, don't fetch it from GitHub:
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "MODULE_NAME" {
|
module "MODULE_NAME" {
|
||||||
source = "registry.coder.com/modules/MODULE_NAME/coder"
|
source = "https://registry.coder.com/modules/MODULE_NAME"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
offline = true
|
||||||
offline = true
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Convert templated variables to shell variables
|
|
||||||
# shellcheck disable=SC2269
|
|
||||||
LOG_PATH=${LOG_PATH}
|
|
||||||
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
BOLD='\033[0;1m'
|
BOLD='\033[0;1m'
|
||||||
|
|
||||||
# shellcheck disable=SC2059
|
|
||||||
printf "$${BOLD}Installing MODULE_NAME ...\n\n"
|
printf "$${BOLD}Installing MODULE_NAME ...\n\n"
|
||||||
|
|
||||||
# Add code here
|
# Add code here
|
||||||
# Use varibles from the templatefile function in main.tf
|
# Use varibles from the templatefile function in main.tf
|
||||||
# e.g. LOG_PATH, PORT, etc.
|
# e.g. LOG_PATH, PORT, etc.
|
||||||
@@ -21,6 +13,6 @@ printf "👷 Starting MODULE_NAME in background...\n\n"
|
|||||||
# 1. Use & to run it in background
|
# 1. Use & to run it in background
|
||||||
# 2. redirct stdout and stderr to log files
|
# 2. redirct stdout and stderr to log files
|
||||||
|
|
||||||
./app > "$${LOG_PATH}" 2>&1 &
|
./app >${LOG_PATH} 2>&1 &
|
||||||
|
|
||||||
printf "check logs at %s\n\n" "$${LOG_PATH}"
|
printf "check logs at ${LOG_PATH} \n\n"
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ $ 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
|
```hcl
|
||||||
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.
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ e.g.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "code-server" {
|
module "code-server" {
|
||||||
source = "registry.coder.com/modules/code-server/coder"
|
source = "https://registry.coder.com/modules/code-server"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.main.id
|
||||||
agent_id = coder_agent.main.id
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,12 @@ Customize the preselected parameter value:
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "aws-region" {
|
module "aws-region" {
|
||||||
source = "registry.coder.com/modules/aws-region/coder"
|
source = "https://registry.coder.com/modules/aws-region"
|
||||||
version = "1.0.0"
|
default = "us-east-1"
|
||||||
default = "us-east-1"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
provider "aws" {
|
provider "aws" {
|
||||||
region = module.aws_region.value
|
region = module.aws_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -36,19 +35,18 @@ Change the display name and icon for a region using the corresponding maps:
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "aws-region" {
|
module "aws-region" {
|
||||||
source = "registry.coder.com/modules/aws-region/coder"
|
source = "https://registry.coder.com/modules/aws-region"
|
||||||
version = "1.0.0"
|
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"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
provider "aws" {
|
provider "aws" {
|
||||||
region = module.aws_region.value
|
region = module.aws_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -60,13 +58,12 @@ Hide the Asia Pacific regions Seoul and Osaka:
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "aws-region" {
|
module "aws-region" {
|
||||||
source = "registry.coder.com/modules/aws-region/coder"
|
source = "https://registry.coder.com/modules/aws-region"
|
||||||
version = "1.0.0"
|
exclude = [ "ap-northeast-2", "ap-northeast-3" ]
|
||||||
exclude = [ "ap-northeast-2", "ap-northeast-3" ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
provider "aws" {
|
provider "aws" {
|
||||||
region = module.aws_region.value
|
region = module.aws_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,12 @@ This module adds a parameter with all Azure regions, allowing developers to sele
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "azure_region" {
|
module "azure_region" {
|
||||||
source = "registry.coder.com/modules/azure-region/coder"
|
source = "https://registry.coder.com/modules/azure-region"
|
||||||
version = "1.0.0"
|
default = "eastus"
|
||||||
default = "eastus"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azurem_resource_group" "example" {
|
resource "azurem_resource_group" "example" {
|
||||||
location = module.azure_region.value
|
location = module.azure_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -33,18 +32,17 @@ Change the display name and icon for a region using the corresponding maps:
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "azure-region" {
|
module "azure-region" {
|
||||||
source = "registry.coder.com/modules/azure-region/coder"
|
source = "https://registry.coder.com/modules/azure-region"
|
||||||
version = "1.0.0"
|
custom_names = {
|
||||||
custom_names = {
|
"australia": "Go Australia!"
|
||||||
"australia": "Go Australia!"
|
}
|
||||||
}
|
custom_icons = {
|
||||||
custom_icons = {
|
"australia": "/icons/smiley.svg"
|
||||||
"australia": "/icons/smiley.svg"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azurerm_resource_group" "example" {
|
resource "azurerm_resource_group" "example" {
|
||||||
location = module.azure_region.value
|
location = module.azure_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -56,18 +54,17 @@ Hide all regions in Australia except australiacentral:
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "azure-region" {
|
module "azure-region" {
|
||||||
source = "registry.coder.com/modules/azure-region/coder"
|
source = "https://registry.coder.com/modules/azure-region"
|
||||||
version = "1.0.0"
|
exclude = [
|
||||||
exclude = [
|
"australia",
|
||||||
"australia",
|
"australiacentral2",
|
||||||
"australiacentral2",
|
"australiaeast",
|
||||||
"australiaeast",
|
"australiasoutheast"
|
||||||
"australiasoutheast"
|
]
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azurerm_resource_group" "example" {
|
resource "azurerm_resource_group" "example" {
|
||||||
location = module.azure_region.value
|
location = module.azure_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,8 @@ Automatically install [code-server](https://github.com/coder/code-server) in a w
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "code-server" {
|
module "code-server" {
|
||||||
source = "registry.coder.com/modules/code-server/coder"
|
source = "https://registry.coder.com/modules/code-server"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -27,8 +26,7 @@ module "code-server" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "code-server" {
|
module "code-server" {
|
||||||
source = "registry.coder.com/modules/code-server/coder"
|
source = "https://registry.coder.com/modules/code-server"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
install_version = "4.8.3"
|
install_version = "4.8.3"
|
||||||
}
|
}
|
||||||
@@ -40,12 +38,11 @@ Install the Dracula theme from [OpenVSX](https://open-vsx.org/):
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "code-server" {
|
module "code-server" {
|
||||||
source = "registry.coder.com/modules/code-server/coder"
|
source = "https://registry.coder.com/modules/code-server"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
extensions = [
|
||||||
extensions = [
|
"dracula-theme.theme-dracula"
|
||||||
"dracula-theme.theme-dracula"
|
]
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -57,26 +54,12 @@ Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarte
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "settings" {
|
module "settings" {
|
||||||
source = "registry.coder.com/modules/code-server/coder"
|
source = "https://registry.coder.com/modules/code-server"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
extensions = [ "dracula-theme.theme-dracula" ]
|
||||||
extensions = [ "dracula-theme.theme-dracula" ]
|
settings = {
|
||||||
settings = {
|
"workbench.colorTheme" = "Dracula"
|
||||||
"workbench.colorTheme" = "Dracula"
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install multiple extensions
|
|
||||||
|
|
||||||
Just run code-server in the background, don't fetch it from GitHub:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "settings" {
|
|
||||||
source = "registry.coder.com/modules/code-server/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
extensions = [ "dracula-theme.theme-dracula", "ms-azuretools.vscode-docker" ]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -86,9 +69,8 @@ Just run code-server in the background, don't fetch it from GitHub:
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "settings" {
|
module "settings" {
|
||||||
source = "registry.coder.com/modules/code-server/coder"
|
source = "https://registry.coder.com/modules/code-server"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
offline = true
|
||||||
offline = true
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -62,15 +62,6 @@ variable "install_version" {
|
|||||||
default = ""
|
default = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "share" {
|
|
||||||
type = string
|
|
||||||
default = "owner"
|
|
||||||
validation {
|
|
||||||
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
|
|
||||||
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_script" "code-server" {
|
resource "coder_script" "code-server" {
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
display_name = "code-server"
|
display_name = "code-server"
|
||||||
@@ -94,7 +85,7 @@ resource "coder_app" "code-server" {
|
|||||||
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"
|
||||||
subdomain = false
|
subdomain = false
|
||||||
share = var.share
|
share = "owner"
|
||||||
|
|
||||||
healthcheck {
|
healthcheck {
|
||||||
url = "http://localhost:${var.port}/healthz"
|
url = "http://localhost:${var.port}/healthz"
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ printf "🥳 code-server has been installed in ${INSTALL_PREFIX}\n\n"
|
|||||||
CODE_SERVER="${INSTALL_PREFIX}/bin/code-server"
|
CODE_SERVER="${INSTALL_PREFIX}/bin/code-server"
|
||||||
|
|
||||||
# Install each extension...
|
# Install each extension...
|
||||||
IFS=',' read -r -a EXTENSIONLIST <<< "$${EXTENSIONS}"
|
for extension in "$${EXTENSIONS[@]}"; do
|
||||||
for extension in "$${EXTENSIONLIST[@]}"; do
|
|
||||||
if [ -z "$extension" ]; then
|
if [ -z "$extension" ]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -39,12 +38,12 @@ 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/User/settings.json ]; then
|
if [ ! -f ~/.local/share/code-server/Machine/settings.json ]; then
|
||||||
echo "⚙️ Creating settings file..."
|
echo "⚙️ Creating settings file..."
|
||||||
mkdir -p ~/.local/share/code-server/User
|
mkdir -p ~/.local/share/code-server/Machine
|
||||||
echo "${SETTINGS}" > ~/.local/share/code-server/User/settings.json
|
echo "${SETTINGS}" >~/.local/share/code-server/Machine/settings.json
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "👷 Running code-server in the background..."
|
echo "👷 Running code-server in the background..."
|
||||||
echo "Check logs at ${LOG_PATH}!"
|
echo "Check logs at ${LOG_PATH}!"
|
||||||
$CODE_SERVER --auth none --port ${PORT} > ${LOG_PATH} 2>&1 &
|
$CODE_SERVER --auth none --port ${PORT} >${LOG_PATH} 2>&1 &
|
||||||
|
|||||||
@@ -13,9 +13,8 @@ Automatically logs the user into Coder when creating their workspace.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "coder-login" {
|
module "coder-login" {
|
||||||
source = "registry.coder.com/modules/coder-login/coder"
|
source = "https://registry.coder.com/modules/coder-login"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ BOLD='\033[0;1m'
|
|||||||
|
|
||||||
printf "$${BOLD}Logging into Coder...\n\n$${RESET}"
|
printf "$${BOLD}Logging into Coder...\n\n$${RESET}"
|
||||||
|
|
||||||
if ! coder list > /dev/null 2>&1; then
|
if ! coder list >/dev/null 2>&1; then
|
||||||
set +x
|
set +x; coder login --token="${CODER_USER_TOKEN}" --url="${CODER_DEPLOYMENT_URL}"
|
||||||
coder login --token="${CODER_USER_TOKEN}" --url="${CODER_DEPLOYMENT_URL}"
|
|
||||||
else
|
else
|
||||||
echo "You are already authenticated with coder."
|
echo "You are already authenticated with coder."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ Allow developers to optionally bring their own [dotfiles repository](https://dot
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "dotfiles" {
|
module "dotfiles" {
|
||||||
source = "registry.coder.com/modules/dotfiles/coder"
|
source = "https://registry.coder.com/modules/dotfiles"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
---
|
|
||||||
display_name: exoscale-instance-type
|
|
||||||
description: A parameter with human readable exoscale instance names
|
|
||||||
icon: ../.icons/exoscale.svg
|
|
||||||
maintainer_github: WhizUs
|
|
||||||
verified: false
|
|
||||||
tags: [helper, parameter, instances, exoscale]
|
|
||||||
---
|
|
||||||
|
|
||||||
# exoscale-instance-type
|
|
||||||
|
|
||||||
A parameter with all Exoscale instance types. This allows developers to select
|
|
||||||
their desired virtuell machine for the workspace.
|
|
||||||
|
|
||||||
Customize the preselected parameter value:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "exoscale-instance-type" {
|
|
||||||
source = "registry.coder.com/modules/exoscale-instance-type/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
default = "standard.medium"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "exoscale_compute_instance" "instance" {
|
|
||||||
type = module.exoscale-instance-type.value
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_metadata" "workspace_info" {
|
|
||||||
item {
|
|
||||||
key = "instance type"
|
|
||||||
value = module.exoscale-instance-type.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Customize type
|
|
||||||
|
|
||||||
Change the display name a type using the corresponding maps:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "exoscale-instance-type" {
|
|
||||||
source = "registry.coder.com/modules/exoscale-instance-type/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
default = "standard.medium"
|
|
||||||
custom_names = {
|
|
||||||
"standard.medium": "Mittlere Instanz" # German translation
|
|
||||||
}
|
|
||||||
custom_descriptions = {
|
|
||||||
"standard.medium": "4 GB Arbeitsspeicher, 2 Kerne, 10 - 400 GB Festplatte" # German translation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "exoscale_compute_instance" "instance" {
|
|
||||||
type = module.exoscale-instance-type.value
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_metadata" "workspace_info" {
|
|
||||||
item {
|
|
||||||
key = "instance type"
|
|
||||||
value = module.exoscale-instance-type.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Use category and exlude type
|
|
||||||
|
|
||||||
Show only gpu1 types
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "exoscale-instance-type" {
|
|
||||||
source = "registry.coder.com/modules/exoscale-instance-type/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
default = "gpu.large"
|
|
||||||
type_category = ["gpu"]
|
|
||||||
exclude = [
|
|
||||||
"gpu2.small",
|
|
||||||
"gpu2.medium",
|
|
||||||
"gpu2.large",
|
|
||||||
"gpu2.huge",
|
|
||||||
"gpu3.small",
|
|
||||||
"gpu3.medium",
|
|
||||||
"gpu3.large",
|
|
||||||
"gpu3.huge"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "exoscale_compute_instance" "instance" {
|
|
||||||
type = module.exoscale-instance-type.value
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_metadata" "workspace_info" {
|
|
||||||
item {
|
|
||||||
key = "instance type"
|
|
||||||
value = module.exoscale-instance-type.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Related templates
|
|
||||||
|
|
||||||
A related exoscale template will be provided soon.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { describe, expect, it } from "bun:test";
|
|
||||||
import {
|
|
||||||
runTerraformApply,
|
|
||||||
runTerraformInit,
|
|
||||||
testRequiredVariables,
|
|
||||||
} from "../test";
|
|
||||||
|
|
||||||
describe("exoscale-instance-type", async () => {
|
|
||||||
await runTerraformInit(import.meta.dir);
|
|
||||||
|
|
||||||
testRequiredVariables(import.meta.dir, {});
|
|
||||||
|
|
||||||
it("default output", async () => {
|
|
||||||
const state = await runTerraformApply(import.meta.dir, {});
|
|
||||||
expect(state.outputs.value.value).toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("customized default", async () => {
|
|
||||||
const state = await runTerraformApply(import.meta.dir, {
|
|
||||||
default: "gpu3.huge",
|
|
||||||
type_category: `["gpu", "cpu"]`,
|
|
||||||
});
|
|
||||||
expect(state.outputs.value.value).toBe("gpu3.huge");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("fails because of wrong categroy definition", async () => {
|
|
||||||
expect(async () => {
|
|
||||||
await runTerraformApply(import.meta.dir, {
|
|
||||||
default: "gpu3.huge",
|
|
||||||
// type_category: ["standard"] is standard
|
|
||||||
});
|
|
||||||
}).toThrow('default value "gpu3.huge" must be defined as one of options');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,279 +0,0 @@
|
|||||||
terraform {
|
|
||||||
required_version = ">= 1.0"
|
|
||||||
|
|
||||||
required_providers {
|
|
||||||
coder = {
|
|
||||||
source = "coder/coder"
|
|
||||||
version = ">= 0.12"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "display_name" {
|
|
||||||
default = "Exoscale instance type"
|
|
||||||
description = "The display name of the parameter."
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "description" {
|
|
||||||
default = "Select the exoscale instance type to use for the workspace. Check out the pricing page for more information: https://www.exoscale.com/pricing"
|
|
||||||
description = "The description of the parameter."
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "default" {
|
|
||||||
default = ""
|
|
||||||
description = "The default instance type to use if no type is specified. One of [\"standard.micro\", \"standard.tiny\", \"standard.small\", \"standard.medium\", \"standard.large\", \"standard.extra\", \"standard.huge\", \"standard.mega\", \"standard.titan\", \"standard.jumbo\", \"standard.colossus\", \"cpu.extra\", \"cpu.huge\", \"cpu.mega\", \"cpu.titan\", \"memory.extra\", \"memory.huge\", \"memory.mega\", \"memory.titan\", \"storage.extra\", \"storage.huge\", \"storage.mega\", \"storage.titan\", \"storage.jumbo\", \"gpu.small\", \"gpu.medium\", \"gpu.large\", \"gpu.huge\", \"gpu2.small\", \"gpu2.medium\", \"gpu2.large\", \"gpu2.huge\", \"gpu3.small\", \"gpu3.medium\", \"gpu3.large\", \"gpu3.huge\"]"
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "mutable" {
|
|
||||||
default = false
|
|
||||||
description = "Whether the parameter can be changed after creation."
|
|
||||||
type = bool
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "custom_names" {
|
|
||||||
default = {}
|
|
||||||
description = "A map of custom display names for instance type IDs."
|
|
||||||
type = map(string)
|
|
||||||
}
|
|
||||||
variable "custom_descriptions" {
|
|
||||||
default = {}
|
|
||||||
description = "A map of custom descriptions for instance type IDs."
|
|
||||||
type = map(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "type_category" {
|
|
||||||
default = ["standard"]
|
|
||||||
description = "A list of instance type categories the user is allowed to choose. One of [\"standard\", \"cpu\", \"memory\", \"storage\", \"gpu\"]"
|
|
||||||
type = list(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "exclude" {
|
|
||||||
default = []
|
|
||||||
description = "A list of instance type IDs to exclude. One of [\"standard.micro\", \"standard.tiny\", \"standard.small\", \"standard.medium\", \"standard.large\", \"standard.extra\", \"standard.huge\", \"standard.mega\", \"standard.titan\", \"standard.jumbo\", \"standard.colossus\", \"cpu.extra\", \"cpu.huge\", \"cpu.mega\", \"cpu.titan\", \"memory.extra\", \"memory.huge\", \"memory.mega\", \"memory.titan\", \"storage.extra\", \"storage.huge\", \"storage.mega\", \"storage.titan\", \"storage.jumbo\", \"gpu.small\", \"gpu.medium\", \"gpu.large\", \"gpu.huge\", \"gpu2.small\", \"gpu2.medium\", \"gpu2.large\", \"gpu2.huge\", \"gpu3.small\", \"gpu3.medium\", \"gpu3.large\", \"gpu3.huge\"]"
|
|
||||||
type = list(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
locals {
|
|
||||||
# https://www.exoscale.com/pricing/
|
|
||||||
|
|
||||||
standard_instances = [
|
|
||||||
{
|
|
||||||
value = "standard.micro",
|
|
||||||
name = "Standard Micro",
|
|
||||||
description = "512 MB RAM, 1 Core, 10 - 200 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.tiny",
|
|
||||||
name = "Standard Tiny",
|
|
||||||
description = "1 GB RAM, 1 Core, 10 - 400 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.small",
|
|
||||||
name = "Standard Small",
|
|
||||||
description = "2 GB RAM, 2 Cores, 10 - 400 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.medium",
|
|
||||||
name = "Standard Medium",
|
|
||||||
description = "4 GB RAM, 2 Cores, 10 - 400 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.large",
|
|
||||||
name = "Standard Large",
|
|
||||||
description = "8 GB RAM, 4 Cores, 10 - 400 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.extra",
|
|
||||||
name = "Standard Extra",
|
|
||||||
description = "rge",
|
|
||||||
description = "16 GB RAM, 4 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.huge",
|
|
||||||
name = "Standard Huge",
|
|
||||||
description = "32 GB RAM, 8 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.mega",
|
|
||||||
name = "Standard Mega",
|
|
||||||
description = "64 GB RAM, 12 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.titan",
|
|
||||||
name = "Standard Titan",
|
|
||||||
description = "128 GB RAM, 16 Cores, 10 - 1.6 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.jumbo",
|
|
||||||
name = "Standard Jumbo",
|
|
||||||
description = "256 GB RAM, 24 Cores, 10 - 1.6 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "standard.colossus",
|
|
||||||
name = "Standard Colossus",
|
|
||||||
description = "320 GB RAM, 40 Cores, 10 - 1.6 TB Disk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
cpu_instances = [
|
|
||||||
{
|
|
||||||
value = "cpu.extra",
|
|
||||||
name = "CPU Extra-Large",
|
|
||||||
description = "16 GB RAM, 8 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "cpu.huge",
|
|
||||||
name = "CPU Huge",
|
|
||||||
description = "32 GB RAM, 16 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "cpu.mega",
|
|
||||||
name = "CPU Mega",
|
|
||||||
description = "64 GB RAM, 32 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "cpu.titan",
|
|
||||||
name = "CPU Titan",
|
|
||||||
description = "128 GB RAM, 40 Cores, 0.1 - 1.6 TB Disk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
memory_instances = [
|
|
||||||
{
|
|
||||||
value = "memory.extra",
|
|
||||||
name = "Memory Extra-Large",
|
|
||||||
description = "16 GB RAM, 2 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "memory.huge",
|
|
||||||
name = "Memory Huge",
|
|
||||||
description = "32 GB RAM, 4 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "memory.mega",
|
|
||||||
name = "Memory Mega",
|
|
||||||
description = "64 GB RAM, 8 Cores, 10 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "memory.titan",
|
|
||||||
name = "Memory Titan",
|
|
||||||
description = "128 GB RAM, 12 Cores, 0.1 - 1.6 TB Disk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
storage_instances = [
|
|
||||||
{
|
|
||||||
value = "storage.extra",
|
|
||||||
name = "Storage Extra-Large",
|
|
||||||
description = "16 GB RAM, 4 Cores, 1 - 2 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "storage.huge",
|
|
||||||
name = "Storage Huge",
|
|
||||||
description = "32 GB RAM, 8 Cores, 2 - 3 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "storage.mega",
|
|
||||||
name = "Storage Mega",
|
|
||||||
description = "64 GB RAM, 12 Cores, 3 - 5 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "storage.titan",
|
|
||||||
name = "Storage Titan",
|
|
||||||
description = "128 GB RAM, 16 Cores, 5 - 10 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "storage.jumbo",
|
|
||||||
name = "Storage Jumbo",
|
|
||||||
description = "225 GB RAM, 24 Cores, 10 - 15 TB Disk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
gpu_instances = [
|
|
||||||
{
|
|
||||||
value = "gpu.small",
|
|
||||||
name = "GPU1 Small",
|
|
||||||
description = "56 GB RAM, 12 Cores, 1 GPU, 100 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu.medium",
|
|
||||||
name = "GPU1 Medium",
|
|
||||||
description = "90 GB RAM, 16 Cores, 2 GPU, 0.1 - 1.2 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu.large",
|
|
||||||
name = "GPU1 Large",
|
|
||||||
description = "120 GB RAM, 24 Cores, 3 GPU, 0.1 - 1.6 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu.huge",
|
|
||||||
name = "GPU1 Huge",
|
|
||||||
description = "225 GB RAM, 48 Cores, 4 GPU, 0.1 - 1.6 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu2.small",
|
|
||||||
name = "GPU2 Small",
|
|
||||||
description = "56 GB RAM, 12 Cores, 1 GPU, 100 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu2.medium",
|
|
||||||
name = "GPU2 Medium",
|
|
||||||
description = "90 GB RAM, 16 Cores, 2 GPU, 0.1 - 1.2 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu2.large",
|
|
||||||
name = "GPU2 Large",
|
|
||||||
description = "120 GB RAM, 24 Cores, 3 GPU, 0.1 - 1.6 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu2.huge",
|
|
||||||
name = "GPU2 Huge",
|
|
||||||
description = "225 GB RAM, 48 Cores, 4 GPU, 0.1 - 1.6 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu3.small",
|
|
||||||
name = "GPU3 Small",
|
|
||||||
description = "56 GB RAM, 12 Cores, 1 GPU, 100 - 800 GB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu3.medium",
|
|
||||||
name = "GPU3 Medium",
|
|
||||||
description = "120 GB RAM, 24 Cores, 2 GPU, 0.1 - 1.2 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu3.large",
|
|
||||||
name = "GPU3 Large",
|
|
||||||
description = "224 GB RAM, 48 Cores, 4 GPU, 0.1 - 1.6 TB Disk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value = "gpu3.huge",
|
|
||||||
name = "GPU3 Huge",
|
|
||||||
description = "448 GB RAM, 96 Cores, 8 GPU, 0.1 - 1.6 TB Disk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
data "coder_parameter" "instance_type" {
|
|
||||||
name = "exoscale_instance_type"
|
|
||||||
display_name = var.display_name
|
|
||||||
description = var.description
|
|
||||||
default = var.default == "" ? null : var.default
|
|
||||||
mutable = var.mutable
|
|
||||||
dynamic "option" {
|
|
||||||
for_each = [for k, v in concat(
|
|
||||||
contains(var.type_category, "standard") ? local.standard_instances : [],
|
|
||||||
contains(var.type_category, "cpu") ? local.cpu_instances : [],
|
|
||||||
contains(var.type_category, "memory") ? local.memory_instances : [],
|
|
||||||
contains(var.type_category, "storage") ? local.storage_instances : [],
|
|
||||||
contains(var.type_category, "gpu") ? local.gpu_instances : []
|
|
||||||
) : v if !(contains(var.exclude, v.value))]
|
|
||||||
content {
|
|
||||||
name = try(var.custom_names[option.value.value], option.value.name)
|
|
||||||
description = try(var.custom_descriptions[option.value.value], option.value.description)
|
|
||||||
value = option.value.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output "value" {
|
|
||||||
value = data.coder_parameter.instance_type.value
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
---
|
|
||||||
display_name: exoscale-zone
|
|
||||||
description: A parameter with human zone names and icons
|
|
||||||
icon: ../.icons/exoscale.svg
|
|
||||||
maintainer_github: WhizUs
|
|
||||||
verified: false
|
|
||||||
tags: [helper, parameter, zones, regions, exoscale]
|
|
||||||
---
|
|
||||||
|
|
||||||
# exoscale-zone
|
|
||||||
|
|
||||||
A parameter with all Exoscale zones. This allows developers to select
|
|
||||||
the zone closest to them.
|
|
||||||
|
|
||||||
Customize the preselected parameter value:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "exoscale-zone" {
|
|
||||||
source = "registry.coder.com/modules/exoscale-zone/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
default = "ch-dk-2"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
data "exoscale_compute_template" "my_template" {
|
|
||||||
zone = module.exoscale-zone.value
|
|
||||||
name = "Linux Ubuntu 22.04 LTS 64-bit"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "exoscale_compute_instance" "instance" {
|
|
||||||
zone = module.exoscale-zone.value
|
|
||||||
....
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Customize zones
|
|
||||||
|
|
||||||
Change the display name and icon for a zone using the corresponding maps:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "exoscale-zone" {
|
|
||||||
source = "registry.coder.com/modules/exoscale-zone/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
default = "at-vie-1"
|
|
||||||
custom_names = {
|
|
||||||
"at-vie-1": "Home Vienna"
|
|
||||||
}
|
|
||||||
custom_icons = {
|
|
||||||
"at-vie-1": "/emojis/1f3e0.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data "exoscale_compute_template" "my_template" {
|
|
||||||
zone = module.exoscale-zone.value
|
|
||||||
name = "Linux Ubuntu 22.04 LTS 64-bit"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "exoscale_compute_instance" "instance" {
|
|
||||||
zone = module.exoscale-zone.value
|
|
||||||
....
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Exclude regions
|
|
||||||
|
|
||||||
Hide the Switzerland zones Geneva and Zurich
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "exoscale-zone" {
|
|
||||||
source = "registry.coder.com/modules/exoscale-zone/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
exclude = [ "ch-gva-2", "ch-dk-2" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
data "exoscale_compute_template" "my_template" {
|
|
||||||
zone = module.exoscale-zone.value
|
|
||||||
name = "Linux Ubuntu 22.04 LTS 64-bit"
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "exoscale_compute_instance" "instance" {
|
|
||||||
zone = module.exoscale-zone.value
|
|
||||||
....
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Related templates
|
|
||||||
|
|
||||||
An exoscale sample template will be delivered soon.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { describe, expect, it } from "bun:test";
|
|
||||||
import {
|
|
||||||
executeScriptInContainer,
|
|
||||||
runTerraformApply,
|
|
||||||
runTerraformInit,
|
|
||||||
testRequiredVariables,
|
|
||||||
} from "../test";
|
|
||||||
|
|
||||||
describe("exoscale-zone", async () => {
|
|
||||||
await runTerraformInit(import.meta.dir);
|
|
||||||
|
|
||||||
testRequiredVariables(import.meta.dir, {});
|
|
||||||
|
|
||||||
it("default output", async () => {
|
|
||||||
const state = await runTerraformApply(import.meta.dir, {});
|
|
||||||
expect(state.outputs.value.value).toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("customized default", async () => {
|
|
||||||
const state = await runTerraformApply(import.meta.dir, {
|
|
||||||
default: "at-vie-1",
|
|
||||||
});
|
|
||||||
expect(state.outputs.value.value).toBe("at-vie-1");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
terraform {
|
|
||||||
required_version = ">= 1.0"
|
|
||||||
|
|
||||||
required_providers {
|
|
||||||
coder = {
|
|
||||||
source = "coder/coder"
|
|
||||||
version = ">= 0.12"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "display_name" {
|
|
||||||
default = "Exoscale Region"
|
|
||||||
description = "The display name of the parameter."
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "description" {
|
|
||||||
default = "The region to deploy workspace infrastructure."
|
|
||||||
description = "The description of the parameter."
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "default" {
|
|
||||||
default = ""
|
|
||||||
description = "The default region to use if no region is specified."
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "mutable" {
|
|
||||||
default = false
|
|
||||||
description = "Whether the parameter can be changed after creation."
|
|
||||||
type = bool
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "custom_names" {
|
|
||||||
default = {}
|
|
||||||
description = "A map of custom display names for region IDs."
|
|
||||||
type = map(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "custom_icons" {
|
|
||||||
default = {}
|
|
||||||
description = "A map of custom icons for region IDs."
|
|
||||||
type = map(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "exclude" {
|
|
||||||
default = []
|
|
||||||
description = "A list of region IDs to exclude."
|
|
||||||
type = list(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
locals {
|
|
||||||
# This is a static list because the zones don't change _that_
|
|
||||||
# frequently and including the `exoscale_zones` data source requires
|
|
||||||
# the provider, which requires a zone.
|
|
||||||
# https://www.exoscale.com/datacenters/
|
|
||||||
zones = {
|
|
||||||
"de-fra-1" = {
|
|
||||||
name = "Frankfurt - Germany"
|
|
||||||
icon = "/emojis/1f1e9-1f1ea.png"
|
|
||||||
}
|
|
||||||
"at-vie-1" = {
|
|
||||||
name = "Vienna 1 - Austria"
|
|
||||||
icon = "/emojis/1f1e6-1f1f9.png"
|
|
||||||
}
|
|
||||||
"at-vie-2" = {
|
|
||||||
name = "Vienna 2 - Austria"
|
|
||||||
icon = "/emojis/1f1e6-1f1f9.png"
|
|
||||||
}
|
|
||||||
"ch-gva-2" = {
|
|
||||||
name = "Geneva - Switzerland"
|
|
||||||
icon = "/emojis/1f1e8-1f1ed.png"
|
|
||||||
}
|
|
||||||
"ch-dk-2" = {
|
|
||||||
name = "Zurich - Switzerland"
|
|
||||||
icon = "/emojis/1f1e8-1f1ed.png"
|
|
||||||
}
|
|
||||||
"bg-sof-1" = {
|
|
||||||
name = "Sofia - Bulgaria"
|
|
||||||
icon = "/emojis/1f1e7-1f1ec.png"
|
|
||||||
}
|
|
||||||
"de-muc-1" = {
|
|
||||||
name = "Munich - Germany"
|
|
||||||
icon = "/emojis/1f1e9-1f1ea.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data "coder_parameter" "zone" {
|
|
||||||
name = "exoscale_zone"
|
|
||||||
display_name = var.display_name
|
|
||||||
description = var.description
|
|
||||||
default = var.default == "" ? null : var.default
|
|
||||||
mutable = var.mutable
|
|
||||||
dynamic "option" {
|
|
||||||
for_each = { for k, v in local.zones : k => v if !(contains(var.exclude, k)) }
|
|
||||||
content {
|
|
||||||
name = try(var.custom_names[option.key], option.value.name)
|
|
||||||
icon = try(var.custom_icons[option.key], option.value.icon)
|
|
||||||
value = option.key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output "value" {
|
|
||||||
value = data.coder_parameter.zone.value
|
|
||||||
}
|
|
||||||
@@ -13,9 +13,8 @@ A file browser for your workspace.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "filebrowser" {
|
module "filebrowser" {
|
||||||
source = "registry.coder.com/modules/filebrowser/coder"
|
source = "https://registry.coder.com/modules/filebrowser"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -27,10 +26,9 @@ module "filebrowser" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "filebrowser" {
|
module "filebrowser" {
|
||||||
source = "registry.coder.com/modules/filebrowser/coder"
|
source = "https://registry.coder.com/modules/filebrowser"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
folder = "/home/coder/project"
|
||||||
folder = "/home/coder/project"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -38,9 +36,8 @@ module "filebrowser" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "filebrowser" {
|
module "filebrowser" {
|
||||||
source = "registry.coder.com/modules/filebrowser/coder"
|
source = "https://registry.coder.com/modules/filebrowser"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
database_path = ".config/filebrowser.db"
|
||||||
database_path = ".config/filebrowser.db"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -43,15 +43,6 @@ variable "folder" {
|
|||||||
default = "~"
|
default = "~"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "share" {
|
|
||||||
type = string
|
|
||||||
default = "owner"
|
|
||||||
validation {
|
|
||||||
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
|
|
||||||
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_script" "filebrowser" {
|
resource "coder_script" "filebrowser" {
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
display_name = "File Browser"
|
display_name = "File Browser"
|
||||||
@@ -73,5 +64,5 @@ resource "coder_app" "filebrowser" {
|
|||||||
url = "http://localhost:${var.port}"
|
url = "http://localhost:${var.port}"
|
||||||
icon = "https://raw.githubusercontent.com/filebrowser/logo/master/icon_raw.svg"
|
icon = "https://raw.githubusercontent.com/filebrowser/logo/master/icon_raw.svg"
|
||||||
subdomain = true
|
subdomain = true
|
||||||
share = var.share
|
share = "owner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ printf "📂 Serving $${ROOT_DIR} at http://localhost:${PORT} \n\n"
|
|||||||
|
|
||||||
printf "Running 'filebrowser --noauth --root $ROOT_DIR --port ${PORT}$${DB_FLAG}' \n\n"
|
printf "Running 'filebrowser --noauth --root $ROOT_DIR --port ${PORT}$${DB_FLAG}' \n\n"
|
||||||
|
|
||||||
filebrowser --noauth --root $ROOT_DIR --port ${PORT}$${DB_FLAG} > ${LOG_PATH} 2>&1 &
|
filebrowser --noauth --root $ROOT_DIR --port ${PORT}$${DB_FLAG} >${LOG_PATH} 2>&1 &
|
||||||
|
|
||||||
printf "📝 Logs at ${LOG_PATH} \n\n"
|
printf "📝 Logs at ${LOG_PATH} \n\n"
|
||||||
|
|||||||
@@ -15,9 +15,8 @@ We can use the simplest format here, only adding a default selection as the `atl
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "fly-region" {
|
module "fly-region" {
|
||||||
source = "registry.coder.com/modules/fly-region/coder"
|
source = "https://registry.coder.com/modules/fly-region"
|
||||||
version = "1.0.0"
|
default = "atl"
|
||||||
default = "atl"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -31,10 +30,9 @@ The regions argument can be used to display only the desired regions in the Code
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "fly-region" {
|
module "fly-region" {
|
||||||
source = "registry.coder.com/modules/fly-region/coder"
|
source = "https://registry.coder.com/modules/fly-region"
|
||||||
version = "1.0.0"
|
default = "ams"
|
||||||
default = "ams"
|
regions = ["ams", "arn", "atl"]
|
||||||
regions = ["ams", "arn", "atl"]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -46,15 +44,14 @@ Set custom icons and names with their respective maps.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "fly-region" {
|
module "fly-region" {
|
||||||
source = "registry.coder.com/modules/fly-region/coder"
|
source = "https://registry.coder.com/modules/fly-region"
|
||||||
version = "1.0.0"
|
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!"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,12 @@ This module adds Google Cloud Platform regions to your Coder template.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "gcp_region" {
|
module "gcp_region" {
|
||||||
source = "registry.coder.com/modules/gcp-region/coder"
|
source = "https://registry.coder.com/modules/gcp-region"
|
||||||
version = "1.0.0"
|
regions = ["us", "europe"]
|
||||||
regions = ["us", "europe"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_compute_instance" "example" {
|
resource "google_compute_instance" "example" {
|
||||||
zone = module.gcp_region.value
|
zone = module.gcp_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -33,15 +32,14 @@ Note: setting `gpu_only = true` and using a default region without GPU support,
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "gcp_region" {
|
module "gcp_region" {
|
||||||
source = "registry.coder.com/modules/gcp-region/coder"
|
source = "https://registry.coder.com/modules/gcp-region"
|
||||||
version = "1.0.0"
|
default = ["us-west1-a"]
|
||||||
default = ["us-west1-a"]
|
regions = ["us-west1"]
|
||||||
regions = ["us-west1"]
|
gpu_only = false
|
||||||
gpu_only = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_compute_instance" "example" {
|
resource "google_compute_instance" "example" {
|
||||||
zone = module.gcp_region.value
|
zone = module.gcp_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -49,14 +47,13 @@ resource "google_compute_instance" "example" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "gcp_region" {
|
module "gcp_region" {
|
||||||
source = "registry.coder.com/modules/gcp-region/coder"
|
source = "https://registry.coder.com/modules/gcp-region"
|
||||||
version = "1.0.0"
|
regions = ["europe-west"]
|
||||||
regions = ["europe-west"]
|
single_zone_per_region = false
|
||||||
single_zone_per_region = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_compute_instance" "example" {
|
resource "google_compute_instance" "example" {
|
||||||
zone = module.gcp_region.value
|
zone = module.gcp_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -64,14 +61,13 @@ resource "google_compute_instance" "example" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "gcp_region" {
|
module "gcp_region" {
|
||||||
source = "registry.coder.com/modules/gcp-region/coder"
|
source = "https://registry.coder.com/modules/gcp-region"
|
||||||
version = "1.0.0"
|
regions = ["us", "europe"]
|
||||||
regions = ["us", "europe"]
|
gpu_only = true
|
||||||
gpu_only = true
|
single_zone_per_region = true
|
||||||
single_zone_per_region = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_compute_instance" "example" {
|
resource "google_compute_instance" "example" {
|
||||||
zone = module.gcp_region.value
|
zone = module.gcp_region.value
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -13,10 +13,9 @@ This module allows you to automatically clone a repository by URL and skip if it
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "git-clone" {
|
module "git-clone" {
|
||||||
source = "registry.coder.com/modules/git-clone/coder"
|
source = "https://registry.coder.com/modules/git-clone"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
url = "https://github.com/coder/coder"
|
||||||
url = "https://github.com/coder/coder"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ To use with [Git Authentication](https://coder.com/docs/v2/latest/admin/git-prov
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
data "coder_git_auth" "github" {
|
data "coder_git_auth" "github" {
|
||||||
id = "github"
|
id = "github"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -34,10 +33,9 @@ data "coder_git_auth" "github" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "git-clone" {
|
module "git-clone" {
|
||||||
source = "registry.coder.com/modules/git-clone/coder"
|
source = "https://registry.coder.com/modules/git-clone"
|
||||||
version = "1.0.0"
|
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"
|
||||||
path = "~/projects/coder/coder"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
coder = {
|
coder = {
|
||||||
source = "coder/coder"
|
source = "coder/coder"
|
||||||
version = ">= 0.12"
|
version = ">= 0.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ variable "agent_id" {
|
|||||||
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 != "" ? join("/", [var.path, replace(basename(var.url), ".git", "")]) : join("/", ["~", replace(basename(var.url), ".git", "")])
|
CLONE_PATH : var.path != "" ? var.path : join("/", ["~", basename(var.url)]),
|
||||||
REPO_URL : var.url,
|
REPO_URL : var.url,
|
||||||
})
|
})
|
||||||
display_name = "Git Clone"
|
display_name = "Git Clone"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ if [ -z "$CLONE_PATH" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if `git` is installed...
|
# Check if `git` is installed...
|
||||||
if ! command -v git > /dev/null; then
|
if ! command -v git >/dev/null; then
|
||||||
echo "Git is not installed!"
|
echo "Git is not installed!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
display_name: Git commit signing
|
|
||||||
description: Configures Git to sign commits using your Coder SSH key
|
|
||||||
icon: ../.icons/git.svg
|
|
||||||
maintainer_github: phorcys420
|
|
||||||
verified: false
|
|
||||||
tags: [helper, git]
|
|
||||||
---
|
|
||||||
|
|
||||||
# git-commit-signing
|
|
||||||
|
|
||||||
This module downloads your SSH key from Coder and uses it to sign commits with Git.
|
|
||||||
It requires `curl` and `jq` to be installed inside your workspace.
|
|
||||||
|
|
||||||
Please observe that using the SSH key that's part of your Coder account for commit signing, means that in the event of a breach of your Coder account, or a malicious admin, someone could perform commit signing pretending to be you.
|
|
||||||
|
|
||||||
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
|
|
||||||
module "git-commit-signing" {
|
|
||||||
source = "registry.coder.com/modules/git-commit-signing/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
terraform {
|
|
||||||
required_version = ">= 1.0"
|
|
||||||
|
|
||||||
required_providers {
|
|
||||||
coder = {
|
|
||||||
source = "coder/coder"
|
|
||||||
version = ">= 0.12"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "agent_id" {
|
|
||||||
type = string
|
|
||||||
description = "The ID of a Coder agent."
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_script" "git-commit-signing" {
|
|
||||||
display_name = "Git commit signing"
|
|
||||||
icon = "https://raw.githubusercontent.com/coder/modules/main/.icons/git.svg"
|
|
||||||
|
|
||||||
script = file("${path.module}/run.sh")
|
|
||||||
run_on_start = true
|
|
||||||
|
|
||||||
agent_id = var.agent_id
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
if ! command -v git > /dev/null; then
|
|
||||||
echo "git is not installed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v curl > /dev/null; then
|
|
||||||
echo "curl is not installed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v jq > /dev/null; then
|
|
||||||
echo "jq is not installed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p ~/.ssh/git-commit-signing
|
|
||||||
|
|
||||||
echo "Downloading SSH key"
|
|
||||||
|
|
||||||
ssh_key=$(curl --request GET \
|
|
||||||
--url "${CODER_AGENT_URL}api/v2/workspaceagents/me/gitsshkey" \
|
|
||||||
--header "Coder-Session-Token: ${CODER_AGENT_TOKEN}")
|
|
||||||
|
|
||||||
jq --raw-output ".public_key" > ~/.ssh/git-commit-signing/coder.pub << EOF
|
|
||||||
$ssh_key
|
|
||||||
EOF
|
|
||||||
|
|
||||||
jq --raw-output ".private_key" > ~/.ssh/git-commit-signing/coder << EOF
|
|
||||||
$ssh_key
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod -R 400 ~/.ssh/git-commit-signing/coder
|
|
||||||
chmod -R 400 ~/.ssh/git-commit-signing/coder.pub
|
|
||||||
|
|
||||||
echo "Configuring git to use the SSH key"
|
|
||||||
|
|
||||||
git config --global gpg.format ssh
|
|
||||||
git config --global commit.gpgsign true
|
|
||||||
git config --global user.signingkey ~/.ssh/git-commit-signing/coder
|
|
||||||
@@ -13,8 +13,7 @@ Runs a script that updates git credentials in the workspace to match the user's
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "git-config" {
|
module "git-config" {
|
||||||
source = "registry.coder.com/modules/git-config/coder"
|
source = "https://registry.coder.com/modules/git-config"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -27,8 +26,7 @@ TODO: Add screenshot
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "git-config" {
|
module "git-config" {
|
||||||
source = "registry.coder.com/modules/git-config/coder"
|
source = "https://registry.coder.com/modules/git-config"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
allow_email_change = true
|
allow_email_change = true
|
||||||
}
|
}
|
||||||
@@ -40,8 +38,7 @@ TODO: Add screenshot
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "git-config" {
|
module "git-config" {
|
||||||
source = "registry.coder.com/modules/git-config/coder"
|
source = "https://registry.coder.com/modules/git-config"
|
||||||
version = "1.0.0"
|
|
||||||
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
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ BOLD='\033[0;1m'
|
|||||||
printf "$${BOLD}Checking git-config!\n"
|
printf "$${BOLD}Checking git-config!\n"
|
||||||
|
|
||||||
# Check if git is installed
|
# Check if git is installed
|
||||||
command -v git > /dev/null 2>&1 || {
|
command -v git >/dev/null 2>&1 || {
|
||||||
echo "Git is not installed!"
|
echo "Git is not installed!"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set git username and email if missing
|
# Set git username and email if missing
|
||||||
if [ -z $(git config --get user.email) ]; then
|
if [ -z $(git config --get user.email) ]; then
|
||||||
printf "git-config: No user.email found, setting to ${GIT_EMAIL}\n"
|
printf "git-config: No user.email found, setting to ${GIT_EMAIL}\n"
|
||||||
git config --global user.email "${GIT_EMAIL}"
|
git config --global user.email "${GIT_EMAIL}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z $(git config --get user.name) ]; then
|
if [ -z $(git config --get user.name) ]; then
|
||||||
printf "git-config: No user.name found, setting to ${GIT_USERNAME}\n"
|
printf "git-config: No user.name found, setting to ${GIT_USERNAME}\n"
|
||||||
git config --global user.name "${GIT_USERNAME}"
|
git config --global user.name "${GIT_USERNAME}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf "\n$${BOLD}git-config: using email: $(git config --get user.email)\n"
|
printf "\n$${BOLD}git-config: using email: $(git config --get user.email)\n"
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ This module adds a JetBrains Gateway Button to open any workspace with a single
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "jetbrains_gateway" {
|
module "jetbrains_gateway" {
|
||||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
source = "https://registry.coder.com/modules/jetbrains-gateway"
|
||||||
version = "1.0.0"
|
|
||||||
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", "IC", "PY", "PC", "PS", "CL", "RM", "DB", "RD"]
|
||||||
default = "PY"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -30,9 +29,9 @@ module "jetbrains_gateway" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "jetbrains_gateway" {
|
module "jetbrains_gateway" {
|
||||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
source = "https://registry.coder.com/modules/jetbrains-gateway"
|
||||||
version = "1.0.0"
|
|
||||||
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"
|
||||||
@@ -46,7 +45,11 @@ This module and JetBrains Gateway support the following JetBrains IDEs:
|
|||||||
- GoLand (`GO`)
|
- GoLand (`GO`)
|
||||||
- WebStorm (`WS`)
|
- WebStorm (`WS`)
|
||||||
- IntelliJ IDEA Ultimate (`IU`)
|
- IntelliJ IDEA Ultimate (`IU`)
|
||||||
|
- IntelliJ IDEA Community (`IC`)
|
||||||
- PyCharm Professional (`PY`)
|
- PyCharm Professional (`PY`)
|
||||||
|
- PyCharm Community (`PC`)
|
||||||
- PhpStorm (`PS`)
|
- PhpStorm (`PS`)
|
||||||
- CLion (`CL`)
|
- CLion (`CL`)
|
||||||
- RubyMine (`RM`)
|
- RubyMine (`RM`)
|
||||||
|
- DataGrip (`DB`)
|
||||||
|
- Rider (`RD`)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { it, expect, describe } from "bun:test";
|
|||||||
import {
|
import {
|
||||||
runTerraformInit,
|
runTerraformInit,
|
||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
|
executeScriptInContainer,
|
||||||
runTerraformApply,
|
runTerraformApply,
|
||||||
} from "../test";
|
} from "../test";
|
||||||
|
|
||||||
@@ -10,19 +11,20 @@ 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: "bar",
|
||||||
folder: "/baz/",
|
folder: "/baz/",
|
||||||
|
jetbrains_ides: '["IU", "IC", "PY"]',
|
||||||
});
|
});
|
||||||
|
|
||||||
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: "bar",
|
||||||
folder: "/baz/",
|
folder: "/baz/",
|
||||||
jetbrains_ides: '["IU", "GO", "PY"]',
|
jetbrains_ides: '["IU", "IC", "PY"]',
|
||||||
});
|
});
|
||||||
expect(state.outputs.jetbrains_ides.value).toBe(
|
expect(state.outputs.jetbrains_ides.value).toBe(
|
||||||
'["IU","232.10203.10","https://download.jetbrains.com/idea/ideaIU-2023.2.4.tar.gz"]',
|
'["IU","232.9921.47","https://download.jetbrains.com/idea/ideaIU-2023.2.2.tar.gz"]',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ variable "agent_id" {
|
|||||||
|
|
||||||
variable "agent_name" {
|
variable "agent_name" {
|
||||||
type = string
|
type = string
|
||||||
description = "Agent name."
|
description = "The name of a Coder agent."
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "folder" {
|
variable "folder" {
|
||||||
@@ -33,14 +33,13 @@ variable "default" {
|
|||||||
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."
|
||||||
default = ["IU", "PS", "WS", "PY", "CL", "GO", "RM"]
|
|
||||||
validation {
|
validation {
|
||||||
condition = (
|
condition = (
|
||||||
alltrue([
|
alltrue([
|
||||||
for code in var.jetbrains_ides : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM"], code)
|
for code in var.jetbrains_ides : contains(["IU", "IC", "PS", "WS", "PY", "PC", "CL", "GO", "DB", "RD", "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: IU, IC, PS, WS, PY, PC, CL, GO, DB, RD, RM."
|
||||||
}
|
}
|
||||||
# check if the list is empty
|
# check if the list is empty
|
||||||
validation {
|
validation {
|
||||||
@@ -59,37 +58,57 @@ 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"])
|
value = jsonencode(["GO", "232.9921.53", "https://download.jetbrains.com/go/goland-2023.2.2.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"])
|
value = jsonencode(["WS", "232.9921.42", "https://download.jetbrains.com/webstorm/WebStorm-2023.2.2.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"])
|
value = jsonencode(["IU", "232.9921.47", "https://download.jetbrains.com/idea/ideaIU-2023.2.2.tar.gz"])
|
||||||
|
},
|
||||||
|
"IC" = {
|
||||||
|
icon = "/icon/intellij.svg",
|
||||||
|
name = "IntelliJ IDEA Community",
|
||||||
|
value = jsonencode(["IC", "232.9921.47", "https://download.jetbrains.com/idea/ideaIC-2023.2.2.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"])
|
value = jsonencode(["PY", "232.9559.58", "https://download.jetbrains.com/python/pycharm-professional-2023.2.1.tar.gz"])
|
||||||
},
|
},
|
||||||
|
"PC" = {
|
||||||
|
icon = "/icon/pycharm.svg",
|
||||||
|
name = "PyCharm Community",
|
||||||
|
value = jsonencode(["PC", "232.9559.58", "https://download.jetbrains.com/python/pycharm-community-2023.2.1.tar.gz"])
|
||||||
|
},
|
||||||
|
"RD" = {
|
||||||
|
icon = "/icon/rider.svg",
|
||||||
|
name = "Rider",
|
||||||
|
value = jsonencode(["RD", "232.9559.61", "https://download.jetbrains.com/rider/JetBrains.Rider-2023.2.1.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"])
|
value = jsonencode(["CL", "232.9921.42", "https://download.jetbrains.com/cpp/CLion-2023.2.2.tar.gz"])
|
||||||
},
|
},
|
||||||
|
"DB" = {
|
||||||
|
icon = "/icon/datagrip.svg",
|
||||||
|
name = "DataGrip",
|
||||||
|
value = jsonencode(["DB", "232.9559.28", "https://download.jetbrains.com/datagrip/datagrip-2023.2.1.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"])
|
value = jsonencode(["PS", "232.9559.64", "https://download.jetbrains.com/webide/PhpStorm-2023.2.1.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"])
|
value = jsonencode(["RM", "232.9921.48", "https://download.jetbrains.com/ruby/RubyMine-2023.2.2.tar.gz"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
---
|
|
||||||
display_name: JFrog (OAuth)
|
|
||||||
description: Install the JF CLI and authenticate with Artifactory using OAuth.
|
|
||||||
icon: ../.icons/jfrog.svg
|
|
||||||
maintainer_github: coder
|
|
||||||
partner_github: jfrog
|
|
||||||
verified: true
|
|
||||||
tags: [integration, jfrog]
|
|
||||||
---
|
|
||||||
|
|
||||||
# JFrog
|
|
||||||
|
|
||||||
Install the JF CLI and authenticate package managers with Artifactory using OAuth configured via the Coder `external-auth` feature.
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<img src='../.images/jfrog-oauth.png' alt="JFrog OAuth" width='600'>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "jfrog" {
|
|
||||||
source = "registry.coder.com/modules/jfrog-oauth/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
jfrog_url = "https://jfrog.example.com"
|
|
||||||
username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username"
|
|
||||||
package_managers = {
|
|
||||||
"npm": "npm",
|
|
||||||
"go": "go",
|
|
||||||
"pypi": "pypi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Note
|
|
||||||
> This module does not install `npm`, `go`, `pip`, etc but only configure them. You need to handle the installation of these tools yourself.
|
|
||||||
|
|
||||||
## 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`.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Configure the Python pip package manager to fetch packages from Artifactory while mapping the Coder email to the Artifactory username.
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "jfrog" {
|
|
||||||
source = "registry.coder.com/modules/jfrog-oauth/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
jfrog_url = "https://jfrog.example.com"
|
|
||||||
auth_method = "oauth"
|
|
||||||
username_field = "email"
|
|
||||||
package_managers = {
|
|
||||||
"pypi": "pypi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You should now be able to install packages from Artifactory using both the `jf pip` and `pip` command.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
jf pip install requests
|
|
||||||
```
|
|
||||||
|
|
||||||
```shell
|
|
||||||
pip install requests
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure code-server with JFrog extension
|
|
||||||
|
|
||||||
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
|
|
||||||
module "jfrog" {
|
|
||||||
source = "registry.coder.com/modules/jfrog-oauth/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
jfrog_url = "https://jfrog.example.com"
|
|
||||||
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
|
|
||||||
package_managers = {
|
|
||||||
"npm": "npm",
|
|
||||||
"go": "go",
|
|
||||||
"pypi": "pypi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the access token in other terraform resources
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
provider "docker" {
|
|
||||||
...
|
|
||||||
registry_auth {
|
|
||||||
address = "https://YYYY.jfrog.io/artifactory/api/docker/REPO-KEY"
|
|
||||||
username = module.jfrog.username
|
|
||||||
password = module.jfrog.access_token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { serve } from "bun";
|
|
||||||
import { describe } from "bun:test";
|
|
||||||
import {
|
|
||||||
createJSONResponse,
|
|
||||||
runTerraformInit,
|
|
||||||
testRequiredVariables,
|
|
||||||
} from "../test";
|
|
||||||
|
|
||||||
describe("jfrog-oauth", async () => {
|
|
||||||
await runTerraformInit(import.meta.dir);
|
|
||||||
|
|
||||||
testRequiredVariables(import.meta.dir, {
|
|
||||||
agent_id: "some-agent-id",
|
|
||||||
jfrog_url: "http://localhost:8081",
|
|
||||||
package_managers: "{}",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//TODO add more tests
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
terraform {
|
|
||||||
required_version = ">= 1.0"
|
|
||||||
|
|
||||||
required_providers {
|
|
||||||
coder = {
|
|
||||||
source = "coder/coder"
|
|
||||||
version = ">= 0.12.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "jfrog_url" {
|
|
||||||
type = string
|
|
||||||
description = "JFrog instance URL. e.g. https://myartifactory.jfrog.io"
|
|
||||||
# ensue the URL is HTTPS or HTTP
|
|
||||||
validation {
|
|
||||||
condition = can(regex("^(https|http)://", var.jfrog_url))
|
|
||||||
error_message = "jfrog_url must be a valid URL starting with either 'https://' or 'http://'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "username_field" {
|
|
||||||
type = string
|
|
||||||
description = "The field to use for the artifactory username. i.e. Coder username or email."
|
|
||||||
default = "username"
|
|
||||||
validation {
|
|
||||||
condition = can(regex("^(email|username)$", var.username_field))
|
|
||||||
error_message = "username_field must be either 'email' or 'username'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "external_auth_id" {
|
|
||||||
type = string
|
|
||||||
description = "JFrog external auth ID. Default: 'jfrog'"
|
|
||||||
default = "jfrog"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "agent_id" {
|
|
||||||
type = string
|
|
||||||
description = "The ID of a Coder agent."
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "configure_code_server" {
|
|
||||||
type = bool
|
|
||||||
description = "Set to true to configure code-server to use JFrog."
|
|
||||||
default = false
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "package_managers" {
|
|
||||||
type = map(string)
|
|
||||||
description = <<EOF
|
|
||||||
A map of package manager names to their respective artifactory repositories.
|
|
||||||
For example:
|
|
||||||
{
|
|
||||||
"npm": "YOUR_NPM_REPO_KEY",
|
|
||||||
"go": "YOUR_GO_REPO_KEY",
|
|
||||||
"pypi": "YOUR_PYPI_REPO_KEY",
|
|
||||||
"docker": "YOUR_DOCKER_REPO_KEY"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
locals {
|
|
||||||
# The username field to use for artifactory
|
|
||||||
username = var.username_field == "email" ? data.coder_workspace.me.owner_email : data.coder_workspace.me.owner
|
|
||||||
jfrog_host = replace(var.jfrog_url, "https://", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
data "coder_workspace" "me" {}
|
|
||||||
|
|
||||||
data "coder_external_auth" "jfrog" {
|
|
||||||
id = var.external_auth_id
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_script" "jfrog" {
|
|
||||||
agent_id = var.agent_id
|
|
||||||
display_name = "jfrog"
|
|
||||||
icon = "/icon/jfrog.svg"
|
|
||||||
script = templatefile("${path.module}/run.sh", {
|
|
||||||
JFROG_URL : var.jfrog_url,
|
|
||||||
JFROG_HOST : local.jfrog_host,
|
|
||||||
ARTIFACTORY_USERNAME : local.username,
|
|
||||||
ARTIFACTORY_EMAIL : data.coder_workspace.me.owner_email,
|
|
||||||
ARTIFACTORY_ACCESS_TOKEN : data.coder_external_auth.jfrog.access_token,
|
|
||||||
CONFIGURE_CODE_SERVER : var.configure_code_server,
|
|
||||||
REPOSITORY_NPM : lookup(var.package_managers, "npm", ""),
|
|
||||||
REPOSITORY_GO : lookup(var.package_managers, "go", ""),
|
|
||||||
REPOSITORY_PYPI : lookup(var.package_managers, "pypi", ""),
|
|
||||||
REPOSITORY_DOCKER : lookup(var.package_managers, "docker", ""),
|
|
||||||
})
|
|
||||||
run_on_start = true
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "jfrog_ide_url" {
|
|
||||||
count = var.configure_code_server ? 1 : 0
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "JFROG_IDE_URL"
|
|
||||||
value = var.jfrog_url
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "jfrog_ide_access_token" {
|
|
||||||
count = var.configure_code_server ? 1 : 0
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "JFROG_IDE_ACCESS_TOKEN"
|
|
||||||
value = data.coder_external_auth.jfrog.access_token
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "jfrog_ide_store_connection" {
|
|
||||||
count = var.configure_code_server ? 1 : 0
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "JFROG_IDE_STORE_CONNECTION"
|
|
||||||
value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "goproxy" {
|
|
||||||
count = lookup(var.package_managers, "go", "") == "" ? 0 : 1
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "GOPROXY"
|
|
||||||
value = "https://${local.username}:${data.coder_external_auth.jfrog.access_token}@${local.jfrog_host}/artifactory/api/go/${lookup(var.package_managers, "go", "")}"
|
|
||||||
}
|
|
||||||
|
|
||||||
output "access_token" {
|
|
||||||
description = "value of the JFrog access token"
|
|
||||||
value = data.coder_external_auth.jfrog.access_token
|
|
||||||
sensitive = true
|
|
||||||
}
|
|
||||||
|
|
||||||
output "username" {
|
|
||||||
description = "value of the JFrog username"
|
|
||||||
value = local.username
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
BOLD='\033[0;1m'
|
|
||||||
|
|
||||||
# check if JFrog CLI is already installed
|
|
||||||
if command -v jf > /dev/null 2>&1; then
|
|
||||||
echo "✅ JFrog CLI is already installed, skipping installation."
|
|
||||||
else
|
|
||||||
echo "📦 Installing JFrog CLI..."
|
|
||||||
curl -fL https://install-cli.jfrog.io | sudo sh
|
|
||||||
sudo chmod 755 /usr/local/bin/jf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# The jf CLI checks $CI when determining whether to use interactive
|
|
||||||
# flows.
|
|
||||||
export CI=true
|
|
||||||
# Authenticate JFrog CLI with Artifactory.
|
|
||||||
echo "${ARTIFACTORY_ACCESS_TOKEN}" | jf c add --access-token-stdin --url "${JFROG_URL}" --overwrite 0
|
|
||||||
# Set the configured server as the default.
|
|
||||||
jf c use 0
|
|
||||||
|
|
||||||
# Configure npm to use the Artifactory "npm" repository.
|
|
||||||
if [ -z "${REPOSITORY_NPM}" ]; then
|
|
||||||
echo "🤔 no npm repository is set, skipping npm configuration."
|
|
||||||
echo "You can configure an npm repository by providing the a key for 'npm' in the 'package_managers' input."
|
|
||||||
else
|
|
||||||
echo "📦 Configuring npm..."
|
|
||||||
jf npmc --global --repo-resolve "${REPOSITORY_NPM}"
|
|
||||||
cat << EOF > ~/.npmrc
|
|
||||||
email=${ARTIFACTORY_EMAIL}
|
|
||||||
registry=${JFROG_URL}/artifactory/api/npm/${REPOSITORY_NPM}
|
|
||||||
EOF
|
|
||||||
echo "//${JFROG_HOST}/artifactory/api/npm/${REPOSITORY_NPM}/:_authToken=${ARTIFACTORY_ACCESS_TOKEN}" >> ~/.npmrc
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure the `pip` to use the Artifactory "python" repository.
|
|
||||||
if [ -z "${REPOSITORY_PYPI}" ]; then
|
|
||||||
echo "🤔 no pypi repository is set, skipping pip configuration."
|
|
||||||
echo "You can configure a pypi repository by providing the a key for 'pypi' in the 'package_managers' input."
|
|
||||||
else
|
|
||||||
echo "📦 Configuring pip..."
|
|
||||||
jf pipc --global --repo-resolve "${REPOSITORY_PYPI}"
|
|
||||||
mkdir -p ~/.pip
|
|
||||||
cat << EOF > ~/.pip/pip.conf
|
|
||||||
[global]
|
|
||||||
index-url = https://${ARTIFACTORY_USERNAME}:${ARTIFACTORY_ACCESS_TOKEN}@${JFROG_HOST}/artifactory/api/pypi/${REPOSITORY_PYPI}/simple
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure Artifactory "go" repository.
|
|
||||||
if [ -z "${REPOSITORY_GO}" ]; then
|
|
||||||
echo "🤔 no go repository is set, skipping go configuration."
|
|
||||||
echo "You can configure a go repository by providing the a key for 'go' in the 'package_managers' input."
|
|
||||||
else
|
|
||||||
echo "🐹 Configuring go..."
|
|
||||||
jf goc --global --repo-resolve "${REPOSITORY_GO}"
|
|
||||||
fi
|
|
||||||
echo "🥳 Configuration complete!"
|
|
||||||
|
|
||||||
# Configure the JFrog CLI to use the Artifactory "docker" repository.
|
|
||||||
if [ -z "${REPOSITORY_DOCKER}" ]; then
|
|
||||||
echo "🤔 no docker repository is set, skipping docker configuration."
|
|
||||||
echo "You can configure a docker repository by providing the a key for 'docker' in the 'package_managers' input."
|
|
||||||
else
|
|
||||||
if command -v docker > /dev/null 2>&1; then
|
|
||||||
echo "🔑 Configuring 🐳 docker credentials..."
|
|
||||||
mkdir -p ~/.docker
|
|
||||||
echo -n "${ARTIFACTORY_ACCESS_TOKEN}" | docker login ${JFROG_HOST} --username ${ARTIFACTORY_USERNAME} --password-stdin
|
|
||||||
else
|
|
||||||
echo "🤔 no docker is installed, skipping docker configuration."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install the JFrog vscode extension for code-server.
|
|
||||||
if [ "${CONFIGURE_CODE_SERVER}" == "true" ]; then
|
|
||||||
while ! [ -x /tmp/code-server/bin/code-server ]; do
|
|
||||||
counter=0
|
|
||||||
if [ $counter -eq 60 ]; then
|
|
||||||
echo "Timed out waiting for /tmp/code-server/bin/code-server to be installed."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Waiting for /tmp/code-server/bin/code-server to be installed..."
|
|
||||||
sleep 1
|
|
||||||
((counter++))
|
|
||||||
done
|
|
||||||
echo "📦 Installing JFrog extension..."
|
|
||||||
/tmp/code-server/bin/code-server --install-extension jfrog.jfrog-vscode-extension
|
|
||||||
echo "🥳 JFrog extension installed!"
|
|
||||||
else
|
|
||||||
echo "🤔 Skipping JFrog extension installation. Set configure_code_server to true to install the JFrog extension."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure the JFrog CLI completion
|
|
||||||
echo "📦 Configuring JFrog CLI completion..."
|
|
||||||
# Get the user's shell
|
|
||||||
SHELLNAME=$(grep "^$USER" /etc/passwd | awk -F':' '{print $7}' | awk -F'/' '{print $NF}')
|
|
||||||
# Generate the completion script
|
|
||||||
jf completion $SHELLNAME --install
|
|
||||||
# Add the completion script to the user's shell profile
|
|
||||||
if [ "$SHELLNAME" == "bash" ] && [ -f ~/.bashrc ]; then
|
|
||||||
if ! grep -q "# jf CLI shell completion" ~/.bashrc; then
|
|
||||||
echo "" >> ~/.bashrc
|
|
||||||
echo "# BEGIN: jf CLI shell completion (added by coder module jfrog-oauth)" >> ~/.bashrc
|
|
||||||
echo 'source "$HOME/.jfrog/jfrog_bash_completion"' >> ~/.bashrc
|
|
||||||
echo "# END: jf CLI shell completion" >> ~/.bashrc
|
|
||||||
else
|
|
||||||
echo "🥳 ~/.bashrc already contains jf CLI shell completion configuration, skipping."
|
|
||||||
fi
|
|
||||||
elif [ "$SHELLNAME" == "zsh" ] && [ -f ~/.zshrc ]; then
|
|
||||||
if ! grep -q "# jf CLI shell completion" ~/.zshrc; then
|
|
||||||
echo "" >> ~/.zshrc
|
|
||||||
echo "# BEGIN: jf CLI shell completion (added by coder module jfrog-oauth)" >> ~/.zshrc
|
|
||||||
echo "autoload -Uz compinit" >> ~/.zshrc
|
|
||||||
echo "compinit" >> ~/.zshrc
|
|
||||||
echo 'source "$HOME/.jfrog/jfrog_zsh_completion"' >> ~/.zshrc
|
|
||||||
echo "# END: jf CLI shell completion" >> ~/.zshrc
|
|
||||||
else
|
|
||||||
echo "🥳 ~/.zshrc already contains jf CLI shell completion configuration, skipping."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "🤔 ~/.bashrc or ~/.zshrc does not exist, skipping jf CLI shell completion configuration."
|
|
||||||
fi
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
---
|
|
||||||
display_name: JFrog (Token)
|
|
||||||
description: Install the JF CLI and authenticate with Artifactory using Artifactory terraform provider.
|
|
||||||
icon: ../.icons/jfrog.svg
|
|
||||||
maintainer_github: coder
|
|
||||||
partner_github: jfrog
|
|
||||||
verified: true
|
|
||||||
tags: [integration, jfrog]
|
|
||||||
---
|
|
||||||
|
|
||||||
# JFrog
|
|
||||||
|
|
||||||
Install the JF CLI and authenticate package managers with Artifactory using Artifactory terraform provider.
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "jfrog" {
|
|
||||||
source = "registry.coder.com/modules/jfrog-token/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
jfrog_url = "https://XXXX.jfrog.io"
|
|
||||||
artifactory_access_token = var.artifactory_access_token
|
|
||||||
package_managers = {
|
|
||||||
"npm": "npm",
|
|
||||||
"go": "go",
|
|
||||||
"pypi": "pypi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
variable "artifactory_access_token" {
|
|
||||||
type = string
|
|
||||||
sensitive = true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Note
|
|
||||||
> This module does not install `npm`, `go`, `pip`, etc but only configure them. You need to handle the installation of these tools yourself.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Configure npm, go, and pypi to use Artifactory local repositories
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "jfrog" {
|
|
||||||
source = "registry.coder.com/modules/jfrog-token/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
jfrog_url = "https://YYYY.jfrog.io"
|
|
||||||
artifactory_access_token = var.artifactory_access_token # An admin access token
|
|
||||||
package_managers = {
|
|
||||||
"npm": "npm-local",
|
|
||||||
"go": "go-local",
|
|
||||||
"pypi": "pypi-local"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You should now be able to install packages from Artifactory using both the `jf npm`, `jf go`, `jf pip` and `npm`, `go`, `pip` commands.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
jf npm install prettier
|
|
||||||
jf go get github.com/golang/example/hello
|
|
||||||
jf pip install requests
|
|
||||||
```
|
|
||||||
|
|
||||||
```shell
|
|
||||||
npm install prettier
|
|
||||||
go get github.com/golang/example/hello
|
|
||||||
pip install requests
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure code-server with JFrog extension
|
|
||||||
|
|
||||||
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
|
|
||||||
module "jfrog" {
|
|
||||||
source = "registry.coder.com/modules/jfrog-token/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
jfrog_url = "https://XXXX.jfrog.io"
|
|
||||||
artifactory_access_token = var.artifactory_access_token
|
|
||||||
configure_code_server = true # Add JFrog extension configuration for code-server
|
|
||||||
package_managers = {
|
|
||||||
"npm": "npm",
|
|
||||||
"go": "go",
|
|
||||||
"pypi": "pypi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the access token in other terraform resources
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
provider "docker" {
|
|
||||||
...
|
|
||||||
registry_auth {
|
|
||||||
address = "https://YYYY.jfrog.io/artifactory/api/docker/REPO-KEY"
|
|
||||||
username = module.jfrog.username
|
|
||||||
password = module.jfrog.access_token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
terraform {
|
|
||||||
required_version = ">= 1.0"
|
|
||||||
|
|
||||||
required_providers {
|
|
||||||
coder = {
|
|
||||||
source = "coder/coder"
|
|
||||||
version = ">= 0.12.4"
|
|
||||||
}
|
|
||||||
artifactory = {
|
|
||||||
source = "registry.terraform.io/jfrog/artifactory"
|
|
||||||
version = "~> 10.0.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "jfrog_url" {
|
|
||||||
type = string
|
|
||||||
description = "JFrog instance URL. e.g. https://myartifactory.jfrog.io"
|
|
||||||
# ensue the URL is HTTPS or HTTP
|
|
||||||
validation {
|
|
||||||
condition = can(regex("^(https|http)://", var.jfrog_url))
|
|
||||||
error_message = "jfrog_url must be a valid URL starting with either 'https://' or 'http://'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "artifactory_access_token" {
|
|
||||||
type = string
|
|
||||||
description = "The admin-level access token to use for JFrog."
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "check_license" {
|
|
||||||
type = bool
|
|
||||||
description = "Toggle for pre-flight checking of Artifactory license. Default to `true`."
|
|
||||||
default = true
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "refreshable" {
|
|
||||||
type = bool
|
|
||||||
description = "Is this token refreshable? Default is `false`."
|
|
||||||
default = false
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "expires_in" {
|
|
||||||
type = number
|
|
||||||
description = "The amount of time, in seconds, it would take for the token to expire."
|
|
||||||
default = null
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "username_field" {
|
|
||||||
type = string
|
|
||||||
description = "The field to use for the artifactory username. Default `username`."
|
|
||||||
default = "username"
|
|
||||||
validation {
|
|
||||||
condition = can(regex("^(email|username)$", var.username_field))
|
|
||||||
error_message = "username_field must be either 'email' or 'username'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "agent_id" {
|
|
||||||
type = string
|
|
||||||
description = "The ID of a Coder agent."
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "configure_code_server" {
|
|
||||||
type = bool
|
|
||||||
description = "Set to true to configure code-server to use JFrog."
|
|
||||||
default = false
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "package_managers" {
|
|
||||||
type = map(string)
|
|
||||||
description = <<EOF
|
|
||||||
A map of package manager names to their respective artifactory repositories.
|
|
||||||
For example:
|
|
||||||
{
|
|
||||||
"npm": "YOUR_NPM_REPO_KEY",
|
|
||||||
"go": "YOUR_GO_REPO_KEY",
|
|
||||||
"pypi": "YOUR_PYPI_REPO_KEY",
|
|
||||||
"docker": "YOUR_DOCKER_REPO_KEY"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
locals {
|
|
||||||
# The username field to use for artifactory
|
|
||||||
username = var.username_field == "email" ? data.coder_workspace.me.owner_email : data.coder_workspace.me.owner
|
|
||||||
jfrog_host = replace(var.jfrog_url, "https://", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configure the Artifactory provider
|
|
||||||
provider "artifactory" {
|
|
||||||
url = join("/", [var.jfrog_url, "artifactory"])
|
|
||||||
access_token = var.artifactory_access_token
|
|
||||||
check_license = var.check_license
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "artifactory_scoped_token" "me" {
|
|
||||||
# This is hacky, but on terraform plan the data source gives empty strings,
|
|
||||||
# which fails validation.
|
|
||||||
username = length(local.username) > 0 ? local.username : "dummy"
|
|
||||||
scopes = ["applied-permissions/user"]
|
|
||||||
refreshable = var.refreshable
|
|
||||||
expires_in = var.expires_in
|
|
||||||
}
|
|
||||||
|
|
||||||
data "coder_workspace" "me" {}
|
|
||||||
|
|
||||||
resource "coder_script" "jfrog" {
|
|
||||||
agent_id = var.agent_id
|
|
||||||
display_name = "jfrog"
|
|
||||||
icon = "/icon/jfrog.svg"
|
|
||||||
script = templatefile("${path.module}/run.sh", {
|
|
||||||
JFROG_URL : var.jfrog_url,
|
|
||||||
JFROG_HOST : local.jfrog_host,
|
|
||||||
ARTIFACTORY_USERNAME : local.username,
|
|
||||||
ARTIFACTORY_EMAIL : data.coder_workspace.me.owner_email,
|
|
||||||
ARTIFACTORY_ACCESS_TOKEN : artifactory_scoped_token.me.access_token,
|
|
||||||
CONFIGURE_CODE_SERVER : var.configure_code_server,
|
|
||||||
REPOSITORY_NPM : lookup(var.package_managers, "npm", ""),
|
|
||||||
REPOSITORY_GO : lookup(var.package_managers, "go", ""),
|
|
||||||
REPOSITORY_PYPI : lookup(var.package_managers, "pypi", ""),
|
|
||||||
REPOSITORY_DOCKER : lookup(var.package_managers, "docker", ""),
|
|
||||||
})
|
|
||||||
run_on_start = true
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "jfrog_ide_url" {
|
|
||||||
count = var.configure_code_server ? 1 : 0
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "JFROG_IDE_URL"
|
|
||||||
value = var.jfrog_url
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "jfrog_ide_access_token" {
|
|
||||||
count = var.configure_code_server ? 1 : 0
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "JFROG_IDE_ACCESS_TOKEN"
|
|
||||||
value = artifactory_scoped_token.me.access_token
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "jfrog_ide_store_connection" {
|
|
||||||
count = var.configure_code_server ? 1 : 0
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "JFROG_IDE_STORE_CONNECTION"
|
|
||||||
value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "goproxy" {
|
|
||||||
count = lookup(var.package_managers, "go", "") == "" ? 0 : 1
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "GOPROXY"
|
|
||||||
value = "https://${local.username}:${artifactory_scoped_token.me.access_token}@${local.jfrog_host}/artifactory/api/go/${lookup(var.package_managers, "go", "")}"
|
|
||||||
}
|
|
||||||
|
|
||||||
output "access_token" {
|
|
||||||
description = "value of the JFrog access token"
|
|
||||||
value = artifactory_scoped_token.me.access_token
|
|
||||||
sensitive = true
|
|
||||||
}
|
|
||||||
|
|
||||||
output "username" {
|
|
||||||
description = "value of the JFrog username"
|
|
||||||
value = local.username
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
BOLD='\033[0;1m'
|
|
||||||
|
|
||||||
# check if JFrog CLI is already installed
|
|
||||||
if command -v jf > /dev/null 2>&1; then
|
|
||||||
echo "✅ JFrog CLI is already installed, skipping installation."
|
|
||||||
else
|
|
||||||
echo "📦 Installing JFrog CLI..."
|
|
||||||
curl -fL https://install-cli.jfrog.io | sudo sh
|
|
||||||
sudo chmod 755 /usr/local/bin/jf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# The jf CLI checks $CI when determining whether to use interactive
|
|
||||||
# flows.
|
|
||||||
export CI=true
|
|
||||||
# Authenticate JFrog CLI with Artifactory.
|
|
||||||
echo "${ARTIFACTORY_ACCESS_TOKEN}" | jf c add --access-token-stdin --url "${JFROG_URL}" --overwrite 0
|
|
||||||
# Set the configured server as the default.
|
|
||||||
jf c use 0
|
|
||||||
|
|
||||||
# Configure npm to use the Artifactory "npm" repository.
|
|
||||||
if [ -z "${REPOSITORY_NPM}" ]; then
|
|
||||||
echo "🤔 no npm repository is set, skipping npm configuration."
|
|
||||||
echo "You can configure an npm repository by providing the a key for 'npm' in the 'package_managers' input."
|
|
||||||
else
|
|
||||||
echo "📦 Configuring npm..."
|
|
||||||
jf npmc --global --repo-resolve "${REPOSITORY_NPM}"
|
|
||||||
cat << EOF > ~/.npmrc
|
|
||||||
email=${ARTIFACTORY_EMAIL}
|
|
||||||
registry=${JFROG_URL}/artifactory/api/npm/${REPOSITORY_NPM}
|
|
||||||
EOF
|
|
||||||
echo "//${JFROG_HOST}/artifactory/api/npm/${REPOSITORY_NPM}/:_authToken=${ARTIFACTORY_ACCESS_TOKEN}" >> ~/.npmrc
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure the `pip` to use the Artifactory "python" repository.
|
|
||||||
if [ -z "${REPOSITORY_PYPI}" ]; then
|
|
||||||
echo "🤔 no pypi repository is set, skipping pip configuration."
|
|
||||||
echo "You can configure a pypi repository by providing the a key for 'pypi' in the 'package_managers' input."
|
|
||||||
else
|
|
||||||
echo "🐍 Configuring pip..."
|
|
||||||
jf pipc --global --repo-resolve "${REPOSITORY_PYPI}"
|
|
||||||
mkdir -p ~/.pip
|
|
||||||
cat << EOF > ~/.pip/pip.conf
|
|
||||||
[global]
|
|
||||||
index-url = https://${ARTIFACTORY_USERNAME}:${ARTIFACTORY_ACCESS_TOKEN}@${JFROG_HOST}/artifactory/api/pypi/${REPOSITORY_PYPI}/simple
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure Artifactory "go" repository.
|
|
||||||
if [ -z "${REPOSITORY_GO}" ]; then
|
|
||||||
echo "🤔 no go repository is set, skipping go configuration."
|
|
||||||
echo "You can configure a go repository by providing the a key for 'go' in the 'package_managers' input."
|
|
||||||
else
|
|
||||||
echo "🐹 Configuring go..."
|
|
||||||
jf goc --global --repo-resolve "${REPOSITORY_GO}"
|
|
||||||
fi
|
|
||||||
echo "🥳 Configuration complete!"
|
|
||||||
|
|
||||||
# Configure the JFrog CLI to use the Artifactory "docker" repository.
|
|
||||||
if [ -z "${REPOSITORY_DOCKER}" ]; then
|
|
||||||
echo "🤔 no docker repository is set, skipping docker configuration."
|
|
||||||
echo "You can configure a docker repository by providing the a key for 'docker' in the 'package_managers' input."
|
|
||||||
else
|
|
||||||
if command -v docker > /dev/null 2>&1; then
|
|
||||||
echo "🔑 Configuring 🐳 docker credentials..."
|
|
||||||
mkdir -p ~/.docker
|
|
||||||
echo -n "${ARTIFACTORY_ACCESS_TOKEN}" | docker login ${JFROG_HOST} --username ${ARTIFACTORY_USERNAME} --password-stdin
|
|
||||||
else
|
|
||||||
echo "🤔 no docker is installed, skipping docker configuration."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install the JFrog vscode extension for code-server.
|
|
||||||
if [ "${CONFIGURE_CODE_SERVER}" == "true" ]; then
|
|
||||||
while ! [ -x /tmp/code-server/bin/code-server ]; do
|
|
||||||
counter=0
|
|
||||||
if [ $counter -eq 60 ]; then
|
|
||||||
echo "Timed out waiting for /tmp/code-server/bin/code-server to be installed."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Waiting for /tmp/code-server/bin/code-server to be installed..."
|
|
||||||
sleep 1
|
|
||||||
((counter++))
|
|
||||||
done
|
|
||||||
echo "📦 Installing JFrog extension..."
|
|
||||||
/tmp/code-server/bin/code-server --install-extension jfrog.jfrog-vscode-extension
|
|
||||||
echo "🥳 JFrog extension installed!"
|
|
||||||
else
|
|
||||||
echo "🤔 Skipping JFrog extension installation. Set configure_code_server to true to install the JFrog extension."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure the JFrog CLI completion
|
|
||||||
echo "📦 Configuring JFrog CLI completion..."
|
|
||||||
# Get the user's shell
|
|
||||||
SHELLNAME=$(grep "^$USER" /etc/passwd | awk -F':' '{print $7}' | awk -F'/' '{print $NF}')
|
|
||||||
# Generate the completion script
|
|
||||||
jf completion $SHELLNAME --install
|
|
||||||
# Add the completion script to the user's shell profile
|
|
||||||
if [ "$SHELLNAME" == "bash" ] && [ -f ~/.bashrc ]; then
|
|
||||||
if ! grep -q "# jf CLI shell completion" ~/.bashrc; then
|
|
||||||
echo "" >> ~/.bashrc
|
|
||||||
echo "# BEGIN: jf CLI shell completion (added by coder module jfrog-token)" >> ~/.bashrc
|
|
||||||
echo 'source "$HOME/.jfrog/jfrog_bash_completion"' >> ~/.bashrc
|
|
||||||
echo "# END: jf CLI shell completion" >> ~/.bashrc
|
|
||||||
else
|
|
||||||
echo "🥳 ~/.bashrc already contains jf CLI shell completion configuration, skipping."
|
|
||||||
fi
|
|
||||||
elif [ "$SHELLNAME" == "zsh" ] && [ -f ~/.zshrc ]; then
|
|
||||||
if ! grep -q "# jf CLI shell completion" ~/.zshrc; then
|
|
||||||
echo "" >> ~/.zshrc
|
|
||||||
echo "# BEGIN: jf CLI shell completion (added by coder module jfrog-token)" >> ~/.zshrc
|
|
||||||
echo "autoload -Uz compinit" >> ~/.zshrc
|
|
||||||
echo "compinit" >> ~/.zshrc
|
|
||||||
echo 'source "$HOME/.jfrog/jfrog_zsh_completion"' >> ~/.zshrc
|
|
||||||
echo "# END: jf CLI shell completion" >> ~/.zshrc
|
|
||||||
else
|
|
||||||
echo "🥳 ~/.zshrc already contains jf CLI shell completion configuration, skipping."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "🤔 ~/.bashrc or ~/.zshrc does not exist, skipping jf CLI shell completion configuration."
|
|
||||||
fi
|
|
||||||
56
jfrog/README.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
display_name: JFrog
|
||||||
|
description: Install the JF CLI and authenticate with Artifactory
|
||||||
|
icon: ../.icons/jfrog.svg
|
||||||
|
maintainer_github: coder
|
||||||
|
partner_github: jfrog
|
||||||
|
verified: true
|
||||||
|
tags: [integration]
|
||||||
|
---
|
||||||
|
|
||||||
|
# JFrog
|
||||||
|
|
||||||
|
Install the JF CLI and authenticate package managers with Artifactory.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "jfrog" {
|
||||||
|
source = "https://registry.coder.com/modules/jfrog"
|
||||||
|
agent_id = coder_agent.example.id
|
||||||
|
jfrog_url = "https://YYYY.jfrog.io"
|
||||||
|
artifactory_access_token = var.artifactory_access_token # An admin access token
|
||||||
|
package_managers = {
|
||||||
|
"npm": "npm-remote",
|
||||||
|
"go": "go-remote",
|
||||||
|
"pypi": "pypi-remote"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Get a JFrog access token from your Artifactory instance. The token must have admin permissions. It is recommended to store the token in a secret terraform variable.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
variable "artifactory_access_token" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Configure npm, go, and pypi to use Artifactory local repositories
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "jfrog" {
|
||||||
|
source = "https://registry.coder.com/modules/jfrog"
|
||||||
|
agent_id = coder_agent.example.id
|
||||||
|
jfrog_url = "https://YYYY.jfrog.io"
|
||||||
|
artifactory_access_token = var.artifactory_access_token # An admin access token
|
||||||
|
package_managers = {
|
||||||
|
"npm": "npm-local",
|
||||||
|
"go": "go-local",
|
||||||
|
"pypi": "pypi-local"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "../test";
|
} from "../test";
|
||||||
|
|
||||||
describe("jfrog-token", async () => {
|
describe("jfrog", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|
||||||
// Run a fake JFrog server so the provider can initialize
|
// Run a fake JFrog server so the provider can initialize
|
||||||
@@ -25,7 +25,7 @@ describe("jfrog-token", async () => {
|
|||||||
return createJSONResponse({
|
return createJSONResponse({
|
||||||
token_id: "xxx",
|
token_id: "xxx",
|
||||||
access_token: "xxx",
|
access_token: "xxx",
|
||||||
scopes: "any",
|
scope: "any",
|
||||||
});
|
});
|
||||||
return createJSONResponse({});
|
return createJSONResponse({});
|
||||||
},
|
},
|
||||||
71
jfrog/main.tf
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = ">= 1.0"
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
coder = {
|
||||||
|
source = "coder/coder"
|
||||||
|
version = ">= 0.12"
|
||||||
|
}
|
||||||
|
artifactory = {
|
||||||
|
source = "registry.terraform.io/jfrog/artifactory"
|
||||||
|
version = "~> 8.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "jfrog_url" {
|
||||||
|
type = string
|
||||||
|
description = "JFrog instance URL. e.g. https://YYY.jfrog.io"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "artifactory_access_token" {
|
||||||
|
type = string
|
||||||
|
description = "The admin-level access token to use for JFrog."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure the Artifactory provider
|
||||||
|
provider "artifactory" {
|
||||||
|
url = join("/", [var.jfrog_url, "artifactory"])
|
||||||
|
access_token = var.artifactory_access_token
|
||||||
|
}
|
||||||
|
resource "artifactory_scoped_token" "me" {
|
||||||
|
# This is hacky, but on terraform plan the data source gives empty strings,
|
||||||
|
# which fails validation.
|
||||||
|
username = length(data.coder_workspace.me.owner_email) > 0 ? data.coder_workspace.me.owner_email : "plan"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "agent_id" {
|
||||||
|
type = string
|
||||||
|
description = "The ID of a Coder agent."
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "package_managers" {
|
||||||
|
type = map(string)
|
||||||
|
description = <<EOF
|
||||||
|
A map of package manager names to their respective artifactory repositories.
|
||||||
|
For example:
|
||||||
|
{
|
||||||
|
"npm": "npm-local",
|
||||||
|
"go": "go-local",
|
||||||
|
"pypi": "pypi-local"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_workspace" "me" {}
|
||||||
|
|
||||||
|
resource "coder_script" "jfrog" {
|
||||||
|
agent_id = var.agent_id
|
||||||
|
display_name = "jfrog"
|
||||||
|
icon = "/icon/jfrog.svg"
|
||||||
|
script = templatefile("${path.module}/run.sh", {
|
||||||
|
JFROG_URL : var.jfrog_url,
|
||||||
|
JFROG_HOST : replace(var.jfrog_url, "https://", ""),
|
||||||
|
ARTIFACTORY_USERNAME : data.coder_workspace.me.owner_email,
|
||||||
|
ARTIFACTORY_ACCESS_TOKEN : artifactory_scoped_token.me.access_token,
|
||||||
|
REPOSITORY_NPM : lookup(var.package_managers, "npm", ""),
|
||||||
|
REPOSITORY_GO : lookup(var.package_managers, "go", ""),
|
||||||
|
REPOSITORY_PYPI : lookup(var.package_managers, "pypi", ""),
|
||||||
|
})
|
||||||
|
run_on_start = true
|
||||||
|
}
|
||||||
49
jfrog/run.sh
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
BOLD='\033[0;1m'
|
||||||
|
printf "$${BOLD}Installing JFrog CLI..."
|
||||||
|
|
||||||
|
# Install the JFrog CLI.
|
||||||
|
curl -fL https://install-cli.jfrog.io | sudo sh
|
||||||
|
sudo chmod 755 /usr/local/bin/jf
|
||||||
|
|
||||||
|
# The jf CLI checks $CI when determining whether to use interactive
|
||||||
|
# flows.
|
||||||
|
export CI=true
|
||||||
|
# Authenticate with the JFrog CLI.
|
||||||
|
jf c rm 0 || true
|
||||||
|
echo "${ARTIFACTORY_ACCESS_TOKEN}" | jf c add --access-token-stdin --url "${JFROG_URL}" 0
|
||||||
|
|
||||||
|
# Configure the `npm` CLI to use the Artifactory "npm" repository.
|
||||||
|
if [ -z "${REPOSITORY_NPM}" ]; then
|
||||||
|
echo "🤔 REPOSITORY_NPM is not set, skipping npm configuration."
|
||||||
|
else
|
||||||
|
echo "📦 Configuring npm..."
|
||||||
|
jf npmc --global --repo-resolve "${JFROG_URL}/artifactory/api/npm/${REPOSITORY_NPM}"
|
||||||
|
cat <<EOF >~/.npmrc
|
||||||
|
email = ${ARTIFACTORY_USERNAME}
|
||||||
|
registry = ${JFROG_URL}/artifactory/api/npm/${REPOSITORY_NPM}
|
||||||
|
EOF
|
||||||
|
jf rt curl /api/npm/auth >>~/.npmrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure the `pip` to use the Artifactory "python" repository.
|
||||||
|
if [ -z "${REPOSITORY_PYPI}" ]; then
|
||||||
|
echo "🤔 REPOSITORY_PYPI is not set, skipping pip configuration."
|
||||||
|
else
|
||||||
|
echo "🐍 Configuring pip..."
|
||||||
|
mkdir -p ~/.pip
|
||||||
|
cat <<EOF >~/.pip/pip.conf
|
||||||
|
[global]
|
||||||
|
index-url = https://${ARTIFACTORY_USERNAME}:${ARTIFACTORY_ACCESS_TOKEN}@${JFROG_HOST}/artifactory/api/pypi/${REPOSITORY_PYPI}/simple
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set GOPROXY to use the Artifactory "go" repository.
|
||||||
|
if [ -z "${REPOSITORY_GO}" ]; then
|
||||||
|
echo "🤔 REPOSITORY_GO is not set, skipping go configuration."
|
||||||
|
else
|
||||||
|
echo "🐹 Configuring go..."
|
||||||
|
export GOPROXY="https://${ARTIFACTORY_USERNAME}:${ARTIFACTORY_ACCESS_TOKEN}@${JFROG_HOST}/artifactory/api/go/${REPOSITORY_GO}"
|
||||||
|
fi
|
||||||
|
echo "🥳 Configuration complete!"
|
||||||
@@ -15,8 +15,7 @@ A module that adds Jupyter Notebook in your Coder template.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "jupyter-notebook" {
|
module "jupyter-notebook" {
|
||||||
source = "registry.coder.com/modules/jupyter-notebook/coder"
|
source = "https://registry.coder.com/modules/jupyter-notebook"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -27,15 +27,6 @@ variable "port" {
|
|||||||
default = 19999
|
default = 19999
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "share" {
|
|
||||||
type = string
|
|
||||||
default = "owner"
|
|
||||||
validation {
|
|
||||||
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
|
|
||||||
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_script" "jupyter-notebook" {
|
resource "coder_script" "jupyter-notebook" {
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
display_name = "jupyter-notebook"
|
display_name = "jupyter-notebook"
|
||||||
@@ -54,5 +45,5 @@ resource "coder_app" "jupyter-notebook" {
|
|||||||
url = "http://localhost:${var.port}"
|
url = "http://localhost:${var.port}"
|
||||||
icon = "/icon/jupyter.svg"
|
icon = "/icon/jupyter.svg"
|
||||||
subdomain = true
|
subdomain = true
|
||||||
share = var.share
|
share = "owner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,21 @@ BOLD='\033[0;1m'
|
|||||||
printf "$${BOLD}Installing jupyter-notebook!\n"
|
printf "$${BOLD}Installing jupyter-notebook!\n"
|
||||||
|
|
||||||
# check if jupyter-notebook is installed
|
# check if jupyter-notebook is installed
|
||||||
if ! command -v jupyter-notebook > /dev/null 2>&1; then
|
if ! command -v jupyter-notebook >/dev/null 2>&1; then
|
||||||
# install jupyter-notebook
|
# install jupyter-notebook
|
||||||
# check if python3 pip is installed
|
# check if python3 pip is installed
|
||||||
if ! command -v pip3 > /dev/null 2>&1; then
|
if ! command -v pip3 >/dev/null 2>&1; then
|
||||||
echo "pip3 is not installed"
|
echo "pip3 is not installed"
|
||||||
echo "Please install pip3 in your Dockerfile/VM image before running this script"
|
echo "Please install pip3 in your Dockerfile/VM image before running this script"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# install jupyter-notebook
|
# install jupyter-notebook
|
||||||
pip3 install --upgrade --no-cache-dir --no-warn-script-location jupyter
|
pip3 install --upgrade --no-cache-dir --no-warn-script-location jupyter
|
||||||
echo "🥳 jupyter-notebook has been installed\n\n"
|
echo "🥳 jupyter-notebook has been installed\n\n"
|
||||||
else
|
else
|
||||||
echo "🥳 jupyter-notebook is already installed\n\n"
|
echo "🥳 jupyter-notebook is already installed\n\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "👷 Starting jupyter-notebook in background..."
|
echo "👷 Starting jupyter-notebook in background..."
|
||||||
echo "check logs at ${LOG_PATH}"
|
echo "check logs at ${LOG_PATH}"
|
||||||
$HOME/.local/bin/jupyter notebook --NotebookApp.ip='0.0.0.0' --ServerApp.port=${PORT} --no-browser --ServerApp.token='' --ServerApp.password='' > ${LOG_PATH} 2>&1 &
|
$HOME/.local/bin/jupyter notebook --NotebookApp.ip='0.0.0.0' --ServerApp.port=${PORT} --no-browser --ServerApp.token='' --ServerApp.password='' >${LOG_PATH} 2>&1 &
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ A module that adds JupyterLab in your Coder template.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "jupyterlab" {
|
module "jupyterlab" {
|
||||||
source = "registry.coder.com/modules/jupyterlab/coder"
|
source = "https://registry.coder.com/modules/jupyterlab"
|
||||||
version = "1.0.0"
|
agent_id = coder_agent.example.id
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -27,15 +27,6 @@ variable "port" {
|
|||||||
default = 19999
|
default = 19999
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "share" {
|
|
||||||
type = string
|
|
||||||
default = "owner"
|
|
||||||
validation {
|
|
||||||
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
|
|
||||||
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_script" "jupyterlab" {
|
resource "coder_script" "jupyterlab" {
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
display_name = "jupyterlab"
|
display_name = "jupyterlab"
|
||||||
@@ -54,5 +45,5 @@ resource "coder_app" "jupyterlab" {
|
|||||||
url = "http://localhost:${var.port}"
|
url = "http://localhost:${var.port}"
|
||||||
icon = "/icon/jupyter.svg"
|
icon = "/icon/jupyter.svg"
|
||||||
subdomain = true
|
subdomain = true
|
||||||
share = var.share
|
share = "owner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,21 @@ BOLD='\033[0;1m'
|
|||||||
printf "$${BOLD}Installing jupyterlab!\n"
|
printf "$${BOLD}Installing jupyterlab!\n"
|
||||||
|
|
||||||
# check if jupyterlab is installed
|
# check if jupyterlab is installed
|
||||||
if ! command -v jupyterlab > /dev/null 2>&1; then
|
if ! command -v jupyterlab >/dev/null 2>&1; then
|
||||||
# install jupyterlab
|
# install jupyterlab
|
||||||
# check if python3 pip is installed
|
# check if python3 pip is installed
|
||||||
if ! command -v pip3 > /dev/null 2>&1; then
|
if ! command -v pip3 >/dev/null 2>&1; then
|
||||||
echo "pip3 is not installed"
|
echo "pip3 is not installed"
|
||||||
echo "Please install pip3 in your Dockerfile/VM image before running this script"
|
echo "Please install pip3 in your Dockerfile/VM image before running this script"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# install jupyterlab
|
# install jupyterlab
|
||||||
pip3 install --upgrade --no-cache-dir --no-warn-script-location jupyterlab
|
pip3 install --upgrade --no-cache-dir --no-warn-script-location jupyterlab
|
||||||
echo "🥳 jupyterlab has been installed\n\n"
|
echo "🥳 jupyterlab has been installed\n\n"
|
||||||
else
|
else
|
||||||
echo "🥳 jupyterlab is already installed\n\n"
|
echo "🥳 jupyterlab is already installed\n\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "👷 Starting jupyterlab in background..."
|
echo "👷 Starting jupyterlab in background..."
|
||||||
echo "check logs at ${LOG_PATH}"
|
echo "check logs at ${LOG_PATH}"
|
||||||
$HOME/.local/bin/jupyter lab --ServerApp.ip='0.0.0.0' --ServerApp.port=${PORT} --no-browser --ServerApp.token='' --ServerApp.password='' > ${LOG_PATH} 2>&1 &
|
$HOME/.local/bin/jupyter lab --ServerApp.ip='0.0.0.0' --ServerApp.port=${PORT} --no-browser --ServerApp.token='' --ServerApp.password='' >${LOG_PATH} 2>&1 &
|
||||||
|
|||||||
22
new.sh
@@ -7,15 +7,15 @@ MODULE_NAME=$1
|
|||||||
|
|
||||||
# Check if module name is provided
|
# Check if module name is provided
|
||||||
if [ -z "$MODULE_NAME" ]; then
|
if [ -z "$MODULE_NAME" ]; then
|
||||||
echo "Usage: ./new.sh <module_name>"
|
echo "Usage: ./new.sh <module_name>"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create module directory and exit if it alredy exists
|
# Create module directory and exit if it alredy 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"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
mkdir -p "${MODULE_NAME}"
|
mkdir -p "${MODULE_NAME}"
|
||||||
|
|
||||||
@@ -27,13 +27,13 @@ cd "${MODULE_NAME}"
|
|||||||
|
|
||||||
# Detect OS
|
# Detect OS
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
# macOS
|
# macOS
|
||||||
sed -i '' "s/MODULE_NAME/${MODULE_NAME}/g" main.tf
|
sed -i '' "s/MODULE_NAME/${MODULE_NAME}/g" main.tf
|
||||||
sed -i '' "s/MODULE_NAME/${MODULE_NAME}/g" README.md
|
sed -i '' "s/MODULE_NAME/${MODULE_NAME}/g" README.md
|
||||||
else
|
else
|
||||||
# Linux
|
# Linux
|
||||||
sed -i "s/MODULE_NAME/${MODULE_NAME}/g" main.tf
|
sed -i "s/MODULE_NAME/${MODULE_NAME}/g" main.tf
|
||||||
sed -i "s/MODULE_NAME/${MODULE_NAME}/g" README.md
|
sed -i "s/MODULE_NAME/${MODULE_NAME}/g" README.md
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Make run.sh executable
|
# Make run.sh executable
|
||||||
|
|||||||
11
package.json
@@ -2,17 +2,16 @@
|
|||||||
"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 **/*.ts **/*.md *.md && terraform fmt **/*.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 **/*.ts **/*.md *.md && terraform fmt -check **/*.tf",
|
||||||
"lint": "bun run lint.ts"
|
"lint": "bun run lint.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bun-types": "^1.0.18",
|
"bun-types": "^1.0.3",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"marked": "^11.1.0",
|
"marked": "^9.0.3"
|
||||||
"prettier-plugin-sh": "^0.13.1"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ Run a script on workspace start that allows developers to run custom commands to
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "personalize" {
|
module "personalize" {
|
||||||
source = "registry.coder.com/modules/personalize/coder"
|
source = "https://registry.coder.com/modules/personalize"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -9,18 +9,18 @@ SCRIPT="$${SCRIPT/#\~/$${HOME}}"
|
|||||||
# If the personalize script doesn't exist, educate
|
# If the personalize script doesn't exist, educate
|
||||||
# the user how they can customize their environment!
|
# the user how they can customize their environment!
|
||||||
if [ ! -f $SCRIPT ]; then
|
if [ ! -f $SCRIPT ]; then
|
||||||
printf "✨ $${BOLD}You don't have a personalize script!\n\n"
|
printf "✨ $${BOLD}You don't have a personalize script!\n\n"
|
||||||
printf "Run $${CODE}touch $${SCRIPT} && chmod +x $${SCRIPT}$${RESET} to create one.\n"
|
printf "Run $${CODE}touch $${SCRIPT} && chmod +x $${SCRIPT}$${RESET} to create one.\n"
|
||||||
printf "It will run every time your workspace starts. Use it to install personal packages!\n\n"
|
printf "It will run every time your workspace starts. Use it to install personal packages!\n\n"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if the personalize script is executable, if not,
|
# Check if the personalize script is executable, if not,
|
||||||
# try to make it executable and educate the user if it fails.
|
# try to make it executable and educate the user if it fails.
|
||||||
if [ ! -x $SCRIPT ]; then
|
if [ ! -x $SCRIPT ]; then
|
||||||
echo "🔐 Your personalize script isn't executable!"
|
echo "🔐 Your personalize script isn't executable!"
|
||||||
printf "Run $CODE\`chmod +x $SCRIPT\`$RESET to make it executable.\n"
|
printf "Run $CODE\`chmod +x $SCRIPT\`$RESET to make it executable.\n"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run the personalize script!
|
# Run the personalize script!
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -56,8 +56,7 @@ slackme npm run long-build
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "slackme" {
|
module "slackme" {
|
||||||
source = "registry.coder.com/modules/slackme/coder"
|
source = "https://registry.coder.com/modules/slackme"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
auth_provider_id = "slack"
|
auth_provider_id = "slack"
|
||||||
}
|
}
|
||||||
@@ -72,8 +71,7 @@ slackme npm run long-build
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "slackme" {
|
module "slackme" {
|
||||||
source = "registry.coder.com/modules/slackme/coder"
|
source = "https://registry.coder.com/modules/slackme"
|
||||||
version = "1.0.0"
|
|
||||||
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
|
||||||
|
|||||||
@@ -56,6 +56,15 @@ describe("slackme", async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("exits with command code", async () => {
|
||||||
|
const { instance, id } = await setupContainer();
|
||||||
|
await writeCoder(id, "echo 'some-url' && exit 1");
|
||||||
|
let exec = await execContainer(id, ["sh", "-c", instance.script]);
|
||||||
|
expect(exec.exitCode).toBe(0);
|
||||||
|
exec = await execContainer(id, ["sh", "-c", "slackme exit 1"]);
|
||||||
|
expect(exec.exitCode).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("formats multiline message", async () => {
|
it("formats multiline message", async () => {
|
||||||
await assertSlackMessage({
|
await assertSlackMessage({
|
||||||
command: "echo test",
|
command: "echo test",
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
PROVIDER_ID=${PROVIDER_ID}
|
PROVIDER_ID=${PROVIDER_ID}
|
||||||
SLACK_MESSAGE=$(
|
SLACK_MESSAGE=$(cat << "EOF"
|
||||||
cat << "EOF"
|
|
||||||
${SLACK_MESSAGE}
|
${SLACK_MESSAGE}
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
SLACK_URL=$${SLACK_URL:-https://slack.com}
|
SLACK_URL=$${SLACK_URL:-https://slack.com}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat << EOF
|
cat <<EOF
|
||||||
slackme — Send a Slack notification when a command finishes
|
slackme — Send a Slack notification when a command finishes
|
||||||
Usage: slackme <command>
|
Usage: slackme <command>
|
||||||
|
|
||||||
@@ -18,45 +17,45 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
pretty_duration() {
|
pretty_duration() {
|
||||||
local duration_ms=$1
|
local duration_ms=$1
|
||||||
|
|
||||||
# If the duration is less than 1 second, display in milliseconds
|
# If the duration is less than 1 second, display in milliseconds
|
||||||
if [ $duration_ms -lt 1000 ]; then
|
if [ $duration_ms -lt 1000 ]; then
|
||||||
echo "$${duration_ms}ms"
|
echo "$${duration_ms}ms"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Convert the duration to seconds
|
# Convert the duration to seconds
|
||||||
local duration_sec=$((duration_ms / 1000))
|
local duration_sec=$((duration_ms / 1000))
|
||||||
local remaining_ms=$((duration_ms % 1000))
|
local remaining_ms=$((duration_ms % 1000))
|
||||||
|
|
||||||
# If the duration is less than 1 minute, display in seconds (with ms)
|
# If the duration is less than 1 minute, display in seconds (with ms)
|
||||||
if [ $duration_sec -lt 60 ]; then
|
if [ $duration_sec -lt 60 ]; then
|
||||||
echo "$${duration_sec}.$${remaining_ms}s"
|
echo "$${duration_sec}.$${remaining_ms}s"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Convert the duration to minutes
|
# Convert the duration to minutes
|
||||||
local duration_min=$((duration_sec / 60))
|
local duration_min=$((duration_sec / 60))
|
||||||
local remaining_sec=$((duration_sec % 60))
|
local remaining_sec=$((duration_sec % 60))
|
||||||
|
|
||||||
# If the duration is less than 1 hour, display in minutes and seconds
|
# If the duration is less than 1 hour, display in minutes and seconds
|
||||||
if [ $duration_min -lt 60 ]; then
|
if [ $duration_min -lt 60 ]; then
|
||||||
echo "$${duration_min}m $${remaining_sec}.$${remaining_ms}s"
|
echo "$${duration_min}m $${remaining_sec}.$${remaining_ms}s"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Convert the duration to hours
|
# Convert the duration to hours
|
||||||
local duration_hr=$((duration_min / 60))
|
local duration_hr=$((duration_min / 60))
|
||||||
local remaining_min=$((duration_min % 60))
|
local remaining_min=$((duration_min % 60))
|
||||||
|
|
||||||
# Display in hours, minutes, and seconds
|
# Display in hours, minutes, and seconds
|
||||||
echo "$${duration_hr}hr $${remaining_min}m $${remaining_sec}.$${remaining_ms}s"
|
echo "$${duration_hr}hr $${remaining_min}m $${remaining_sec}.$${remaining_ms}s"
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
usage
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BOT_TOKEN=$(coder external-auth access-token $PROVIDER_ID)
|
BOT_TOKEN=$(coder external-auth access-token $PROVIDER_ID)
|
||||||
@@ -73,9 +72,10 @@ fi
|
|||||||
|
|
||||||
START=$(date +%s%N)
|
START=$(date +%s%N)
|
||||||
# Run all arguments as a command
|
# Run all arguments as a command
|
||||||
$@
|
"$@"
|
||||||
|
CODE=$?
|
||||||
END=$(date +%s%N)
|
END=$(date +%s%N)
|
||||||
DURATION_MS=$${DURATION_MS:-$(((END - START) / 1000000))}
|
DURATION_MS=$${DURATION_MS:-$(( (END - START) / 1000000 ))}
|
||||||
PRETTY_DURATION=$(pretty_duration $DURATION_MS)
|
PRETTY_DURATION=$(pretty_duration $DURATION_MS)
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
@@ -84,5 +84,7 @@ SLACK_MESSAGE=$(echo "$SLACK_MESSAGE" | sed "s|\\$COMMAND|$COMMAND|g")
|
|||||||
SLACK_MESSAGE=$(echo "$SLACK_MESSAGE" | sed "s|\\$DURATION|$PRETTY_DURATION|g")
|
SLACK_MESSAGE=$(echo "$SLACK_MESSAGE" | sed "s|\\$DURATION|$PRETTY_DURATION|g")
|
||||||
|
|
||||||
curl --silent -o /dev/null --header "Authorization: Bearer $BOT_TOKEN" \
|
curl --silent -o /dev/null --header "Authorization: Bearer $BOT_TOKEN" \
|
||||||
-G --data-urlencode "text=$${SLACK_MESSAGE}" \
|
-G --data-urlencode "text=$${SLACK_MESSAGE}" \
|
||||||
"$SLACK_URL/api/chat.postMessage?channel=$USER_ID&pretty=1"
|
"$SLACK_URL/api/chat.postMessage?channel=$USER_ID&pretty=1"
|
||||||
|
|
||||||
|
exit $CODE
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
---
|
|
||||||
display_name: Hashicorp Vault Integration (GitHub)
|
|
||||||
description: Authenticates with Vault using GitHub
|
|
||||||
icon: ../.icons/vault.svg
|
|
||||||
maintainer_github: coder
|
|
||||||
verified: true
|
|
||||||
tags: [helper, integration, vault, github]
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hashicorp Vault Integration (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
|
|
||||||
module "vault" {
|
|
||||||
source = "registry.coder.com/modules/vault-github/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
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 -mount=secret my-secret
|
|
||||||
```
|
|
||||||
|
|
||||||
or using the Vault API:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
curl -H "X-Vault-Token: ${VAULT_TOKEN}" -X GET "${VAULT_ADDR}/v1/secret/data/my-secret"
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
To configure the Vault module, you must set up a Vault GitHub auth method. See the [Vault documentation](https://www.vaultproject.io/docs/auth/github) for more information.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Configure Vault integration with a different Coder GitHub external auth ID (i.e., not the default `github`)
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "vault" {
|
|
||||||
source = "registry.coder.com/modules/vault-github/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
vault_addr = "https://vault.example.com"
|
|
||||||
coder_github_auth_id = "my-github-auth-id"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure Vault integration with a different Coder GitHub external auth ID and a different Vault GitHub auth path
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "vault" {
|
|
||||||
source = "registry.coder.com/modules/vault-github/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
vault_addr = "https://vault.example.com"
|
|
||||||
coder_github_auth_id = "my-github-auth-id"
|
|
||||||
vault_github_auth_path = "my-github-auth-path"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure Vault integration and install a specific version of the Vault CLI
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
module "vault" {
|
|
||||||
source = "registry.coder.com/modules/vault-github/coder"
|
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
|
||||||
vault_addr = "https://vault.example.com"
|
|
||||||
vault_cli_version = "1.15.0"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
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 "coder_github_auth_id" {
|
|
||||||
type = string
|
|
||||||
description = "The ID of the GitHub external auth."
|
|
||||||
default = "github"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "vault_github_auth_path" {
|
|
||||||
type = string
|
|
||||||
description = "The path to the GitHub auth method."
|
|
||||||
default = "github"
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (GitHub)"
|
|
||||||
icon = "/icon/vault.svg"
|
|
||||||
script = templatefile("${path.module}/run.sh", {
|
|
||||||
VAULT_ADDR : var.vault_addr,
|
|
||||||
AUTH_PATH : var.vault_github_auth_path,
|
|
||||||
GITHUB_EXTERNAL_AUTH_ID : data.coder_external_auth.github.id,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
data "coder_external_auth" "github" {
|
|
||||||
id = var.coder_github_auth_id
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
# Convert all templated variables to shell variables
|
|
||||||
INSTALL_VERSION=${INSTALL_VERSION}
|
|
||||||
VAULT_ADDR=${VAULT_ADDR}
|
|
||||||
GITHUB_EXTERNAL_AUTH_ID=${GITHUB_EXTERNAL_AUTH_ID}
|
|
||||||
AUTH_PATH=${AUTH_PATH}
|
|
||||||
|
|
||||||
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"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
unzip() {
|
|
||||||
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"
|
|
||||||
exit 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 -oP 'vault/\K[0-9]+\.[0-9]+\.[0-9]+' | 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"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
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}" "$${VERSION}"
|
|
||||||
fi
|
|
||||||
fetch vault.zip "https://releases.hashicorp.com/vault/$${VERSION}/vault_$${VERSION}_linux_amd64.zip"
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
printf "Failed to download Vault.\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
unzip vault.zip
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
printf "Failed to unzip Vault.\n"
|
|
||||||
exit 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
|
|
||||||
mv vault ~/.local/bin/vault
|
|
||||||
if [ ! -f ~/.local/bin/vault ]; then
|
|
||||||
printf "Failed to move Vault to local bin.\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
printf "Please add ~/.local/bin to your PATH to use vault CLI.\n"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Authenticate with Vault
|
|
||||||
printf "🔑 Authenticating with Vault ...\n\n"
|
|
||||||
GITHUB_TOKEN=$(coder external-auth access-token "$${GITHUB_EXTERNAL_AUTH_ID}")
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
printf "Authentication with Vault failed. Please check your credentials.\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
export VAULT_ADDR="$${VAULT_ADDR}"
|
|
||||||
|
|
||||||
# Login to vault using the GitHub token
|
|
||||||
printf "🔑 Logging in to Vault ...\n\n"
|
|
||||||
vault login -no-print -method=github -path=/$${AUTH_PATH} token="$${GITHUB_TOKEN}"
|
|
||||||
printf "🥳 Vault authentication complete!\n\n"
|
|
||||||
printf "You can now use Vault CLI to access secrets.\n"
|
|
||||||
@@ -15,8 +15,7 @@ Uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder)
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "vscode" {
|
module "vscode" {
|
||||||
source = "registry.coder.com/modules/vscode-desktop/coder"
|
source = "https://registry.coder.com/modules/vscode-desktop"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -27,8 +26,7 @@ module "vscode" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "vscode" {
|
module "vscode" {
|
||||||
source = "registry.coder.com/modules/vscode-desktop/coder"
|
source = "https://registry.coder.com/modules/vscode-desktop"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
folder = "/home/coder/project"
|
folder = "/home/coder/project"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ resource "coder_app" "vscode" {
|
|||||||
data.coder_workspace.me.name,
|
data.coder_workspace.me.name,
|
||||||
"&folder=",
|
"&folder=",
|
||||||
var.folder,
|
var.folder,
|
||||||
"&url=",
|
|
||||||
data.coder_workspace.me.access_url,
|
|
||||||
"&token=$SESSION_TOKEN",
|
"&token=$SESSION_TOKEN",
|
||||||
]) : join("", [
|
]) : join("", [
|
||||||
"vscode://coder.coder-remote/open?owner=",
|
"vscode://coder.coder-remote/open?owner=",
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ Automatically install [Visual Studio Code Server](https://code.visualstudio.com/
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "vscode-web" {
|
module "vscode-web" {
|
||||||
source = "registry.coder.com/modules/vscode-web/coder"
|
source = "https://registry.coder.com/modules/vscode-web"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
accept_license = true
|
accept_license = true
|
||||||
}
|
}
|
||||||
@@ -28,8 +27,7 @@ module "vscode-web" {
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
module "vscode-web" {
|
module "vscode-web" {
|
||||||
source = "registry.coder.com/modules/vscode-web/coder"
|
source = "https://registry.coder.com/modules/vscode-web"
|
||||||
version = "1.0.0"
|
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
install_dir = "/home/coder/.vscode-web"
|
install_dir = "/home/coder/.vscode-web"
|
||||||
folder = "/home/coder"
|
folder = "/home/coder"
|
||||||
|
|||||||
@@ -26,15 +26,6 @@ variable "folder" {
|
|||||||
default = ""
|
default = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "share" {
|
|
||||||
type = string
|
|
||||||
default = "owner"
|
|
||||||
validation {
|
|
||||||
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
|
|
||||||
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "log_path" {
|
variable "log_path" {
|
||||||
type = string
|
type = string
|
||||||
description = "The path to log."
|
description = "The path to log."
|
||||||
@@ -76,7 +67,7 @@ resource "coder_app" "vscode-web" {
|
|||||||
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
|
||||||
share = var.share
|
share = "owner"
|
||||||
|
|
||||||
healthcheck {
|
healthcheck {
|
||||||
url = "http://localhost:${var.port}/healthz"
|
url = "http://localhost:${var.port}/healthz"
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ printf "🥳 vscode-cli has been installed.\n\n"
|
|||||||
|
|
||||||
echo "👷 Running ${INSTALL_DIR}/bin/code serve-web --port ${PORT} --without-connection-token --accept-server-license-terms in the background..."
|
echo "👷 Running ${INSTALL_DIR}/bin/code serve-web --port ${PORT} --without-connection-token --accept-server-license-terms 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_DIR}/code serve-web --port ${PORT} --without-connection-token --accept-server-license-terms >${LOG_PATH} 2>&1 &
|
||||||
|
|||||||