From b5445ee3c8fc980f17ff6790dc1ad886b5280c31 Mon Sep 17 00:00:00 2001 From: Michael Radwin Date: Tue, 26 May 2026 13:49:43 -0700 Subject: [PATCH 1/3] Initial checkin of minimal ttyd Workbench devcontainer --- src/ttyd/.devcontainer.json | 26 +++++++++++++++++ src/ttyd/README.md | 43 +++++++++++++++++++++++++++++ src/ttyd/devcontainer-template.json | 21 ++++++++++++++ src/ttyd/docker-compose.yaml | 38 +++++++++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 src/ttyd/.devcontainer.json create mode 100644 src/ttyd/README.md create mode 100644 src/ttyd/devcontainer-template.json create mode 100644 src/ttyd/docker-compose.yaml diff --git a/src/ttyd/.devcontainer.json b/src/ttyd/.devcontainer.json new file mode 100644 index 00000000..9a8965bb --- /dev/null +++ b/src/ttyd/.devcontainer.json @@ -0,0 +1,26 @@ +{ + "name": "ttyd", + "dockerComposeFile": "docker-compose.yaml", + "service": "app", + "shutdownAction": "none", + "workspaceFolder": "/workspace", + "postCreateCommand": [ + "./startupscript/post-startup.sh", + "abc", + "/home/abc", + "${templateOption:cloud}", + "${templateOption:login}" + ], + "postStartCommand": [ + "./startupscript/remount-on-restart.sh", + "abc", + "/home/abc", + "${templateOption:cloud}", + "${templateOption:login}" + ], + "features": { + "ghcr.io/ar90n/devcontainer-features/ttyd:1": {}, + "ghcr.io/dhoeric/features/google-cloud-cli:1": {} + }, + "remoteUser": "root" +} diff --git a/src/ttyd/README.md b/src/ttyd/README.md new file mode 100644 index 00000000..610612a4 --- /dev/null +++ b/src/ttyd/README.md @@ -0,0 +1,43 @@ +# ttyd + +Custom Workbench application based on . + +## Configuration + +- **Image**: +- **Port**: 2222 +- **User**: abc +- **Home Directory**: /home/abc + +## Access + +Once deployed in Workbench, access your terminal at the app URL (port 2222). + +For local testing: +1. Create Docker network: `docker network create app-network` +2. Run the app: `devcontainer up --workspace-folder .` +3. Access at: `http://localhost:2222` + +## Customization + +Edit the following files to customize your app: + +- `.devcontainer.json` - Devcontainer configuration and features +- `docker-compose.yaml` - Docker Compose configuration (change the `command` to customize ttyd options) +- `devcontainer-template.json` - Template options and metadata + +## Testing + +To test this app template: + +```bash +cd test +./test.sh ttyd +``` + +## Usage + +1. Fork the repository +2. Modify the configuration files as needed +3. In Workbench UI, create a custom app pointing to your forked repository +4. Select this app template (ttyd) diff --git a/src/ttyd/devcontainer-template.json b/src/ttyd/devcontainer-template.json new file mode 100644 index 00000000..ed2143bb --- /dev/null +++ b/src/ttyd/devcontainer-template.json @@ -0,0 +1,21 @@ +{ + "id": "ttyd", + "version": "0.9.0", + "name": "ttyd", + "description": "Custom Workbench app: ttyd (Image: mcr.microsoft.com/devcontainers/base:ubuntu, Port: 2222, User: abc)", + "options": { + "cloud": { + "type": "string", + "enum": ["gcp", "aws"], + "default": "gcp", + "description": "Cloud provider (gcp or aws)" + }, + "login": { + "type": "string", + "description": "Whether to log in to workbench CLI", + "proposals": ["true", "false"], + "default": "false" + } + }, + "platforms": ["Any"] +} diff --git a/src/ttyd/docker-compose.yaml b/src/ttyd/docker-compose.yaml new file mode 100644 index 00000000..88f4bae6 --- /dev/null +++ b/src/ttyd/docker-compose.yaml @@ -0,0 +1,38 @@ +services: + app: + # The container name must be "application-server" + container_name: "application-server" + # This can be either a pre-existing image or built from a Dockerfile + image: "mcr.microsoft.com/devcontainers/base:ubuntu-24.04" + # build: + # context: . + restart: always + volumes: + - .:/workspace:cached + - work:/home/abc/work + user: abc + command: ["ttyd", "-W", "-p", "2222", "bash"] + # The port specified here will be forwarded and accessible from the + # Workbench UI. + ports: + - 2222:2222 + # The service must be connected to the "app-network" Docker network + networks: + - app-network + # SYS_ADMIN and fuse are required to mount workspace resources into the + # container. + cap_add: + - SYS_ADMIN + devices: + - /dev/fuse + security_opt: + - apparmor:unconfined + +volumes: + work: + +networks: + # The Docker network must be named "app-network". This is an external network + # that is created outside of this docker-compose file. + app-network: + external: true From 128d16023ebbb63ad83c4c320f135616268ba6aa Mon Sep 17 00:00:00 2001 From: Michael Radwin Date: Tue, 26 May 2026 15:28:08 -0700 Subject: [PATCH 2/3] Attempt to fix bugs in docker image previously pushed --- src/ttyd/.devcontainer.json | 19 ++++++++++--------- src/ttyd/devcontainer-template.json | 12 +++++++----- src/ttyd/docker-compose.yaml | 15 ++++++++++----- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/ttyd/.devcontainer.json b/src/ttyd/.devcontainer.json index 9a8965bb..af79527e 100644 --- a/src/ttyd/.devcontainer.json +++ b/src/ttyd/.devcontainer.json @@ -2,25 +2,26 @@ "name": "ttyd", "dockerComposeFile": "docker-compose.yaml", "service": "app", + "runServices": ["app"], "shutdownAction": "none", "workspaceFolder": "/workspace", - "postCreateCommand": [ - "./startupscript/post-startup.sh", - "abc", - "/home/abc", - "${templateOption:cloud}", - "${templateOption:login}" - ], + "postCreateCommand": + "./startupscript/post-startup.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh abc", "postStartCommand": [ "./startupscript/remount-on-restart.sh", "abc", - "/home/abc", + "/config", "${templateOption:cloud}", "${templateOption:login}" ], "features": { + "ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {}, "ghcr.io/ar90n/devcontainer-features/ttyd:1": {}, - "ghcr.io/dhoeric/features/google-cloud-cli:1": {} + "./.devcontainer/features/workbench-tools": { + "cloud": "${templateOption:cloud}", + "username": "abc", + "userHomeDir": "/config" + } }, "remoteUser": "root" } diff --git a/src/ttyd/devcontainer-template.json b/src/ttyd/devcontainer-template.json index ed2143bb..2c47a42e 100644 --- a/src/ttyd/devcontainer-template.json +++ b/src/ttyd/devcontainer-template.json @@ -1,14 +1,16 @@ { "id": "ttyd", + "description": "ttyd is a simple command-line tool for sharing terminal over the web", "version": "0.9.0", - "name": "ttyd", - "description": "Custom Workbench app: ttyd (Image: mcr.microsoft.com/devcontainers/base:ubuntu, Port: 2222, User: abc)", + "name": "Workbench minimal ttyd", + "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/add-ttyd-devcontainer/src/ttyd", + "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", "options": { "cloud": { "type": "string", - "enum": ["gcp", "aws"], - "default": "gcp", - "description": "Cloud provider (gcp or aws)" + "description": "VM cloud environment", + "proposals": ["gcp", "aws"], + "default": "gcp" }, "login": { "type": "string", diff --git a/src/ttyd/docker-compose.yaml b/src/ttyd/docker-compose.yaml index 88f4bae6..a1dfc12d 100644 --- a/src/ttyd/docker-compose.yaml +++ b/src/ttyd/docker-compose.yaml @@ -3,19 +3,24 @@ services: # The container name must be "application-server" container_name: "application-server" # This can be either a pre-existing image or built from a Dockerfile - image: "mcr.microsoft.com/devcontainers/base:ubuntu-24.04" - # build: - # context: . + image: "ubuntu:24.04" + # An unmodified Ubuntu image will exit immediately without a running process. + # If you are just testing it out, you can keep it alive with: + tty: true restart: always volumes: - .:/workspace:cached - work:/home/abc/work - user: abc command: ["ttyd", "-W", "-p", "2222", "bash"] # The port specified here will be forwarded and accessible from the # Workbench UI. ports: - - 2222:2222 + - 8822:2222 + - 8443:8443 + environment: + USER: "abc" + DEFAULT_WORKSPACE: "/config" + SUDO_PASSWORD: "pwd" # The service must be connected to the "app-network" Docker network networks: - app-network From e51bcc1f7d67a2e86adf2c160348acdd9406a6d4 Mon Sep 17 00:00:00 2001 From: Michael Radwin Date: Wed, 27 May 2026 10:13:33 -0700 Subject: [PATCH 3/3] More blind attempts to get a Workbench devcontainer working without debug logs --- src/ttyd/README.md | 42 +---------------------------- src/ttyd/devcontainer-template.json | 2 +- src/ttyd/docker-compose.yaml | 15 ++--------- src/ttyd/sudo-passwordless.sh | 34 +++++++++++++++++++++++ 4 files changed, 38 insertions(+), 55 deletions(-) create mode 100755 src/ttyd/sudo-passwordless.sh diff --git a/src/ttyd/README.md b/src/ttyd/README.md index 610612a4..b26d785a 100644 --- a/src/ttyd/README.md +++ b/src/ttyd/README.md @@ -1,43 +1,3 @@ # ttyd -Custom Workbench application based on . - -## Configuration - -- **Image**: -- **Port**: 2222 -- **User**: abc -- **Home Directory**: /home/abc - -## Access - -Once deployed in Workbench, access your terminal at the app URL (port 2222). - -For local testing: -1. Create Docker network: `docker network create app-network` -2. Run the app: `devcontainer up --workspace-folder .` -3. Access at: `http://localhost:2222` - -## Customization - -Edit the following files to customize your app: - -- `.devcontainer.json` - Devcontainer configuration and features -- `docker-compose.yaml` - Docker Compose configuration (change the `command` to customize ttyd options) -- `devcontainer-template.json` - Template options and metadata - -## Testing - -To test this app template: - -```bash -cd test -./test.sh ttyd -``` - -## Usage - -1. Fork the repository -2. Modify the configuration files as needed -3. In Workbench UI, create a custom app pointing to your forked repository -4. Select this app template (ttyd) +Custom Workbench application based on ttyd diff --git a/src/ttyd/devcontainer-template.json b/src/ttyd/devcontainer-template.json index 2c47a42e..dc527d9c 100644 --- a/src/ttyd/devcontainer-template.json +++ b/src/ttyd/devcontainer-template.json @@ -1,7 +1,7 @@ { "id": "ttyd", "description": "ttyd is a simple command-line tool for sharing terminal over the web", - "version": "0.9.0", + "version": "0.9.1", "name": "Workbench minimal ttyd", "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/add-ttyd-devcontainer/src/ttyd", "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", diff --git a/src/ttyd/docker-compose.yaml b/src/ttyd/docker-compose.yaml index a1dfc12d..4ae2da91 100644 --- a/src/ttyd/docker-compose.yaml +++ b/src/ttyd/docker-compose.yaml @@ -4,19 +4,14 @@ services: container_name: "application-server" # This can be either a pre-existing image or built from a Dockerfile image: "ubuntu:24.04" - # An unmodified Ubuntu image will exit immediately without a running process. - # If you are just testing it out, you can keep it alive with: - tty: true restart: always volumes: - .:/workspace:cached - - work:/home/abc/work - command: ["ttyd", "-W", "-p", "2222", "bash"] + command: ["ttyd", "-W", "-p", "8888", "bash"] # The port specified here will be forwarded and accessible from the # Workbench UI. ports: - - 8822:2222 - - 8443:8443 + - 8888:8888 environment: USER: "abc" DEFAULT_WORKSPACE: "/config" @@ -24,12 +19,6 @@ services: # The service must be connected to the "app-network" Docker network networks: - app-network - # SYS_ADMIN and fuse are required to mount workspace resources into the - # container. - cap_add: - - SYS_ADMIN - devices: - - /dev/fuse security_opt: - apparmor:unconfined diff --git a/src/ttyd/sudo-passwordless.sh b/src/ttyd/sudo-passwordless.sh new file mode 100755 index 00000000..d14bd0a1 --- /dev/null +++ b/src/ttyd/sudo-passwordless.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This script is used to set up passwordless sudo for the core user on the VM. +# It requires to be run with root priviledges and USER_NAME to be set in the environment. +# It is typically called from post-startup.sh. + +USER_NAME="${1}" + +if [[ -z "${USER_NAME}" ]]; then + echo "Usage: $0 " + exit 1 +fi + +sudoers_file="/etc/sudoers" +sudoers_d_file="/etc/sudoers.d/${USER_NAME}" + +# Make sure user exists +if ! id "${USER_NAME}" &>/dev/null; then + echo "User ${USER_NAME} does not exist." + exit 1 +fi + +# Check if there's an old rule in the main sudoers file that requires a password +if grep -q "^${USER_NAME} ALL=(ALL:ALL) ALL" "${sudoers_file}"; then + echo "Found password-requiring rule for ${USER_NAME} in /etc/sudoers. Commenting it out." + + # Comment out the old rule in /etc/sudoers + sed -i "s/^${USER_NAME} ALL=(ALL:ALL) ALL/# ${USER_NAME} ALL=(ALL:ALL) ALL/" "${sudoers_file}" +fi + +echo "${USER_NAME} ALL=(ALL) NOPASSWD:ALL" > "${sudoers_d_file}" +chmod 440 "${sudoers_d_file}" + +echo "User ${USER_NAME} has been given passwordless sudo access."