Skip to content

Commit 70d5d88

Browse files
committed
chore: refactor workflows and scripts for improved version management and clean semver enforcement
1 parent 778cca6 commit 70d5d88

11 files changed

+689
-250
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: bump-chart-version
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
chart_version:
6+
description: "Chart version to set (does not touch appVersion)"
7+
required: true
8+
type: string
9+
ref:
10+
description: "Git ref to bump (default: main)"
11+
required: false
12+
type: string
13+
14+
permissions:
15+
contents: write
16+
pull-requests: write
17+
18+
jobs:
19+
bump:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
with:
24+
fetch-depth: 0
25+
ref: ${{ inputs.ref || 'main' }}
26+
27+
- name: Setup Python
28+
uses: actions/setup-python@v5
29+
with:
30+
python-version: '3.13'
31+
32+
- name: Install deps
33+
run: |
34+
python -m pip install --upgrade pip
35+
python -m pip install "pyyaml==6.0.2" "packaging==25.0"
36+
37+
- name: Bump chart version only
38+
env:
39+
CHART_VERSION: ${{ inputs.chart_version }}
40+
run: |
41+
if [ -z "${CHART_VERSION}" ]; then
42+
echo "chart_version input is required" >&2
43+
exit 1
44+
fi
45+
python tools/bump_chart_versions.py --mode chart-only --chart-version "$CHART_VERSION"
46+
47+
- name: Open PR for chart version bump
48+
uses: peter-evans/create-pull-request@v6
49+
with:
50+
base: main
51+
branch: chore/chart-bump-${{ inputs.chart_version }}
52+
title: "chore(release): bump chart version to ${{ inputs.chart_version }}"
53+
body: |
54+
Bump Chart.yaml version to ${{ inputs.chart_version }} (appVersion unchanged).
55+
commit-message: "chore(release): bump chart version to ${{ inputs.chart_version }}"
56+
add-paths: |
57+
infrastructure/**/Chart.yaml
58+
labels: chart-bump

.github/workflows/create-release.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,24 @@ jobs:
3131
fi
3232
echo "version=$VERSION" >> $GITHUB_OUTPUT
3333
34+
- name: Verify appVersion matches release version (clean semver)
35+
env:
36+
RELEASE_VERSION: ${{ steps.ver.outputs.version }}
37+
run: |
38+
if echo "$RELEASE_VERSION" | grep -q '\.post'; then
39+
echo "Release version must be clean semver (no .post): $RELEASE_VERSION" >&2
40+
exit 1
41+
fi
42+
APP_VERSION=$(awk '/^appVersion:/ {print $2}' infrastructure/rag/Chart.yaml | tr -d "\"'")
43+
if [ -z "$APP_VERSION" ]; then
44+
echo "Could not read appVersion from infrastructure/rag/Chart.yaml" >&2
45+
exit 1
46+
fi
47+
if [ "$APP_VERSION" != "$RELEASE_VERSION" ]; then
48+
echo "Chart appVersion ($APP_VERSION) does not match release version ($RELEASE_VERSION)" >&2
49+
exit 1
50+
fi
51+
3452
- name: Create Git tag
3553
run: |
3654
git config user.name "github-actions"

.github/workflows/prepare-release.yml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,36 @@ permissions:
1010
pull-requests: write
1111

