diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3ce5e7..7f6d830 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: # Blocking scan: OS packages only. We control the base image and can act on # these. ignore-unfixed skips CVEs with no Debian patch available yet. - name: Run trivy OS package scan (blocking) - uses: aquasecurity/trivy-action@0.28.0 + uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 with: image-ref: ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} severity: CRITICAL,HIGH @@ -59,7 +59,7 @@ jobs: # are uploaded to the GitHub Security tab for visibility. Not blocking # because Go binary CVEs depend on upstream tool releases we don't control. - name: Run trivy full scan (SARIF, advisory) - uses: aquasecurity/trivy-action@0.28.0 + uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 if: always() with: image-ref: ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} diff --git a/Dockerfile b/Dockerfile index 0d5746b..60c73bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,6 +40,20 @@ RUN curl -L --proto '=https' --tlsv1.2 -sSf \ | bash RUN cargo binstall --no-confirm cargo-audit cargo-deny +# === Swift builder stage === +# Provides Swift toolchain and builds swift-format from source +# (swift-format has no pre-built Linux binaries) +FROM swift:6.1-bookworm AS swift-builder +RUN git clone --depth 1 --branch 602.0.0 https://github.com/swiftlang/swift-format.git /tmp/swift-format \ + && cd /tmp/swift-format \ + && swift build -c release \ + && install -m 755 .build/release/swift-format /usr/local/bin/swift-format \ + && rm -rf /tmp/swift-format + +# === JDK builder stage === +# Provides JDK 21 for Kotlin tooling (ktlint, detekt, Gradle) +FROM eclipse-temurin:21-jdk AS jdk-builder + # === Node.js base: provides Node runtime for JS/TS tooling === FROM node:22-bookworm-slim AS node-base @@ -110,8 +124,18 @@ COPY --from=rust-builder /usr/local/cargo /usr/local/cargo ENV RUSTUP_HOME=/usr/local/rustup ENV CARGO_HOME=/usr/local/cargo -# Set up environment -ENV PATH="/opt/devrail/bin:/usr/local/cargo/bin:/usr/local/go/bin:${PATH}" +# Copy Swift toolchain from swift-builder (selective: binaries + runtime libs + swift-format) +COPY --from=swift-builder /usr/bin/swift /usr/bin/swiftc /usr/bin/swift-build /usr/bin/swift-test /usr/bin/swift-package /usr/bin/swift-run /usr/local/swift/bin/ +COPY --from=swift-builder /usr/lib/swift /usr/local/swift/lib/swift +COPY --from=swift-builder /usr/lib/swift_static /usr/local/swift/lib/swift_static +COPY --from=swift-builder /usr/local/bin/swift-format /usr/local/bin/swift-format + +# Copy JDK 21 from jdk-builder (required for Kotlin tooling: ktlint, detekt, Gradle) +COPY --from=jdk-builder /opt/java/openjdk /opt/java/openjdk +ENV JAVA_HOME=/opt/java/openjdk + +# Set up environment (consolidated PATH — all language runtimes in one line) +ENV PATH="/opt/devrail/bin:/usr/local/cargo/bin:/usr/local/go/bin:/usr/local/swift/bin:/opt/java/openjdk/bin:${PATH}" ENV DEVRAIL_LIB="/opt/devrail/lib" # Copy Go SDK from builder (required at runtime by golangci-lint, govulncheck) @@ -135,6 +159,8 @@ RUN bash /opt/devrail/scripts/install-ruby.sh RUN bash /opt/devrail/scripts/install-go.sh RUN bash /opt/devrail/scripts/install-javascript.sh RUN bash /opt/devrail/scripts/install-rust.sh +RUN bash /opt/devrail/scripts/install-swift.sh +RUN bash /opt/devrail/scripts/install-kotlin.sh RUN bash /opt/devrail/scripts/install-universal.sh # Allow git operations on mounted workspaces with different ownership diff --git a/Makefile b/Makefile index 4690b5a..d9593af 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,8 @@ HAS_RUBY := $(filter ruby,$(LANGUAGES)) HAS_GO := $(filter go,$(LANGUAGES)) HAS_JAVASCRIPT := $(filter javascript,$(LANGUAGES)) HAS_RUST := $(filter rust,$(LANGUAGES)) +HAS_SWIFT := $(filter swift,$(LANGUAGES)) +HAS_KOTLIN := $(filter kotlin,$(LANGUAGES)) # --------------------------------------------------------------------------- # .PHONY declarations @@ -283,6 +285,39 @@ _lint: _check-config exit $$overall_exit; \ fi; \ fi; \ + if [ -n "$(HAS_SWIFT)" ]; then \ + ran_languages="$${ran_languages}\"swift\","; \ + swift_files=$$(find . -name '*.swift' -not -path './.git/*' -not -path './.build/*' -not -path './DerivedData/*' 2>/dev/null); \ + if [ -n "$$swift_files" ]; then \ + swiftlint lint --strict || { overall_exit=1; failed_languages="$${failed_languages}\"swift\","; }; \ + else \ + echo '{"level":"info","msg":"skipping swift lint: no .swift files found","language":"swift"}' >&2; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"lint\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ + if [ -n "$(HAS_KOTLIN)" ]; then \ + ran_languages="$${ran_languages}\"kotlin\","; \ + kt_files=$$(find . \( -name '*.kt' -o -name '*.kts' \) -not -path './.git/*' -not -path './build/*' -not -path './.gradle/*' 2>/dev/null); \ + if [ -n "$$kt_files" ]; then \ + ktlint || { overall_exit=1; failed_languages="$${failed_languages}\"kotlin:ktlint\","; }; \ + else \ + echo '{"level":"info","msg":"skipping kotlin lint: no .kt/.kts files found","language":"kotlin"}' >&2; \ + fi; \ + if [ -f "detekt.yml" ] && [ -n "$$kt_files" ]; then \ + detekt-cli --build-upon-default-config --config detekt.yml || { overall_exit=1; failed_languages="$${failed_languages}\"kotlin:detekt\","; }; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"lint\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ end_time=$$(date +%s%3N); \ duration=$$((end_time - start_time)); \ if [ $$overall_exit -eq 0 ]; then \ @@ -401,6 +436,36 @@ _format: _check-config exit $$overall_exit; \ fi; \ fi; \ + if [ -n "$(HAS_SWIFT)" ]; then \ + ran_languages="$${ran_languages}\"swift\","; \ + swift_files=$$(find . -name '*.swift' -not -path './.git/*' -not -path './.build/*' -not -path './DerivedData/*' 2>/dev/null); \ + if [ -n "$$swift_files" ]; then \ + swift-format lint --strict -r . || { overall_exit=1; failed_languages="$${failed_languages}\"swift\","; }; \ + else \ + echo '{"level":"info","msg":"skipping swift format: no .swift files found","language":"swift"}' >&2; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"format\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ + if [ -n "$(HAS_KOTLIN)" ]; then \ + ran_languages="$${ran_languages}\"kotlin\","; \ + kt_files=$$(find . \( -name '*.kt' -o -name '*.kts' \) -not -path './.git/*' -not -path './build/*' -not -path './.gradle/*' 2>/dev/null); \ + if [ -n "$$kt_files" ]; then \ + ktlint --format --dry-run || { overall_exit=1; failed_languages="$${failed_languages}\"kotlin\","; }; \ + else \ + echo '{"level":"info","msg":"skipping kotlin format: no .kt/.kts files found","language":"kotlin"}' >&2; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"format\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ end_time=$$(date +%s%3N); \ duration=$$((end_time - start_time)); \ if [ $$overall_exit -eq 0 ]; then \ @@ -519,6 +584,36 @@ _fix: _check-config exit $$overall_exit; \ fi; \ fi; \ + if [ -n "$(HAS_SWIFT)" ]; then \ + ran_languages="$${ran_languages}\"swift\","; \ + swift_files=$$(find . -name '*.swift' -not -path './.git/*' -not -path './.build/*' -not -path './DerivedData/*' 2>/dev/null); \ + if [ -n "$$swift_files" ]; then \ + swift-format format -i -r . || { overall_exit=1; failed_languages="$${failed_languages}\"swift\","; }; \ + else \ + echo '{"level":"info","msg":"skipping swift fix: no .swift files found","language":"swift"}' >&2; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"fix\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ + if [ -n "$(HAS_KOTLIN)" ]; then \ + ran_languages="$${ran_languages}\"kotlin\","; \ + kt_files=$$(find . \( -name '*.kt' -o -name '*.kts' \) -not -path './.git/*' -not -path './build/*' -not -path './.gradle/*' 2>/dev/null); \ + if [ -n "$$kt_files" ]; then \ + ktlint --format || { overall_exit=1; failed_languages="$${failed_languages}\"kotlin\","; }; \ + else \ + echo '{"level":"info","msg":"skipping kotlin fix: no .kt/.kts files found","language":"kotlin"}' >&2; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"fix\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ end_time=$$(date +%s%3N); \ duration=$$((end_time - start_time)); \ if [ $$overall_exit -eq 0 ]; then \ @@ -656,6 +751,37 @@ _test: _check-config exit $$overall_exit; \ fi; \ fi; \ + if [ -n "$(HAS_SWIFT)" ]; then \ + swift_files=$$(find . -name '*.swift' -not -path './.git/*' -not -path './.build/*' -not -path './DerivedData/*' 2>/dev/null); \ + if [ -n "$$swift_files" ] && [ -f "Package.swift" ]; then \ + ran_languages="$${ran_languages}\"swift\","; \ + swift test || { overall_exit=1; failed_languages="$${failed_languages}\"swift\","; }; \ + else \ + skipped_languages="$${skipped_languages}\"swift\","; \ + echo '{"level":"info","msg":"skipping swift tests: no .swift files or Package.swift found","language":"swift"}' >&2; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"test\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}],\"skipped\":[$${skipped_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ + if [ -n "$(HAS_KOTLIN)" ]; then \ + if [ -f "build.gradle.kts" ] || [ -f "build.gradle" ]; then \ + ran_languages="$${ran_languages}\"kotlin\","; \ + gradle test || { overall_exit=1; failed_languages="$${failed_languages}\"kotlin\","; }; \ + else \ + skipped_languages="$${skipped_languages}\"kotlin\","; \ + echo '{"level":"info","msg":"skipping kotlin tests: no build.gradle.kts or build.gradle found","language":"kotlin"}' >&2; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"test\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}],\"skipped\":[$${skipped_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ end_time=$$(date +%s%3N); \ duration=$$((end_time - start_time)); \ if [ -z "$${ran_languages}" ] && [ -n "$${skipped_languages}" ]; then \ @@ -797,6 +923,25 @@ _security: _check-config exit $$overall_exit; \ fi; \ fi; \ + if [ -n "$(HAS_SWIFT)" ]; then \ + skipped_languages="$${skipped_languages}\"swift\","; \ + echo '{"level":"info","msg":"skipping swift security: no language-specific scanner","language":"swift"}' >&2; \ + fi; \ + if [ -n "$(HAS_KOTLIN)" ]; then \ + if [ -f "build.gradle.kts" ] || [ -f "build.gradle" ]; then \ + ran_languages="$${ran_languages}\"kotlin\","; \ + gradle dependencyCheckAnalyze || { overall_exit=1; failed_languages="$${failed_languages}\"kotlin:owasp\","; }; \ + else \ + skipped_languages="$${skipped_languages}\"kotlin\","; \ + echo '{"level":"info","msg":"skipping kotlin security: no build.gradle.kts or build.gradle found","language":"kotlin"}' >&2; \ + fi; \ + if [ "$(DEVRAIL_FAIL_FAST)" = "1" ] && [ $$overall_exit -ne 0 ]; then \ + end_time=$$(date +%s%3N); \ + duration=$$((end_time - start_time)); \ + echo "{\"target\":\"security\",\"status\":\"fail\",\"duration_ms\":$$duration,\"languages\":[$${ran_languages%,}],\"failed\":[$${failed_languages%,}]}"; \ + exit $$overall_exit; \ + fi; \ + fi; \ end_time=$$(date +%s%3N); \ duration=$$((end_time - start_time)); \ if [ -z "$${ran_languages}" ] && [ -n "$${skipped_languages}" ]; then \ diff --git a/scripts/install-kotlin.sh b/scripts/install-kotlin.sh new file mode 100755 index 0000000..e7f66ee --- /dev/null +++ b/scripts/install-kotlin.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# scripts/install-kotlin.sh — Install and verify Kotlin tooling for DevRail +# +# Purpose: Installs Kotlin development tools (ktlint, detekt, Gradle) and verifies +# the JDK is available in the dev-toolchain container. JDK 21 is COPY'd +# from the jdk-builder stage; this script installs additional tools. +# Usage: bash scripts/install-kotlin.sh [--help] +# Dependencies: lib/log.sh, lib/platform.sh +# +# Tools installed/verified: +# - java (JDK 21 — COPY'd from builder) +# - ktlint (Linter/formatter — downloaded binary) +# - detekt-cli (Static analysis — downloaded JAR) +# - gradle (Build tool — downloaded distribution) + +set -euo pipefail + +# --- Resolve library path --- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEVRAIL_LIB="${DEVRAIL_LIB:-${SCRIPT_DIR}/../lib}" + +# shellcheck source=../lib/log.sh +source "${DEVRAIL_LIB}/log.sh" +# shellcheck source=../lib/platform.sh +source "${DEVRAIL_LIB}/platform.sh" + +# --- Help --- +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + log_info "install-kotlin.sh — Install and verify Kotlin tooling for DevRail" + log_info "Usage: bash scripts/install-kotlin.sh [--help]" + log_info "Tools: java, ktlint, detekt-cli, gradle" + exit 0 +fi + +# --- Cleanup trap --- +TMPDIR_CLEANUP="" +cleanup() { + if [[ -n "${TMPDIR_CLEANUP}" && -d "${TMPDIR_CLEANUP}" ]]; then + rm -rf "${TMPDIR_CLEANUP}" + fi +} +trap cleanup EXIT + +# --- Tool installation functions --- + +install_ktlint() { + if command -v ktlint &>/dev/null; then + log_info "ktlint already installed, skipping" + return 0 + fi + + log_info "Installing ktlint..." + local version="1.5.0" + curl -fsSL "https://github.com/pinterest/ktlint/releases/download/${version}/ktlint" \ + -o /usr/local/bin/ktlint + chmod +x /usr/local/bin/ktlint + + require_cmd "ktlint" "Failed to install ktlint" + log_info "ktlint installed successfully" +} + +install_detekt() { + if [ -f /usr/local/lib/detekt-cli.jar ]; then + log_info "detekt-cli already installed, skipping" + return 0 + fi + + log_info "Installing detekt-cli..." + local version="1.23.7" + curl -fsSL "https://github.com/detekt/detekt/releases/download/v${version}/detekt-cli-${version}-all.jar" \ + -o /usr/local/lib/detekt-cli.jar + + # Create wrapper script + cat >/usr/local/bin/detekt-cli <<'WRAPPER' +#!/usr/bin/env bash +exec java -jar /usr/local/lib/detekt-cli.jar "$@" +WRAPPER + chmod +x /usr/local/bin/detekt-cli + + require_cmd "detekt-cli" "Failed to install detekt-cli" + log_info "detekt-cli installed successfully" +} + +install_gradle() { + if command -v gradle &>/dev/null; then + log_info "gradle already installed, skipping" + return 0 + fi + + log_info "Installing Gradle..." + local version="8.12" + TMPDIR_CLEANUP="$(mktemp -d)" + curl -fsSL "https://services.gradle.org/distributions/gradle-${version}-bin.zip" \ + -o "${TMPDIR_CLEANUP}/gradle.zip" + unzip -q "${TMPDIR_CLEANUP}/gradle.zip" -d /opt + ln -sf "/opt/gradle-${version}/bin/gradle" /usr/local/bin/gradle + + require_cmd "gradle" "Failed to install Gradle" + log_info "Gradle installed successfully" +} + +# --- Main --- +log_info "Installing Kotlin tools..." + +# Verify JDK is available (COPY'd from builder) +if command -v java &>/dev/null; then + log_info "java is already installed" +else + log_warn "java not found — expected to be copied from JDK builder stage" +fi + +install_ktlint +install_detekt +install_gradle + +log_info "Kotlin tools installed successfully" diff --git a/scripts/install-swift.sh b/scripts/install-swift.sh new file mode 100755 index 0000000..52ee81c --- /dev/null +++ b/scripts/install-swift.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# scripts/install-swift.sh — Install and verify Swift tooling for DevRail +# +# Purpose: Installs SwiftLint and swift-format, and verifies the Swift toolchain +# is available in the dev-toolchain container. The Swift SDK (swiftc, swift +# build, swift test, Swift Package Manager) is COPY'd from the swift-builder +# stage; this script installs additional tools and confirms all are on PATH. +# Usage: bash scripts/install-swift.sh [--help] +# Dependencies: lib/log.sh, lib/platform.sh +# +# Tools installed/verified: +# - swift (Swift compiler — COPY'd from builder) +# - swiftlint (Linter — installed from GitHub releases) +# - swift-format (Formatter — installed from GitHub releases) + +set -euo pipefail + +# --- Resolve library path --- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEVRAIL_LIB="${DEVRAIL_LIB:-${SCRIPT_DIR}/../lib}" + +# shellcheck source=../lib/log.sh +source "${DEVRAIL_LIB}/log.sh" +# shellcheck source=../lib/platform.sh +source "${DEVRAIL_LIB}/platform.sh" + +# --- Help --- +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + log_info "install-swift.sh — Install and verify Swift tooling for DevRail" + log_info "Usage: bash scripts/install-swift.sh [--help]" + log_info "Tools: swift, swiftlint, swift-format" + exit 0 +fi + +# --- Cleanup trap --- +TMPDIR_CLEANUP="" +cleanup() { + if [[ -n "${TMPDIR_CLEANUP}" && -d "${TMPDIR_CLEANUP}" ]]; then + rm -rf "${TMPDIR_CLEANUP}" + fi +} +trap cleanup EXIT + +# --- Tool installation functions --- + +install_swiftlint() { + if command -v swiftlint &>/dev/null; then + log_info "swiftlint already installed, skipping" + return 0 + fi + + log_info "Installing SwiftLint..." + TMPDIR_CLEANUP="$(mktemp -d)" + local arch + arch="$(dpkg --print-architecture)" + # SwiftLint provides pre-built Linux binaries + local version="0.58.0" + if [ "${arch}" = "amd64" ]; then + curl -fsSL "https://github.com/realm/SwiftLint/releases/download/${version}/swiftlint_linux.zip" \ + -o "${TMPDIR_CLEANUP}/swiftlint.zip" + unzip -q "${TMPDIR_CLEANUP}/swiftlint.zip" -d "${TMPDIR_CLEANUP}" + install -m 755 "${TMPDIR_CLEANUP}/swiftlint" /usr/local/bin/swiftlint + elif [ "${arch}" = "arm64" ]; then + curl -fsSL "https://github.com/realm/SwiftLint/releases/download/${version}/swiftlint_linux_aarch64.zip" \ + -o "${TMPDIR_CLEANUP}/swiftlint.zip" + unzip -q "${TMPDIR_CLEANUP}/swiftlint.zip" -d "${TMPDIR_CLEANUP}" + install -m 755 "${TMPDIR_CLEANUP}/swiftlint" /usr/local/bin/swiftlint + else + log_error "SwiftLint: unsupported architecture ${arch}" + return 1 + fi + + require_cmd "swiftlint" "Failed to install SwiftLint" + log_info "SwiftLint installed successfully" +} + +verify_swift_format() { + # swift-format is built from source in the Dockerfile swift-builder stage + # and COPY'd to /usr/local/bin/swift-format. This function only verifies. + if command -v swift-format &>/dev/null; then + log_info "swift-format is already installed" + else + log_warn "swift-format not found — expected to be copied from Swift builder stage" + fi +} + +# --- Main --- +log_info "Installing Swift tools..." + +# Verify Swift compiler is available (COPY'd from builder) +if command -v swift &>/dev/null; then + log_info "swift is already installed" +else + log_warn "swift not found — expected to be copied from Swift builder stage" +fi + +install_swiftlint +verify_swift_format + +log_info "Swift tools installed successfully" diff --git a/tests/test-kotlin.sh b/tests/test-kotlin.sh new file mode 100755 index 0000000..8ca99ee --- /dev/null +++ b/tests/test-kotlin.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# tests/test-kotlin.sh — Verify Kotlin tooling is installed correctly +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEVRAIL_LIB="${DEVRAIL_LIB:-${SCRIPT_DIR}/../lib}" + +# shellcheck source=../lib/log.sh +source "${DEVRAIL_LIB}/log.sh" + +log_info "Testing Kotlin tooling installation..." + +# Verify JDK +if command -v java &>/dev/null; then + log_info "PASS: java found — $(java --version 2>&1 | head -1)" +else + log_error "FAIL: java not found" + exit 1 +fi + +# Verify ktlint +if command -v ktlint &>/dev/null; then + log_info "PASS: ktlint found — $(ktlint --version 2>&1)" +else + log_error "FAIL: ktlint not found" + exit 1 +fi + +# Verify detekt-cli +if command -v detekt-cli &>/dev/null; then + log_info "PASS: detekt-cli found" +else + log_error "FAIL: detekt-cli not found" + exit 1 +fi + +# Verify gradle +if command -v gradle &>/dev/null; then + log_info "PASS: gradle found — $(gradle --version 2>&1 | grep 'Gradle' | head -1)" +else + log_error "FAIL: gradle not found" + exit 1 +fi + +log_info "All Kotlin tools verified successfully" diff --git a/tests/test-swift.sh b/tests/test-swift.sh new file mode 100755 index 0000000..52ee8ac --- /dev/null +++ b/tests/test-swift.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# tests/test-swift.sh — Verify Swift tooling is installed correctly +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEVRAIL_LIB="${DEVRAIL_LIB:-${SCRIPT_DIR}/../lib}" + +# shellcheck source=../lib/log.sh +source "${DEVRAIL_LIB}/log.sh" + +log_info "Testing Swift tooling installation..." + +# Verify swift +if command -v swift &>/dev/null; then + log_info "PASS: swift found — $(swift --version 2>&1 | head -1)" +else + log_error "FAIL: swift not found" + exit 1 +fi + +# Verify swiftlint +if command -v swiftlint &>/dev/null; then + log_info "PASS: swiftlint found — $(swiftlint version)" +else + log_error "FAIL: swiftlint not found" + exit 1 +fi + +# Verify swift-format +if command -v swift-format &>/dev/null; then + log_info "PASS: swift-format found — $(swift-format --version 2>&1 || echo 'version check N/A')" +else + log_error "FAIL: swift-format not found" + exit 1 +fi + +log_info "All Swift tools verified successfully"