Release #44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: LibreFolio Release | |
| run-name: Release ${{ github.event.release.tag_name }} | |
| on: | |
| push: | |
| branches: | |
| - dev | |
| workflow_dispatch: | |
| release: | |
| types: [published] | |
| permissions: | |
| contents: write # Required to push to the gh-pages branch with mkdocs gh-deploy | |
| packages: write # Required to push images to GHCR | |
| jobs: | |
| release-pipeline: | |
| name: Full Release Pipeline | |
| runs-on: ubuntu-latest | |
| steps: | |
| # ========================================== | |
| # 1. BASE SETUP (Checkout & Environments) | |
| # ========================================== | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 # Required for mkdocs gh-deploy (history and branches) | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.13' | |
| - name: Install Pipenv | |
| run: pip install pipenv | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: '24' | |
| # ========================================== | |
| # 2. CACHES | |
| # ========================================== | |
| - name: Cache Pipenv | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.local/share/virtualenvs | |
| key: ${{ runner.os }}-pipenv-${{ hashFiles('**/Pipfile.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pipenv- | |
| - name: Cache NPM | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.npm | |
| key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-node- | |
| # For Playwright we use a fixed cache per version | |
| - name: Get Playwright version (from package.json) | |
| id: playwright-version | |
| working-directory: ./frontend | |
| run: echo "PLAYWRIGHT_VERSION=$(node -p "require('./package.json').devDependencies['@playwright/test'].replace(/[\^~]/g, '')" || echo 'fallback')" >> $GITHUB_ENV | |
| - name: Cache Playwright Browsers | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }} | |
| # ========================================== | |
| # 3. INSTALLATION & ASSETS BUILD | |
| # ========================================== | |
| - name: Install Python Dependencies | |
| run: pipenv install --dev | |
| - name: "[INFO] Environment summary before install" | |
| run: | | |
| pwd | |
| ls -la | |
| node -v | |
| npm -v | |
| python --version | |
| pipenv --version | |
| git log -1 --oneline | |
| git status --short | |
| node -p "require('./package.json').devDependencies?.['@playwright/test'] || require('./package.json').dependencies?.['@playwright/test'] || 'None'" || true | |
| node -p "require('./frontend/package.json').devDependencies?.['@playwright/test'] || require('./frontend/package.json').dependencies?.['@playwright/test'] || 'None'" || true | |
| ls -la ~/.cache/ms-playwright || true | |
| find ~/.cache/ms-playwright -maxdepth 2 -type d | sort || true | |
| - name: Install Frontend & Playwright Dependencies (via dev.py) | |
| timeout-minutes: 15 | |
| run: pipenv run ./dev.py install | |
| - name: "[INFO] Playwright installation check" | |
| run: | | |
| npm --prefix frontend exec -- playwright --version || true | |
| - name: Build SvelteKit Frontend | |
| # Essential for the gallery and the Dockerfile which COPYs frontend/build/ | |
| run: pipenv run ./dev.py front build | |
| - name: Check MkDocs translation structure consistency | |
| continue-on-error: ${{ github.ref_name == 'dev' }} | |
| run: pipenv run ./dev.py mkdocs translate-diff --issues-only | |
| - name: Validate broken MkDocs translation links | |
| continue-on-error: ${{ github.ref_name == 'dev' }} | |
| run: pipenv run ./dev.py mkdocs translate-validate --hide-localized | |
| - name: Validate cross-boundary links | |
| continue-on-error: ${{ github.ref_name == 'dev' }} | |
| run: pipenv run ./dev.py mkdocs check-links | |
| - name: Generate Screenshots with Playwright | |
| continue-on-error: ${{ github.ref_name == 'dev' }} | |
| timeout-minutes: 120 | |
| # ubuntu-latest (public repo) runners have 4 vCPU. --workers 8 was 2x | |
| # oversubscribed, causing heavy CPU contention across Chromium instances | |
| # + backend uvicorn workers + node build processes — this was the root | |
| # cause of inconsistent per-test timeouts (a different test would time | |
| # out on each run). Matched to actual CPU count for stable, predictable | |
| # per-test timing (verified locally: --workers 2-3 gives a constant, | |
| # non-degrading ~21s/interaction rate with zero flakiness). | |
| run: pipenv run ./dev.py mkdocs gallery --workers 4 | |
| - name: Build MkDocs Documentation | |
| continue-on-error: ${{ github.ref_name == 'dev' }} | |
| run: pipenv run ./dev.py mkdocs build | |
| - name: Generate requirements.txt for Docker | |
| # The Dockerfile installs dependencies from a native requirements.txt | |
| run: pipenv requirements > requirements.txt | |
| - name: Generate VERSION file for Docker | |
| # The Dockerfile COPYs a VERSION file (gitignored build artifact) so the | |
| # image can report its version at runtime — the image has no .git/, so | |
| # git describe cannot run there. Freeze it here on the host (fetch-depth:0 | |
| # gives us tags) reusing the same get_git_version() the app uses. | |
| run: pipenv run python -c "from pathlib import Path; from backend.app.utils.version import get_git_version; get_git_version.cache_clear(); Path('VERSION').write_text(get_git_version())" | |
| # ========================================== | |
| # 4. DOCKER BUILD & PUSH (Single Image) | |
| # ========================================== | |
| - name: Setup QEMU | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Setup Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: GHCR Authentication | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract Docker Metadata (Version) | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| images: ghcr.io/librefolio/librefolio | |
| tags: | | |
| type=semver,pattern={{version}} | |
| type=raw,value=latest,enable=${{ github.ref_name == 'main' }} | |
| type=raw,value=nightly,enable=${{ github.ref_name == 'dev' }} | |
| labels: | | |
| org.opencontainers.image.title=LibreFolio | |
| org.opencontainers.image.description=LibreFolio: Open Source Personal Finance & Portfolio Tracker | |
| - name: Build and Push Docker Image | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # ========================================== | |
| # 5. GITHUB PAGES DEPLOY | |
| # ========================================== | |
| - name: Setup Git User | |
| run: | | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Deploy to gh-pages branch | |
| if: github.ref_name == 'main' || github.event_name == 'release' | |
| run: pipenv run ./dev.py mkdocs deploy | |
| # ========================================== | |
| # 6. ARTIFACTS (Optional, for archival) | |
| # ========================================== | |
| - name: Archive Generated Screenshots | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: playwright-screenshots | |
| path: | | |
| mkdocs_src/docs/gallery/**/*.png | |
| retention-days: 1 | |
| # ========================================== | |
| # 7. RELEASE NOTES UPDATE | |
| # ========================================== | |
| - name: Append Docker info to Release Notes | |
| if: github.event.release.tag_name != '' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG_NAME="${{ github.event.release.tag_name }}" | |
| DOCKER_CMD=$(printf '\n\n## 📦 Docker Installation\n\n```bash\n# Install the latest version\ndocker pull ghcr.io/librefolio/librefolio:latest\n\n# Or install this specific release\ndocker pull ghcr.io/librefolio/librefolio:%s\n```\n' "$TAG_NAME") | |
| # gh CLI has no --notes-append flag: fetch current notes and append manually | |
| CURRENT_NOTES=$(gh release view "$TAG_NAME" --json body -q .body) | |
| gh release edit "$TAG_NAME" --notes "${CURRENT_NOTES}${DOCKER_CMD}" | |