1212
jobs:
13-
prepare:
13+
changes:
1414
if: >-
1515
${{
1616
github.event.pull_request.merged &&
1717
!contains(github.event.pull_request.labels.*.name, 'prepare-release') &&
1818
!contains(github.event.pull_request.labels.*.name, 'refresh-locks') &&
1919
!contains(github.event.pull_request.labels.*.name, 'chart-bump')
2020
}}
21+
name: Detect release-relevant changes
22+
runs-on: ubuntu-latest
23+
outputs:
24+
releasable: ${{ steps.filter.outputs.releasable }}
25+
steps:
26+
- uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 0
29+
30+
- name: Filter paths
31+
id: filter
32+
uses: dorny/paths-filter@v2
33+
with:
34+
filters: |
35+
releasable:
36+
- 'services/**'
37+
- 'libs/**'
38+
39+
prepare:
40+
if: ${{ needs.changes.outputs.releasable == 'true' }}
2141
runs-on: ubuntu-latest
42+
needs: [changes]
2243
steps:
2344
- uses: actions/checkout@v4
2445
with:
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
name: promote-clean-semver
2+
on:
3+
workflow_run:
4+
workflows: ["publish-pre-and-qa"]
5+
types: [completed]
6+
workflow_dispatch:
7+
inputs:
8+
clean_version:
9+
description: "Clean semver to promote (no .post)"
10+
required: true
11+
type: string
12+
13+
permissions:
14+
contents: write
15+
pull-requests: write
16+
packages: write
17+
18+
jobs:
19+
promote:
20+
name: Publish clean semver, refresh locks, bump appVersion
21+
if: |
22+
github.event_name == 'workflow_dispatch' ||
23+
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 0
29+
ref: ${{ inputs.ref || 'main' }}
30+
31+
- name: Load version metadata (workflow_run)
32+
if: ${{ github.event_name == 'workflow_run' }}
33+
uses: actions/download-artifact@v4
34+
with:
35+
name: pre-release-meta
36+
github-token: ${{ secrets.GITHUB_TOKEN }}
37+
run-id: ${{ github.event.workflow_run.id }}
38+
path: meta
39+
40+
- name: Determine versions
41+
id: versions
42+
run: |
43+
set -euo pipefail
44+
CLEAN_VERSION_INPUT="${{ inputs.clean_version || '' }}"
45+
if [ -f meta/version.env ]; then
46+
source meta/version.env
47+
fi
48+
CLEAN_VERSION="${CLEAN_VERSION_INPUT:-${CLEAN_VERSION:-}}"
49+
if [ -z "${CLEAN_VERSION:-}" ]; then
50+
echo "CLEAN_VERSION is required (input or artifact)" >&2
51+
exit 1
52+
fi
53+
if echo "$CLEAN_VERSION" | grep -q '\.post'; then
54+
echo "CLEAN_VERSION must be clean semver (no .post): $CLEAN_VERSION" >&2
55+
exit 1
56+
fi
57+
echo "clean_version=$CLEAN_VERSION" >> $GITHUB_OUTPUT
58+
59+
- name: Setup Python
60+
uses: actions/setup-python@v5
61+
with:
62+
python-version: '3.13'
63+
64+
- name: Install Poetry and deps
65+
run: |
66+
pip install poetry==2.1.3
67+
python -m pip install --upgrade pip
68+
python -m pip install "tomlkit==0.13.3" "pyyaml==6.0.2" "packaging==25.0"
69+
70+
- name: Configure TestPyPI repository
71+
run: |
72+
poetry config repositories.testpypi https://test.pypi.org/legacy/
73+
74+
- name: Install jq
75+
run: sudo apt-get update && sudo apt-get install -y jq
76+
77+
- name: Update internal libs and service pins to clean version
78+
env:
79+
VERSION: ${{ steps.versions.outputs.clean_version }}
80+
run: |
81+
python tools/bump_pyproject_deps.py --version "$VERSION" --bump-libs --bump-service-pins
82+
83+
- name: Publish clean semver to TestPyPI and PyPI (core-first)
84+
env:
85+
CLEAN_VERSION: ${{ steps.versions.outputs.clean_version }}
86+
POETRY_HTTP_BASIC_TESTPYPI_USERNAME: __token__
87+
POETRY_HTTP_BASIC_TESTPYPI_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }}
88+
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
89+
run: |
90+
set -euo pipefail
91+
if [ -z "${POETRY_HTTP_BASIC_TESTPYPI_PASSWORD:-}" ]; then
92+
echo "Missing TEST_PYPI_TOKEN secret" >&2
93+
exit 1
94+
fi
95+
if [ -z "${POETRY_PYPI_TOKEN_PYPI:-}" ]; then
96+
echo "Missing PYPI_TOKEN secret" >&2
97+
exit 1
98+
fi
99+
100+
source tools/publish_libs.sh
101+
102+
echo "Publishing clean version $CLEAN_VERSION (core-first) to TestPyPI..."
103+
publish_lib "rag-core-lib" "-r testpypi" "$CLEAN_VERSION"
104+
wait_for_index "rag-core-lib" "$CLEAN_VERSION" "https://test.pypi.org" "TestPyPI"
105+
for lib in rag-core-api admin-api-lib extractor-api-lib; do
106+
publish_lib "$lib" "-r testpypi" "$CLEAN_VERSION"
107+
done
108+
109+
echo "Publishing clean version $CLEAN_VERSION (core-first) to PyPI..."
110+
publish_lib "rag-core-lib" "" "$CLEAN_VERSION"
111+
wait_for_index "rag-core-lib" "$CLEAN_VERSION" "https://pypi.org" "PyPI"
112+
for lib in rag-core-api admin-api-lib extractor-api-lib; do
113+
publish_lib "$lib" "" "$CLEAN_VERSION"
114+
done
115+
116+
- name: Clear poetry caches
117+
run: |
118+
poetry cache clear --all pypi -n || true
119+
poetry cache clear --all testpypi -n || true
120+
121+
- name: Refresh service lockfiles
122+
env:
123+
VERSION: ${{ steps.versions.outputs.clean_version }}
124+
run: |
125+
for svc in services/rag-backend services/admin-backend services/document-extractor services/mcp-server; do
126+
if [ -f "$svc/pyproject.toml" ]; then
127+
echo "Locking $svc"
128+
(
129+
cd "$svc"
130+
poetry lock -v || (
131+
echo "Lock failed, clearing caches and retrying...";
132+
poetry cache clear --all pypi -n || true;
133+
poetry cache clear --all testpypi -n || true;
134+
sleep 10;
135+
poetry lock -v
136+
)
137+
)
138+
fi
139+
done
140+
141+
- name: Bump Chart appVersion to clean version (leave chart version for manual chart publish)
142+
env:
143+
APP_VERSION: ${{ steps.versions.outputs.clean_version }}
144+
run: |
145+
python tools/bump_chart_versions.py --app-version "$APP_VERSION" --mode app-only
146+
147+
- name: Open PR with updated locks, pins, and Chart appVersion
148+
id: cpr
149+
uses: peter-evans/create-pull-request@v6
150+
with:
151+
branch: chore/refresh-locks-${{ steps.versions.outputs.clean_version }}-${{ github.run_number }}
152+
title: "chore(release): refresh service lockfiles for ${{ steps.versions.outputs.clean_version }}"
153+
body: |
154+
Refresh service poetry.lock files, dependency pins, and Chart appVersion for version ${{ steps.versions.outputs.clean_version }}.
155+
commit-message: "chore(release): refresh service lockfiles, pins, and Chart appVersion"
156+
add-paths: |
157+
services/**/pyproject.toml
158+
services/**/poetry.lock
159+
infrastructure/**/Chart.yaml
160+
libs/**/pyproject.toml
161+
labels: refresh-locks
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
name: publish-chart-packages
2+
on:
3+
pull_request:
4+
branches: [main]
5+
types: [closed]
6+
workflow_dispatch:
7+
inputs:
8+
chart_version:
9+
description: "Chart version to publish (default: read from Chart.yaml)"
10+
required: false
11+
type: string
12+
ref:
13+
description: "Git ref to package from (default: main)"
14+
required: false
15+
type: string
16+
17+
permissions:
18+
contents: write
19+
packages: write
20+
pages: write
21+
id-token: write
22+
23+
env:
24+
OCI_REGISTRY: ghcr.io
25+
26+
jobs:
27+
publish:
28+
runs-on: ubuntu-latest
29+
if: |
30+
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'chart-bump'))
31+
|| github.event_name == 'workflow_dispatch'
32+
steps:
33+
- uses: actions/checkout@v4
34+
with:
35+
fetch-depth: 0
36+
ref: ${{ inputs.ref || 'main' }}
37+
38+
- name: Setup Helm
39+
uses: azure/setup-helm@v4
40+
41+
- name: Login to GHCR for Helm OCI
42+
run: echo ${{ secrets.GHCR_PAT }} | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
43+
44+
- name: Determine chart version
45+
id: meta
46+
run: |
47+
set -euo pipefail
48+
INPUT_VER="${{ inputs.chart_version }}"
49+
FILE_VER=$(awk '/^version:/ {print $2}' infrastructure/rag/Chart.yaml | tr -d "\"'")
50+
CHART_VERSION="${INPUT_VER:-$FILE_VER}"
51+
if [ -z "$CHART_VERSION" ]; then
52+
echo "Could not determine chart version" >&2
53+
exit 1
54+
fi
55+
echo "chart_version=$CHART_VERSION" >> $GITHUB_OUTPUT
56+
57+
- name: Verify chart version matches input (if provided)
58+
env:
59+
INPUT_VER: ${{ inputs.chart_version }}
60+
FILE_VER: ${{ steps.meta.outputs.chart_version }}
61+
run: |
62+
if [ -n "$INPUT_VER" ] && [ "$INPUT_VER" != "$FILE_VER" ]; then
63+
echo "Chart.yaml version ($FILE_VER) does not match input $INPUT_VER" >&2
64+
exit 1
65+
fi
66+
67+
- name: Package chart
68+
run: |
69+
set -euo pipefail
70+
CHART_DIR="infrastructure/rag"
71+
mkdir -p dist
72+
helm dependency update "$CHART_DIR" || true
73+
helm package "$CHART_DIR" --destination dist
74+
ls -la dist
75+
76+
- name: Push chart to GHCR (OCI)
77+
env:
78+
CHART_VERSION: ${{ steps.meta.outputs.chart_version }}
79+
run: |
80+
set -euo pipefail
81+
PKG=$(ls dist/*.tgz)
82+
helm show chart "$PKG" | grep -E "^version: "
83+
helm push "$PKG" oci://$OCI_REGISTRY/${{ github.repository_owner }}/charts
84+
85+
- name: Build Helm repo index for Pages
86+
env:
87+
CHART_VERSION: ${{ steps.meta.outputs.chart_version }}
88+
run: |
89+
set -euo pipefail
90+
PKG=$(ls dist/*.tgz)
91+
REPO="${GITHUB_REPOSITORY#*/}"
92+
BASE_URL="https://${GITHUB_REPOSITORY_OWNER}.github.io/${REPO}"
93+
helm repo index dist --url "$BASE_URL"
94+
echo "Index generated for $BASE_URL"
95+
96+
- name: Upload Pages artifact
97+
uses: actions/upload-pages-artifact@v3
98+
with:
99+
path: dist
100+
101+
deploy-pages:
102+
needs: publish
103+
runs-on: ubuntu-latest
104+
permissions:
105+
pages: write
106+
id-token: write
107+
environment:
108+
name: github-pages
109+
url: ${{ steps.deployment.outputs.page_url }}
110+
steps:
111+
- name: Deploy to GitHub Pages
112+
id: deployment
113+
uses: actions/deploy-pages@v4

0 commit comments

Comments
 (0)