diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index f7317f3878..455d4cdbb3 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -12,6 +12,12 @@ on: - '**.md' - 'docs/**' workflow_dispatch: + inputs: + force_run: + description: 'Force run all jobs (bypass detect-changes)' + required: false + type: boolean + default: false jobs: detect-changes: @@ -32,6 +38,17 @@ jobs: - name: Detect relevant changes id: detect run: | + # If force_run is true, run everything + if [ "${{ inputs.force_run }}" = "true" ]; then + echo "run_lint=true" >> "$GITHUB_OUTPUT" + echo "run_js_tests=true" >> "$GITHUB_OUTPUT" + echo "run_ruby_tests=true" >> "$GITHUB_OUTPUT" + echo "run_dummy_tests=true" >> "$GITHUB_OUTPUT" + echo "run_generators=true" >> "$GITHUB_OUTPUT" + echo "docs_only=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + BASE_REF="${{ github.event.pull_request.base.sha || github.event.before || 'origin/master' }}" script/ci-changes-detector "$BASE_REF" shell: bash @@ -52,9 +69,9 @@ jobs: - ruby-version: '3.2' dependency-level: 'minimum' exclude: - # Skip minimum dependency matrix on regular PRs (run only on master/workflow_dispatch) - - ruby-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && '3.2' || '' }} - dependency-level: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && 'minimum' || '' }} + # Skip minimum dependency matrix on regular PRs (run only on master/workflow_dispatch/force_run) + - ruby-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && inputs.force_run != true && '3.2' || '' }} + dependency-level: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && inputs.force_run != true && 'minimum' || '' }} env: SKIP_YARN_COREPACK_CHECK: 0 BUNDLE_FROZEN: ${{ matrix.dependency-level == 'minimum' && 'false' || 'true' }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 908cc28c85..0b02d517ba 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,6 +12,12 @@ on: - '**.md' - 'docs/**' workflow_dispatch: + inputs: + force_run: + description: 'Force run all jobs (bypass detect-changes)' + required: false + type: boolean + default: false jobs: detect-changes: @@ -32,6 +38,17 @@ jobs: - name: Detect relevant changes id: detect run: | + # If force_run is true, run everything + if [ "${{ inputs.force_run }}" = "true" ]; then + echo "run_lint=true" >> "$GITHUB_OUTPUT" + echo "run_js_tests=true" >> "$GITHUB_OUTPUT" + echo "run_ruby_tests=true" >> "$GITHUB_OUTPUT" + echo "run_dummy_tests=true" >> "$GITHUB_OUTPUT" + echo "run_generators=true" >> "$GITHUB_OUTPUT" + echo "docs_only=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + BASE_REF="${{ github.event.pull_request.base.sha || github.event.before || 'origin/master' }}" script/ci-changes-detector "$BASE_REF" shell: bash @@ -53,10 +70,10 @@ jobs: node-version: '20' dependency-level: 'minimum' exclude: - # Skip minimum dependency matrix on regular PRs (run only on master/workflow_dispatch) - - ruby-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && '3.2' || '' }} - node-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && '20' || '' }} - dependency-level: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && 'minimum' || '' }} + # Skip minimum dependency matrix on regular PRs (run only on master/workflow_dispatch/force_run) + - ruby-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && inputs.force_run != true && '3.2' || '' }} + node-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && inputs.force_run != true && '20' || '' }} + dependency-level: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && inputs.force_run != true && 'minimum' || '' }} runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -143,10 +160,10 @@ jobs: node-version: '20' dependency-level: 'minimum' exclude: - # Skip minimum dependency matrix on regular PRs (run only on master/workflow_dispatch) - - ruby-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && '3.2' || '' }} - node-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && '20' || '' }} - dependency-level: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && 'minimum' || '' }} + # Skip minimum dependency matrix on regular PRs (run only on master/workflow_dispatch/force_run) + - ruby-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && inputs.force_run != true && '3.2' || '' }} + node-version: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && inputs.force_run != true && '20' || '' }} + dependency-level: ${{ github.event_name == 'pull_request' && github.ref != 'refs/heads/master' && inputs.force_run != true && 'minimum' || '' }} runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pro-integration-tests.yml b/.github/workflows/pro-integration-tests.yml index 6b0d3088f4..a8c0a149db 100644 --- a/.github/workflows/pro-integration-tests.yml +++ b/.github/workflows/pro-integration-tests.yml @@ -6,6 +6,12 @@ on: - 'master' pull_request: workflow_dispatch: + inputs: + force_run: + description: 'Force run all jobs (bypass detect-changes)' + required: false + type: boolean + default: false defaults: run: @@ -27,6 +33,14 @@ jobs: id: detect working-directory: . run: | + # If force_run is true, run everything + if [ "${{ inputs.force_run }}" = "true" ]; then + echo "run_pro_lint=true" >> "$GITHUB_OUTPUT" + echo "run_pro_tests=true" >> "$GITHUB_OUTPUT" + echo "docs_only=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + BASE_REF="${{ github.event.pull_request.base.sha || github.event.before || 'origin/master' }}" script/ci-changes-detector "$BASE_REF" shell: bash diff --git a/.github/workflows/pro-lint.yml b/.github/workflows/pro-lint.yml index 3702567993..92be479934 100644 --- a/.github/workflows/pro-lint.yml +++ b/.github/workflows/pro-lint.yml @@ -6,6 +6,12 @@ on: - 'master' pull_request: workflow_dispatch: + inputs: + force_run: + description: 'Force run all jobs (bypass detect-changes)' + required: false + type: boolean + default: false defaults: run: @@ -27,6 +33,14 @@ jobs: id: detect working-directory: . run: | + # If force_run is true, run everything + if [ "${{ inputs.force_run }}" = "true" ]; then + echo "run_pro_lint=true" >> "$GITHUB_OUTPUT" + echo "run_pro_tests=true" >> "$GITHUB_OUTPUT" + echo "docs_only=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + BASE_REF="${{ github.event.pull_request.base.sha || github.event.before || 'origin/master' }}" script/ci-changes-detector "$BASE_REF" shell: bash diff --git a/.github/workflows/pro-package-tests.yml b/.github/workflows/pro-package-tests.yml index fba39b815c..13a3dfe5f4 100644 --- a/.github/workflows/pro-package-tests.yml +++ b/.github/workflows/pro-package-tests.yml @@ -6,6 +6,12 @@ on: - 'master' pull_request: workflow_dispatch: + inputs: + force_run: + description: 'Force run all jobs (bypass detect-changes)' + required: false + type: boolean + default: false defaults: run: @@ -27,6 +33,14 @@ jobs: id: detect working-directory: . run: | + # If force_run is true, run everything + if [ "${{ inputs.force_run }}" = "true" ]; then + echo "run_pro_lint=true" >> "$GITHUB_OUTPUT" + echo "run_pro_tests=true" >> "$GITHUB_OUTPUT" + echo "docs_only=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + BASE_REF="${{ github.event.pull_request.base.sha || github.event.before || 'origin/master' }}" script/ci-changes-detector "$BASE_REF" shell: bash diff --git a/.github/workflows/run-skipped-ci.yml b/.github/workflows/run-skipped-ci.yml index 13e2afd9fe..e589cf9a43 100644 --- a/.github/workflows/run-skipped-ci.yml +++ b/.github/workflows/run-skipped-ci.yml @@ -81,39 +81,85 @@ jobs: sha: pr.data.head.sha }; - - name: Trigger all workflows and collect results + - name: Get skipped checks and trigger workflows id: trigger_workflows uses: actions/github-script@v7 with: script: | const prData = ${{ steps.pr.outputs.result }}; - const workflows = [ - 'main.yml', - 'examples.yml', - 'pro-integration-tests.yml', - 'pro-package-tests.yml' - ]; + + // Fetch PR checks to find skipped ones + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }); + + const checks = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: pr.head.sha, + per_page: 100 + }); + + // Find all skipped checks + const skippedChecks = checks.data.check_runs.filter(check => + check.conclusion === 'SKIPPED' && check.status === 'COMPLETED' + ); + + console.log(`Found ${skippedChecks.length} skipped checks:`); + skippedChecks.forEach(check => { + console.log(` - ${check.name} (${check.workflow_name})`); + }); + + // Map workflow names to workflow files + const workflowMap = { + 'Main test': 'main.yml', + 'Generator tests': 'examples.yml', + 'React on Rails Pro - Integration Tests': 'pro-integration-tests.yml', + 'React on Rails Pro - Package Tests': 'pro-package-tests.yml', + 'React on Rails Pro - Lint': 'pro-lint.yml' + }; + + // Get unique workflows that have skipped checks + const uniqueWorkflows = new Set(); + skippedChecks.forEach(check => { + if (workflowMap[check.workflow_name]) { + uniqueWorkflows.add(check.workflow_name); + } + }); const succeeded = []; const failed = []; + const notApplicable = []; - // Trigger all workflows - for (const workflowId of workflows) { + // Trigger each workflow that has skipped checks + for (const workflowName of uniqueWorkflows) { + const workflowFile = workflowMap[workflowName]; try { await github.rest.actions.createWorkflowDispatch({ owner: context.repo.owner, repo: context.repo.repo, - workflow_id: workflowId, - ref: prData.ref + workflow_id: workflowFile, + ref: prData.ref, + inputs: { + force_run: 'true' + } }); - console.log(`✅ Triggered ${workflowId}`); - succeeded.push(workflowId); + console.log(`✅ Triggered ${workflowFile} (${workflowName})`); + succeeded.push({ id: workflowFile, name: workflowName }); } catch (error) { - console.error(`❌ Failed to trigger ${workflowId}:`, error.message); - failed.push({ workflow: workflowId, error: error.message }); + console.error(`❌ Failed to trigger ${workflowFile}:`, error.message); + failed.push({ workflow: workflowName, error: error.message }); } } + // Note workflows with no skipped checks + if (uniqueWorkflows.size === 0) { + console.log('ℹ️ No skipped checks found - all tests already running on this PR'); + notApplicable.push('No skipped checks to run'); + } + // Wait a bit for workflows to queue if (succeeded.length > 0) { console.log('Waiting 5 seconds for workflows to queue...'); @@ -132,48 +178,55 @@ jobs: created: `>${new Date(Date.now() - 60000).toISOString()}` }); - for (const workflowId of succeeded) { + for (const workflow of succeeded) { const found = runs.data.workflow_runs.some(run => - run.path === `.github/workflows/${workflowId}` && + run.path === `.github/workflows/${workflow.id}` && run.head_sha === prData.sha && run.event === 'workflow_dispatch' ); if (found) { - verified.push(workflowId); + verified.push(workflow); } else { - notFound.push(workflowId); + notFound.push(workflow); } } } // Build the comment body based on actual results - let status = '✅ **Successfully triggered and verified all workflows**'; - if (failed.length > 0 && notFound.length > 0) { + let status; + if (notApplicable.length > 0) { + status = '✅ **All checks are already running - nothing to do!**'; + } else if (failed.length > 0 && notFound.length > 0) { status = '❌ **Failed to trigger or verify workflows**'; } else if (failed.length > 0) { status = '⚠️ **Some workflows failed to trigger**'; } else if (notFound.length > 0) { status = '⚠️ **Workflows triggered but not yet verified**'; + } else { + status = '✅ **Successfully triggered skipped CI checks**'; } - const verifiedList = verified.length > 0 ? verified.map(w => `- ✅ ${w}`).join('\n') : ''; - const notFoundList = notFound.length > 0 ? `\n\n**Triggered but not yet queued (may still start):**\n${notFound.map(w => `- ⏳ ${w}`).join('\n')}` : ''; + // List the skipped checks we found + const skippedChecksList = skippedChecks.length > 0 + ? `\n**Skipped checks detected:**\n${skippedChecks.map(c => `- ${c.name} (${c.workflow_name})`).join('\n')}` + : ''; + + const verifiedList = verified.length > 0 ? `\n**Triggered workflows:**\n${verified.map(w => `- ✅ ${w.name}`).join('\n')}` : ''; + const notFoundList = notFound.length > 0 ? `\n\n**Triggered but not yet queued (may still start):**\n${notFound.map(w => `- ⏳ ${w.name}`).join('\n')}` : ''; const failedList = failed.length > 0 ? `\n\n**Failed to trigger:**\n${failed.map(f => `- ❌ ${f.workflow}: ${f.error}`).join('\n')}` : ''; - const body = `🚀 **Full CI Suite Results** + const body = `🚀 **Skipped CI Checks - Trigger Results** ${status} + ${skippedChecksList} + ${verifiedList}${notFoundList}${failedList} - ${verifiedList ? `**Verified workflows:**\n${verifiedList}` : ''}${notFoundList}${failedList} + ${verified.length > 0 ? `\n**Note:** These workflows will run with \`force_run: true\` to bypass detect-changes logic that caused them to skip. - ${verified.length > 0 ? `\nThese will run all CI jobs including those normally skipped on PRs: - - ✅ Minimum dependency versions (Ruby 3.2, Node 20) - - ✅ All example app tests - - ✅ Pro package integration tests - - ✅ Pro package unit tests + View progress in the [Actions tab](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions).` : ''} - View progress in the [Actions tab](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions).` : ''}`; + ${notApplicable.length > 0 ? `\nAll CI checks are already running on this PR. Use this command when you see skipped checks that you want to run.` : ''}`; // Post the comment await github.rest.issues.createComment({