-
Notifications
You must be signed in to change notification settings - Fork 6
refactor: update GitHub workflows for release management and image build #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: refactor/prod-and-dev-Dockerfiles
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
name: build-images | ||
run-name: build-images ${{ github.event.release.tag_name }} | ||
on: | ||
release: | ||
types: [published] | ||
|
||
permissions: | ||
contents: read | ||
packages: write | ||
|
||
jobs: | ||
prepare: | ||
if: ${{ github.event_name == 'release' }} | ||
runs-on: ubuntu-latest | ||
outputs: | ||
tag: ${{ steps.release_tag.outputs.tag }} | ||
version: ${{ steps.release_tag.outputs.version }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
with: | ||
fetch-depth: 0 | ||
- name: Resolve release tag & version | ||
id: release_tag | ||
run: | | ||
git fetch --tags --force | ||
TAG="${{ github.event.release.tag_name }}" | ||
if [ -z "$TAG" ]; then | ||
echo "No Git tag found to check out" >&2 | ||
exit 1 | ||
fi | ||
VER_NO_V="${TAG#v}" | ||
echo "tag=$TAG" >> $GITHUB_OUTPUT | ||
echo "version=$VER_NO_V" >> $GITHUB_OUTPUT | ||
|
||
build-image: | ||
needs: prepare | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
include: | ||
- name: rag-backend | ||
dockerfile: services/rag-backend/Dockerfile | ||
- name: admin-backend | ||
dockerfile: services/admin-backend/Dockerfile | ||
- name: document-extractor | ||
dockerfile: services/document-extractor/Dockerfile | ||
- name: mcp-server | ||
dockerfile: services/mcp-server/Dockerfile | ||
- name: frontend | ||
dockerfile: services/frontend/apps/chat-app/Dockerfile | ||
- name: admin-frontend | ||
dockerfile: services/frontend/apps/admin-app/Dockerfile | ||
env: | ||
REGISTRY: ghcr.io | ||
IMAGE_NS: ${{ github.repository }} | ||
VERSION: ${{ needs.prepare.outputs.version }} | ||
TAG: ${{ needs.prepare.outputs.tag }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
with: | ||
fetch-depth: 0 | ||
- name: Checkout release tag | ||
run: git checkout "$TAG" | ||
- name: Normalize IMAGE_NS to lowercase | ||
run: echo "IMAGE_NS=$(echo '${{ env.IMAGE_NS }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV | ||
- name: Login to GHCR | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GHCR_PAT }} | ||
- name: Set up Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
- name: Build & push ${{ matrix.name }} | ||
run: | | ||
docker buildx build --push \ | ||
-t "$REGISTRY/$IMAGE_NS/${{ matrix.name }}:${VERSION}" \ | ||
-t "$REGISTRY/$IMAGE_NS/${{ matrix.name }}:latest" \ | ||
-f "${{ matrix.dockerfile }}" . | ||
- name: Capture digest | ||
run: | | ||
sudo apt-get update && sudo apt-get install -y jq | ||
ref="$REGISTRY/$IMAGE_NS/${{ matrix.name }}:${VERSION}" | ||
digest=$(docker buildx imagetools inspect "$ref" --format '{{json .Manifest.Digest}}' | jq -r . || true) | ||
jq -n --arg name "${{ matrix.name }}" --arg tag "$VERSION" --arg digest "$digest" '{($name): {tag: $tag, digest: $digest}}' > digest.json | ||
- name: Upload digest artifact | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: image-digest-${{ matrix.name }} | ||
path: digest.json | ||
|
||
collect-digests: | ||
needs: [build-image] | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Download digest artifacts | ||
uses: actions/download-artifact@v4 | ||
with: | ||
pattern: image-digest-* | ||
merge-multiple: false | ||
- name: Merge digests | ||
run: | | ||
sudo apt-get update && sudo apt-get install -y jq | ||
jq -s 'reduce .[] as $item ({}; . * $item)' image-digest-*/digest.json > image-digests.json | ||
- name: Upload merged digests | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: image-digests | ||
path: image-digests.json |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,47 @@ | ||||||
name: create-release | ||||||
on: | ||||||
pull_request_target: | ||||||
types: [closed] | ||||||
branches: [main] | ||||||
|
||||||
permissions: | ||||||
contents: write | ||||||
|
||||||
jobs: | ||||||
release: | ||||||
if: >- | ||||||
${{ | ||||||
github.event.pull_request.merged == true && | ||||||
contains(github.event.pull_request.labels.*.name, 'refresh-locks') | ||||||
}} | ||||||
runs-on: ubuntu-latest | ||||||
steps: | ||||||
- uses: actions/checkout@v4 | ||||||
with: | ||||||
fetch-depth: 0 | ||||||
|
||||||
- name: Derive version from PR title | ||||||
id: ver | ||||||
run: | | ||||||
TITLE="${{ github.event.pull_request.title }}" | ||||||
VERSION=$(echo "$TITLE" | sed -nE 's/.*([0-9]+\.[0-9]+\.[0-9]+(\.post[0-9]+)?).*/\1/p' || true) | ||||||
if [ -z "$VERSION" ]; then | ||||||
echo "Could not extract version from PR title: $TITLE" >&2 | ||||||
exit 1 | ||||||
fi | ||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT | ||||||
|
||||||
- name: Create Git tag | ||||||
run: | | ||||||
git config user.name "github-actions" | ||||||
git config user.email "[email protected]" | ||||||
git tag -a "v${{ steps.ver.outputs.version }}" -m "Release v${{ steps.ver.outputs.version }}" | ||||||
git push origin "v${{ steps.ver.outputs.version }}" | ||||||
|
||||||
- name: Create GitHub Release | ||||||
uses: softprops/action-gh-release@v2 | ||||||
with: | ||||||
tag_name: v${{ steps.ver.outputs.version }} | ||||||
name: v${{ steps.ver.outputs.version }} | ||||||
generate_release_notes: true | ||||||
token: ${{ secrets.GHCR_PAT }} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using GHCR_PAT token for GitHub release creation is inconsistent. The standard GITHUB_TOKEN should be sufficient for creating releases and would be more appropriate here.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
name: prepare-release | ||
on: | ||
pull_request: | ||
types: [closed] | ||
branches: | ||
- main | ||
|
||
permissions: | ||
contents: write | ||
pull-requests: write | ||
|
||
jobs: | ||
prepare: | ||
if: >- | ||
${{ | ||
github.event.pull_request.merged && | ||
!contains(github.event.pull_request.labels.*.name, 'prepare-release') && | ||
!contains(github.event.pull_request.labels.*.name, 'refresh-locks') && | ||
!contains(github.event.pull_request.labels.*.name, 'chart-bump') | ||
}} | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- name: Setup Node | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: '20' | ||
|
||
- name: Install semantic-release deps | ||
run: npm ci | ||
|
||
- name: verify-dependencies-integrity | ||
run: npm audit signatures | ||
|
||
- name: Compute next version (dry-run) | ||
id: semrel | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
run: | | ||
npx semantic-release --dry-run --no-ci | tee semrel.log | ||
BASE_VERSION=$(grep -Eo "next release version is [0-9]+\.[0-9]+\.[0-9]+" semrel.log | awk '{print $5}') | ||
if [ -z "$BASE_VERSION" ]; then echo "No new release required"; exit 1; fi | ||
VERSION="${BASE_VERSION}.post$(date +%Y%m%d%H%M%S)" | ||
echo "version=$VERSION" >> $GITHUB_OUTPUT | ||
|
||
- name: Setup Python | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: '3.13' | ||
|
||
- name: Install bump script deps | ||
run: | | ||
python -m pip install --upgrade pip | ||
python -m pip install "tomlkit==0.13.3" "pyyaml==6.0.2" "packaging==25.0" | ||
|
||
- name: Bump internal libs only (no service pins) | ||
run: | | ||
python tools/bump_pyproject_deps.py --version "${{ steps.semrel.outputs.version }}" --bump-libs | ||
|
||
- name: Commit and open PR | ||
uses: peter-evans/create-pull-request@v6 | ||
with: | ||
branch: chore/release-${{ steps.semrel.outputs.version }} | ||
title: "chore(release): prepare ${{ steps.semrel.outputs.version }}" | ||
body: | | ||
Prepare release ${{ steps.semrel.outputs.version }} | ||
- bump internal libs versions only | ||
commit-message: "chore(release): prepare ${{ steps.semrel.outputs.version }}" | ||
add-paths: | | ||
libs/**/pyproject.toml | ||
labels: prepare-release |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,95 @@ | ||||||
name: publish-chart | ||||||
run-name: publish-chart (post-build-images) | ||||||
on: | ||||||
workflow_run: | ||||||
workflows: [build-images] | ||||||
types: [completed] | ||||||
|
||||||
permissions: | ||||||
contents: write | ||||||
pull-requests: write | ||||||
packages: write | ||||||
|
||||||
jobs: | ||||||
chart: | ||||||
if: ${{ github.event.workflow_run.conclusion == 'success' }} | ||||||
runs-on: ubuntu-latest | ||||||
steps: | ||||||
- uses: actions/checkout@v4 | ||||||
with: | ||||||
fetch-depth: 0 | ||||||
|
||||||
- name: Checkout release tag from triggering run | ||||||
run: | | ||||||
git fetch --tags --force | ||||||
HEAD_SHA="${{ github.event.workflow_run.head_sha }}" | ||||||
if [ -n "$HEAD_SHA" ]; then | ||||||
TAG=$(git tag --points-at "$HEAD_SHA" | head -n 1 || true) | ||||||
if [ -z "$TAG" ]; then | ||||||
TAG=$(git describe --tags --abbrev=0 "$HEAD_SHA" 2>/dev/null || true) | ||||||
fi | ||||||
fi | ||||||
if [ -z "$TAG" ]; then | ||||||
echo "No tag found (head_sha=$HEAD_SHA)" >&2 | ||||||
exit 1 | ||||||
fi | ||||||
git checkout "$TAG" | ||||||
echo "RELEASE_TAG=$TAG" >> $GITHUB_ENV | ||||||
echo "APP_VERSION=${TAG#v}" >> $GITHUB_ENV | ||||||
|
||||||
- name: Expose app version | ||||||
id: meta | ||||||
run: echo "app_version=${APP_VERSION}" >> $GITHUB_OUTPUT | ||||||
|
||||||
- name: Setup Helm | ||||||
uses: azure/setup-helm@v4 | ||||||
|
||||||
- name: Login to GHCR for Helm OCI | ||||||
run: echo ${{ secrets.GHCR_PAT }} | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The GHCR_PAT secret is being echoed directly in the pipeline output, which could expose the token in logs. Use the helm registry login command with the --password-stdin flag correctly by piping from a secure environment variable instead.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
|
||||||
- name: Setup Python (for bump script) | ||||||
uses: actions/setup-python@v5 | ||||||
with: | ||||||
python-version: '3.13' | ||||||
|
||||||
- name: Install bump script deps | ||||||
run: | | ||||||
python -m pip install --upgrade pip | ||||||
python -m pip install "pyyaml==6.0.2" "packaging==25.0" | ||||||
|
||||||
- name: Bump Chart.yaml (set release version) | ||||||
env: | ||||||
APP_VERSION: ${{ env.APP_VERSION }} | ||||||
run: | | ||||||
python tools/bump_chart_versions.py --app-version "$APP_VERSION" | ||||||
|
||||||
- name: Package and push rag chart | ||||||
env: | ||||||
APP_VERSION: ${{ env.APP_VERSION }} | ||||||
run: | | ||||||
set -euo pipefail | ||||||
export HELM_EXPERIMENTAL_OCI=1 | ||||||
CHART_DIR="infrastructure/rag" | ||||||
if [ ! -f "$CHART_DIR/Chart.yaml" ]; then | ||||||
echo "Expected chart at $CHART_DIR/Chart.yaml not found" >&2 | ||||||
exit 1 | ||||||
fi | ||||||
mkdir -p dist | ||||||
helm dependency update "$CHART_DIR" || true | ||||||
helm package "$CHART_DIR" --destination dist | ||||||
PKG=$(ls dist/*.tgz) | ||||||
helm show chart "$PKG" | grep -E "^version: " | ||||||
helm push "$PKG" oci://ghcr.io/${{ github.repository_owner }}/charts | ||||||
|
||||||
- name: Create PR for chart version bumps | ||||||
uses: peter-evans/create-pull-request@v6 | ||||||
with: | ||||||
base: main | ||||||
branch: chore/chart-bump-${{ steps.meta.outputs.app_version }} | ||||||
title: "chore(release): bump chart versions to ${{ steps.meta.outputs.app_version }}" | ||||||
body: | | ||||||
Persist Chart.yaml appVersion/version to match release ${{ steps.meta.outputs.app_version }}. | ||||||
commit-message: "chore(release): bump charts to ${{ steps.meta.outputs.app_version }}" | ||||||
add-paths: | | ||||||
infrastructure/**/Chart.yaml | ||||||
labels: chart-bump |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using GHCR_PAT for Docker registry authentication is inconsistent with GitHub's recommended approach. The standard GITHUB_TOKEN should be sufficient for pushing to ghcr.io.
Copilot uses AI. Check for mistakes.