diff --git a/.github/workflows/module-release.yml b/.github/workflows/module-release.yml index 294219d0..403846d4 100644 --- a/.github/workflows/module-release.yml +++ b/.github/workflows/module-release.yml @@ -33,6 +33,20 @@ on: - minor - major default: 'patch' + workflow_call: + inputs: + module: + description: 'Module to release' + required: true + type: string + version: + description: 'Version to release (leave blank for auto-increment)' + required: false + type: string + releaseType: + description: 'Release type' + required: true + type: string jobs: prepare-release: diff --git a/.github/workflows/release-all.yml b/.github/workflows/release-all.yml new file mode 100644 index 00000000..8428e90c --- /dev/null +++ b/.github/workflows/release-all.yml @@ -0,0 +1,248 @@ +# 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 + MODULES_JSON=$(echo "$MODULES_WITH_CHANGES" | tr ' ' '\n' | grep -v '^$' | jq -R . | jq -s .) + 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 "================================================" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc08ae8a..6cba2127 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,16 @@ on: - minor - major default: 'patch' + workflow_call: + inputs: + version: + description: 'Version to release (leave blank for auto-increment)' + required: false + type: string + releaseType: + description: 'Release type' + required: true + type: string jobs: release: