devcontainer provivioning update 11-14-25
This commit is contained in:
parent
9c8e3a2b22
commit
46637f6759
@ -4,39 +4,28 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"zlh-agent/internal/provision"
|
||||
"zlh-agent/internal/provision/addons"
|
||||
"zlh-agent/internal/provision/executil"
|
||||
"zlh-agent/internal/provision/markers"
|
||||
"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 {
|
||||
const marker = "addon-codeserver"
|
||||
|
||||
if addons.IsAddonProvisioned("codeserver") {
|
||||
if markers.IsPresent(marker) {
|
||||
return nil
|
||||
}
|
||||
|
||||
scriptPath := filepath.Join(
|
||||
provision.ScriptsRoot,
|
||||
executil.ScriptsRoot,
|
||||
"addons",
|
||||
"codeserver",
|
||||
"install.sh",
|
||||
)
|
||||
|
||||
if err := provision.RunScript(scriptPath); err != nil {
|
||||
if err := executil.RunScript(scriptPath); err != nil {
|
||||
return fmt.Errorf("codeserver install failed: %w", err)
|
||||
}
|
||||
|
||||
if err := addons.WriteAddonMarker("codeserver"); err != nil {
|
||||
return fmt.Errorf("failed to write codeserver marker: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return markers.Write(marker)
|
||||
}
|
||||
|
||||
@ -2,17 +2,17 @@ package codeserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"zlh-agent/internal/state"
|
||||
)
|
||||
|
||||
/*
|
||||
Verify validates that code-server is installed and usable.
|
||||
const marker = "/opt/zlh/.zlh/addons/code-server.installed"
|
||||
|
||||
This does NOT start the service.
|
||||
*/
|
||||
func Verify(cfg state.Config) error {
|
||||
func Verify() error {
|
||||
|
||||
if _, err := os.Stat(marker); err != nil {
|
||||
return fmt.Errorf("code-server addon marker missing")
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath("code-server"); err != nil {
|
||||
return fmt.Errorf("code-server binary not found in PATH")
|
||||
|
||||
@ -12,46 +12,19 @@ import (
|
||||
"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 {
|
||||
|
||||
runtime := strings.ToLower(cfg.Runtime)
|
||||
|
||||
switch runtime {
|
||||
|
||||
case "node":
|
||||
if err := node.Install(cfg); err != nil {
|
||||
return fmt.Errorf("node devcontainer install failed: %w", err)
|
||||
}
|
||||
|
||||
return node.Install(cfg)
|
||||
case "python":
|
||||
if err := python.Install(cfg); err != nil {
|
||||
return fmt.Errorf("python devcontainer install failed: %w", err)
|
||||
}
|
||||
|
||||
return python.Install(cfg)
|
||||
case "go":
|
||||
if err := devgo.Install(cfg); err != nil {
|
||||
return fmt.Errorf("go devcontainer install failed: %w", err)
|
||||
}
|
||||
|
||||
return devgo.Install(cfg)
|
||||
case "java":
|
||||
if err := java.Install(cfg); err != nil {
|
||||
return fmt.Errorf("java devcontainer install failed: %w", err)
|
||||
}
|
||||
|
||||
return java.Install(cfg)
|
||||
default:
|
||||
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"
|
||||
"path/filepath"
|
||||
|
||||
"zlh-agent/internal/provision"
|
||||
"zlh-agent/internal/provision/devcontainer"
|
||||
"zlh-agent/internal/provision/executil"
|
||||
"zlh-agent/internal/provision/markers"
|
||||
"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 {
|
||||
const marker = "devcontainer-go"
|
||||
|
||||
// Idempotency guard
|
||||
if devcontainer.IsProvisioned() {
|
||||
if markers.IsPresent(marker) {
|
||||
return nil
|
||||
}
|
||||
|
||||
scriptPath := filepath.Join(
|
||||
provision.ScriptsRoot,
|
||||
executil.ScriptsRoot,
|
||||
"devcontainer",
|
||||
"go",
|
||||
"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)
|
||||
}
|
||||
|
||||
if err := devcontainer.WriteReadyMarker("go"); err != nil {
|
||||
return fmt.Errorf("failed to write devcontainer ready marker: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return markers.Write(marker)
|
||||
}
|
||||
|
||||
@ -7,30 +7,9 @@ import (
|
||||
"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 {
|
||||
|
||||
// Check go binary
|
||||
if _, err := exec.LookPath("go"); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@ -4,44 +4,27 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"zlh-agent/internal/provision"
|
||||
"zlh-agent/internal/provision/devcontainer"
|
||||
"zlh-agent/internal/provision/executil"
|
||||
"zlh-agent/internal/provision/markers"
|
||||
"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 {
|
||||
|
||||
// Idempotency guard
|
||||
if devcontainer.IsProvisioned() {
|
||||
if markers.IsPresent("devcontainer-java") {
|
||||
return nil
|
||||
}
|
||||
|
||||
scriptPath := filepath.Join(
|
||||
provision.ScriptsRoot,
|
||||
executil.ScriptsRoot,
|
||||
"devcontainer",
|
||||
"java",
|
||||
"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)
|
||||
}
|
||||
|
||||
if err := devcontainer.WriteReadyMarker("java"); err != nil {
|
||||
return fmt.Errorf("failed to write devcontainer ready marker: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return markers.Write("devcontainer-java")
|
||||
}
|
||||
|
||||
@ -7,29 +7,9 @@ import (
|
||||
"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 {
|
||||
|
||||
// Check java runtime
|
||||
if _, err := exec.LookPath("java"); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@ -4,46 +4,28 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"zlh-agent/internal/provision"
|
||||
"zlh-agent/internal/provision/devcontainer"
|
||||
"zlh-agent/internal/provision/executil"
|
||||
"zlh-agent/internal/provision/markers"
|
||||
"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 {
|
||||
const marker = "devcontainer-node"
|
||||
|
||||
// Idempotency guard
|
||||
if devcontainer.IsProvisioned() {
|
||||
if markers.IsPresent(marker) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Local script path (inside agent repo / container)
|
||||
scriptPath := filepath.Join(
|
||||
provision.ScriptsRoot,
|
||||
executil.ScriptsRoot,
|
||||
"devcontainer",
|
||||
"node",
|
||||
"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)
|
||||
}
|
||||
|
||||
// Mark devcontainer ready
|
||||
if err := devcontainer.WriteReadyMarker("node"); err != nil {
|
||||
return fmt.Errorf("failed to write devcontainer ready marker: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return markers.Write(marker)
|
||||
}
|
||||
|
||||
@ -7,26 +7,13 @@ import (
|
||||
"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 {
|
||||
|
||||
// Check node binary
|
||||
// Version is optional at verify-time; existence is authoritative
|
||||
if _, err := exec.LookPath("node"); err != nil {
|
||||
return fmt.Errorf("node binary not found in PATH")
|
||||
}
|
||||
|
||||
// Check npm binary
|
||||
if _, err := exec.LookPath("npm"); err != nil {
|
||||
return fmt.Errorf("npm binary not found in PATH")
|
||||
}
|
||||
|
||||
@ -4,43 +4,28 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"zlh-agent/internal/provision"
|
||||
"zlh-agent/internal/provision/devcontainer"
|
||||
"zlh-agent/internal/provision/executil"
|
||||
"zlh-agent/internal/provision/markers"
|
||||
"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 {
|
||||
const marker = "devcontainer-python"
|
||||
|
||||
// Idempotency guard
|
||||
if devcontainer.IsProvisioned() {
|
||||
if markers.IsPresent(marker) {
|
||||
return nil
|
||||
}
|
||||
|
||||
scriptPath := filepath.Join(
|
||||
provision.ScriptsRoot,
|
||||
executil.ScriptsRoot,
|
||||
"devcontainer",
|
||||
"python",
|
||||
"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)
|
||||
}
|
||||
|
||||
if err := devcontainer.WriteReadyMarker("python"); err != nil {
|
||||
return fmt.Errorf("failed to write devcontainer ready marker: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return markers.Write(marker)
|
||||
}
|
||||
|
||||
@ -7,29 +7,12 @@ import (
|
||||
"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 {
|
||||
|
||||
// Check python3 binary
|
||||
if _, err := exec.LookPath("python3"); err != nil {
|
||||
return fmt.Errorf("python3 binary not found in PATH")
|
||||
}
|
||||
|
||||
// Check pip3 binary
|
||||
if _, err := exec.LookPath("pip3"); err != nil {
|
||||
return fmt.Errorf("pip3 binary not found in PATH")
|
||||
}
|
||||
|
||||
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
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/../workspace.sh"
|
||||
echo "[go] starting go devcontainer install"
|
||||
|
||||
echo "[devcontainer:go] installing golang"
|
||||
# --------------------------------------------------
|
||||
# Config
|
||||
# --------------------------------------------------
|
||||
RUNTIME_ROOT="/opt/zlh/runtime/go"
|
||||
ARTIFACT_ROOT="/opt/zlh/devcontainer/go"
|
||||
|
||||
apt update
|
||||
apt install -y golang
|
||||
VERSION="${RUNTIME_VERSION:-1.25}"
|
||||
|
||||
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
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/../workspace.sh"
|
||||
echo "[java] starting java devcontainer install"
|
||||
|
||||
echo "[devcontainer:java] installing jdk"
|
||||
# --------------------------------------------------
|
||||
# Config
|
||||
# --------------------------------------------------
|
||||
RUNTIME_ROOT="/opt/zlh/runtime/java"
|
||||
ARTIFACT_ROOT="/opt/zlh/devcontainer/java"
|
||||
|
||||
apt update
|
||||
apt install -y openjdk-21-jdk
|
||||
VERSION="${RUNTIME_VERSION:-17}"
|
||||
|
||||
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
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/../workspace.sh"
|
||||
RUNTIME_ROOT="/opt/zlh/runtime/node"
|
||||
ARTIFACT_ROOT="/opt/zlh/devcontainer/node"
|
||||
|
||||
echo "[devcontainer:node] installing nodejs"
|
||||
VERSION="${RUNTIME_VERSION:-24}"
|
||||
|
||||
apt update
|
||||
apt install -y nodejs npm
|
||||
ARCHIVE="node-v${VERSION}.*-linux-x64.tar.xz"
|
||||
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
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/../workspace.sh"
|
||||
echo "[python] starting python devcontainer install"
|
||||
|
||||
echo "[devcontainer:python] installing python"
|
||||
# --------------------------------------------------
|
||||
# Config
|
||||
# --------------------------------------------------
|
||||
RUNTIME_ROOT="/opt/zlh/runtime/python"
|
||||
ARTIFACT_ROOT="/opt/zlh/devcontainer/python"
|
||||
|
||||
apt update
|
||||
apt install -y python3 python3-pip python3-venv
|
||||
VERSION="${RUNTIME_VERSION:-3.12}"
|
||||
|
||||
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