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

This commit is contained in:
wehub-resource-sync
2026-07-13 11:55:53 +08:00
commit dde272c4b8
19405 changed files with 2730632 additions and 0 deletions
+68
View File
@@ -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"]
+96
View File
@@ -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
+50
View File
@@ -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"]
}
+19
View File
@@ -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'
+50
View File
@@ -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