Skip to content

Commit 1793679

Browse files
committed
feat: combine module from coder
1 parent 0a33250 commit 1793679

9 files changed

+481
-383
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto eol=lf

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.terraform*

LICENSE

+21-373
Large diffs are not rendered by default.

README.md

+39-10
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
<div align="center">
33

44
<!-- <a href="#">
5-
<img src="https://github.com/katorlys/.github/blob/main/assets/logo/logo.png" height="100">
5+
<img src="https://github.com/katorlys/.github/blob/main/assets/mark/mark.png" height="100">
66
</a><br> -->
77

88
<h1>
9-
ExampleProject
9+
Coder Dotfiles after code-server Module
1010
</h1>
1111

1212
<p>
13-
One sentence to describe project
13+
Terraform Module to install dotfiles after code-server installation in Coderv2.
1414
</p>
1515

1616
[![Pull Requests][github-pr-badge]][github-pr-link]
@@ -23,7 +23,36 @@
2323
<!-- Main Body -->
2424

2525
## Introduction
26-
Describe your project clearly here.
26+
This module is copied from [Coder code-server Module](https://registry.coder.com/modules/code-server) and [Coder Dotfiles Module](https://registry.coder.com/modules/dotfiles) (Both licensed under [Apache License 2.0](https://github.com/coder/modules/blob/438c9045673c629e95416960c94a6b6344e3d298/LICENSE)), and adds the feature of executing the dotfiles install script after code-server installation.
27+
28+
This is a temporary fix for issue [https://github.com/coder/coder/issues](https://github.com/coder/coder/issues)#10352.
29+
30+
By using this module, you don't need to use the Coder code-server Module and Coder Dotfiles Module anymore, and you'll never see `/tmp/code-server/bin/code-server: 12: /tmp/code-server/node: Text file busy` if you want to install code-server installation in Coderv2 from your dotfiles now.
31+
32+
33+
## Examples
34+
Combine [Coder code-server Module](https://registry.coder.com/modules/code-server?tab=readme) and [Coder Dotfiles Module](https://registry.coder.com/modules/dotfiles?tab=readme) together.
35+
```tf
36+
module "dotfiles-after-code-server" {
37+
source = "katorlys-samples/dotfiles-after-code-server/coder"
38+
version = "0.1.0"
39+
agent_id = coder_agent.example.id
40+
url = "http://localhost:13337/?folder=/home/coder"
41+
}
42+
```
43+
44+
45+
## How?
46+
This modules combines the Coder code-server Module and Coder Dotfiles Module together in one module. Then, it executes the original dotfiles install script right after code-server install script is done. (See [run.sh](/run.sh) for detailed information)
47+
48+
49+
## Why?
50+
I initially intended to fix the issue privately. However, the shell scripts on Windows use `\r\n` for End of Line instead of `\n`, and Coder cannot automatically convert them, causing script execution to fail.
51+
52+
Since I deploy the Coder instance using Docker, I can only copy the script to the online editor. Additionally, the `Upload template` function is broken, preventing me from packaging and uploading the scripts.
53+
54+
All attempts failed, so I had no choice but to publish a module from Git to avoid the End of Line issue.
55+
2756

2857
<!-- /Main Body -->
2958

@@ -47,11 +76,11 @@ Describe your project clearly here.
4776
</div>
4877

4978
[back-to-top-button]: https://img.shields.io/badge/BACK_TO_TOP-151515?style=flat-square
50-
[github-pr-badge]: https://img.shields.io/github/issues-pr/katorlys/ExampleProject?label=pulls&labelColor=151515&color=79E096&style=flat-square
51-
[github-pr-link]: https://github.com/katorlys/ExampleProject/pulls
52-
[github-issue-badge]: https://img.shields.io/github/issues/katorlys/ExampleProject?labelColor=151515&color=FFC868&style=flat-square
53-
[github-issue-link]: https://github.com/katorlys/ExampleProject/issues
54-
[github-license-badge]: https://img.shields.io/github/license/katorlys/ExampleProject?labelColor=151515&color=EFEFEF&style=flat-square
79+
[github-pr-badge]: https://img.shields.io/github/issues-pr/katorlys-samples/terraform-coder-dotfiles-after-code-server?label=pulls&labelColor=151515&color=79E096&style=flat-square
80+
[github-pr-link]: https://github.com/katorlys-samples/terraform-coder-dotfiles-after-code-server/pulls
81+
[github-issue-badge]: https://img.shields.io/github/issues/katorlys-samples/terraform-coder-dotfiles-after-code-server?labelColor=151515&color=FFC868&style=flat-square
82+
[github-issue-link]: https://github.com/katorlys-samples/terraform-coder-dotfiles-after-code-server/issues
83+
[github-license-badge]: https://img.shields.io/github/license/katorlys-samples/terraform-coder-dotfiles-after-code-server?labelColor=151515&color=EFEFEF&style=flat-square
5584
<!-- https://img.shields.io/badge/license-CC_BY--NC--SA_4.0-EFEFEF?labelColor=151515&style=flat-square -->
56-
[github-license-badge-bottom]: https://img.shields.io/github/license/katorlys/ExampleProject?labelColor=151515&color=EFEFEF&style=for-the-badge
85+
[github-license-badge-bottom]: https://img.shields.io/github/license/katorlys-samples/terraform-coder-dotfiles-after-code-server?labelColor=151515&color=EFEFEF&style=for-the-badge
5786
<!-- https://img.shields.io/badge/license-CC_BY--NC--SA_4.0-EFEFEF?labelColor=151515&style=for-the-badge -->

dotfiles_run.sh

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bash
2+
DOTFILES_URI="${DOTFILES_URI}"
3+
DOTFILES_USER="${DOTFILES_USER}"
4+
5+
if [ -n "$${DOTFILES_URI// }" ]; then
6+
if [ -z "$DOTFILES_USER" ]; then
7+
DOTFILES_USER="$USER"
8+
fi
9+
10+
echo "✨ Applying dotfiles for user $DOTFILES_USER"
11+
12+
if [ "$DOTFILES_USER" = "$USER" ]; then
13+
coder dotfiles "$DOTFILES_URI" -y 2>&1 | tee ~/.dotfiles.log
14+
else
15+
# The `eval echo ~"$DOTFILES_USER"` part is used to dynamically get the home directory of the user, see https://superuser.com/a/484280
16+
# eval echo ~coder -> "/home/coder"
17+
# eval echo ~root -> "/root"
18+
19+
CODER_BIN=$(which coder)
20+
DOTFILES_USER_HOME=$(eval echo ~"$DOTFILES_USER")
21+
sudo -u "$DOTFILES_USER" sh -c "'$CODER_BIN' dotfiles '$DOTFILES_URI' -y 2>&1 | tee '$DOTFILES_USER_HOME'/.dotfiles.log"
22+
fi
23+
fi

main.tf

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
coder = {
6+
source = "coder/coder"
7+
version = ">= 0.17"
8+
}
9+
}
10+
}
11+
12+
data "coder_parameter" "dotfiles_uri" {
13+
count = var.dotfiles_uri == null ? 1 : 0
14+
type = "string"
15+
name = "dotfiles_uri"
16+
display_name = "Dotfiles URL"
17+
order = var.coder_parameter_order
18+
default = var.default_dotfiles_uri
19+
description = "Enter a URL for a [dotfiles repository](https://dotfiles.github.io) to personalize your workspace"
20+
mutable = true
21+
icon = "/icon/dotfiles.svg"
22+
}
23+
24+
locals {
25+
dotfiles_uri = var.dotfiles_uri != null ? var.dotfiles_uri : data.coder_parameter.dotfiles_uri[0].value
26+
user = var.user != null ? var.user : ""
27+
}
28+
29+
resource "coder_script" "dotfiles-after-code-server" {
30+
agent_id = var.agent_id
31+
display_name = "code-server"
32+
icon = "/icon/code.svg"
33+
script = templatefile("${path.module}/run.sh", {
34+
VERSION : var.install_version,
35+
EXTENSIONS : join(",", var.extensions),
36+
APP_NAME : var.display_name,
37+
PORT : var.port,
38+
LOG_PATH : var.log_path,
39+
INSTALL_PREFIX : var.install_prefix,
40+
// This is necessary otherwise the quotes are stripped!
41+
SETTINGS : replace(jsonencode(var.settings), "\"", "\\\""),
42+
OFFLINE : var.offline,
43+
USE_CACHED : var.use_cached,
44+
USE_CACHED_EXTENSIONS : var.use_cached_extensions,
45+
EXTENSIONS_DIR : var.extensions_dir,
46+
FOLDER : var.folder,
47+
AUTO_INSTALL_EXTENSIONS : var.auto_install_extensions,
48+
DOTFILES_URI : local.dotfiles_uri,
49+
DOTFILES_USER : local.user
50+
})
51+
run_on_start = true
52+
53+
lifecycle {
54+
precondition {
55+
condition = !var.offline || length(var.extensions) == 0
56+
error_message = "Offline mode does not allow extensions to be installed"
57+
}
58+
59+
precondition {
60+
condition = !var.offline || !var.use_cached
61+
error_message = "Offline and Use Cached can not be used together"
62+
}
63+
}
64+
}
65+
66+
resource "coder_app" "code-server" {
67+
agent_id = var.agent_id
68+
slug = var.slug
69+
display_name = var.display_name
70+
url = "http://localhost:${var.port}/${var.folder != "" ? "?folder=${urlencode(var.folder)}" : ""}"
71+
icon = "/icon/code.svg"
72+
subdomain = var.subdomain
73+
share = var.share
74+
order = var.order
75+
76+
healthcheck {
77+
url = "http://localhost:${var.port}/healthz"
78+
interval = 5
79+
threshold = 6
80+
}
81+
}
82+
83+
# resource "coder_script" "dotfiles" {
84+
# agent_id = var.agent_id
85+
# script = templatefile("${path.module}/dotfiles_run.sh", {
86+
# DOTFILES_URI : local.dotfiles_uri,
87+
# DOTFILES_USER : local.user
88+
# })
89+
# display_name = "Dotfiles"
90+
# icon = "/icon/dotfiles.svg"
91+
# run_on_start = true
92+
# }
93+
94+
resource "coder_app" "dotfiles" {
95+
count = var.manual_update ? 1 : 0
96+
agent_id = var.agent_id
97+
display_name = "Refresh Dotfiles"
98+
slug = "dotfiles"
99+
icon = "/icon/dotfiles.svg"
100+
command = templatefile("${path.module}/dotfiles_run.sh", {
101+
DOTFILES_URI : local.dotfiles_uri,
102+
DOTFILES_USER : local.user
103+
})
104+
}

outputs.tf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
output "dotfiles_uri" {
2+
description = "Dotfiles URI"
3+
value = local.dotfiles_uri
4+
}

run.sh

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env bash
2+
3+
EXTENSIONS=("${EXTENSIONS}")
4+
BOLD='\033[0;1m'
5+
CODE='\033[36;40;1m'
6+
RESET='\033[0m'
7+
CODE_SERVER="${INSTALL_PREFIX}/bin/code-server"
8+
9+
# Set extension directory
10+
EXTENSION_ARG=""
11+
if [ -n "${EXTENSIONS_DIR}" ]; then
12+
EXTENSION_ARG="--extensions-dir=${EXTENSIONS_DIR}"
13+
mkdir -p "${EXTENSIONS_DIR}"
14+
fi
15+
16+
function run_code_server() {
17+
echo "👷 Running code-server in the background..."
18+
echo "Check logs at ${LOG_PATH}!"
19+
$CODE_SERVER "$EXTENSION_ARG" --auth none --port "${PORT}" --app-name "${APP_NAME}" > "${LOG_PATH}" 2>&1 &
20+
}
21+
22+
# Check if the settings file exists...
23+
if [ ! -f ~/.local/share/code-server/User/settings.json ]; then
24+
echo "⚙️ Creating settings file..."
25+
mkdir -p ~/.local/share/code-server/User
26+
echo "${SETTINGS}" > ~/.local/share/code-server/User/settings.json
27+
fi
28+
29+
# Check if code-server is already installed for offline
30+
if [ "${OFFLINE}" = true ]; then
31+
if [ -f "$CODE_SERVER" ]; then
32+
echo "🥳 Found a copy of code-server"
33+
run_code_server
34+
exit 0
35+
fi
36+
# Offline mode always expects a copy of code-server to be present
37+
echo "Failed to find a copy of code-server"
38+
exit 1
39+
fi
40+
41+
# If there is no cached install OR we don't want to use a cached install
42+
if [ ! -f "$CODE_SERVER" ] || [ "${USE_CACHED}" != true ]; then
43+
printf "$${BOLD}Installing code-server!\n"
44+
45+
ARGS=(
46+
"--method=standalone"
47+
"--prefix=${INSTALL_PREFIX}"
48+
)
49+
if [ -n "${VERSION}" ]; then
50+
ARGS+=("--version=${VERSION}")
51+
fi
52+
53+
output=$(curl -fsSL https://code-server.dev/install.sh | sh -s -- "$${ARGS[@]}")
54+
if [ $? -ne 0 ]; then
55+
echo "Failed to install code-server: $output"
56+
exit 1
57+
fi
58+
printf "🥳 code-server has been installed in ${INSTALL_PREFIX}\n\n"
59+
fi
60+
61+
# Get the list of installed extensions...
62+
LIST_EXTENSIONS=$($CODE_SERVER --list-extensions $EXTENSION_ARG)
63+
readarray -t EXTENSIONS_ARRAY <<< "$LIST_EXTENSIONS"
64+
function extension_installed() {
65+
if [ "${USE_CACHED_EXTENSIONS}" != true ]; then
66+
return 1
67+
fi
68+
for _extension in "$${EXTENSIONS_ARRAY[@]}"; do
69+
if [ "$_extension" == "$1" ]; then
70+
echo "Extension $1 was already installed."
71+
return 0
72+
fi
73+
done
74+
return 1
75+
}
76+
77+
# Install each extension...
78+
IFS=',' read -r -a EXTENSIONLIST <<< "$${EXTENSIONS}"
79+
for extension in "$${EXTENSIONLIST[@]}"; do
80+
if [ -z "$extension" ]; then
81+
continue
82+
fi
83+
if extension_installed "$extension"; then
84+
continue
85+
fi
86+
printf "🧩 Installing extension $${CODE}$extension$${RESET}...\n"
87+
output=$($CODE_SERVER "$EXTENSION_ARG" --force --install-extension "$extension")
88+
if [ $? -ne 0 ]; then
89+
echo "Failed to install extension: $extension: $output"
90+
exit 1
91+
fi
92+
done
93+
94+
if [ "${AUTO_INSTALL_EXTENSIONS}" = true ]; then
95+
if ! command -v jq > /dev/null; then
96+
echo "jq is required to install extensions from a workspace file."
97+
exit 0
98+
fi
99+
100+
WORKSPACE_DIR="$HOME"
101+
if [ -n "${FOLDER}" ]; then
102+
WORKSPACE_DIR="${FOLDER}"
103+
fi
104+
105+
if [ -f "$WORKSPACE_DIR/.vscode/extensions.json" ]; then
106+
printf "🧩 Installing extensions from %s/.vscode/extensions.json...\n" "$WORKSPACE_DIR"
107+
extensions=$(jq -r '.recommendations[]' "$WORKSPACE_DIR"/.vscode/extensions.json)
108+
for extension in $extensions; do
109+
if extension_installed "$extension"; then
110+
continue
111+
fi
112+
$CODE_SERVER "$EXTENSION_ARG" --force --install-extension "$extension"
113+
done
114+
fi
115+
fi
116+
117+
run_code_server
118+
119+
120+
# dotfiles, the same as dotfiles_run.sh
121+
DOTFILES_URI="${DOTFILES_URI}"
122+
DOTFILES_USER="${DOTFILES_USER}"
123+
124+
echo "Running install.sh..."
125+
126+
if [ -n "$${DOTFILES_URI// }" ]; then
127+
if [ -z "$DOTFILES_USER" ]; then
128+
DOTFILES_USER="$USER"
129+
fi
130+
131+
echo "✨ Applying dotfiles for user $DOTFILES_USER"
132+
133+
if [ "$DOTFILES_USER" = "$USER" ]; then
134+
coder dotfiles "$DOTFILES_URI" -y 2>&1 | tee ~/.dotfiles.log
135+
else
136+
# The `eval echo ~"$DOTFILES_USER"` part is used to dynamically get the home directory of the user, see https://superuser.com/a/484280
137+
# eval echo ~coder -> "/home/coder"
138+
# eval echo ~root -> "/root"
139+
140+
CODER_BIN=$(which coder)
141+
DOTFILES_USER_HOME=$(eval echo ~"$DOTFILES_USER")
142+
sudo -u "$DOTFILES_USER" sh -c "'$CODER_BIN' dotfiles '$DOTFILES_URI' -y 2>&1 | tee '$DOTFILES_USER_HOME'/.dotfiles.log"
143+
fi
144+
fi
145+
146+
echo "Dotfiles installation complete."

0 commit comments

Comments
 (0)