IMPORTANT: Developer documentation for the current development branch. This content is unreleased, may change without notice, and must not be treated as Buildish release documentation.

Buildish no-gradle-wrapper-jar blueprint

Use Gradle Wrapper launchers without tracking the Wrapper JAR in Git.

Buildish no-gradle-wrapper-jar blueprint

The blueprint lets a project keep one or both ordinary gradlew and gradlew.bat entry points without tracking gradle/wrapper/gradle-wrapper.jar in Git. A missing JAR is downloaded to ignored project-local state and checked against a committed SHA-256 before it becomes executable.

No compiled Wrapper executable is tracked in Git. Exclusion from a source release remains an objective. Release publication and any unconditional source-release claim remain blocked until the real component archive assembler proves that exclusion with a local ignored Wrapper JAR present.

Source acquisition

There is intentionally no stable release selector yet. Obtain a full <reviewed-commit> SHA through a channel you trust, then clone and select exactly that revision without executing project content:

1git clone https://github.com/buildish-tooling/buildish-no-gradle-wrapper-jar.git
2cd buildish-no-gradle-wrapper-jar
3reviewed_commit=REPLACE_WITH_FULL_REVIEWED_COMMIT_SHA
4git checkout --detach "$reviewed_commit"
5test "$(git rev-parse HEAD)" = "$reviewed_commit"

Review the selected commit and complete source tree before copying any product file into a consumer project. The commit SHA identifies bytes; deciding that the revision is trusted remains an adopter responsibility.

Product files

Acquire the init script and the bootstrap helper for each launcher the project maintains from an exact reviewed revision of this component. Copy them basename-for-basename into the consumer project’s gradle/ directory:

Reviewed component source Consumer destination
buildish-wrapper-bootstrap.sh gradle/buildish-wrapper-bootstrap.sh
buildish-wrapper-bootstrap.ps1 gradle/buildish-wrapper-bootstrap.ps1
buildish-wrapper.init.gradle.kts gradle/buildish-wrapper.init.gradle.kts

The first implementation has no automatic installer, release bootstrap, or download-and-execute shortcut. Acquiring, authenticating, and reviewing these executable source files is an adopter responsibility outside the runtime JAR download guarantee.

The init script makes the native top-level :wrapper task the single path that patches the maintained launcher set and writes Wrapper JAR metadata. A POSIX-only or Windows-only project remains that way; if neither launcher exists before initial adoption, the normal Gradle pair is retained. Do not copy launcher patches from examples or calculate the Buildish properties by hand.

Wrapper configuration

After successful adoption, gradle/wrapper/gradle-wrapper.properties contains exactly one canonical line for each Buildish-owned property:

1buildishWrapperJarVersion=<canonical three-component stable Gradle version>
2buildishWrapperJarSha256Sum=<lowercase 64-character Wrapper JAR SHA-256>

The enhanced Wrapper task derives this pair from the Gradle process and JAR that generated the launchers. The pair is independent from the target distribution settings:

1distributionUrl=https\://services.gradle.org/distributions/gradle-<version>-bin.zip
2distributionSha256Sum=<lowercase 64-character distribution ZIP SHA-256>

The init script accepts canonical stable Gradle versions and official binary distribution URLs with a canonical SHA-256. It records the digest of the JAR actually generated by the executing Gradle process. The Wrapper JAR version may temporarily differ from the target distribution version during a normal Gradle Wrapper upgrade or downgrade.

Initial adoption

Perform adoption from a clean, reviewable project state. Keep the existing Wrapper JAR available until the enhanced Wrapper task succeeds. If that JAR is already untracked and cannot be recovered from version control, make a collision-safe project-local backup first.

Confirm that distributionSha256Sum is already present for the selected distribution, or obtain the reviewed value before running the task.

Project with a functional POSIX launcher

From the project root, explicitly attach the newly copied init script and force the native task to execute:

1./gradlew \
2  --init-script gradle/buildish-wrapper.init.gradle.kts \
3  --rerun-tasks \
4  :wrapper

If the properties file does not already contain the reviewed distribution checksum, also pass:

1--gradle-distribution-sha256-sum <reviewed-distribution-sha256>

Project with a functional native-Windows launcher

