Skip to main content
Version: Next

Monorepo Support

Overviewโ€‹

All reusable workflows now support Python monorepos (multiple packages in one repository) while maintaining 100% backward compatibility with single-project repositories.

Key Featuresโ€‹

โœ… Multiple packages in one repository - Test, build, and release packages independently
โœ… Project-specific artifacts - No naming collisions between packages
โœ… Independent releases - Each package can have its own version and release cycle
โœ… Configurable paths - Specify working directories and file paths per package
โœ… Backward compatible - Existing single-project workflows work unchanged

Quick Startโ€‹

Monorepo Structure Exampleโ€‹

my-monorepo/
โ”œโ”€โ”€ packages/
โ”‚ โ”œโ”€โ”€ core/
โ”‚ โ”‚ โ”œโ”€โ”€ src/
โ”‚ โ”‚ โ”œโ”€โ”€ tests/
โ”‚ โ”‚ โ””โ”€โ”€ pyproject.toml
โ”‚ โ”œโ”€โ”€ utils/
โ”‚ โ”‚ โ”œโ”€โ”€ src/
โ”‚ โ”‚ โ”œโ”€โ”€ tests/
โ”‚ โ”‚ โ””โ”€โ”€ pyproject.toml
โ”‚ โ””โ”€โ”€ api/
โ”‚ โ”œโ”€โ”€ src/
โ”‚ โ”œโ”€โ”€ tests/
โ”‚ โ””โ”€โ”€ pyproject.toml
โ””โ”€โ”€ .github/workflows/

Basic Example: Testing Multiple Packagesโ€‹

name: Test All Packages

on: [push, pull_request]

jobs:
test-core:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_uv_run_test.yaml@master
with:
project_name: core # Package identifier
test_working_directory: ./packages/core # Package directory
test_type: unit-test
all_test_items_paths: '["./packages/core/tests"]'

test-utils:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_uv_run_test.yaml@master
with:
project_name: utils
test_working_directory: ./packages/utils
test_type: unit-test
all_test_items_paths: '["./packages/utils/tests"]'

Essential Parametersโ€‹

Core Monorepo Parametersโ€‹

ParameterUsed InPurposeExample
project_nameMost workflowsPackage identifier for artifact namingcore, utils, api
test_working_directoryTest workflowsDirectory for running tests./packages/core
package_working_directoryBuild workflowsDirectory for building packages./packages/core
build_contextDocker workflowsDocker build context directory./services/api
tag_prefixRelease workflowsGit tag prefix for releasescore/ โ†’ core/v1.2.3

Parameter Usage by Workflowโ€‹

Testing Workflowsโ€‹

  • rw_uv_run_test.yaml: project_name, test_working_directory
  • rw_poetry_run_test.yaml: project_name, test_working_directory
  • rw_run_test.yaml: project_name, test_working_directory, requirements_path, requirements_test_path
  • rw_get_tests.yaml: test_working_directory

Building & Publishingโ€‹

  • rw_python_package.yaml: project_name, package_working_directory
  • rw_push_pypi.yaml: package_working_directory

Coverage & Qualityโ€‹

  • rw_organize_test_cov_reports.yaml: project_name
  • rw_upload_test_cov_report.yaml: project_name
  • rw_sonarqube_scan.yaml: project_name, project_working_directory, sonar_project_key

Release & Deploymentโ€‹

  • rw_build_git-tag_and_create_github-release.yaml: package_working_directory, tag_prefix
  • rw_docker_operations.yaml: build_context, dockerfile_path, project_name
  • rw_documentation_deployment.yaml: docs_working_directory, project_name

Complete Examplesโ€‹

Example 1: Full CI/CD Pipelineโ€‹

name: CI/CD - Core Package

on:
push:
branches: [main]
paths:
- 'packages/core/**'
- '.github/workflows/ci-core.yaml'

jobs:
# Test
test:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_poetry_run_test.yaml@master
with:
project_name: core
test_working_directory: ./packages/core
test_type: unit-test
all_test_items_paths: '["./tests"]'

# Organize coverage
coverage:
needs: test
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_organize_test_cov_reports.yaml@master
with:
project_name: core
test_type: unit-test

# Upload to Codecov
codecov:
needs: coverage
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_upload_test_cov_report.yaml@master
with:
project_name: core
test_type: unit-test
upload-to-codecov: true
secrets:
codecov_token: ${{ secrets.CODECOV_TOKEN }}

# Build package
build:
needs: test
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_python_package.yaml@master
with:
operation: build
package_working_directory: ./packages/core
project_name: core
upload-artifacts: true

Example 2: Tag-Triggered Releases ๐Ÿท๏ธโ€‹

The standard Git workflow - Push a tag to trigger automatic releases.

