Skip to content

Commit ba6df8e

Browse files
authored
Merge pull request #11 from appwrite/aggressive-improvements
Add: aggressive cleanup
2 parents 75d9505 + b913791 commit ba6df8e

File tree

9 files changed

+347
-48
lines changed

9 files changed

+347
-48
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ LICENSE
1616
test-screenshots/
1717
comparison/
1818
.lighthouse/
19+
lighthouse/
1920

2021
# Build output
2122
dist/

.github/workflows/test.yml

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ jobs:
99
tests:
1010
name: "Unit and E2E Tests"
1111
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
pull-requests: write
1215
steps:
1316
- name: Checkout code
1417
uses: actions/checkout@v4
@@ -28,10 +31,16 @@ jobs:
2831
run: docker compose up -d
2932

3033
- name: Wait for container to be ready
34+
id: container-startup
3135
run: |
36+
START_TIME=$(date +%s%3N)
3237
for i in {1..30}; do
3338
if curl -f http://localhost:3000/v1/health > /dev/null 2>&1; then
34-
echo "Container is ready!"
39+
END_TIME=$(date +%s%3N)
40+
STARTUP_TIME_MS=$((END_TIME - START_TIME))
41+
STARTUP_TIME=$(echo "scale=2; $STARTUP_TIME_MS / 1000" | bc)
42+
echo "startup_time=$STARTUP_TIME" >> $GITHUB_OUTPUT
43+
echo "Container is ready in ${STARTUP_TIME}s!"
3544
exit 0
3645
fi
3746
echo "Waiting for container... ($i/30)"
@@ -42,6 +51,57 @@ jobs:
4251
docker compose logs
4352
exit 1
4453
54+
- name: Collect Docker stats
55+
if: github.event_name == 'pull_request'
56+
continue-on-error: true
57+
id: docker-stats
58+
run: |
59+
# Get image size
60+
IMAGE_SIZE=$(docker images appwrite/browser:local --format "{{.Size}}")
61+
62+
# Get container stats
63+
CONTAINER_ID=$(docker compose ps -q appwrite-browser)
64+
MEMORY_USAGE=$(docker stats $CONTAINER_ID --no-stream --format "{{.MemUsage}}" | cut -d'/' -f1 | xargs)
65+
66+
# Quick screenshot benchmark (3 runs, average)
67+
TOTAL=0
68+
for i in {1..3}; do
69+
START=$(date +%s%3N)
70+
curl -s -X POST http://localhost:3000/v1/screenshots \
71+
-H "Content-Type: application/json" \
72+
-d '{"url":"https://appwrite.io"}' \
73+
-o /dev/null
74+
END=$(date +%s%3N)
75+
DURATION=$((END - START))
76+
TOTAL=$((TOTAL + DURATION))
77+
done
78+
SCREENSHOT_AVG_MS=$((TOTAL / 3))
79+
SCREENSHOT_AVG=$(echo "scale=2; $SCREENSHOT_AVG_MS / 1000" | bc)
80+
81+
# Store in GitHub output
82+
echo "image_size=$IMAGE_SIZE" >> $GITHUB_OUTPUT
83+
echo "memory_usage=$MEMORY_USAGE" >> $GITHUB_OUTPUT
84+
echo "screenshot_time=$SCREENSHOT_AVG" >> $GITHUB_OUTPUT
85+
86+
- name: Comment PR with stats
87+
if: github.event_name == 'pull_request' && steps.docker-stats.outcome == 'success'
88+
continue-on-error: true
89+
uses: marocchino/sticky-pull-request-comment@v2
90+
with:
91+
header: docker-image-stats
92+
skip_unchanged: true
93+
message: |
94+
## Docker Image Stats
95+
96+
| Metric | Value |
97+
|--------|-------|
98+
| Image Size | ${{ steps.docker-stats.outputs.image_size }} |
99+
| Memory Usage | ${{ steps.docker-stats.outputs.memory_usage }} |
100+
| Cold Start Time | ${{ steps.container-startup.outputs.startup_time }}s |
101+
| Screenshot Time | ${{ steps.docker-stats.outputs.screenshot_time }}s |
102+
103+
<sub>Screenshot benchmark: Average of 3 runs on https://appwrite.io</sub>
104+
45105
- name: Run e2e tests
46106
run: bun test:e2e
47107

Dockerfile

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,47 @@
1-
FROM oven/bun:1.3.2-alpine AS base
1+
# debian so we can re-use!
2+
FROM oven/bun:1.3.2-debian AS base
23

34
WORKDIR /app
45

