devcontainer provivioning update 11-14-25
This commit is contained in:
parent
9c8e3a2b22
commit
46637f6759
@ -4,39 +4,28 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"zlh-agent/internal/provision"
|
"zlh-agent/internal/provision/executil"
|
||||||
"zlh-agent/internal/provision/addons"
|
"zlh-agent/internal/provision/markers"
|
||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Install installs the code-server addon.
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- Addon-only (not devcontainer-specific)
|
|
||||||
- No assumptions about users or workspaces
|
|
||||||
- Idempotent via addon marker
|
|
||||||
*/
|
|
||||||
func Install(cfg state.Config) error {
|
func Install(cfg state.Config) error {
|
||||||
|
const marker = "addon-codeserver"
|
||||||
|
|
||||||
if addons.IsAddonProvisioned("codeserver") {
|
if markers.IsPresent(marker) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptPath := filepath.Join(
|
scriptPath := filepath.Join(
|
||||||
provision.ScriptsRoot,
|
executil.ScriptsRoot,
|
||||||
"addons",
|
"addons",
|
||||||
"codeserver",
|
"codeserver",
|
||||||
"install.sh",
|
"install.sh",
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := provision.RunScript(scriptPath); err != nil {
|
if err := executil.RunScript(scriptPath); err != nil {
|
||||||
return fmt.Errorf("codeserver install failed: %w", err)
|
return fmt.Errorf("codeserver install failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := addons.WriteAddonMarker("codeserver"); err != nil {
|
return markers.Write(marker)
|
||||||
return fmt.Errorf("failed to write codeserver marker: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,17 +2,17 @@ package codeserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"zlh-agent/internal/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
const marker = "/opt/zlh/.zlh/addons/code-server.installed"
|
||||||
Verify validates that code-server is installed and usable.
|
|
||||||
|
|
||||||
This does NOT start the service.
|
func Verify() error {
|
||||||
*/
|
|
||||||
func Verify(cfg state.Config) error {
|
if _, err := os.Stat(marker); err != nil {
|
||||||
|
return fmt.Errorf("code-server addon marker missing")
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := exec.LookPath("code-server"); err != nil {
|
if _, err := exec.LookPath("code-server"); err != nil {
|
||||||
return fmt.Errorf("code-server binary not found in PATH")
|
return fmt.Errorf("code-server binary not found in PATH")
|
||||||
|
|||||||
@ -12,46 +12,19 @@ import (
|
|||||||
"zlh-agent/internal/provision/devcontainer/python"
|
"zlh-agent/internal/provision/devcontainer/python"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Provision — entrypoint for dev container provisioning.
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- This function ONLY performs installation.
|
|
||||||
- Verification happens in ensureProvisioned().
|
|
||||||
- state.Config is treated as immutable desired state.
|
|
||||||
- Routing is based strictly on cfg.Runtime.
|
|
||||||
*/
|
|
||||||
func Provision(cfg state.Config) error {
|
func Provision(cfg state.Config) error {
|
||||||
|
|
||||||
runtime := strings.ToLower(cfg.Runtime)
|
runtime := strings.ToLower(cfg.Runtime)
|
||||||
|
|
||||||
switch runtime {
|
switch runtime {
|
||||||
|
|
||||||
case "node":
|
case "node":
|
||||||
if err := node.Install(cfg); err != nil {
|
return node.Install(cfg)
|
||||||
return fmt.Errorf("node devcontainer install failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "python":
|
case "python":
|
||||||
if err := python.Install(cfg); err != nil {
|
return python.Install(cfg)
|
||||||
return fmt.Errorf("python devcontainer install failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "go":
|
case "go":
|
||||||
if err := devgo.Install(cfg); err != nil {
|
return devgo.Install(cfg)
|
||||||
return fmt.Errorf("go devcontainer install failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "java":
|
case "java":
|
||||||
if err := java.Install(cfg); err != nil {
|
return java.Install(cfg)
|
||||||
return fmt.Errorf("java devcontainer install failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported dev container runtime: %s", runtime)
|
return fmt.Errorf("unsupported dev container runtime: %s", runtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO NOT VERIFY HERE.
|
|
||||||
// Verification happens in ensureProvisioned().
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,43 +4,28 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"zlh-agent/internal/provision"
|
"zlh-agent/internal/provision/executil"
|
||||||
"zlh-agent/internal/provision/devcontainer"
|
"zlh-agent/internal/provision/markers"
|
||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Install installs the Go dev container runtime.
|
|
||||||
|
|
||||||
Execution model:
|
|
||||||
- Uses local, versioned install scripts from the agent repo
|
|
||||||
- Scripts handle downloading and installing Go
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- This function ONLY installs
|
|
||||||
- No verification here
|
|
||||||
*/
|
|
||||||
func Install(cfg state.Config) error {
|
func Install(cfg state.Config) error {
|
||||||
|
const marker = "devcontainer-go"
|
||||||
|
|
||||||
// Idempotency guard
|
if markers.IsPresent(marker) {
|
||||||
if devcontainer.IsProvisioned() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptPath := filepath.Join(
|
scriptPath := filepath.Join(
|
||||||
provision.ScriptsRoot,
|
executil.ScriptsRoot,
|
||||||
"devcontainer",
|
"devcontainer",
|
||||||
"go",
|
"go",
|
||||||
"install.sh",
|
"install.sh",
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := provision.RunScript(scriptPath); err != nil {
|
if err := executil.RunScript(scriptPath); err != nil {
|
||||||
return fmt.Errorf("go devcontainer install failed: %w", err)
|
return fmt.Errorf("go devcontainer install failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := devcontainer.WriteReadyMarker("go"); err != nil {
|
return markers.Write(marker)
|
||||||
return fmt.Errorf("failed to write devcontainer ready marker: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,30 +7,9 @@ import (
|
|||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Verify validates that the Go dev container runtime is usable.
|
|
||||||
|
|
||||||
Responsibilities:
|
|
||||||
- Ensure `go` binary is present and executable
|
|
||||||
- Ensure GOPATH / GOMOD usage won’t immediately fail
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- No installation here
|
|
||||||
- No state mutation
|
|
||||||
- Safe to call multiple times
|
|
||||||
*/
|
|
||||||
func Verify(cfg state.Config) error {
|
func Verify(cfg state.Config) error {
|
||||||
|
|
||||||
// Check go binary
|
|
||||||
if _, err := exec.LookPath("go"); err != nil {
|
if _, err := exec.LookPath("go"); err != nil {
|
||||||
return fmt.Errorf("go binary not found in PATH")
|
return fmt.Errorf("go binary not found in PATH")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional sanity check: go env should run
|
|
||||||
cmd := exec.Command("go", "env")
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return fmt.Errorf("go env failed to execute")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,44 +4,27 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"zlh-agent/internal/provision"
|
"zlh-agent/internal/provision/executil"
|
||||||
"zlh-agent/internal/provision/devcontainer"
|
"zlh-agent/internal/provision/markers"
|
||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Install installs the Java dev container runtime.
|
|
||||||
|
|
||||||
Execution model:
|
|
||||||
- Uses local, versioned install scripts from the agent repo
|
|
||||||
- Does NOT assume Minecraft semantics
|
|
||||||
- Provides a general-purpose Java environment for development
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- This function ONLY installs
|
|
||||||
- No verification here
|
|
||||||
*/
|
|
||||||
func Install(cfg state.Config) error {
|
func Install(cfg state.Config) error {
|
||||||
|
|
||||||
// Idempotency guard
|
if markers.IsPresent("devcontainer-java") {
|
||||||
if devcontainer.IsProvisioned() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptPath := filepath.Join(
|
scriptPath := filepath.Join(
|
||||||
provision.ScriptsRoot,
|
executil.ScriptsRoot,
|
||||||
"devcontainer",
|
"devcontainer",
|
||||||
"java",
|
"java",
|
||||||
"install.sh",
|
"install.sh",
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := provision.RunScript(scriptPath); err != nil {
|
if err := executil.RunScript(scriptPath); err != nil {
|
||||||
return fmt.Errorf("java devcontainer install failed: %w", err)
|
return fmt.Errorf("java devcontainer install failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := devcontainer.WriteReadyMarker("java"); err != nil {
|
return markers.Write("devcontainer-java")
|
||||||
return fmt.Errorf("failed to write devcontainer ready marker: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,29 +7,9 @@ import (
|
|||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Verify validates that the Java dev container runtime is usable.
|
|
||||||
|
|
||||||
Responsibilities:
|
|
||||||
- Ensure `java` binary is present and executable
|
|
||||||
- Ensure `javac` is present (development use case)
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- No installation here
|
|
||||||
- No state mutation
|
|
||||||
- Safe to call multiple times
|
|
||||||
*/
|
|
||||||
func Verify(cfg state.Config) error {
|
func Verify(cfg state.Config) error {
|
||||||
|
|
||||||
// Check java runtime
|
|
||||||
if _, err := exec.LookPath("java"); err != nil {
|
if _, err := exec.LookPath("java"); err != nil {
|
||||||
return fmt.Errorf("java binary not found in PATH")
|
return fmt.Errorf("java binary not found in PATH")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Java compiler (dev requirement)
|
|
||||||
if _, err := exec.LookPath("javac"); err != nil {
|
|
||||||
return fmt.Errorf("javac binary not found in PATH")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,46 +4,28 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"zlh-agent/internal/provision"
|
"zlh-agent/internal/provision/executil"
|
||||||
"zlh-agent/internal/provision/devcontainer"
|
"zlh-agent/internal/provision/markers"
|
||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Install installs the Node.js dev container runtime.
|
|
||||||
|
|
||||||
Execution model:
|
|
||||||
- Uses local, versioned install scripts from the agent repo
|
|
||||||
- Scripts are responsible for fetching artifacts
|
|
||||||
- Artifact server is NOT an execution source
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- This function ONLY installs
|
|
||||||
- No verification here
|
|
||||||
*/
|
|
||||||
func Install(cfg state.Config) error {
|
func Install(cfg state.Config) error {
|
||||||
|
const marker = "devcontainer-node"
|
||||||
|
|
||||||
// Idempotency guard
|
if markers.IsPresent(marker) {
|
||||||
if devcontainer.IsProvisioned() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local script path (inside agent repo / container)
|
|
||||||
scriptPath := filepath.Join(
|
scriptPath := filepath.Join(
|
||||||
provision.ScriptsRoot,
|
executil.ScriptsRoot,
|
||||||
"devcontainer",
|
"devcontainer",
|
||||||
"node",
|
"node",
|
||||||
"install.sh",
|
"install.sh",
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := provision.RunScript(scriptPath); err != nil {
|
if err := executil.RunScript(scriptPath); err != nil {
|
||||||
return fmt.Errorf("node devcontainer install failed: %w", err)
|
return fmt.Errorf("node devcontainer install failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark devcontainer ready
|
return markers.Write(marker)
|
||||||
if err := devcontainer.WriteReadyMarker("node"); err != nil {
|
|
||||||
return fmt.Errorf("failed to write devcontainer ready marker: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,26 +7,13 @@ import (
|
|||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Verify validates that the Node.js dev container runtime is usable.
|
|
||||||
|
|
||||||
Responsibilities:
|
|
||||||
- Ensure `node` is present and executable
|
|
||||||
- Ensure `npm` is present and executable
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- No installation here
|
|
||||||
- No state mutation
|
|
||||||
- Safe to call multiple times
|
|
||||||
*/
|
|
||||||
func Verify(cfg state.Config) error {
|
func Verify(cfg state.Config) error {
|
||||||
|
|
||||||
// Check node binary
|
// Version is optional at verify-time; existence is authoritative
|
||||||
if _, err := exec.LookPath("node"); err != nil {
|
if _, err := exec.LookPath("node"); err != nil {
|
||||||
return fmt.Errorf("node binary not found in PATH")
|
return fmt.Errorf("node binary not found in PATH")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check npm binary
|
|
||||||
if _, err := exec.LookPath("npm"); err != nil {
|
if _, err := exec.LookPath("npm"); err != nil {
|
||||||
return fmt.Errorf("npm binary not found in PATH")
|
return fmt.Errorf("npm binary not found in PATH")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,43 +4,28 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"zlh-agent/internal/provision"
|
"zlh-agent/internal/provision/executil"
|
||||||
"zlh-agent/internal/provision/devcontainer"
|
"zlh-agent/internal/provision/markers"
|
||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Install installs the Python dev container runtime.
|
|
||||||
|
|
||||||
Execution model:
|
|
||||||
- Uses local, versioned install scripts from the agent repo
|
|
||||||
- Scripts are responsible for fetching artifacts if needed
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- This function ONLY installs
|
|
||||||
- No verification here
|
|
||||||
*/
|
|
||||||
func Install(cfg state.Config) error {
|
func Install(cfg state.Config) error {
|
||||||
|
const marker = "devcontainer-python"
|
||||||
|
|
||||||
// Idempotency guard
|
if markers.IsPresent(marker) {
|
||||||
if devcontainer.IsProvisioned() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptPath := filepath.Join(
|
scriptPath := filepath.Join(
|
||||||
provision.ScriptsRoot,
|
executil.ScriptsRoot,
|
||||||
"devcontainer",
|
"devcontainer",
|
||||||
"python",
|
"python",
|
||||||
"install.sh",
|
"install.sh",
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := provision.RunScript(scriptPath); err != nil {
|
if err := executil.RunScript(scriptPath); err != nil {
|
||||||
return fmt.Errorf("python devcontainer install failed: %w", err)
|
return fmt.Errorf("python devcontainer install failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := devcontainer.WriteReadyMarker("python"); err != nil {
|
return markers.Write(marker)
|
||||||
return fmt.Errorf("failed to write devcontainer ready marker: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,29 +7,12 @@ import (
|
|||||||
"zlh-agent/internal/state"
|
"zlh-agent/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
Verify validates that the Python dev container runtime is usable.
|
|
||||||
|
|
||||||
Responsibilities:
|
|
||||||
- Ensure `python3` is present and executable
|
|
||||||
- Ensure `pip3` is present and executable
|
|
||||||
|
|
||||||
IMPORTANT:
|
|
||||||
- No installation here
|
|
||||||
- No state mutation
|
|
||||||
- Safe to call multiple times
|
|
||||||
*/
|
|
||||||
func Verify(cfg state.Config) error {
|
func Verify(cfg state.Config) error {
|
||||||
|
|
||||||
// Check python3 binary
|
|
||||||
if _, err := exec.LookPath("python3"); err != nil {
|
if _, err := exec.LookPath("python3"); err != nil {
|
||||||
return fmt.Errorf("python3 binary not found in PATH")
|
return fmt.Errorf("python3 binary not found in PATH")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check pip3 binary
|
|
||||||
if _, err := exec.LookPath("pip3"); err != nil {
|
if _, err := exec.LookPath("pip3"); err != nil {
|
||||||
return fmt.Errorf("pip3 binary not found in PATH")
|
return fmt.Errorf("pip3 binary not found in PATH")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
15
internal/provision/executil/executil.go
Normal file
15
internal/provision/executil/executil.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package executil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ScriptsRoot = "/opt/zlh-agent/scripts"
|
||||||
|
|
||||||
|
func RunScript(path string) error {
|
||||||
|
cmd := exec.Command("/bin/bash", path)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
22
internal/provision/markers/markers.go
Normal file
22
internal/provision/markers/markers.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package markers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
baseDir = "/opt/zlh/.zlh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsPresent(name string) bool {
|
||||||
|
_, err := os.Stat(filepath.Join(baseDir, name))
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Write(name string) error {
|
||||||
|
if err := os.MkdirAll(baseDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.WriteFile(filepath.Join(baseDir, name), []byte("ok"), 0644)
|
||||||
|
}
|
||||||
@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "[addon:codeserver] installing code-server"
|
|
||||||
|
|
||||||
# Official install script (can be replaced later if you want full control)
|
|
||||||
curl -fsSL https://code-server.dev/install.sh | sh
|
|
||||||
|
|
||||||
echo "[addon:codeserver] install complete"
|
|
||||||
60
scripts/devcontainer/codeserver/install.sh
Normal file
60
scripts/devcontainer/codeserver/install.sh
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "[code-server] starting install"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Config
|
||||||
|
# --------------------------------------------------
|
||||||
|
ADDON_ROOT="/opt/zlh/addons/code-server"
|
||||||
|
ARTIFACT_DIR="/opt/zlh/addons/code-server"
|
||||||
|
MARKER="/opt/zlh/.zlh/addons/code-server.installed"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "${MARKER}")"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Idempotency
|
||||||
|
# --------------------------------------------------
|
||||||
|
if [ -f "${MARKER}" ]; then
|
||||||
|
echo "[code-server] already installed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARCHIVE="$(ls ${ARTIFACT_DIR}/code-server.* 2>/dev/null | head -n1)"
|
||||||
|
if [ -z "${ARCHIVE}" ]; then
|
||||||
|
echo "[code-server][ERROR] artifact not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[code-server] extracting ${ARCHIVE}"
|
||||||
|
mkdir -p "${ADDON_ROOT}"
|
||||||
|
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
|
||||||
|
case "${ARCHIVE}" in
|
||||||
|
*.tar.gz)
|
||||||
|
tar -xzf "${ARCHIVE}" -C "${TMP_DIR}"
|
||||||
|
;;
|
||||||
|
*.zip)
|
||||||
|
unzip -q "${ARCHIVE}" -d "${TMP_DIR}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "[code-server][ERROR] unsupported archive format"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
EXTRACTED_DIR="$(find ${TMP_DIR} -maxdepth 1 -type d -name 'code-server*' | head -n1)"
|
||||||
|
if [ -z "${EXTRACTED_DIR}" ]; then
|
||||||
|
echo "[code-server][ERROR] failed to locate extracted directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "${EXTRACTED_DIR}"/* "${ADDON_ROOT}/"
|
||||||
|
rm -rf "${TMP_DIR}"
|
||||||
|
|
||||||
|
chmod +x "${ADDON_ROOT}/bin/code-server"
|
||||||
|
|
||||||
|
touch "${MARKER}"
|
||||||
|
|
||||||
|
echo "[code-server] install complete"
|
||||||
@ -1,12 +1,51 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
echo "[go] starting go devcontainer install"
|
||||||
source "${SCRIPT_DIR}/../workspace.sh"
|
|
||||||
|
|
||||||
echo "[devcontainer:go] installing golang"
|
# --------------------------------------------------
|
||||||
|
# Config
|
||||||
|
# --------------------------------------------------
|
||||||
|
RUNTIME_ROOT="/opt/zlh/runtime/go"
|
||||||
|
ARTIFACT_ROOT="/opt/zlh/devcontainer/go"
|
||||||
|
|
||||||
apt update
|
VERSION="${RUNTIME_VERSION:-1.25}"
|
||||||
apt install -y golang
|
|
||||||
|
|
||||||
echo "[devcontainer:go] install complete"
|
SRC_DIR="${ARTIFACT_ROOT}/${VERSION}"
|
||||||
|
DEST_DIR="${RUNTIME_ROOT}/${VERSION}"
|
||||||
|
SYMLINK="${RUNTIME_ROOT}/current"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Guards
|
||||||
|
# --------------------------------------------------
|
||||||
|
if [ ! -d "${SRC_DIR}" ]; then
|
||||||
|
echo "[go][ERROR] artifact directory not found: ${SRC_DIR}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -x "${DEST_DIR}/bin/go" ]; then
|
||||||
|
echo "[go] go ${VERSION} already installed"
|
||||||
|
else
|
||||||
|
ARCHIVE="$(ls ${SRC_DIR}/go*.linux-amd64.tar.gz 2>/dev/null | head -n1)"
|
||||||
|
if [ -z "${ARCHIVE}" ]; then
|
||||||
|
echo "[go][ERROR] no Go archive found in ${SRC_DIR}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[go] installing go ${VERSION}"
|
||||||
|
mkdir -p "${DEST_DIR}"
|
||||||
|
|
||||||
|
tar -xzf "${ARCHIVE}" -C "${DEST_DIR}" --strip-components=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Symlink current
|
||||||
|
# --------------------------------------------------
|
||||||
|
ln -sfn "${DEST_DIR}" "${SYMLINK}"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Verify
|
||||||
|
# --------------------------------------------------
|
||||||
|
"${SYMLINK}/bin/go" version
|
||||||
|
|
||||||
|
echo "[go] go ${VERSION} install complete"
|
||||||
|
|||||||
@ -1,12 +1,62 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
echo "[java] starting java devcontainer install"
|
||||||
source "${SCRIPT_DIR}/../workspace.sh"
|
|
||||||
|
|
||||||
echo "[devcontainer:java] installing jdk"
|
# --------------------------------------------------
|
||||||
|
# Config
|
||||||
|
# --------------------------------------------------
|
||||||
|
RUNTIME_ROOT="/opt/zlh/runtime/java"
|
||||||
|
ARTIFACT_ROOT="/opt/zlh/devcontainer/java"
|
||||||
|
|
||||||
apt update
|
VERSION="${RUNTIME_VERSION:-17}"
|
||||||
apt install -y openjdk-21-jdk
|
|
||||||
|
|
||||||
echo "[devcontainer:java] install complete"
|
SRC_DIR="${ARTIFACT_ROOT}/${VERSION}"
|
||||||
|
DEST_DIR="${RUNTIME_ROOT}/${VERSION}"
|
||||||
|
SYMLINK="${RUNTIME_ROOT}/current"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Guards
|
||||||
|
# --------------------------------------------------
|
||||||
|
if [ ! -d "${SRC_DIR}" ]; then
|
||||||
|
echo "[java][ERROR] artifact directory not found: ${SRC_DIR}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -x "${DEST_DIR}/bin/java" ]; then
|
||||||
|
echo "[java] java ${VERSION} already installed"
|
||||||
|
else
|
||||||
|
ARCHIVE="$(ls ${SRC_DIR}/*.tar.gz 2>/dev/null | head -n1)"
|
||||||
|
if [ -z "${ARCHIVE}" ]; then
|
||||||
|
echo "[java][ERROR] no Java archive found in ${SRC_DIR}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[java] installing java ${VERSION}"
|
||||||
|
mkdir -p "${DEST_DIR}"
|
||||||
|
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
tar -xzf "${ARCHIVE}" -C "${TMP_DIR}"
|
||||||
|
|
||||||
|
# Java archives contain a single root dir
|
||||||
|
EXTRACTED_DIR="$(find ${TMP_DIR} -maxdepth 1 -type d -name 'jdk*' | head -n1)"
|
||||||
|
if [ -z "${EXTRACTED_DIR}" ]; then
|
||||||
|
echo "[java][ERROR] failed to locate extracted jdk directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "${EXTRACTED_DIR}"/* "${DEST_DIR}/"
|
||||||
|
rm -rf "${TMP_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Symlink current
|
||||||
|
# --------------------------------------------------
|
||||||
|
ln -sfn "${DEST_DIR}" "${SYMLINK}"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Verify
|
||||||
|
# --------------------------------------------------
|
||||||
|
"${SYMLINK}/bin/java" -version
|
||||||
|
|
||||||
|
echo "[java] java ${VERSION} install complete"
|
||||||
|
|||||||
@ -1,12 +1,40 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
RUNTIME_ROOT="/opt/zlh/runtime/node"
|
||||||
source "${SCRIPT_DIR}/../workspace.sh"
|
ARTIFACT_ROOT="/opt/zlh/devcontainer/node"
|
||||||
|
|
||||||
echo "[devcontainer:node] installing nodejs"
|
VERSION="${RUNTIME_VERSION:-24}"
|
||||||
|
|
||||||
apt update
|
ARCHIVE="node-v${VERSION}.*-linux-x64.tar.xz"
|
||||||
apt install -y nodejs npm
|
SRC_DIR="${ARTIFACT_ROOT}/${VERSION}"
|
||||||
|
DEST_DIR="${RUNTIME_ROOT}/${VERSION}"
|
||||||
|
|
||||||
echo "[devcontainer:node] install complete"
|
echo "[node] Installing Node.js version ${VERSION}"
|
||||||
|
|
||||||
|
# Ensure runtime root exists
|
||||||
|
mkdir -p "${RUNTIME_ROOT}"
|
||||||
|
|
||||||
|
# Idempotency check
|
||||||
|
if [ -d "${DEST_DIR}" ]; then
|
||||||
|
echo "[node] Node ${VERSION} already installed"
|
||||||
|
else
|
||||||
|
ARCHIVE_PATH=$(ls "${SRC_DIR}/${ARCHIVE}" 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [ -z "${ARCHIVE_PATH}" ]; then
|
||||||
|
echo "[node][ERROR] Artifact not found for Node ${VERSION}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[node] Extracting ${ARCHIVE_PATH}"
|
||||||
|
mkdir -p "${DEST_DIR}"
|
||||||
|
tar -xJf "${ARCHIVE_PATH}" -C "${DEST_DIR}" --strip-components=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update stable symlink
|
||||||
|
ln -sfn "${DEST_DIR}" "${RUNTIME_ROOT}/current"
|
||||||
|
|
||||||
|
# Permissions sanity
|
||||||
|
chmod -R 755 "${DEST_DIR}"
|
||||||
|
|
||||||
|
echo "[node] Node ${VERSION} installed successfully"
|
||||||
|
|||||||
@ -1,12 +1,72 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
echo "[python] starting python devcontainer install"
|
||||||
source "${SCRIPT_DIR}/../workspace.sh"
|
|
||||||
|
|
||||||
echo "[devcontainer:python] installing python"
|
# --------------------------------------------------
|
||||||
|
# Config
|
||||||
|
# --------------------------------------------------
|
||||||
|
RUNTIME_ROOT="/opt/zlh/runtime/python"
|
||||||
|
ARTIFACT_ROOT="/opt/zlh/devcontainer/python"
|
||||||
|
|
||||||
apt update
|
VERSION="${RUNTIME_VERSION:-3.12}"
|
||||||
apt install -y python3 python3-pip python3-venv
|
|
||||||
|
|
||||||
echo "[devcontainer:python] install complete"
|
SRC_DIR="${ARTIFACT_ROOT}/${VERSION}"
|
||||||
|
DEST_DIR="${RUNTIME_ROOT}/${VERSION}"
|
||||||
|
SYMLINK="${RUNTIME_ROOT}/current"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Guards
|
||||||
|
# --------------------------------------------------
|
||||||
|
if [ ! -d "${SRC_DIR}" ]; then
|
||||||
|
echo "[python][ERROR] artifact directory not found: ${SRC_DIR}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -x "${DEST_DIR}/bin/python3" ]; then
|
||||||
|
echo "[python] python ${VERSION} already installed"
|
||||||
|
else
|
||||||
|
ARCHIVE="$(ls ${SRC_DIR}/Python-*.tgz 2>/dev/null | head -n1)"
|
||||||
|
if [ -z "${ARCHIVE}" ]; then
|
||||||
|
echo "[python][ERROR] no Python archive found in ${SRC_DIR}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[python] building python ${VERSION}"
|
||||||
|
mkdir -p "${DEST_DIR}"
|
||||||
|
|
||||||
|
BUILD_DIR="$(mktemp -d)"
|
||||||
|
tar -xzf "${ARCHIVE}" -C "${BUILD_DIR}"
|
||||||
|
|
||||||
|
PY_SRC="$(find ${BUILD_DIR} -maxdepth 1 -type d -name 'Python-*' | head -n1)"
|
||||||
|
if [ -z "${PY_SRC}" ]; then
|
||||||
|
echo "[python][ERROR] failed to locate extracted python source"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "${PY_SRC}"
|
||||||
|
|
||||||
|
./configure \
|
||||||
|
--prefix="${DEST_DIR}" \
|
||||||
|
--enable-optimizations \
|
||||||
|
--with-ensurepip=install
|
||||||
|
|
||||||
|
make -j"$(nproc)"
|
||||||
|
make install
|
||||||
|
|
||||||
|
cd /
|
||||||
|
rm -rf "${BUILD_DIR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Symlink current
|
||||||
|
# --------------------------------------------------
|
||||||
|
ln -sfn "${DEST_DIR}" "${SYMLINK}"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# Verify
|
||||||
|
# --------------------------------------------------
|
||||||
|
"${SYMLINK}/bin/python3" --version
|
||||||
|
"${SYMLINK}/bin/pip3" --version
|
||||||
|
|
||||||
|
echo "[python] python ${VERSION} install complete"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user