Single-Project Tag Releaseโ€‹

Workflow (.github/workflows/release.yaml):

name: Release on Tag

on:
push:
tags:
- 'v*.*.*' # Matches v1.0.0, v2.1.3, etc.

permissions:
contents: write
packages: write
id-token: write

jobs:
release:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_release_complete.yaml@master
secrets: inherit

Usage:

git tag v1.2.3
git push origin v1.2.3
# Automatic release triggered!

Monorepo Tag Releaseโ€‹

Tag Format: {package-name}/v{version}

Workflow (.github/workflows/release.yaml):

name: Monorepo Release on Tag

on:
push:
tags:
- '*/v*.*.*' # Matches core/v1.0.0, utils/v2.1.3, etc.

jobs:
# Step 1: Extract package from tag
extract-package:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_extract_package_from_tag.yaml@master

# Step 2: Release the package
release:
needs: extract-package
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_release_complete.yaml@master
with:
package-name: ${{ needs.extract-package.outputs.package_name }}
secrets: inherit

Usage:

# Release core package
git tag core/v1.2.3
git push origin core/v1.2.3

# Release utils package
git tag utils/v2.0.0
git push origin utils/v2.0.0

Complete Examples:


Example 3: Centralized Configuration with Intent.yamlโ€‹

Combine tag-triggered releases with centralized configuration!

Intent.yaml Configurationโ€‹

# .github/tag_and_release/intent.yaml
packages:
- name: core
working_directory: ./packages/core
tag_prefix: core/
python:
auth_method: oidc
docker:
registries:
dockerhub: docker.io
ghcr: ghcr.io

- name: utils
working_directory: ./packages/utils
tag_prefix: utils/
python:
auth_method: token # Override default

- name: api
working_directory: ./services/api
tag_prefix: api/
# Uses all defaults

defaults:
git:
commit:
name: "GitHub Actions Bot"
email: "actions@github.com"
python:
auth_method: oidc
docker:
registries:
dockerhub: docker.io
ghcr: ghcr.io
health_check:
port: 8000
path: "/health"

Release Workflow Using Intent.yamlโ€‹

name: Release Package

on:
push:
tags:
- 'core/v*.*.*'
- 'utils/v*.*.*'
- 'api/v*.*.*'

jobs:
# Extract package name from tag
parse-tag:
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_extract_package_from_tag.yaml@master

# Release with intent.yaml configuration
release:
needs: parse-tag
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_release_complete.yaml@master
with:
package-name: ${{ needs.parse-tag.outputs.package_name }} # Loads from intent.yaml
secrets: inherit

Benefits of Intent.yaml:

  • โœ… Centralized configuration - single source of truth
  • โœ… Shared defaults across packages
  • โœ… Per-package overrides for flexibility
  • โœ… Validated by JSON schema
  • โœ… No workflow duplication

Example 3: Independent Package Releases (Direct Parameters)โ€‹

name: Release Package

on:
push:
tags:
- 'core/v*.*.*'
- 'utils/v*.*.*'

