From 94a3e102b46c3f130faea5d50693998d6c21c122 Mon Sep 17 00:00:00 2001 From: Sergey Filkin Date: Fri, 17 Apr 2026 23:44:16 +0300 Subject: [PATCH] Add release versioning and Docker release workflow --- .github/workflows/release-docker.yml | 61 ++++++++++++++++++++++++++++ CHANGELOG.md | 14 +++++++ README.md | 28 +++++++++++++ VERSION | 1 + app/__init__.py | 4 ++ app/api.py | 6 +++ app/streamlit_app.py | 6 ++- app/version.py | 10 +++++ 8 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release-docker.yml create mode 100644 CHANGELOG.md create mode 100644 VERSION create mode 100644 app/version.py diff --git a/.github/workflows/release-docker.yml b/.github/workflows/release-docker.yml new file mode 100644 index 0000000..9f14ba3 --- /dev/null +++ b/.github/workflows/release-docker.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a9ae042 --- /dev/null +++ b/CHANGELOG.md @@ -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. diff --git a/README.md b/README.md index c85b1a6..962a261 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Сервис конвертации Markdown в самодостаточный HTML (через GitHub API). +Текущая версия: `0.1.0` + Часто нужен адекватно (минималистично) выглядящий HTML из Markdown. HTML получем через открытый API GitHub, а стили просто захардкожены в шаблоне. ![Streamlit UI](screen.png) @@ -51,3 +53,29 @@ curl -X POST http://localhost:8000/convert \ ```bash 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 +``` diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/app/__init__.py b/app/__init__.py index 021f655..7cbe2a7 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1 +1,5 @@ """Application package for the md-to-html service.""" + +from app.version import __version__ + +__all__ = ["__version__"] diff --git a/app/api.py b/app/api.py index 2fb2e4b..ebebe62 100644 --- a/app/api.py +++ b/app/api.py @@ -10,6 +10,7 @@ from fastapi.responses import JSONResponse from pydantic import BaseModel, ConfigDict, field_validator from app.converter import convert, load_template_text +from app.version import __version__ DEFAULT_MAX_MARKDOWN_BYTES = 1_048_576 DEFAULT_MAX_REQUEST_BYTES = 1_200_000 @@ -154,6 +155,11 @@ async def health() -> dict[str, str]: return {"status": "ok"} +@app.get("/version") +async def version() -> dict[str, str]: + return {"version": __version__} + + @app.get("/ready") async def ready() -> dict[str, Any]: details: dict[str, Any] = {"status": "ok", "template_loaded": True} diff --git a/app/streamlit_app.py b/app/streamlit_app.py index 10d42ac..89fef26 100644 --- a/app/streamlit_app.py +++ b/app/streamlit_app.py @@ -10,9 +10,11 @@ import streamlit as st try: from app.converter import convert + from app.version import __version__ except ModuleNotFoundError: sys.path.append(str(Path(__file__).resolve().parent.parent)) from app.converter import convert + from app.version import __version__ MAX_PREVIEW_STORE_ITEMS = 20 @@ -146,7 +148,9 @@ if "preview_url" not in st.session_state: st.session_state["preview_url"] = None st.title("Markdown → HTML") -st.caption("Загрузите markdown-файл, проверьте превью и скачайте готовый HTML.") +st.caption( + f"Версия {__version__}. Загрузите markdown-файл, проверьте превью и скачайте готовый HTML." +) uploaded_file = st.file_uploader( "Загрузите .md файл", diff --git a/app/version.py b/app/version.py new file mode 100644 index 0000000..4c6bf3c --- /dev/null +++ b/app/version.py @@ -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()