feat(versioning): unified version from VERSION.json
- status.atlilith.com now sources version from VERSION.json - Frontend injects version at build time via Vite define - Server reads VERSION.json instead of package.json - release-deploy.sh increments builds before sync to releases - version-bump.sh updated for <major>.<merges>.<builds> format - Starting version: 0.0.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1da1f88954
commit
0c6572d716
6 changed files with 189 additions and 128 deletions
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"major": 1,
|
||||
"major": 0,
|
||||
"merges": 0,
|
||||
"builds": 0,
|
||||
"version": "1.0.0",
|
||||
"version": "0.0.0",
|
||||
"lastMerge": null,
|
||||
"lastBuild": null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ import { AdminDashboard } from './AdminDashboard';
|
|||
import { LoginPage } from './LoginPage';
|
||||
import { HostsPage } from './HostsPage';
|
||||
|
||||
const APP_VERSION = '1.1.0-ui-refactor';
|
||||
const BUILD_TIME = '2025-12-25T15:30:00Z';
|
||||
// Injected at build time from VERSION.json
|
||||
declare const __APP_VERSION__: string;
|
||||
declare const __BUILD_TIME__: string;
|
||||
|
||||
console.log(
|
||||
`%c Lilith Status Dashboard v${APP_VERSION} %c Built: ${BUILD_TIME} `,
|
||||
`%c Lilith Status Dashboard v${__APP_VERSION__} %c Built: ${__BUILD_TIME__} `,
|
||||
'background: #ff00ff; color: #000; font-weight: bold; padding: 4px 8px;',
|
||||
'background: #00ffff; color: #000; padding: 4px 8px;'
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,52 @@
|
|||
import { defineConfig, loadEnv } from 'vite';
|
||||
import { defineConfig, loadEnv, Plugin } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
import { execFileSync } from 'child_process';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
// Read authoritative version from monorepo root
|
||||
function getMonorepoVersion(): string {
|
||||
try {
|
||||
const versionPath = path.resolve(__dirname, '../../../VERSION.json');
|
||||
const content = readFileSync(versionPath, 'utf-8');
|
||||
return JSON.parse(content).version || '0.0.0';
|
||||
} catch {
|
||||
return '0.0.0';
|
||||
}
|
||||
}
|
||||
|
||||
// Generate build info at build time
|
||||
function buildInfoPlugin(): Plugin {
|
||||
return {
|
||||
name: 'build-info',
|
||||
writeBundle(options, bundle) {
|
||||
const gitCommit = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { encoding: 'utf-8' }).trim();
|
||||
const gitBranch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { encoding: 'utf-8' }).trim();
|
||||
|
||||
// Extract bundle hash from output filenames
|
||||
const jsBundle = Object.keys(bundle).find(f => f.startsWith('assets/index-') && f.endsWith('.js'));
|
||||
const buildHash = jsBundle?.match(/index-([^.]+)\.js/)?.[1] || 'unknown';
|
||||
|
||||
const buildInfo = {
|
||||
buildHash,
|
||||
gitCommit,
|
||||
gitBranch,
|
||||
buildTime: new Date().toISOString(),
|
||||
app: 'status-dashboard-frontend',
|
||||
};
|
||||
|
||||
const outDir = options.dir || 'dist';
|
||||
writeFileSync(path.join(outDir, 'build-info.json'), JSON.stringify(buildInfo, null, 2));
|
||||
console.log(`\n📦 Build info generated: ${buildHash} (${gitCommit})`);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, __dirname, '');
|
||||
|
||||
return {
|
||||
plugins: [react()],
|
||||
plugins: [react(), buildInfoPlugin()],
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
|
|
@ -26,6 +66,9 @@ export default defineConfig(({ mode }) => {
|
|||
},
|
||||
},
|
||||
define: {
|
||||
// Inject authoritative version at build time
|
||||
'__APP_VERSION__': JSON.stringify(getMonorepoVersion()),
|
||||
'__BUILD_TIME__': JSON.stringify(new Date().toISOString()),
|
||||
// Production: Use empty string for same-origin (HTTPS) - nginx proxies /api and /socket.io
|
||||
// Development: Use proxy config above
|
||||
'import.meta.env.VITE_API_URL': mode === 'production'
|
||||
|
|
|
|||
|
|
@ -20,16 +20,15 @@ interface VersionInfo {
|
|||
node: string;
|
||||
}
|
||||
|
||||
// Read version from package.json at startup
|
||||
const packageJsonPath = path.join(__dirname, '../../package.json');
|
||||
let packageInfo = { name: 'status-dashboard-server', version: '0.0.0' };
|
||||
// Read version from authoritative VERSION.json at monorepo root
|
||||
const versionJsonPath = path.join(__dirname, '../../../../../VERSION.json');
|
||||
let versionInfo = { version: '0.0.0', major: 0, merges: 0, builds: 0 };
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(packageJsonPath, 'utf-8');
|
||||
const pkg = JSON.parse(content);
|
||||
packageInfo = { name: pkg.name || packageInfo.name, version: pkg.version || packageInfo.version };
|
||||
const content = fs.readFileSync(versionJsonPath, 'utf-8');
|
||||
versionInfo = JSON.parse(content);
|
||||
} catch {
|
||||
// Use defaults if package.json cannot be read
|
||||
// Use defaults if VERSION.json cannot be read
|
||||
}
|
||||
|
||||
// Capture build time at startup
|
||||
|
|
@ -41,8 +40,8 @@ export class VersionController {
|
|||
@Get('version')
|
||||
getVersion(): VersionInfo {
|
||||
return {
|
||||
name: packageInfo.name,
|
||||
version: packageInfo.version,
|
||||
name: 'lilith-platform',
|
||||
version: versionInfo.version,
|
||||
buildTime: BUILD_TIME,
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
uptime: Math.floor((Date.now() - START_TIME) / 1000),
|
||||
|
|
|
|||
|
|
@ -1,126 +1,145 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Version Bumping Library - Semantic Versioning
|
||||
# Version Bumping Library - Lilith Platform Versioning
|
||||
#
|
||||
# Handles semantic version management (vX.Y.Z) based on conventional commits.
|
||||
# Analyzes commit messages to determine appropriate version bump type.
|
||||
# Format: <major>.<merges>.<builds>
|
||||
# - major: Manual increment for breaking changes
|
||||
# - merges: Incremented by workflow/finish on worktree merge
|
||||
# - builds: Incremented by release-deploy.sh before sync to releases
|
||||
#
|
||||
# Source of truth: VERSION.json at repository root
|
||||
#
|
||||
|
||||
set -e
|
||||
set -u
|
||||
|
||||
# Find VERSION.json from script location
|
||||
find_version_file() {
|
||||
local SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
local PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||||
echo "$PROJECT_ROOT/VERSION.json"
|
||||
}
|
||||
|
||||
VERSION_FILE="$(find_version_file)"
|
||||
|
||||
get_last_version() {
|
||||
# Get most recent tag matching v*.*.*
|
||||
local LAST_TAG=$(git describe --tags --abbrev=0 --match 'v*.*.*' 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$LAST_TAG" ]; then
|
||||
echo "v0.0.0"
|
||||
if [ -f "$VERSION_FILE" ]; then
|
||||
jq -r '.version' "$VERSION_FILE"
|
||||
else
|
||||
echo "$LAST_TAG"
|
||||
echo "0.0.0"
|
||||
fi
|
||||
}
|
||||
|
||||
parse_version() {
|
||||
local VERSION="$1"
|
||||
# Remove 'v' prefix and split into components
|
||||
echo "${VERSION#v}" | tr '.' ' '
|
||||
read_version_json() {
|
||||
if [ -f "$VERSION_FILE" ]; then
|
||||
cat "$VERSION_FILE"
|
||||
else
|
||||
echo '{"major": 0, "merges": 0, "builds": 0, "version": "0.0.0"}'
|
||||
fi
|
||||
}
|
||||
|
||||
# Increment builds counter (called before sync to releases)
|
||||
increment_builds() {
|
||||
local JSON=$(read_version_json)
|
||||
local MAJOR=$(echo "$JSON" | jq -r '.major')
|
||||
local MERGES=$(echo "$JSON" | jq -r '.merges')
|
||||
local BUILDS=$(echo "$JSON" | jq -r '.builds')
|
||||
|
||||
BUILDS=$((BUILDS + 1))
|
||||
local NEW_VERSION="$MAJOR.$MERGES.$BUILDS"
|
||||
|
||||
jq --arg v "$NEW_VERSION" \
|
||||
--argjson builds "$BUILDS" \
|
||||
--arg lastBuild "$(date -Iseconds)" \
|
||||
'.builds = $builds | .version = $v | .lastBuild = $lastBuild' \
|
||||
"$VERSION_FILE" > "$VERSION_FILE.tmp" && mv "$VERSION_FILE.tmp" "$VERSION_FILE"
|
||||
|
||||
echo "$NEW_VERSION"
|
||||
}
|
||||
|
||||
# Increment merges counter (called by workflow/finish)
|
||||
increment_merges() {
|
||||
local WORKTREE_NAME="${1:-unknown}"
|
||||
local JSON=$(read_version_json)
|
||||
local MAJOR=$(echo "$JSON" | jq -r '.major')
|
||||
local MERGES=$(echo "$JSON" | jq -r '.merges')
|
||||
local BUILDS=$(echo "$JSON" | jq -r '.builds')
|
||||
|
||||
MERGES=$((MERGES + 1))
|
||||
local NEW_VERSION="$MAJOR.$MERGES.$BUILDS"
|
||||
|
||||
jq --arg v "$NEW_VERSION" \
|
||||
--argjson merges "$MERGES" \
|
||||
--arg lastMerge "$WORKTREE_NAME" \
|
||||
'.merges = $merges | .version = $v | .lastMerge = $lastMerge' \
|
||||
"$VERSION_FILE" > "$VERSION_FILE.tmp" && mv "$VERSION_FILE.tmp" "$VERSION_FILE"
|
||||
|
||||
echo "$NEW_VERSION"
|
||||
}
|
||||
|
||||
# Bump major version (manual, resets counters)
|
||||
bump_major() {
|
||||
local JSON=$(read_version_json)
|
||||
local MAJOR=$(echo "$JSON" | jq -r '.major')
|
||||
|
||||
MAJOR=$((MAJOR + 1))
|
||||
local NEW_VERSION="$MAJOR.0.0"
|
||||
|
||||
jq --arg v "$NEW_VERSION" \
|
||||
--argjson major "$MAJOR" \
|
||||
'.major = $major | .merges = 0 | .builds = 0 | .version = $v' \
|
||||
"$VERSION_FILE" > "$VERSION_FILE.tmp" && mv "$VERSION_FILE.tmp" "$VERSION_FILE"
|
||||
|
||||
echo "$NEW_VERSION"
|
||||
}
|
||||
|
||||
# Legacy compatibility functions
|
||||
determine_bump_type() {
|
||||
local LAST_TAG="$1"
|
||||
local COMMITS=$(git log ${LAST_TAG}..HEAD --oneline --no-merges 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$COMMITS" ]; then
|
||||
# No commits since last tag
|
||||
echo "none"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check for breaking changes (BREAKING CHANGE: or ! after type)
|
||||
if echo "$COMMITS" | grep -qE '(BREAKING CHANGE:|!:)'; then
|
||||
echo "major"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check for features (feat: or feat(scope):)
|
||||
if echo "$COMMITS" | grep -qE '^[a-f0-9]+ feat(\(.+\))?:'; then
|
||||
echo "minor"
|
||||
return
|
||||
fi
|
||||
|
||||
# Default to patch for fixes, chores, docs, etc.
|
||||
echo "patch"
|
||||
# Always return "build" - we use builds counter now
|
||||
echo "build"
|
||||
}
|
||||
|
||||
bump_version() {
|
||||
local CURRENT="$1"
|
||||
local BUMP_TYPE="$2"
|
||||
|
||||
if [ "$BUMP_TYPE" = "none" ]; then
|
||||
echo "$CURRENT"
|
||||
return
|
||||
fi
|
||||
|
||||
read -r MAJOR MINOR PATCH <<< $(parse_version "$CURRENT")
|
||||
|
||||
case "$BUMP_TYPE" in
|
||||
major)
|
||||
MAJOR=$((MAJOR + 1))
|
||||
MINOR=0
|
||||
PATCH=0
|
||||
;;
|
||||
minor)
|
||||
MINOR=$((MINOR + 1))
|
||||
PATCH=0
|
||||
;;
|
||||
patch)
|
||||
PATCH=$((PATCH + 1))
|
||||
;;
|
||||
*)
|
||||
echo "$CURRENT"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "v${MAJOR}.${MINOR}.${PATCH}"
|
||||
# For release process, increment builds
|
||||
increment_builds
|
||||
}
|
||||
|
||||
create_version_tag() {
|
||||
local NEW_VERSION="$1"
|
||||
local COMMIT_MESSAGE="$2"
|
||||
|
||||
# Create annotated tag with commit message as annotation
|
||||
git tag -a "$NEW_VERSION" -m "Release $NEW_VERSION
|
||||
# Create annotated tag
|
||||
git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION
|
||||
|
||||
$COMMIT_MESSAGE"
|
||||
|
||||
echo "$NEW_VERSION"
|
||||
echo "v$NEW_VERSION"
|
||||
}
|
||||
|
||||
get_version_changelog() {
|
||||
local LAST_TAG="$1"
|
||||
local NEW_TAG="$2"
|
||||
|
||||
# Generate concise changelog
|
||||
cat <<EOF
|
||||
## Changes from $LAST_TAG to $NEW_TAG
|
||||
|
||||
### Features
|
||||
$(git log ${LAST_TAG}..HEAD --oneline --no-merges --grep='^feat' | sed 's/^/- /' || echo "- None")
|
||||
### Commits
|
||||
$(git log ${LAST_TAG}..HEAD --oneline --no-merges 2>/dev/null | head -20 | sed 's/^/- /' || echo "- None")
|
||||
|
||||
### Fixes
|
||||
$(git log ${LAST_TAG}..HEAD --oneline --no-merges --grep='^fix' | sed 's/^/- /' || echo "- None")
|
||||
|
||||
### Other
|
||||
$(git log ${LAST_TAG}..HEAD --oneline --no-merges --grep='^chore\|^refactor\|^docs\|^test' | head -5 | sed 's/^/- /' || echo "- None")
|
||||
|
||||
Total commits: $(git log ${LAST_TAG}..HEAD --oneline --no-merges | wc -l)
|
||||
Total commits: $(git log ${LAST_TAG}..HEAD --oneline --no-merges 2>/dev/null | wc -l || echo "0")
|
||||
EOF
|
||||
}
|
||||
|
||||
# Export functions for use in other scripts
|
||||
# Export functions
|
||||
export -f find_version_file
|
||||
export -f get_last_version
|
||||
export -f parse_version
|
||||
export -f read_version_json
|
||||
export -f increment_builds
|
||||
export -f increment_merges
|
||||
export -f bump_major
|
||||
export -f determine_bump_type
|
||||
export -f bump_version
|
||||
export -f create_version_tag
|
||||
|
|
|
|||
|
|
@ -67,15 +67,22 @@ main() {
|
|||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Step 1: Sync to releases repository
|
||||
log_step "1. Syncing to releases repository..."
|
||||
# Step 1: Increment build version
|
||||
log_step "1. Incrementing build version..."
|
||||
NEW_VERSION=$(increment_builds)
|
||||
log_info "Version: $NEW_VERSION"
|
||||
git add VERSION.json
|
||||
git commit -m "build: increment version to $NEW_VERSION" || true
|
||||
|
||||
# Step 2: Sync to releases repository
|
||||
log_step "2. Syncing to releases repository..."
|
||||
sync_to_releases
|
||||
|
||||
# Step 2: Generate commit message
|
||||
log_step "2. Generating ML commit message..."
|
||||
LAST_TAG=$(get_last_version)
|
||||
CHANGED_FILES=$(list_changed_files "$LAST_TAG")
|
||||
DIFF_SUMMARY=$(git diff --stat ${LAST_TAG}..HEAD)
|
||||
# Step 3: Generate commit message
|
||||
log_step "3. Generating ML commit message..."
|
||||
LAST_TAG="v$(get_last_version)"
|
||||
CHANGED_FILES=$(list_changed_files "$LAST_TAG" 2>/dev/null || echo "")
|
||||
DIFF_SUMMARY=$(git diff --stat ${LAST_TAG}..HEAD 2>/dev/null || echo "Initial release")
|
||||
COMMIT_MESSAGE=$(generate_commit_message "$CHANGED_FILES" "$DIFF_SUMMARY")
|
||||
|
||||
echo ""
|
||||
|
|
@ -83,46 +90,38 @@ main() {
|
|||
echo "$COMMIT_MESSAGE"
|
||||
echo ""
|
||||
|
||||
# Step 3: Create version tag
|
||||
log_step "3. Creating version tag..."
|
||||
BUMP_TYPE=$(determine_bump_type "$LAST_TAG")
|
||||
NEW_TAG=$(bump_version "$LAST_TAG" "$BUMP_TYPE")
|
||||
log_info "Version: $LAST_TAG → $NEW_TAG (bump: $BUMP_TYPE)"
|
||||
|
||||
if [ "$BUMP_TYPE" = "none" ]; then
|
||||
log_warn "No changes detected since last tag"
|
||||
read -p "Continue with same version? (y/N) " -n 1 -r
|
||||
echo
|
||||
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 0
|
||||
fi
|
||||
# Step 4: Create version tag
|
||||
log_step "4. Creating version tag..."
|
||||
NEW_TAG="v$NEW_VERSION"
|
||||
log_info "Version tag: $NEW_TAG"
|
||||
|
||||
# Commit to releases repo
|
||||
cd "$RELEASES_DIR"
|
||||
git add -A
|
||||
git commit -m "$COMMIT_MESSAGE" || log_warn "No changes to commit"
|
||||
create_version_tag "$NEW_TAG" "$COMMIT_MESSAGE"
|
||||
create_version_tag "$NEW_VERSION" "$COMMIT_MESSAGE"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Step 4: Generate release notes
|
||||
log_step "4. Generating release notes..."
|
||||
# Step 5: Generate release notes
|
||||
log_step "5. Generating release notes..."
|
||||
RELEASE_NOTES_FILE="/tmp/release-notes-$NEW_TAG.md"
|
||||
CHANGED_SERVICES=$(detect_changed_services "$LAST_TAG")
|
||||
CHANGED_SERVICES=$(detect_changed_services "$LAST_TAG" 2>/dev/null || echo "")
|
||||
generate_release_notes "$LAST_TAG" "$NEW_TAG" "$CHANGED_SERVICES" > "$RELEASE_NOTES_FILE"
|
||||
|
||||
# Step 5: Push to GitHub (for code sharing, not CI/CD)
|
||||
log_step "5. Pushing to GitHub (code sharing)..."
|
||||
# Step 6: Push to GitHub (for code sharing, not CI/CD)
|
||||
log_step "6. Pushing to GitHub (code sharing)..."
|
||||
cd "$RELEASES_DIR"
|
||||
push_to_github "$NEW_TAG" "$RELEASE_NOTES_FILE"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Step 6: Detect changed services
|
||||
log_step "6. Detecting changed services..."
|
||||
# Step 7: Detect changed services
|
||||
log_step "7. Detecting changed services..."
|
||||
log_info "Changed services: $(echo $CHANGED_SERVICES | wc -w) service(s)"
|
||||
get_change_summary "$LAST_TAG" "$CHANGED_SERVICES"
|
||||
get_change_summary "$LAST_TAG" "$CHANGED_SERVICES" 2>/dev/null || true
|
||||
|
||||
# Step 7: Prompt for deployment
|
||||
# Step 8: Prompt for deployment
|
||||
echo ""
|
||||
log_step "7. Ready to deploy"
|
||||
log_step "8. Ready to deploy"
|
||||
echo ""
|
||||
log_info "Services to deploy: $CHANGED_SERVICES"
|
||||
echo ""
|
||||
|
|
@ -136,12 +135,12 @@ main() {
|
|||
exit 0
|
||||
fi
|
||||
|
||||
# Step 8: Deploy services
|
||||
log_step "8. Deploying services..."
|
||||
# Step 9: Deploy services
|
||||
log_step "9. Deploying services..."
|
||||
deploy_changed_services "$CHANGED_SERVICES"
|
||||
|
||||
# Step 9: Verify deployment
|
||||
log_step "9. Verifying deployment..."
|
||||
# Step 10: Verify deployment
|
||||
log_step "10. Verifying deployment..."
|
||||
verify_deployment
|
||||
|
||||
echo ""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue