Release All Components with Changes #17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Release workflow that automatically detects and releases all components with changes | |
| # This workflow will: | |
| # 1. Check for changes in the main Modular library (excluding modules/ and non-code files) | |
| # 2. If changes exist, trigger the existing release.yml workflow | |
| # 3. Check each module for changes since its last release (excluding tests and docs) | |
| # 4. Trigger module-release.yml workflow for any modules that have changes | |
| # | |
| # Use this workflow when you want to release everything that has changed. | |
| # Use individual workflows (release.yml, module-release.yml) for specific releases. | |
| name: Release All Components with Changes | |
| run-name: Release All Components with Changes | |
| permissions: | |
| contents: write | |
| actions: write | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| releaseType: | |
| description: 'Release type for all components' | |
| required: true | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| default: 'patch' | |
| jobs: | |
| detect-changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| main_has_changes: ${{ steps.check_main.outputs.has_changes }} | |
| modules_with_changes: ${{ steps.check_modules.outputs.modules_with_changes }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for main library changes | |
| id: check_main | |
| run: | | |
| # Find the latest tag for the main library (excluding module tags) | |
| LATEST_TAG=$(git tag -l "v*" | grep -v "/" | sort -V | tail -n1 || echo "") | |
| echo "Latest main library tag: $LATEST_TAG" | |
| # Define patterns for files that should trigger a release | |
| # Include: .go files (except tests), go.mod, go.sum | |
| # Exclude: *_test.go, *.md, .github/*, examples/, cmd/ (if they don't contain logic) | |
| INCLUDE_PATTERNS="*.go go.mod go.sum" | |
| EXCLUDE_PATTERNS="*_test.go *.md .github/* examples/* docs/*" | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "No previous main library release found, checking if any relevant files exist" | |
| # Check if there are any .go files or go.mod in the root (excluding modules/) | |
| RELEVANT_FILES=$(find . -maxdepth 1 -name "*.go" -o -name "go.mod" -o -name "go.sum" | grep -v test | head -1) | |
| if [ -n "$RELEVANT_FILES" ]; then | |
| HAS_CHANGES=true | |
| else | |
| HAS_CHANGES=false | |
| fi | |
| else | |
| echo "Checking for relevant changes since $LATEST_TAG in main library" | |
| # Get all changed files since the last tag, excluding modules/ directory | |
| CHANGED_FILES=$(git diff --name-only ${LATEST_TAG}..HEAD | grep -v "^modules/" || true) | |
| echo "Files changed since $LATEST_TAG (excluding modules/):" | |
| echo "$CHANGED_FILES" | |
| # Filter for files that should trigger a release | |
| RELEVANT_CHANGES="" | |
| if [ -n "$CHANGED_FILES" ]; then | |
| for file in $CHANGED_FILES; do | |
| # Skip test files | |
| if [[ $file == *_test.go ]]; then | |
| continue | |
| fi | |
| # Skip documentation files | |
| if [[ $file == *.md ]]; then | |
| continue | |
| fi | |
| # Skip github workflows | |
| if [[ $file == .github/* ]]; then | |
| continue | |
| fi | |
| # Skip example files (unless they contain important logic) | |
| if [[ $file == examples/* ]]; then | |
| continue | |
| fi | |
| # Include .go files, go.mod, go.sum | |
| if [[ $file == *.go ]] || [[ $file == go.mod ]] || [[ $file == go.sum ]]; then | |
| RELEVANT_CHANGES="$RELEVANT_CHANGES $file" | |
| fi | |
| done | |
| fi | |
| if [ -n "$RELEVANT_CHANGES" ]; then | |
| echo "Found relevant changes in main library:" | |
| echo "$RELEVANT_CHANGES" | |
| HAS_CHANGES=true | |
| else | |
| echo "No relevant changes found in main library (only tests, docs, or workflows changed)" | |
| HAS_CHANGES=false | |
| fi | |
| fi | |
| echo "has_changes=$HAS_CHANGES" >> $GITHUB_OUTPUT | |
| echo "Main library has changes: $HAS_CHANGES" | |
| - name: Check for module changes | |
| id: check_modules | |
| run: | | |
| # Get list of all modules | |
| MODULES=$(find modules -maxdepth 1 -mindepth 1 -type d -exec basename {} \; | grep -v "README" || true) | |
| echo "Found modules: $MODULES" | |
| MODULES_WITH_CHANGES="" | |
| for MODULE in $MODULES; do | |
| echo "================================================" | |
| echo "Checking module: $MODULE" | |
| # Find the latest tag for this module | |
| LATEST_TAG=$(git tag -l "modules/${MODULE}/v*" | sort -V | tail -n1 || echo "") | |
| echo "Latest tag for $MODULE: $LATEST_TAG" | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "No previous release found for $MODULE, checking if module has relevant files" | |
| # Check if module has any .go files or go.mod | |
| RELEVANT_FILES=$(find "modules/${MODULE}" -name "*.go" -o -name "go.mod" -o -name "go.sum" | grep -v test | head -1) | |
| if [ -n "$RELEVANT_FILES" ]; then | |
| HAS_CHANGES=true | |
| else | |
| HAS_CHANGES=false | |
| fi | |
| else | |
| echo "Checking for relevant changes since $LATEST_TAG in modules/$MODULE" | |
| # Get all changed files in this module since the last tag | |
| CHANGED_FILES=$(git diff --name-only ${LATEST_TAG}..HEAD -- "modules/${MODULE}" || true) | |
| echo "Files changed in $MODULE since $LATEST_TAG:" | |
| echo "$CHANGED_FILES" | |
| # Filter for files that should trigger a release | |
| RELEVANT_CHANGES="" | |
| if [ -n "$CHANGED_FILES" ]; then | |
| for file in $CHANGED_FILES; do | |
| # Skip test files | |
| if [[ $file == *_test.go ]]; then | |
| continue | |
| fi | |
| # Skip documentation files | |
| if [[ $file == *.md ]]; then | |
| continue | |
| fi | |
| # Include .go files, go.mod, go.sum | |
| if [[ $file == *.go ]] || [[ $file == go.mod ]] || [[ $file == go.sum ]]; then | |
| RELEVANT_CHANGES="$RELEVANT_CHANGES $file" | |
| fi | |
| done | |
| fi | |
| if [ -n "$RELEVANT_CHANGES" ]; then | |
| echo "Found relevant changes in $MODULE:" | |
| echo "$RELEVANT_CHANGES" | |
| HAS_CHANGES=true | |
| else | |
| echo "No relevant changes found in $MODULE (only tests or docs changed)" | |
| HAS_CHANGES=false | |
| fi | |
| fi | |
| if [ "$HAS_CHANGES" = "true" ]; then | |
| echo "$MODULE has changes and needs a release" | |
| MODULES_WITH_CHANGES="$MODULES_WITH_CHANGES $MODULE" | |
| else | |
| echo "$MODULE has no relevant changes" | |
| fi | |
| done | |
| # Clean up the modules list and output as JSON array for matrix | |
| if [ -n "$MODULES_WITH_CHANGES" ]; then | |
| # Convert space-separated list to JSON array (compact format for GitHub Actions) | |
| MODULES_JSON=$(echo "$MODULES_WITH_CHANGES" | tr ' ' '\n' | grep -v '^$' | jq -R . | jq -s . -c) | |
| echo "modules_with_changes=$MODULES_JSON" >> $GITHUB_OUTPUT | |
| echo "Modules with changes: $MODULES_WITH_CHANGES" | |
| else | |
| echo "modules_with_changes=[]" >> $GITHUB_OUTPUT | |
| echo "No modules have relevant changes" | |
| fi | |
| release-main: | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.main_has_changes == 'true' | |
| uses: ./.github/workflows/release.yml | |
| with: | |
| releaseType: ${{ github.event.inputs.releaseType }} | |
| secrets: inherit | |
| release-modules: | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.modules_with_changes != '[]' | |
| strategy: | |
| matrix: | |
| module: ${{ fromJson(needs.detect-changes.outputs.modules_with_changes) }} | |
| uses: ./.github/workflows/module-release.yml | |
| with: | |
| module: ${{ matrix.module }} | |
| releaseType: ${{ github.event.inputs.releaseType }} | |
| secrets: inherit | |
| summary: | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes, release-main, release-modules] | |
| if: always() | |
| steps: | |
| - name: Display Release Summary | |
| run: | | |
| echo "================================================" | |
| echo "🎉 RELEASE SUMMARY" | |
| echo "==================" | |
| if [ "${{ needs.detect-changes.outputs.main_has_changes }}" = "true" ]; then | |
| if [ "${{ needs.release-main.result }}" = "success" ]; then | |
| echo "✅ Main library: Released successfully" | |
| else | |
| echo "❌ Main library: Release failed" | |
| fi | |
| else | |
| echo "⏭️ Main library: No relevant changes, no release needed" | |
| fi | |
| MODULES_WITH_CHANGES='${{ needs.detect-changes.outputs.modules_with_changes }}' | |
| if [ "$MODULES_WITH_CHANGES" != "[]" ]; then | |
| echo "📦 Modules:" | |
| if [ "${{ needs.release-modules.result }}" = "success" ]; then | |
| echo "✅ Module releases: Completed successfully" | |
| else | |
| echo "❌ Module releases: Some releases failed" | |
| fi | |
| echo " Modules processed: $(echo '${{ needs.detect-changes.outputs.modules_with_changes }}' | jq -r '.[]' | tr '\n' ' ')" | |
| else | |
| echo "⏭️ Modules: No relevant changes, no releases needed" | |
| fi | |
| echo "================================================" |