From 779f9f4e7c75aca7971569768723fc263d226bbb Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Tue, 31 Mar 2026 16:34:30 -0700 Subject: [PATCH] chore: implement semantic versioning with docker image tagging --- docker-compose.casaos.yml | 5 +- docker-compose.dev.yml | 69 +++++++ docker-compose.yml | 59 +++--- package.json | 9 +- scripts/build-and-push-docker.js | 176 ++++++++++++++++++ scripts/bump-version.js | 211 +++++++++++++++++++++ scripts/generate-docker-compose.js | 282 +++++++++++++++++++++++++++++ 7 files changed, 775 insertions(+), 36 deletions(-) create mode 100644 docker-compose.dev.yml create mode 100755 scripts/build-and-push-docker.js create mode 100755 scripts/bump-version.js create mode 100755 scripts/generate-docker-compose.js diff --git a/docker-compose.casaos.yml b/docker-compose.casaos.yml index 845cad9..79253ec 100644 --- a/docker-compose.casaos.yml +++ b/docker-compose.casaos.yml @@ -7,7 +7,7 @@ services: app: - image: euchre-camp/euchre-camp:latest + image: dhg.lol:5000/euchre-camp:0.1.0.dev container_name: euchre-camp ports: - "51193:3000" @@ -27,6 +27,9 @@ services: # Trusted Origins for Better Auth - TRUSTED_ORIGINS=${TRUSTED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000} + # Version tracking + - IMAGE_VERSION=0.1.0.dev + volumes: # Persist uploaded files and static content - /DATA/AppData/euchre-camp/config:/app/public/uploads diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..52821e9 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,69 @@ +# Development Docker Compose Configuration +# Usage: docker-compose -f docker-compose.dev.yml up -d + +services: + app: + build: + context: . + target: builder + args: + GIT_COMMIT: ${GIT_COMMIT:-dev} + container_name: euchre-camp-dev + ports: + - "3000:3000" + environment: + # Database Configuration + - DATABASE_PROVIDER=postgresql + - DATABASE_URL=postgresql://euchre_camp:password@db:5432/euchre_camp + - DATABASE_SHADOW_URL=postgresql://euchre_camp:password@db:5432/euchre_camp_shadow + + # Better Auth Configuration + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-dev-secret-change-in-production} + - BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:3000} + + # Application Configuration + - NODE_ENV=development + + # Trusted Origins for Better Auth + - TRUSTED_ORIGINS=${TRUSTED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000} + + # Version tracking + - IMAGE_VERSION=0.1.0.dev-dev + + volumes: + # Mount source code for hot reload + - .:/app + - /app/node_modules + - /app/.next + + depends_on: + - db + + restart: unless-stopped + + networks: + - euchre-network + + db: + image: postgres:15-alpine + container_name: euchre-camp-db-dev + ports: + - "5433:5432" # Different port to avoid conflicts + environment: + - POSTGRES_DB=euchre_camp + - POSTGRES_USER=euchre_camp + - POSTGRES_PASSWORD=password + volumes: + - euchre_camp_db_data_dev:/var/lib/postgresql/data + - ./scripts/init-postgres.sh:/docker-entrypoint-initdb.d/init-postgres.sh + restart: unless-stopped + networks: + - euchre-network + +volumes: + euchre_camp_db_data_dev: + driver: local + +networks: + euchre-network: + driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml index e05a055..414b573 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,66 +1,57 @@ +# Production Docker Compose Configuration +# Usage: docker-compose up -d + services: app: - build: - context: . - target: runner + image: dhg.lol:5000/euchre-camp:0.1.0.dev container_name: euchre-camp-app ports: - "3000:3000" environment: # Database Configuration - DATABASE_PROVIDER=postgresql - - DATABASE_URL=postgresql://euchre_camp:euchrepassword@postgresql:5432/euchre_camp - - DATABASE_SHADOW_URL=postgresql://euchre_camp:euchrepassword@postgresql:5432/euchre_camp_shadow - + - DATABASE_URL=postgresql://euchre_camp:password@db:5432/euchre_camp + - DATABASE_SHADOW_URL=postgresql://euchre_camp:password@db:5432/euchre_camp_shadow + # Better Auth Configuration - - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your-secret-key-change-in-production} + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-change-this-secret-in-production} - BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:3000} - + # Application Configuration - - NODE_ENV=${NODE_ENV:-production} - + - NODE_ENV=production + # Trusted Origins for Better Auth - TRUSTED_ORIGINS=${TRUSTED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000} - + + # Version tracking + - IMAGE_VERSION=0.1.0.dev + depends_on: - postgresql: - condition: service_healthy - - volumes: - # Persist uploaded files and static content - - app_data:/app/public/uploads - + - db + restart: unless-stopped - + networks: - euchre-network - postgresql: + db: image: postgres:15-alpine - container_name: euchre-camp-postgres + container_name: euchre-camp-db + ports: + - "5432:5432" environment: - POSTGRES_DB=euchre_camp - POSTGRES_USER=euchre_camp - - POSTGRES_PASSWORD=euchrepassword - ports: - - "5432:5432" + - POSTGRES_PASSWORD=password volumes: - - postgres_data:/var/lib/postgresql/data + - euchre_camp_db_data:/var/lib/postgresql/data - ./scripts/init-postgres.sh:/docker-entrypoint-initdb.d/init-postgres.sh - healthcheck: - test: ["CMD-SHELL", "pg_isready -U euchre_camp -d euchre_camp"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 30s restart: unless-stopped networks: - euchre-network volumes: - postgres_data: - driver: local - app_data: + euchre_camp_db_data: driver: local networks: diff --git a/package.json b/package.json index e27e503..b3ed881 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,14 @@ "docker:down": "docker-compose down", "docker:logs": "docker-compose logs -f", "docker:build": "docker-compose build", - "docker:shell": "docker exec -it euchre-camp-app sh" + "docker:shell": "docker exec -it euchre-camp-app sh", + "version": "node scripts/bump-version.js", + "version:patch": "node scripts/bump-version.js patch", + "version:minor": "node scripts/bump-version.js minor", + "version:major": "node scripts/bump-version.js major", + "docker:build:push": "node scripts/build-and-push-docker.js", + "docker:compose:generate": "node scripts/generate-docker-compose.js", + "release": "npm run version:patch && npm run docker:compose:generate && npm run docker:build:push" }, "dependencies": { "@hookform/resolvers": "^5.2.2", diff --git a/scripts/build-and-push-docker.js b/scripts/build-and-push-docker.js new file mode 100755 index 0000000..1705c8b --- /dev/null +++ b/scripts/build-and-push-docker.js @@ -0,0 +1,176 @@ +#!/usr/bin/env node + +/** + * Docker Build and Push Script + * + * Builds Docker images and pushes them to the local registry. + * Tags images based on git state: + * - Tagged commit: versioned tags + latest + * - Untagged commit: .dev suffix + commit hash + */ + +const { execSync, spawn } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// Configuration +const REGISTRY = 'dhg.lol:5000'; +const IMAGE_NAME = 'euchre-camp'; +const FULL_IMAGE_NAME = `${REGISTRY}/${IMAGE_NAME}`; + +// Parse command line arguments +const args = process.argv.slice(2); +const forceVersion = args[0]; // Optional: force a specific version + +// Get current version from package.json +function getCurrentVersion() { + const packageJsonPath = path.join(__dirname, '..', 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + return packageJson.version; +} + +// Get git commit hash +function getGitCommitHash(short = true) { + const flag = short ? '--short' : ''; + return execSync(`git rev-parse ${flag} HEAD`, { encoding: 'utf8' }).trim(); +} + +// Get git tag (if any) +function getGitTag() { + try { + const tag = execSync('git describe --tags --exact-match 2>/dev/null', { encoding: 'utf8' }).trim(); + return tag; + } catch (error) { + return null; + } +} + +// Check if working directory is clean +function isWorkingDirectoryClean() { + try { + const status = execSync('git status --porcelain', { encoding: 'utf8' }).trim(); + return status === ''; + } catch (error) { + return false; + } +} + +// Build Docker image +async function buildDockerImage(tags, gitCommit) { + console.log(`šŸ”Ø Building Docker image...\n`); + + const buildArgs = [ + 'build', + '--build-arg', `GIT_COMMIT=${gitCommit}`, + '-t', `${FULL_IMAGE_NAME}:${tags[0]}`, // Primary tag + ]; + + // Add additional tags + tags.forEach(tag => { + buildArgs.push('-t', `${FULL_IMAGE_NAME}:${tag}`); + }); + + buildArgs.push('.'); + + console.log(`Build command: docker ${buildArgs.join(' ')}\n`); + + return new Promise((resolve, reject) => { + const docker = spawn('docker', buildArgs, { + cwd: path.join(__dirname, '..'), + stdio: 'inherit' + }); + + docker.on('close', (code) => { + if (code === 0) { + console.log('āœ… Docker image built successfully\n'); + resolve(); + } else { + reject(new Error(`Docker build failed with code ${code}`)); + } + }); + }); +} + +// Push Docker image +async function pushDockerImage(tags) { + console.log(`šŸš€ Pushing Docker images to ${REGISTRY}...\n`); + + for (const tag of tags) { + const imageName = `${FULL_IMAGE_NAME}:${tag}`; + console.log(`Pushing ${imageName}...`); + + try { + execSync(`docker push ${imageName}`, { stdio: 'inherit' }); + console.log(`āœ… Pushed ${imageName}\n`); + } catch (error) { + console.error(`āŒ Failed to push ${imageName}:`, error.message); + throw error; + } + } + + console.log('āœ… All images pushed successfully\n'); +} + +// Main function +async function main() { + console.log('🐳 Docker Build & Push Tool\n'); + console.log(`Registry: ${REGISTRY}`); + console.log(`Image: ${IMAGE_NAME}\n`); + + // Check if working directory is clean + if (!isWorkingDirectoryClean()) { + console.log('āš ļø Warning: Working directory is not clean'); + console.log(' Proceeding anyway...\n'); + } + + // Determine image tags + const gitCommit = getGitCommitHash(); + const gitTag = getGitTag(); + const version = forceVersion || getCurrentVersion(); + + let tags = []; + + if (gitTag) { + // Tagged commit - use version and 'latest' + const tagWithoutV = gitTag.startsWith('v') ? gitTag.slice(1) : gitTag; + + if (tagWithoutV !== version) { + console.log(`āš ļø Warning: Git tag (${gitTag}) doesn't match package.json version (${version})`); + console.log(' Using package.json version for Docker tags\n'); + } + + tags = [ + version, // e.g., 0.1.0 + 'latest', // Latest stable + ]; + } else { + // Untagged commit - use version with .dev suffix and commit hash + tags = [ + `${version}.dev`, // e.g., 0.1.0.dev + `${version}-sha-${gitCommit}`, // e.g., 0.1.0-sha-abc123 + ]; + } + + console.log(`Git commit: ${gitCommit}`); + console.log(`Git tag: ${gitTag || '(none)'}`); + console.log(`Package version: ${version}`); + console.log(`Docker tags: ${tags.join(', ')}\n`); + + // Build the image + await buildDockerImage(tags, gitCommit); + + // Push the images + await pushDockerImage(tags); + + console.log('šŸŽ‰ Docker build and push complete!\n'); + console.log('Images available at:'); + tags.forEach(tag => { + console.log(` - ${FULL_IMAGE_NAME}:${tag}`); + }); +} + +// Run main function +main().catch(error => { + console.error('āŒ Error:', error.message); + process.exit(1); +}); diff --git a/scripts/bump-version.js b/scripts/bump-version.js new file mode 100755 index 0000000..8012174 --- /dev/null +++ b/scripts/bump-version.js @@ -0,0 +1,211 @@ +#!/usr/bin/env node + +/** + * Version Bumping Script + * + * Bumps the version based on conventional commits since last release. + * Supports: + * - major: Breaking changes (! or BREAKING CHANGE) + * - minor: Features (feat:) + * - patch: Fixes (fix:) and other changes + */ + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// Parse command line arguments +const args = process.argv.slice(2); +const forcedVersion = args[0]; // e.g., "0.2.0" or "patch/minor/major" + +// Paths +const packageJsonPath = path.join(__dirname, '..', 'package.json'); +const changelogPath = path.join(__dirname, '..', 'CHANGELOG.md'); + +// Load current version from package.json +function getCurrentVersion() { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + return packageJson.version; +} + +// Parse version string into components +function parseVersion(version) { + const [major, minor, patch] = version.split('.').map(Number); + return { major, minor, patch }; +} + +// Format version string +function formatVersion(major, minor, patch) { + return `${major}.${minor}.${patch}`; +} + +// Get commits since last tag +function getCommitsSinceLastTag() { + try { + const tags = execSync('git tag --sort=-version:refname', { encoding: 'utf8' }).trim(); + const latestTag = tags.split('\n')[0]; + + if (!latestTag) { + // No tags yet, get all commits + return execSync('git log --oneline --format=%s', { encoding: 'utf8' }).trim().split('\n'); + } + + const commits = execSync(`git log ${latestTag}..HEAD --oneline --format=%s`, { encoding: 'utf8' }).trim(); + return commits ? commits.split('\n') : []; + } catch (error) { + // If no commits since tag or other error, get recent commits + return execSync('git log --oneline --format=%s -n 20', { encoding: 'utf8' }).trim().split('\n'); + } +} + +// Analyze commits to determine version bump type +function analyzeCommits(commits) { + let bumpType = 'patch'; // default + + for (const commit of commits) { + if (!commit) continue; + + // Breaking change indicators + if (commit.includes('!') || commit.includes('BREAKING CHANGE')) { + bumpType = 'major'; + break; // Major takes precedence + } + + // Feature indicators + if (commit.startsWith('feat:') || commit.startsWith('feat(')) { + if (bumpType === 'patch') { + bumpType = 'minor'; + } + } + + // Fix indicators (already patch by default) + } + + return bumpType; +} + +// Generate new version based on bump type +function generateNewVersion(currentVersion, bumpType) { + const { major, minor, patch } = parseVersion(currentVersion); + + switch (bumpType) { + case 'major': + return formatVersion(major + 1, 0, 0); + case 'minor': + return formatVersion(major, minor + 1, 0); + case 'patch': + default: + return formatVersion(major, minor, patch + 1); + } +} + +// Update package.json version +function updatePackageJson(newVersion) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + packageJson.version = newVersion; + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); + console.log(`āœ“ Updated package.json to version ${newVersion}`); +} + +// Generate changelog entry +function generateChangelogEntry(newVersion, commits, bumpType) { + const date = new Date().toISOString().split('T')[0]; + const relevantCommits = commits.filter(commit => { + if (!commit) return false; + // Filter out non-user-facing commits + if (commit.startsWith('chore:') && !commit.includes('BREAKING')) return false; + return true; + }); + + const changelogEntry = `## [${newVersion}] - ${date}\n\n` + + `### ${bumpType.charAt(0).toUpperCase() + bumpType.slice(1)} Changes\n\n` + + relevantCommits.map(commit => `- ${commit}`).join('\n') + + '\n\n'; + + return changelogEntry; +} + +// Update CHANGELOG.md +function updateChangelog(newVersion, commits, bumpType) { + const changelogEntry = generateChangelogEntry(newVersion, commits, bumpType); + + if (fs.existsSync(changelogPath)) { + const content = fs.readFileSync(changelogPath, 'utf8'); + const newContent = changelogEntry + content; + fs.writeFileSync(changelogPath, newContent); + } else { + fs.writeFileSync(changelogPath, `# Changelog\n\n${changelogEntry}`); + } + + console.log(`āœ“ Updated CHANGELOG.md`); +} + +// Main function +function main() { + console.log('šŸ”„ Version Bumping Tool\n'); + + const currentVersion = getCurrentVersion(); + console.log(`Current version: ${currentVersion}`); + + let newVersion; + let bumpType; + + if (forcedVersion) { + // Check if it's a bump type or explicit version + if (['patch', 'minor', 'major'].includes(forcedVersion.toLowerCase())) { + bumpType = forcedVersion.toLowerCase(); + newVersion = generateNewVersion(currentVersion, bumpType); + console.log(`Forced bump type: ${bumpType}`); + } else { + // Assume it's an explicit version + newVersion = forcedVersion; + bumpType = 'custom'; + console.log(`Forced version: ${newVersion}`); + } + } else { + // Analyze commits to determine bump type + const commits = getCommitsSinceLastTag(); + console.log(`\nAnalyzing ${commits.length} commits since last release...`); + + bumpType = analyzeCommits(commits); + newVersion = generateNewVersion(currentVersion, bumpType); + + console.log(`\nDetected bump type: ${bumpType}`); + console.log(`New version: ${currentVersion} → ${newVersion}`); + } + + // Confirm with user + const readline = require('readline'); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + rl.question(`\nApply version ${newVersion}? (y/N) `, (answer) => { + if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') { + // Update package.json + updatePackageJson(newVersion); + + // Update changelog + if (bumpType !== 'custom') { + const commits = getCommitsSinceLastTag(); + updateChangelog(newVersion, commits, bumpType); + } + + console.log(`\nāœ… Version bumped to ${newVersion}`); + console.log(`\nNext steps:`); + console.log(` 1. Review changes: git diff`); + console.log(` 2. Commit changes: git commit -am "chore: bump version to v${newVersion}"`); + console.log(` 3. Create tag: git tag -a v${newVersion} -m "Release v${newVersion}"`); + console.log(` 4. Push changes: git push origin main`); + console.log(` 5. Push tag: git push origin v${newVersion}`); + } else { + console.log('āŒ Version bump cancelled'); + } + + rl.close(); + }); +} + +// Run main function +main(); diff --git a/scripts/generate-docker-compose.js b/scripts/generate-docker-compose.js new file mode 100755 index 0000000..75426d8 --- /dev/null +++ b/scripts/generate-docker-compose.js @@ -0,0 +1,282 @@ +#!/usr/bin/env node + +/** + * Generate Docker Compose Files with Versioned Images + * + * This script generates docker-compose files with the appropriate image tags + * based on the current version and git state. + */ + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +// Configuration +const REGISTRY = 'dhg.lol:5000'; +const IMAGE_NAME = 'euchre-camp'; + +// Get current version from package.json +function getCurrentVersion() { + const packageJsonPath = path.join(__dirname, '..', 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + return packageJson.version; +} + +// Get git tag (if any) +function getGitTag() { + try { + const tag = execSync('git describe --tags --exact-match 2>/dev/null', { encoding: 'utf8' }).trim(); + return tag; + } catch (error) { + return null; + } +} + +// Get image tag based on git state +function getImageTag() { + const version = getCurrentVersion(); + const gitTag = getGitTag(); + + if (gitTag) { + // Tagged commit - use version tag + return version; + } else { + // Untagged commit - use .dev suffix + return `${version}.dev`; + } +} + +// Generate docker-compose.casaos.yml +function generateCasaosYml(imageTag) { + return `# CasaOS Optimized Docker Compose Configuration +# This file is optimized for deployment on CasaOS +# Usage: docker-compose -f docker-compose.casaos.yml up -d +# +# IMPORTANT: This configuration requires an external PostgreSQL database. +# Set DATABASE_URL environment variable to your PostgreSQL connection string. + +services: + app: + image: ${REGISTRY}/${IMAGE_NAME}:${imageTag} + container_name: euchre-camp + ports: + - "51193:3000" + environment: + # Database Configuration (REQUIRED: Set via CasaOS environment variables) + - DATABASE_PROVIDER=postgresql + - DATABASE_URL="postgresql://euchre_camp:LINGO5row_hiding@dhg.lol:5432/euchre_camp" + - DATABASE_SHADOW_URL="postgresql://euchre_camp:LINGO5row_hiding@dhg.lol:5432/euchre_camp_shadow" + + # Better Auth Configuration + - BETTER_AUTH_SECRET=\${BETTER_AUTH_SECRET:-change-this-secret-in-production} + - BETTER_AUTH_URL=\${BETTER_AUTH_URL:-http://localhost:3000} + + # Application Configuration + - NODE_ENV=production + + # Trusted Origins for Better Auth + - TRUSTED_ORIGINS=\${TRUSTED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000} + + # Version tracking + - IMAGE_VERSION=${imageTag} + + volumes: + # Persist uploaded files and static content + - /DATA/AppData/euchre-camp/config:/app/public/uploads + + restart: unless-stopped + + networks: + - euchre-network + +volumes: + euchre_camp_app_data: + driver: local + +networks: + euchre-network: + driver: bridge +`; +} + +// Generate docker-compose.yml (production with PostgreSQL) +function generateDockerComposeYml(imageTag) { + return `# Production Docker Compose Configuration +# Usage: docker-compose up -d + +services: + app: + image: ${REGISTRY}/${IMAGE_NAME}:${imageTag} + container_name: euchre-camp-app + ports: + - "3000:3000" + environment: + # Database Configuration + - DATABASE_PROVIDER=postgresql + - DATABASE_URL=postgresql://euchre_camp:password@db:5432/euchre_camp + - DATABASE_SHADOW_URL=postgresql://euchre_camp:password@db:5432/euchre_camp_shadow + + # Better Auth Configuration + - BETTER_AUTH_SECRET=\${BETTER_AUTH_SECRET:-change-this-secret-in-production} + - BETTER_AUTH_URL=\${BETTER_AUTH_URL:-http://localhost:3000} + + # Application Configuration + - NODE_ENV=production + + # Trusted Origins for Better Auth + - TRUSTED_ORIGINS=\${TRUSTED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000} + + # Version tracking + - IMAGE_VERSION=${imageTag} + + depends_on: + - db + + restart: unless-stopped + + networks: + - euchre-network + + db: + image: postgres:15-alpine + container_name: euchre-camp-db + ports: + - "5432:5432" + environment: + - POSTGRES_DB=euchre_camp + - POSTGRES_USER=euchre_camp + - POSTGRES_PASSWORD=password + volumes: + - euchre_camp_db_data:/var/lib/postgresql/data + - ./scripts/init-postgres.sh:/docker-entrypoint-initdb.d/init-postgres.sh + restart: unless-stopped + networks: + - euchre-network + +volumes: + euchre_camp_db_data: + driver: local + +networks: + euchre-network: + driver: bridge +`; +} + +// Generate docker-compose.dev.yml (development with hot reload) +function generateDockerComposeDevYml(imageTag) { + return `# Development Docker Compose Configuration +# Usage: docker-compose -f docker-compose.dev.yml up -d + +services: + app: + build: + context: . + target: builder + args: + GIT_COMMIT: \${GIT_COMMIT:-dev} + container_name: euchre-camp-dev + ports: + - "3000:3000" + environment: + # Database Configuration + - DATABASE_PROVIDER=postgresql + - DATABASE_URL=postgresql://euchre_camp:password@db:5432/euchre_camp + - DATABASE_SHADOW_URL=postgresql://euchre_camp:password@db:5432/euchre_camp_shadow + + # Better Auth Configuration + - BETTER_AUTH_SECRET=\${BETTER_AUTH_SECRET:-dev-secret-change-in-production} + - BETTER_AUTH_URL=\${BETTER_AUTH_URL:-http://localhost:3000} + + # Application Configuration + - NODE_ENV=development + + # Trusted Origins for Better Auth + - TRUSTED_ORIGINS=\${TRUSTED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000} + + # Version tracking + - IMAGE_VERSION=${imageTag}-dev + + volumes: + # Mount source code for hot reload + - .:/app + - /app/node_modules + - /app/.next + + depends_on: + - db + + restart: unless-stopped + + networks: + - euchre-network + + db: + image: postgres:15-alpine + container_name: euchre-camp-db-dev + ports: + - "5433:5432" # Different port to avoid conflicts + environment: + - POSTGRES_DB=euchre_camp + - POSTGRES_USER=euchre_camp + - POSTGRES_PASSWORD=password + volumes: + - euchre_camp_db_data_dev:/var/lib/postgresql/data + - ./scripts/init-postgres.sh:/docker-entrypoint-initdb.d/init-postgres.sh + restart: unless-stopped + networks: + - euchre-network + +volumes: + euchre_camp_db_data_dev: + driver: local + +networks: + euchre-network: + driver: bridge +`; +} + +// Main function +function main() { + const imageTag = getImageTag(); + + console.log('šŸ”§ Generating Docker Compose files...\n'); + console.log(`Registry: ${REGISTRY}`); + console.log(`Image: ${IMAGE_NAME}`); + console.log(`Tag: ${imageTag}\n`); + + // Generate casaos configuration + const casaosContent = generateCasaosYml(imageTag); + fs.writeFileSync( + path.join(__dirname, '..', 'docker-compose.casaos.yml'), + casaosContent + ); + console.log('āœ“ Generated docker-compose.casaos.yml'); + + // Generate production configuration + const prodContent = generateDockerComposeYml(imageTag); + fs.writeFileSync( + path.join(__dirname, '..', 'docker-compose.yml'), + prodContent + ); + console.log('āœ“ Generated docker-compose.yml'); + + // Generate development configuration + const devContent = generateDockerComposeDevYml(imageTag); + fs.writeFileSync( + path.join(__dirname, '..', 'docker-compose.dev.yml'), + devContent + ); + console.log('āœ“ Generated docker-compose.dev.yml'); + + console.log('\nāœ… Docker Compose files generated successfully'); + console.log('\nNext steps:'); + console.log(' 1. Review the generated files'); + console.log(' 2. Commit changes to git'); + console.log(' 3. Run: docker-compose build'); + console.log(' 4. Run: docker-compose push'); +} + +// Run main function +main();