Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c6b6d22
ci: Add draft release automation and update actions
Xieyt Jul 2, 2026
0dbb84e
Merge pull request #441 from rtCamp/fix/workflows
Xieyt Jul 2, 2026
e7122f0
fix: add workflow_dispatch to publish-pypi for manual retry
Xieyt Jul 2, 2026
f0e2493
Merge pull request #442 from rtCamp/fix/publish-workflow-dispatch
Xieyt Jul 2, 2026
6ef0005
chore: bump develop version to 0.20.0.dev0
Xieyt Jul 2, 2026
09963f9
Merge pull request #443 from rtCamp/chore/bump-dev-version
Xieyt Jul 2, 2026
c3f9547
fix(docker): add chromium runtime libraries for chrome PDF generator
Xieyt Aug 7, 2026
0b72a31
fix(ci): base release changelog on main to stop dropping released sec…
Xieyt Aug 7, 2026
3c86163
feat(ci): add shared release-notes extraction script
Xieyt Aug 7, 2026
12132f0
fix(ci): set the release version automatically and guard it against t…
Xieyt Aug 7, 2026
37c6845
fix(ci): push release tags with a PAT and drop the duplicated release…
Xieyt Aug 7, 2026
e4a3e1d
ci: build arm64 on GitHub-hosted runners and scope build cache per ref
Xieyt Aug 7, 2026
c49eebb
docs: add changelog preview recipes to the justfile
Xieyt Aug 7, 2026
23cc8ca
fix(migration): add v0.19.1 migration so benches receive the new images
Xieyt Aug 7, 2026
15f4aeb
fix(migration): back up every compose file fm manages, not just docke…
Xieyt Aug 7, 2026
450ce22
fix(migrate): stamp bench migration state even when the release ships…
Xieyt Aug 7, 2026
606b0c4
test(migration): cover the v0.19.1 migration and bench stamping
Xieyt Aug 7, 2026
74e76c1
test(migration): quiet unused-arg lint in the v0.19.1 compose fixture
Xieyt Aug 7, 2026
2d248d3
fix: Recreate running containers in v0.19.1 migration
Xieyt Aug 7, 2026
c637c5d
chore: Remove issue reference from Dockerfile comment
Xieyt Aug 7, 2026
47f6a54
Merge pull request #463 from rtCamp/fix/452
Xieyt Aug 7, 2026
3ca746e
chore(release): prepare for v0.19.1
github-actions[bot] Aug 7, 2026
9d622b8
Merge branch 'main' into release/v0.19.1
Xieyt Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 49 additions & 20 deletions .github/workflows/bake-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,15 @@ jobs:
build-and-push:
name: Build and push image
strategy:
# One arch failing must not cancel the other.
fail-fast: false
matrix:
os: [self-hosted-arm64,ubuntu-latest]
platform: [linux/amd64, linux/arm64]
service_name: [frappe, nginx]
exclude:
- os: ubuntu-latest
platform: linux/arm64
- os: self-hosted-arm64
platform: linux/amd64
target:
- { os: ubuntu-latest, platform: linux/amd64, arch: amd64 }
- { os: ubuntu-24.04-arm, platform: linux/arm64, arch: arm64 }

runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.target.os }}
steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down Expand Up @@ -70,7 +68,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
platforms: ${{ matrix.platform }}
platforms: ${{ matrix.target.platform }}

- name: Login to GitHub Container Registry
uses: docker/login-action@v4
Expand All @@ -86,37 +84,68 @@ jobs:
version=$(grep '^__version__' frappe_manager/__about__.py | sed 's/__version__ = "\(.*\)"/\1/')
tag="v${version}"
echo "Image tag $tag"
echo "image_name=ghcr.io/${owner}/frappe-manager-${{ matrix.service_name }}:$(basename ${{ matrix.platform }})-${tag}" >> $GITHUB_ENV
echo "image_name=ghcr.io/${owner}/frappe-manager-${{ matrix.service_name }}:${{ matrix.target.arch }}-${tag}" >> $GITHUB_ENV

- name: Set owner
run: |
echo "owned=$( echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]' )" >> $GITHUB_ENV

# cache-from needs a multi-line value, which a ${{ }} expression cannot
# produce, so the refs are assembled here.
- name: Compute build cache refs
env:
REF_NAME: ${{ github.ref_name }}
EVENT_NAME: ${{ github.event_name }}
REF_TYPE: ${{ github.ref_type }}
ARCH: ${{ matrix.target.arch }}
SERVICE: ${{ matrix.service_name }}
run: |
# Tag and nightly builds both build the latest release tag, so they
# share one scope instead of leaving a dead cache ref per release.
if [ "$EVENT_NAME" = "schedule" ] || [ "$REF_TYPE" = "tag" ]; then
scope="release"
else
# OCI tags cannot contain '/', so refs like dockerimage/1 must be
# flattened. printf avoids tr eating the trailing newline.
scope=$(printf '%s' "$REF_NAME" | tr -c 'a-zA-Z0-9_.-' '-' | cut -c1-100)
fi
echo "Cache scope: $scope"

