From 1f8242440cda41c4f0f94675b03dcacce400af99 Mon Sep 17 00:00:00 2001 From: tsotchke Date: Tue, 14 Jul 2026 09:39:23 -0400 Subject: [PATCH] ci: make Railway deploys deterministic --- .dockerignore | 8 ++++++++ .github/workflows/ci.yml | 25 +++++++++++++++++++++++++ .gitignore | 3 +++ Dockerfile | 31 +++++++++++++++++++++++++++++++ README.md | 12 ++++++++---- package.json | 4 ++-- railway.toml | 9 ++++----- 7 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..24306e3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.git +.github +.railway +.env* +build +node_modules +test +npm-debug.log* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27bcb4e..9f33428 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,3 +26,28 @@ jobs: cache: npm - run: npm ci --ignore-scripts - run: npm test + + container-smoke: + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - uses: actions/checkout@v4 + - run: docker build --tag quantum-rng-api:ci . + - run: docker run --detach --name quantum-rng-api --publish 127.0.0.1:8080:8080 quantum-rng-api:ci + - name: Verify production container + run: | + for attempt in {1..60}; do + if response="$(curl --fail --silent http://127.0.0.1:8080/v1/health)"; then + HEALTH_RESPONSE="$response" node - <<'NODE' + const health = JSON.parse(process.env.HEALTH_RESPONSE); + if (health.status !== 'ok') throw new Error(`unexpected status: ${health.status}`); + if (health.apiVersion !== '2.0.0') throw new Error(`unexpected API version: ${health.apiVersion}`); + if (health.engine?.name !== 'quantum_rng_v3') throw new Error(`unexpected engine: ${health.engine?.name}`); + NODE + exit 0 + fi + sleep 2 + done + docker logs quantum-rng-api + exit 1 diff --git a/.gitignore b/.gitignore index 9c4d77e..699f975 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ yarn-error.log* # Environment variables .env .env.local + +# Local Railway project link +.railway/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f569967 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM node:22-bookworm-slim AS build + +WORKDIR /app + +RUN apt-get update \ + && apt-get install --yes --no-install-recommends python3 make g++ \ + && rm -rf /var/lib/apt/lists/* + +COPY package.json package-lock.json binding.gyp ./ +COPY src ./src +COPY vendor ./vendor + +RUN npm ci --omit=dev + +FROM node:22-bookworm-slim AS runtime + +ENV NODE_ENV=production \ + HOST=0.0.0.0 \ + PORT=8080 + +WORKDIR /app + +COPY package.json package-lock.json server.js ./ +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/build ./build + +USER node + +EXPOSE 8080 + +CMD ["npm", "start"] diff --git a/README.md b/README.md index 923dcd2..b133fb3 100644 --- a/README.md +++ b/README.md @@ -140,10 +140,14 @@ portable SIMD/scalar paths. ## Deployment -`railway.toml` supplies the Railway build packages, start command, and -`/v1/health` probe. A production deployment must place an authenticated, -rate-limited TLS gateway in front of this process when the output is used by -untrusted clients or security-sensitive systems. +Railway builds the root `Dockerfile` with Node.js 22 and deploys `master` +automatically after the GitHub Actions check suite passes. `railway.toml` +defines the Docker builder, `/v1/health` probe, and restart policy. CI builds +and starts the same production image before a change can reach production. + +A production deployment must place an authenticated, rate-limited TLS gateway +in front of this process when the output is used by untrusted clients or +security-sensitive systems. The service starts only after the native engine initializes. Unless disabled, it also requires the startup CHSH simulation check to exceed the classical diff --git a/package.json b/package.json index 0d2532a..fcff9ef 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "scripts": { "build": "node-gyp rebuild", "clean": "node-gyp clean", + "install": "npm run build", "start": "node server.js", - "test": "npm run build && node --test", - "postinstall": "npm run build" + "test": "npm run build && node --test" }, "gypfile": true, "keywords": [ diff --git a/railway.toml b/railway.toml index c87de6d..31897cf 100644 --- a/railway.toml +++ b/railway.toml @@ -1,10 +1,9 @@ [build] -builder = "nixpacks" +builder = "DOCKERFILE" +dockerfilePath = "Dockerfile" [deploy] -startCommand = "npm start" healthcheckPath = "/v1/health" healthcheckTimeout = 100 - -[nixpacks] -pkgs = ["python3", "make", "gcc"] +restartPolicyType = "ON_FAILURE" +restartPolicyMaxRetries = 3