forked from we-promise/sure
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile.preview
More file actions
247 lines (200 loc) · 8.73 KB
/
Dockerfile.preview
File metadata and controls
247 lines (200 loc) · 8.73 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
# syntax = docker/dockerfile:1
# Preview Dockerfile for Cloudflare Containers
# Includes PostgreSQL and Redis for self-contained development testing
ARG RUBY_VERSION=3.4.7
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim AS base
WORKDIR /rails
# Install base packages including PostgreSQL and Redis servers
RUN apt-get update -qq \
&& apt-get install --no-install-recommends -y \
curl libvips postgresql postgresql-client redis-server libyaml-0-2 procps sudo openssl strace \
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Set development environment
ARG BUILD_COMMIT_SHA
ENV RAILS_ENV="development" \
BUNDLE_PATH="/usr/local/bundle" \
BUILD_COMMIT_SHA=${BUILD_COMMIT_SHA}
# Build stage
FROM base AS build
RUN apt-get update -qq \
&& apt-get install --no-install-recommends -y build-essential libpq-dev git pkg-config libyaml-dev \
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives
COPY .ruby-version Gemfile Gemfile.lock ./
RUN bundle install \
&& rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git \
&& bundle exec bootsnap precompile --gemfile -j 0
COPY . .
RUN bundle exec bootsnap precompile -j 0 app/ lib/
# Precompile assets
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
# Final stage
FROM base
# Create rails user and configure PostgreSQL/Redis permissions
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
echo "rails ALL=(ALL) NOPASSWD: /usr/bin/pg_ctlcluster, /usr/bin/redis-server" > /etc/sudoers.d/rails && \
chmod 0440 /etc/sudoers.d/rails
# Configure PostgreSQL to allow local connections
RUN PG_HBA=$(find /etc/postgresql -name pg_hba.conf 2>/dev/null | head -1) && \
if [ -n "$PG_HBA" ]; then \
echo "local all all trust" > "$PG_HBA" && \
echo "host all all 127.0.0.1/32 trust" >> "$PG_HBA" && \
echo "host all all ::1/128 trust" >> "$PG_HBA"; \
fi
# Create database directory with correct permissions
RUN mkdir -p /var/run/postgresql && \
chown -R postgres:postgres /var/run/postgresql && \
chmod 2775 /var/run/postgresql
# Copy built artifacts
COPY --chown=rails:rails --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --chown=rails:rails --from=build /rails /rails
# Create preview entrypoint script inline
RUN cat > /rails/bin/preview-entrypoint << 'ENTRYPOINT_EOF'
#!/bin/bash
set -e
cd /rails
emit_status() {
if [ -n "$PREVIEW_ORIGIN" ]; then
local stage="$1"
local detail="$2"
local payload
payload=$(STAGE="$stage" DETAIL="$detail" ruby -rjson -e 'print JSON.generate({stage: ENV.fetch("STAGE"), detail: ENV.fetch("DETAIL", "")})' 2>/dev/null) || return 0
curl -fsS -X POST "$PREVIEW_ORIGIN/_container_event" \
-H 'content-type: application/json' \
--data "$payload" >/dev/null || true
fi
}
trap 'emit_status failed "preview-entrypoint failed on line ${LINENO}"' ERR
emit_status boot "preview-entrypoint started"
REDIS_READY=0
POSTGRES_READY=0
# Start Redis
echo "Starting Redis..."
emit_status redis-start "starting redis"
sudo redis-server --daemonize yes --bind 127.0.0.1
# Wait for Redis to be ready
echo "Waiting for Redis to be ready..."
for i in {1..10}; do
if redis-cli ping > /dev/null 2>&1; then
echo "Redis is ready"
emit_status redis-ready "redis is ready"
REDIS_READY=1
break
fi
sleep 1
done
if [ "$REDIS_READY" -ne 1 ]; then
echo "Redis did not become ready in time"
exit 1
fi
# Start PostgreSQL
echo "Starting PostgreSQL..."
emit_status postgres-start "starting postgres"
PG_VERSION=$(ls /etc/postgresql/ | sort -V | tail -1)
if [ -z "$PG_VERSION" ]; then
echo "Could not determine installed PostgreSQL version"
exit 1
fi
if sudo pg_ctlcluster --skip-systemctl-redirect "$PG_VERSION" main status > /dev/null 2>&1; then
emit_status postgres-already-running "postgres cluster already running"
else
sudo pg_ctlcluster --skip-systemctl-redirect "$PG_VERSION" main start
fi
# Wait for PostgreSQL to be ready
echo "Waiting for PostgreSQL to be ready..."
for i in {1..30}; do
if pg_isready -h localhost -U postgres > /dev/null 2>&1; then
echo "PostgreSQL is ready"
emit_status postgres-ready "postgres is ready"
POSTGRES_READY=1
break
fi
sleep 1
done
if [ "$POSTGRES_READY" -ne 1 ]; then
echo "PostgreSQL did not become ready in time"
exit 1
fi
# Create database user and database if they don't exist
echo "Setting up database..."
emit_status db-setup "setting up database"
psql -h localhost -U postgres -tc "SELECT 1 FROM pg_roles WHERE rolname='rails'" | grep -q 1 || \
psql -h localhost -U postgres -c "CREATE USER rails WITH SUPERUSER PASSWORD 'rails';"
psql -h localhost -U postgres -tc "SELECT 1 FROM pg_database WHERE datname='sure_development'" | grep -q 1 || \
psql -h localhost -U postgres -c "CREATE DATABASE sure_development OWNER rails;"
# Set DATABASE_URL if not already set
export DATABASE_URL="${DATABASE_URL:-postgres://rails:rails@localhost:5432/sure_development}"
# Set REDIS_URL if not already set
export REDIS_URL="${REDIS_URL:-redis://localhost:6379/0}"
# Generate SECRET_KEY_BASE if not set
export SECRET_KEY_BASE="${SECRET_KEY_BASE:-$(openssl rand -hex 64)}"
# Run database migrations
echo "Running database migrations..."
emit_status db-prepare "running rails db:prepare"
/rails/bin/rails db:prepare
emit_status db-prepare-done "rails db:prepare finished"
# Defer all demo-data creation until after Rails is up so preview can boot first
echo "Checking demo dataset..."
emit_status demo-data-check "checking for default demo user"
DEMO_EMAIL="${DEMO_USER_EMAIL:-user@example.com}"
DEMO_EMAIL_SQL=${DEMO_EMAIL//\'/\'\'}
DEMO_SEED="${DEMO_DATA_SEED:-880}"
DEMO_HAS_USER=0
DEMO_HAS_DATA=0
if psql "$DATABASE_URL" -tAc "SELECT 1 FROM users WHERE email = '${DEMO_EMAIL_SQL}' LIMIT 1" | grep -q 1; then
DEMO_HAS_USER=1
emit_status demo-data-user-present "default demo user already exists"
fi
if psql "$DATABASE_URL" -tAc "SELECT 1 FROM accounts a JOIN users u ON u.family_id = a.family_id WHERE u.email = '${DEMO_EMAIL_SQL}' LIMIT 1" | grep -q 1; then
DEMO_HAS_DATA=1
emit_status demo-data-skip "demo financial data already exists"
else
emit_status demo-data-deferred "deferring demo data creation until after rails boot"
fi
# Execute the main command with an internal readiness probe
echo "Starting Rails server..."
emit_status rails-start "starting rails server"
"$@" > /tmp/rails.log 2>&1 &
RAILS_PID=$!
for i in {1..180}; do
if curl -fsS http://127.0.0.1:3000/up > /dev/null 2>&1; then
emit_status rails-up-ready "rails responded on localhost:3000/up"
if [ "$DEMO_HAS_USER" -ne 1 ] || [ "$DEMO_HAS_DATA" -ne 1 ]; then
emit_status demo-data-load "creating/backfilling demo dataset in background (seed=${DEMO_SEED})"
(
(
DEMO_USER_EMAIL="$DEMO_EMAIL" DEMO_DATA_SEED="$DEMO_SEED" /rails/bin/rails runner '
email = ENV.fetch("DEMO_USER_EMAIL")
generator = Demo::Generator.new(seed: ENV.fetch("DEMO_DATA_SEED"))
user = User.find_by(email: email)
unless user
generator.generate_empty_data!(skip_clear: true)
user = User.find_by!(email: email)
end
has_accounts = user.family.accounts.exists?
generator.generate_new_user_data_for!(user.family, email: user.email) unless has_accounts
'
) > /tmp/demo-data.log 2>&1 && \
emit_status demo-data-ready "default demo dataset loaded in background" || \
emit_status demo-data-failed "background demo dataset load failed"
) &
fi
break
fi
sleep 1
done
if ! curl -fsS http://127.0.0.1:3000/up > /dev/null 2>&1; then
emit_status rails-up-timeout "rails did not answer localhost:3000/up in time"
emit_status rails-process-status "$(ps -o pid=,ppid=,stat=,comm=,args= -p "$RAILS_PID" 2>/dev/null | tr -s ' ' | sed 's/^ //')"
emit_status rails-process-wchan "$(cat /proc/$RAILS_PID/wchan 2>/dev/null | tr '\n' ' ' | cut -c 1-200)"
emit_status rails-process-children "$(ps -o pid=,ppid=,stat=,comm=,args= --ppid "$RAILS_PID" 2>/dev/null | tail -n +2 | tr '\n' '|' | cut -c 1-600)"
emit_status rails-socket-state "$(ruby -e 'hex="0BB8"; rows=File.readlines("/proc/net/tcp")+File.readlines("/proc/net/tcp6"); hits=rows.select{|l| l.include?(":#{hex} ")}.map{|l| l.strip.split[3] rescue nil}.compact; puts(hits.empty? ? "no-listener" : hits.join(","))' 2>&1 | tr '\n' ' ' | cut -c 1-400)"
emit_status rails-log-tail "$(tail -n 40 /tmp/rails.log 2>&1 | sed 's/"/'"'"'/g' | tr '\n' ' ' | cut -c 1-1200)"
fi
wait "$RAILS_PID"
ENTRYPOINT_EOF
RUN chmod 755 /rails/bin/preview-entrypoint && chown rails:rails /rails/bin/preview-entrypoint
USER 1000:1000
ENTRYPOINT ["/rails/bin/preview-entrypoint"]
EXPOSE 3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]