repo="ghcr.io/${owned}/frappe-manager-${SERVICE}"

# Scoped, then develop's, then the legacy shared ref, so adopting
# scoped caches never forces a cold build. Drop the legacy entry once
# every active branch has populated its own.
{
echo "cache_from<<CACHE_FROM_EOF"
echo "type=registry,ref=${repo}:buildcache-${ARCH}-${scope}"
echo "type=registry,ref=${repo}:buildcache-${ARCH}-develop"
echo "type=registry,ref=${repo}:buildcache-${ARCH}"
echo "CACHE_FROM_EOF"
} >> "$GITHUB_ENV"

# Write scope-only, so no branch can clobber another's cache.
echo "cache_to=type=registry,ref=${repo}:buildcache-${ARCH}-${scope},mode=max" >> "$GITHUB_ENV"

- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: Docker/${{ matrix.service_name }}/.
push: true
platforms: ${{ matrix.platform }}
platforms: ${{ matrix.target.platform }}
tags: ${{ env.image_name }}
provenance: false
cache-from: ${{ env.USE_CACHE == 'true' && format('type=registry,ref=ghcr.io/{0}/frappe-manager-{1}:buildcache-{2}', env.owned, matrix.service_name, matrix.platform == 'linux/amd64' && 'amd64' || 'arm64') || '' }}
cache-to: ${{ format('type=registry,ref=ghcr.io/{0}/frappe-manager-{1}:buildcache-{2},mode=max', env.owned, matrix.service_name, matrix.platform == 'linux/amd64' && 'amd64' || 'arm64') || '' }}

- name: Prune buildx cache (self-hosted only)
if: startsWith(matrix.os, 'self-hosted')
run: |
docker buildx prune -af
cache-from: ${{ env.USE_CACHE == 'true' && env.cache_from || '' }}
cache-to: ${{ env.cache_to }}

combine:
name: Combine both platform images
needs: build-and-push
strategy:
matrix:
os: [ubuntu-latest]
service_name: [frappe, nginx]

runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down
25 changes: 13 additions & 12 deletions .github/workflows/draft-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

- uses: actions/checkout@v7

- uses: astral-sh/setup-uv@v8
- uses: astral-sh/setup-uv@v8.2.0

- name: Run tests
run: uv run --frozen pytest tests/ -v --cov=frappe_manager
Expand All @@ -33,7 +33,7 @@ jobs:
steps:
- uses: actions/checkout@v7

- uses: astral-sh/setup-uv@v8
- uses: astral-sh/setup-uv@v8.2.0

- name: Build
run: uv build
Expand All @@ -53,22 +53,23 @@ jobs:
fetch-depth: 0

- name: Download dist artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: dist
path: dist/

- name: Extract release notes from changelog
# The tag drives the package version and the published image tag.
- name: Verify version matches tag
run: |
VERSION="${GITHUB_REF_NAME#v}"
python3 -c "
import re, sys
with open('docs/changelog.md') as f:
content = f.read()
pattern = rf'(## v{re.escape(sys.argv[1])}.*?)(?=\n## v|\Z)'
match = re.search(pattern, content, re.DOTALL)
print(match.group(1).strip() if match else f'Release {sys.argv[1]}')
" "$VERSION" > release-notes.md
DECLARED=$(sed -n 's/^__version__ = "\(.*\)"/\1/p' frappe_manager/__about__.py)
if [ "$DECLARED" != "$VERSION" ]; then
echo "::error::tag ${GITHUB_REF_NAME} does not match frappe_manager/__about__.py ($DECLARED)"
exit 1
fi

- name: Extract release notes from changelog
run: python3 scripts/release_notes.py "${GITHUB_REF_NAME#v}" --output release-notes.md

- name: Create draft GitHub Release
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
with:
python-version: '3.x'
- name: Install uv
uses: astral-sh/setup-uv@v8
uses: astral-sh/setup-uv@v8.2.0
with:
version: "latest"
- name: Install dependencies
Expand Down
23 changes: 21 additions & 2 deletions .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ name: Publish to PyPI
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g. v0.19.0)'
required: true

jobs:
publish:
Expand All @@ -12,18 +17,32 @@ jobs:
id-token: write
contents: read
steps:
- name: Set tag
id: tag
env:
TRIGGER: ${{ github.event_name }}
INPUT_TAG: ${{ github.event.inputs.tag }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
if [ "$TRIGGER" = "workflow_dispatch" ]; then
echo "value=$INPUT_TAG" >> "$GITHUB_OUTPUT"
else
echo "value=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
fi

- name: Download dist from release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.value }}
run: |
mkdir dist
gh release download "${{ github.event.release.tag_name }}" \
gh release download "$TAG" \
--repo "${{ github.repository }}" \
--dir dist/ \
--pattern "*.whl" \
--pattern "*.tar.gz"

