Add release versioning and Docker release workflow

This commit is contained in:
Sergey Filkin
2026-04-17 23:44:16 +03:00
parent 0fe596383e
commit 94a3e102b4
8 changed files with 129 additions and 1 deletions
+61
View File
@@ -0,0 +1,61 @@
name: Release Docker Image
on:
release:
types:
- published
permissions:
contents: read
packages: write
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Derive image name
id: vars
run: echo "image=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.vars.outputs.image }}
flavor: |
latest=false
tags: |
type=raw,value=latest,enable=${{ github.event.release.prerelease == false }}
type=semver,pattern={{version}},value=${{ github.event.release.tag_name }}
type=semver,pattern={{major}}.{{minor}},value=${{ github.event.release.tag_name }}
type=semver,pattern={{major}},value=${{ github.event.release.tag_name }}
labels: |
org.opencontainers.image.title=md-to-html
org.opencontainers.image.description=Markdown to standalone HTML converter
org.opencontainers.image.vendor=${{ github.repository_owner }}
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.version=${{ github.event.release.tag_name }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
+14
View File
@@ -0,0 +1,14 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and the project uses Semantic Versioning.
## [0.1.0] - 2026-04-17
### Added
- FastAPI service with `/convert`, `/health`, and `/ready` endpoints.
- Streamlit UI for uploading Markdown, previewing rendered HTML, and downloading output.
- Docker image packaging for running the API and UI together.
- GitHub Actions workflow for building and publishing Docker images to GitHub Container Registry on every GitHub release.
+28
View File
@@ -2,6 +2,8 @@
Сервис конвертации Markdown в самодостаточный HTML (через GitHub API). Сервис конвертации Markdown в самодостаточный HTML (через GitHub API).
Текущая версия: `0.1.0`
Часто нужен адекватно (минималистично) выглядящий HTML из Markdown. HTML получем через открытый API GitHub, а стили просто захардкожены в шаблоне. Часто нужен адекватно (минималистично) выглядящий HTML из Markdown. HTML получем через открытый API GitHub, а стили просто захардкожены в шаблоне.
![Streamlit UI](screen.png) ![Streamlit UI](screen.png)
@@ -51,3 +53,29 @@ curl -X POST http://localhost:8000/convert \
```bash ```bash
curl http://localhost:8000/health curl http://localhost:8000/health
``` ```
`GET /version`
```bash
curl http://localhost:8000/version
```
## Релизы
Проект использует Semantic Versioning. Текущая версия хранится в файле `VERSION`, история изменений ведётся в `CHANGELOG.md`.
Чтобы выпустить релиз:
```bash
git add VERSION CHANGELOG.md
git commit -m "Release v0.1.0"
git tag v0.1.0
git push origin main --tags
gh release create v0.1.0 --notes-file CHANGELOG.md
```
После публикации релиза GitHub Actions автоматически собирает Docker-образ и публикует его в GitHub Container Registry:
```bash
docker pull ghcr.io/fserg/md-to-html:v0.1.0
```
+1
View File
@@ -0,0 +1 @@
0.1.0
+4
View File
@@ -1 +1,5 @@
"""Application package for the md-to-html service.""" """Application package for the md-to-html service."""
from app.version import __version__
__all__ = ["__version__"]
+6
View File
@@ -10,6 +10,7 @@ from fastapi.responses import JSONResponse
from pydantic import BaseModel, ConfigDict, field_validator from pydantic import BaseModel, ConfigDict, field_validator
from app.converter import convert, load_template_text from app.converter import convert, load_template_text
from app.version import __version__
DEFAULT_MAX_MARKDOWN_BYTES = 1_048_576 DEFAULT_MAX_MARKDOWN_BYTES = 1_048_576
DEFAULT_MAX_REQUEST_BYTES = 1_200_000 DEFAULT_MAX_REQUEST_BYTES = 1_200_000
@@ -154,6 +155,11 @@ async def health() -> dict[str, str]:
return {"status": "ok"} return {"status": "ok"}
@app.get("/version")
async def version() -> dict[str, str]:
return {"version": __version__}
@app.get("/ready") @app.get("/ready")
async def ready() -> dict[str, Any]: async def ready() -> dict[str, Any]:
details: dict[str, Any] = {"status": "ok", "template_loaded": True} details: dict[str, Any] = {"status": "ok", "template_loaded": True}
+5 -1
View File
@@ -10,9 +10,11 @@ import streamlit as st
try: try:
from app.converter import convert from app.converter import convert
from app.version import __version__
except ModuleNotFoundError: except ModuleNotFoundError:
sys.path.append(str(Path(__file__).resolve().parent.parent)) sys.path.append(str(Path(__file__).resolve().parent.parent))
from app.converter import convert from app.converter import convert
from app.version import __version__
MAX_PREVIEW_STORE_ITEMS = 20 MAX_PREVIEW_STORE_ITEMS = 20
@@ -146,7 +148,9 @@ if "preview_url" not in st.session_state:
st.session_state["preview_url"] = None st.session_state["preview_url"] = None
st.title("Markdown → HTML") st.title("Markdown → HTML")
st.caption("Загрузите markdown-файл, проверьте превью и скачайте готовый HTML.") st.caption(
f"Версия {__version__}. Загрузите markdown-файл, проверьте превью и скачайте готовый HTML."
)
uploaded_file = st.file_uploader( uploaded_file = st.file_uploader(
"Загрузите .md файл", "Загрузите .md файл",
+10
View File
@@ -0,0 +1,10 @@
from pathlib import Path
VERSION_FILE = Path(__file__).resolve().parent.parent / "VERSION"
def read_version() -> str:
return VERSION_FILE.read_text(encoding="utf-8").strip()
__version__ = read_version()