56
COPY package.json bun.lock ./
7+
COPY src/utils/clean-modules.ts ./src/utils/clean-modules.ts
68

79
RUN bun install --frozen-lockfile --production && \
10+
bun run ./src/utils/clean-modules.ts && \
811
rm -rf ~/.bun/install/cache /tmp/*
912

10-
FROM oven/bun:1.3.2-alpine AS final
13+
# well-known OSS docker image
14+
FROM chromedp/headless-shell:143.0.7445.3 AS final
1115

12-
RUN apk upgrade --no-cache --available && \
13-
apk add --no-cache \
14-
chromium \
15-
ttf-freefont \
16-
font-noto-emoji \
17-
tini && \
18-
apk add --no-cache font-wqy-zenhei --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community && \
19-
# remove unnecessary chromium files to save space
20-
rm -rf /usr/lib/chromium/chrome_200_percent.pak \
21-
/usr/lib/chromium/chrome_100_percent.pak \
22-
/usr/lib/chromium/xdg-mime \
23-
/usr/lib/chromium/xdg-settings \
24-
/usr/lib/chromium/chrome-sandbox
16+
# install fonts only
17+
RUN apt-get update && \
18+
apt-get install -y --no-install-recommends \
19+
tini \
20+
ca-certificates \
21+
fonts-liberation \
22+
fonts-noto-color-emoji \
23+
fonts-wqy-zenhei && \
24+
apt-get clean && \
25+
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/cache/apt/archives/*
2526

26-
RUN addgroup -S chrome && adduser -S -G chrome chrome
27+
# copy bun from debian base above!
28+
COPY --from=base /usr/local/bin/bun /usr/local/bin/bun
29+
30+
# Add chrome user
31+
RUN groupadd -r chrome && useradd -r -g chrome chrome
2732

2833
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 \
29-
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium-browser \
34+
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/headless-shell/headless-shell \
3035
NODE_ENV=production
3136

3237
WORKDIR /app
3338

34-
COPY package.json ./
35-
COPY --from=base /app/node_modules ./node_modules
36-
COPY src/ ./src/
39+
COPY --chown=chrome:chrome src/ ./src/
40+
COPY --chown=chrome:chrome package.json ./
41+
COPY --chown=chrome:chrome --from=base /app/node_modules ./node_modules
3742

38-
RUN chown -R chrome:chrome /app
43+
# for e2e tests and `reports` endpoint!
44+
RUN install -d -o chrome -g chrome lighthouse
3945

4046
USER chrome
4147

bun.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"lockfileVersion": 1,
3+
"configVersion": 0,
34
"workspaces": {
45
"": {
56
"name": "appwrite-browser",

src/schemas/screenshot.schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export const screenshotSchema = z.object({
44
url: z.string().url(),
55
theme: z.enum(["light", "dark"]).default("light"),
66
headers: z.record(z.string(), z.any()).optional(),
7-
sleep: z.number().min(0).max(60000).default(3000),
7+
sleep: z.number().min(0).max(60000).default(0),
88
// Viewport options
99
viewport: z
1010
.object({

src/server.ts

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,17 @@ import {
55
handleScreenshotsRequest,
66
handleTestRequest,
77
} from "./routes";
8+
import { Router } from "./utils/router";
9+
10+
const router = new Router();
11+
router.add("POST", "/v1/screenshots", handleScreenshotsRequest);
12+
router.add("POST", "/v1/reports", handleReportsRequest);
13+
router.add("GET", "/v1/health", handleHealthRequest);
14+
router.add("GET", "/v1/test", handleTestRequest);
815

916
const server = Bun.serve({
1017
port,
11-
async fetch(req) {
12-
const url = new URL(req.url);
13-
const path = url.pathname;
14-
15-
// Route matching
16-
if (path === "/v1/screenshots" && req.method === "POST") {
17-
return await handleScreenshotsRequest(req);
18-
}
19-
20-
if (path === "/v1/reports" && req.method === "POST") {
21-
return await handleReportsRequest(req);
22-
}
23-
24-
if (path === "/v1/health" && req.method === "GET") {
25-
return await handleHealthRequest(req);
26-
}
27-
28-
if (path === "/v1/test" && req.method === "GET") {
29-
return await handleTestRequest(req);
30-
}
31-
32-
// 404 Not Found
33-
return new Response("Not Found", { status: 404 });
34-
},
18+
fetch: (request) => router.handle(request),
3519
});
3620

3721
console.log(`Server running on http://0.0.0.0:${server.port}`);

0 commit comments

Comments
 (0)