Files
silo/Makefile
Forbes afb382b68d feat: LibreOffice Calc extension, ODS library, AI description, audit design
Calc extension (pkg/calc/):
- Python UNO ProtocolHandler with 8 toolbar commands
- SiloClient HTTP client adapted from FreeCAD workbench
- Pull BOM/Project: populates sheets with 28-col format, hidden property
  columns, row hash tracking, auto project tagging
- Push: row classification, create/update items, conflict detection
- Completion wizard: 3-step category/description/fields with PN conflict
  resolution dialog
- OpenRouter AI integration: generate standardized descriptions from seller
  text, configurable model/instructions, review dialog
- Settings: JSON persistence, env var fallbacks, OpenRouter fields
- 31 unit tests (no UNO/network required)

Go ODS library (internal/ods/):
- Pure Go ODS read/write (ZIP of XML, no headless LibreOffice)
- Writer, reader, 10 round-trip tests

Server ODS endpoints (internal/api/ods.go):
- GET /api/items/export.ods, template.ods, POST import.ods
- GET /api/items/{pn}/bom/export.ods
- GET /api/projects/{code}/sheet.ods
- POST /api/sheets/diff

Documentation:
- docs/CALC_EXTENSION.md: extension progress report
- docs/COMPONENT_AUDIT.md: web audit tool design with weighted scoring,
  assembly computed fields, batch AI assistance plan
2026-02-01 10:06:20 -06:00

279 lines
8.9 KiB
Makefile

