Skip to content

Conversation

a-klos
Copy link
Member

@a-klos a-klos commented Sep 10, 2025

This pull request restructures and improves the release automation for the repository by splitting the previous monolithic workflow into several specialized GitHub Actions workflows. These new workflows automate versioning, library publishing, Docker image building, Helm chart packaging, and dependency lockfile refreshing. Additionally, the workflows now use label-based gating to control releases and dependency updates, and they improve version extraction and artifact handling. The previous workflow file .github/workflows/semantic-release.yml has been removed and replaced with more maintainable, modular workflows.

Release and Versioning Automation

  • Added .github/workflows/prepare-release.yml to automate semantic version calculation and bump internal library versions, opening a PR with the new versions and gating future steps with the prepare-release label.
  • Added .github/workflows/create-release.yml to create a Git tag and GitHub Release when a PR with the refresh-locks label is merged to main, extracting the version from the PR title.

Library Publishing and Dependency Management

  • Added .github/workflows/publish-libs-on-merge.yml to publish Python libraries to TestPyPI after a prepare-release PR is merged, update service dependency pins, refresh lockfiles, and open a PR with updated lockfiles using the refresh-locks label.

Docker Image and Helm Chart Automation

  • Added .github/workflows/build-images.yml to build and publish Docker images for all services when a release is published, capturing image digests as artifacts.
  • Added .github/workflows/publish-chart.yml to package and publish the Helm chart after images are built, bumping chart versions and opening a PR for chart version updates using the chart-bump label.

Workflow Gating and Cleanup

  • Updated .github/workflows/lint-and-test.yml to skip jobs if any of the release-related labels (prepare-release, refresh-locks, chart-bump) are present, preventing unnecessary CI runs during release automation.
  • Removed the legacy .github/workflows/semantic-release.yml workflow, which previously handled all release steps in one file, in favor of the new modular approach.

These changes collectively make the release process more robust, modular, and maintainable, while ensuring that versioning, publishing, and dependency updates are tightly controlled and automated.

…ilding

- Added new workflows for creating releases, preparing releases, and publishing libraries on merge.
- Implemented logic to derive version from pull request titles and create Git tags/releases accordingly.
- Enhanced image building workflows to include digest capturing and improved error handling.
- Refactored existing workflows to streamline the process of bumping versions for internal libraries and services.
- Introduced scripts for bumping chart versions and updating pyproject dependencies.
- Removed obsolete scripts and workflows to clean up the repository.
@huhn511 huhn511 requested a review from Copilot September 23, 2025 15:29
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request refactors the release automation from a single monolithic workflow to a modular approach with label-based gating. It removes the legacy semantic-release workflow and introduces specialized workflows for each stage of the release process, along with new Python utilities for dependency and version management.

  • Split release automation into 5 specialized workflows with label-based gating
  • Added Python utilities for version bumping, dependency management, and chart updates
  • Updated service names and versions to align with new release structure

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tools/update-helm-values.py Removed legacy Helm values update script
tools/bump_pyproject_deps.py Added utility for managing Python library versions and service dependency pins
tools/bump_chart_versions.py Added utility for converting app versions to Helm chart versions
services/*/pyproject.toml Updated service names and versions for consistency
.github/workflows/semantic-release.yml Removed monolithic release workflow
.github/workflows/*.yml Added modular workflows for prepare-release, publish-libs, build-images, create-release, and publish-chart
.github/workflows/lint-and-test.yml Added label-based gating to skip CI during release automation

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

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
Copy link
Preview

Copilot AI Sep 23, 2025

Choose a reason for hiding this comment

The 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
run: echo ${{ secrets.GHCR_PAT }} | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
run: printf "%s" "${{ secrets.GHCR_PAT }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin

Copilot uses AI. Check for mistakes.

tag_name: v${{ steps.ver.outputs.version }}
name: v${{ steps.ver.outputs.version }}
generate_release_notes: true
token: ${{ secrets.GHCR_PAT }}
Copy link
Preview

Copilot AI Sep 23, 2025

Choose a reason for hiding this comment

The 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
token: ${{ secrets.GHCR_PAT }}
token: ${{ secrets.GITHUB_TOKEN }}

Copilot uses AI. Check for mistakes.

with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GHCR_PAT }}
Copy link
Preview

Copilot AI Sep 23, 2025

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.

Suggested change
password: ${{ secrets.GHCR_PAT }}
password: ${{ secrets.GITHUB_TOKEN }}

Copilot uses AI. Check for mistakes.

Comment on lines +39 to +51
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith('[tool.poetry]'):
in_tool_poetry = True
continue
if in_tool_poetry and stripped.startswith('[') and not stripped.startswith('[tool.poetry]'):
# left the section without finding version; stop scanning section
break
if in_tool_poetry and stripped.startswith('version'):
# Replace only the version value, keep indentation and spacing
lines[i] = re.sub(r'version\s*=\s*"[^"]*"', f'version = "{new_version}"', line)
return ''.join(lines)
# If no version line found, append it to the [tool.poetry] section
Copy link
Preview

Copilot AI Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fallback adds a duplicate [tool.poetry] section when no version line is found, which would create invalid TOML. The logic should append the version line to an existing [tool.poetry] section or handle the case where the section doesn't exist properly.

Suggested change
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith('[tool.poetry]'):
in_tool_poetry = True
continue
if in_tool_poetry and stripped.startswith('[') and not stripped.startswith('[tool.poetry]'):
# left the section without finding version; stop scanning section
break
if in_tool_poetry and stripped.startswith('version'):
# Replace only the version value, keep indentation and spacing
lines[i] = re.sub(r'version\s*=\s*"[^"]*"', f'version = "{new_version}"', line)
return ''.join(lines)
# If no version line found, append it to the [tool.poetry] section
tool_poetry_start = None
tool_poetry_end = None
version_found = False
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith('[tool.poetry]'):
in_tool_poetry = True
tool_poetry_start = i
continue
if in_tool_poetry and stripped.startswith('[') and not stripped.startswith('[tool.poetry]'):
# left the section without finding version; stop scanning section
tool_poetry_end = i
in_tool_poetry = False
break
if in_tool_poetry and stripped.startswith('version'):
# Replace only the version value, keep indentation and spacing
lines[i] = re.sub(r'version\s*=\s*"[^"]*"', f'version = "{new_version}"', line)
version_found = True
return ''.join(lines)
# If [tool.poetry] section exists but no version line found, insert it
if tool_poetry_start is not None:
insert_at = tool_poetry_start + 1
# If we found the end of the section, insert before it
if tool_poetry_end is not None:
insert_at = tool_poetry_end
# Determine indentation (match other lines in section, or default to 4 spaces)
indent = ""
for j in range(tool_poetry_start + 1, (tool_poetry_end or len(lines))):
content = lines[j].lstrip('\r\n')
if content and not content.strip().startswith('['):
indent = lines[j][:len(lines[j]) - len(lines[j].lstrip())]
break
lines.insert(insert_at, f"{indent}version = \"{new_version}\"\n")
return ''.join(lines)
# If no [tool.poetry] section found, append it at the end

Copilot uses AI. Check for mistakes.

Copy link
Collaborator

@huhn511 huhn511 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants