Skip to content

Commit e2fdf9e

Browse files
committed
Merge remote-tracking branch 'origin/master' into ci/publish-docker-image
2 parents f5f4e76 + 3acff56 commit e2fdf9e

8 files changed

Lines changed: 2433 additions & 1565 deletions

File tree

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
!/yarn.lock
77
!/src
88
!/fonts
9+
!/smoketest.js

Dockerfile

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,58 @@
1-
FROM node:20.19.5-bookworm AS builder
1+
FROM us-docker.pkg.dev/sentryio/dhi/node:24-debian13-dev AS builder
22

3-
COPY package.json yarn.lock .
3+
WORKDIR /build
4+
5+
COPY package.json yarn.lock ./
46
RUN yarn install --frozen-lockfile
57

68
COPY tsconfig.json .
79
COPY src src
810
RUN yarn build
911

10-
FROM node:20.19.5-bookworm-slim
12+
# Drop devDependencies from node_modules for the runtime image
13+
RUN yarn install --frozen-lockfile --production
14+
15+
# canvas 3.x bundles its graphics libs (libcairo, libpango, etc.) but some of
16+
# those bundled libs still need system libs absent from the minimal runtime image.
17+
# Use ldd to auto-detect all transitive system dependencies of every native module
18+
# so this stays correct when canvas or any other native dependency is updated.
19+
RUN mkdir -p /canvas-sys-libs && \
20+
find /build/node_modules -name "*.node" | \
21+
xargs ldd 2>/dev/null | \
22+
awk '/=> \// { print $3 }' | \
23+
grep -v '^/build' | \
24+
sort -u | \
25+
while IFS= read -r lib; do \
26+
real=$(readlink -f "$lib"); \
27+
cp --parents "$real" /canvas-sys-libs/ 2>/dev/null || true; \
28+
usr="${lib/#\/lib\//\/usr\/lib\/}"; \
29+
[ "$usr" != "$real" ] && \
30+
ln -sf "$(basename "$real")" "/canvas-sys-libs$usr" 2>/dev/null || true; \
31+
done
32+
33+
# canvas bundles libfontconfig but needs /etc/fonts/fonts.conf to initialise.
34+
# Without it fontconfig silently fails and all text renders as box glyphs.
35+
RUN apt-get update -qq && \
36+
apt-get install -qq -y --no-install-recommends fontconfig && \
37+
rm -rf /var/lib/apt/lists/*
38+
39+
40+
FROM us-docker.pkg.dev/sentryio/dhi/node:24-debian13
1141

1242
ENV NODE_ENV=production
1343

14-
RUN apt-get update && apt-get install -y --no-install-recommends \
15-
build-essential \
16-
libcairo2-dev \
17-
libpango1.0-dev \
18-
libpangocairo-1.0-0 \
19-
libjpeg-dev \
20-
libgif-dev \
21-
librsvg2-dev \
22-
&& rm -rf /var/lib/apt/lists/*
23-
2444
WORKDIR /usr/src/app
2545

26-
COPY package.json yarn.lock ./
27-
RUN yarn install --frozen-lockfile
28-
46+
COPY package.json ./
2947
COPY fonts fonts
30-
COPY --from=builder lib lib
31-
32-
RUN node lib/index.js --help
48+
COPY --from=builder /build/node_modules node_modules
49+
COPY --from=builder /build/lib lib
50+
COPY --from=builder /canvas-sys-libs/usr/lib/ /usr/lib/
51+
COPY --from=builder /etc/fonts/ /etc/fonts/
52+
COPY --from=builder /usr/share/fonts/ /usr/share/fonts/
53+
COPY smoketest.js .
54+
55+
RUN ["node", "smoketest.js"]
3356

3457
EXPOSE 9090/tcp
3558
CMD ["node", "./lib/index.js", "server", "9090"]

devservices/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ services:
2525
labels:
2626
- orchestrator=devservices
2727
healthcheck:
28-
test: python3 -c "import urllib.request; urllib.request.urlopen(\"http://127.0.0.1:9090/api/chartcuterie/healthcheck/live\", timeout=5)"
28+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:9090/api/chartcuterie/healthcheck/live').then(r=>r.ok?0:1,()=>1).then(process.exit)"]
2929
interval: 5s
3030
timeout: 5s
3131
retries: 3

gocd/templates/jsonnetfile.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"subdir": "libs"
99
}
1010
},
11-
"version": "v2.13.0"
11+
"version": "v2.18.0"
1212
}
1313
],
1414
"legacyImports": true

gocd/templates/jsonnetfile.lock.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"subdir": "libs"
99
}
1010
},
11-
"version": "6ddc943ae87444b48e16995639dfe89f33a0f444",
12-
"sum": "NH9U5jQ8oCSPXLuBw27OqAaPLBUDqMGHvRLxfo84hNQ="
11+
"version": "3b7e3b151fb20d21d66c2f04d17a740ba0b09ac0",
12+
"sum": "cgfkKWTz+bBKHa8WVji1v4Ke5JM3bTeMnqPtYZuy9VM="
1313
}
1414
],
1515
"legacyImports": false

package.json

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,39 @@
2020
"README"
2121
],
2222
"dependencies": {
23-
"@sentry/node": "10.0.0",
24-
"@sentry/profiling-node": "10.0.0",
23+
"@sentry/node": "10.40.0",
24+
"@sentry/profiling-node": "10.40.0",
2525
"canvas": "^3.2.0",
2626
"dotenv": "^8.2.0",
2727
"echarts": "6.0.0",
28-
"express": "4.21.2",
28+
"express": "5.2.1",
2929
"joi": "17.6.0",
3030
"winston": "3.7.2",
3131
"yargs": "^16.2.0"
3232
},
3333
"devDependencies": {
34-
"@types/express": "^4.17.21",
34+
"@types/express": "^5.0.6",
3535
"@types/jest-image-snapshot": "4.3.1",
36-
"@types/node": "^20.10.6",
37-
"@types/supertest": "^2.0.10",
36+
"@types/node": "^24.0.0",
37+
"@types/supertest": "^7.2.0",
3838
"@types/yargs": "^17.0.32",
3939
"eslint": "^8.56.0",
40-
"eslint-config-sentry-app": "^1.129.0",
40+
"eslint-config-sentry-app": "^2.10.0",
4141
"jest": "^29.7.0",
4242
"jest-fetch-mock": "^3.0.3",
4343
"jest-image-snapshot": "5.1.0",
4444
"prettier": "2.6.2",
45-
"supertest": "6.2.3",
45+
"supertest": "7.2.2",
4646
"ts-jest": "28.0.5",
4747
"tsc-watch": "5.0.3",
4848
"typescript": "^5.3.3"
4949
},
50+
"resolutions": {
51+
"json5": "^2.2.3",
52+
"minimatch": "9.0.9"
53+
},
5054
"volta": {
51-
"node": "20.10.0",
55+
"node": "24.14.0",
5256
"yarn": "1.22.5"
5357
}
5458
}

smoketest.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
// Exercises the full rendering path: canvas init, font loading, echarts render, PNG output.
3+
// Runs as a build-time check in the Docker runtime stage — any missing .so or font files
4+
// will cause this to throw and fail the build.
5+
//
6+
// The expected hash pins the exact pixel output. If it changes, update it by running:
7+
// docker build -t chartcuterie:local . && \
8+
// docker run --rm chartcuterie:local node -e "
9+
// const crypto = require('crypto');
10+
// const {renderSync} = require('./lib/render');
11+
// const r = renderSync({key:'smoke',height:100,width:100,getOption:()=>({
12+
// xAxis:{type:'category'},yAxis:{type:'value'},series:[{type:'line',data:[1,2,3]}]
13+
// })},[]);
14+
// console.log(crypto.createHash('sha256').update(r.buffer).digest('hex'));
15+
// r.dispose();
16+
// "
17+
const crypto = require('crypto');
18+
const {renderSync} = require('./lib/render');
19+
20+
const result = renderSync({
21+
key: 'smoke',
22+
height: 100,
23+
width: 100,
24+
getOption: () => ({
25+
xAxis: {type: 'category'},
26+
yAxis: {type: 'value'},
27+
series: [{type: 'line', data: [1, 2, 3]}],
28+
}),
29+
}, []);
30+
31+
const {buffer} = result;
32+
result.dispose();
33+
34+
const EXPECTED_SHA256 = '2616947b26199985b53250044725868a530ee2e2328ed2380825a2cbd71c37f9';
35+
const actual = crypto.createHash('sha256').update(buffer).digest('hex');
36+
if (actual !== EXPECTED_SHA256) {
37+
throw new Error(`smoke test failed: output hash ${actual} !== expected ${EXPECTED_SHA256}`);
38+
}

0 commit comments

Comments
 (0)