Skip to content

Commit 13d9c7a

Browse files
authored
Merge pull request #3036 from metacpan/haarg/esbuild
build assets with esbuild, with docker compose for running with auto-reload
2 parents ab6c57f + 89abd3c commit 13d9c7a

File tree

21 files changed

+767
-340
lines changed

21 files changed

+767
-340
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
steps:
1717
- uses: actions/checkout@v3
1818
- name: docker build
19-
run: docker build . -t metacpan/metacpan-web:latest
19+
run: docker build . --target test -t metacpan/metacpan-web:latest
2020
- name: run Perl tests
2121
run: >
2222
docker run -i metacpan/metacpan-web
@@ -57,6 +57,8 @@ jobs:
5757
--resolver ${{ matrix.resolver }}
5858
--show-build-log-on-failure
5959
--local-lib-contained=local
60+
- name: Build assets
61+
run: npm run build
6062
- name: Run tests without coverage
6163
if: matrix.resolver != 'snapshot'
6264
run: carton exec prove -lr --jobs 2 t

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
!/root/assets/.gitignore
12
*.bak
23
*.sw*
34
.DS_Store
@@ -17,6 +18,7 @@
1718
/node_modules/
1819
/perltidy.LOG
1920
/pm_to_blib
21+
/root/assets
2022
/root/static/sitemaps
2123
/tidyall.ERR
2224
/var

Dockerfile

Lines changed: 99 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,95 @@
1-
FROM metacpan/metacpan-base:latest
1+
################### Asset Builder
22

3-
ARG CPM_ARGS=--with-test
3+
FROM node:22 AS build-assets
4+
ENV NO_UPDATE_NOTIFIER=1
5+
6+
WORKDIR /build/
7+
8+
COPY package.json package-lock.json .
9+
RUN \
10+
--mount=type=cache,target=/root/.npm,sharing=private \
11+
<<EOT /bin/bash -euo pipefail
12+
npm install --verbose
13+
npm audit fix
14+
EOT
15+
16+
# not supported yet
17+
#COPY --parents build-assets.mjs root/static .
18+
19+
COPY build-assets.mjs .
20+
COPY root/static root/static
21+
RUN <<EOT /bin/bash -euo pipefail
22+
npm run build:min
23+
EOT
24+
25+
################### Web Server
26+
FROM metacpan/metacpan-base:latest AS server
27+
28+
RUN \
29+
--mount=type=cache,target=/var/cache/apt,sharing=private \
30+
--mount=type=cache,target=/var/lib/apt/lists,sharing=private \
31+
<<EOT /bin/bash -euo pipefail
32+
apt update
33+
apt install -y -f libcmark-dev
34+
EOT
35+
36+
WORKDIR /metacpan-web/
37+
38+
COPY cpanfile cpanfile.snapshot .
39+
RUN \
40+
--mount=type=cache,target=/root/.perl-cpm,sharing=private \
41+
<<EOT /bin/bash -euo pipefail
42+
cpm install
43+
EOT
44+
45+
RUN mkdir var && chown metacpan:users var
46+
47+
ENV PERL5LIB="/metacpan-web/local/lib/perl5"
48+
ENV PATH="/metacpan-web/local/bin:${PATH}"
49+
50+
COPY *.md app.psgi *.conf .
51+
COPY bin bin
52+
COPY lib lib
53+
COPY root root
54+
COPY --from=build-assets /build/root/assets root/assets
55+
56+
STOPSIGNAL SIGKILL
57+
58+
CMD [ \
59+
"/uwsgi.sh", \
60+
"--http-socket", ":80" \
61+
]
62+
63+
EXPOSE 80
64+
65+
################### Development Server
66+
FROM server AS develop
67+
68+
ENV COLUMNS="${COLUMNS:-120}"
69+
ENV PLACK_ENV=development
70+
71+
USER root
72+
73+
RUN \
74+
--mount=type=cache,target=/root/.perl-cpm \
75+
<<EOT /bin/bash -euo pipefail
76+
cpm install --with-develop
77+
EOT
78+
79+
USER metacpan
80+
81+
CMD [ \
82+
"/uwsgi.sh", \
83+
"--http-socket", ":80" \
84+
]
85+
86+
################### Test Runner
87+
FROM develop AS test
488

589
ENV NO_UPDATE_NOTIFIER=1
90+
ENV PLACK_ENV=
91+
92+
USER root
693

794
RUN \
895
--mount=type=cache,target=/var/cache/apt,sharing=private \
@@ -13,40 +100,29 @@ RUN \
13100
apt update
14101
apt install -y -f --no-install-recommends nodejs
15102
npm install -g npm
16-
apt install -y -f libcmark-dev
17103
EOT
18104

19-
WORKDIR /metacpan-web/
20-
RUN chown metacpan:users /metacpan-web/
21-
22-
COPY --chown=metacpan:users package.json package-lock.json .
105+
COPY package.json package-lock.json .
23106
RUN \
24107
--mount=type=cache,target=/root/.npm,sharing=private \
25108
<<EOT /bin/bash -euo pipefail
26-
npm install --verbose
109+
npm install --verbose --include=dev
27110
npm audit fix
28111
EOT
29112