.PHONY: build run test clean migrate fmt lint \
docker-build docker-up docker-down docker-logs docker-ps \
docker-clean docker-rebuild \
build-calc-oxt install-calc uninstall-calc install-calc-dev test-calc
# =============================================================================
# Local Development
# =============================================================================
# Build all binaries
build:
go build -o silo ./cmd/silo
go build -o silod ./cmd/silod
# Run the API server locally
run:
go run ./cmd/silod -config config.yaml
# Run the CLI
cli:
go run ./cmd/silo $(ARGS)
# Run tests (Go + Python)
test:
go test -v ./...
python3 -m unittest pkg/calc/tests/test_basics.py -v
# Clean build artifacts
clean:
rm -f silo silod silo-calc.oxt
rm -f *.out
# Format code
fmt:
go fmt ./...
goimports -w .
# Lint code
lint:
golangci-lint run
# Tidy dependencies
tidy:
go mod tidy
# =============================================================================
# Database
# =============================================================================
# Run database migrations (requires SILO_DB_* environment variables)
migrate:
./scripts/init-db.sh
# Connect to database (requires psql)
db-shell:
PGPASSWORD=$${SILO_DB_PASSWORD:-silodev} psql -h $${SILO_DB_HOST:-localhost} -U $${SILO_DB_USER:-silo} -d $${SILO_DB_NAME:-silo}
# =============================================================================
# Docker
# =============================================================================
# Build Docker image
docker-build:
docker build -t silo:latest -f build/package/Dockerfile .
# Start the full stack (postgres + minio + silo)
docker-up:
docker compose -f deployments/docker-compose.yaml up -d
# Start with rebuild
docker-up-build:
docker compose -f deployments/docker-compose.yaml up -d --build
# Stop the stack
docker-down:
docker compose -f deployments/docker-compose.yaml down
# Stop and remove volumes (WARNING: deletes data)
docker-clean:
docker compose -f deployments/docker-compose.yaml down -v
# View logs
docker-logs:
docker compose -f deployments/docker-compose.yaml logs -f
# View logs for specific service
docker-logs-silo:
docker compose -f deployments/docker-compose.yaml logs -f silo
docker-logs-postgres:
docker compose -f deployments/docker-compose.yaml logs -f postgres
docker-logs-minio:
docker compose -f deployments/docker-compose.yaml logs -f minio
# Show running containers
docker-ps:
docker compose -f deployments/docker-compose.yaml ps
# Rebuild and restart
docker-rebuild: docker-down docker-build docker-up
# Shell into silo container
docker-shell:
docker compose -f deployments/docker-compose.yaml exec silo /bin/sh
# =============================================================================
# FreeCAD Integration
# =============================================================================
# Detect FreeCAD Mod directory (Flatpak or native)
# Flatpak app ID can be org.freecad.FreeCAD or org.freecadweb.FreeCAD
FREECAD_MOD_DIR_FLATPAK := $(HOME)/.var/app/org.freecad.FreeCAD/data/FreeCAD/Mod
FREECAD_MOD_DIR_NATIVE := $(HOME)/.local/share/FreeCAD/Mod
FREECAD_MOD_DIR_LEGACY := $(HOME)/.FreeCAD/Mod
# Install FreeCAD workbench (auto-detect Flatpak or native)
install-freecad:
@if [ -d "$(HOME)/.var/app/org.freecad.FreeCAD" ]; then \
echo "Detected Flatpak FreeCAD (org.freecad.FreeCAD)"; \
mkdir -p $(FREECAD_MOD_DIR_FLATPAK); \
rm -f $(FREECAD_MOD_DIR_FLATPAK)/Silo; \
ln -sf $(PWD)/pkg/freecad $(FREECAD_MOD_DIR_FLATPAK)/Silo; \
echo "Installed to $(FREECAD_MOD_DIR_FLATPAK)/Silo"; \
else \
echo "Using native FreeCAD installation"; \
mkdir -p $(FREECAD_MOD_DIR_NATIVE); \
mkdir -p $(FREECAD_MOD_DIR_LEGACY); \
rm -f $(FREECAD_MOD_DIR_NATIVE)/Silo; \
rm -f $(FREECAD_MOD_DIR_LEGACY)/Silo; \
ln -sf $(PWD)/pkg/freecad $(FREECAD_MOD_DIR_NATIVE)/Silo; \
ln -sf $(PWD)/pkg/freecad $(FREECAD_MOD_DIR_LEGACY)/Silo; \
echo "Installed to $(FREECAD_MOD_DIR_NATIVE)/Silo"; \
fi
@echo ""
@echo "Restart FreeCAD to load the Silo workbench"
# Install for Flatpak FreeCAD explicitly
install-freecad-flatpak:
mkdir -p $(FREECAD_MOD_DIR_FLATPAK)
rm -f $(FREECAD_MOD_DIR_FLATPAK)/Silo
ln -sf $(PWD)/pkg/freecad $(FREECAD_MOD_DIR_FLATPAK)/Silo
@echo "Installed to $(FREECAD_MOD_DIR_FLATPAK)/Silo"
@echo "Restart FreeCAD to load the Silo workbench"
# Install for native FreeCAD explicitly
install-freecad-native:
mkdir -p $(FREECAD_MOD_DIR_NATIVE)
mkdir -p $(FREECAD_MOD_DIR_LEGACY)
rm -f $(FREECAD_MOD_DIR_NATIVE)/Silo
rm -f $(FREECAD_MOD_DIR_LEGACY)/Silo
ln -sf $(PWD)/pkg/freecad $(FREECAD_MOD_DIR_NATIVE)/Silo
ln -sf $(PWD)/pkg/freecad $(FREECAD_MOD_DIR_LEGACY)/Silo
@echo "Installed to $(FREECAD_MOD_DIR_NATIVE)/Silo"
# Uninstall FreeCAD workbench
uninstall-freecad:
rm -f $(FREECAD_MOD_DIR_FLATPAK)/Silo
rm -f $(FREECAD_MOD_DIR_NATIVE)/Silo
rm -f $(FREECAD_MOD_DIR_LEGACY)/Silo
@echo "Uninstalled Silo workbench"
# =============================================================================
# LibreOffice Calc Extension
# =============================================================================
# Build .oxt extension package
build-calc-oxt:
@echo "Building silo-calc.oxt..."
@cd pkg/calc && zip -r ../../silo-calc.oxt . \
-x '*.pyc' '*__pycache__/*' 'tests/*' '.gitignore'
@echo "Built silo-calc.oxt"
# Install extension system-wide (requires unopkg)
install-calc: build-calc-oxt
unopkg add --shared silo-calc.oxt 2>/dev/null || unopkg add silo-calc.oxt
@echo "Installed silo-calc extension. Restart LibreOffice to load."
# Uninstall extension
uninstall-calc:
unopkg remove io.kindredsystems.silo.calc 2>/dev/null || true
@echo "Uninstalled silo-calc extension."
# Development install: symlink into user extensions dir
install-calc-dev:
@CALC_EXT_DIR="$${HOME}/.config/libreoffice/4/user/extensions"; \
if [ -d "$$CALC_EXT_DIR" ]; then \
rm -rf "$$CALC_EXT_DIR/silo-calc"; \
ln -sf $(PWD)/pkg/calc "$$CALC_EXT_DIR/silo-calc"; \
echo "Symlinked to $$CALC_EXT_DIR/silo-calc"; \
else \
echo "LibreOffice extensions dir not found at $$CALC_EXT_DIR"; \
echo "Try: install-calc (uses unopkg)"; \
fi
@echo "Restart LibreOffice to load the Silo Calc extension"
# Run Python tests for the Calc extension
test-calc:
python3 -m unittest pkg/calc/tests/test_basics.py -v
# Clean extension package
clean-calc:
rm -f silo-calc.oxt
# =============================================================================
# API Testing
# =============================================================================
# Test health endpoint
api-health:
curl -s http://localhost:8080/health | jq .
# Test ready endpoint
api-ready:
curl -s http://localhost:8080/ready | jq .
# List schemas
api-schemas:
curl -s http://localhost:8080/api/schemas | jq .
# List items
api-items:
curl -s http://localhost:8080/api/items | jq .
# Create sample item
api-create-item:
curl -s -X POST http://localhost:8080/api/items \
-H "Content-Type: application/json" \
-d '{"schema":"kindred-rd","project":"CS100","category":"F01","material":"316","description":"Test screw"}' | jq .
# =============================================================================
# Help
# =============================================================================
help:
@echo "Silo Makefile targets:"
@echo ""
@echo "Local Development:"
@echo " build - Build CLI and server binaries"
@echo " run - Run API server locally"
@echo " cli ARGS=... - Run CLI with arguments"
@echo " test - Run tests"
@echo " fmt - Format code"
@echo " lint - Run linter"
@echo " tidy - Tidy go.mod"
@echo ""
@echo "Docker:"
@echo " docker-build - Build Docker image"
@echo " docker-up - Start full stack (postgres + minio + silo)"
@echo " docker-down - Stop the stack"
@echo " docker-clean - Stop and remove volumes (deletes data)"
@echo " docker-logs - View all logs"
@echo " docker-ps - Show running containers"
@echo " docker-rebuild - Rebuild and restart"
@echo ""
@echo "Database:"
@echo " migrate - Run database migrations"
@echo " db-shell - Connect to database with psql"
@echo ""
@echo "FreeCAD:"
@echo " install-freecad - Install workbench (auto-detect Flatpak/native)"
@echo " install-freecad-flatpak - Install for Flatpak FreeCAD"
@echo " install-freecad-native - Install for native FreeCAD"
@echo " uninstall-freecad - Remove workbench symlinks"
@echo ""
@echo "LibreOffice Calc:"
@echo " build-calc-oxt - Build .oxt extension package"
@echo " install-calc - Install extension (uses unopkg)"
@echo " install-calc-dev - Symlink for development"
@echo " uninstall-calc - Remove extension"
@echo " test-calc - Run Python tests for extension"
@echo " clean-calc - Remove .oxt file"
@echo ""
@echo "API Testing:"
@echo " api-health - Test health endpoint"
@echo " api-schemas - List schemas"
@echo " api-items - List items"
@echo " api-create-item - Create a test item"