commit
3433cc4432
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
Binary file not shown.
After Width: | Height: | Size: 654 KiB |
@ -0,0 +1,48 @@
|
||||
---
|
||||
display_name: Git Config
|
||||
description: Stores Git configuration from Coder credentials
|
||||
icon: ../.icons/git.svg
|
||||
maintainer_github: coder
|
||||
verified: true
|
||||
tags: [helper, git]
|
||||
---
|
||||
|
||||
# git-config
|
||||
|
||||
Runs a script that updates git credentials in the workspace to match the user's Coder credentials, optionally allowing to the developer to override the defaults.
|
||||
|
||||
```hcl
|
||||
module "git-config" {
|
||||
source = "https://registry.coder.com/modules/git-config"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
||||
|
||||
TODO: Add screenshot
|
||||
|
||||
## Examples
|
||||
|
||||
### Allow users to override both username and email
|
||||
|
||||
```hcl
|
||||
module "git-config" {
|
||||
source = "https://registry.coder.com/modules/git-config"
|
||||
agent_id = coder_agent.example.id
|
||||
allow_email_change = true
|
||||
}
|
||||
```
|
||||
|
||||
TODO: Add screenshot
|
||||
|
||||
## Disallowing users from overriding both username and email
|
||||
|
||||
```hcl
|
||||
module "git-config" {
|
||||
source = "https://registry.coder.com/modules/git-config"
|
||||
agent_id = coder_agent.example.id
|
||||
allow_username_change = false
|
||||
allow_email_change = false
|
||||
}
|
||||
```
|
||||
|
||||
TODO: Add screenshot
|
@ -0,0 +1,43 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import {
|
||||
executeScriptInContainer,
|
||||
runTerraformApply,
|
||||
runTerraformInit,
|
||||
testRequiredVariables,
|
||||
} from "../test";
|
||||
|
||||
describe("git-config", async () => {
|
||||
await runTerraformInit(import.meta.dir);
|
||||
|
||||
testRequiredVariables(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
});
|
||||
|
||||
it("fails without git", async () => {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
});
|
||||
const output = await executeScriptInContainer(state, "alpine");
|
||||
expect(output.exitCode).toBe(1);
|
||||
expect(output.stdout).toEqual([
|
||||
"\u001B[0;1mChecking git-config!",
|
||||
"Git is not installed!",
|
||||
]);
|
||||
});
|
||||
|
||||
it("runs with git", async () => {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
});
|
||||
const output = await executeScriptInContainer(state, "alpine/git");
|
||||
expect(output.exitCode).toBe(0);
|
||||
expect(output.stdout).toEqual([
|
||||
"\u001B[0;1mChecking git-config!",
|
||||
"git-config: No user.email found, setting to ",
|
||||
"git-config: No user.name found, setting to default",
|
||||
"",
|
||||
"\u001B[0;1mgit-config: using email: ",
|
||||
"\u001B[0;1mgit-config: using username: default",
|
||||
]);
|
||||
});
|
||||
});
|
@ -0,0 +1,61 @@
|
||||
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."
|
||||
}
|
||||
|
||||
variable "allow_username_change" {
|
||||
type = bool
|
||||
description = "Allow developers to change their git username."
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "allow_email_change" {
|
||||
type = bool
|
||||
description = "Allow developers to change their git email."
|
||||
default = false
|
||||
}
|
||||
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
data "coder_parameter" "user_email" {
|
||||
count = var.allow_email_change ? 1 : 0
|
||||
name = "user_email"
|
||||
type = "string"
|
||||
default = ""
|
||||
description = "Git user.email to be used for commits. Leave empty to default to Coder username."
|
||||
display_name = "Git config user.email"
|
||||
mutable = true
|
||||
}
|
||||
|
||||
data "coder_parameter" "username" {
|
||||
count = var.allow_username_change ? 1 : 0
|
||||
name = "username"
|
||||
type = "string"
|
||||
default = ""
|
||||
description = "Git user.name to be used for commits. Leave empty to default to Coder username."
|
||||
display_name = "Git config user.name"
|
||||
mutable = true
|
||||
}
|
||||
|
||||
resource "coder_script" "git_config" {
|
||||
agent_id = var.agent_id
|
||||
script = templatefile("${path.module}/run.sh", {
|
||||
GIT_USERNAME = try(data.coder_parameter.username[0].value, "") == "" ? data.coder_workspace.me.owner : try(data.coder_parameter.username[0].value, "")
|
||||
GIT_EMAIL = try(data.coder_parameter.user_email[0].value, "") == "" ? data.coder_workspace.me.owner_email : try(data.coder_parameter.user_email[0].value, "")
|
||||
})
|
||||
display_name = "Git Config"
|
||||
icon = "/icon/git.svg"
|
||||
run_on_start = true
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
BOLD='\033[0;1m'
|
||||
printf "$${BOLD}Checking git-config!\n"
|
||||
|
||||
# Check if git is installed
|
||||
command -v git >/dev/null 2>&1 || {
|
||||
echo "Git is not installed!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Set git username and email if missing
|
||||
if [ -z $(git config --get user.email) ]; then
|
||||
printf "git-config: No user.email found, setting to ${GIT_EMAIL}\n"
|
||||
git config --global user.email "${GIT_EMAIL}"
|
||||
fi
|
||||
|
||||
if [ -z $(git config --get user.name) ]; then
|
||||
printf "git-config: No user.name found, setting to ${GIT_USERNAME}\n"
|
||||
git config --global user.name "${GIT_USERNAME}"
|
||||
fi
|
||||
|
||||
printf "\n$${BOLD}git-config: using email: $(git config --get user.email)\n"
|
||||
printf "$${BOLD}git-config: using username: $(git config --get user.name)\n\n"
|
@ -0,0 +1,21 @@
|
||||
---
|
||||
display_name: Jupyter Notebook
|
||||
description: A module that adds Jupyter Notebook in your Coder template.
|
||||
icon: ../.icons/jupyter.svg
|
||||
maintainer_github: coder
|
||||
verified: true
|
||||
tags: [jupyter, helper, ide, web]
|
||||
---
|
||||
|
||||
# Jupyter Notebook
|
||||
|
||||
A module that adds Jupyter Notebook in your Coder template.
|
||||
|
||||

|
||||
|
||||
```hcl
|
||||
module "jupyter-notebook" {
|
||||
source = "https://registry.coder.com/modules/jupyter-notebook"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
@ -0,0 +1,49 @@
|
||||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = ">= 0.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add required variables for your modules and remove any unneeded variables
|
||||
variable "agent_id" {
|
||||
type = string
|
||||
description = "The ID of a Coder agent."
|
||||
}
|
||||
|
||||
variable "log_path" {
|
||||
type = string
|
||||
description = "The path to log jupyter notebook to."
|
||||
default = "/tmp/jupyter-notebook.log"
|
||||
}
|
||||
|
||||
variable "port" {
|
||||
type = number
|
||||
description = "The port to run jupyter-notebook on."
|
||||
default = 19999
|
||||
}
|
||||
|
||||
resource "coder_script" "jupyter-notebook" {
|
||||
agent_id = var.agent_id
|
||||
display_name = "jupyter-notebook"
|
||||
icon = "/icon/jupyter.svg"
|
||||
script = templatefile("${path.module}/run.sh", {
|
||||
LOG_PATH : var.log_path,
|
||||
PORT : var.port
|
||||
})
|
||||
run_on_start = true
|
||||
}
|
||||
|
||||
resource "coder_app" "jupyter-notebook" {
|
||||
agent_id = var.agent_id
|
||||
slug = "jupyter-notebook"
|
||||
display_name = "Jupyter Notebook"
|
||||
url = "http://localhost:${var.port}"
|
||||
icon = "/icon/jupyter.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
BOLD='\033[0;1m'
|
||||
|
||||
printf "$${BOLD}Installing jupyter-notebook!\n"
|
||||
|
||||
# check if jupyter-notebook is installed
|
||||
if ! command -v jupyter-notebook >/dev/null 2>&1; then
|
||||
# install jupyter-notebook
|
||||
# check if python3 pip is installed
|
||||
if ! command -v pip3 >/dev/null 2>&1; then
|
||||
echo "pip3 is not installed"
|
||||
echo "Please install pip3 in your Dockerfile/VM image before running this script"
|
||||
exit 1
|
||||
fi
|
||||
# install jupyter-notebook
|
||||
pip3 install --upgrade --no-cache-dir --no-warn-script-location jupyter
|
||||
echo "🥳 jupyter-notebook has been installed\n\n"
|
||||
else
|
||||
echo "🥳 jupyter-notebook is already installed\n\n"
|
||||
fi
|
||||
|
||||
echo "👷 Starting jupyter-notebook in background..."
|
||||
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 &
|
@ -0,0 +1,96 @@
|
||||
import { readFile, readdir, stat } from "fs/promises";
|
||||
import * as path from "path";
|
||||
import * as marked from "marked";
|
||||
import grayMatter from "gray-matter";
|
||||
|
||||
const files = await readdir(".", { withFileTypes: true });
|
||||
const dirs = files.filter(
|
||||
(f) => f.isDirectory() && !f.name.startsWith(".") && f.name !== "node_modules"
|
||||
);
|
||||
|
||||
let badExit = false;
|
||||
|
||||
// error reports an error to the console and sets badExit to true
|
||||
// so that the process will exit with a non-zero exit code.
|
||||
const error = (...data: any[]) => {
|
||||
console.error(...data);
|
||||
badExit = true;
|
||||
}
|
||||
|
||||
// Ensures that each README has the proper format.
|
||||
// Exits with 0 if all is good!
|
||||
for (const dir of dirs) {
|
||||
const readme = path.join(dir.name, "README.md");
|
||||
// Ensure exists
|
||||
try {
|
||||
await stat(readme);
|
||||
} catch (ex) {
|
||||
throw new Error(`Missing README.md in ${dir.name}`);
|
||||
}
|
||||
const content = await readFile(readme, "utf8");
|
||||
const matter = grayMatter(content);
|
||||
const data = matter.data as {
|
||||
display_name?: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
maintainer_github?: string;
|
||||
partner_github?: string;
|
||||
verified?: boolean;
|
||||
tags?: string[];
|
||||
};
|
||||
if (!data.display_name) {
|
||||
error(dir.name, "missing display_name");
|
||||
}
|
||||
if (!data.description) {
|
||||
error(dir.name, "missing description");
|
||||
}
|
||||
if (!data.icon) {
|
||||
error(dir.name, "missing icon");
|
||||
}
|
||||
if (!data.maintainer_github) {
|
||||
error(dir.name, "missing maintainer_github");
|
||||
}
|
||||
try {
|
||||
await stat(path.join(".", dir.name, data.icon));
|
||||
} catch (ex) {
|
||||
error(dir.name, "icon does not exist", data.icon);
|
||||
}
|
||||
|
||||
const tokens = marked.lexer(content);
|
||||
// Ensure there is an h1 and some text, then a code block
|
||||
|
||||
let h1 = false;
|
||||
let code = false;
|
||||
let paragraph = false;
|
||||
|
||||
for (const token of tokens) {
|
||||
if (token.type === "heading" && token.depth === 1) {
|
||||
h1 = true;
|
||||
continue;
|
||||
}
|
||||
if (h1 && token.type === "heading") {
|
||||
break;
|
||||
}
|
||||
if (token.type === "paragraph") {
|
||||
paragraph = true;
|
||||
continue;
|
||||
}
|
||||
if (token.type === "code") {
|
||||
code = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!h1) {
|
||||
error(dir.name, "missing h1");
|
||||
}
|
||||
if (!paragraph) {
|
||||
error(dir.name, "missing paragraph after h1");
|
||||
}
|
||||
if (!code) {
|
||||
error(dir.name, "missing example code block after paragraph");
|
||||
}
|
||||
}
|
||||
|
||||
if (badExit) {
|
||||
process.exit(1);
|
||||
}
|
Loading…
Reference in New Issue