30-
COPY --chown=metacpan:users cpanfile cpanfile.snapshot .
31113
RUN \
32-
--mount=type=cache,target=/root/.perl-cpm,sharing=private \
114+
--mount=type=cache,target=/root/.perl-cpm \
33115
<<EOT /bin/bash -euo pipefail
34-
cpm install -g ${CPM_ARGS}
116+
cpm install --with-test
35117
EOT
36118

37-
COPY --chown=metacpan:users . .
38-
RUN mkdir var && chown metacpan:users var
119+
COPY .perlcriticrc .perltidyrc perlimports.toml tidyall.ini ./
120+
COPY t t
39121

40122
USER metacpan
123+
CMD [ "prove", "-lr", "t" ]
41124

42-
CMD [ \
43-
"/usr/bin/uwsgi", \
44-
"--plugins", "psgi", \
45-
"--uwsgi-socket", ":3031", \
46-
"--http-socket", ":80", \
47-
"--http-socket-modifier1", "5", \
48-
"--ini", "/metacpan-web/servers/uwsgi.ini", \
49-
"--psgi", "app.psgi" \
50-
]
125+
################### Production Server
126+
FROM server AS production
51127

52-
EXPOSE 80 3031
128+
USER metacpan

app.psgi

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@ BEGIN {
4848
use lib "$root_dir/lib";
4949
use MetaCPAN::Web;
5050

51-
# do not use the read only mount point when running from a docker container
52-
my $tempdir = is_linux_container() ? '/var/tmp' : "$root_dir/var/tmp";
53-
5451
STDERR->autoflush;
5552

5653
# explicitly call ->to_app on every Plack::App::* for performance
@@ -137,7 +134,6 @@ builder {
137134
enable '+MetaCPAN::Middleware::Static' => (
138135
root => $root_dir,
139136
dev_mode => $dev_mode,
140-
temp_dir => $tempdir,
141137
config => $config,
142138
);
143139

@@ -158,6 +154,3 @@ builder {
158154
};
159155
};
160156

161-
sub is_linux_container {
162-
return -e '/proc/1/cgroup';
163-
}

build-assets.mjs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env node
2+
"use strict";
3+
import * as esbuild from 'esbuild'
4+
import { lessLoader } from 'esbuild-plugin-less';
5+
import fs from 'fs';
6+
import parseArgs from 'minimist';
7+
8+
const config = {
9+
entryPoints: [
10+
'root/static/js/main.js',
11+
'root/static/less/style.less',
12+
],
13+
assetNames: '[name]-[hash]',
14+
entryNames: '[name]-[hash]',
15+
outdir: 'root/assets',
16+
bundle: true,
17+
sourcemap: true,
18+
metafile: true,
19+
inject: ['root/static/js/inject.js'],
20+
loader: {
21+
'.eot': 'file',
22+
'.svg': 'file',
23+
'.ttf': 'file',
24+
'.woff': 'file',
25+
'.woff2': 'file',
26+
},
27+
plugins: [
28+
lessLoader(),
29+
new class {
30+
name = 'metacpan-build';
31+
32+
setup(build) {
33+
build.onResolve(
34+
{ filter: /^(shCore|xregexp)$/ },
35+
args => ({ external: true }),
36+
);
37+
build.onResolve(
38+
{ filter: /^\// },
39+
args => ({ external: true }),
40+
);
41+
build.onEnd(result => {
42+
const metafile = result.metafile;
43+
if (metafile && metafile.outputs) {
44+
const files = Object.keys(metafile.outputs).sort()
45+
.map(file => file.replace(/^root\/assets\//, ''));
46+
fs.writeFile(
47+
'root/assets/assets.json',
48+
JSON.stringify(files),
49+
'utf8',
50+
(e) => {
51+
if (e) {
52+
console.log(e);
53+
}
54+
}
55+
);
56+
console.log('assets built');
57+
}
58+
else {
59+
console.log('asset build failure');
60+
}
61+
});
62+
}
63+
},
64+
],
65+
};
66+
67+
const args = parseArgs(process.argv, {
68+
boolean: [
69+
'watch',
70+
'minify',
71+
],
72+
});
73+
if (args.minify) {
74+
config.minify = true;
75+
}
76+
const ctx = await esbuild.context(config);
77+
if (args.watch) {
78+
await ctx.watch();
79+
const sig = await new Promise(resolve => {
80+
[
81+
'SIGTERM',
82+
'SIGQUIT',
83+
'SIGINT',
84+
].map(sig => process.on(sig, resolve));
85+
});
86+
process.stderr.write(`Caught signal: ${sig}\n`);
87+
ctx.dispose();
88+
}
89+
else {
90+
await ctx.rebuild();
91+
ctx.dispose();
92+
}

docker-compose.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
services:
2+
asset-build:
3+
build:
4+
context: .
5+
target: build-assets
6+
volumes:
7+
- './root:/build/root/'
8+
- 'assets:/build/root/assets/'
9+
command: [ './build-assets.mjs', '--watch' ]
10+
server:
11+
build:
12+
context: .
13+
target: develop
14+
volumes:
15+
- './:/metacpan-web/'
16+
- 'assets:/metacpan-web/root/assets'
17+
- '/metacpan-web/local'
18+
ports:
19+
- "8000:80"
20+
volumes:
21+
assets:

0 commit comments

Comments
 (0)