- uses: astral-sh/setup-uv@v8
- uses: astral-sh/setup-uv@v8.2.0

- name: Publish to PyPI
run: uv publish
43 changes: 39 additions & 4 deletions .github/workflows/release-prepare.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ jobs:
exit 1
fi

# The cumulative changelog is only ever committed to main, so develop's copy
# is stale. Prepending onto it would drop every section main has since
# accumulated, and then conflict with main at merge time.
- name: Base changelog on main
run: |
git fetch origin main
git checkout origin/main -- docs/changelog.md
echo "Sections currently in the changelog:"
grep -c '^## v' docs/changelog.md

- name: Update changelog
uses: orhun/git-cliff-action@v4
with:
Expand All @@ -38,6 +48,27 @@ jobs:
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# hatchling reads the package version from this file, and bake-images.yml
# derives the published image tag from it, so a release branch left at
# develop's dev version would publish images under the dev tag.
- name: Set release version
env:
VERSION: ${{ github.event.inputs.version }}
run: |
sed -i "s/^__version__ = \".*\"/__version__ = \"${VERSION}\"/" frappe_manager/__about__.py

DECLARED=$(sed -n 's/^__version__ = "\(.*\)"/\1/p' frappe_manager/__about__.py)
if [ "$DECLARED" != "$VERSION" ]; then
echo "::error::failed to set version in frappe_manager/__about__.py (got '$DECLARED')"
exit 1
fi
echo "frappe_manager/__about__.py -> $DECLARED"

- name: Verify changelog contains the new section
env:
VERSION: ${{ github.event.inputs.version }}
run: python3 scripts/release_notes.py "$VERSION" >/dev/null

- name: Open release PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -47,7 +78,7 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
git add docs/changelog.md
git add docs/changelog.md frappe_manager/__about__.py
git commit -m "chore(release): prepare for v${VERSION}"
git push origin "$BRANCH"
gh pr create \
Expand All @@ -57,7 +88,11 @@ jobs:
--body "## Release v${VERSION}

Review the generated changelog entries below before merging.
Merging this PR will automatically create the \`v${VERSION}\` tag and trigger the publish workflow.
Merging this PR creates the \`v${VERSION}\` tag, which triggers the draft
release and the image build.

\`docs/changelog.md\` is based on main's copy and \`frappe_manager/__about__.py\`
has been set to \`${VERSION}\` automatically.

- [ ] Changelog entries look correct
- [ ] Version in \`pyproject.toml\` is \`${VERSION}\`"
- [ ] Changelog entries look correct and complete
- [ ] No entries from previous releases have been duplicated"
21 changes: 20 additions & 1 deletion .github/workflows/tag-on-release-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,36 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
# A tag pushed with the default GITHUB_TOKEN does not start new workflow
# runs, which would leave draft-release.yml and bake-images.yml unfired.
token: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}

# Tagging only. draft-release.yml owns building and creating the release off
# the resulting tag push.
- name: Create and push tag
env:
HEAD_REF: ${{ github.head_ref }}
HAS_PAT: ${{ secrets.RELEASE_PAT != '' }}
run: |
VERSION=$(echo "$HEAD_REF" | sed 's|^release/||')

if ! echo "$VERSION" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "Error: branch '$HEAD_REF' does not match release/vX.Y.Z — aborting tag creation."
echo "Error: branch '$HEAD_REF' does not match release/vX.Y.Z. Aborting."
exit 1
fi

DECLARED=$(sed -n 's/^__version__ = "\(.*\)"/\1/p' frappe_manager/__about__.py)
if [ "v$DECLARED" != "$VERSION" ]; then
echo "::error::tag $VERSION does not match frappe_manager/__about__.py ($DECLARED). Images would publish under the wrong tag."
exit 1
fi

git tag "$VERSION"
git push origin "$VERSION"
echo "Pushed $VERSION"

if [ "$HAS_PAT" != "true" ]; then
echo "::warning::RELEASE_PAT is not configured, so this tag was pushed with GITHUB_TOKEN and will NOT trigger draft-release.yml or bake-images.yml. Run both manually via workflow_dispatch against the $VERSION tag."
fi
15 changes: 15 additions & 0 deletions Docker/frappe/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-instal
fonts-powerline \
file \
unzip \
# Chromium runtime libs for frappe's chrome PDF generator (frappe downloads
# headless_shell itself). jammy has no chromium-headless-shell package and
# chromium-browser is a snap stub, so the libs are listed individually
libnss3 \
libnspr4 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libatspi2.0-0 \
libxcomposite1 \
libxdamage1 \
libxfixes3 \
libxrandr2 \
libgbm1 \
libxkbcommon0 \
libasound2 \
&& rm -rf /var/lib/apt/lists/*

RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
Expand Down
29 changes: 0 additions & 29 deletions development.md

This file was deleted.

Loading