forked from macnev2013/anySCP
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
328 lines (275 loc) · 13.6 KB
/
Copy pathMakefile
File metadata and controls
328 lines (275 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
.PHONY: start-ssh-pass start-ssh-key stop-ssh-pass stop-ssh-key \
connect-ssh-pass connect-ssh-key \
logs-ssh-pass logs-ssh-key \
shell-ssh-pass shell-ssh-key \
ssh-keygen ssh-status ssh-clean ssh-clean-keys \
scp-build start-scp-pass start-scp-key stop-scp-pass stop-scp-key \
connect-scp-pass connect-scp-key \
logs-scp-pass logs-scp-key \
shell-scp-pass shell-scp-key scp-clean-image \
e2e e2e-build e2e-shell e2e-logs e2e-clean screenshots
# Test SSH servers (linuxserver/openssh-server) for local development.
# Two independent containers — password-auth and key-auth — can run in parallel.
SSH_IMAGE := lscr.io/linuxserver/openssh-server:latest
SSH_USER := testuser
SSH_PASSWORD := testpass
# Password-auth container
SSH_PASS_CONTAINER := anyscp-test-sshd-pass
SSH_PASS_PORT := 2222
# Key-auth container
SSH_KEY_CONTAINER := anyscp-test-sshd-key
SSH_KEY_PORT := 2223
# Test SSH keypair — generated on demand, kept out of git.
SSH_KEY_DIR := .test-ssh
SSH_KEY := $(SSH_KEY_DIR)/id_ed25519
SSH_PUB_KEY := $(SSH_KEY).pub
# SCP-only test server (extends linuxserver/openssh-server with SFTP stripped).
SCP_IMAGE := anyscp-test-scp-only:latest
SCP_IMAGE_STAMP := tests/scp-server/.image-stamp
SCP_IMAGE_SRC := tests/scp-server/Dockerfile tests/scp-server/disable-sftp.sh
# SCP-only password container
SSH_SCP_PASS_CONTAINER := anyscp-test-scp-pass
SSH_SCP_PASS_PORT := 2224
# SCP-only key container
SSH_SCP_KEY_CONTAINER := anyscp-test-scp-key
SSH_SCP_KEY_PORT := 2225
# ─── Keypair ──────────────────────────────────────────────────────────────────
$(SSH_KEY):
@mkdir -p $(SSH_KEY_DIR)
@ssh-keygen -t ed25519 -f $(SSH_KEY) -N "" -C "anyscp-test-key" >/dev/null
@echo "Generated test SSH key at $(SSH_KEY)"
ssh-keygen: $(SSH_KEY)
@echo "Private key: $(SSH_KEY)"
@echo "Public key: $(SSH_PUB_KEY)"
# ─── Password container ───────────────────────────────────────────────────────
start-ssh-pass:
@if [ "$$(docker ps -aq -f name=^/$(SSH_PASS_CONTAINER)$$)" ]; then \
echo "Starting existing container $(SSH_PASS_CONTAINER)..."; \
docker start $(SSH_PASS_CONTAINER) >/dev/null; \
else \
echo "Creating container $(SSH_PASS_CONTAINER) on port $(SSH_PASS_PORT)..."; \
docker run -d \
--name $(SSH_PASS_CONTAINER) \
-p $(SSH_PASS_PORT):2222 \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-e USER_NAME=$(SSH_USER) \
-e USER_PASSWORD=$(SSH_PASSWORD) \
-e PASSWORD_ACCESS=true \
-e SUDO_ACCESS=true \
$(SSH_IMAGE) >/dev/null; \
fi
@echo ""
@echo " Password SSH server running at:"
@echo " Host: localhost"
@echo " Port: $(SSH_PASS_PORT)"
@echo " User: $(SSH_USER)"
@echo " Password: $(SSH_PASSWORD)"
@echo ""
@echo " Connect: ssh -p $(SSH_PASS_PORT) $(SSH_USER)@localhost"
stop-ssh-pass:
@docker stop $(SSH_PASS_CONTAINER) >/dev/null 2>&1 && echo "Stopped $(SSH_PASS_CONTAINER)" || echo "$(SSH_PASS_CONTAINER) not running"
connect-ssh-pass:
@ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $(SSH_PASS_PORT) $(SSH_USER)@localhost
logs-ssh-pass:
@docker logs -f $(SSH_PASS_CONTAINER)
shell-ssh-pass:
@docker exec -it $(SSH_PASS_CONTAINER) /bin/sh
# ─── Key container ────────────────────────────────────────────────────────────
start-ssh-key: $(SSH_KEY)
@if [ "$$(docker ps -aq -f name=^/$(SSH_KEY_CONTAINER)$$)" ]; then \
echo "Starting existing container $(SSH_KEY_CONTAINER)..."; \
docker start $(SSH_KEY_CONTAINER) >/dev/null; \
else \
echo "Creating container $(SSH_KEY_CONTAINER) on port $(SSH_KEY_PORT)..."; \
docker run -d \
--name $(SSH_KEY_CONTAINER) \
-p $(SSH_KEY_PORT):2222 \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-e USER_NAME=$(SSH_USER) \
-e PASSWORD_ACCESS=false \
-e SUDO_ACCESS=true \
-e PUBLIC_KEY="$$(cat $(SSH_PUB_KEY))" \
$(SSH_IMAGE) >/dev/null; \
fi
@echo ""
@echo " Key-auth SSH server running at:"
@echo " Host: localhost"
@echo " Port: $(SSH_KEY_PORT)"
@echo " User: $(SSH_USER)"
@echo " Key: $(SSH_KEY)"
@echo ""
@echo " Connect: ssh -i $(SSH_KEY) -p $(SSH_KEY_PORT) $(SSH_USER)@localhost"
stop-ssh-key:
@docker stop $(SSH_KEY_CONTAINER) >/dev/null 2>&1 && echo "Stopped $(SSH_KEY_CONTAINER)" || echo "$(SSH_KEY_CONTAINER) not running"
connect-ssh-key: $(SSH_KEY)
@ssh -i $(SSH_KEY) -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $(SSH_KEY_PORT) $(SSH_USER)@localhost
logs-ssh-key:
@docker logs -f $(SSH_KEY_CONTAINER)
shell-ssh-key:
@docker exec -it $(SSH_KEY_CONTAINER) /bin/sh
# ─── SCP-only image ───────────────────────────────────────────────────────────
# Built from tests/scp-server/. Rebuilt automatically whenever the Dockerfile
# or the init script change.
$(SCP_IMAGE_STAMP): $(SCP_IMAGE_SRC)
@echo " building $(SCP_IMAGE) (sources changed)"
@cd tests/scp-server && docker build -t $(SCP_IMAGE) .
@touch $@
scp-build: $(SCP_IMAGE_STAMP)
# ─── SCP-only password container ──────────────────────────────────────────────
start-scp-pass: $(SCP_IMAGE_STAMP)
@if [ "$$(docker ps -aq -f name=^/$(SSH_SCP_PASS_CONTAINER)$$)" ]; then \
echo "Starting existing container $(SSH_SCP_PASS_CONTAINER)..."; \
docker start $(SSH_SCP_PASS_CONTAINER) >/dev/null; \
else \
echo "Creating container $(SSH_SCP_PASS_CONTAINER) on port $(SSH_SCP_PASS_PORT)..."; \
docker run -d \
--name $(SSH_SCP_PASS_CONTAINER) \
-p $(SSH_SCP_PASS_PORT):2222 \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-e USER_NAME=$(SSH_USER) \
-e USER_PASSWORD=$(SSH_PASSWORD) \
-e PASSWORD_ACCESS=true \
-e SUDO_ACCESS=true \
$(SCP_IMAGE) >/dev/null; \
fi
@echo ""
@echo " SCP-only (password) server running at:"
@echo " Host: localhost"
@echo " Port: $(SSH_SCP_PASS_PORT)"
@echo " User: $(SSH_USER)"
@echo " Password: $(SSH_PASSWORD)"
@echo " SFTP: disabled (Subsystem stripped)"
@echo ""
@echo " SCP (legacy proto): scp -O -P $(SSH_SCP_PASS_PORT) FILE $(SSH_USER)@localhost:"
@echo " SFTP (should fail): sftp -P $(SSH_SCP_PASS_PORT) $(SSH_USER)@localhost"
stop-scp-pass:
@docker stop $(SSH_SCP_PASS_CONTAINER) >/dev/null 2>&1 && echo "Stopped $(SSH_SCP_PASS_CONTAINER)" || echo "$(SSH_SCP_PASS_CONTAINER) not running"
connect-scp-pass:
@ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $(SSH_SCP_PASS_PORT) $(SSH_USER)@localhost
logs-scp-pass:
@docker logs -f $(SSH_SCP_PASS_CONTAINER)
shell-scp-pass:
@docker exec -it $(SSH_SCP_PASS_CONTAINER) /bin/sh
# ─── SCP-only key container ───────────────────────────────────────────────────
start-scp-key: $(SCP_IMAGE_STAMP) $(SSH_KEY)
@if [ "$$(docker ps -aq -f name=^/$(SSH_SCP_KEY_CONTAINER)$$)" ]; then \
echo "Starting existing container $(SSH_SCP_KEY_CONTAINER)..."; \
docker start $(SSH_SCP_KEY_CONTAINER) >/dev/null; \
else \
echo "Creating container $(SSH_SCP_KEY_CONTAINER) on port $(SSH_SCP_KEY_PORT)..."; \
docker run -d \
--name $(SSH_SCP_KEY_CONTAINER) \
-p $(SSH_SCP_KEY_PORT):2222 \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-e USER_NAME=$(SSH_USER) \
-e PASSWORD_ACCESS=false \
-e SUDO_ACCESS=true \
-e PUBLIC_KEY="$$(cat $(SSH_PUB_KEY))" \
$(SCP_IMAGE) >/dev/null; \
fi
@echo ""
@echo " SCP-only (key) server running at:"
@echo " Host: localhost"
@echo " Port: $(SSH_SCP_KEY_PORT)"
@echo " User: $(SSH_USER)"
@echo " Key: $(SSH_KEY)"
@echo " SFTP: disabled (Subsystem stripped)"
@echo ""
@echo " SCP (legacy proto): scp -O -i $(SSH_KEY) -P $(SSH_SCP_KEY_PORT) FILE $(SSH_USER)@localhost:"
stop-scp-key:
@docker stop $(SSH_SCP_KEY_CONTAINER) >/dev/null 2>&1 && echo "Stopped $(SSH_SCP_KEY_CONTAINER)" || echo "$(SSH_SCP_KEY_CONTAINER) not running"
connect-scp-key: $(SSH_KEY)
@ssh -i $(SSH_KEY) -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $(SSH_SCP_KEY_PORT) $(SSH_USER)@localhost
logs-scp-key:
@docker logs -f $(SSH_SCP_KEY_CONTAINER)
shell-scp-key:
@docker exec -it $(SSH_SCP_KEY_CONTAINER) /bin/sh
# ─── Combined status / cleanup ────────────────────────────────────────────────
ssh-status:
@docker ps -a \
-f name=^/$(SSH_PASS_CONTAINER)$$ \
-f name=^/$(SSH_KEY_CONTAINER)$$ \
-f name=^/$(SSH_SCP_PASS_CONTAINER)$$ \
-f name=^/$(SSH_SCP_KEY_CONTAINER)$$
ssh-clean:
@docker rm -f $(SSH_PASS_CONTAINER) >/dev/null 2>&1 && echo "Removed $(SSH_PASS_CONTAINER)" || echo "$(SSH_PASS_CONTAINER) not present"
@docker rm -f $(SSH_KEY_CONTAINER) >/dev/null 2>&1 && echo "Removed $(SSH_KEY_CONTAINER)" || echo "$(SSH_KEY_CONTAINER) not present"
@docker rm -f $(SSH_SCP_PASS_CONTAINER) >/dev/null 2>&1 && echo "Removed $(SSH_SCP_PASS_CONTAINER)" || echo "$(SSH_SCP_PASS_CONTAINER) not present"
@docker rm -f $(SSH_SCP_KEY_CONTAINER) >/dev/null 2>&1 && echo "Removed $(SSH_SCP_KEY_CONTAINER)" || echo "$(SSH_SCP_KEY_CONTAINER) not present"
scp-clean-image:
@docker rmi $(SCP_IMAGE) 2>/dev/null && echo "Removed $(SCP_IMAGE)" || echo "$(SCP_IMAGE) not present"
@rm -f $(SCP_IMAGE_STAMP)
ssh-clean-keys:
@rm -rf $(SSH_KEY_DIR) && echo "Removed $(SSH_KEY_DIR)"
# ─── E2E tests (containerised) ────────────────────────────────────────────────
# Full UI E2E suite under WebKitWebDriver + xvfb inside Docker. The runner
# image bakes in tauri-driver, the webkit2gtk driver, and a build toolchain
# so the same setup works on dev machines (incl. Arch) and CI.
# CI sets E2E_CI=1 to layer in docker-compose.ci.yml, which redirects the
# build-cache volumes (cargo/target/node-modules) to host bind mounts under
# $E2E_CACHE_DIR so they can be persisted via actions/cache. Locally E2E_CI is
# unset → plain named volumes, unchanged behaviour.
E2E_COMPOSE := docker compose -f tests/e2e/docker-compose.yml $(if $(filter 1,$(E2E_CI)),-f tests/e2e/docker-compose.ci.yml) -p anyscp-e2e
# Stamp file — make rebuilds the image whenever any of its source inputs
# change (Dockerfile, entrypoint, or harness package.json).
E2E_IMAGE_STAMP := tests/e2e/.image-stamp
E2E_IMAGE_SRC := tests/e2e/Dockerfile tests/e2e/entrypoint.sh tests/e2e/package.json
# (Re)build the runner image whenever any source input is newer than the stamp.
$(E2E_IMAGE_STAMP): $(E2E_IMAGE_SRC)
@echo " building anyscp-e2e-runner:latest (sources changed)"
@cd tests/e2e && docker build -t anyscp-e2e-runner:latest .
@touch $@
# Explicit rebuild target (alias).
e2e-build: $(E2E_IMAGE_STAMP)
# Run the full suite. Brings up the SSH targets + runner, runs WDIO, then
# tears containers down — but KEEPS volumes (rust-target, cargo-cache,
# node-modules) so the next run does an incremental compile (~5s instead
# of ~80s). Use `make e2e-clean` to wipe volumes when you want a fresh start.
e2e: $(E2E_IMAGE_STAMP)
@$(E2E_COMPOSE) up --abort-on-container-exit --exit-code-from e2e e2e; \
ec=$$?; \
$(E2E_COMPOSE) down --remove-orphans >/dev/null 2>&1; \
exit $$ec
# Drop into an interactive shell in the runner with the SSH targets up
# (useful when debugging a failing spec).
e2e-shell:
@$(E2E_COMPOSE) up -d sshd-pass sshd-key keygen
@$(E2E_COMPOSE) run --rm --entrypoint /bin/bash e2e
# Tail logs from the runner.
e2e-logs:
@$(E2E_COMPOSE) logs -f e2e
# Tear down everything and remove cached volumes (forces a clean next run).
e2e-clean:
@$(E2E_COMPOSE) down -v --remove-orphans
@docker rmi anyscp-e2e-runner:latest 2>/dev/null || true
@rm -f $(E2E_IMAGE_STAMP)
e2e-clean-artifacts:
@rm -rf ./tests/e2e/{screenshots,videos,junit,report.md,.test-records.ndjson,.image-stamp}
# ─── Marketing screenshots (containerised) ────────────────────────────────────
# Regenerate the README screenshots + demo gif from the real app — no manual
# capture. Reuses the e2e runner image (it bakes ImageMagick + ffmpeg + a font),
# so the whole pipeline is deterministic and needs no host tooling:
# 1. drive the app to each view, save raw WebKit captures
# 2. frame them (titlebar + rounded corners + shadow + wallpaper)
# 3. convert the recorded tour to screens/anyscp.gif
# Note: screens/header.png is a hand-made marketing banner and is NOT regenerated.
SCREENSHOT_SPEC := ./screenshot-tools/capture.screens.ts
SCREENSHOT_BUILD := /workspace/tests/e2e/screenshot-tools/build-assets.sh
screenshots: $(E2E_IMAGE_STAMP)
@echo " [1/2] capturing raw screenshots + tour video"
@$(E2E_COMPOSE) run --rm -e WDIO_SPEC=$(SCREENSHOT_SPEC) e2e; \
ec=$$?; \
if [ $$ec -ne 0 ]; then $(E2E_COMPOSE) down --remove-orphans >/dev/null 2>&1; exit $$ec; fi
@echo " [2/2] framing screenshots + building gif → screens/"
@$(E2E_COMPOSE) run --rm --no-deps --entrypoint bash e2e $(SCREENSHOT_BUILD); \
ec=$$?; \
$(E2E_COMPOSE) down --remove-orphans >/dev/null 2>&1; \
exit $$ec