chore: implement semantic versioning with docker image tagging
This commit is contained in:
Executable
+176
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user