-
Notifications
You must be signed in to change notification settings - Fork 321
Expand file tree
/
Copy pathDockerfile.template
More file actions
180 lines (170 loc) · 6.4 KB
/
Copy pathDockerfile.template
File metadata and controls
180 lines (170 loc) · 6.4 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
{{
def is_alpine:
env.variant | startswith("alpine")
;
def clean_apt:
# TODO once bookworm is EOL, remove this and just hard-code "apt-get dist-clean" instead
if env.variant | contains("bookworm") then
"rm -rf /var/lib/apt/lists/*"
else "apt-get dist-clean" end
-}}
FROM {{ .variants[env.variant].from }}
{{ if is_alpine then ( -}}
RUN apk add --no-cache \
# add "bash" for "[["
bash
{{ ) else "" end -}}
# grab gosu for easy step-down from root
# https://github.com/tianon/gosu/releases
ENV GOSU_VERSION=1.19
RUN set -eux; \
{{ if is_alpine then ( -}}
\
apk add --no-cache --virtual .gosu-deps \
ca-certificates \
dpkg \
gnupg \
; \
{{ ) else ( -}}
# save list of currently installed packages for later so we can clean up
savedAptMark="$(apt-mark showmanual)"; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates gnupg wget; \
{{ ) end -}}
\
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
\
# verify the signature
export GNUPGHOME="$(mktemp -d)"; \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
gpgconf --kill all; \
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
\
# clean up fetch dependencies
{{ if is_alpine then ( -}}
apk del --no-network .gosu-deps; \
{{ ) else ( -}}
apt-mark auto '.*' > /dev/null; \
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
{{ clean_apt }}; \
{{ ) end -}}
\
chmod +x /usr/local/bin/gosu; \
# verify that the binary works
gosu --version; \
gosu nobody true
ENV NODE_ENV=production
# Ghost-CLI is installed from source rather than "npm install -g ghost-cli" so that
# pnpm can resolve the dependency tree from the lockfile committed alongside the tag
# ("npm install -g" ignores lockfiles entirely, so the tree it produces varies by build date)
ENV GHOST_CLI_VERSION={{ .cli.version }}
ENV GHOST_CLI_SHA={{ .cli.sha }}
ENV GHOST_CLI_INSTALL=/usr/local/lib/ghost-cli
RUN set -eux; \
\
{{ if is_alpine then ( -}}
apk add --no-cache --virtual .ghost-cli-deps git; \
{{ ) else ( -}}
savedAptMark="$(apt-mark showmanual)"; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates git; \
{{ ) end -}}
\
corepack enable; \
\
# corepack's downloads, pnpm's metadata cache and pnpm's store all derive their location
# from these, so pointing them at /tmp keeps every throwaway byte in one place
export XDG_CACHE_HOME=/tmp/xdg-cache XDG_DATA_HOME=/tmp/xdg-data; \
\
mkdir -p "$GHOST_CLI_INSTALL"; \
cd "$GHOST_CLI_INSTALL"; \
git init --quiet .; \
git remote add origin https://github.com/TryGhost/Ghost-CLI.git; \
# fetching the commit by hash means git's own object verification pins the source
# (a moved tag or a re-rolled release tarball cannot change what we get)
git fetch --quiet --depth 1 origin "$GHOST_CLI_SHA"; \
git checkout --quiet FETCH_HEAD; \
[ "$(node -p 'require("./package.json").version')" = "$GHOST_CLI_VERSION" ]; \
\
# "--frozen-lockfile" is the point of all this: it installs pnpm-lock.yaml exactly, or fails
pnpm install --prod --frozen-lockfile; \
\
ln -s "$GHOST_CLI_INSTALL/bin/ghost" /usr/local/bin/ghost; \
\
# drop what "npm publish" would not have shipped (see "files" in Ghost-CLI's package.json)
rm -rf .git test .github; \
\
{{ if is_alpine then ( -}}
apk del --no-network .ghost-cli-deps; \
{{ ) else ( -}}
apt-mark auto '.*' > /dev/null; \
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
{{ clean_apt }}; \
{{ ) end -}}
\
rm -rf /tmp/xdg-cache /tmp/xdg-data; \
npm cache clean --force; \
\
ghost --version
ENV GHOST_INSTALL=/var/lib/ghost
ENV GHOST_CONTENT=/var/lib/ghost/content
ENV GHOST_VERSION={{ .version }}
RUN set -eux; \
mkdir -p "$GHOST_INSTALL"; \
chown node:node "$GHOST_INSTALL"; \
\
{{ if is_alpine then ( -}}
apk add --no-cache --virtual .build-deps-ghost g++ linux-headers make python3 py3-setuptools; \
{{ ) else ( -}}
savedAptMark="$(apt-mark showmanual)"; \
apt-get update; \
apt-get install -y --no-install-recommends g++ make python3; \
{{ ) end -}}
\
gosu node ghost install "$GHOST_VERSION" --db mysql --dbhost mysql --no-prompt --no-stack --no-setup --dir "$GHOST_INSTALL"{{ if env.version | endswith("-rc") then ( }} --channel next{{ ) else "" end }}; \
\
# Tell Ghost to listen on all ips and not prompt for additional configuration
cd "$GHOST_INSTALL"; \
gosu node ghost config --no-prompt --ip '::' --port 2368 --url 'http://localhost:2368'; \
gosu node ghost config paths.contentPath "$GHOST_CONTENT"; \
\
# make a config.json symlink for NODE_ENV=development (and sanity check that it's correct)
gosu node ln -s config.production.json "$GHOST_INSTALL/config.development.json"; \
readlink -f "$GHOST_INSTALL/config.development.json"; \
\
# need to save initial content for pre-seeding empty volumes
mv "$GHOST_CONTENT" "$GHOST_INSTALL/content.orig"; \
mkdir -p "$GHOST_CONTENT"; \
chown node:node "$GHOST_CONTENT"; \
chmod 1777 "$GHOST_CONTENT"; \
\
{{ if is_alpine then ( -}}
apk del --no-network .build-deps-ghost; \
{{ ) else ( -}}
apt-mark auto '.*' > /dev/null; \
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
{{ clean_apt }}; \
{{ ) end -}}
\
gosu node pnpm store prune; \
gosu node npm cache clean --force; \
npm cache clean --force; \
# none of these are needed to run Ghost: corepack re-downloads pnpm on demand and node-gyp
# only matters while compiling native modules, which is finished by this point
rm -rf /home/node/.cache/node/corepack /home/node/.cache/node-gyp /home/node/.cache/pnpm; \
\
# test that the optional dependencies are installed and loadable
cd current; \
gosu node node -e 'require("better-sqlite3"); if (!require("@tryghost/image-transform").canTransformFiles()) throw new Error("sharp not installed");'
WORKDIR $GHOST_INSTALL
VOLUME $GHOST_CONTENT
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 2368
CMD ["node", "current/index.js"]