Skip to content

fix(ci): correct reusable-dispatch stage skip expression#1730

Open
ifireball wants to merge 1 commit into
fullsend-ai:mainfrom
ifireball:fix/1199-dispatch-skip-expression
Open

fix(ci): correct reusable-dispatch stage skip expression#1730
ifireball wants to merge 1 commit into
fullsend-ai:mainfrom
ifireball:fix/1199-dispatch-skip-expression

Conversation

@ifireball
Copy link
Copy Markdown
Contributor

Summary

  • Fixes the route job stage output in .github/workflows/reusable-dispatch.yml so per-repo silent-skip works when a routed stage's role is not in defaults.roles.
  • Replaces skipped == 'true' && '' || stage (always evaluates to stage because '' is falsy in GHA) with skipped != 'true' && stage || ''.

Fixes #1199

Test plan

  • On a per-repo install, trigger dispatch for a stage whose mapped role is not in .fullsend/config.yaml rolesroute should output empty stage and all stage jobs should be skipped.
  • Trigger dispatch for a configured role — stage jobs should still run as before.

Made with Cursor

@ifireball ifireball requested a review from ralphbean June 1, 2026 08:02
@ifireball ifireball self-assigned this Jun 1, 2026
@ifireball ifireball force-pushed the fix/1199-dispatch-skip-expression branch from 5594d71 to 7d24618 Compare June 1, 2026 08:02
The per-repo silent-skip output used a GHA expression where empty string
is falsy, so skipped roles still dispatched. Fixes fullsend-ai#1199.

Signed-off-by: Barak Korren <bkorren@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Site preview

Preview: https://4a204e0c-site.fullsend-ai.workers.dev

Commit: 1efe7cbd2b19a411fe93dee157439f455c2e1feb

@fullsend-ai-review
Copy link
Copy Markdown

Review

Findings

Medium

Low

  • [docs-staleness] docs/superpowers/plans/2026-05-15-silent-skip-unconfigured-roles.md:179 — The plan document contains the buggy expression (skipped == 'true' && '' || steps.route.outputs.stage) that this PR fixes. Since this is a historical plan document rather than a user guide, the staleness is low impact, but updating line 179 to reflect the corrected expression would keep the plan accurate for future reference.

Info

  • [correctness] .github/workflows/reusable-dispatch.yml:52 — The fix is correct. In GHA expressions, &&/|| return operand values (like JavaScript short-circuit evaluation). The old expression skipped == 'true' && '' || stage always evaluated to stage because true && '' yields '' (falsy), which falls through via || to stage. The new expression skipped != 'true' && stage || '' correctly places the truthy value (stage) in the if-true position, so the ternary pattern works as intended. This is consistent with the same skipped != 'true' pattern already used on line 278.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Jun 1, 2026
pull-requests: read
outputs:
stage: ${{ steps.role-check.outputs.skipped == 'true' && '' || steps.route.outputs.stage }}
stage: ${{ steps.role-check.outputs.skipped != 'true' && steps.route.outputs.stage || '' }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It does look like this does not produce a string output, it seems like it would produce a boolean. If it is bash then I guess this makes sense but I'm not sure. If you are confident this does what it is supposed to do, then go ahead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The \!= does produce a boolean, but GHA short-circuit semantics keep it from leaking into the output. When skipped is 'true': false && stage short-circuits to false, then false || '' yields ''. When skipped isn't 'true': true && stage yields the stage string (e.g. 'review'), then 'review' || '' yields 'review'. So the final value is always either the stage string or empty — the intermediate boolean never reaches the output.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For reference, the GHA expression evaluator source confirms && and || return operand values, not booleans — same semantics as JavaScript:

visitLogical(logical: Logical): data.ExpressionData {
  let result: data.ExpressionData;
  for (const arg of logical.args) {
    result = this.eval(arg);
    if (
      (logical.operator.type === TokenType.AND && falsy(result)) ||
      (logical.operator.type === TokenType.OR && truthy(result))
    ) {
      break;
    }
  }
  return result!;
}

@ifireball
Copy link
Copy Markdown
Contributor Author

@ralphbean @rh-hemartin @ascerra can we merge this or should we wait until after Wednesday?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Merged PR contains broken GHA expression that defeats the silent-skip feature

3 participants