Add release process, dynamic versioning, and changelog catch-up
Replace hardcoded version strings with build-time injection: - Frontend: Vite __APP_VERSION__ from env or package.json - Backend: APP_VERSION env var exposed via /health endpoint - Docker: build arg propagated through both stages - CI: release workflow extracts version from git tag Document branching strategy and release process in CONTRIBUTING.md. Catch up CHANGELOG with v0.2.0 and Unreleased sections. Sync package.json version to 0.3.0.
This commit is contained in:
parent
84645f0d70
commit
1ff8c5108f
14 changed files with 139 additions and 13 deletions
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
|
|
@ -38,11 +38,16 @@ jobs:
|
|||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: echo "value=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: APP_VERSION=${{ steps.version.outputs.value }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
|
|
|
|||
32
CHANGELOG.md
32
CHANGELOG.md
|
|
@ -4,6 +4,38 @@ All notable changes to Docling Studio will be documented in this file.
|
|||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Chunking support: domain objects, persistence, API endpoints, and frontend Prepare mode
|
||||
- Chunk-to-bbox hover highlighting in Prepare mode
|
||||
- Page filtering and collapsible config in Prepare mode
|
||||
- Feature flipping mechanism
|
||||
- Reusable pagination composable and PaginationBar component
|
||||
- Version display in sidebar, settings page, and health endpoint
|
||||
|
||||
### Fixed
|
||||
|
||||
- Feature flag health check blocked by CORS
|
||||
- Zombie jobs and unprotected JSON parse
|
||||
- Upload error not displayed in DocumentUpload component
|
||||
- Serve API contract: send `to_formats` as repeated form fields
|
||||
- Audit findings: security, robustness, dead code, domain-infra violation
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactored backend to hexagonal architecture for converter extensibility
|
||||
- Added ServeConverter adapter for remote Docling Serve integration
|
||||
- Moved `@vitest/mocker` from dependencies to devDependencies
|
||||
|
||||
## [0.2.0] - 2025-05-14
|
||||
|
||||
### Added
|
||||
|
||||
- Multi-arch Docker image release pipeline (GitHub Actions)
|
||||
- Docker image published to `ghcr.io/scub-france/Docling-Studio`
|
||||
|
||||
## [0.1.0] - 2025-01-01
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -80,6 +80,74 @@ All tests must pass before submitting a PR.
|
|||
4. Describe **what** changed and **why** in the PR description
|
||||
5. Ensure CI passes (tests + build)
|
||||
|
||||
## Branching Strategy
|
||||
|
||||
We follow a simplified Git Flow:
|
||||
|
||||
| Branch | Purpose |
|
||||
|--------|---------|
|
||||
| `main` | Always stable — latest release merged back |
|
||||
| `release/X.Y.Z` | Release preparation (freeze, bugfixes, changelog) |
|
||||
| `feature/*` | New features — PR to `main` |
|
||||
| `fix/*` | Bug fixes — PR to `main` (or `release/*` for pre-release fixes) |
|
||||
| `hotfix/X.Y.Z` | Urgent fix on a released version — PR to `main` |
|
||||
|
||||
Rules:
|
||||
- All PRs target `main` (never stack branches on other feature branches)
|
||||
- `release/*` branches are created from `main` when preparing a release
|
||||
- `hotfix/*` branches are created from the release tag
|
||||
|
||||
## Versioning
|
||||
|
||||
We use [Semantic Versioning](https://semver.org/): `MAJOR.MINOR.PATCH`.
|
||||
|
||||
- **Source of truth**: the git tag (`vX.Y.Z`)
|
||||
- `package.json` version should match the current release branch
|
||||
- The build injects the version automatically (Vite `__APP_VERSION__` for frontend, `APP_VERSION` env var for backend)
|
||||
|
||||
## Release Process
|
||||
|
||||
1. **Create the release branch** from `main`:
|
||||
```bash
|
||||
git checkout main && git pull
|
||||
git checkout -b release/X.Y.Z
|
||||
```
|
||||
|
||||
2. **On the release branch**, only:
|
||||
- Bug fixes
|
||||
- Move `[Unreleased]` to `[X.Y.Z] - YYYY-MM-DD` in `CHANGELOG.md`
|
||||
- Update `version` in `frontend/package.json`
|
||||
|
||||
3. **Merge into `main`** via PR, then **tag on `main`**:
|
||||
```bash
|
||||
git checkout main && git pull
|
||||
git tag vX.Y.Z
|
||||
git push origin vX.Y.Z
|
||||
```
|
||||
|
||||
4. The tag triggers the **release workflow** which builds and pushes the Docker image to `ghcr.io`.
|
||||
|
||||
### Docker Image Tags
|
||||
|
||||
| Tag | Description |
|
||||
|-----|-------------|
|
||||
| `X.Y.Z` | Exact version |
|
||||
| `X.Y` | Latest patch of this minor |
|
||||
| `latest` | Latest stable release |
|
||||
|
||||
### Hotfix
|
||||
|
||||
```bash
|
||||
git checkout vX.Y.Z # from the release tag
|
||||
git checkout -b hotfix/X.Y.Z+1
|
||||
# fix, commit, PR to main
|
||||
git tag vX.Y.Z+1 # tag on main after merge
|
||||
```
|
||||
|
||||
### Changelog
|
||||
|
||||
We follow [Keep a Changelog](https://keepachangelog.com/). Every PR should add a line under `[Unreleased]` in `CHANGELOG.md`. The release branch moves `[Unreleased]` to the versioned section.
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
- Keep PRs focused — one feature or fix per PR
|
||||
|
|
|
|||
|
|
@ -10,15 +10,20 @@
|
|||
# --- Stage 1: Build frontend assets ---
|
||||
FROM node:20-alpine AS frontend-build
|
||||
|
||||
ARG APP_VERSION=dev
|
||||
|
||||
WORKDIR /build
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm ci
|
||||
COPY frontend/ .
|
||||
RUN npm run build
|
||||
RUN VITE_APP_VERSION=${APP_VERSION} npm run build
|
||||
|
||||
# --- Stage 2: Runtime (Python + Nginx) ---
|
||||
FROM python:3.12-slim
|
||||
|
||||
ARG APP_VERSION=dev
|
||||
ENV APP_VERSION=${APP_VERSION}
|
||||
|
||||
# System deps: poppler (pdf2image), nginx, and OpenCV runtime libs
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
poppler-utils \
|
||||
|
|
|
|||
|
|
@ -167,14 +167,11 @@ GitHub Actions pipelines (see [`.github/workflows/`](.github/workflows/)):
|
|||
|
||||
| Workflow | Trigger | What it does |
|
||||
|----------|---------|--------------|
|
||||
| **CI** | push to `main`, pull requests | Lint + type check + Backend tests (99) + Frontend tests (81) + build |
|
||||
| **CI** | push to `main`, pull requests | Lint + type check + Backend tests + Frontend tests + build |
|
||||
| **Release** | push tag `v*` | Build & push multi-arch Docker image to `ghcr.io` |
|
||||
| **Docs** | push to `main` (docs changes) | Build & deploy MkDocs to GitHub Pages |
|
||||
|
||||
To publish a new version:
|
||||
```bash
|
||||
git tag v0.2.0
|
||||
git push origin v0.2.0
|
||||
```
|
||||
We follow [Semantic Versioning](https://semver.org/) with a simplified Git Flow. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full release process.
|
||||
|
||||
## Performance & System Requirements
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from dataclasses import dataclass, field
|
|||
|
||||
@dataclass(frozen=True)
|
||||
class Settings:
|
||||
app_version: str = "dev"
|
||||
conversion_engine: str = "local" # "local" or "remote"
|
||||
docling_serve_url: str = "http://localhost:5001"
|
||||
docling_serve_api_key: str | None = None
|
||||
|
|
@ -22,6 +23,7 @@ class Settings:
|
|||
def from_env(cls) -> Settings:
|
||||
cors_raw = os.environ.get("CORS_ORIGINS", "http://localhost:3000,http://localhost:5173")
|
||||
return cls(
|
||||
app_version=os.environ.get("APP_VERSION", "dev"),
|
||||
conversion_engine=os.environ.get("CONVERSION_ENGINE", "local"),
|
||||
docling_serve_url=os.environ.get("DOCLING_SERVE_URL", "http://localhost:5001"),
|
||||
docling_serve_api_key=os.environ.get("DOCLING_SERVE_API_KEY"),
|
||||
|
|
|
|||
|
|
@ -106,4 +106,8 @@ app.include_router(analyses_router)
|
|||
@app.get("/health")
|
||||
def health():
|
||||
"""Health check endpoint."""
|
||||
return {"status": "ok", "engine": settings.conversion_engine}
|
||||
return {
|
||||
"status": "ok",
|
||||
"version": settings.app_version,
|
||||
"engine": settings.conversion_engine,
|
||||
}
|
||||
|
|
|
|||
2
frontend/env.d.ts
vendored
2
frontend/env.d.ts
vendored
|
|
@ -1,5 +1,7 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
declare const __APP_VERSION__: string
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ export default [
|
|||
{
|
||||
files: ['src/**/*.{ts,js,vue}'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
__APP_VERSION__: 'readonly',
|
||||
},
|
||||
parserOptions: {
|
||||
parser: tseslint.parser,
|
||||
},
|
||||
|
|
|
|||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "docling-studio",
|
||||
"version": "0.1.0",
|
||||
"version": "0.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "docling-studio",
|
||||
"version": "0.1.0",
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"dompurify": "^3.3.3",
|
||||
"marked": "^17.0.4",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "docling-studio",
|
||||
"version": "0.1.0",
|
||||
"version": "0.3.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
<div class="setting-group">
|
||||
<label class="setting-label">{{ t('settings.version') }}</label>
|
||||
<span class="setting-value">0.1.0</span>
|
||||
<span class="setting-value">{{ version }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
import { useSettingsStore } from '../store'
|
||||
import { useI18n } from '../../../shared/i18n'
|
||||
|
||||
const version = __APP_VERSION__
|
||||
const store = useSettingsStore()
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">v0.1.0</span>
|
||||
<span class="version">v{{ version }}</span>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
import { RouterLink, useRoute } from 'vue-router'
|
||||
import { useI18n } from '../i18n'
|
||||
|
||||
const version = __APP_VERSION__
|
||||
const route = useRoute()
|
||||
const { t } = useI18n()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
const pkg = JSON.parse(readFileSync('./package.json', 'utf-8'))
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
define: {
|
||||
__APP_VERSION__: JSON.stringify(process.env.VITE_APP_VERSION || pkg.version),
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue