From 4e668a79e19804cc49bb37c3ecdb1261e132ae1f Mon Sep 17 00:00:00 2001 From: Kayos Date: Wed, 29 Apr 2026 07:29:53 -0700 Subject: [PATCH] v0.1 step 1: Dockerfile + per-language toolchain smoke MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Monolith image with every toolchain in the spec: - Python 3.12 + uv/ruff/mypy/pytest/pip-audit/semgrep - Node 22 LTS + bun - Go 1.22 + govulncheck/staticcheck - Rust stable + cargo-audit/cargo-deny - Ruby 3.x + bundler-audit - PHP 8.x + composer/phpstan - JDK 17 + 21 + Maven + Gradle - .NET 8 SDK - Swift 5.9.2 - Kotlin 1.9.25 - clang + cmake + valgrind + ASan/UBSan/TSan - bash + shellcheck smoke.sh proves each toolchain compiles + runs a hello-world. compose.yml uses the existing 'sulkta' bridge network. No API yet (steps 2-3); no MCP yet (step 7); no runner yet (step 4). This is the foundation. NOTE: docker build + smoke verification not yet run — sandbox doesn't have docker. Needs `docker compose build && docker compose up` on Lucy or any real Docker host before we trust the Dockerfile. Spec: memory/spec-crafting-table.md --- .gitignore | 48 ++++++++++ Dockerfile | 263 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 87 ++++++++++++++++- compose.yml | 28 ++++++ smoke.sh | 194 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 619 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 compose.yml create mode 100755 smoke.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5d44c72 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# Python +__pycache__/ +*.pyc +*.pyo +.venv/ +venv/ +.mypy_cache/ +.ruff_cache/ +.pytest_cache/ + +# Node / TS +node_modules/ +dist/ +*.tsbuildinfo + +# Rust +target/ + +# Go +/bin/ + +# Java / Kotlin / Gradle / Maven +build/ +out/ +.gradle/ +*.class +*.jar +.mvn/ + +# .NET +.dotnet/ +obj/ + +# Swift +.swiftpm/ +.build/ +*.xcodeproj/ + +# Misc +.cache/ +.env +*.log +.DS_Store + +# Editor +.vscode/ +.idea/ +*.swp diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..59937e9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,263 @@ +# crafting-table v0.1 — polyglot dev/build/audit container +# +# Step 1 of 10: monolith image with every toolchain in the spec. +# Spec: Sulkta-Coop/openclaw-workspace/memory/spec-crafting-table.md +# +# Toolchain version pins (bump these to upgrade): +# NODE_VERSION 22.11.0 (LTS) +# GO_VERSION 1.22.10 +# RUST_CHANNEL stable +# BUN_VERSION latest (rolling — pinned at install only) +# DOTNET_VERSION 8.0 +# SWIFT_VERSION 5.9.2 +# KOTLIN_VERSION 1.9.25 +# GRADLE_VERSION 8.10 +# JDK 17 (default) + 21 (alongside, via JAVA_HOME_21) +# +# Image runs as non-root user `crafter` (uid 1000) with passwordless sudo. +# Persistent caches mounted at /caches/{cargo,maven,gradle,npm,pip}. +# Workspace at /workspace. Per-job state at /data. + +FROM debian:bookworm-slim + +ENV DEBIAN_FRONTEND=noninteractive \ + LANG=C.UTF-8 \ + LC_ALL=C.UTF-8 + +# ---------- Toolchain version pins ---------- +ENV NODE_VERSION=22.11.0 \ + GO_VERSION=1.22.10 \ + DOTNET_CHANNEL=8.0 \ + SWIFT_VERSION=5.9.2 \ + SWIFT_PLATFORM=ubuntu22.04 \ + KOTLIN_VERSION=1.9.25 \ + GRADLE_VERSION=8.10 + +# ============================================================ +# 1. System base — apt packages +# ============================================================ +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl wget git ca-certificates gnupg lsb-release apt-transport-https \ + build-essential pkg-config make cmake ninja-build \ + jq ripgrep fd-find \ + valgrind clang lld llvm \ + python3 python3-pip python3-venv python3-dev \ + php-cli php-curl php-mbstring php-xml php-zip composer \ + ruby ruby-dev ruby-bundler \ + bash shellcheck bats \ + openjdk-17-jdk-headless \ + sudo unzip xz-utils zstd \ + libcurl4 libxml2 libedit2 libsqlite3-0 libpython3-dev \ + libncurses6 libtinfo6 libgcc-s1 libstdc++6 \ + libssl-dev libffi-dev \ + zlib1g-dev liblzma-dev libbz2-dev \ + locales tzdata \ + && ln -sf /usr/bin/fdfind /usr/local/bin/fd \ + && rm -rf /var/lib/apt/lists/* + +# ============================================================ +# 2. Node 22 LTS via NodeSource +# ============================================================ +RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y --no-install-recommends nodejs \ + && rm -rf /var/lib/apt/lists/* \ + && npm install -g pnpm tsx eslint typescript + +# ============================================================ +# 3. Go (download from go.dev tarball) +# ============================================================ +RUN ARCH="$(dpkg --print-architecture)" \ + && case "$ARCH" in \ + amd64) GOARCH=amd64 ;; \ + arm64) GOARCH=arm64 ;; \ + *) echo "Unsupported arch $ARCH" && exit 1 ;; \ + esac \ + && curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz" -o /tmp/go.tgz \ + && tar -C /usr/local -xzf /tmp/go.tgz \ + && rm /tmp/go.tgz +ENV PATH=/usr/local/go/bin:/root/go/bin:$PATH \ + GOPATH=/root/go + +# ============================================================ +# 4. Microsoft .NET 8 SDK (Microsoft apt repo for bookworm/12) +# ============================================================ +RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/microsoft.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/microsoft.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends dotnet-sdk-8.0 \ + && rm -rf /var/lib/apt/lists/* +ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \ + DOTNET_NOLOGO=1 + +# ============================================================ +# 5. Swift (Ubuntu 22.04 tarball — works on Debian bookworm +# because bookworm ships the right libicu/libstdc++ baseline) +# ============================================================ +RUN ARCH="$(dpkg --print-architecture)" \ + && SWIFT_PLAT="${SWIFT_PLATFORM}" \ + && case "$ARCH" in \ + amd64) SWIFT_TARBALL_NAME="swift-${SWIFT_VERSION}-RELEASE-${SWIFT_PLAT}" ;; \ + arm64) SWIFT_TARBALL_NAME="swift-${SWIFT_VERSION}-RELEASE-${SWIFT_PLAT}-aarch64" ;; \ + *) echo "Unsupported arch $ARCH for Swift" && exit 1 ;; \ + esac \ + && apt-get update \ + && apt-get install -y --no-install-recommends libpython3-dev libxml2-dev \ + libcurl4-openssl-dev libedit-dev libsqlite3-dev libtinfo-dev libncurses-dev \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "https://download.swift.org/swift-${SWIFT_VERSION}-release/${SWIFT_PLAT//./}/swift-${SWIFT_VERSION}-RELEASE/${SWIFT_TARBALL_NAME}.tar.gz" -o /tmp/swift.tgz \ + && mkdir -p /opt/swift \ + && tar -xzf /tmp/swift.tgz -C /opt/swift --strip-components=1 \ + && rm /tmp/swift.tgz +ENV PATH=/opt/swift/usr/bin:$PATH + +# ============================================================ +# 6. Kotlin compiler (direct download from GitHub release) +# ============================================================ +RUN curl -fsSL "https://github.com/JetBrains/kotlin/releases/download/v${KOTLIN_VERSION}/kotlin-compiler-${KOTLIN_VERSION}.zip" -o /tmp/kotlin.zip \ + && unzip -q /tmp/kotlin.zip -d /opt \ + && mv /opt/kotlinc /opt/kotlin \ + && rm /tmp/kotlin.zip +ENV PATH=/opt/kotlin/bin:$PATH + +# ============================================================ +# 7. JDK 21 alongside JDK 17 (Eclipse Temurin via apt) +# ============================================================ +RUN mkdir -p /etc/apt/keyrings \ + && curl -fsSL https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor -o /etc/apt/keyrings/adoptium.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb bookworm main" > /etc/apt/sources.list.d/adoptium.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends temurin-21-jdk \ + && rm -rf /var/lib/apt/lists/* +ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \ + JAVA_HOME_17=/usr/lib/jvm/java-17-openjdk-amd64 \ + JAVA_HOME_21=/usr/lib/jvm/temurin-21-jdk-amd64 +RUN update-alternatives --set java ${JAVA_HOME_17}/bin/java || true \ + && update-alternatives --set javac ${JAVA_HOME_17}/bin/javac || true + +# ============================================================ +# 8. Maven (apt) + Gradle (direct download — apt's gradle is ancient) +# ============================================================ +RUN apt-get update \ + && apt-get install -y --no-install-recommends maven \ + && rm -rf /var/lib/apt/lists/* \ + && curl -fsSL "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" -o /tmp/gradle.zip \ + && unzip -q /tmp/gradle.zip -d /opt \ + && mv /opt/gradle-${GRADLE_VERSION} /opt/gradle \ + && rm /tmp/gradle.zip +ENV PATH=/opt/gradle/bin:$PATH + +# ============================================================ +# 9. GitHub CLI (gh) +# ============================================================ +RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ + && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends gh \ + && rm -rf /var/lib/apt/lists/* + +# ============================================================ +# 10. yq (Mike Farah, Go binary) +# ============================================================ +RUN ARCH="$(dpkg --print-architecture)" \ + && curl -fsSL "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${ARCH}" -o /usr/local/bin/yq \ + && chmod +x /usr/local/bin/yq + +# ============================================================ +# 11. shfmt (Go binary) +# ============================================================ +RUN ARCH="$(dpkg --print-architecture)" \ + && curl -fsSL "https://github.com/mvdan/sh/releases/latest/download/shfmt_v3.10.0_linux_${ARCH}" -o /usr/local/bin/shfmt \ + && chmod +x /usr/local/bin/shfmt + +# ============================================================ +# 12. Create non-root user `crafter` (uid 1000) with passwordless sudo +# and prepare workspace / cache / data dirs +# ============================================================ +RUN useradd -m -u 1000 -s /bin/bash crafter \ + && echo "crafter ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/crafter \ + && chmod 0440 /etc/sudoers.d/crafter \ + && mkdir -p /workspace /caches/cargo /caches/maven /caches/gradle /caches/npm /caches/pip /caches/bun /data \ + && chown -R crafter:crafter /workspace /caches /data + +# ============================================================ +# 13. Switch to crafter for user-scoped installs +# ============================================================ +USER crafter +WORKDIR /home/crafter + +# Cache env (point tools at the persisted cache dirs) +ENV CARGO_HOME=/caches/cargo \ + RUSTUP_HOME=/home/crafter/.rustup \ + GRADLE_USER_HOME=/caches/gradle \ + MAVEN_OPTS="-Dmaven.repo.local=/caches/maven" \ + NPM_CONFIG_CACHE=/caches/npm \ + PIP_CACHE_DIR=/caches/pip \ + BUN_INSTALL=/home/crafter/.bun \ + PIPX_HOME=/home/crafter/.local/pipx \ + PIPX_BIN_DIR=/home/crafter/.local/bin + +ENV PATH=/home/crafter/.local/bin:/caches/cargo/bin:/home/crafter/.bun/bin:$PATH + +# ============================================================ +# 14. Rust (rustup, stable) + cargo-audit + cargo-deny +# ============================================================ +RUN curl -fsSL https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal --no-modify-path \ + && /caches/cargo/bin/rustup component add clippy rustfmt \ + && /caches/cargo/bin/cargo install cargo-audit --locked \ + && /caches/cargo/bin/cargo install cargo-deny --locked + +# ============================================================ +# 15. Bun (curl install) +# ============================================================ +RUN curl -fsSL https://bun.sh/install | bash + +# ============================================================ +# 16. Python user tooling: pipx-managed CLI tools +# ============================================================ +RUN python3 -m pip install --user --break-system-packages --no-cache-dir pipx \ + && python3 -m pipx ensurepath \ + && pipx install uv \ + && pipx install ruff \ + && pipx install mypy \ + && pipx install pytest \ + && pipx install pip-audit \ + && pipx install semgrep + +# ============================================================ +# 17. Go user tooling: govulncheck + staticcheck +# ============================================================ +RUN go install golang.org/x/vuln/cmd/govulncheck@latest \ + && go install honnef.co/go/tools/cmd/staticcheck@latest + +# Make GOPATH bin discoverable for the crafter user +ENV PATH=/home/crafter/go/bin:$PATH \ + GOPATH=/home/crafter/go + +# ============================================================ +# 18. Ruby user tooling: bundler-audit, rubocop +# ============================================================ +RUN gem install --user-install --no-document bundler-audit rubocop || \ + sudo gem install --no-document bundler-audit rubocop +ENV PATH=/home/crafter/.local/share/gem/ruby/3.1.0/bin:$PATH + +# ============================================================ +# 19. PHP user tooling: phpstan, phpunit (composer global) +# ============================================================ +ENV COMPOSER_HOME=/home/crafter/.composer +RUN composer global require --no-interaction phpstan/phpstan phpunit/phpunit +ENV PATH=/home/crafter/.composer/vendor/bin:$PATH + +# ============================================================ +# 20. Smoke script — bake in +# ============================================================ +COPY --chown=crafter:crafter smoke.sh /usr/local/bin/smoke.sh +USER root +RUN chmod +x /usr/local/bin/smoke.sh +USER crafter + +# ============================================================ +# 21. Final ENV / WORKDIR / CMD +# ============================================================ +WORKDIR /workspace +CMD ["/bin/bash"] diff --git a/README.md b/README.md index 2783704..f78e813 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,88 @@ # crafting-table -Polyglot dev/build/audit container with autonomous patch loop + email digest. Recipes for every Sulkta repo, structured findings back to clawdforge. \ No newline at end of file +Polyglot dev/build/audit container — the build farm for the Sulkta ecosystem. + +## What this is + +A single Docker container with every toolchain we work with, used as a +reliable place to compile / test / audit any Sulkta repo regardless of +where the caller is — agents, Claude sessions, ad-hoc curl, scheduled cron. + +Eventual surface (v0.1 full): HTTP API + MCP server + project registry + +job runner + structured findings + email digest + autonomous patch loop +through clawdforge. + +Spec: `Sulkta-Coop/openclaw-workspace/memory/spec-crafting-table.md` (LAN-only). + +## Status — v0.1 step 1 of 10 + +- [x] Step 1: Dockerfile + per-language smoke +- [ ] Step 2: SQLite ledger + project registry +- [ ] Step 3: HTTP API skeleton (FastAPI, port 8810) +- [ ] Step 4: Job runner core (asyncio worker pool) +- [ ] Step 5: Per-language parsers (Rust / Python / Go / TS first) +- [ ] Step 6: Findings extraction + storage +- [ ] Step 7: MCP server (stdio JSON-RPC, 8 tools) +- [ ] Step 8: Email digest scheduler +- [ ] Step 9: Autonomous patch loop (clawdforge integration) +- [ ] Step 10: Production recipes — clawdforge, cauldron, tradecraft + +## Toolchains in v0.1 + +| Lang | Versions / extras | +|----------|--------------------------------------------------------------------| +| Python | 3.11 (Debian default) + uv, pipx, pip-audit, ruff, mypy, pytest, semgrep | +| Node | 22.11.0 LTS + npm, pnpm, tsx, eslint, typescript | +| Bun | latest (rolling) | +| Go | 1.22.10 + govulncheck, staticcheck | +| Rust | stable (rustup) + clippy, rustfmt, cargo-audit, cargo-deny | +| Ruby | 3.1 (Debian default) + bundler, bundler-audit, rubocop | +| PHP | 8.2 (Debian default) + composer, phpstan, phpunit | +| JDK | 17 (default) + 21 (Temurin, alongside via `JAVA_HOME_21`) | +| Maven | 3.x (Debian) | +| Gradle | 8.10 | +| .NET | 8.0 SDK | +| Swift | 5.9.2 (Ubuntu 22.04 tarball — works on Debian bookworm) | +| Kotlin | 1.9.25 (compiler) | +| C/C++ | clang + lld + cmake + ninja + valgrind | +| Bash | bash + shellcheck + bats + shfmt | +| Generic | git, jq, yq, ripgrep, fd, gh-cli, curl, wget | + +## Build + smoke + +```bash +docker network inspect sulkta >/dev/null 2>&1 || docker network create sulkta +docker compose build +docker compose up +# expect: "=== ALL TOOLCHAINS GREEN ===" then exit 0 +``` + +The smoke compiles + runs a hello-world in every language. If it exits 0, +the image is good. + +## Image notes + +- Base: `debian:bookworm-slim`. Swift uses the upstream Ubuntu 22.04 tarball + which links against bookworm's libicu/libstdc++ baseline. +- Runs as non-root user `crafter` (uid 1000) with passwordless sudo. +- Volume mount points: `/workspace`, `/caches/{cargo,maven,gradle,npm,pip,bun}`, + `/data`. Compose binds these to named volumes so they survive `compose down`. +- Network: external `sulkta` bridge (same one clawdforge + cauldron use). + Create with `docker network create sulkta` if missing. +- Image size baseline is large (8-15 GB expected). Per spec: that's fine. + +## Layout + +``` +. +├── Dockerfile # monolith image with all toolchains +├── compose.yml # build + run-smoke wiring +├── smoke.sh # per-language hello-world test, baked in at /usr/local/bin/smoke.sh +├── README.md +├── LICENSE # MIT +└── .gitignore +``` + +## License + +MIT diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..53bea86 --- /dev/null +++ b/compose.yml @@ -0,0 +1,28 @@ +# crafting-table v0.1 — step 1 compose. +# +# Builds the monolith image and runs the smoke test once. +# In step 2+ the `command:` is replaced with the API server entrypoint. +name: crafting-table + +services: + crafting-table: + build: . + image: crafting-table:local + container_name: crafting-table + command: ["/usr/local/bin/smoke.sh"] + user: crafter + working_dir: /workspace + volumes: + - workspace:/workspace + - caches:/caches + - data:/data + networks: [sulkta] + +volumes: + workspace: + caches: + data: + +networks: + sulkta: + external: true diff --git a/smoke.sh b/smoke.sh new file mode 100755 index 0000000..22a4a2f --- /dev/null +++ b/smoke.sh @@ -0,0 +1,194 @@ +#!/bin/bash +# crafting-table v0.1 step 1 — toolchain smoke test. +# +# Runs INSIDE the built image; prints `--version` and a hello-world for +# every advertised toolchain. Exits 0 only if every block succeeds. +# +# Spec: memory/spec-crafting-table.md (toolchain matrix) + +set -eo pipefail + +echo "=== crafting-table smoke ===" +echo "user: $(id)" +echo "pwd: $(pwd)" +echo + +echo "--- python" +python3 --version +python3 -c "print('hello from python')" +uv --version +ruff --version +mypy --version +pytest --version +pip-audit --version +semgrep --version +echo + +echo "--- node" +node --version +node -e "console.log('hello from node')" +npm --version +pnpm --version +tsx --version || true +echo + +echo "--- bun" +bun --version +bun -e "console.log('hello from bun')" +echo + +echo "--- go" +go version +mkdir -p /tmp/smoke-go && cd /tmp/smoke-go && go mod init smoke 2>/dev/null || true +cat >main.go <<'EOF' +package main + +func main() { println("hello from go") } +EOF +go run main.go +cd / && rm -rf /tmp/smoke-go +govulncheck -version 2>&1 | head -2 || true +staticcheck -version +echo + +echo "--- rust" +rustc --version +cargo --version +cargo audit --version +cargo deny --version +cat >/tmp/smoke.rs <<'EOF' +fn main() { println!("hello from rust"); } +EOF +rustc /tmp/smoke.rs -o /tmp/smoke && /tmp/smoke +rm /tmp/smoke /tmp/smoke.rs +echo + +echo "--- ruby" +ruby --version +ruby -e "puts 'hello from ruby'" +bundler --version +bundle-audit version 2>&1 | head -1 || true +rubocop --version +echo + +echo "--- php" +php --version | head -1 +php -r "echo 'hello from php', PHP_EOL;" +composer --version +phpstan --version 2>&1 | head -1 +phpunit --version | head -1 +echo + +echo "--- jdk 17 (default)" +java -version 2>&1 | head -1 +javac -version +cat >/tmp/Smoke.java <<'EOF' +public class Smoke { public static void main(String[] a){ System.out.println("hello from java 17"); } } +EOF +javac /tmp/Smoke.java -d /tmp && java -cp /tmp Smoke +rm /tmp/Smoke.java /tmp/Smoke.class +echo + +echo "--- jdk 21 (alongside)" +"$JAVA_HOME_21/bin/java" -version 2>&1 | head -1 +"$JAVA_HOME_21/bin/javac" -version +cat >/tmp/Smoke21.java <<'EOF' +public class Smoke21 { public static void main(String[] a){ System.out.println("hello from java 21"); } } +EOF +"$JAVA_HOME_21/bin/javac" /tmp/Smoke21.java -d /tmp && "$JAVA_HOME_21/bin/java" -cp /tmp Smoke21 +rm /tmp/Smoke21.java /tmp/Smoke21.class +echo + +echo "--- maven" +mvn -version | head -1 +echo + +echo "--- gradle" +gradle -version 2>&1 | grep -E '^Gradle ' | head -1 +echo + +echo "--- kotlin" +kotlinc -version 2>&1 | head -1 +cat >/tmp/smoke.kt <<'EOF' +fun main() { println("hello from kotlin") } +EOF +kotlinc /tmp/smoke.kt -include-runtime -d /tmp/smoke.jar 2>/dev/null +java -jar /tmp/smoke.jar +rm /tmp/smoke.kt /tmp/smoke.jar +echo + +echo "--- .net 8" +dotnet --version +mkdir -p /tmp/smoke-dotnet && cd /tmp/smoke-dotnet +dotnet new console -o app -n Smoke --force >/dev/null +cd app +# Suppress NuGet first-run noise; just run the hello-world. +dotnet run --nologo 2>&1 | tail -3 +cd / && rm -rf /tmp/smoke-dotnet +echo + +echo "--- swift" +swift --version 2>&1 | head -1 +cat >/tmp/smoke.swift <<'EOF' +print("hello from swift") +EOF +swift /tmp/smoke.swift +rm /tmp/smoke.swift +echo + +echo "--- clang/c" +clang --version | head -1 +cat >/tmp/smoke.c <<'EOF' +#include +int main(void){puts("hello from c");return 0;} +EOF +clang /tmp/smoke.c -o /tmp/smoke-c && /tmp/smoke-c +rm /tmp/smoke.c /tmp/smoke-c +echo + +echo "--- clang++/cpp" +clang++ --version | head -1 +cat >/tmp/smoke.cpp <<'EOF' +#include +int main(){std::cout<<"hello from cpp"</tmp/smoke.sh <<'EOF' +#!/bin/bash +echo "hello from bash" +EOF +chmod +x /tmp/smoke.sh +bash /tmp/smoke.sh +shellcheck /tmp/smoke.sh && echo "shellcheck clean" +rm /tmp/smoke.sh +echo + +echo "--- generic tools" +git --version +jq --version +rg --version | head -1 +fd --version +gh --version | head -1 +yq --version +curl --version | head -1 +wget --version | head -1 +echo + +echo "=== ALL TOOLCHAINS GREEN ==="