2025-12-18 01:18:59 +00:00
|
|
|
|
# ╔══════════════════════════════════════════════════════════════════════╗
|
2025-12-24 03:24:31 +00:00
|
|
|
|
# ║ CI/CD RECIPES ║
|
|
|
|
|
|
# ║ Validation and deployment tasks ║
|
2025-12-18 01:18:59 +00:00
|
|
|
|
# ╚══════════════════════════════════════════════════════════════════════╝
|
|
|
|
|
|
|
2025-12-24 03:24:31 +00:00
|
|
|
|
# Workspace root directory
|
|
|
|
|
|
WORKSPACE_ROOT := justfile_directory()
|
|
|
|
|
|
|
2025-12-18 01:18:59 +00:00
|
|
|
|
# Help for CI module
|
|
|
|
|
|
help:
|
|
|
|
|
|
@echo "CI/CD MODULE"
|
|
|
|
|
|
@echo ""
|
|
|
|
|
|
@echo "Validation:"
|
|
|
|
|
|
@echo " just ci::lint Lint all code"
|
|
|
|
|
|
@echo " just ci::test-all Test all features"
|
|
|
|
|
|
@echo " just ci::check Check + format + lint"
|
|
|
|
|
|
@echo " just ci::audit Audit dependencies (security)"
|
|
|
|
|
|
@echo ""
|
|
|
|
|
|
@echo "Compliance:"
|
|
|
|
|
|
@echo " just ci::verify-sbom Verify SBOMs are up to date"
|
2025-12-24 03:24:31 +00:00
|
|
|
|
@echo " just ci::audit Audit dependencies (security)"
|
|
|
|
|
|
@echo " just ci::deny Check dependencies with cargo-deny"
|
2025-12-18 01:18:59 +00:00
|
|
|
|
@echo ""
|
|
|
|
|
|
@echo "Build validation:"
|
|
|
|
|
|
@echo " just ci::build-debug Debug build"
|
|
|
|
|
|
@echo " just ci::build-release Release build"
|
|
|
|
|
|
@echo ""
|
|
|
|
|
|
@echo "Full pipeline:"
|
|
|
|
|
|
@echo " just ci::full Complete CI: check + test + build"
|
|
|
|
|
|
|
|
|
|
|
|
# === VALIDATION STAGE ===
|
|
|
|
|
|
|
|
|
|
|
|
# Format check
|
|
|
|
|
|
[doc("Check format")]
|
|
|
|
|
|
fmt-check:
|
|
|
|
|
|
@echo "=== Format validation ==="
|
2025-12-24 03:24:31 +00:00
|
|
|
|
cargo fmt -p typedialog-core -p typedialog -p typedialog-tui -p typedialog-web -p typedialog-ai -p typedialog-prov-gen -p typedialog-ag-core -p typedialog-ag -- --check
|
2025-12-18 01:18:59 +00:00
|
|
|
|
@echo "✓ Format valid"
|
|
|
|
|
|
|
|
|
|
|
|
# Lint
|
|
|
|
|
|
[doc("Run clippy")]
|
|
|
|
|
|
lint:
|
2025-12-24 03:24:31 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
echo "=== Linting ==="
|
|
|
|
|
|
if [ -d "target/debug" ]; then
|
|
|
|
|
|
echo " → Using: target/debug (exists)"
|
|
|
|
|
|
cargo clippy --all-targets --all-features -- -D warnings
|
|
|
|
|
|
else
|
|
|
|
|
|
echo " → Using: /tmp/typedialog-ci (target/debug does not exist)"
|
|
|
|
|
|
cargo clippy --target-dir=/tmp/typedialog-ci --all-targets --all-features -- -D warnings
|
|
|
|
|
|
fi
|
|
|
|
|
|
echo "✓ Lint passed"
|
|
|
|
|
|
|
|
|
|
|
|
# === MULTI-LANGUAGE LINTING ===
|
|
|
|
|
|
|
|
|
|
|
|
# Lint Rust
|
|
|
|
|
|
[doc("Lint Rust (CI)")]
|
|
|
|
|
|
lint-rust:
|
|
|
|
|
|
just dev::lint-rust
|
|
|
|
|
|
|
|
|
|
|
|
# Lint bash scripts
|
|
|
|
|
|
[doc("Lint bash (CI)")]
|
|
|
|
|
|
lint-bash:
|
|
|
|
|
|
just dev::lint-bash
|
2025-12-18 01:18:59 +00:00
|
|
|
|
|
2025-12-24 03:24:31 +00:00
|
|
|
|
# Lint Nickel files
|
|
|
|
|
|
[doc("Lint Nickel (CI)")]
|
|
|
|
|
|
lint-nickel:
|
|
|
|
|
|
just dev::lint-nickel
|
|
|
|
|
|
|
|
|
|
|
|
# Lint Nushell scripts
|
|
|
|
|
|
[doc("Lint Nushell (CI)")]
|
|
|
|
|
|
lint-nushell:
|
|
|
|
|
|
just dev::lint-nushell
|
|
|
|
|
|
|
|
|
|
|
|
# Lint Markdown
|
|
|
|
|
|
[doc("Lint Markdown (CI)")]
|
|
|
|
|
|
lint-markdown:
|
|
|
|
|
|
just dev::lint-markdown
|
|
|
|
|
|
|
|
|
|
|
|
# Lint all languages (CI)
|
|
|
|
|
|
[doc("Lint all languages (CI)")]
|
|
|
|
|
|
lint-all:
|
|
|
|
|
|
just dev::lint-all
|
|
|
|
|
|
|
|
|
|
|
|
# Check formatting and linting (all languages)
|
|
|
|
|
|
[doc("Check: format + lint (all)")]
|
2025-12-18 01:18:59 +00:00
|
|
|
|
check:
|
|
|
|
|
|
just ci::fmt-check
|
2025-12-24 03:24:31 +00:00
|
|
|
|
just ci::lint-all
|
2025-12-18 01:18:59 +00:00
|
|
|
|
@echo "✓ All checks passed"
|
|
|
|
|
|
|
|
|
|
|
|
# === TEST STAGE ===
|
|
|
|
|
|
|
|
|
|
|
|
# Test all features
|
|
|
|
|
|
[doc("Test all features")]
|
|
|
|
|
|
test-all:
|
2025-12-24 03:24:31 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
cd "{{ WORKSPACE_ROOT }}"
|
|
|
|
|
|
echo "=== Testing (all features) ==="
|
|
|
|
|
|
if [ -d "target/debug" ]; then
|
|
|
|
|
|
echo " → Using: target/debug (exists)"
|
|
|
|
|
|
cargo test --workspace --all-features
|
|
|
|
|
|
else
|
|
|
|
|
|
echo " → Using: /tmp/typedialog-ci (target/debug does not exist)"
|
|
|
|
|
|
cargo test --target-dir=/tmp/typedialog-ci --workspace --all-features
|
|
|
|
|
|
fi
|
|
|
|
|
|
echo "✓ All tests passed"
|
2025-12-18 01:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
# Test default features
|
|
|
|
|
|
[doc("Test default features")]
|
|
|
|
|
|
test-default:
|
2025-12-24 03:24:31 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
cd "{{ WORKSPACE_ROOT }}"
|
|
|
|
|
|
echo "=== Testing (default features) ==="
|
|
|
|
|
|
if [ -d "target/debug" ]; then
|
|
|
|
|
|
echo " → Using: target/debug (exists)"
|
|
|
|
|
|
cargo test --workspace
|
|
|
|
|
|
else
|
|
|
|
|
|
echo " → Using: /tmp/typedialog-ci (target/debug does not exist)"
|
|
|
|
|
|
cargo test --target-dir=/tmp/typedialog-ci --workspace
|
|
|
|
|
|
fi
|
|
|
|
|
|
echo "✓ Tests passed (default features)"
|
2025-12-18 01:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
# Test integration only
|
|
|
|
|
|
[doc("Test integration")]
|
|
|
|
|
|
test-integration:
|
2025-12-24 03:24:31 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
cd "{{ WORKSPACE_ROOT }}"
|
|
|
|
|
|
echo "=== Testing integration ==="
|
|
|
|
|
|
if [ -d "target/debug" ]; then
|
|
|
|
|
|
echo " → Using: target/debug (exists)"
|
|
|
|
|
|
cargo test --test '*' --all-features
|
|
|
|
|
|
else
|
|
|
|
|
|
echo " → Using: /tmp/typedialog-ci (target/debug does not exist)"
|
|
|
|
|
|
cargo test --target-dir=/tmp/typedialog-ci --test '*' --all-features
|
|
|
|
|
|
fi
|
|
|
|
|
|
echo "✓ Integration tests passed"
|
2025-12-18 01:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
# === BUILD STAGE ===
|
|
|
|
|
|
|
2025-12-24 03:24:31 +00:00
|
|
|
|
# Debug build (commented out - large disk usage)
|
|
|
|
|
|
# [doc("Build debug")]
|
|
|
|
|
|
# build-debug:
|
|
|
|
|
|
# @echo "=== Building debug ==="
|
|
|
|
|
|
# cargo build --workspace --all-features
|
|
|
|
|
|
# @echo "✓ Debug build complete"
|
2025-12-18 01:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
# Release build
|
|
|
|
|
|
[doc("Build release")]
|
|
|
|
|
|
build-release:
|
|
|
|
|
|
@echo "=== Building release (optimized) ==="
|
|
|
|
|
|
cargo build --workspace --all-features --release
|
|
|
|
|
|
@echo ""
|
|
|
|
|
|
@echo "Binaries:"
|
|
|
|
|
|
@ls -lh target/release/typedialog* 2>/dev/null | awk '{printf " %-30s %8s\n", $NF, $5}'
|
|
|
|
|
|
@echo "✓ Release build complete"
|
|
|
|
|
|
|
|
|
|
|
|
# === COMPLIANCE STAGE ===
|
|
|
|
|
|
|
|
|
|
|
|
# Audit dependencies for security issues
|
|
|
|
|
|
[doc("Audit dependencies for vulnerabilities")]
|
|
|
|
|
|
audit:
|
2025-12-24 03:24:31 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
echo "=== Auditing dependencies ==="
|
|
|
|
|
|
|
|
|
|
|
|
cd "{{ WORKSPACE_ROOT }}"
|
2025-12-18 01:18:59 +00:00
|
|
|
|
|
2025-12-24 03:24:31 +00:00
|
|
|
|
# Ensure HOME is set to absolute path, never literal ~
|
|
|
|
|
|
if [ -z "$HOME" ]; then
|
|
|
|
|
|
export HOME="$(getent passwd $(whoami) | cut -d: -f6)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
export CARGO_HOME="${HOME}/.cargo"
|
|
|
|
|
|
|
|
|
|
|
|
# Use explicit --db path to prevent cargo audit from creating literal ~ directory
|
|
|
|
|
|
# This is a workaround for a cargo audit issue with ~ expansion
|
|
|
|
|
|
audit_db="${CARGO_HOME}/advisory-db"
|
|
|
|
|
|
mkdir -p "$audit_db"
|
|
|
|
|
|
|
|
|
|
|
|
if cargo audit --quiet --db "$audit_db" 2>&1 > /dev/null; then
|
|
|
|
|
|
echo "✓ No vulnerabilities found"
|
|
|
|
|
|
else
|
|
|
|
|
|
echo "⚠️ Check failed (run 'cargo audit' manually)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# cargo-deny check (licenses only, show errors but suppress warnings)
|
|
|
|
|
|
[doc("Check dependencies with cargo-deny")]
|
|
|
|
|
|
deny:
|
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
echo "=== Running cargo-deny ==="
|
|
|
|
|
|
|
|
|
|
|
|
cd "{{ WORKSPACE_ROOT }}"
|
|
|
|
|
|
|
|
|
|
|
|
# Ensure HOME is set to absolute path
|
|
|
|
|
|
if [ -z "$HOME" ]; then
|
|
|
|
|
|
export HOME="$(getent passwd $(whoami) | cut -d: -f6)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
{ cargo deny check licenses 2>&1 | sed '/^warning\[/,/^$$/d' | grep -q "error\[" && cargo deny check licenses 2>&1 | sed '/^warning\[/,/^$$/d' && exit 1; } || echo "✓ No license violations"
|
|
|
|
|
|
|
|
|
|
|
|
# Verify SBOMs can be generated
|
|
|
|
|
|
[doc("Verify SBOMs can be generated")]
|
2025-12-18 01:18:59 +00:00
|
|
|
|
verify-sbom:
|
2025-12-24 03:24:31 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
echo "=== Verifying SBOMs ==="
|
|
|
|
|
|
echo "Generating SBOMs to verify compliance..."
|
|
|
|
|
|
|
|
|
|
|
|
cd "{{ WORKSPACE_ROOT }}"
|
|
|
|
|
|
|
|
|
|
|
|
# Ensure HOME is set to absolute path
|
|
|
|
|
|
if [ -z "$HOME" ]; then
|
|
|
|
|
|
export HOME="$(getent passwd $(whoami) | cut -d: -f6)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
cargo sbom --output-format spdx_json_2_3 > /tmp/sbom_spdx.json || echo "⚠️ SPDX SBOM generation failed"
|
|
|
|
|
|
cargo sbom --output-format cyclone_dx_json_1_4 > /tmp/sbom_cyclonedx.json || echo "⚠️ CycloneDX SBOM generation failed"
|
|
|
|
|
|
echo "✓ SBOM verification complete"
|
2025-12-18 01:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
# === FULL PIPELINE ===
|
|
|
|
|
|
|
|
|
|
|
|
# Complete CI pipeline
|
|
|
|
|
|
[doc("Full CI: check + test + build")]
|
|
|
|
|
|
full:
|
2025-12-24 03:24:31 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
|
|
# Clean any stray ~ directory from previous runs (created by cargo audit download)
|
|
|
|
|
|
if [ -d "~" ]; then
|
|
|
|
|
|
rm -rf "~"
|
|
|
|
|
|
echo "✓ Cleaned stray ~ directory"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
echo "╔═══════════════════════════════════════════════════════════╗"
|
|
|
|
|
|
echo "║ TYPEDIALOG CI/CD PIPELINE ║"
|
|
|
|
|
|
echo "╚═══════════════════════════════════════════════════════════╝"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
# Ensure HOME and CARGO_HOME are set to absolute paths (prevents cargo from creating literal ~ directory)
|
|
|
|
|
|
if [ -z "$HOME" ]; then
|
|
|
|
|
|
export HOME="$(getent passwd $(whoami) | cut -d: -f6)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
# Force CARGO_HOME to absolute path to prevent literal ~ directory creation
|
|
|
|
|
|
export CARGO_HOME="${HOME}/.cargo"
|
|
|
|
|
|
# Pre-create CARGO_HOME if it doesn't exist
|
|
|
|
|
|
mkdir -p "$CARGO_HOME"
|
|
|
|
|
|
|
|
|
|
|
|
echo "ℹ️ Using HOME: $HOME"
|
|
|
|
|
|
echo "ℹ️ Using CARGO_HOME: $CARGO_HOME"
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
# Detect if target/debug exists at start
|
|
|
|
|
|
debug_existed=0
|
|
|
|
|
|
if [ -d "target/debug" ]; then
|
|
|
|
|
|
debug_existed=1
|
|
|
|
|
|
echo "ℹ️ target/debug detected - will preserve after CI"
|
|
|
|
|
|
fi
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
echo "=== Stage 1: Validation ==="
|
|
|
|
|
|
just ci::fmt-check
|
|
|
|
|
|
just ci::lint-all
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
echo "=== Stage 2: Compliance ==="
|
2025-12-18 01:18:59 +00:00
|
|
|
|
just ci::verify-sbom
|
2025-12-24 03:24:31 +00:00
|
|
|
|
just ci::audit
|
|
|
|
|
|
just ci::deny
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
echo "=== Stage 3: Testing ==="
|
2025-12-18 01:18:59 +00:00
|
|
|
|
just ci::test-all
|
2025-12-24 03:24:31 +00:00
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
echo "=== Stage 4: Build Release ==="
|
2025-12-18 01:18:59 +00:00
|
|
|
|
just ci::build-release
|
2025-12-24 03:24:31 +00:00
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
echo "=== Cleanup ==="
|
|
|
|
|
|
if [ $debug_existed -eq 0 ] && [ -d "/tmp/typedialog-ci" ]; then
|
|
|
|
|
|
rm -rf /tmp/typedialog-ci
|
|
|
|
|
|
echo "✓ Temporary artifacts cleaned"
|
|
|
|
|
|
else
|
|
|
|
|
|
if [ $debug_existed -eq 1 ]; then
|
|
|
|
|
|
echo "✓ Preserving target/debug (already existed)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
if [ -d "/tmp/typedialog-ci" ]; then
|
|
|
|
|
|
echo "✓ Temporary CI directory left in place"
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
|
|
echo "╔═══════════════════════════════════════════════════════════╗"
|
|
|
|
|
|
echo "║ CI PIPELINE COMPLETE ✓ ║"
|
|
|
|
|
|
echo "╚═══════════════════════════════════════════════════════════╝"
|