Skip to content

feat/v2.1-polish-dsl-errors: sharpen v2 config-error UX + pin v2.1.0 release via .release-version#35

Merged
vedanthvdev merged 1 commit intomasterfrom
feat/v2.1-polish-dsl-errors
Apr 23, 2026
Merged

feat/v2.1-polish-dsl-errors: sharpen v2 config-error UX + pin v2.1.0 release via .release-version#35
vedanthvdev merged 1 commit intomasterfrom
feat/v2.1-polish-dsl-errors

Conversation

@vedanthvdev
Copy link
Copy Markdown
Owner

Why

Three small-but-sharp fixes uncovered by the v2.0.0-local pilot against security-service, bundled into one PR because they all close the same gap: "something in my build.gradle is wrong → the plugin told me exactly what to change".

Two fixes are DSL-error polish; the third teaches the release workflow a version-override mechanism so this PR's own merge can ship as v2.1.0 on a normal push-to-master without needing workflow_dispatch coordination. The mechanism is reusable for future minor/major bumps.

What

1. Targeted v2 migration hint on the three removed legacy knobs

Before (Gradle's generic error):

> Could not set unknown property 'runAllIfNoMatches' for extension
  'affectedTests' of type io.affectedtests.gradle.AffectedTestsExtension.

After (v2.1, via setRunAllIfNoMatches / setRunAllOnNonJavaChange / setExcludePaths shims on the extension):

> affectedTests.runAllIfNoMatches was removed in v2.0.0. Use
  onEmptyDiff = "full_suite" and/or onDiscoveryEmpty = "full_suite"
  instead (or set mode = "ci" / "strict" to get those defaults). See
  CHANGELOG.md v2.0 for the full migration table.

Kotlin DSL already gets a compile error naming the removed property so shims only fire for Groovy — which is where the unhelpful default lived.

2. gradlewTimeoutSeconds range check at configuration time

The v1.9.22 value >= 0 check lived on the core builder and only ran at task-execution. IDE sync, ./gradlew help, ./gradlew tasks all stayed green against a misconfigured build; operators only saw the rejection when they ran affectedTest.

v2.1 mirrors the check in AffectedTestsPlugin#apply via project.afterEvaluate, so the same message fires at configuration completion — IDE sync surfaces it immediately. Builder-side check stays as belt-and-braces for programmatic callers that bypass the DSL.

3. .release-version override file for merge-to-master minor/major releases

Until v2.1, the only way to ship a non-patch bump on a merge-to-master release was workflow_dispatch with an explicit version input. That meant every minor release had a coordination window where the auto-release on the merge push could mint an unwanted patch tag before the manual dispatch took over.

The workflow now reads a .release-version SemVer file at repo root (regex-validated before use) when no dispatch input is present, passes it to axion-release, and deletes the file in a [skip ci] follow-up commit after the tag → publish → GitHub-Release triad succeeds.

This PR commits .release-version = 2.1.0 so its own merge ships as v2.1.0 on the Gradle Plugin Portal. The file self-cleans afterwards so subsequent patch releases resume auto-incrementing.

Priority order in release.yml:

  1. workflow_dispatch input (unchanged)
  2. .release-version file (new)
  3. Auto-patch-increment (unchanged)

Regression coverage

Pinned in AffectedTestsPluginTest:

  • legacyKnobAssignmentThrowsWithV2MigrationHint_runAllIfNoMatches
  • legacyKnobAssignmentThrowsWithV2MigrationHint_runAllOnNonJavaChange
  • legacyKnobAssignmentThrowsWithV2MigrationHint_excludePaths
  • negativeGradlewTimeoutFailsAtConfigurationTime

Each message assertion pins both the user-facing knob name AND the v2 replacement / valid-range bound, so a well-intentioned "tidy up the error message" refactor that drops either half is caught.

Revert-verified: reverting AffectedTestsPlugin.java in isolation (keeping the rest) makes negativeGradlewTimeoutFailsAtConfigurationTime fail at the assertion, not at compile. Reverting the extension shims makes the other three tests fail to compile — which is also a guard against silent regression.

The release-workflow logic was simulated locally against 9 input cases (input-wins, file-used, trailing-newline trimmed, empty file rejected, bad SemVer rejected, pre-release allowed, ...) before relying on it to ship v2.1.0.

Test plan

  • ./gradlew check green on branch HEAD
  • Revert-checks for both fixes
  • YAML valid (ruby -e 'require "yaml"; YAML.load_file(...)')
  • Release-workflow version-resolver simulated across 9 cases
  • On merge: .release-version picked up → tag v2.1.0 → publish to Plugin Portal → GitHub Release → self-cleanup commit [skip ci]
  • Post-merge: re-pilot the security-service scenarios against io.github.vedanthvdev.affectedtests:2.1.0 pulled directly from the portal (not mavenLocal)

Notes for reviewers

  • The v2 breaking changes are already in master under the v1.9.23 tag that auto-patch-increment minted on the PR feat/v2.0-remove-legacy-knobs: Phase 3 — delete the v1 DSL surface for v2.0.0 #34 merge — so the Plugin Portal has v2-content under a patch version already. v2.1.0 is the first publicly tagged v2 release and supersedes v1.9.23 in name.
  • README §Versioning has the new decision matrix entry for the file-based override.

@vedanthvdev vedanthvdev reopened this Apr 23, 2026
…release via `.release-version`

Three small-but-sharp fixes that surfaced during the v2.0.0-local
pilot against security-service, bundled into one PR because they all
share the same goal: close the gap between "something in my
build.gradle is wrong" and "the plugin told me what to change". Two
are DSL-error polish, the third teaches the release workflow a new
version-override mechanism so this exact release can ship as v2.1.0
on a merge-to-master push without needing workflow_dispatch
coordination.

1. Targeted v2 migration hint on the three removed legacy knobs.
   Adds `setRunAllIfNoMatches` / `setRunAllOnNonJavaChange` /
   `setExcludePaths` shims on `AffectedTestsExtension` that intercept
   the Groovy DSL assignment and throw a `GradleException` naming
   both the removed v1 knob and its v2 replacement(s) — pointing at
   the exact onXxx action, the mode shortcut where one exists, and
   the CHANGELOG section for the full table. Kotlin DSL callers
   already get a compile error naming the removed property so the
   shims only fire in Groovy, which is where the unhelpful default
   error lived.

2. `gradlewTimeoutSeconds` range check at configuration time.
   The v1.9.22 check (value >= 0, 0 disables) lived on the core
   builder and only ran at task-execution, which meant IDE sync /
   `./gradlew help` / `./gradlew tasks` all stayed green against a
   misconfigured build and the operator only saw the rejection when
   they tried to run affectedTest. Adds a mirror check in the
   plugin's `project.afterEvaluate` so the same message fires at
   configuration end. Builder-side check stays as belt-and-braces
   for programmatic callers that bypass the DSL extension.

3. `.release-version` override file in `.github/workflows/release.yml`.
   Until now, shipping a non-patch bump on a merge-to-master release
   required running the release workflow via `workflow_dispatch` with
   an explicit `version` input. That meant every minor release had a
   coordination window where the auto-release on the merge push could
   mint an unwanted patch tag before the manual dispatch took over.
   The workflow now reads a `.release-version` SemVer file at repo
   root (validated via regex before use) when no dispatch input is
   present, passes it to axion-release, and deletes the file in a
   `[skip ci]` follow-up commit after the tag/publish/release triad
   succeeds. This PR commits `.release-version = 2.1.0` so its own
   merge ships as v2.1.0 on the portal; the file self-cleans
   afterwards so subsequent patch releases resume
   auto-incrementing.

Regression coverage for the DSL fixes is pinned in
`AffectedTestsPluginTest`:

- `legacyKnobAssignmentThrowsWithV2MigrationHint_runAllIfNoMatches`
- `legacyKnobAssignmentThrowsWithV2MigrationHint_runAllOnNonJavaChange`
- `legacyKnobAssignmentThrowsWithV2MigrationHint_excludePaths`
- `negativeGradlewTimeoutFailsAtConfigurationTime`

Each assertion pins both the user-facing knob name AND the v2
replacement / valid-range bound, so a well-intentioned "tidy up the
error message" refactor that drops either half is caught by the test
suite rather than rediscovered by the next migrating adopter.

The release-workflow logic was simulated end-to-end against nine
input cases (input-wins, file-used, trailing-newline trimmed, empty
file rejected, bad SemVer rejected, pre-release allowed, etc.) to
verify branching before relying on it to ship v2.1.0 itself.

CHANGELOG calls v2.1.0 out as the first publicly tagged v2 release,
bundling the v2.0 legacy-knob removal (already in master under the
v1.9.23 tag the auto-patch-incrementer minted) with these polish
fixes.
@vedanthvdev vedanthvdev force-pushed the feat/v2.1-polish-dsl-errors branch from a1f5744 to 7864101 Compare April 23, 2026 10:01
@vedanthvdev
Copy link
Copy Markdown
Owner Author

Triggering CI - workflow didn't dispatch on initial push.

@vedanthvdev vedanthvdev merged commit a3be2e7 into master Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant