blackopsrepl commited on
Commit
7be2122
·
1 Parent(s): c5a0215

chore(space): add Space and local validation tooling

Browse files

Add the hospital-style local workflow for the deliveries tutorial. The Makefile now exposes the supported build, run, lint, test, live-road, pre-release, Docker, and Hugging Face Space validation commands, with ci-local including the Space image build as the release gate.

Add a Dockerfile for Hugging Face Docker Spaces that builds the Rust 1.95 Alpine release binary from published crates.io dependencies, copies the static browser assets and solver.toml into a minimal Alpine runtime image, and binds the app through PORT with 7860 as the documented default.

Add .dockerignore entries so target output, git metadata, the OSM route cache, Playwright output, and Rust backup files stay out of the Space build context. This is a tooling and hosting boundary: reverting it removes local orchestration and Space packaging without changing application behavior or tests.

Files changed (3) hide show
  1. .dockerignore +6 -0
  2. Dockerfile +34 -0
  3. Makefile +266 -0
.dockerignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ target/
2
+ .git/
3
+ .osm_cache/
4
+ test-results/
5
+ playwright-report/
6
+ *.rs.bk
Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Multi-stage build for solverforge-deliveries.
2
+ #
3
+ # The app depends on published crates.io packages, so the repository root is the
4
+ # complete Docker build context:
5
+ # docker build -f Dockerfile -t solverforge-deliveries .
6
+
7
+ FROM rust:1.95-alpine AS builder
8
+
9
+ RUN apk add --no-cache musl-dev
10
+
11
+ WORKDIR /build
12
+
13
+ COPY Cargo.toml Cargo.lock ./
14
+ COPY src/ ./src/
15
+ COPY static/ ./static/
16
+ COPY solver.toml ./solver.toml
17
+
18
+ RUN cargo build --release --target x86_64-unknown-linux-musl
19
+
20
+ FROM alpine:latest
21
+
22
+ RUN apk add --no-cache ca-certificates
23
+
24
+ WORKDIR /app
25
+
26
+ COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/solverforge_deliveries ./solverforge_deliveries
27
+ COPY --from=builder /build/static/ ./static/
28
+ COPY --from=builder /build/solver.toml ./solver.toml
29
+
30
+ ENV PORT=7860
31
+
32
+ EXPOSE 7860
33
+
34
+ CMD ["./solverforge_deliveries"]
Makefile ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SolverForge Deliveries Makefile v1.0.0
2
+ # Rust + frontend + Space-oriented local build system.
3
+ #
4
+ # This app is validated for local development and Docker-based Hugging Face
5
+ # Space deployment. `ci-local` therefore includes a Docker image build.
6
+
7
+ # ============== Colors & Symbols ==============
8
+ GREEN := \033[92m
9
+ EMERALD := \033[38;2;16;185;129m
10
+ CYAN := \033[96m
11
+ YELLOW := \033[93m
12
+ RED := \033[91m
13
+ GRAY := \033[90m
14
+ BOLD := \033[1m
15
+ RESET := \033[0m
16
+
17
+ CHECK := OK
18
+ CROSS := FAIL
19
+ ARROW := =>
20
+ PROGRESS := ..
21
+
22
+ # ============== Project Metadata ==============
23
+ APP_NAME := solverforge_deliveries
24
+ PACKAGE_NAME := solverforge-deliveries
25
+ VERSION := $(shell sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)
26
+ RUST_VERSION := 1.95+
27
+ PORT ?= 7860
28
+ DOCKER_IMAGE ?= $(PACKAGE_NAME)
29
+ DOCKER_CONTEXT ?= .
30
+ DOCKERFILE_PATH := Dockerfile
31
+ PLAYWRIGHT ?= ../solverforge-ui/node_modules/.bin/playwright
32
+ PLAYWRIGHT_NODE_PATH ?= ../solverforge-ui/node_modules
33
+
34
+ # ============== Phony Targets ==============
35
+ .PHONY: banner help doctor build build-release run run-release test test-rust \
36
+ test-frontend-syntax test-frontend test-e2e test-live-road test-one lint fmt \
37
+ fmt-check clippy check ci-local space-ci space-build space-run docker-build \
38
+ docker-run pre-release version clean watch require-node require-docker
39
+
40
+ .DEFAULT_GOAL := help
41
+
42
+ # ============== Banner ==============
43
+ banner:
44
+ @printf "$(EMERALD)$(BOLD) ____ _ _____\n"
45
+ @printf " / ___| ___ | |_ _____ _ __| ___|__ _ __ __ _ ___\n"
46
+ @printf " \\___ \\\\ / _ \\\\| \\\\ \\\\ / / _ \\\\ '__| |_ / _ \\\\| '__/ _\` |/ _ \\\\\n"
47
+ @printf " ___) | (_) | |\\\\ V / __/ | | _| (_) | | | (_| | __/\n"
48
+ @printf " |____/ \\\\___/|_| \\_/ \\___|_| |_| \\___/|_| \\__, |\\___|\n"
49
+ @printf " |___/$(RESET)\n"
50
+ @printf " $(GRAY)v$(VERSION)$(RESET) $(EMERALD)Deliveries demo build system$(RESET)\n\n"
51
+
52
+ # ============== Environment Checks ==============
53
+ require-node:
54
+ @command -v node >/dev/null 2>&1 || (printf "$(RED)$(CROSS) node is required for frontend validation$(RESET)\n" && exit 1)
55
+
56
+ require-docker:
57
+ @command -v docker >/dev/null 2>&1 || (printf "$(RED)$(CROSS) docker is required for Space/Docker targets$(RESET)\n" && exit 1)
58
+
59
+ doctor: banner
60
+ @printf "$(CYAN)$(BOLD)Environment Check$(RESET)\n\n"
61
+ @missing=0; \
62
+ if command -v cargo >/dev/null 2>&1; then \
63
+ printf "$(GREEN)$(CHECK) cargo: $$(cargo --version)$(RESET)\n"; \
64
+ else \
65
+ printf "$(RED)$(CROSS) cargo not found$(RESET)\n"; missing=1; \
66
+ fi; \
67
+ if command -v rustc >/dev/null 2>&1; then \
68
+ printf "$(GREEN)$(CHECK) rustc: $$(rustc --version)$(RESET)\n"; \
69
+ else \
70
+ printf "$(RED)$(CROSS) rustc not found$(RESET)\n"; missing=1; \
71
+ fi; \
72
+ if command -v node >/dev/null 2>&1; then \
73
+ printf "$(GREEN)$(CHECK) node: $$(node --version)$(RESET)\n"; \
74
+ else \
75
+ printf "$(RED)$(CROSS) node not found$(RESET)\n"; missing=1; \
76
+ fi; \
77
+ if command -v docker >/dev/null 2>&1; then \
78
+ printf "$(GREEN)$(CHECK) docker: $$(docker --version)$(RESET)\n"; \
79
+ else \
80
+ printf "$(YELLOW)! docker not found; Space/Docker targets will be unavailable$(RESET)\n"; \
81
+ fi; \
82
+ printf "$(GRAY)Docker build context: $(DOCKER_CONTEXT)$(RESET)\n"; \
83
+ printf "$(GRAY)Default app port: $(PORT)$(RESET)\n"; \
84
+ if [ $$missing -ne 0 ]; then exit 1; fi
85
+ @printf "\n"
86
+
87
+ # ============== Build & Run ==============
88
+ build: banner
89
+ @printf "$(ARROW) $(BOLD)Building $(PACKAGE_NAME)...$(RESET)\n"
90
+ @cargo build --bin $(APP_NAME) && \
91
+ printf "$(GREEN)$(CHECK) Debug build successful$(RESET)\n\n" || \
92
+ (printf "$(RED)$(CROSS) Debug build failed$(RESET)\n\n" && exit 1)
93
+
94
+ build-release: banner
95
+ @printf "$(ARROW) $(BOLD)Building release binary...$(RESET)\n"
96
+ @cargo build --release --bin $(APP_NAME) && \
97
+ printf "$(GREEN)$(CHECK) Release build successful$(RESET)\n\n" || \
98
+ (printf "$(RED)$(CROSS) Release build failed$(RESET)\n\n" && exit 1)
99
+
100
+ run:
101
+ @printf "$(ARROW) Running $(PACKAGE_NAME) on port $(PORT)...\n"
102
+ @PORT=$(PORT) cargo run --bin $(APP_NAME)
103
+
104
+ run-release:
105
+ @printf "$(ARROW) Running release build on port $(PORT)...\n"
106
+ @PORT=$(PORT) cargo run --release --bin $(APP_NAME)
107
+
108
+ # ============== Test Targets ==============
109
+ test: test-rust test-frontend test-e2e
110
+ @printf "\n$(GREEN)$(BOLD)$(CHECK) Standard validation passed$(RESET)\n\n"
111
+
112
+ test-rust: banner
113
+ @printf "$(ARROW) $(BOLD)Running cargo test --quiet...$(RESET)\n"
114
+ @cargo test --quiet && \
115
+ printf "\n$(GREEN)$(CHECK) Rust tests passed$(RESET)\n\n" || \
116
+ (printf "\n$(RED)$(CROSS) Rust tests failed$(RESET)\n\n" && exit 1)
117
+
118
+ test-frontend-syntax: require-node
119
+ @printf "$(PROGRESS) Checking frontend module syntax...\n"
120
+ @find static/app -name '*.mjs' -print0 | xargs -0 -n1 node --check && \
121
+ printf "$(GREEN)$(CHECK) Frontend syntax checks passed$(RESET)\n" || \
122
+ (printf "$(RED)$(CROSS) Frontend syntax checks failed$(RESET)\n" && exit 1)
123
+
124
+ test-frontend: test-frontend-syntax
125
+ @printf "$(PROGRESS) Running frontend tests...\n"
126
+ @node --test tests/frontend_models.test.mjs && \
127
+ printf "$(GREEN)$(CHECK) Frontend tests passed$(RESET)\n" || \
128
+ (printf "$(RED)$(CROSS) Frontend tests failed$(RESET)\n" && exit 1)
129
+
130
+ test-e2e: build-release require-node
131
+ @printf "$(PROGRESS) Running Playwright browser tests...\n"
132
+ @NODE_PATH=$(PLAYWRIGHT_NODE_PATH) $(PLAYWRIGHT) test --config tests/e2e/playwright.config.js && \
133
+ printf "$(GREEN)$(CHECK) Playwright browser tests passed$(RESET)\n" || \
134
+ (printf "$(RED)$(CROSS) Playwright browser tests failed$(RESET)\n" && exit 1)
135
+
136
+ test-live-road: banner
137
+ @printf "$(ARROW) $(BOLD)Running live road-network route tests...$(RESET)\n"
138
+ @SOLVERFORGE_RUN_LIVE_TESTS=1 cargo test live_demo_locations_are_mutually_reachable_when_enabled -- --nocapture && \
139
+ SOLVERFORGE_RUN_LIVE_TESTS=1 cargo test road_network_job_routes_work_when_live_tests_are_enabled -- --nocapture && \
140
+ printf "\n$(GREEN)$(CHECK) Live road-network test passed$(RESET)\n\n" || \
141
+ (printf "\n$(RED)$(CROSS) Live road-network test failed$(RESET)\n\n" && exit 1)
142
+
143
+ test-one:
144
+ @printf "$(PROGRESS) Running test: $(YELLOW)$(TEST)$(RESET)\n"
145
+ @RUST_LOG=info cargo test $(TEST) -- --nocapture
146
+
147
+ # ============== Lint & Format ==============
148
+ fmt:
149
+ @printf "$(PROGRESS) Formatting Rust code...\n"
150
+ @cargo fmt
151
+ @printf "$(GREEN)$(CHECK) Code formatted$(RESET)\n"
152
+
153
+ fmt-check:
154
+ @printf "$(PROGRESS) Checking Rust formatting...\n"
155
+ @cargo fmt --check && \
156
+ printf "$(GREEN)$(CHECK) Formatting valid$(RESET)\n" || \
157
+ (printf "$(RED)$(CROSS) Formatting issues found$(RESET)\n" && exit 1)
158
+
159
+ clippy:
160
+ @printf "$(PROGRESS) Running clippy...\n"
161
+ @cargo clippy --all-targets -- -D warnings && \
162
+ printf "$(GREEN)$(CHECK) Clippy passed$(RESET)\n" || \
163
+ (printf "$(RED)$(CROSS) Clippy warnings found$(RESET)\n" && exit 1)
164
+
165
+ lint: fmt-check clippy test-frontend-syntax
166
+ @printf "\n$(GREEN)$(BOLD)$(CHECK) Lint checks passed$(RESET)\n\n"
167
+
168
+ check: lint test
169
+
170
+ # ============== Space & Docker ==============
171
+ docker-build: require-docker
172
+ @printf "$(PROGRESS) Building Docker image $(DOCKER_IMAGE)...\n"
173
+ @docker build -f "$(DOCKERFILE_PATH)" -t "$(DOCKER_IMAGE)" "$(DOCKER_CONTEXT)" && \
174
+ printf "$(GREEN)$(CHECK) Docker image built$(RESET)\n" || \
175
+ (printf "$(RED)$(CROSS) Docker build failed$(RESET)\n" && exit 1)
176
+
177
+ docker-run: require-docker
178
+ @printf "$(ARROW) Running $(DOCKER_IMAGE) on port $(PORT)...\n"
179
+ @docker run --rm -it -e PORT=$(PORT) -p $(PORT):$(PORT) "$(DOCKER_IMAGE)"
180
+
181
+ space-build: docker-build
182
+
183
+ space-run: space-build
184
+ @printf "$(GREEN)$(CHECK) Starting local container that mirrors the Space image$(RESET)\n"
185
+ @$(MAKE) docker-run --no-print-directory PORT=$(PORT) DOCKER_IMAGE=$(DOCKER_IMAGE)
186
+
187
+ space-ci: ci-local
188
+
189
+ # ============== CI & Release Validation ==============
190
+ ci-local: banner
191
+ @printf "$(CYAN)$(BOLD)Local Validation Pipeline$(RESET)\n\n"
192
+ @printf "$(PROGRESS) Step 1/5: Format check...\n"
193
+ @$(MAKE) fmt-check --no-print-directory
194
+ @printf "$(PROGRESS) Step 2/5: Clippy...\n"
195
+ @$(MAKE) clippy --no-print-directory
196
+ @printf "$(PROGRESS) Step 3/5: Release build...\n"
197
+ @$(MAKE) build-release --no-print-directory
198
+ @printf "$(PROGRESS) Step 4/5: Standard test surface...\n"
199
+ @$(MAKE) test --no-print-directory
200
+ @printf "$(PROGRESS) Step 5/5: Docker/Space image build...\n"
201
+ @$(MAKE) space-build --no-print-directory
202
+ @printf "\n$(GREEN)$(BOLD)$(CHECK) LOCAL SPACE VALIDATION PASSED$(RESET)\n\n"
203
+
204
+ pre-release: banner
205
+ @printf "$(CYAN)$(BOLD)Pre-Release Validation v$(VERSION)$(RESET)\n\n"
206
+ @$(MAKE) ci-local --no-print-directory
207
+ @printf "$(PROGRESS) Final step: live road-network smoke...\n"
208
+ @$(MAKE) test-live-road --no-print-directory
209
+ @printf "$(GREEN)$(BOLD)$(CHECK) Ready for publication or Space update$(RESET)\n\n"
210
+
211
+ # ============== Metadata & Cleanup ==============
212
+ version:
213
+ @printf "$(CYAN)Current version:$(RESET) $(YELLOW)$(BOLD)$(VERSION)$(RESET)\n"
214
+ @printf "$(CYAN)Default port:$(RESET) $(YELLOW)$(BOLD)$(PORT)$(RESET)\n"
215
+
216
+ clean:
217
+ @printf "$(ARROW) Cleaning build artifacts...\n"
218
+ @cargo clean
219
+ @printf "$(GREEN)$(CHECK) Clean complete$(RESET)\n"
220
+
221
+ watch:
222
+ @printf "$(ARROW) Watching and rerunning the app on port $(PORT)...\n"
223
+ @cargo watch --version >/dev/null 2>&1 || \
224
+ (printf "$(RED)$(CROSS) cargo-watch is required for make watch$(RESET)\n" && exit 1)
225
+ @PORT=$(PORT) cargo watch -x "run --bin $(APP_NAME)"
226
+
227
+ # ============== Help ==============
228
+ help: banner
229
+ @/bin/echo -e "$(CYAN)$(BOLD)Environment:$(RESET)"
230
+ @/bin/echo -e " $(GREEN)make doctor$(RESET) - Check local cargo/rustc/node readiness"
231
+ @/bin/echo -e ""
232
+ @/bin/echo -e "$(CYAN)$(BOLD)Build & Run:$(RESET)"
233
+ @/bin/echo -e " $(GREEN)make build$(RESET) - Build the app in debug mode"
234
+ @/bin/echo -e " $(GREEN)make build-release$(RESET) - Build the app in release mode"
235
+ @/bin/echo -e " $(GREEN)make run$(RESET) - Run locally on port $(PORT)"
236
+ @/bin/echo -e " $(GREEN)make run-release$(RESET) - Run the release build on port $(PORT)"
237
+ @/bin/echo -e ""
238
+ @/bin/echo -e "$(CYAN)$(BOLD)Tests & Validation:$(RESET)"
239
+ @/bin/echo -e " $(GREEN)make test$(RESET) - Run Rust, frontend, and Playwright tests"
240
+ @/bin/echo -e " $(GREEN)make test-rust$(RESET) - Run Rust tests only"
241
+ @/bin/echo -e " $(GREEN)make test-frontend$(RESET) - Run frontend syntax checks and tests"
242
+ @/bin/echo -e " $(GREEN)make test-e2e$(RESET) - Run Playwright browser tests"
243
+ @/bin/echo -e " $(GREEN)make test-live-road$(RESET) - Run the live road-network smoke test"
244
+ @/bin/echo -e " $(GREEN)make test-one TEST=name$(RESET) - Run a specific Rust test with output"
245
+ @/bin/echo -e " $(GREEN)make lint$(RESET) - Run fmt-check, clippy, and frontend syntax checks"
246
+ @/bin/echo -e " $(GREEN)make check$(RESET) - Run lint plus standard tests"
247
+ @/bin/echo -e " $(GREEN)make ci-local$(RESET) - Run local Space validation pipeline"
248
+ @/bin/echo -e " $(GREEN)make pre-release$(RESET) - Run ci-local plus live road-network smoke"
249
+ @/bin/echo -e ""
250
+ @/bin/echo -e "$(CYAN)$(BOLD)Space & Docker:$(RESET)"
251
+ @/bin/echo -e " $(GREEN)make space-build$(RESET) - Build the Docker image used for Space deployment"
252
+ @/bin/echo -e " $(GREEN)make space-run$(RESET) - Build and run that image locally on port $(PORT)"
253
+ @/bin/echo -e " $(GREEN)make docker-build$(RESET) - Build the Docker image directly"
254
+ @/bin/echo -e " $(GREEN)make docker-run$(RESET) - Run the Docker image directly"
255
+ @/bin/echo -e ""
256
+ @/bin/echo -e "$(CYAN)$(BOLD)Other:$(RESET)"
257
+ @/bin/echo -e " $(GREEN)make fmt$(RESET) - Format Rust code"
258
+ @/bin/echo -e " $(GREEN)make version$(RESET) - Show version and default port"
259
+ @/bin/echo -e " $(GREEN)make clean$(RESET) - Clean build artifacts"
260
+ @/bin/echo -e " $(GREEN)make watch$(RESET) - Watch source files and rerun the app"
261
+ @/bin/echo -e " $(GREEN)make help$(RESET) - Show this help message"
262
+ @/bin/echo -e ""
263
+ @/bin/echo -e "$(GRAY)Rust version required: $(RUST_VERSION)$(RESET)"
264
+ @/bin/echo -e "$(GRAY)Current version: v$(VERSION)$(RESET)"
265
+ @/bin/echo -e "$(GRAY)Default port: $(PORT)$(RESET)"
266
+ @/bin/echo -e ""