Use a same-directory stable copy because the Wrapper task may replace gradlew.bat while cmd.exe is still executing it. Save this as a temporary PowerShell script in the project root, for example run-stable-wrapper.ps1:

 1$wrapperExitCode = 1
 2$stableLauncher = Join-Path $PWD (
 3  '.gradlew-buildish-update-{0}.bat' -f [System.Guid]::NewGuid().ToString('N')
 4)
 5try {
 6  Copy-Item -LiteralPath .\gradlew.bat -Destination $stableLauncher `
 7    -ErrorAction Stop
 8  & $stableLauncher @args
 9  $wrapperExitCode = $LASTEXITCODE
10} finally {
11  Remove-Item -LiteralPath $stableLauncher -Force -ErrorAction SilentlyContinue
12}
13exit $wrapperExitCode

Run that script with the adoption arguments:

1powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass `
2  -File .\run-stable-wrapper.ps1 `
3  --init-script gradle\buildish-wrapper.init.gradle.kts `
4  --rerun-tasks `
5  :wrapper

Pass the reviewed --gradle-distribution-sha256-sum value as an additional argument when it is not already configured. The GUID-named copy preserves APP_HOME, propagates success or any native nonzero exit code, and is removed in finally. An interrupted update may leave an ignored copy; remove it only after confirming that no update still uses it. Remove the temporary run-stable-wrapper.ps1 driver after the reviewed operation as well.

Project without a functional Wrapper

An independently installed stable Gradle may establish or recover the integration. Run it from the project root and supply every target choice explicitly:

1gradle \
2  --init-script gradle/buildish-wrapper.init.gradle.kts \
3  --rerun-tasks \
4  :wrapper \
5  --gradle-version <reviewed-version> \
6  --distribution-type bin \
7  --gradle-distribution-sha256-sum <reviewed-distribution-sha256>

The installed Gradle runtime, its user-home state, and ambient init scripts are trusted generation inputs. A bare gradle wrapper command does not attach the project-local script and may replace the integrated launchers.

Review and tracking changes

After the task succeeds:

  1. Compare the generated Wrapper JAR version and digest with the reviewed configuration and tested-version evidence.

  2. Review the complete changes to every maintained launcher and gradle-wrapper.properties.

  3. Add these exact root-scoped ignore rules:

    /gradle/wrapper/gradle-wrapper.jar
    /.gradlew-buildish-update-*.bat
    
  4. Add these exact root-scoped checkout-attribute rules:

    /gradlew text eol=lf
    /gradlew.bat -text
    /gradle/buildish-wrapper-bootstrap.sh text eol=lf
    /gradle/buildish-wrapper-bootstrap.ps1 text eol=lf
    /gradle/buildish-wrapper.init.gradle.kts text eol=lf
    

    These rules keep clean checkouts usable across Git core.autocrlf settings.

  5. Stop tracking the JAR without deleting the reviewed warm copy:

    1git rm --cached gradle/wrapper/gradle-wrapper.jar
    
  6. Run the repository invariant check and a cold --version smoke test.

Investigate every change outside the selected source files, maintained launchers, properties, and root-scoped ignore and checkout-attribute rules before committing the adoption.

Cold and warm execution

Cold path

When gradle/wrapper/gradle-wrapper.jar is absent, the bootstrap requires exactly one canonical stable Gradle Wrapper JAR version and lowercase digest. It derives that version’s fixed raw-tagged Gradle Wrapper JAR URL; membership in the finite test manifest is not required. The response is downloaded to a unique sibling temporary file with bounded time and a 10 MiB limit. Only bytes whose SHA-256 equals the committed pin are moved into the executable JAR path.

The bootstrap does not download checksum or signature sidecars and does not invoke GPG. Gradle separately applies distributionSha256Sum when it provisions a missing target distribution; the presence of that property is not evidence that an already cached distribution was reverified.

Warm path

An existing local JAR is trusted checkout state and is not hashed again. The POSIX launcher still sources its small helper to attach the project-local init script. The Windows launcher performs the existence check in cmd.exe and starts neither PowerShell nor network or hashing work on the warm path.

This blueprint does not defend against an actor who can modify the checkout, committed pin, helper sources, build logic, or existing ignored JAR. That actor already controls executable project state. See the canonical threat model for the full boundary and reporting guidance.

Updating Gradle Wrapper

First pass

Run the ordinary enhanced Wrapper task with the reviewed target distribution checksum:

1./gradlew wrapper \
2  --gradle-version <canonical-stable-target-version> \
3  --distribution-type bin \
4  --gradle-distribution-sha256-sum <reviewed-distribution-sha256>

If Wrapper JAR A launches target distribution B, this pass may correctly leave the Buildish Wrapper JAR pair at A while moving distributionUrl and distributionSha256Sum to B. That state is complete and supported; the two versions are intentionally independent.

On native Windows, the ordinary first A-to-B pass is supported directly when the active and generated launcher control flow is unchanged. Use the stable copy whenever a pass may cross launcher generations.

Optional second pass

A later ./gradlew wrapper invocation runs under distribution B and can refresh the generated Wrapper implementation and Buildish JAR pair to B. This pass is optional. On native Windows it must run through the GUID-named stable-copy script above when it may replace one launcher generation with another.

Always review every maintained launcher, the independent Wrapper JAR pair, the target URL, and distributionSha256Sum together.

Renovate

The supported Renovate contract uses Renovate 44.7.0, the gradle-wrapper manager, binarySource="global", and administrator configuration allowedUnsafeExecutions=["gradleWrapper"]. It executes the project launcher and root :wrapper task, allowing the init script to restore both launcher integrations and the two Buildish properties in the files Renovate collects.

A one-pass Renovate update may validly retain Wrapper JAR A while selecting distribution B. CI must validate each value generically instead of requiring version equality or membership in the repository’s finite test matrix. An updater that directly replaces generated launchers without executing the enhanced task is unsupported and should fail the repository invariant check.

Recovery and failure behavior

  • Missing, duplicate, or malformed Buildish properties fail before download or execution.
  • Network, timeout, oversize, and digest failures remove the temporary download and do not publish it as the Wrapper JAR.
  • If an existing local JAR is corrupt or otherwise unusable, delete only gradle/wrapper/gradle-wrapper.jar and rerun the command to take the verified cold path.
  • If Wrapper postprocessing fails, inspect the tracked diff. Restore generated Wrapper files from version control or rerun :wrapper from a known-good stable launcher with the init script attached explicitly.
  • If a bare installed gradle wrapper removes the integration, rerun the explicit installed-Gradle recovery command above.
  • If a generated launcher no longer contains an accepted anchor, do not patch it manually or guess. Add reviewed compatibility evidence and implementation support first.

The helpers do not repair arbitrary launcher changes outside the enhanced Wrapper task and do not verify an existing ignored JAR on every invocation.

Compatibility evidence

Product behavior is not restricted to an enumerated version table. Any canonical three-component stable Gradle version uses the fixed upstream Wrapper JAR path derived from that version and the committed digest authorizes the downloaded bytes. Wrapper generation accepts official binary distribution URLs and canonical SHA-256 values; upgrades and downgrades are both valid.

tests/fixtures/compatibility-manifest.json is a finite test matrix, not a product allowlist. It records exact JAR digests, distribution checksums, launcher fixtures, and lifecycle cases for the versions exercised in this repository. Newer versions are supported when their generated launchers retain the required structural invocation shape. Unfamiliar launcher templates fail closed so that support can be reviewed without adding a version table to the product.

Custom distributions, mirrors, -all archives, prereleases, snapshots, and Gradle forks remain outside the supported contract.

Required environment

Scenario Required environment
Source acquisition and adoption review Git and the normal tools used to review the selected revision and project diff
POSIX runtime POSIX shell, curl 8.4.0 or newer, and either sha256sum or shasum
Native Windows runtime cmd.exe, Windows PowerShell 5.1 or later, and the required .NET HTTP and SHA-256 APIs
Wrapper generation An existing Wrapper or independently installed stable Gradle plus its required JDK
Repository validation The tools listed in the component’s contributing and test documentation

Nothing is installed under $GRADLE_USER_HOME/init.d/, $GRADLE_HOME/init.d/, or another user-global location. The project-local init script is attached explicitly through the launchers.


Threat model

Security boundaries and triage guidance for the minimal Gradle Wrapper bootstrap.