chore: import upstream snapshot with attribution
i18n - Build Validation / Validate i18n Builds (24) (push) Has been cancelled
CI - Node.js / Lint (24) (push) Has been cancelled
CI - Node.js / Build (24) (push) Has been cancelled
CI - Node.js / Test (24) (push) Has been cancelled
CI - Node.js / Test - Upcoming Changes (24) (push) Has been cancelled
CI - Node.js / Test - i18n (italian, 24) (push) Has been cancelled
CI - Node.js / Test - i18n (portuguese, 24) (push) Has been cancelled
CD - Docker - GHCR Images / Build and Push Images (push) Has been cancelled
i18n - Build Validation / Validate i18n Builds (24) (push) Has been cancelled
CI - Node.js / Lint (24) (push) Has been cancelled
CI - Node.js / Build (24) (push) Has been cancelled
CI - Node.js / Test (24) (push) Has been cancelled
CI - Node.js / Test - Upcoming Changes (24) (push) Has been cancelled
CI - Node.js / Test - i18n (italian, 24) (push) Has been cancelled
CI - Node.js / Test - i18n (portuguese, 24) (push) Has been cancelled
CD - Docker - GHCR Images / Build and Push Images (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
FROM node:24-bookworm AS builder
|
||||
RUN apt-get update && apt-get install -y jq
|
||||
# global installs need root permissions, so have to happen before we switch to
|
||||
# the node user
|
||||
RUN npm i -g pnpm@10
|
||||
# TODO(o11y): re-enable Sentry source maps + release marking in a follow-up PR
|
||||
# (needs a stable release-id scheme). See .scratchpad/o11y-cutover-review-2026-07-10.md
|
||||
# RUN npm i -g @sentry/cli@3.6.0
|
||||
# node images create a non-root user that we can use
|
||||
USER node
|
||||
WORKDIR /home/node/build
|
||||
|
||||
COPY --chown=node:node *.* .
|
||||
COPY --chown=node:node api/ api/
|
||||
COPY --chown=node:node packages/ packages/
|
||||
COPY --chown=node:node tools/ tools/
|
||||
COPY --chown=node:node curriculum/ curriculum/
|
||||
# TODO: AFAIK it's just the intro translations. Those should be folded into the
|
||||
# curriculum and then we can remove this.
|
||||
COPY --chown=node:node client/ client/
|
||||
|
||||
RUN pnpm config set dedupe-peer-dependents false
|
||||
# While we want to ignore scripts generally, we do need to generate the prisma
|
||||
# client.
|
||||
RUN pnpm install --frozen-lockfile --ignore-scripts
|
||||
RUN cd api && pnpm prisma generate
|
||||
|
||||
# The api needs to source curriculum.json and the build process relies on the
|
||||
# following env vars.
|
||||
ARG SHOW_UPCOMING_CHANGES=false
|
||||
ENV SHOW_UPCOMING_CHANGES=$SHOW_UPCOMING_CHANGES
|
||||
ARG CURRICULUM_LOCALE=english
|
||||
ENV CURRICULUM_LOCALE=$CURRICULUM_LOCALE
|
||||
|
||||
RUN pnpm turbo -F=@freecodecamp/api build
|
||||
|
||||
# TODO(o11y): re-enable in the source-maps follow-up PR
|
||||
# RUN sentry-cli sourcemaps inject api/dist
|
||||
|
||||
FROM node:24-bookworm AS deps
|
||||
RUN apt-get update && apt-get install -y jq
|
||||
|
||||
WORKDIR /home/node/build
|
||||
COPY --chown=node:node pnpm*.yaml .
|
||||
COPY --chown=node:node package.json .
|
||||
COPY --chown=node:node api/ api/
|
||||
COPY --chown=node:node packages/ packages/
|
||||
RUN npm i -g pnpm@10
|
||||
|
||||
# Weirdly this config does not seem necessary for the new api (the same number
|
||||
# of deps are installed in both cases), but I'm including it just for
|
||||
# consistency.
|
||||
RUN pnpm config set dedupe-peer-dependents false
|
||||
RUN pnpm install --prod --ignore-scripts -F=api -F=packages/shared --frozen-lockfile
|
||||
RUN cd api && npx prisma@$(jq -r '.devDependencies.prisma' < package.json) generate
|
||||
|
||||
FROM node:24-bookworm
|
||||
USER node
|
||||
WORKDIR /home/node/fcc
|
||||
COPY --from=builder --chown=node:node /home/node/build/api/dist/ ./
|
||||
COPY --from=builder --chown=node:node /home/node/build/api/package.json api/
|
||||
COPY --from=builder --chown=node:node /home/node/build/curriculum/generated/curriculum.json curriculum/generated/
|
||||
COPY --from=builder --chown=node:node /home/node/build/packages/ packages/
|
||||
|
||||
COPY --from=deps --chown=node:node /home/node/build/node_modules/ node_modules/
|
||||
COPY --from=deps --chown=node:node /home/node/build/api/node_modules/ api/node_modules/
|
||||
|
||||
CMD ["node", "api/src/server.js"]
|
||||
@@ -0,0 +1,96 @@
|
||||
# =============================================================================
|
||||
# BASE - System dependencies, pnpm, mongosh
|
||||
# =============================================================================
|
||||
FROM node:24-bookworm AS base
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
curl \
|
||||
jq \
|
||||
gnupg \
|
||||
sudo \
|
||||
rsync \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& echo "node ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/node
|
||||
|
||||
RUN curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | gpg --dearmor -o /usr/share/keyrings/mongodb-archive-keyring.gpg \
|
||||
&& echo "deb [signed-by=/usr/share/keyrings/mongodb-archive-keyring.gpg] https://repo.mongodb.org/apt/debian bookworm/mongodb-org/8.0 main" > /etc/apt/sources.list.d/mongodb-org-8.0.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends mongodb-mongosh \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# renovate: datasource=npm depName=pnpm
|
||||
RUN npm install -g pnpm@10.28.1
|
||||
|
||||
ENV PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright
|
||||
ENV npm_config_store_dir=/home/node/.local/share/pnpm/store
|
||||
|
||||
# =============================================================================
|
||||
# BUILDER - Install dependencies and build to populate caches (disposable)
|
||||
# =============================================================================
|
||||
FROM base AS builder
|
||||
|
||||
USER node
|
||||
WORKDIR /home/node/.cache/fcc
|
||||
|
||||
# Package manifests only (for pnpm install caching)
|
||||
COPY --chown=node:node pnpm-lock.yaml pnpm-workspace.yaml package.json turbo.json tsconfig-base.json ./
|
||||
COPY --chown=node:node api/package.json api/
|
||||
COPY --chown=node:node client/package.json client/turbo.json client/
|
||||
COPY --chown=node:node curriculum/package.json curriculum/turbo.json curriculum/
|
||||
COPY --chown=node:node e2e/package.json e2e/
|
||||
|
||||
COPY --chown=node:node tools/challenge-helper-scripts/package.json tools/challenge-helper-scripts/
|
||||
COPY --chown=node:node tools/challenge-parser/package.json tools/challenge-parser/
|
||||
COPY --chown=node:node tools/client-plugins/browser-scripts/package.json tools/client-plugins/browser-scripts/
|
||||
COPY --chown=node:node tools/client-plugins/gatsby-source-challenges/package.json tools/client-plugins/gatsby-source-challenges/
|
||||
COPY --chown=node:node tools/daily-challenges/package.json tools/daily-challenges/
|
||||
COPY --chown=node:node tools/scripts/seed/package.json tools/scripts/seed/
|
||||
COPY --chown=node:node tools/scripts/seed-exams/package.json tools/scripts/seed-exams/
|
||||
|
||||
COPY --chown=node:node packages/challenge-builder/package.json packages/challenge-builder/
|
||||
COPY --chown=node:node packages/challenge-linter/package.json packages/challenge-linter/
|
||||
COPY --chown=node:node packages/eslint-config/package.json packages/eslint-config/
|
||||
COPY --chown=node:node packages/shared/package.json packages/shared/
|
||||
|
||||
# Prisma schema needed for api postinstall script (prisma generate)
|
||||
COPY --chown=node:node api/prisma/ api/prisma/
|
||||
COPY --chown=node:node api/prisma.config.ts api/
|
||||
|
||||
# Install dependencies (populates pnpm store)
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=true
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Source files for builds
|
||||
COPY --chown=node:node packages/ packages/
|
||||
COPY --chown=node:node tools/ tools/
|
||||
COPY --chown=node:node curriculum/ curriculum/
|
||||
|
||||
# Build shared packages and curriculum (populates Turbo cache)
|
||||
# Source .env so turbo hashes env vars (e.g. FCC_*) identically to runtime
|
||||
COPY --chown=node:node sample.env .env
|
||||
RUN set -a && . ./.env && set +a && \
|
||||
pnpm turbo build --filter=@freecodecamp/shared && \
|
||||
pnpm turbo build --filter=@freecodecamp/curriculum
|
||||
|
||||
# =============================================================================
|
||||
# DEVCONTAINER - Clean image with only pre-populated caches
|
||||
# Used by: GitHub Codespaces, local VS Code devcontainers
|
||||
# =============================================================================
|
||||
FROM base AS devcontainer
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/freeCodeCamp/freeCodeCamp \
|
||||
org.opencontainers.image.description="Development container for freeCodeCamp with pre-populated caches" \
|
||||
org.opencontainers.image.licenses=BSD-3-Clause
|
||||
|
||||
USER node
|
||||
|
||||
# Copy pre-populated pnpm store (for fast pnpm install --prefer-offline)
|
||||
COPY --from=builder --chown=node:node /home/node/.local/share/pnpm/store /home/node/.local/share/pnpm/store
|
||||
|
||||
# Copy turbo cache only (preserving directory structure for rsync at runtime)
|
||||
WORKDIR /home/node/.cache/fcc
|
||||
RUN --mount=type=bind,from=builder,source=/home/node/.cache/fcc,target=/tmp/fcc-build \
|
||||
rsync -a --include='*/' --include='.turbo/***' --exclude='*' /tmp/fcc-build/ ./
|
||||
|
||||
WORKDIR /workspaces/freeCodeCamp
|
||||
@@ -0,0 +1,50 @@
|
||||
// Docker Bake configuration for freeCodeCamp devcontainer images
|
||||
//
|
||||
// Usage (from repo root):
|
||||
// docker buildx bake -f docker/devcontainer/docker-bake.hcl # Local build (native platform)
|
||||
// docker buildx bake -f docker/devcontainer/docker-bake.hcl devcontainer --push # Push multi-arch to GHCR
|
||||
//
|
||||
// With custom tag:
|
||||
// TAG=v1.0.0 docker buildx bake -f docker/devcontainer/docker-bake.hcl devcontainer --push
|
||||
|
||||
variable "REGISTRY" {
|
||||
default = "ghcr.io/freecodecamp"
|
||||
}
|
||||
|
||||
variable "TAG" {
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "TAG_LATEST" {
|
||||
default = "true"
|
||||
}
|
||||
|
||||
group "default" {
|
||||
targets = ["local-devcontainer"]
|
||||
}
|
||||
|
||||
target "_common" {
|
||||
context = "."
|
||||
dockerfile = "docker/devcontainer/Dockerfile"
|
||||
}
|
||||
|
||||
// Multi-arch image for pushing to registry (CI and local --push)
|
||||
target "devcontainer" {
|
||||
inherits = ["_common"]
|
||||
target = "devcontainer"
|
||||
platforms = ["linux/amd64", "linux/arm64"]
|
||||
cache-from = ["type=gha"]
|
||||
cache-to = ["type=gha,mode=max"]
|
||||
tags = concat(
|
||||
["${REGISTRY}/devcontainer:${TAG}"],
|
||||
TAG_LATEST == "true" ? ["${REGISTRY}/devcontainer:latest"] : []
|
||||
)
|
||||
}
|
||||
|
||||
// Native platform only (fast local builds)
|
||||
target "local-devcontainer" {
|
||||
inherits = ["_common"]
|
||||
target = "devcontainer"
|
||||
output = ["type=docker"]
|
||||
tags = ["ghcr.io/freecodecamp/devcontainer:latest"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Docker Compose override for E2E testing
|
||||
# Usage: docker compose -f docker/docker-compose.yml -f docker/docker-compose.e2e.yml up -d
|
||||
services:
|
||||
api:
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
- mailpit
|
||||
image: fcc-api
|
||||
env_file:
|
||||
- ../.env
|
||||
environment:
|
||||
# The api cannot connect to mongodb or mailpit via localhost from inside the
|
||||
# container, so we have to override these variables.
|
||||
- MONGOHQ_URL=mongodb://mongodb:27017/freecodecamp?replicaSet=rs0
|
||||
- MAILPIT_HOST=mailpit
|
||||
- HOST=0.0.0.0
|
||||
ports:
|
||||
- '3000:3000'
|
||||
@@ -0,0 +1,50 @@
|
||||
name: freecodecamp-local
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mongo:8.2
|
||||
container_name: mongodb
|
||||
command: mongod --replSet rs0
|
||||
restart: unless-stopped
|
||||
hostname: mongodb
|
||||
ports:
|
||||
- 27017:27017
|
||||
volumes:
|
||||
- db-data:/data/db
|
||||
healthcheck:
|
||||
test: ['CMD', 'mongosh', '--eval', "db.adminCommand('ping')"]
|
||||
interval: 2s
|
||||
retries: 5
|
||||
|
||||
setup:
|
||||
image: mongo:8.2
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: on-failure
|
||||
# This will try to initiate the replica set, until it succeeds twice (i.e. until the replica set is already initialized)
|
||||
command: >
|
||||
mongosh --host mongodb:27017 --eval '
|
||||
var cfg = {
|
||||
_id: "rs0",
|
||||
members: [
|
||||
{ _id: 0, host: "mongodb:27017" }
|
||||
]
|
||||
};
|
||||
try {
|
||||
rs.initiate(cfg);
|
||||
} catch (err) {
|
||||
if(err.codeName !== "AlreadyInitialized") throw err;
|
||||
}
|
||||
'
|
||||
|
||||
mailpit:
|
||||
restart: unless-stopped
|
||||
image: axllent/mailpit
|
||||
ports:
|
||||
- '1025:1025'
|
||||
- '8025:8025'
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
driver: local
|
||||
Reference in New Issue
Block a user