jobs:
# Parse tag to determine package
parse-tag:
runs-on: ubuntu-latest
outputs:
package: ${{ steps.parse.outputs.package }}
version: ${{ steps.parse.outputs.version }}
steps:
- name: Parse tag
id: parse
run: |
TAG=${GITHUB_REF#refs/tags/}
PACKAGE=$(echo $TAG | cut -d'/' -f1)
VERSION=$(echo $TAG | cut -d'/' -f2 | sed 's/^v//')
echo "package=$PACKAGE" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT

# Build and release
release:
needs: parse-tag
uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_build_git-tag_and_create_github-release.yaml@master
with:
project_type: python-package
project_name: ${{ needs.parse-tag.outputs.package }}
package_working_directory: ./packages/${{ needs.parse-tag.outputs.package }}
tag_prefix: ${{ needs.parse-tag.outputs.package }}/
secrets:
github_auth_token: ${{ secrets.GITHUB_TOKEN }}

Example 3: Docker Multi-Service Buildโ€‹

name: Build Docker Images

on:
push:
branches: [main]
paths:
- 'services/**'

jobs:
build-services:
strategy:
matrix:
service: [api, worker, admin]

uses: Chisanan232/GitHub-Action_Reusable_Workflows-Python/.github/workflows/rw_docker_operations.yaml@master
with:
operation: build
build_context: ./services/${{ matrix.service }}
dockerfile_path: Dockerfile
project_name: ${{ matrix.service }}
image-name: myorg/${{ matrix.service }}
version: ${{ github.sha }}

Artifact Namingโ€‹

Single-Project (Backward Compatible)โ€‹

coverage_unit-test_ubuntu-latest_3.13
unit-test_coverage_data_file

Monorepo (with project_name)โ€‹

coverage_core_unit-test_ubuntu-latest_3.13
core_unit-test_coverage_data_file

This prevents naming collisions when multiple packages are tested simultaneously.

Git Tagging Strategyโ€‹

Single-Projectโ€‹

git tag v1.2.3
git push origin v1.2.3

Monorepoโ€‹

# Tag individual packages
git tag core/v1.2.3
git tag utils/v2.0.0
git tag api/v1.5.0

git push origin --tags

Benefits:

  • Clear package identification
  • Independent versioning
  • Automated release detection
  • Better GitHub release organization

Best Practicesโ€‹

1. Consistent Namingโ€‹

Use the same project_name across all workflows for a package:

# โœ… Good - consistent naming
test:
with:
project_name: core

build:
with:
project_name: core

release:
with:
project_name: core

2. Path-Based Triggersโ€‹

Only run workflows when specific packages change:

on:
push:
paths:
- 'packages/core/**'
- '.github/workflows/ci-core.yaml'

3. Matrix Strategy for Multiple Packagesโ€‹

Test all packages efficiently:

jobs:
test:
strategy:
matrix:
package: [core, utils, api]

uses: ./.github/workflows/rw_uv_run_test.yaml
with:
project_name: ${{ matrix.package }}
test_working_directory: ./packages/${{ matrix.package }}
test_type: unit-test

4. Independent Coverage Reportsโ€‹

Configure separate coverage for each package:

jobs:
upload-coverage:
strategy:
matrix:
package: [core, utils]

uses: ./.github/workflows/rw_upload_test_cov_report.yaml
with:
project_name: ${{ matrix.package }}
upload-to-codecov: true

Migration Guideโ€‹

Step 1: Update Workflow Structureโ€‹

Add project_name and working directory parameters to existing workflows.

Before (Single-Project):

test:
uses: ./.github/workflows/rw_uv_run_test.yaml
with:
test_type: unit-test

After (Monorepo):

test-core:
uses: ./.github/workflows/rw_uv_run_test.yaml
with:
project_name: core
test_working_directory: ./packages/core
test_type: unit-test

Step 2: Update Artifact Dependenciesโ€‹

Ensure artifact downloads use correct project names:

organize-coverage:
with:
project_name: core # Downloads coverage_core* artifacts

Step 3: Configure SonarQube (if used)โ€‹

Set up project-specific SonarQube keys:

sonar:
with:
project_name: core
project_working_directory: ./packages/core
sonar_project_key: myorg_monorepo_core

Backward Compatibilityโ€‹

All changes are optional with sensible defaults. Existing workflows continue to work without modifications:

# This still works - no changes needed!
test:
uses: ./.github/workflows/rw_uv_run_test.yaml
with:
test_type: unit-test
all_test_items_paths: '["./tests"]'

Advanced Documentationโ€‹

For comprehensive guides and examples, see:

  • Planning Document: .ai/prompt/2026.4.4/monorepo-support-planning.md
  • Usage Guide: .ai/prompt/2026.4.4/monorepo-usage-guide.md
  • Migration Guide: .ai/prompt/2026.4.4/migration-guide-single-to-monorepo.md
  • Release & Docker Guide: .ai/prompt/2026.4.4/phase-3-release-docker-guide.md
  • Implementation Summary: .ai/prompt/2026.4.4/IMPLEMENTATION-COMPLETE.md

Troubleshootingโ€‹

Issue: Artifacts Not Foundโ€‹

Symptom: Workflow can't find coverage artifacts

Solution: Ensure project_name matches across test and coverage workflows

# Test workflow
with:
project_name: core # Must match

# Coverage workflow
with:
project_name: core # Must match

Issue: Wrong Working Directoryโ€‹

Symptom: Dependencies not found during test/build

Solution: Verify working directory points to package location

with:
test_working_directory: ./packages/core # Correct path

Issue: Tag Conflictsโ€‹

Symptom: Git tag already exists

Solution: Use package-specific tag prefixes

with:
tag_prefix: core/ # Creates core/v1.2.3

FAQโ€‹

Q: Do I need to change my existing workflows?
A: No! All monorepo parameters are optional. Existing workflows work unchanged.

Q: Can I use both single-project and monorepo workflows?
A: Yes! You can gradually migrate or use different approaches per project.

Q: How do I handle shared dependencies?
A: Each package can have its own dependencies. For shared code, consider creating a shared package.

Q: What about coverage aggregation across packages?
A: Currently, each package has separate coverage. Use external tools for cross-package aggregation if needed.

Q: Can I release packages independently?
A: Yes! Use tag prefixes (core/v1.2.3) to trigger package-specific releases.