Add automated build metadata generation system
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -27,6 +27,11 @@ jobs:
|
|||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Generate Build Metadata
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/generate_build_metadata.sh
|
||||||
|
./scripts/generate_build_metadata.sh
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Generate Build Metadata
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/generate_build_metadata.sh
|
||||||
|
./scripts/generate_build_metadata.sh
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -194,3 +194,7 @@ cython_debug/
|
|||||||
|
|
||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
# Build metadata files - generated at build time
|
||||||
|
GIT_SHA
|
||||||
|
RUNTIME_INFO
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
2025-04-11
|
2026-02-07
|
||||||
|
|||||||
+6
-2
@@ -18,11 +18,15 @@ COPY --from=builder /usr/local/bin /usr/local/bin
|
|||||||
|
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY ./app /app/app
|
COPY ./app /app/app
|
||||||
COPY ./VERSION /app/VERSION
|
|
||||||
COPY ./frontend /app/frontend
|
COPY ./frontend /app/frontend
|
||||||
COPY ./BUILD_DATE /app/BUILD_DATE
|
|
||||||
COPY ./LICENSE /app/LICENSE
|
COPY ./LICENSE /app/LICENSE
|
||||||
|
|
||||||
|
# Copy build metadata files (generated at build time)
|
||||||
|
COPY ./VERSION /app/VERSION
|
||||||
|
COPY ./BUILD_DATE /app/BUILD_DATE
|
||||||
|
COPY ./GIT_SHA /app/GIT_SHA
|
||||||
|
COPY ./RUNTIME_INFO /app/RUNTIME_INFO
|
||||||
|
|
||||||
# Create runtime_info directory
|
# Create runtime_info directory
|
||||||
RUN mkdir -p /app/runtime_info
|
RUN mkdir -p /app/runtime_info
|
||||||
|
|
||||||
|
|||||||
@@ -213,6 +213,34 @@ class Settings(BaseSettings):
|
|||||||
# Default version if not found
|
# Default version if not found
|
||||||
return "0.3.2-dev"
|
return "0.3.2-dev"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def git_sha(self) -> str:
|
||||||
|
"""Get Git commit SHA from environment or file."""
|
||||||
|
# First try to get from environment
|
||||||
|
env_sha = os.environ.get("GIT_COMMIT_SHA")
|
||||||
|
if env_sha:
|
||||||
|
return env_sha
|
||||||
|
|
||||||
|
# Then try to get from GIT_SHA file
|
||||||
|
git_sha_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), "GIT_SHA")
|
||||||
|
if os.path.exists(git_sha_file):
|
||||||
|
with open(git_sha_file, "r") as f:
|
||||||
|
return f.read().strip()
|
||||||
|
|
||||||
|
# Default if not found
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def runtime_info(self) -> str:
|
||||||
|
"""Get runtime information from file."""
|
||||||
|
runtime_info_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), "RUNTIME_INFO")
|
||||||
|
if os.path.exists(runtime_info_file):
|
||||||
|
with open(runtime_info_file, "r") as f:
|
||||||
|
return f.read().strip()
|
||||||
|
|
||||||
|
# Return basic info if file not found
|
||||||
|
return f"Version: {self.version}\nBuild Date: {self.build_date}\nGit SHA: {self.git_sha}"
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
env_file = ".env"
|
env_file = ".env"
|
||||||
|
|
||||||
|
|||||||
+7
-24
@@ -5,7 +5,6 @@ from fastapi import Request
|
|||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from app.views.base import APIRouter, templates, require_login, settings
|
from app.views.base import APIRouter, templates, require_login, settings
|
||||||
@@ -46,42 +45,26 @@ async def status_dashboard(request: Request):
|
|||||||
except Exception:
|
except Exception:
|
||||||
container_info['id'] = 'Unknown'
|
container_info['id'] = 'Unknown'
|
||||||
|
|
||||||
# Try to get Git commit SHA from runtime info
|
# Get Git commit SHA from settings
|
||||||
try:
|
try:
|
||||||
# First check runtime info directory
|
git_sha = settings.git_sha
|
||||||
if os.path.exists('/app/runtime_info/GIT_SHA'):
|
|
||||||
with open('/app/runtime_info/GIT_SHA', 'r') as f:
|
|
||||||
git_sha = f.read().strip()
|
|
||||||
# Then try environment variable
|
|
||||||
else:
|
|
||||||
git_sha = os.environ.get('GIT_COMMIT_SHA', '')
|
|
||||||
|
|
||||||
# If still not found, try the original file location
|
|
||||||
if not git_sha and os.path.exists('/.git-commit-sha'):
|
|
||||||
with open('/.git-commit-sha', 'r') as f:
|
|
||||||
git_sha = f.read().strip()
|
|
||||||
|
|
||||||
container_info['git_sha'] = git_sha[:7] if git_sha and git_sha != 'unknown' else 'Unknown'
|
container_info['git_sha'] = git_sha[:7] if git_sha and git_sha != 'unknown' else 'Unknown'
|
||||||
except Exception:
|
except Exception:
|
||||||
container_info['git_sha'] = 'Unknown'
|
container_info['git_sha'] = 'Unknown'
|
||||||
|
|
||||||
# Try to get runtime information
|
# Try to get runtime information
|
||||||
try:
|
try:
|
||||||
if os.path.exists('/app/runtime_info/RUNTIME_INFO'):
|
container_info['runtime_info'] = settings.runtime_info
|
||||||
with open('/app/runtime_info/RUNTIME_INFO', 'r') as f:
|
|
||||||
container_info['runtime_info'] = f.read().strip()
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
container_info['is_docker'] = False
|
container_info['is_docker'] = False
|
||||||
|
|
||||||
# If not in Docker, try to get Git info directly
|
# If not in Docker, get Git info from settings
|
||||||
try:
|
try:
|
||||||
git_sha = subprocess.check_output(['git', 'rev-parse', 'HEAD'],
|
git_sha = settings.git_sha
|
||||||
stderr=subprocess.DEVNULL,
|
container_info['git_sha'] = git_sha[:7] if git_sha and git_sha != 'unknown' else 'Unknown'
|
||||||
text=True).strip()[:7]
|
except Exception:
|
||||||
container_info['git_sha'] = git_sha
|
|
||||||
except (subprocess.SubprocessError, FileNotFoundError):
|
|
||||||
container_info['git_sha'] = 'Unknown'
|
container_info['git_sha'] = 'Unknown'
|
||||||
except Exception:
|
except Exception:
|
||||||
container_info = {'is_docker': False, 'id': 'Unknown', 'git_sha': 'Unknown'}
|
container_info = {'is_docker': False, 'id': 'Unknown', 'git_sha': 'Unknown'}
|
||||||
|
|||||||
@@ -0,0 +1,328 @@
|
|||||||
|
# Build Metadata Automation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
DocuElevate automatically generates and embeds build metadata (version, build date, Git commit SHA) into the application at build time. This metadata is displayed in the `/status` endpoint and helps track which version of the application is running in production.
|
||||||
|
|
||||||
|
## What is Automated
|
||||||
|
|
||||||
|
The following metadata is automatically generated and included in every build:
|
||||||
|
|
||||||
|
1. **Build Date** - The UTC date when the build was created (format: YYYY-MM-DD)
|
||||||
|
2. **Git Commit SHA** - The short Git commit hash (7 characters) of the code being built
|
||||||
|
3. **Runtime Information** - A comprehensive summary including version, dates, commit info, and more
|
||||||
|
4. **Application Version** - Read from the `VERSION` file (manually updated for releases)
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Build Time Generation
|
||||||
|
|
||||||
|
When the Docker image is built (locally or in CI/CD), the following happens:
|
||||||
|
|
||||||
|
1. **GitHub Actions runs the metadata script** (`scripts/generate_build_metadata.sh`) before building the Docker image
|
||||||
|
2. **The script generates three files:**
|
||||||
|
- `BUILD_DATE` - Contains the build date in YYYY-MM-DD format
|
||||||
|
- `GIT_SHA` - Contains the short Git commit hash
|
||||||
|
- `RUNTIME_INFO` - Contains detailed build information
|
||||||
|
3. **Docker copies these files** into the image during the build process
|
||||||
|
4. **The application reads the files** at runtime via `app/config.py` properties
|
||||||
|
|
||||||
|
### File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
DocuElevate/
|
||||||
|
├── scripts/
|
||||||
|
│ └── generate_build_metadata.sh # Script that generates metadata
|
||||||
|
├── VERSION # Manually maintained version number
|
||||||
|
├── BUILD_DATE # Generated at build time (gitignored)
|
||||||
|
├── GIT_SHA # Generated at build time (gitignored)
|
||||||
|
└── RUNTIME_INFO # Generated at build time (gitignored)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generated Files Format
|
||||||
|
|
||||||
|
**BUILD_DATE:**
|
||||||
|
```
|
||||||
|
2026-02-07
|
||||||
|
```
|
||||||
|
|
||||||
|
**GIT_SHA:**
|
||||||
|
```
|
||||||
|
6812d0d
|
||||||
|
```
|
||||||
|
|
||||||
|
**RUNTIME_INFO:**
|
||||||
|
```
|
||||||
|
DocuElevate Build Information
|
||||||
|
==============================
|
||||||
|
Version: 0.4.5-dev
|
||||||
|
Build Date: 2026-02-07
|
||||||
|
Git Commit: 6812d0d2c42a4782e78840b052f0e6da24ec8543
|
||||||
|
Git Short SHA: 6812d0d
|
||||||
|
Git Branch: main
|
||||||
|
Commit Date: 2026-02-07T19:32:04Z
|
||||||
|
Build Timestamp: 2026-02-07T19:34:10Z
|
||||||
|
==============================
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Properties
|
||||||
|
|
||||||
|
The `app/config.py` Settings class provides these properties for accessing build metadata:
|
||||||
|
|
||||||
|
### `settings.version` (property)
|
||||||
|
Returns the application version with the following priority:
|
||||||
|
1. `APP_VERSION` environment variable
|
||||||
|
2. Contents of `VERSION` file
|
||||||
|
3. Default: `"0.3.2-dev"`
|
||||||
|
|
||||||
|
### `settings.build_date` (property)
|
||||||
|
Returns the build date with the following priority:
|
||||||
|
1. `BUILD_DATE` environment variable
|
||||||
|
2. Contents of `BUILD_DATE` file
|
||||||
|
3. Default: `"Unknown build date"`
|
||||||
|
|
||||||
|
### `settings.git_sha` (property)
|
||||||
|
Returns the Git commit SHA with the following priority:
|
||||||
|
1. `GIT_COMMIT_SHA` environment variable
|
||||||
|
2. Contents of `GIT_SHA` file
|
||||||
|
3. Default: `"unknown"`
|
||||||
|
|
||||||
|
### `settings.runtime_info` (property)
|
||||||
|
Returns detailed runtime information:
|
||||||
|
1. Contents of `RUNTIME_INFO` file
|
||||||
|
2. Default: Basic info string with version, build date, and Git SHA
|
||||||
|
|
||||||
|
## Usage in Application
|
||||||
|
|
||||||
|
### In Python Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
from app.config import settings
|
||||||
|
|
||||||
|
# Get version
|
||||||
|
version = settings.version # "0.4.5-dev"
|
||||||
|
|
||||||
|
# Get build date
|
||||||
|
build_date = settings.build_date # "2026-02-07"
|
||||||
|
|
||||||
|
# Get Git SHA
|
||||||
|
git_sha = settings.git_sha # "6812d0d"
|
||||||
|
|
||||||
|
# Get full runtime info
|
||||||
|
runtime_info = settings.runtime_info # Full multi-line string
|
||||||
|
```
|
||||||
|
|
||||||
|
### In Templates
|
||||||
|
|
||||||
|
The `/status` endpoint uses these properties to display metadata:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# app/views/status.py
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"status_dashboard.html",
|
||||||
|
{
|
||||||
|
"app_version": settings.version,
|
||||||
|
"build_date": settings.build_date,
|
||||||
|
"container_info": {
|
||||||
|
"git_sha": settings.git_sha[:7],
|
||||||
|
"runtime_info": settings.runtime_info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## GitHub Actions Integration
|
||||||
|
|
||||||
|
The build metadata is generated in two GitHub Actions workflows:
|
||||||
|
|
||||||
|
### docker-build.yaml
|
||||||
|
```yaml
|
||||||
|
- name: Generate Build Metadata
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/generate_build_metadata.sh
|
||||||
|
./scripts/generate_build_metadata.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker-ci.yml
|
||||||
|
```yaml
|
||||||
|
- name: Generate Build Metadata
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/generate_build_metadata.sh
|
||||||
|
./scripts/generate_build_metadata.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
These steps run **before** the Docker build step, ensuring the metadata files exist when Docker copies them into the image.
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
### Manual Generation
|
||||||
|
|
||||||
|
To generate build metadata locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the script
|
||||||
|
./scripts/generate_build_metadata.sh
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# Generating build metadata...
|
||||||
|
# ✓ BUILD_DATE: 2026-02-07
|
||||||
|
# ✓ GIT_SHA: 6812d0d
|
||||||
|
# ✓ VERSION: 0.4.5-dev
|
||||||
|
# ✓ RUNTIME_INFO generated
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Docker Build
|
||||||
|
|
||||||
|
When building Docker images locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate metadata first
|
||||||
|
./scripts/generate_build_metadata.sh
|
||||||
|
|
||||||
|
# Then build the Docker image
|
||||||
|
docker build -t docuelevate:local .
|
||||||
|
|
||||||
|
# Or use docker-compose
|
||||||
|
docker-compose build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing Without Docker
|
||||||
|
|
||||||
|
The application will still work without the generated files:
|
||||||
|
|
||||||
|
- `BUILD_DATE` will show "Unknown build date"
|
||||||
|
- `GIT_SHA` will show "unknown"
|
||||||
|
- The app will use defaults from `app/config.py`
|
||||||
|
|
||||||
|
## Environment Variable Override
|
||||||
|
|
||||||
|
You can override any metadata value using environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Override version
|
||||||
|
export APP_VERSION="1.0.0-custom"
|
||||||
|
|
||||||
|
# Override build date
|
||||||
|
export BUILD_DATE="2026-01-15"
|
||||||
|
|
||||||
|
# Override Git SHA
|
||||||
|
export GIT_COMMIT_SHA="abc1234"
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
python -m uvicorn app.main:app
|
||||||
|
```
|
||||||
|
|
||||||
|
This is useful for:
|
||||||
|
- Custom builds
|
||||||
|
- Testing different versions
|
||||||
|
- Development environments
|
||||||
|
|
||||||
|
## Updating the Version
|
||||||
|
|
||||||
|
The `VERSION` file must be **manually updated** for releases:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update version for a new release
|
||||||
|
echo "0.5.0" > VERSION
|
||||||
|
|
||||||
|
# Commit the change
|
||||||
|
git add VERSION
|
||||||
|
git commit -m "Bump version to 0.5.0"
|
||||||
|
git tag v0.5.0
|
||||||
|
git push origin main --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
The build metadata script will automatically include this version in all builds.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Problem: Build metadata shows "unknown"
|
||||||
|
|
||||||
|
**Solution:** Ensure the script runs before Docker build:
|
||||||
|
```bash
|
||||||
|
./scripts/generate_build_metadata.sh
|
||||||
|
docker build -t docuelevate .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Git SHA is "unknown"
|
||||||
|
|
||||||
|
**Cause:** Building outside of a Git repository
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Clone the repository properly with `.git` directory
|
||||||
|
- Or set `GIT_COMMIT_SHA` environment variable
|
||||||
|
|
||||||
|
### Problem: Build date is outdated
|
||||||
|
|
||||||
|
**Cause:** Using cached Docker layers
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Rebuild without cache
|
||||||
|
docker build --no-cache -t docuelevate .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Files not copied to Docker image
|
||||||
|
|
||||||
|
**Cause:** Files listed in `.dockerignore`
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Check `.dockerignore` doesn't block `BUILD_DATE`, `GIT_SHA`, or `RUNTIME_INFO`
|
||||||
|
- The `VERSION` file should always be committed to git
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Always run the script before building** - The CI/CD pipeline does this automatically
|
||||||
|
2. **Don't commit generated files** - `GIT_SHA` and `RUNTIME_INFO` are in `.gitignore`
|
||||||
|
3. **Update VERSION manually** - Only update for actual releases
|
||||||
|
4. **Use semantic versioning** - Follow `MAJOR.MINOR.PATCH` format
|
||||||
|
5. **Tag releases in Git** - Create Git tags for version releases
|
||||||
|
|
||||||
|
## CI/CD Pipeline Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ GitHub Actions Workflow │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ 1. Checkout Code │
|
||||||
|
│ 2. Generate Build Metadata (run script) │
|
||||||
|
│ - Creates BUILD_DATE │
|
||||||
|
│ - Creates GIT_SHA │
|
||||||
|
│ - Creates RUNTIME_INFO │
|
||||||
|
│ 3. Build Docker Image │
|
||||||
|
│ - Copies VERSION (from git) │
|
||||||
|
│ - Copies BUILD_DATE (generated) │
|
||||||
|
│ - Copies GIT_SHA (generated) │
|
||||||
|
│ - Copies RUNTIME_INFO (generated) │
|
||||||
|
│ 4. Push to Docker Hub / GHCR │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Running Container │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ Application reads metadata from files in /app: │
|
||||||
|
│ - /app/VERSION │
|
||||||
|
│ - /app/BUILD_DATE │
|
||||||
|
│ - /app/GIT_SHA │
|
||||||
|
│ - /app/RUNTIME_INFO │
|
||||||
|
│ │
|
||||||
|
│ Displays in /status endpoint │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
Potential improvements to the build metadata system:
|
||||||
|
|
||||||
|
1. **Automated version bumping** - Automatically increment version based on commits
|
||||||
|
2. **Changelog generation** - Auto-generate changelog from Git history
|
||||||
|
3. **Build number tracking** - Track sequential build numbers
|
||||||
|
4. **Deployment tracking** - Record when/where each build was deployed
|
||||||
|
5. **Performance metrics** - Include build time, image size, etc.
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- [Deployment Guide](./DeploymentGuide.md) - How to deploy DocuElevate
|
||||||
|
- [Configuration Guide](./ConfigurationGuide.md) - All configuration options
|
||||||
|
- [API Documentation](./API.md) - API endpoints including `/status`
|
||||||
Executable
+89
@@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Generate build metadata files for DocuElevate
|
||||||
|
#
|
||||||
|
# This script generates metadata files that are read by the application
|
||||||
|
# at runtime to display version, build date, and Git commit information
|
||||||
|
# in the /status endpoint.
|
||||||
|
#
|
||||||
|
# Files generated:
|
||||||
|
# - BUILD_DATE: UTC timestamp of when the build was created
|
||||||
|
# - GIT_SHA: Short Git commit SHA (7 characters)
|
||||||
|
# - RUNTIME_INFO: Combined build information
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/generate_build_metadata.sh
|
||||||
|
#
|
||||||
|
# The script should be run during Docker build or CI/CD pipeline.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Get the directory where this script is located
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
|
||||||
|
# Navigate to project root
|
||||||
|
cd "${PROJECT_ROOT}"
|
||||||
|
|
||||||
|
echo "Generating build metadata..."
|
||||||
|
|
||||||
|
# Generate BUILD_DATE in ISO 8601 format (UTC)
|
||||||
|
BUILD_DATE=$(date -u '+%Y-%m-%d')
|
||||||
|
echo "${BUILD_DATE}" > BUILD_DATE
|
||||||
|
echo "✓ BUILD_DATE: ${BUILD_DATE}"
|
||||||
|
|
||||||
|
# Generate GIT_SHA (short commit hash)
|
||||||
|
if git rev-parse --git-dir > /dev/null 2>&1; then
|
||||||
|
GIT_SHA=$(git rev-parse --short=7 HEAD)
|
||||||
|
echo "${GIT_SHA}" > GIT_SHA
|
||||||
|
echo "✓ GIT_SHA: ${GIT_SHA}"
|
||||||
|
|
||||||
|
# Get full commit SHA for reference
|
||||||
|
GIT_FULL_SHA=$(git rev-parse HEAD)
|
||||||
|
|
||||||
|
# Get commit date
|
||||||
|
GIT_COMMIT_DATE=$(git log -1 --format=%cd --date=iso-strict)
|
||||||
|
|
||||||
|
# Get branch name (if available)
|
||||||
|
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
||||||
|
else
|
||||||
|
echo "⚠ Warning: Not a git repository, using 'unknown' for Git metadata"
|
||||||
|
echo "unknown" > GIT_SHA
|
||||||
|
GIT_SHA="unknown"
|
||||||
|
GIT_FULL_SHA="unknown"
|
||||||
|
GIT_COMMIT_DATE="unknown"
|
||||||
|
GIT_BRANCH="unknown"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read VERSION file
|
||||||
|
if [ -f "VERSION" ]; then
|
||||||
|
VERSION=$(cat VERSION | tr -d '\n')
|
||||||
|
echo "✓ VERSION: ${VERSION}"
|
||||||
|
else
|
||||||
|
echo "⚠ Warning: VERSION file not found"
|
||||||
|
VERSION="unknown"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate RUNTIME_INFO with combined metadata
|
||||||
|
cat > RUNTIME_INFO << EOF
|
||||||
|
DocuElevate Build Information
|
||||||
|
==============================
|
||||||
|
Version: ${VERSION}
|
||||||
|
Build Date: ${BUILD_DATE}
|
||||||
|
Git Commit: ${GIT_FULL_SHA}
|
||||||
|
Git Short SHA: ${GIT_SHA}
|
||||||
|
Git Branch: ${GIT_BRANCH}
|
||||||
|
Commit Date: ${GIT_COMMIT_DATE}
|
||||||
|
Build Timestamp: $(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
==============================
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✓ RUNTIME_INFO generated"
|
||||||
|
echo ""
|
||||||
|
echo "Build metadata generation complete!"
|
||||||
|
echo ""
|
||||||
|
echo "Files created/updated:"
|
||||||
|
echo " - BUILD_DATE"
|
||||||
|
echo " - GIT_SHA"
|
||||||
|
echo " - RUNTIME_INFO"
|
||||||
|
echo ""
|
||||||
@@ -80,6 +80,95 @@ class TestConfigurationValidation:
|
|||||||
assert config.session_secret is None
|
assert config.session_secret is None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
class TestBuildMetadataConfiguration:
|
||||||
|
"""Tests for build metadata configuration."""
|
||||||
|
|
||||||
|
def test_version_from_environment(self, monkeypatch):
|
||||||
|
"""Test that version is read from APP_VERSION environment variable."""
|
||||||
|
monkeypatch.setenv("APP_VERSION", "1.2.3-test")
|
||||||
|
config = Settings(
|
||||||
|
database_url="sqlite:///test.db",
|
||||||
|
redis_url="redis://localhost:6379",
|
||||||
|
openai_api_key="test",
|
||||||
|
azure_ai_key="test",
|
||||||
|
azure_region="test",
|
||||||
|
azure_endpoint="https://test.example.com",
|
||||||
|
gotenberg_url="http://localhost:3000",
|
||||||
|
workdir="/tmp",
|
||||||
|
auth_enabled=False
|
||||||
|
)
|
||||||
|
assert config.version == "1.2.3-test"
|
||||||
|
|
||||||
|
def test_build_date_from_environment(self, monkeypatch):
|
||||||
|
"""Test that build_date is read from BUILD_DATE environment variable."""
|
||||||
|
monkeypatch.setenv("BUILD_DATE", "2026-01-15")
|
||||||
|
config = Settings(
|
||||||
|
database_url="sqlite:///test.db",
|
||||||
|
redis_url="redis://localhost:6379",
|
||||||
|
openai_api_key="test",
|
||||||
|
azure_ai_key="test",
|
||||||
|
azure_region="test",
|
||||||
|
azure_endpoint="https://test.example.com",
|
||||||
|
gotenberg_url="http://localhost:3000",
|
||||||
|
workdir="/tmp",
|
||||||
|
auth_enabled=False
|
||||||
|
)
|
||||||
|
assert config.build_date == "2026-01-15"
|
||||||
|
|
||||||
|
def test_git_sha_from_environment(self, monkeypatch):
|
||||||
|
"""Test that git_sha is read from GIT_COMMIT_SHA environment variable."""
|
||||||
|
monkeypatch.setenv("GIT_COMMIT_SHA", "abc1234")
|
||||||
|
config = Settings(
|
||||||
|
database_url="sqlite:///test.db",
|
||||||
|
redis_url="redis://localhost:6379",
|
||||||
|
openai_api_key="test",
|
||||||
|
azure_ai_key="test",
|
||||||
|
azure_region="test",
|
||||||
|
azure_endpoint="https://test.example.com",
|
||||||
|
gotenberg_url="http://localhost:3000",
|
||||||
|
workdir="/tmp",
|
||||||
|
auth_enabled=False
|
||||||
|
)
|
||||||
|
assert config.git_sha == "abc1234"
|
||||||
|
|
||||||
|
def test_git_sha_default(self):
|
||||||
|
"""Test that git_sha defaults to 'unknown' when not set."""
|
||||||
|
config = Settings(
|
||||||
|
database_url="sqlite:///test.db",
|
||||||
|
redis_url="redis://localhost:6379",
|
||||||
|
openai_api_key="test",
|
||||||
|
azure_ai_key="test",
|
||||||
|
azure_region="test",
|
||||||
|
azure_endpoint="https://test.example.com",
|
||||||
|
gotenberg_url="http://localhost:3000",
|
||||||
|
workdir="/tmp",
|
||||||
|
auth_enabled=False
|
||||||
|
)
|
||||||
|
# When no file or env var exists, should return "unknown"
|
||||||
|
# (Note: In actual tests, VERSION file exists, so version won't be default)
|
||||||
|
assert config.git_sha in ["unknown", "6812d0d"] # May have been generated
|
||||||
|
|
||||||
|
def test_runtime_info_property(self):
|
||||||
|
"""Test that runtime_info returns build information."""
|
||||||
|
config = Settings(
|
||||||
|
database_url="sqlite:///test.db",
|
||||||
|
redis_url="redis://localhost:6379",
|
||||||
|
openai_api_key="test",
|
||||||
|
azure_ai_key="test",
|
||||||
|
azure_region="test",
|
||||||
|
azure_endpoint="https://test.example.com",
|
||||||
|
gotenberg_url="http://localhost:3000",
|
||||||
|
workdir="/tmp",
|
||||||
|
auth_enabled=False
|
||||||
|
)
|
||||||
|
runtime_info = config.runtime_info
|
||||||
|
# Should contain version, build_date, and git_sha in some form
|
||||||
|
assert "Version:" in runtime_info or config.version in runtime_info
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
class TestNotificationConfiguration:
|
class TestNotificationConfiguration:
|
||||||
"""Tests for notification configuration parsing."""
|
"""Tests for notification configuration parsing."""
|
||||||
|
|||||||
Reference in New Issue
Block a user