Merge pull request #640 from yamadashy/feat/docker-slim

feat(website): Optimize Docker infrastructure and reduce resource usage
This commit is contained in:
Kazuki Yamada
2025-06-07 18:33:56 +09:00
committed by GitHub
9 changed files with 60 additions and 61 deletions

View File

@@ -1,9 +1,10 @@
{
"name": "app",
"name": "repomix-website-client",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "repomix-website-client",
"dependencies": {
"jszip": "^3.10.1",
"lucide-vue-next": "^0.474.0",

View File

@@ -1,4 +1,5 @@
{
"name": "repomix-website-client",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,18 +1,13 @@
# ==============================================================================
# Base image
# ==============================================================================
FROM node:23-alpine AS builder
# Install git and other dependencies
RUN apk add --no-cache \
git \
ca-certificates
FROM node:24-alpine AS builder
WORKDIR /app
COPY package*.json ./
# Install dependencies
RUN npm i
# Install all dependencies (including dev dependencies for build)
RUN npm ci
# Copy source code
COPY . .
@@ -21,33 +16,46 @@ COPY . .
RUN npm run build
# ==============================================================================
# Production image
# Production dependencies
# ==============================================================================
FROM node:23-alpine
FROM node:24-alpine AS deps
# Install git and other dependencies
RUN apk add --no-cache \
git \
ca-certificates \
curl
WORKDIR /app
COPY package*.json ./
# Install only production dependencies
RUN npm ci --only=production --ignore-scripts && \
npm cache clean --force
# ==============================================================================
# Runtime image
# ==============================================================================
FROM node:24-alpine
# Install git and ca-certificates (required by repomix for remote repository processing)
RUN apk add --no-cache git ca-certificates
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copy only necessary files
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
# Copy built application
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
# Copy production dependencies
COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules
# Set environment variables
ENV NODE_ENV=production \
PORT=8080
# Switch to non-root user
USER nodejs
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Start the server
CMD ["npm", "start"]
# Start the server directly
CMD ["node", "dist/index.js"]

View File

@@ -38,13 +38,13 @@ steps:
- '--memory'
- '2048Mi'
- '--cpu'
- '4'
- '2'
- '--min-instances'
- '0'
- '--max-instances'
- '10'
- '--timeout'
- '35s'
- '31s'
- '--ingress'
- 'all'
- '--allow-unauthenticated'

View File

@@ -1,15 +1,15 @@
{
"name": "app",
"name": "repomix-website-server",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "repomix-website-server",
"dependencies": {
"@google-cloud/logging-winston": "^6.0.0",
"@hono/node-server": "^1.13.8",
"adm-zip": "^0.5.16",
"hono": "^4.6.20",
"pako": "^2.1.0",
"repomix": "^0.3.7",
"winston": "^3.17.0",
"zod": "^3.24.1"
@@ -17,7 +17,6 @@
"devDependencies": {
"@types/adm-zip": "^0.5.7",
"@types/node": "^22.13.0",
"@types/pako": "^2.0.3",
"rimraf": "^6.0.1",
"tsx": "^4.19.2",
"typescript": "^5.7.3"
@@ -1183,13 +1182,6 @@
"undici-types": "~6.20.0"
}
},
"node_modules/@types/pako": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz",
"integrity": "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/parse-path": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/parse-path/-/parse-path-7.0.3.tgz",
@@ -3427,12 +3419,6 @@
"dev": true,
"license": "BlueOak-1.0.0"
},
"node_modules/pako": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
"license": "(MIT AND Zlib)"
},
"node_modules/parse-path": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.1.0.tgz",
@@ -3706,9 +3692,9 @@
}
},
"node_modules/repomix": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/repomix/-/repomix-0.3.7.tgz",
"integrity": "sha512-/b0DKC6MIVVsjcJTkTiWRwNwJwx0u+1Ph1LMEKkID5txN6mhHUFXuHdyF1ZDklyjlWzEEt6gOBWMzc3sJB+s/Q==",
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/repomix/-/repomix-0.3.9.tgz",
"integrity": "sha512-Olo/vORZChL98HOC3tZaE5+kwSaoox8KoF9N+lfQRb7dWT4qNa/SLFHT40Xq54cbZ7l9qTIZhXWmU1d/AtwqGQ==",
"license": "MIT",
"dependencies": {
"@clack/prompts": "^0.10.1",

View File

@@ -1,4 +1,5 @@
{
"name": "repomix-website-server",
"private": true,
"type": "module",
"scripts": {
@@ -15,7 +16,6 @@
"@hono/node-server": "^1.13.8",
"adm-zip": "^0.5.16",
"hono": "^4.6.20",
"pako": "^2.1.0",
"repomix": "^0.3.7",
"winston": "^3.17.0",
"zod": "^3.24.1"
@@ -23,7 +23,6 @@
"devDependencies": {
"@types/adm-zip": "^0.5.7",
"@types/node": "^22.13.0",
"@types/pako": "^2.0.3",
"rimraf": "^6.0.1",
"tsx": "^4.19.2",
"typescript": "^5.7.3"

View File

@@ -54,7 +54,7 @@ export async function processZipFile(
);
// Check if the result is already cached
const cachedResult = cache.get(cacheKey);
const cachedResult = await cache.get(cacheKey);
if (cachedResult) {
return cachedResult;
}
@@ -123,7 +123,7 @@ export async function processZipFile(
};
// Save the result to cache
cache.set(cacheKey, packResultData);
await cache.set(cacheKey, packResultData);
return packResultData;
} catch (error) {

View File

@@ -35,7 +35,7 @@ export async function processRemoteRepo(
const cacheKey = generateCacheKey(validatedData.url, validatedData.format, validatedData.options, 'url');
// Check if the result is already cached
const cachedResult = cache.get(cacheKey);
const cachedResult = await cache.get(cacheKey);
if (cachedResult) {
return cachedResult;
}
@@ -100,7 +100,7 @@ export async function processRemoteRepo(
};
// Save the result to cache
cache.set(cacheKey, packResultData);
await cache.set(cacheKey, packResultData);
return packResultData;
} catch (error) {

View File

@@ -1,13 +1,17 @@
import pako from 'pako';
import { promisify } from 'node:util';
import * as zlib from 'node:zlib';
import type { PackOptions } from '../types.js';
interface CacheEntry<T> {
const inflateAsync = promisify(zlib.inflate);
const deflateAsync = promisify(zlib.deflate);
interface CacheEntry {
value: Uint8Array; // Compressed data
timestamp: number;
}
export class RequestCache<T> {
private cache: Map<string, CacheEntry<T>> = new Map();
private cache: Map<string, CacheEntry> = new Map();
private readonly ttl: number;
constructor(ttlInSeconds = 60) {
@@ -17,7 +21,7 @@ export class RequestCache<T> {
setInterval(() => this.cleanup(), ttlInSeconds * 1000);
}
get(key: string): T | undefined {
async get(key: string): Promise<T | undefined> {
const entry = this.cache.get(key);
if (!entry) {
return undefined;
@@ -31,8 +35,8 @@ export class RequestCache<T> {
try {
// Decompress and return the data
const decompressedData = pako.inflate(entry.value, { to: 'string' });
return JSON.parse(decompressedData);
const decompressedData = await inflateAsync(entry.value);
return JSON.parse(decompressedData.toString('utf8'));
} catch (error) {
console.error('Error decompressing cache entry:', error);
this.cache.delete(key);
@@ -40,11 +44,11 @@ export class RequestCache<T> {
}
}
set(key: string, value: T): void {
async set(key: string, value: T): Promise<void> {
try {
// Convert data to JSON string and compress
const jsonString = JSON.stringify(value);
const compressedData = pako.deflate(jsonString);
const compressedData = await deflateAsync(Buffer.from(jsonString, 'utf8'));
this.cache.set(key, {
value: compressedData,