Compare commits

..

254 Commits

Author SHA1 Message Date
Daz DeBoer
3edb3cb004 Reorder inputs in metadata file for v2.1 2021-12-30 18:16:51 -07:00
Daz DeBoer
3609b7787a Polish README for v2.1 2021-12-30 18:12:37 -07:00
Daz DeBoer
f804351b51 Merge pull request #131 from gradle/cache-jdks
- Cache downloaded Java Toolchains
- Treat configuration-cache entries as extracted entries
2021-12-30 16:19:02 -07:00
Daz DeBoer
39e51526fb Build outputs 2021-12-30 16:03:02 -07:00
Daz DeBoer
76ea8a76b2 Treat configuration-cache as an extracted entry
Instead of using a fallback strategy to locate a configuration-cache entry
based on the current job and git SHA, these entries are now keyed based on their
file content with the keys persisted in the primary Gradle User Home entry.

This removes the chance of having a configuration-cache entry restored that is
incompatible with the restored Gradle User Home state, and makes the logic easier
to understand.

This change involved a fairly major refactor, with the CacheEntryExtractor being
split out from the primary cache implementation, and adding a separate extractor
implementation for configuration-cache.
2021-12-30 16:03:02 -07:00
Daz DeBoer
12fc52a49a Combine java-toolchain entry definitions
- Remove configuration parameter for cache-entry-definitions. This allows richer modelling of extracted entries.
- Include both *.zip and *.tar.gz in entry definition for java-toolchains.
2021-12-30 16:03:02 -07:00
Daz DeBoer
c157d0a332 Cache downloaded Java Toolchains
Due to limitations with the way extracted entries are defined, a separate entry
is defined for *nix and windows.

Fixes #33
2021-12-30 16:02:02 -07:00
Daz DeBoer
c61f5d87b4 Test java-toolchain cache 2021-12-17 21:10:31 -07:00
Daz DeBoer
c208b4c1f7 Test caching with gradle-plugin project 2021-12-17 20:54:04 -07:00
Daz DeBoer
b4e6c2b28a Minor improvements to README 2021-12-17 13:34:22 -07:00
Daz DeBoer
b84b650c31 Merge pull request #130 from gradle/setup-gradle
Allow action to be used without Gradle execution
2021-12-17 13:08:36 -07:00
Daz DeBoer
eda0038770 Reduce the number of automated comments left on PR 2021-12-17 10:49:41 -07:00
Daz DeBoer
ed5b4ae5dc Verify GUH restore combined with Gradle execution 2021-12-17 10:44:29 -07:00
Daz DeBoer
d50632cfd1 Build outputs 2021-12-17 10:29:59 -07:00
Daz DeBoer
40c0a12211 Update README for use without Gradle execution 2021-12-17 10:29:59 -07:00
Daz DeBoer
e977669c8c Update most test workflows to separate config from execution 2021-12-17 10:29:59 -07:00
Daz DeBoer
0e8b9655a0 Allow action to be used without Gradle invocation
If the user supplies no 'arguments' parameter, the action will function as a
'setup-gradle' action, adding Gradle to the PATH and enabling other features
without actually running a Gradle build.

Any subsequent Gradle invocations in the workflow will benefit from:
- Save/restore of Gradle User Home
- Save/restore of configuration-cache data
- Capture of build-scan URLs

These features are enabled via Gradle User Home, so any Gradle invocation that
uses the same Gradle User Home will be included.
2021-12-17 10:29:58 -07:00
Daz DeBoer
367ce74a5f Merge pull request #128 from gradle/configuration-caching
Restore/save configuration-cache data in first action step. This will enable the use of the action for caching without managing all gradle invocations.
2021-12-17 10:28:09 -07:00
Daz DeBoer
3814d56740 Build outputs 2021-12-08 15:14:19 -07:00
Daz DeBoer
e1f84aa44d Restore/save configuration-cache data in first action
Previously, the action was restoring/saving the configuration-cache data for each
step that applied the action. In order to support Gradle invocations that are _not_
managed by the action, the configuration-cache restore is now performed in the initial
action step, and save is performed in the final post-action step.

The build root directories are recorded for each invocation via an init script.
2021-12-08 15:09:27 -07:00
Daz DeBoer
1041604f29 Ensure save/restore only on first action step
Instead of relying on the separate cache implementations to check for the
existence of cached products, we now explicitly track whether or not the execution
is the first time the action has been invoked for a job.
2021-12-08 15:09:27 -07:00
Daz DeBoer
253d6427fd Extract cache-reporting into separate file
Cache reporting is self-contained enough that it deserves some separation.
2021-12-08 15:09:26 -07:00
Daz DeBoer
69aad1f173 Test config-cache support for multiple builds in one job 2021-12-08 15:09:26 -07:00
Daz DeBoer
887e1a09ab Include original error in log messages 2021-12-08 14:44:57 -07:00
Daz DeBoer
f344e25b84 Attempt to reduce duplicate workflows on PR creation 2021-12-08 14:20:13 -07:00
Daz DeBoer
574cf0ddce Build outputs 2021-12-08 14:05:13 -07:00
Daz DeBoer
37f2880a8a Cache wrapper zips and generated jars individually
Using a single cache entry for all files of a type is necessary to avoid
overloading the cache service. However, this mechanism is not very efficient
for certain artifacts like wrapper zips and generated-gradle-jars, where the
same individual files are often shared between different jobs.

With this change, any configured file patterns that do not end in '*' will
be cached as individual files. At this time this includes downloaded wrapper
zips and generated-gradle-jars.

Fixes #78
2021-12-08 14:05:13 -07:00
Daz DeBoer
b88c4086b9 Don't overwrite title in build-scan notice 2021-12-08 13:07:52 -07:00
Daz DeBoer
aa9bf7774e Add build-scan comment to any PRs 2021-12-08 13:07:51 -07:00
Daz DeBoer
ed2ff1a448 Use a matrix for testing Gradle versions 2021-12-08 10:04:24 -07:00
Daz DeBoer
ddee3ca933 Update all outdated dependencies 2021-12-02 14:35:52 -07:00
Daz DeBoer
03d1894080 Update to build with NPM 8.1 2021-12-02 12:59:54 -07:00
Daz DeBoer
322805e800 Refactor: use a single .json file to describe all cached artifact bundles (#121)
This is a pure refactor, moving from a separate .cache file per bundle to a single cache-metadata.json file describing all bundles. Instead of storing cache metadata in a separate .cache file per artifact bundle, all of the metadata is now stored in a single `.json` file.

This will make it easier to implement more flexible artifact-caching strategies, such as caching each wrapper zip separately.

* Always include cache protocol version in cache key
* Store all cache metadata in a single JSON file
* Rename cache-metadata file and bump protocol version
* Polish and documentation
2021-11-28 10:19:56 -07:00
Daz DeBoer
92a1f98d35 Merge pull request #123 from gradle/toward-setup-gradle
Avoid using command-line modification to enhance build execution. Instead, files are written to Gradle User Home to achieve the same functionality.
2021-11-28 08:22:36 -07:00
Daz DeBoer
fdfc5bf12f Build outputs 2021-11-28 08:04:54 -07:00
Daz DeBoer
a25c0ce6cb Improve title on build scan notice 2021-11-28 08:04:54 -07:00
Daz DeBoer
76f85a724c Record build scan url directly from init script
Instead of writing the URL to a file on disk, reading it later and
using the Actions API to record the output parameter and write the notice,
these things are now done directly via Actions commands emitted directly
from the init script.
2021-11-27 20:49:10 -07:00
Daz DeBoer
45ef022607 Always initialize Gradle User Home with init script
The generated gradle.properties file and init script are not cached,
so need to be generated even when restoring from cache.
2021-11-27 20:45:49 -07:00
Daz DeBoer
a72af0b6a6 Persist build-scan-capture init script into Gradle User Home
Instead of writing this file to a temp directory and referencing it
on the Gradle command line, the init script is now written to Gradle
User Home so that it is picked up automatically.
2021-11-27 20:44:43 -07:00
Daz DeBoer
b0c29bffb7 Use a properties file to disable daemon execution
Instead of passing `--no-daemon` on the command line, the same
functionality is now acheived by writing a gradle.properties file
when initializing Gradle User Home.
2021-11-27 16:07:07 -07:00
Daz DeBoer
996094e8e8 Avoid failing job on any failure in post-action
Failures to store cache entries should not fail the action or the Job.
This fix attempts to catch and log any unexpected errors that occur when
saving cache entries.

Fixes: #119
Fixes: #120
2021-11-15 09:31:56 -07:00
Daz DeBoer
4137be6a8b Minor improvement to logging in post-action
- Ensure that "Caching Gradle state" group always has 1 message
- Only print cache report when entries were restored or saved
2021-11-05 08:35:45 -06:00
Daz DeBoer
4e899835b3 Avoid failing build on distributions cache errors
- Warn and continue on failure to restore a Gradle distribution from cache
- Warn and continue on failure to save a Gradle distribution to cache
- Extract common functionality for consistent handling of cache failures

Fixes #116
2021-11-05 07:01:48 -06:00
Jonathan Leitschuh
3812292b26 Add CodeQL Analysis (#114) 2021-11-02 09:56:02 -06:00
Daz DeBoer
c12283ec57 Add a section explaining advantages 2021-11-01 12:35:15 -06:00
Daz DeBoer
f2dc0d8256 Add section on optimizing cache effectiveness 2021-11-01 08:17:33 -06:00
Daz DeBoer
717db318c1 Fix logging of cache key for Gradle User Home 2021-10-31 20:49:29 -06:00
Daz DeBoer
2a57ddf74a Merge pull request #111 from gradle/dd/cache-report
Add a report to assist with diagnosing and optimizing cache performance
2021-10-30 21:53:01 +02:00
Daz DeBoer
230fd6b47f Build outputs 2021-10-30 13:46:29 -06:00
Daz DeBoer
472ac8a356 Report sizes of cache entries
Using the patched version of @actions/cache, we now report the total
size of cache entries restored/saved, as well as details of each one.
2021-10-30 13:45:39 -06:00
Daz DeBoer
3ba05ede1f Add patch for @actions/cache to expose entry size
There's no easy way to get the size of restored/saved cache entries
using the @actions/cache library. Rather than reimplement it, this commit
adds a patch that will be applied whenever 'npm install' is run.

The work is done by 'patch-package'.
2021-10-30 13:44:24 -06:00
Daz DeBoer
d785346c8c Use cache.description 2021-10-30 13:44:22 -06:00
Daz DeBoer
6ca4d4ade2 Improve formatting for caching report 2021-10-30 12:15:00 -06:00
Daz DeBoer
75cec40e58 Rename 'report' classes to 'listener'
- `CachingReport` -> `CacheListener`
- `CacheEntryReport` -> `CacheEntryListener`
2021-10-30 12:15:00 -06:00
Daz DeBoer
c317ccac62 Refactor: extract cache-base out of cache-utils 2021-10-30 12:15:00 -06:00
Daz DeBoer
a74bb0fad6 Monitor cache saves and add basic caching report
- Restore `CachingReport` instance in 'post' action
- Record keys for any entries saved
- Report caching activity as JSON in post action
2021-10-30 12:14:59 -06:00
Daz DeBoer
6ff2065a12 Upgrade Jest to address security warning 2021-10-30 12:11:59 -06:00
Daz DeBoer
727b4612ba Fix early return from bundle processing 2021-10-30 12:11:59 -06:00
Daz DeBoer
613f4ec588 Merge pull request #109 from gradle/dd/rc21
Fix issues for 2.0-rc.2
- Support multi-line strings for cache-tuning parameters #106 
- Include all downloaded files in `dependencies` bundle #100 
- Only restore configuration-cache if Gradle User Home is fully restored #107
2021-10-30 14:39:28 +02:00
Daz DeBoer
db6202adcd Build outputs 2021-10-30 06:29:25 -06:00
Daz DeBoer
f0f68e07c3 Better handling of .cache files that are not restored
- Remove any .cache file that is not restored
- Report on any .cache file that exists but has no config
2021-10-30 06:28:20 -06:00
Daz DeBoer
8ba5a0033b Track 'fully-restored' by tracking each cache restore
Instead of tracking a single 'fully-restored' flag, track the restore status of each
cache entry restore. If any of these are requested but not restored, then the overall
Gradle User Home cache is not fully restored.

Added special handling for the case when zero artifact bundles are set: this is used
in tests to simulate a not-fully-restored state.
2021-10-30 06:05:21 -06:00
Daz DeBoer
9edc2a11bd Only restore configuration-cache if Gradle Home is fully restored
Fixes #107
2021-10-29 09:34:36 -06:00
Daz DeBoer
079e4844d6 Split tests for caching
- Separate testing of Gradle Home caching from configuration-cache caching
- Add test for configuration-cache when Gradle Home is not fully restored
2021-10-29 09:34:35 -06:00
Daz DeBoer
4ebd000afd Bundle all downloaded dependency files
Previously, only .jar files were bundled, with other files (modules, POMs, zips, etc)
being left in Gradle User Home. All downloaded files are now included in the bundle.

Fixes #100
2021-10-29 08:03:03 -06:00
Daz DeBoer
063fc6a872 Allow source files to contain lines up to 120 characters
This avoids excessive line-feeds when reformatting code to 80 char lines.
2021-10-29 07:50:06 -06:00
Daz DeBoer
e3ada7e5c2 Use multiline input parameters instead of JSON input
The `gradle-home-cache-includes` and `gradle-home-cache-excludes` parameters were initially implemented
as JSON string inputs. This makes these inputs non-idiomatic and easier to get wrong.

This change converts them to multi-line input parameters.

Fixes #106
2021-10-29 07:29:57 -06:00
Daz DeBoer
d61e5be06a Remove note about Beta release from README 2021-10-27 16:27:21 -06:00
Daz DeBoer
db2b34260f Merge pull request #101 from gradle/dd/rename-parameters
Rename and document parameters for clarity 

- cache-paths -> gradle-home-cache-includes
- cache-exclude-paths -> gradle-home-cache-excludes
- CACHE_DEBUG_ENABLED -> GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED
2021-10-28 00:18:36 +02:00
Daz DeBoer
c031dc946b Build outputs 2021-10-27 16:09:53 -06:00
Daz DeBoer
0eb881f067 Adapt workflows to renamed parameters 2021-10-27 16:07:24 -06:00
Daz DeBoer
27f2dc276c Rename and document parameters for clarity
- cache-paths -> gradle-home-cache-includes
- cache-exclude-paths -> gradle-home-cache-excludes
- CACHE_DEBUG_ENABLED -> GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED
2021-10-27 16:05:07 -06:00
Daz DeBoer
cba1833dde Run test builds on Ubuntu and Windows
- Remove MacOS since the agents are current flaky
- Add Windows to a few more test pipelines
2021-10-21 12:08:17 -06:00
Daz DeBoer
39db90e99b Include build scan link in failure notice 2021-10-21 12:04:25 -06:00
Daz DeBoer
947a893558 Merge pull request #97 from gradle/dd/configurable-cache
Add some internal options for fine-tuning the gradle-build-action cache

- Can specify cache-paths to override the default directories cached from Gradle User Home
- Can specify cache-exclude-paths to exclude files from the Gradle User Home cache
- Files under caches/<gradle-version>/kotlin-dsl are now cached as a bundle
2021-10-21 19:26:19 +02:00
Daz DeBoer
b99e9f0bc3 Build outputs 2021-10-21 11:13:10 -06:00
Daz DeBoer
4cf255df10 Move bundle metadata files out of caches directory
Instead, use a separate '.gradle-build-action' directory for bundle
metadata files. This directory is always part of the cache-path.
2021-10-21 11:13:09 -06:00
Daz DeBoer
614d8770a4 Add test for cache configuration 2021-10-21 11:13:09 -06:00
Daz DeBoer
69453dbfc5 Include 'kotlin-dsl' as a cache-artifact-bundle 2021-10-21 11:13:09 -06:00
Daz DeBoer
1113cb87cb Allow cache-exclude-paths to be configured for action
All excluded paths are deleted prior to caching the Gradle User Home.
2021-10-21 11:13:09 -06:00
Daz DeBoer
9c95294209 Allow cache-paths to be set via action config 2021-10-21 11:13:08 -06:00
Daz DeBoer
f901ec9c20 Bump cache-protocol version 2021-10-20 15:04:14 -06:00
Daz DeBoer
a94b9252d5 Improve cache logging 2021-10-16 10:15:40 -06:00
Daz DeBoer
25672bf196 Build outputs 2021-10-16 09:50:40 -06:00
Daz DeBoer
cb6a0acca4 Use precise matching for artifact bundles
This should fix the warnings issued when saving artifact bundles.
2021-10-16 09:49:15 -06:00
Daz DeBoer
aa2ed2e033 Use cache protocol version for bundle keys too 2021-10-16 09:49:14 -06:00
Daz DeBoer
263f84178a Prefix cache key with protocol version
This will ensure that incompatiblee cache entries generated by previous action releases
will not be used.
2021-10-16 08:33:42 -06:00
Daz DeBoer
0eb5996567 Merge pull request #93 from gradle/dd/instrumented-jars-fix
Ensure all-or-nothing restore of cached instrumented-jars

Leaving the .lock and .receipt files lying around was causing issues when the actual jar files were not restored. Now the entire directory will either be missing, or completely restored.

Fixes #91
2021-10-15 23:04:47 +02:00
Daz DeBoer
fe55bf4667 Build outputs 2021-10-15 14:56:44 -06:00
Daz DeBoer
709ded51a5 Treat directory for instrumented jar as single artifact
Leaving the `.lock` and `.receipt` files lying around was causing
issues when the actual jar files were not restored. Now the entire
directory will either be missing, or completely restored.
2021-10-15 14:54:29 -06:00
Daz DeBoer
8b1f1a3817 Add test for execution when no bundles are restored 2021-10-15 13:29:09 -06:00
Daz DeBoer
7abf13ee48 Build outputs 2021-10-15 13:21:26 -06:00
Daz DeBoer
da64595ccc Make artifact bundle definitions an input parameter 2021-10-15 13:21:13 -06:00
Daz DeBoer
29b14c7fca Refactor: rename methods for 'bundle' concept 2021-10-15 12:34:38 -06:00
Daz DeBoer
d1ab42cddf Document support for multi-line arguments
Fixes #88
2021-10-15 12:24:41 -06:00
Daz DeBoer
422726cec5 Add test for multi-line input arguments 2021-10-15 12:20:01 -06:00
Daz DeBoer
4bc52c85c3 Merge pull request #92 from gradle/dd/cache-debug-logging
Improve cache logging and behaviour
2021-10-15 20:17:35 +02:00
Daz DeBoer
e7b5fd0b28 Build outputs 2021-10-15 11:46:51 -06:00
Daz DeBoer
53ccc3e0d7 Add more cache debug logging 2021-10-15 11:45:15 -06:00
Daz DeBoer
8ab7c9d8dd Do not fail action on cache errors
Ensure that we catch and log errors in `beforeSave` and `afterRestore`,
and do not fail the entire workflow in these cases.
2021-10-14 12:19:24 -06:00
Daz DeBoer
0cf00ed767 Fix test for release-candidate
Since this is an ever-changing version, this fix removes the `gradleVersionCheck` from the invocation.
2021-10-14 10:48:44 -06:00
Daz DeBoer
aedc5fc8f9 Mention that CACHE_DEBUG_ENABLED will disable parallel save/restore 2021-10-14 10:41:45 -06:00
Daz DeBoer
78e25cd233 Document the CACHE_DEBUG_ENABLED flag 2021-10-14 10:39:12 -06:00
Daz DeBoer
29894757f3 Merge pull request #90 from gradle/dd/windows-locks
Allow time for processes to delete file locks on windows
2021-10-05 01:30:13 +02:00
Daz DeBoer
5328161026 Build outputs 2021-10-04 23:59:42 +02:00
Daz DeBoer
4968d2280b Allow time for processes to release file locks on windows 2021-10-04 23:59:08 +02:00
Daz DeBoer
c000a0b58f Merge pull request #87 from gradle/dd/gradle-versions
Fix build-scan link detection with older build-scan plugins

Fixes #86
2021-09-29 15:15:24 -06:00
Daz DeBoer
6ff498182a Add checks for build scan links 2021-09-29 15:10:39 -06:00
Daz DeBoer
60b1ffac6b Fix build-scan-init script to work with build-in build-scan plugins 2021-09-29 14:39:48 -06:00
Daz DeBoer
9b7c81f8f6 Test execution with older Gradle versions 2021-09-29 13:34:05 -06:00
Daz DeBoer
b650771559 Cleanup samples 2021-09-29 13:23:25 -06:00
Daz DeBoer
17f624cb5b Rename 'basic' sample to 'groovy-dsl' 2021-09-28 20:31:11 -06:00
Daz DeBoer
21dee71590 Update README for v2 2021-09-28 11:30:55 -06:00
Daz DeBoer
83e2129213 Merge pull request #84 from gradle/dependabot/npm_and_yarn/ansi-regex-5.0.1
Bump ansi-regex from 5.0.0 to 5.0.1
2021-09-28 08:45:48 -06:00
Daz DeBoer
b16787d530 Merge pull request #80 from gradle/dependabot/npm_and_yarn/tmpl-1.0.5
Bump tmpl from 1.0.4 to 1.0.5
2021-09-28 08:45:34 -06:00
Daz DeBoer
204870af89 Merge pull request #85 from gradle/dd/init-script
Use an init-script to capture buildScanPublished event
2021-09-28 00:08:42 -06:00
Daz DeBoer
0918f5f2a4 Rename source file for clarity 2021-09-28 00:04:50 -06:00
Daz DeBoer
1b1a3c48ad Publish scans from no-wrapper sample build
This allows more testing of build scan capture functionality
2021-09-28 00:04:50 -06:00
Daz DeBoer
a7174b82a2 Use init script to capture build scan URL
Instead of parsing the log output, we instead register a
buildScanPublished listener and record the build scan URL
to a file. This file is subsequently read to report the
build scan URL.

Fixes #30
2021-09-28 00:04:41 -06:00
dependabot[bot]
3de71f2c52 Bump ansi-regex from 5.0.0 to 5.0.1
Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v5.0.0...v5.0.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-28 04:09:27 +00:00
Daz DeBoer
5576baa56b Merge pull request #83 from gradle/dd/v2
Adapt paths to differing Gradle User Home
2021-09-27 22:08:40 -06:00
Daz DeBoer
1026c62889 Only run failure-cases workflow on manual trigger 2021-09-27 22:05:23 -06:00
Daz DeBoer
1fb8644035 Build outputs 2021-09-27 21:53:19 -06:00
Daz DeBoer
5a5a5b4387 Normalize paths to Gradle User Home when calculating cache keys
Fixes #77
2021-09-27 21:52:03 -06:00
Daz DeBoer
cc5cdb7fe0 Adapt caching for GRADLE_USER_HOME env var
Fixes #74
2021-09-27 21:50:14 -06:00
Daz DeBoer
e0d37eb073 Workflow to test with custom GRADLE_USER_HOME 2021-09-27 19:48:26 -06:00
Daz DeBoer
bdd89aa34f Refactored workflows
- Improve workflow names
- Split execution testing from cache testing
2021-09-27 19:33:22 -06:00
dependabot[bot]
18cdc8bf28 Bump tmpl from 1.0.4 to 1.0.5
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-21 13:17:04 +00:00
Daz DeBoer
656ad4b5f2 Move continue-on-error into the step definition
Hopefully this will allow GitHub to ignore the failure.
2021-09-15 17:57:43 -06:00
Daz DeBoer
bebb162342 Usability improvements:
- Include bundle name in cache key
- Emit a few more messages at 'info'
2021-09-15 17:50:53 -06:00
Daz DeBoer
6084a4eb65 Cache artifacts with single entry per type
When caching is too fine-grained, an excessive number of cache
requests can result in HTTP 429 errors due to rate limiting.
By caching all artifacts of a particular type in a single entry
we hope to mitigate this, at the expense of some reduction in
cache space optimization.

This change also adds caching for all dependency jars, as well as
instrumented jars in the 'caches/jars-X' directory.
2021-09-15 17:45:47 -06:00
Daz DeBoer
dbb485d80d Only perform post-restore and pre-save cache actions when required 2021-09-15 17:45:47 -06:00
Daz DeBoer
9bfa003014 Build outputs 2021-09-14 18:11:31 -06:00
Daz DeBoer
fe64d05f86 Do not cache dependency jars individually
Unfortunately, doing this overloads the GitHub actions cache infrastructure
leading to failures and unpredictable results.
A later solution may re-implement artifact sharing for dependency jars
as well as jars within the `caches/jars-9` directory. But for now these
will be duplicated across each Gradle User Home cache entry.
2021-09-14 18:07:28 -06:00
Daz DeBoer
decca791c5 Consolidate error handling for cache restore/save 2021-09-14 13:39:37 -06:00
Daz DeBoer
bd08e7b7cd Do not fail build on error restoring artifact for marker
Instead, catch and report these errors before continuing.
2021-09-14 13:38:48 -06:00
Daz DeBoer
cca55d0890 Fail action execution on unhandled errors
Without this, the error logs contain an "UnhandledPromiseRejectionError"
but the action is reported as succeeding.
2021-09-14 07:48:06 -06:00
Daz DeBoer
a802a3c0ce Generate source-map files when compiling
This will make reported errors easier to link back to Typescript sources.
2021-09-14 05:46:11 -06:00
Daz DeBoer
d06e19f862 Build outputs 2021-09-13 11:50:07 -06:00
Daz DeBoer
bbe1574290 Add some TODOs to the code for future reference 2021-09-13 11:50:07 -06:00
Daz DeBoer
4264cda558 Group log messages generated when provisioning Gradle 2021-09-13 11:50:07 -06:00
Daz DeBoer
3390540145 Simplify setting caches to disabled or read-only 2021-09-13 11:04:42 -06:00
Daz DeBoer
1c72a31463 Allow cache debug logging to be selectively enabled
- Move more messages out of main 'info' log
- Only log cache entry size when debugging
- Process cache entries sequentially when debugging
2021-09-13 10:52:09 -06:00
Daz DeBoer
7dfbe33bba Improve formatting for cache entry size reporting 2021-09-13 10:51:24 -06:00
Daz DeBoer
e63ddf9c00 Avoid warning for cache reservation errors
We have no way of knowing if another build has already cached these
artifacts, so CacheReservationError is expected.
2021-09-13 10:50:47 -06:00
Daz DeBoer
d5cd9d86a1 Split the 'prod' workflow up into different isolated workflows
This is required to ensure that cache entries are namespaced correctly
for the integration tests.
2021-09-12 12:17:05 -06:00
Daz DeBoer
cae99bf6d9 Tidy up cache entry de-duplication
- Rename feature to "common artifact caching"
- Cleanup logging
- Refactor/rename for clarity
2021-09-12 10:31:52 -06:00
Daz DeBoer
5a90152b1f Restore/cache deduplicated files in parallel 2021-09-12 10:31:52 -06:00
Daz DeBoer
4b92b8d013 Cache downloaded dependency jars separately
This will further remove common files out the the Gradle User Home
cache entries, reducing cache usage by removing redundancy.
2021-09-12 10:31:51 -06:00
Daz DeBoer
693293c29a Consolidate logic for separate artifact file caching
Making this functionality more general will make it easier to add other
artifacts to the set that we cache separately.
2021-09-12 10:31:51 -06:00
Daz DeBoer
ac5d8920dd Cache generated-gradle-jars separately from Gradle User Home
Similar to wrapper distributions, these large files are common
to many Gradle User Home cache entries. Storing them separately removes
this redundancy from the Gradle User Home cache.
2021-09-12 10:31:51 -06:00
Daz DeBoer
e833360307 Cache downloaded wrapper dists separately
In the current model, each cached Gradle User Home could contain
a copy of one or more downloaded wrapper distributions. This results
in large cache entries which could easily lead to premature eviction.

With this change, wrapper dists are cached separately from the rest
of the Gradle User Home directory. The artifact file is replaced by
a marker file which allows the action to restore the artifact from
cache when the Gradle user Home cache is restored.
2021-09-12 10:31:51 -06:00
Daz DeBoer
b5a08466b4 Add kotlin-dsl sample to prod workflow tests 2021-09-12 10:30:49 -06:00
Daz DeBoer
4032438d2b Build outputs 2021-09-07 16:26:37 -06:00
Daz DeBoer
9b3abaad52 Use current matrix values for workflow job context 2021-09-07 16:26:28 -06:00
Daz DeBoer
d20d631365 Cleanup code to resolve Gradle wrapper script 2021-09-07 15:17:39 -06:00
Daz DeBoer
378bd0b6f8 Save and restore caches in parallel 2021-09-07 15:17:39 -06:00
Daz DeBoer
6d1455a33e Tidy-up caching code
- Extracted common code for Gradle User Home and Project .gradle caches
  into abstract supertype.
- Improve error handling by checking error types
2021-09-07 15:17:39 -06:00
Daz DeBoer
c44ebadf6f Add build scan link as a notice annotation
- Use 'notice' method introduced in @actions/core v1.5.0
2021-09-05 21:38:34 -06:00
Daz DeBoer
4d37378696 Consolidate cache-enabled options 2021-09-05 21:38:33 -06:00
Daz DeBoer
777a6fc967 Generate cache key based on Job invocation
Attempt to capture as much context as possible about the job run
to generate a unique cache key. Unfortunately much of the matrix context
is not available to the action implementation.
2021-09-05 21:35:17 -06:00
Daz DeBoer
d7ed6d7e8d Extract cache key generation into common function 2021-09-05 17:11:50 -06:00
Daz DeBoer
0ecbac99f3 Group log messsages for restore/save cache 2021-09-03 11:34:22 -06:00
Daz DeBoer
436390bd4e Test coverage: Verify configuration-cache is restored with project-dot-gradle 2021-08-27 12:53:02 -06:00
Daz DeBoer
a587e93714 Test coverage: Verify local build cache is restored with gradle-user-home 2021-08-27 12:53:02 -06:00
Daz DeBoer
75e00ee3d1 Test coverage: Verify dependency caching by running offline build 2021-08-27 12:53:02 -06:00
Daz DeBoer
c01af7a6f6 Test coverage: verify correct Gradle version is being used 2021-08-27 12:53:02 -06:00
Daz DeBoer
c79d4172e0 Build outputs 2021-08-27 12:53:01 -06:00
Daz DeBoer
b85ac67c9a Seed the cache key with workflow id in CI
This will eliminate cache entries from previous workflow runs, allowing
us to test cache functionality in isolation. If the `CACHE_KEY_SEED` environment
variable is not set, this will have no impact.
2021-08-27 12:52:01 -06:00
Daz DeBoer
fa0c026e07 Always execute Gradle with --no-daemon
The Gradle daemon is not useful for ephemeral builds, and the process
can hold file locks which interfere with cache entry generation.

In the case where multiple Gradle invocations occur in the same job,
we could provide a way for users to override this behaviour, taking care
of stopping any daemon process at the end of the job.
2021-08-27 12:21:46 -06:00
Daz DeBoer
986024f0b7 Adapt README and workflow for new caching 2021-08-26 17:25:50 -06:00
Daz DeBoer
6fca6b3929 Add support for read-only caching in v2 2021-08-26 17:25:49 -06:00
Daz DeBoer
d9cc0aeccf Remove old caching code 2021-08-24 12:54:21 -06:00
Daz DeBoer
5340f6e816 Add cache for project .gradle dir
- For now, this is limited to configuration-cache directory
2021-08-24 12:52:51 -06:00
Daz DeBoer
c211be411e Use monolithic cache for Gradle User Home
- Do not restore cache when GUH exists
- Include RUNNER_OS in the cache key
- Do not save cache on exact hit
- Only save cache in the final post action
- Log before saving cache
2021-08-24 12:52:51 -06:00
Daz DeBoer
b3afdc78a7 Truncate Gradle args when constructing cache key (#71)
Cache keys have a hard limit of 512 characters, so we need to ensure that we don't generate a key longer than this.

- Remove excess whitespace
- Truncate to 400 characters

Fixes #70
2021-08-24 12:46:48 -06:00
Daz DeBoer
e0c2736e35 Include Gradle invocation arguments in cache keys (#69)
This permits a new cache entry to be persisted when a subsequent Gradle invocation does more work that an earlier invocation.

Fixes #68
2021-08-22 17:11:05 -06:00
Daz DeBoer
a63892c289 Log cache entry on save 2021-08-22 14:58:36 -06:00
Daz DeBoer
d432f2086c Provide a better description for the action 2021-08-22 14:48:38 -06:00
Daz DeBoer
eaad2cd2bb Merge pull request #67 from gradle/dd/v15
Allow caching to be enabled read-only
2021-08-22 14:45:57 -06:00
Daz DeBoer
a148b21183 Improve prod workflow
- Use a separate job to test read-only cache
- Use dependency jobs to avoid cache race conditions
2021-08-22 14:42:01 -06:00
Daz DeBoer
e7422f245c Fix typo in README 2021-08-22 14:42:01 -06:00
Daz DeBoer
c86093d76a Build distribution 2021-08-22 14:28:35 -06:00
Daz DeBoer
a693ccda4b Allow use of caches 'read-only'
To avoid evicting useful entries, some pipeline may benefit from using existing
cache entries without writing any changes back to the cache.

Fixes #62
2021-08-22 14:28:34 -06:00
Daz DeBoer
543cacb256 Allow manual trigger of prod workflow 2021-08-20 14:00:30 -06:00
Daz DeBoer
3f3947669a Rename action from "Gradle Command" to "Gradle Build Action" 2021-08-17 09:36:14 -06:00
dependabot[bot]
579711fd3c Bump path-parse from 1.0.6 to 1.0.7 (#65)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-16 15:52:54 -06:00
Daz DeBoer
7336529ec5 Build distribution 2021-08-07 16:13:35 -07:00
Daz DeBoer
88af98fab4 Fail if configuration-cache is enabled without dependencies cache
Fixes #61
2021-08-07 16:13:35 -07:00
Daz DeBoer
01bfa29846 Update metadata and docs for repository move/rename
Fixes #60
2021-08-07 16:13:35 -07:00
Paul Merlin
0206df026c Merge pull request #59 from eskatos/dd/v1-improved
Minor cleanups, simplifications and improvements
2021-07-27 08:45:59 +02:00
Paul Merlin
41aebc770d Fix typo in test workflow
Signed-off-by: Paul Merlin <paul@gradle.com>
2021-07-27 08:22:05 +02:00
Daz DeBoer
bc4d6bddd4 Rebuild index files 2021-07-26 15:45:00 -06:00
Daz DeBoer
1f57b4dd2d Re-add workflow jobs for dependencies and configuration caches 2021-07-26 15:45:00 -06:00
Daz DeBoer
02d4f46354 Use core functionality to access action inputs
- Specify default values in action.yaml definition where appropriate
- Replace custom methods with core functions:
  -  getInputBoolean() with core.getBooleanInput()
  - inputOrNull() with core.getInput()
  - inputArrayOrNull() with core.getMultilineInput()
- Remove github-utils.js
2021-07-26 14:54:59 -06:00
Daz DeBoer
b9684c0cf5 Prefer ‘release-candidate’ instead of ‘rc’
This makes the version alias match other places where we reference a release candidate version.
The 'rc' alias is still supported, but emits a deprecation warning.
2021-07-26 14:54:59 -06:00
Daz DeBoer
5423935c9b Deprecate the 'wrapper-directory' input and recommend 'gradle-executable' instead
Use of a wrapper in a non-standard directory is uncommon, and is effectively handled
by referencing the `gradlew` script as the `gradle-executable`.
2021-07-26 14:54:59 -06:00
Daz DeBoer
41ca2299a5 Ignore .tool-versions directory
This directory is created by the ASDF version manager (https://asdf-vm.com),
and can be used to control the version of nodejs/npm used to run.
2021-07-20 11:20:21 -06:00
Daz DeBoer
33e91b639d Cache Gradle distributions downloaded for a particular version (#58)
- Cache is separate from (but similar to) the wrapper distribution cache
- New 'distributions-cache-enabled' flag controls caching of all downloaded distributions
  (including wrapper distributions)
- Deprecated the 'wrapper-cache-enabled' flag for removal in v2
2021-07-08 12:22:48 -06:00
Daz DeBoer
e4ec586f46 Only cache the wrapper ZIP, not the exploded dir
Prior to this change, the wrapper cache contained both the downloaded zip
file as well as the exploded wrapper dir. Only the zip file is required,
as Gradle will automatically detect and unpack.
2021-07-08 08:01:44 -06:00
Daz DeBoer
15a8123fbc Validate presense of gradle wrapper to provide better feedback
- Provide a more useful error message when no Gradle wrapper can be located,
  and 'gradle-version' or 'gradle-executable' is not used.
- Add test for case where wrapper is missing.
  This isn't really a "test" per-se, but this failing build invocation makes it
  easy to verify the GitHub action behaviour when the build is misconfigured.
2021-07-08 08:01:44 -06:00
Daz DeBoer
f0c6ac01d3 Add test for gradle-executable parameter
Use matrix to allow different script suffix on windows
2021-07-06 13:29:55 -06:00
Daz DeBoer
63fea55da4 Add a very-limited ‘no-wrapper’ test Gradle build
Will use this for testing Gradle execution with different versions and mechanisms.
2021-07-06 13:29:55 -06:00
Daz DeBoer
26b92e3f5c Separate unit-test data from integ-test samples
- Upgraded `samples/basic` to use latest Gradle version.
2021-07-06 13:29:55 -06:00
Daz DeBoer
643092d2fc Ensure that test invocations are targeted at a specific use-case
- Remove the 'gradle --stop' step from the prod workflow.
  We either need to stop all instances started, or rely on GitHub to clean up processes on completion.
- Remove configuration-cache and dependencies-cache from basic tests. We will later need to add
  tests invocations specific for these features.
2021-07-06 13:29:55 -06:00
Daz DeBoer
18c8a679dc Look for gradle wrapper in build-root-directory by default
This removes the need to specify `wrapper-directory` when using a Gradle
project that is not located in the root of the workspace.

Fixes #44.
2021-07-06 09:56:27 -06:00
Paul Merlin
13d33a88ca Merge pull request #52 from eskatos/dd/update-versions
Update dependency versions
2021-07-06 11:47:55 +02:00
Daz DeBoer
738bda9866 Use built-in library functions in preference to custom implementations
- Use built-in `hashFiles` function included in '@actions/globv0.2.0'
- Use `downloadTool` and `extractZip` functions from '@actions/tool-cache'
2021-07-05 13:57:22 -06:00
Daz DeBoer
47c9af9d7d Rebuild outputs 2021-07-05 12:40:53 -06:00
Daz DeBoer
3fba6132b4 Update devDependencies versions 2021-07-05 12:40:37 -06:00
Daz DeBoer
127b9b6624 Update to latest lib dependencies 2021-07-05 12:40:16 -06:00
Daz DeBoer
8189d29e96 Run 'npm update'
Get latest versions of transitive deps.
2021-07-05 12:20:36 -06:00
Daz DeBoer
eb7aa853fa Run 'npm audit fix'
Updates transitive library versions to address known vulnerabilities
2021-07-05 12:05:56 -06:00
Tomaz Muraus
15bf8034d6 Fix typo, the directory which contains dependency locks is named
gradle/dependency-locks.
2020-12-22 12:13:55 +01:00
paul
6bf2690b23 Upgrade dependencies 2020-12-22 12:11:50 +01:00
Benedikt Ritter
ced6c34563 Add worknig example for build scan comment
Replaces the dummy example for how to automatically add a comment containing the build scan URL
with a working example using the [GitHub Script action](https://github.com/marketplace/actions/github-script#comment-on-an-issue).

See https://github.com/britter/maven-plugin-development/pull/56#issuecomment-708406163 for how it looks in action.
2020-10-14 15:57:13 +02:00
Paul Merlin
2efcc22ff5 Replace typed-rest-client with @actions/http-client 2020-09-13 14:17:37 +02:00
Paul Merlin
cb2742a00b Protect RC provisioning from network problems and add logging 2020-09-13 13:30:54 +02:00
Paul Merlin
c8c53f54bb Let CI exercises using latest Gradle RC 2020-09-13 13:25:31 +02:00
Paul Merlin
863daedf86 Bump test build wrapper 2020-09-13 13:23:48 +02:00
Paul Merlin
55871cbb47 Simplify build scan url extraction 2020-09-13 13:09:14 +02:00
Paul Merlin
e3d60b3873 Protect wrapper cache restore against tar errors 2020-09-13 13:06:00 +02:00
Paul Merlin
2dd6cc1801 Upgrade dependencies 2020-09-13 12:37:40 +02:00
Paul Merlin
f11e7d60d7 Short-circuit dependencies and configuration caching if cache dir already exists
This is relevant if you run this action several times in a single job.
This prevent doing unnecessary work starting with the second job using the action.
This prevent droping dependencies downloaded by the first job using the action.
This prevent Windows agents to fail unlinking already existing files.
2020-06-22 16:40:58 +02:00
Paul Merlin
7137b09ae2 Upgrade dependencies 2020-06-22 16:36:25 +02:00
Paul Merlin
053f389907 CI prod check workflow stops Gradle daemon at the end
to allow for dependency caching to work on windows
2020-06-15 20:08:27 +02:00
Paul Merlin
4f9b5202aa Faster CI 2020-06-15 20:03:30 +02:00
Paul Merlin
466a737d16 Only restore wrapper cache if dir doesn't exist 2020-06-15 19:33:04 +02:00
Paul Merlin
eb7eb78d95 Refine README 2020-06-15 16:50:15 +02:00
Paul Merlin
682d7347f7 Optional dependencies cache
and bonus configuration cache

Merge branch 'eskatos/caching'
2020-06-15 16:36:19 +02:00
Paul Merlin
3c43b6525f Add some documentation around caching 2020-06-15 16:23:01 +02:00
Paul Merlin
aefd8348d2 Enable configuration cache on CI 2020-06-15 16:03:04 +02:00
Paul Merlin
f2de61db4e Enable configuration cache on CI 2020-06-15 15:58:50 +02:00
Paul Merlin
4c7d97cca4 Add caching of Gradle build configuration cache 2020-06-15 15:58:20 +02:00
Paul Merlin
806543fb3a Enable dependencies cache on CI 2020-06-15 15:57:47 +02:00
Paul Merlin
2afa86ca9f Disable dependencies cache by default 2020-06-15 15:57:09 +02:00
Paul Merlin
ae50675399 Better warning message 2020-06-15 15:43:21 +02:00
Paul Merlin
d7a54a26c7 Don't drop gc.properties in dependencies cache
because it would disable Gradle cache cleanup
2020-06-15 15:41:53 +02:00
Paul Merlin
02a8a21e55 Add input to disable wrapper caching 2020-06-15 15:41:09 +02:00
Paul Merlin
3abad5567a Add input to disable dependencies caching altogether 2020-06-15 14:32:54 +02:00
Paul Merlin
7c8cc1a9ef Fix typo 2020-06-15 14:28:57 +02:00
Paul Merlin
355e9c1f86 Try delete dependencies lock files and disable caching if not possible 2020-06-15 14:22:41 +02:00
Paul Merlin
26dd4cb9bb Make file hashing more robust 2020-06-15 14:09:44 +02:00
Paul Merlin
317ca35dca Refine dependencies caching 2020-06-15 13:16:51 +02:00
Paul Merlin
053762c1c1 Add input to make the dependencies caching strict 2020-06-15 13:05:36 +02:00
Paul Merlin
95e20daa83 Automatic caching of dependencies
in a best effort manner by default
allowing to specify files to hash for computing the cache key
2020-06-15 13:04:42 +02:00
Paul Merlin
fcc1683d01 Extract github actions utils 2020-06-14 19:05:03 +02:00
Paul Merlin
059f2e7791 Rename cache.ts to cache-wrapper.ts 2020-06-14 19:04:18 +02:00
Paul Merlin
692fda9de7 Cache wrapper install when gradle-executable points to a wrapper 2020-06-14 18:01:54 +02:00
Paul Merlin
e8885a31b8 Refine README 2020-06-14 12:57:10 +02:00
Paul Merlin
12e24e843d Fix for Windows
Merge branch 'eskatos/fix-windows'
2020-06-14 12:39:47 +02:00
Paul Merlin
b35e929b1d Simplify CI 2020-06-14 12:36:06 +02:00
Paul Merlin
c839ac993c Locate user home in an OS agnostic way 2020-06-14 12:28:17 +02:00
Paul Merlin
83e6d042d7 Run CI on linux, macos and windows 2020-06-14 12:22:21 +02:00
95 changed files with 21870 additions and 5265 deletions

View File

@@ -10,8 +10,10 @@
"rules": {
"eslint-comments/no-use": "off",
"import/no-namespace": "off",
"i18n-text/no-en": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"sort-imports": "off",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
@@ -37,7 +39,7 @@
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/require-array-sort-compare": ["error", {"ignoreStringArrays": true}],
"@typescript-eslint/restrict-plus-operands": "error",
"semi": "off",
"@typescript-eslint/semi": ["error", "never"],

70
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '25 23 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -1,7 +1,8 @@
name: dev
name: Verify generated outputs
on:
pull_request:
types: [assigned, review_requested]
push:
jobs:
@@ -14,18 +15,6 @@ jobs:
run: |
npm install
npm run all
- name: Test wrapper
uses: ./
with:
wrapper-directory: __tests__/data/basic
build-root-directory: __tests__/data/basic
arguments: help
- name: Test dist download
uses: ./
with:
gradle-version: 6.5
build-root-directory: __tests__/data/basic
arguments: help
- name: Check for uncommitted changes
# Ensure no changes, but ignore node_modules dir since dev/fresh ci deps installed.
run: |

34
.github/workflows/failure-cases.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Execute failure cases
on:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
jobs:
wrapper-missing:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Test wrapper missing
uses: ./
continue-on-error: true
with:
build-root-directory: __tests__/samples/no-wrapper
arguments: help
bad-configuration:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Test bad config value
uses: ./
continue-on-error: true
with:
build-root-directory: __tests__/samples/no-wrapper
arguments: help
cache-disabled: yes

View File

@@ -0,0 +1,31 @@
name: Test different action inputs
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
jobs:
action-inputs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Invoke with multi-line arguments
uses: ./
with:
build-root-directory: __tests__/samples/groovy-dsl
arguments: |
--configuration-cache
--build-cache
-DsystemProperty=FOO
-PgradleProperty=BAR
test
jar

View File

@@ -0,0 +1,60 @@
name: Test caching configuration
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
jobs:
seed-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
# Add "enterprise" to main cache entry but omit "notifications"
gradle-home-cache-includes: |
caches
enterprise
# Exclude build-cache from main cache entry
gradle-home-cache-excludes: |
caches/build-cache-1
- name: Build using Gradle wrapper
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
verify-build:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
# Use the same configuration when restoring state from cache
gradle-home-cache-includes: |
caches
enterprise
gradle-home-cache-excludes: |
caches/build-cache-1
cache-read-only: true
- name: Execute Gradle build with --offline
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test --offline

View File

@@ -0,0 +1,127 @@
name: Test save/restore configuration-cache state
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
jobs:
# Run initial Gradle builds to push initial cache entries
# These builds should start fresh without cache hits, due to the seed injected into the cache key above.
seed-build-groovy:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-groovy-
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Groovy build with configuration-cache enabled
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test --configuration-cache
configuration-cache-groovy:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-groovy-
needs: seed-build-groovy
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
cache-read-only: true
- name: Execute Gradle build and verify cached configuration
env:
VERIFY_CACHED_CONFIGURATION: true
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test --configuration-cache
# Check that the build can run when no extracted cache entries are restored
no-extracted-cache-entries-restored:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-groovy-
needs: seed-build-groovy
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle with no extracted cache entries restored
uses: ./
env:
GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl"
with:
cache-read-only: true
- name: Check execute Gradle build with configuration cache enabled (but not restored)
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test --configuration-cache
seed-build-kotlin:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-kotlin-
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Kotlin build with configuration-cache enabled
working-directory: __tests__/samples/kotlin-dsl
run: ./gradlew help --configuration-cache
modify-build-kotlin:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-kotlin-
needs: seed-build-kotlin
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Kotlin build with configuration-cache enabled
working-directory: __tests__/samples/kotlin-dsl
run: ./gradlew test --configuration-cache
# Test restore configuration-cache from the third build invocation
configuration-cache-kotlin:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-kotlin-
needs: modify-build-kotlin
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
cache-read-only: true
- name: Execute Gradle build and verify cached configuration
env:
VERIFY_CACHED_CONFIGURATION: true
working-directory: __tests__/samples/kotlin-dsl
run: ./gradlew test --configuration-cache

View File

@@ -0,0 +1,82 @@
name: Test save/restore Gradle Home directory
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
jobs:
seed-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Build using Gradle wrapper
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
dependencies-cache:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
cache-read-only: true
- name: Execute Gradle build with --offline
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test --offline
# Test that the gradle-user-home cache will cache and restore local build-cache
build-cache:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
cache-read-only: true
- name: Execute Gradle build and verify tasks from cache
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test -DverifyCachedBuild=true
# Check that the build can run when Gradle User Home is not fully restored
no-extracted-cache-entries-restored:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle with no extracted cache entries restored
uses: ./
env:
GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl"
with:
cache-read-only: true
- name: Check executee Gradle build
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test

View File

@@ -0,0 +1,43 @@
name: Test save/restore java toolchains
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
jobs:
seed-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Build using Gradle wrapper
working-directory: __tests__/samples/java-toolchain
run: ./gradlew test --info
# Test that the gradle-user-home cache will cache the toolchain, by running build with --offline
toolchain-cache:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
cache-read-only: true
- name: Execute Gradle build with --offline
working-directory: __tests__/samples/java-toolchain
run: ./gradlew test --info --offline

View File

@@ -0,0 +1,43 @@
name: Test save/restore Gradle state with direct execution
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
jobs:
seed-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Exucute Gradle build
uses: ./
with:
build-root-directory: __tests__/samples/groovy-dsl
arguments: test
# Test that the gradle-user-home is restored
verify-build:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Execute Gradle build
uses: ./
with:
cache-read-only: true
build-root-directory: __tests__/samples/groovy-dsl
arguments: test --offline -DverifyCachedBuild=true

View File

@@ -0,0 +1,73 @@
name: Test Gradle execution
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
jobs:
# Tests for executing with different Gradle versions.
# Each build verifies that it is executed with the expected Gradle version.
gradle-execution:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
include:
- os: windows-latest
script-suffix: '.bat'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Test use defined Gradle version
uses: ./
with:
gradle-version: 6.9
build-root-directory: __tests__/samples/no-wrapper
arguments: help -DgradleVersionCheck=6.9
- name: Test use Gradle version alias
uses: ./
with:
gradle-version: release-candidate
build-root-directory: __tests__/samples/no-wrapper
arguments: help
- name: Test use defined Gradle executable
uses: ./
with:
gradle-executable: __tests__/samples/groovy-dsl/gradlew${{ matrix.script-suffix }}
build-root-directory: __tests__/samples/no-wrapper
arguments: help -DgradleVersionCheck=7.1.1
gradle-versions:
strategy:
matrix:
gradle: [7.3, 6.9, 5.6.4, 4.10.3]
os: [ubuntu-latest, windows-latest]
include:
- gradle: 5.6.4
build-root-suffix: -gradle-5
- gradle: 4.10.3
build-root-suffix: -gradle-4
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Run Gradle build
uses: ./
id: gradle
with:
gradle-version: ${{matrix.gradle}}
build-root-directory: __tests__/samples/no-wrapper${{ matrix.build-root-suffix }}
arguments: help -DgradleVersionCheck=${{matrix.gradle}}
- name: Check build scan url
if: ${{ !steps.gradle.outputs.build-scan-url }}
uses: actions/github-script@v3
with:
script: |
core.setFailed('No build scan detected')

View File

@@ -0,0 +1,64 @@
name: Test caching with a custom GRADLE_USER_HOME
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
GRADLE_USER_HOME: ${{github.workspace}}/custom/gradle/home
jobs:
# Run initial Gradle builds to push initial cache entries
# These builds should start fresh without cache hits, due to the seed injected into the cache key above.
seed-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Build using Gradle wrapper
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test --info
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
dependencies-cache:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
cache-read-only: true
- name: Execute Gradle build with --offline
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test --offline --info
# Test that the gradle-user-home cache will cache and restore local build-cache
build-cache:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
cache-read-only: true
- name: Execute Gradle build and verify tasks from cache
working-directory: __tests__/samples/groovy-dsl
run: ./gradlew test -DverifyCachedBuild=true --info

View File

@@ -0,0 +1,77 @@
name: Test provision different Gradle versions
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
jobs:
# Tests for executing with different Gradle versions.
# Each build verifies that it is executed with the expected Gradle version.
provision-gradle:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
include:
- os: windows-latest
script-suffix: '.bat'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle with v6.9
uses: ./
with:
gradle-version: 6.9
- name: Test uses Gradle v6.9
working-directory: __tests__/samples/no-wrapper
run: gradle help "-DgradleVersionCheck=6.9"
- name: Setup Gradle with v7.1.1
uses: ./
with:
gradle-version: 7.1.1
- name: Test uses Gradle v7.1.1
working-directory: __tests__/samples/no-wrapper
run: gradle help "-DgradleVersionCheck=7.1.1"
- name: Setup Gradle with release-candidate
uses: ./
with:
gradle-version: release-candidate
- name: Test use release-candidate
working-directory: __tests__/samples/no-wrapper
run: gradle help
gradle-versions:
strategy:
matrix:
gradle: [7.3, 6.9, 5.6.4, 4.10.3]
os: [ubuntu-latest, windows-latest]
include:
- gradle: 5.6.4
build-root-suffix: -gradle-5
- gradle: 4.10.3
build-root-suffix: -gradle-4
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
with:
gradle-version: ${{ matrix.gradle }}
- name: Run Gradle build
id: gradle
working-directory: __tests__/samples/no-wrapper${{ matrix.build-root-suffix }}
run: gradle help "-DgradleVersionCheck=${{matrix.gradle}}"
- name: Check build scan url
if: ${{ !steps.gradle.outputs.build-scan-url }}
uses: actions/github-script@v3
with:
script: |
core.setFailed('No build scan detected')

View File

@@ -0,0 +1,41 @@
name: Test caching with Gradle Plugin project using TestKit
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
jobs:
seed-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Build gradle-plugin project
working-directory: __tests__/samples/gradle-plugin
run: ./gradlew build
verify-build:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Build gradle-plugin project
working-directory: __tests__/samples/gradle-plugin
run: ./gradlew build --offline

View File

@@ -0,0 +1,41 @@
name: Test caching with Kotlin DSL
on:
pull_request:
types: [assigned, review_requested]
push:
workflow_dispatch:
env:
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
jobs:
seed-build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Build kotlin-dsl project
working-directory: __tests__/samples/kotlin-dsl
run: ./gradlew build
verify-build:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Setup Gradle
uses: ./
- name: Build kotlin-dsl project
working-directory: __tests__/samples/kotlin-dsl
run: ./gradlew build --offline

View File

@@ -0,0 +1,27 @@
name: Add a build scan comment to PR
on:
pull_request:
types: [assigned, review_requested]
jobs:
gradle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- uses: ./
- id: gradle
working-directory: __tests__/samples/kotlin-dsl
run: ./gradlew help
- name: "Comment build scan url"
uses: actions/github-script@v3
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'PR ready for review: ${{ steps.gradle.outputs.build-scan-url }}'
})

View File

@@ -1,34 +0,0 @@
# make sure the action works on a clean machine without building
name: prod
on:
push:
branches:
- master
- 'releases/*'
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Test wrapper
uses: ./
with:
wrapper-directory: __tests__/data/basic
build-root-directory: __tests__/data/basic
arguments: help
- name: Test dist download
uses: ./
with:
gradle-version: 6.5
build-root-directory: __tests__/data/basic
arguments: help
- name: Check for uncommitted changes
# Ensure no changes, but ignore node_modules dir since dev/fresh ci deps installed.
run: |
git diff --exit-code --stat -- . ':!node_modules' \
|| (echo "##[error] found changed files after build. please 'npm run all'" \
"and check in all changes" \
&& exit 1)

4
.gitignore vendored
View File

@@ -98,5 +98,9 @@ Thumbs.db
__tests__/runner/*
# lib/**/*
# IntelliJ IDEA config files
.idea/
*.iml
# ASDF tool configuration
.tool-versions

View File

@@ -1,5 +1,5 @@
{
"printWidth": 80,
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"semi": false,

339
README.md
View File

@@ -1,17 +1,15 @@
# Execute Gradle commands in GitHub Actions workflows
# Execute Gradle builds in GitHub Actions workflows
This GitHub Action can be used to run arbitrary Gradle commands on any platform supported by GitHub Actions.
This GitHub Action can be used to configure Gradle and optionally execute a Gradle build on any platform supported by GitHub Actions.
You might also be interested by the related [Gradle Plugin](https://github.com/eskatos/gradle-github-actions-plugin) that allows your build to easily get GitHub Actions environment and tag Gradle Build Scans accordingly.
## Usage
The following workflow will run `./gradlew build` using the wrapper from the repository on ubuntu, macos and windows. The only prerequisite is to have Java installed, you can define the version you need to run the build using the `actions/setup-java` action.
## Use the action to setup Gradle
If you have an existing workflow invoking Gradle, you can add an initial "Setup Gradle" Step to benefit from caching,
build-scan capture and other features of the gradle-build-action.
All subsequent Gradle invocations will benefit from this initial setup, via `init` scripts added to the Gradle User Home.
```yaml
# .github/workflows/gradle-build-pr.yml
name: Run Gradle on PRs
on: pull_request
jobs:
@@ -21,87 +19,58 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- uses: eskatos/gradle-command-action@v1
with:
arguments: build
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Execute Gradle build
run: ./gradlew build
```
## Gradle arguments
## Why use the `gradle-build-action`?
The `arguments` input can used to pass arbitrary arguments to the `gradle` command line.
It is possible to directly invoke Gradle in your workflow, and the `setup-java` action provides a simple way to cache Gradle dependencies.
Here are some valid examples:
```yaml
arguments: build
arguments: check --scan
arguments: some arbitrary tasks
arguments: build -PgradleProperty=foo
arguments: build -DsystemProperty=bar
....
```
However, the `gradle-build-action` offers a number of advantages over this approach:
See `gradle --help` for more information.
- Easily [run the build with different versions of Gradle](#download-install-and-use-a-specific-gradle-version) using the `gradle-version` parameter. Gradle distributions are automatically downloaded and cached.
- More sophisticated and more efficient caching of Gradle User Home between invocations, compared to `setup-java` and most custom configurations using `actions/cache`. [More details below](#caching).
- Detailed reporting of cache usage and cache configuration options allow you to [optimize the use of the GitHub actions cache](#optimizing-cache-effectiveness).
- [Automatic capture of build scan links](#build-scans) from the build, making these easier to locate for workflow run.
If you need to pass environment variables, simply use the GitHub Actions workflow syntax:
The `gradle-build-action` is designed to provide these benefits with minimal configuration.
These features work both when Gradle is executed via the `gradle-build-action` and for any Gradle execution in subsequent steps.
## Use a specific Gradle version
The `gradle-build-action` can download and install a specified Gradle version, adding this installed version to the PATH.
Downloaded Gradle versions are stored in the GitHub Actions cache, to avoid requiring downloading again later.
```yaml
- uses: eskatos/gradle-command-action@v1
env:
CI: true
```
## Run a build from a different directory
```yaml
- uses: eskatos/gradle-command-action@v1
with:
build-root-directory: some/subdirectory
```
## Use a Gradle wrapper from a different directory
```yaml
- uses: eskatos/gradle-command-action@v1
- uses: gradle/gradle-build-action@v2
with:
wrapper-directory: path/to/wrapper-directory
```
## Use a specific `gradle` executable
```yaml
- uses: eskatos/gradle-command-action@v1
with:
gradle-executable: path/to/gradle
gradle-version: 6.5
```
## Setup and use a declared Gradle version
```yaml
- uses: eskatos/gradle-command-action@v1
with:
gradle-version: 5.6.2
```
`gradle-version` can be set to any valid Gradle version.
The `gradle-version` parameter can be set to any valid Gradle version.
Moreover, you can use the following aliases:
| Alias | Selects |
| --- |---|
| `wrapper` | The Gradle wrapper's version (default, useful for matrix builds) |
| `current` | The current [stable release](https://gradle.org/install/) |
| `rc` | The current [release candidate](https://gradle.org/release-candidate/) if any, otherwise fallback to `current` |
| `nightly` | The latest [nightly](https://gradle.org/nightly/), fails if none. |
| `release-nightly` | The latest [release nightly](https://gradle.org/release-nightly/), fails if none. |
| `wrapper` | The Gradle wrapper's version (default, useful for matrix builds) |
| `current` | The current [stable release](https://gradle.org/install/) |
| `release-candidate` | The current [release candidate](https://gradle.org/release-candidate/) if any, otherwise fallback to `current` |
| `nightly` | The latest [nightly](https://gradle.org/nightly/), fails if none. |
| `release-nightly` | The latest [release nightly](https://gradle.org/release-nightly/), fails if none. |
This can be handy to, for example, automatically test your build with the next Gradle version once a release candidate is out:
This can be handy to automatically verify your build works with the latest release candidate of Gradle:
```yaml
# .github/workflows/test-gradle-rc.yml
name: Test latest Gradle RC
on:
schedule:
@@ -110,26 +79,23 @@ jobs:
gradle-rc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- uses: eskatos/gradle-command-action@v1
- uses: gradle/gradle-build-action@v2
with:
gradle-version: rc
arguments: build --dry-run # just test build configuration
gradle-version: release-candidate
- run: gradle build --dry-run # just test build configuration
```
# Build scans
## Gradle Execution
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-command-action` action will emit the link to the published build scan as an output named `build-scan-url`.
If the action is configured with an `arguments` input, then Gradle will execute a Gradle build with the arguments provided.
You can then use that link in subsequent actions of your workflow.
For example:
If no `arguments` are provided, the action will not execute Gradle, but will still cache Gradle state and configure build-scan capture for all subsequent Gradle executions.
```yaml
# .github/workflows/gradle-build-pr.yml
name: Run Gradle on PRs
on: pull_request
jobs:
@@ -139,16 +105,219 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- uses: eskatos/gradle-command-action@v1
- name: Setup and execute Gradle 'test' task
uses: gradle/gradle-build-action@v2
with:
arguments: build
id: gradle
- uses: example/action-that-comments-on-the-pr@v0
if: failure()
with:
comment: Build failed ${{ steps.gradle.outputs.build-scan-url }}
arguments: test
```
### Multiple Gradle executions in the same Job
It is possible to configure multiple Gradle executions to run sequentially in the same job.
The initial Action step will perform the Gradle setup.
```yaml
- uses: gradle/gradle-build-action@v2
with:
arguments: assemble
- uses: gradle/gradle-build-action@v2
with:
arguments: check
```
### Gradle command-line arguments
The `arguments` input can used to pass arbitrary arguments to the `gradle` command line.
Arguments can be supplied in a single line, or as a multi-line input.
Here are some valid examples:
```yaml
arguments: build
arguments: check --scan
arguments: some arbitrary tasks
arguments: build -PgradleProperty=foo
arguments: |
build
--scan
-PgradleProperty=foo
-DsystemProperty=bar
```
If you need to pass environment variables, use the GitHub Actions workflow syntax:
```yaml
- uses: gradle/gradle-build-action@v2
env:
CI: true
with:
arguments: build
```
### Gradle build located in a subdirectory
By default, the action will execute Gradle in the root directory of your project.
Use the `build-root-directory` input to target a Gradle build in a subdirectory.
```yaml
- uses: gradle/gradle-build-action@v2
with:
arguments: build
build-root-directory: some/subdirectory
```
### Using a specific Gradle executable
The action will first look for a Gradle wrapper script in the root directory of your project.
If not found, `gradle` will be executed from the PATH.
Use the `gradle-executable` input to execute using a specific Gradle installation.
```yaml
- uses: gradle/gradle-build-action@v2
with:
arguments: build
gradle-executable: /path/to/installed/gradle
```
This mechanism can also be used to target a Gradle wrapper script that is located in a non-default location.
## Caching
By default, this action aims to cache any and all reusable state that may be speed up a subsequent build invocation.
The state that is cached includes:
- Any distributions downloaded to satisfy a `gradle-version` parameter ;
- A subset of the Gradle User Home directory, including downloaded dependencies, wrapper distributions, and the local build cache ;
- Any [configuration-cache](https://docs.gradle.org/nightly/userguide/configuration_cache.html) data stored in the project `.gradle` directory.
To reduce the space required for caching, this action makes a best effort to reduce duplication in cache entries.
Caching is enabled by default. You can disable caching for the action as follows:
```yaml
cache-disabled: true
```
### Cache keys
For distributions downloaded to satisfy a `gradle-version` parametere are stored outside of Gradle User Home and cached separately. The cache key is unique to the downloaded distribution and will not change over time.
The state of the Gradle User Home and configuration-cache are highly dependent on the Gradle execution, so the cache key is composed of the current commit hash and the GitHub actions job id.
As such, the cache key is likely to change on each subsequent run of GitHub actions.
This allows the most recent state to always be available in the GitHub actions cache.
To reduce duplication between cache entries, certain artifacts are cached independently based on their identity.
Artifacts that are cached independently include downloaded dependencies, downloaded wrapper distributions and generated Gradle API jars.
For example, this means that all jobs executing a particular version of the Gradle wrapper will share common entries for wrapper distributions and for generated Gradle API jars.
### Using the caches read-only
In some circumstances, it makes sense for a Gradle invocation to read any existing cache entries but not to write changes back.
For example, you may want to write cache entries for builds on your `main` branch, but not for any PR build invocations.
You can enable read-only caching for any of the caches as follows:
```yaml
# Only write to the cache for builds on the 'main' branch.
# Builds on other branches will only read existing entries from the cache.
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
```
### Gradle User Home cache tuning
As well as any wrapper distributions, the action will attempt to save and restore the `caches` and `notifications` directories from Gradle User Home.
The contents to be cached can be fine tuned by including and excluding certain paths with Gradle User Home.
```yaml
# Cache downloaded JDKs in addition to the default directories.
gradle-home-cache-includes: |
caches
notifications
jdks
# Exclude the local build-cache from the directories cached.
gradle-home-cache-excludes: |
caches/build-cache-1
```
You can specify any number of fixed paths or patterns to include or exclude.
File pattern support is documented at https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#patterns-to-match-file-paths.
### Cache debugging and analysis
Gradle User Home state will be restored from the cache during the first `gradle-build-action` step for any workflow job.
This state will be saved back to the cache at the end of the job, after all Gradle executions have completed.
A report of all cache entries restored and saved is printed to the action log when saving the cache entries.
This report can provide valuable insignt into how much cache space is being used.
It is possible to enable additional debug logging for cache operations. You do via the `GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED` environment variable:
```yaml
env:
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
```
Note that this setting will also prevent certain cache operations from running in parallel, further assisting with debugging.
### Optimizing cache effectiveness
Cache storage space for GitHub actions is limited, and writing new cache entries can trigger the deletion of exising entries.
Eviction of shared cache entries can reduce cache effectiveness, slowing down your `gradle-build-action` steps.
There are a number of actions you can take if your cache use is less effective due to entry eviction.
#### Only write to the cache from the default branch
GitHub cache entries are not shared between builds on different branches. This means that identical cache entries will be stored separately for different branches.
The exception to the is cache entries for the default (`master`/`main`) branch can be read by actions invoked for other branches.
An easy way to reduce cache usage when you run builds on many different branches is to only permit your default branch to write to the cache,
with all other branch builds using `cache-read-only`. See [Using the caches read-only](#using-the-caches-read-only) for more details.
Similarly, you could use `cache-read-only` for certain jobs in the workflow, and instead have these jobs reuse the cache content from upstream jobs.
#### Exclude content from Gradle User Home cache
Each build is different, and some builds produce more Gradle User Home content than others.
[Cache debugging ](#cache-debugging-and-analysis) can provide insight into which cache entries are the largest,
and you can selectively [exclude content using `gradle-home-cache-exclude`](#gradle-user-home-cache-tuning).
## Build scans
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-build-action` action will:
- Add a notice with the link to the GitHub Actions user interface
- For each step that executes Gradle, adds the link to the published build scan as a Step output named `build-scan-url`.
You can then use that link in subsequent actions of your workflow. For example:
```yaml
# .github/workflows/gradle-build-pr.yml
name: Run Gradle on PRs
on: pull_request
jobs:
gradle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- uses: gradle/gradle-build-action@v2
- id: gradle
run: ./gradlew build
- name: "Comment build scan url"
uses: actions/github-script@v3
if: github.event_name == 'pull_request' && failure()
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '❌ ${{ github.workflow }} failed: ${{ steps.gradle.outputs.build-scan-url }}'
})
```

View File

@@ -0,0 +1,95 @@
import {CacheEntryListener, CacheListener} from '../src/cache-reporting'
describe('caching report', () => {
describe('reports not fully restored', () => {
it('with one requested entry report', async () => {
const report = new CacheListener()
report.entry('foo').markRequested('1', ['2'])
report.entry('bar').markRequested('3').markRestored('4', 500)
expect(report.fullyRestored).toBe(false)
})
})
describe('reports fully restored', () => {
it('when empty', async () => {
const report = new CacheListener()
expect(report.fullyRestored).toBe(true)
})
it('with empty entry reports', async () => {
const report = new CacheListener()
report.entry('foo')
report.entry('bar')
expect(report.fullyRestored).toBe(true)
})
it('with restored entry report', async () => {
const report = new CacheListener()
report.entry('bar').markRequested('3').markRestored('4', 300)
expect(report.fullyRestored).toBe(true)
})
it('with multiple restored entry reportss', async () => {
const report = new CacheListener()
report.entry('foo').markRestored('4', 3300)
report.entry('bar').markRequested('3').markRestored('4', 333)
expect(report.fullyRestored).toBe(true)
})
})
describe('can be stringified and rehydrated', () => {
it('when empty', async () => {
const report = new CacheListener()
const stringRep = report.stringify()
const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
expect(reportClone.cacheEntries).toEqual([])
// Can call methods on rehydrated
expect(reportClone.entry('foo')).toBeInstanceOf(CacheEntryListener)
})
it('with entry reports', async () => {
const report = new CacheListener()
report.entry('foo')
report.entry('bar')
report.entry('baz')
const stringRep = report.stringify()
const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
expect(reportClone.cacheEntries.length).toBe(3)
expect(reportClone.cacheEntries[0].entryName).toBe('foo')
expect(reportClone.cacheEntries[1].entryName).toBe('bar')
expect(reportClone.cacheEntries[2].entryName).toBe('baz')
expect(reportClone.entry('foo')).toBe(reportClone.cacheEntries[0])
})
it('with rehydrated entry report', async () => {
const report = new CacheListener()
const entryReport = report.entry('foo')
entryReport.markRequested('1', ['2', '3'])
entryReport.markSaved('4', 100)
const stringRep = report.stringify()
const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
const entryClone = reportClone.entry('foo')
expect(entryClone.requestedKey).toBe('1')
expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
expect(entryClone.savedKey).toBe('4')
})
it('with live entry report', async () => {
const report = new CacheListener()
const entryReport = report.entry('foo')
entryReport.markRequested('1', ['2', '3'])
const stringRep = report.stringify()
const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
const entryClone = reportClone.entry('foo')
// Check type and call method on rehydrated entry report
expect(entryClone).toBeInstanceOf(CacheEntryListener)
entryClone.markSaved('4', 100)
expect(entryClone.requestedKey).toBe('1')
expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
expect(entryClone.savedKey).toBe('4')
})
})
})

View File

@@ -0,0 +1,20 @@
import * as cacheUtils from '../src/cache-utils'
describe('cacheUtils-utils', () => {
describe('can hash', () => {
it('a string', async () => {
const hash = cacheUtils.hashStrings(['foo'])
expect(hash).toBe('acbd18db4cc2f85cedef654fccc4a4d8')
})
it('multiple strings', async () => {
const hash = cacheUtils.hashStrings(['foo', 'bar', 'baz'])
expect(hash).toBe('6df23dc03f9b54cc38a0fc1483df6e21')
})
it('normalized filenames', async () => {
const fileNames = ['/foo/bar/baz.zip', '../boo.html']
const posixHash = cacheUtils.hashFileNames(fileNames)
const windowsHash = cacheUtils.hashFileNames(fileNames)
expect(posixHash).toBe(windowsHash)
})
})
})

View File

@@ -1,33 +0,0 @@
import * as cache from '../src/cache'
import * as path from 'path'
describe('cache', () => {
describe('can extract gradle wrapper slug', () => {
it('from wrapper properties file', async () => {
const version = cache.extractGradleWrapperSlugFrom(
path.resolve(
'__tests__/data/basic/gradle/wrapper/gradle-wrapper.properties'
)
)
expect(version).toBe('6.5-bin')
})
it('for -bin dist', async () => {
const version = cache.extractGradleWrapperSlugFromDistUri(
'distributionUrl=https\\://services.gradle.org/distributions/gradle-6.5-bin.zip'
)
expect(version).toBe('6.5-bin')
})
it('for -all dist', async () => {
const version = cache.extractGradleWrapperSlugFromDistUri(
'distributionUrl=https\\://services.gradle.org/distributions/gradle-6.5-all.zip'
)
expect(version).toBe('6.5-all')
})
it('for milestone', async () => {
const version = cache.extractGradleWrapperSlugFromDistUri(
'distributionUrl=https\\://services.gradle.org/distributions/gradle-6.6-milestone-1-all.zip'
)
expect(version).toBe('6.6-milestone-1-all')
})
})
})

View File

@@ -1,6 +0,0 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This is a general purpose Gradle build.
* Learn how to create Gradle builds at https://guides.gradle.org/creating-new-gradle-builds
*/

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -0,0 +1,11 @@
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation('junit:junit:4.12')
}

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -0,0 +1,10 @@
package basic;
import org.junit.Test;
public class BasicTest {
@Test
public void test() {
assert true;
}
}

5
__tests__/samples/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
# Ignore Gradle project-specific cache directory
.gradle
# Ignore Gradle build output directory
build

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
__tests__/samples/gradle-plugin/gradlew vendored Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,60 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Gradle plugin project to get you started.
* For more details take a look at the Writing Custom Plugins chapter in the Gradle
* User Manual available at https://docs.gradle.org/7.3/userguide/custom_plugins.html
* This project uses @Incubating APIs which are subject to change.
*/
plugins {
// Apply the Java Gradle plugin development plugin to add support for developing Gradle plugins
id 'java-gradle-plugin'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
testing {
suites {
// Configure the built-in test suite
test {
// Use JUnit Jupiter test framework
useJUnitJupiter('5.7.2')
}
// Create a new test suite
functionalTest(JvmTestSuite) {
dependencies {
// functionalTest test suite depends on the production code in tests
implementation project
}
targets {
all {
// This test suite should run after the built-in test suite has run its tests
testTask.configure { shouldRunAfter(test) }
}
}
}
}
}
gradlePlugin {
// Define the plugin
plugins {
greeting {
id = 'org.example.gradle.plugin.greeting'
implementationClass = 'org.example.gradle.plugin.GradlePluginPlugin'
}
}
}
gradlePlugin.testSourceSets(sourceSets.functionalTest)
tasks.named('check') {
// Include functionalTest as part of the check lifecycle
dependsOn(testing.suites.functionalTest)
}

View File

@@ -0,0 +1,57 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package org.example.gradle.plugin;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.io.FileWriter;
import java.nio.file.Files;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.BuildResult;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.junit.jupiter.api.Assertions.*;
/**
* A simple functional test for the 'org.example.gradle.plugin.greeting' plugin.
*/
class GradlePluginPluginFunctionalTest {
@TempDir
File projectDir;
private File getBuildFile() {
return new File(projectDir, "build.gradle");
}
private File getSettingsFile() {
return new File(projectDir, "settings.gradle");
}
@Test void canRunTaskWithGradle691() throws IOException {
writeString(getSettingsFile(), "");
writeString(getBuildFile(),
"plugins {" +
" id('org.example.gradle.plugin.greeting')" +
"}");
// Run the build
GradleRunner runner = GradleRunner.create();
runner.forwardOutput();
runner.withGradleVersion("6.9.1");
runner.withPluginClasspath();
runner.withArguments("greeting");
runner.withProjectDir(projectDir);
BuildResult result = runner.build();
// Verify the result
assertTrue(result.getOutput().contains("Hello from plugin 'org.example.gradle.plugin.greeting'"));
}
private void writeString(File file, String string) throws IOException {
try (Writer writer = new FileWriter(file)) {
writer.write(string);
}
}
}

View File

@@ -0,0 +1,19 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package org.example.gradle.plugin;
import org.gradle.api.Project;
import org.gradle.api.Plugin;
/**
* A simple 'hello world' plugin.
*/
public class GradlePluginPlugin implements Plugin<Project> {
public void apply(Project project) {
// Register a task
project.getTasks().register("greeting", task -> {
task.doLast(s -> System.out.println("Hello from plugin 'org.example.gradle.plugin.greeting'"));
});
}
}

View File

@@ -0,0 +1,23 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package org.example.gradle.plugin;
import org.gradle.testfixtures.ProjectBuilder;
import org.gradle.api.Project;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* A simple unit test for the 'org.example.gradle.plugin.greeting' plugin.
*/
class GradlePluginPluginTest {
@Test void pluginRegistersATask() {
// Create a test project and apply the plugin
Project project = ProjectBuilder.builder().build();
project.getPlugins().apply("org.example.gradle.plugin.greeting");
// Verify the result
assertNotNull(project.getTasks().findByName("greeting"));
}
}

View File

@@ -0,0 +1,12 @@
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
*
* Detailed information about configuring a multi-project build in Gradle can be found
* in the user manual at https://docs.gradle.org/7.3/userguide/multi_project_builds.html
* This project uses @Incubating APIs which are subject to change.
*/
rootProject.name = 'gradle-plugin'
include('plugin')

View File

@@ -0,0 +1,23 @@
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation('junit:junit:4.12')
}
tasks.named("test").configure {
// Use an environment variable to bypass config-cache checks
if (System.getenv('VERIFY_CACHED_CONFIGURATION') != null) {
throw new RuntimeException("Configuration was not cached: unexpected configuration of test task")
}
doLast {
if (System.properties.verifyCachedBuild) {
throw new RuntimeException("Build was not cached: unexpected execution of test task")
}
}
}

View File

@@ -0,0 +1 @@
org.gradle.caching=true

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
__tests__/samples/groovy-dsl/gradlew vendored Executable file
View File

@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

104
__tests__/samples/groovy-dsl/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,104 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1 @@
rootProject.name = 'basic'

View File

@@ -0,0 +1,10 @@
package basic;
import org.junit.Test;
public class BasicTest {
@Test
public void test() {
assert true;
}
}

View File

@@ -0,0 +1,17 @@
plugins {
id 'java'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(16)
}
}
repositories {
mavenCentral()
}
dependencies {
testImplementation('junit:junit:4.12')
}

View File

@@ -0,0 +1 @@
org.gradle.caching=true

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
__tests__/samples/java-toolchain/gradlew vendored Executable file
View File

@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,104 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1 @@
rootProject.name = 'basic'

View File

@@ -0,0 +1,10 @@
package basic;
import org.junit.Test;
public class BasicTest {
@Test
public void test() {
assert true;
}
}

View File

@@ -0,0 +1,30 @@
plugins {
`java-library`
}
repositories {
mavenCentral()
}
dependencies {
api("org.apache.commons:commons-math3:3.6.1")
implementation("com.google.guava:guava:30.1.1-jre")
testImplementation("org.junit.jupiter:junit-jupiter:5.7.2")
}
tasks.test {
useJUnitPlatform()
}
tasks.named("test").configure {
// Use an environment variable to bypass config-cache checks
if (System.getenv("VERIFY_CACHED_CONFIGURATION") != null) {
throw RuntimeException("Configuration was not cached: unexpected configuration of test task")
}
doLast {
if (System.getProperties().containsKey("verifyCachedBuild")) {
throw RuntimeException("Build was not cached: unexpected execution of test task")
}
}
}

View File

@@ -0,0 +1 @@
org.gradle.caching=true

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
__tests__/samples/kotlin-dsl/gradlew vendored Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,15 @@
plugins {
id("com.gradle.enterprise") version("3.7")
}
gradleEnterprise {
buildScan {
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
publishAlways()
isUploadInBackground = false
}
}
rootProject.name = "kotlin-dsl"

View File

@@ -0,0 +1,10 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package com.example;
public class Library {
public boolean someLibraryMethod() {
return true;
}
}

View File

@@ -0,0 +1,14 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package com.example;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class LibraryTest {
@Test void someLibraryMethodReturnsTrue() {
Library classUnderTest = new Library();
assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'");
}
}

View File

@@ -0,0 +1,10 @@
plugins {
id "com.gradle.build-scan" version "1.16"
}
buildScan {
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
publishAlways()
}

View File

@@ -0,0 +1,8 @@
rootProject.name = 'no-wrapper'
println "Using Gradle version: ${gradle.gradleVersion}"
def gradleVersionCheck = System.properties.gradleVersionCheck
if (gradleVersionCheck && gradle.gradleVersion != gradleVersionCheck) {
throw new RuntimeException("Got the wrong version: expected ${gradleVersionCheck} but was ${gradle.gradleVersion}")
}

View File

@@ -0,0 +1,12 @@
plugins {
id("com.gradle.build-scan") version("3.7")
}
gradleEnterprise {
buildScan {
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
publishAlways()
uploadInBackground = false
}
}

View File

@@ -0,0 +1,8 @@
rootProject.name = 'no-wrapper'
println "Using Gradle version: ${gradle.gradleVersion}"
def gradleVersionCheck = System.properties.gradleVersionCheck
if (gradleVersionCheck && gradle.gradleVersion != gradleVersionCheck) {
throw new RuntimeException("Got the wrong version: expected ${gradleVersionCheck} but was ${gradle.gradleVersion}")
}

View File

@@ -0,0 +1,21 @@
plugins {
id("com.gradle.enterprise") version("3.7")
}
gradleEnterprise {
buildScan {
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
publishAlways()
uploadInBackground = false
}
}
rootProject.name = 'no-wrapper'
println "Using Gradle version: ${gradle.gradleVersion}"
def gradleVersionCheck = System.properties.gradleVersionCheck
if (gradleVersionCheck && gradle.gradleVersion != gradleVersionCheck) {
throw new RuntimeException("Got the wrong version: expected ${gradleVersionCheck} but was ${gradle.gradleVersion}")
}

View File

@@ -1,26 +1,60 @@
name: "Gradle Command"
description: 'Execute Gradle Command Line'
author: 'Paul Merlin <paul@nospere.org>'
name: "Gradle Build Action"
description: 'Configures Gradle for use in GitHub actions, caching useful state in the GitHub actions cache'
# https://help.github.com/en/articles/metadata-syntax-for-github-actions
inputs:
wrapper-directory:
description: Path to the Gradle Wrapper directory
required: false
gradle-executable:
description: Path to the Gradle executable
required: false
gradle-version:
description: Gradle version to use
required: false
cache-disabled:
description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
required: false
default: false
cache-read-only:
description: When 'true', existing entries will be read from the cache but no entries will be written.
required: false
default: false
# e.g. Use the following setting to only write cache entries from your 'main' branch
# cache-read-only: ${{ github.ref != 'refs/heads/main' }}
gradle-home-cache-includes:
description: Paths within Gradle User Home to cache.
required: false
default: |
caches
notifications
gradle-home-cache-excludes:
description: Paths within Gradle User Home to exclude from cache.
required: false
# e.g. Use the following setting to prevent the local build cache from being saved/restored
# gradle-home-cache-excludes: |
# ["caches/build-cache-1"]
arguments:
description: Gradle command line arguments (supports multi-line input)
required: false
build-root-directory:
description: Path to the root directory of the build
required: false
arguments:
description: Gradle command line arguments, see gradle --help
gradle-executable:
description: Path to the Gradle executable
required: false
# EXPERIMENTAL & INTERNAL ACTION INPUTS
# The following action properties allow fine-grained tweaking of the action caching behaviour.
# These properties are not designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.
# Use at your own risk!
workflow-job-context:
description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users (INTERNAL).
required: false
default: ${{ toJSON(matrix) }}
outputs:
build-scan-url:
description: Link to the build scan if any
@@ -29,7 +63,6 @@ runs:
using: 'node12'
main: 'dist/main/index.js'
post: 'dist/post/index.js'
post-if: success()
branding:
icon: 'box'

3
dist/main/index.js vendored

File diff suppressed because one or more lines are too long

1
dist/main/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

3912
dist/main/sourcemap-register.js vendored Normal file

File diff suppressed because it is too large Load Diff

3
dist/post/index.js vendored

File diff suppressed because one or more lines are too long

1
dist/post/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

3912
dist/post/sourcemap-register.js vendored Normal file

File diff suppressed because it is too large Load Diff

14601
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,20 @@
{
"name": "gradle-command-action",
"name": "gradle-build-action",
"version": "1.0.0",
"private": true,
"description": "Execute Gradle Command Line",
"description": "Execute Gradle Build",
"scripts": {
"postinstall": "patch-package",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts",
"build": "ncc build src/main.ts --out dist/main --minify && ncc build src/post.ts --out dist/post --minify",
"build": "ncc build src/main.ts --out dist/main --source-map --minify && ncc build src/post.ts --out dist/post --source-map --minify",
"test": "jest",
"all": "npm run format && npm run lint && npm run build && npm test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/eskatos/gradle-command-action.git"
"url": "git+https://github.com/gradle/gradle-build-action.git"
},
"keywords": [
"github",
@@ -21,32 +22,32 @@
"github-actions",
"gradle"
],
"author": "Paul Merlin <paul@nosphere.org>",
"license": "MIT",
"dependencies": {
"@actions/core": "1.2.4",
"@actions/exec": "1.0.4",
"@actions/io": "1.0.2",
"@actions/tool-cache": "1.5.5",
"@actions/cache": "0.2.1",
"string-argv": "0.3.1",
"typed-rest-client": "1.7.3",
"unzipper": "0.10.11"
"@actions/cache": "1.0.8",
"@actions/core": "1.6.0",
"@actions/exec": "1.1.0",
"@actions/github": "5.0.0",
"@actions/glob": "0.2.0",
"@actions/http-client": "1.0.11",
"@actions/tool-cache": "1.7.1",
"patch-package": "6.4.7",
"string-argv": "0.3.1"
},
"devDependencies": {
"@types/jest": "26.0.0",
"@types/node": "12.12.6",
"@types/unzipper": "0.10.3",
"@typescript-eslint/parser": "3.2.0",
"@types/jest": "27.0.3",
"@types/node": "16.11.11",
"@types/unzipper": "0.10.4",
"@typescript-eslint/parser": "5.5.0",
"@zeit/ncc": "0.22.3",
"eslint": "7.2.0",
"eslint-plugin-github": "4.0.1",
"eslint-plugin-jest": "23.13.2",
"jest": "26.0.1",
"jest-circus": "26.0.1",
"js-yaml": "3.14.0",
"prettier": "2.0.5",
"ts-jest": "26.1.0",
"typescript": "3.8.3"
"eslint": "8.3.0",
"eslint-plugin-github": "4.3.5",
"eslint-plugin-jest": "25.3.0",
"jest": "27.4.3",
"jest-circus": "27.4.2",
"js-yaml": "4.1.0",
"prettier": "2.5.0",
"ts-jest": "27.0.7",
"typescript": "4.5.2"
}
}

View File

@@ -0,0 +1,108 @@
diff --git a/node_modules/@actions/cache/lib/cache.d.ts b/node_modules/@actions/cache/lib/cache.d.ts
index 805a8e5..d3ab419 100644
--- a/node_modules/@actions/cache/lib/cache.d.ts
+++ b/node_modules/@actions/cache/lib/cache.d.ts
@@ -5,6 +5,11 @@ export declare class ValidationError extends Error {
export declare class ReserveCacheError extends Error {
constructor(message: string);
}
+export declare class CacheEntry {
+ key: string;
+ size?: number;
+ constructor(key: string, size?: number);
+}
/**
* Restores cache from keys
*
@@ -14,7 +19,7 @@ export declare class ReserveCacheError extends Error {
* @param downloadOptions cache download options
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
-export declare function restoreCache(paths: string[], primaryKey: string, restoreKeys?: string[], options?: DownloadOptions): Promise<string | undefined>;
+export declare function restoreCache(paths: string[], primaryKey: string, restoreKeys?: string[], options?: DownloadOptions): Promise<CacheEntry | undefined>;
/**
* Saves a list of files with the specified key
*
@@ -23,4 +28,4 @@ export declare function restoreCache(paths: string[], primaryKey: string, restor
* @param options cache upload options
* @returns number returns cacheId if the cache was saved successfully and throws an error if save fails
*/
-export declare function saveCache(paths: string[], key: string, options?: UploadOptions): Promise<number>;
+export declare function saveCache(paths: string[], key: string, options?: UploadOptions): Promise<CacheEntry>;
diff --git a/node_modules/@actions/cache/lib/cache.js b/node_modules/@actions/cache/lib/cache.js
index e3c43e6..c947d02 100644
--- a/node_modules/@actions/cache/lib/cache.js
+++ b/node_modules/@actions/cache/lib/cache.js
@@ -37,6 +37,13 @@ class ReserveCacheError extends Error {
}
}
exports.ReserveCacheError = ReserveCacheError;
+class CacheEntry {
+ constructor(key, size) {
+ this.key = key;
+ this.size = size;
+ }
+}
+exports.CacheEntry = CacheEntry;
function checkPaths(paths) {
if (!paths || paths.length === 0) {
throw new ValidationError(`Path Validation Error: At least one directory or file path is required`);
@@ -84,6 +91,7 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
}
const archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod));
core.debug(`Archive Path: ${archivePath}`);
+ const restoredEntry = new CacheEntry(cacheEntry.cacheKey);
try {
// Download the cache from the cache entry
yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options);
@@ -91,6 +99,7 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
yield tar_1.listTar(archivePath, compressionMethod);
}
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath);
+ restoredEntry.size = archiveFileSize;
core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`);
yield tar_1.extractTar(archivePath, compressionMethod);
core.info('Cache restored successfully');
@@ -104,7 +113,7 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
core.debug(`Failed to delete archive: ${error}`);
}
}
- return cacheEntry.cacheKey;
+ return restoredEntry;
});
}
exports.restoreCache = restoreCache;
@@ -135,6 +144,7 @@ function saveCache(paths, key, options) {
const archiveFolder = yield utils.createTempDirectory();
const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod));
core.debug(`Archive Path: ${archivePath}`);
+ const savedEntry = new CacheEntry(key);
try {
yield tar_1.createTar(archiveFolder, cachePaths, compressionMethod);
if (core.isDebug()) {
@@ -142,6 +152,7 @@ function saveCache(paths, key, options) {
}
const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath);
+ savedEntry.size = archiveFileSize;
core.debug(`File Size: ${archiveFileSize}`);
if (archiveFileSize > fileSizeLimit) {
throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`);
@@ -158,7 +169,7 @@ function saveCache(paths, key, options) {
core.debug(`Failed to delete archive: ${error}`);
}
}
- return cacheId;
+ return savedEntry;
});
}
exports.saveCache = saveCache;
diff --git a/node_modules/@actions/cache/lib/cache.js.map b/node_modules/@actions/cache/lib/cache.js.map
index ec474b2..7e09719 100644
--- a/node_modules/@actions/cache/lib/cache.js.map
+++ b/node_modules/@actions/cache/lib/cache.js.map
@@ -1 +1 @@
-{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
\ No newline at end of file

333
src/cache-base.ts Normal file
View File

@@ -0,0 +1,333 @@
import * as core from '@actions/core'
import * as exec from '@actions/exec'
import * as github from '@actions/github'
import path from 'path'
import fs from 'fs'
import {CacheListener} from './cache-reporting'
import {
getCacheKeyPrefix,
determineJobContext,
saveCache,
restoreCache,
cacheDebug,
isCacheDebuggingEnabled,
tryDelete
} from './cache-utils'
import {ConfigurationCacheEntryExtractor, GradleHomeEntryExtractor} from './cache-extract-entries'
const CACHE_PROTOCOL_VERSION = 'v5-'
export const META_FILE_DIR = '.gradle-build-action'
export const PROJECT_ROOTS_FILE = 'project-roots.txt'
const INCLUDE_PATHS_PARAMETER = 'gradle-home-cache-includes'
const EXCLUDE_PATHS_PARAMETER = 'gradle-home-cache-excludes'
/**
* Represents a key used to restore a cache entry.
* The Github Actions cache will first try for an exact match on the key.
* If that fails, it will try for a prefix match on any of the restoreKeys.
*/
class CacheKey {
key: string
restoreKeys: string[]
constructor(key: string, restoreKeys: string[]) {
this.key = key
this.restoreKeys = restoreKeys
}
}
/**
* Generates a cache key specific to the current job execution.
* The key is constructed from the following inputs:
* - A user-defined prefix (optional)
* - The cache protocol version
* - The name of the cache
* - The runner operating system
* - The name of the Job being executed
* - The matrix values for the Job being executed (job context)
* - The SHA of the commit being executed
*
* Caches are restored by trying to match the these key prefixes in order:
* - The full key with SHA
* - A previous key for this Job + matrix
* - Any previous key for this Job (any matrix)
* - Any previous key for this cache on the current OS
*/
function generateCacheKey(cacheName: string): CacheKey {
const cacheKeyBase = `${getCacheKeyPrefix()}${CACHE_PROTOCOL_VERSION}${cacheName}`
// At the most general level, share caches for all executions on the same OS
const runnerOs = process.env['RUNNER_OS'] || ''
const cacheKeyForOs = `${cacheKeyBase}|${runnerOs}`
// Prefer caches that run this job
const cacheKeyForJob = `${cacheKeyForOs}|${github.context.job}`
// Prefer (even more) jobs that run this job with the same context (matrix)
const cacheKeyForJobContext = `${cacheKeyForJob}[${determineJobContext()}]`
// Exact match on Git SHA
const cacheKey = `${cacheKeyForJobContext}-${github.context.sha}`
return new CacheKey(cacheKey, [cacheKeyForJobContext, cacheKeyForJob, cacheKeyForOs])
}
export class GradleStateCache {
private cacheName: string
private cacheDescription: string
private cacheKeyStateKey: string
private cacheResultStateKey: string
protected readonly gradleUserHome: string
constructor(gradleUserHome: string) {
this.gradleUserHome = gradleUserHome
this.cacheName = 'gradle'
this.cacheDescription = 'Gradle User Home'
this.cacheKeyStateKey = `CACHE_KEY_gradle`
this.cacheResultStateKey = `CACHE_RESULT_gradle`
}
init(): void {
const actionCacheDir = path.resolve(this.gradleUserHome, '.gradle-build-action')
fs.mkdirSync(actionCacheDir, {recursive: true})
const initScriptsDir = path.resolve(this.gradleUserHome, 'init.d')
fs.mkdirSync(initScriptsDir, {recursive: true})
this.initializeGradleUserHome(this.gradleUserHome, initScriptsDir)
}
/**
* Restores the cache entry, finding the closest match to the currently running job.
*/
async restore(listener: CacheListener): Promise<void> {
const entryListener = listener.entry(this.cacheDescription)
const cacheKey = generateCacheKey(this.cacheName)
core.saveState(this.cacheKeyStateKey, cacheKey.key)
cacheDebug(
`Requesting ${this.cacheDescription} with
key:${cacheKey.key}
restoreKeys:[${cacheKey.restoreKeys}]`
)
const cacheResult = await restoreCache(this.getCachePath(), cacheKey.key, cacheKey.restoreKeys)
entryListener.markRequested(cacheKey.key, cacheKey.restoreKeys)
if (!cacheResult) {
core.info(`${this.cacheDescription} cache not found. Will initialize empty.`)
return
}
core.saveState(this.cacheResultStateKey, cacheResult.key)
entryListener.markRestored(cacheResult.key, cacheResult.size)
core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult.key}`)
try {
await this.afterRestore(listener)
} catch (error) {
core.warning(`Restore ${this.cacheDescription} failed in 'afterRestore': ${error}`)
}
}
/**
* Restore any extracted cache entries after the main Gradle User Home entry is restored.
*/
async afterRestore(listener: CacheListener): Promise<void> {
await this.debugReportGradleUserHomeSize('as restored from cache')
await new GradleHomeEntryExtractor(this.gradleUserHome).restore(listener)
await new ConfigurationCacheEntryExtractor(this.gradleUserHome).restore(listener)
await this.debugReportGradleUserHomeSize('after restoring common artifacts')
}
/**
* Saves the cache entry based on the current cache key unless the cache was restored with the exact key,
* in which case we cannot overwrite it.
*
* If the cache entry was restored with a partial match on a restore key, then
* it is saved with the exact key.
*/
async save(listener: CacheListener): Promise<void> {
// Retrieve the state set in the previous 'restore' step.
const cacheKeyFromRestore = core.getState(this.cacheKeyStateKey)
const cacheResultFromRestore = core.getState(this.cacheResultStateKey)
if (cacheResultFromRestore && cacheKeyFromRestore === cacheResultFromRestore) {
core.info(`Cache hit occurred on the cache key ${cacheKeyFromRestore}, not saving cache.`)
return
}
try {
await this.beforeSave(listener)
} catch (error) {
core.warning(`Save ${this.cacheDescription} failed in 'beforeSave': ${error}`)
return
}
core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKeyFromRestore}`)
const cachePath = this.getCachePath()
const savedEntry = await saveCache(cachePath, cacheKeyFromRestore)
if (savedEntry) {
listener.entry(this.cacheDescription).markSaved(savedEntry.key, savedEntry.size)
}
return
}
/**
* Extract and save any defined extracted cache entries prior to the main Gradle User Home entry being saved.
*/
async beforeSave(listener: CacheListener): Promise<void> {
await this.debugReportGradleUserHomeSize('before saving common artifacts')
this.deleteExcludedPaths()
await Promise.all([
new GradleHomeEntryExtractor(this.gradleUserHome).extract(listener),
new ConfigurationCacheEntryExtractor(this.gradleUserHome).extract(listener)
])
await this.debugReportGradleUserHomeSize(
"after extracting common artifacts (only 'caches' and 'notifications' will be stored)"
)
}
/**
* Delete any file paths that are excluded by the `gradle-home-cache-excludes` parameter.
*/
private deleteExcludedPaths(): void {
const rawPaths: string[] = core.getMultilineInput(EXCLUDE_PATHS_PARAMETER)
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
for (const p of resolvedPaths) {
cacheDebug(`Deleting excluded path: ${p}`)
tryDelete(p)
}
}
/**
* Determines the paths within Gradle User Home to cache.
* By default, this is the 'caches' and 'notifications' directories,
* but this can be overridden by the `gradle-home-cache-includes` parameter.
*/
protected getCachePath(): string[] {
const rawPaths: string[] = core.getMultilineInput(INCLUDE_PATHS_PARAMETER)
rawPaths.push(META_FILE_DIR)
const resolvedPaths = rawPaths.map(x => this.resolveCachePath(x))
cacheDebug(`Using cache paths: ${resolvedPaths}`)
return resolvedPaths
}
private resolveCachePath(rawPath: string): string {
if (rawPath.startsWith('!')) {
const resolved = this.resolveCachePath(rawPath.substring(1))
return `!${resolved}`
}
return path.resolve(this.gradleUserHome, rawPath)
}
private initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void {
const propertiesFile = path.resolve(gradleUserHome, 'gradle.properties')
fs.writeFileSync(propertiesFile, 'org.gradle.daemon=false')
const buildScanCapture = path.resolve(initScriptsDir, 'build-scan-capture.init.gradle')
fs.writeFileSync(
buildScanCapture,
`import org.gradle.util.GradleVersion
// Only run again root build. Do not run against included builds.
def isTopLevelBuild = gradle.getParent() == null
if (isTopLevelBuild) {
def version = GradleVersion.current().baseVersion
def atLeastGradle4 = version >= GradleVersion.version("4.0")
def atLeastGradle6 = version >= GradleVersion.version("6.0")
if (atLeastGradle6) {
settingsEvaluated { settings ->
if (settings.pluginManager.hasPlugin("com.gradle.enterprise")) {
registerCallbacks(settings.extensions["gradleEnterprise"].buildScan, settings.rootProject.name)
}
}
} else if (atLeastGradle4) {
projectsEvaluated { gradle ->
if (gradle.rootProject.pluginManager.hasPlugin("com.gradle.build-scan")) {
registerCallbacks(gradle.rootProject.extensions["buildScan"], gradle.rootProject.name)
}
}
}
}
def registerCallbacks(buildScanExtension, rootProjectName) {
buildScanExtension.with {
def buildOutcome = ""
def scanFile = new File("gradle-build-scan.txt")
buildFinished { result ->
buildOutcome = result.failure == null ? " succeeded" : " failed"
}
buildScanPublished { buildScan ->
scanFile.text = buildScan.buildScanUri
// Send commands directly to GitHub Actions via STDOUT.
def message = "Build '\${rootProjectName}'\${buildOutcome} - \${buildScan.buildScanUri}"
println("::notice ::\${message}")
println("::set-output name=build-scan-url::\${buildScan.buildScanUri}")
}
}
}`
)
const projectRootCapture = path.resolve(initScriptsDir, 'project-root-capture.init.gradle')
fs.writeFileSync(
projectRootCapture,
`
// Only run again root build. Do not run against included builds.
def isTopLevelBuild = gradle.getParent() == null
if (isTopLevelBuild) {
settingsEvaluated { settings ->
def projectRootEntry = settings.rootDir.absolutePath + "\\n"
def projectRootList = new File(settings.gradle.gradleUserHomeDir, "${PROJECT_ROOTS_FILE}")
if (!projectRootList.exists() || !projectRootList.text.contains(projectRootEntry)) {
projectRootList << projectRootEntry
}
}
}`
)
}
/**
* When cache debugging is enabled, this method will give a detailed report
* of the Gradle User Home contents.
*/
private async debugReportGradleUserHomeSize(label: string): Promise<void> {
if (!isCacheDebuggingEnabled()) {
return
}
if (!fs.existsSync(this.gradleUserHome)) {
return
}
const result = await exec.getExecOutput('du', ['-h', '-c', '-t', '5M'], {
cwd: this.gradleUserHome,
silent: true,
ignoreReturnCode: true
})
core.info(`Gradle User Home (directories >5M): ${label}`)
core.info(
result.stdout
.trimEnd()
.replace(/\t/g, ' ')
.split('\n')
.map(it => {
return ` ${it}`
})
.join('\n')
)
core.info('-----------------------')
}
}

View File

@@ -0,0 +1,376 @@
import path from 'path'
import fs from 'fs'
import * as core from '@actions/core'
import * as glob from '@actions/glob'
import {META_FILE_DIR, PROJECT_ROOTS_FILE} from './cache-base'
import {CacheEntryListener, CacheListener} from './cache-reporting'
import {
cacheDebug,
getCacheKeyPrefix,
hashFileNames,
isCacheDebuggingEnabled,
restoreCache,
saveCache,
tryDelete
} from './cache-utils'
const SKIP_RESTORE_VAR = 'GRADLE_BUILD_ACTION_SKIP_RESTORE'
/**
* Represents the result of attempting to load or store an extracted cache entry.
* An undefined cacheKey indicates that the operation did not succeed.
* The collected results are then used to populate the `cache-metadata.json` file for later use.
*/
class ExtractedCacheEntry {
artifactType: string
pattern: string
cacheKey: string | undefined
constructor(artifactType: string, pattern: string, cacheKey: string | undefined) {
this.artifactType = artifactType
this.pattern = pattern
this.cacheKey = cacheKey
}
}
/**
* Representation of all of the extracted cache entries for this Gradle User Home.
* This object is persisted to JSON file in the Gradle User Home directory for storing,
* and subsequently used to restore the Gradle User Home.
*/
class ExtractedCacheEntryMetadata {
entries: ExtractedCacheEntry[] = []
}
/**
* The specification for a type of extracted cache entry.
*/
class ExtractedCacheEntryDefinition {
artifactType: string
pattern: string
bundle: boolean
uniqueFileNames = true
constructor(artifactType: string, pattern: string, bundle: boolean) {
this.artifactType = artifactType
this.pattern = pattern
this.bundle = bundle
}
withNonUniqueFileNames(): ExtractedCacheEntryDefinition {
this.uniqueFileNames = false
return this
}
}
/**
* Caches and restores the entire Gradle User Home directory, extracting entries containing common artifacts
* for more efficient storage.
*/
abstract class AbstractEntryExtractor {
protected readonly gradleUserHome: string
private extractorName: string
constructor(gradleUserHome: string, extractorName: string) {
this.gradleUserHome = gradleUserHome
this.extractorName = extractorName
}
/**
* Restores any artifacts that were cached separately, based on the information in the `cache-metadata.json` file.
* Each extracted cache entry is restored in parallel, except when debugging is enabled.
*/
async restore(listener: CacheListener): Promise<void> {
const previouslyExtractedCacheEntries = this.loadExtractedCacheEntries()
const processes: Promise<ExtractedCacheEntry>[] = []
for (const cacheEntry of previouslyExtractedCacheEntries) {
const artifactType = cacheEntry.artifactType
const entryListener = listener.entry(cacheEntry.pattern)
// Handle case where the extracted-cache-entry definitions have been changed
const skipRestore = process.env[SKIP_RESTORE_VAR] || ''
if (skipRestore.includes(artifactType)) {
core.info(`Not restoring extracted cache entry for ${artifactType}`)
entryListener.markRequested('SKIP_RESTORE')
} else {
processes.push(
this.awaitForDebugging(
this.restoreExtractedCacheEntry(
artifactType,
cacheEntry.cacheKey!,
cacheEntry.pattern,
entryListener
)
)
)
}
}
this.saveMetadataForCacheResults(await Promise.all(processes))
}
private async restoreExtractedCacheEntry(
artifactType: string,
cacheKey: string,
pattern: string,
listener: CacheEntryListener
): Promise<ExtractedCacheEntry> {
listener.markRequested(cacheKey)
const restoredEntry = await restoreCache([pattern], cacheKey)
if (restoredEntry) {
core.info(`Restored ${artifactType} with key ${cacheKey} to ${pattern}`)
listener.markRestored(restoredEntry.key, restoredEntry.size)
return new ExtractedCacheEntry(artifactType, pattern, cacheKey)
} else {
core.info(`Did not restore ${artifactType} with key ${cacheKey} to ${pattern}`)
return new ExtractedCacheEntry(artifactType, pattern, undefined)
}
}
/**
* Saves any artifacts that are configured to be cached separately, based on the extracted cache entry definitions.
* Each entry is extracted and saved in parallel, except when debugging is enabled.
*/
async extract(listener: CacheListener): Promise<void> {
// Load the cache entry definitions (from config) and the previously restored entries (from persisted metadata file)
const cacheEntryDefinitions = this.getExtractedCacheEntryDefinitions()
cacheDebug(
`Extracting cache entries for ${this.extractorName}: ${JSON.stringify(cacheEntryDefinitions, null, 2)}`
)
const previouslyRestoredEntries = this.loadExtractedCacheEntries()
const cacheActions: Promise<ExtractedCacheEntry>[] = []
// For each cache entry definition, determine if it has already been restored, and if not, extract it
for (const cacheEntryDefinition of cacheEntryDefinitions) {
const artifactType = cacheEntryDefinition.artifactType
const pattern = cacheEntryDefinition.pattern
// Find all matching files for this cache entry definition
const globber = await glob.create(pattern, {
implicitDescendants: false,
followSymbolicLinks: false
})
const matchingFiles = await globber.glob()
if (matchingFiles.length === 0) {
cacheDebug(`No files found to cache for ${artifactType}`)
continue
}
if (cacheEntryDefinition.bundle) {
// For an extracted "bundle", use the defined pattern and cache all matching files in a single entry.
cacheActions.push(
this.awaitForDebugging(
this.saveExtractedCacheEntry(
matchingFiles,
artifactType,
pattern,
cacheEntryDefinition.uniqueFileNames,
previouslyRestoredEntries,
listener.entry(pattern)
)
)
)
} else {
// Otherwise cache each matching file in a separate entry, using the complete file path as the cache pattern.
for (const cacheFile of matchingFiles) {
cacheActions.push(
this.awaitForDebugging(
this.saveExtractedCacheEntry(
[cacheFile],
artifactType,
cacheFile,
cacheEntryDefinition.uniqueFileNames,
previouslyRestoredEntries,
listener.entry(cacheFile)
)
)
)
}
}
}
this.saveMetadataForCacheResults(await Promise.all(cacheActions))
}
private async saveExtractedCacheEntry(
matchingFiles: string[],
artifactType: string,
pattern: string,
uniqueFileNames: boolean,
previouslyRestoredEntries: ExtractedCacheEntry[],
entryListener: CacheEntryListener
): Promise<ExtractedCacheEntry> {
const cacheKey = uniqueFileNames
? this.createCacheKeyFromFileNames(artifactType, matchingFiles)
: await this.createCacheKeyFromFileContents(artifactType, pattern)
const previouslyRestoredKey = previouslyRestoredEntries.find(
x => x.artifactType === artifactType && x.pattern === pattern
)?.cacheKey
if (previouslyRestoredKey === cacheKey) {
cacheDebug(`No change to previously restored ${artifactType}. Not saving.`)
} else {
core.info(`Caching ${artifactType} with path '${pattern}' and cache key: ${cacheKey}`)
const savedEntry = await saveCache([pattern], cacheKey)
if (savedEntry !== undefined) {
entryListener.markSaved(savedEntry.key, savedEntry.size)
}
}
for (const file of matchingFiles) {
tryDelete(file)
}
return new ExtractedCacheEntry(artifactType, pattern, cacheKey)
}
protected createCacheKeyFromFileNames(artifactType: string, files: string[]): string {
const cacheKeyPrefix = getCacheKeyPrefix()
const relativeFiles = files.map(x => path.relative(this.gradleUserHome, x))
const key = hashFileNames(relativeFiles)
cacheDebug(`Generating cache key for ${artifactType} from file names: ${relativeFiles}`)
return `${cacheKeyPrefix}${artifactType}-${key}`
}
protected async createCacheKeyFromFileContents(artifactType: string, pattern: string): Promise<string> {
const cacheKeyPrefix = getCacheKeyPrefix()
const key = await glob.hashFiles(pattern)
cacheDebug(`Generating cache key for ${artifactType} from files matching: ${pattern}`)
return `${cacheKeyPrefix}${artifactType}-${key}`
}
// Run actions sequentially if debugging is enabled
private async awaitForDebugging(p: Promise<ExtractedCacheEntry>): Promise<ExtractedCacheEntry> {
if (isCacheDebuggingEnabled()) {
await p
}
return p
}
/**
* Load information about the extracted cache entries previously restored/saved. This is loaded from the 'cache-metadata.json' file.
*/
protected loadExtractedCacheEntries(): ExtractedCacheEntry[] {
const cacheMetadataFile = this.getCacheMetadataFile()
if (!fs.existsSync(cacheMetadataFile)) {
return []
}
const filedata = fs.readFileSync(cacheMetadataFile, 'utf-8')
cacheDebug(`Loaded cache metadata: ${filedata}`)
const extractedCacheEntryMetadata = JSON.parse(filedata) as ExtractedCacheEntryMetadata
return extractedCacheEntryMetadata.entries
}
/**
* Saves information about the extracted cache entries into the 'cache-metadata.json' file.
*/
private saveMetadataForCacheResults(results: ExtractedCacheEntry[]): void {
const extractedCacheEntryMetadata = new ExtractedCacheEntryMetadata()
extractedCacheEntryMetadata.entries = results.filter(x => x.cacheKey !== undefined)
const filedata = JSON.stringify(extractedCacheEntryMetadata)
cacheDebug(`Saving cache metadata: ${filedata}`)
fs.writeFileSync(this.getCacheMetadataFile(), filedata, 'utf-8')
}
private getCacheMetadataFile(): string {
const actionMetadataDirectory = path.resolve(this.gradleUserHome, META_FILE_DIR)
fs.mkdirSync(actionMetadataDirectory, {recursive: true})
return path.resolve(actionMetadataDirectory, `${this.extractorName}-entry-metadata.json`)
}
protected abstract getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[]
}
export class GradleHomeEntryExtractor extends AbstractEntryExtractor {
constructor(gradleUserHome: string) {
super(gradleUserHome, 'gradle-home')
}
/**
* Return the extracted cache entry definitions, which determine which artifacts will be cached
* separately from the rest of the Gradle User Home cache entry.
*/
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
const entryDefinition = (
artifactType: string,
patterns: string[],
bundle: boolean
): ExtractedCacheEntryDefinition => {
const resolvedPattern = patterns.map(x => path.resolve(this.gradleUserHome, x)).join('\n')
return new ExtractedCacheEntryDefinition(artifactType, resolvedPattern, bundle)
}
return [
entryDefinition('generated-gradle-jars', ['caches/*/generated-gradle-jars/*.jar'], false),
entryDefinition('wrapper-zips', ['wrapper/dists/*/*/*.zip'], false),
entryDefinition('java-toolchains', ['jdks/*.zip', 'jdks/*.tar.gz'], false),
entryDefinition('dependencies', ['caches/modules-*/files-*/*/*/*/*'], true),
entryDefinition('instrumented-jars', ['caches/jars-*/*'], true),
entryDefinition('kotlin-dsl', ['caches/*/kotlin-dsl/*/*'], true)
]
}
}
export class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
constructor(gradleUserHome: string) {
super(gradleUserHome, 'configuration-cache')
}
/**
* Handle the case where Gradle User Home has not been fully restored, so that the configuration-cache
* entry is not reusable.
*/
async restore(listener: CacheListener): Promise<void> {
if (listener.fullyRestored) {
return super.restore(listener)
}
core.info('Not restoring configuration-cache state, as Gradle User Home was not fully restored')
for (const cacheEntry of this.loadExtractedCacheEntries()) {
listener.entry(cacheEntry.pattern).markRequested('NOT_RESTORED')
}
}
/**
* Extract cache entries for the configuration cache in each project.
*/
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
return this.getProjectRoots().map(projectRoot => {
const configCachePath = path.resolve(projectRoot, '.gradle/configuration-cache')
return new ExtractedCacheEntryDefinition(
'configuration-cache',
configCachePath,
true
).withNonUniqueFileNames()
})
}
/**
* For every Gradle invocation, we record the project root directory. This method returns the entire
* set of project roots, to allow saving of configuration-cache entries for each.
*/
private getProjectRoots(): string[] {
const projectList = path.resolve(this.gradleUserHome, PROJECT_ROOTS_FILE)
if (!fs.existsSync(projectList)) {
core.info(`Missing project list file ${projectList}`)
return []
}
const projectRoots = fs.readFileSync(projectList, 'utf-8')
core.info(`Found project roots '${projectRoots}' in ${projectList}`)
return projectRoots.trim().split('\n')
}
}

123
src/cache-reporting.ts Normal file
View File

@@ -0,0 +1,123 @@
import * as core from '@actions/core'
/**
* Collects information on what entries were saved and restored during the action.
* This information is used to generate a summary of the cache usage.
*/
export class CacheListener {
cacheEntries: CacheEntryListener[] = []
get fullyRestored(): boolean {
return this.cacheEntries.every(x => !x.wasRequestedButNotRestored())
}
entry(name: string): CacheEntryListener {
for (const entry of this.cacheEntries) {
if (entry.entryName === name) {
return entry
}
}
const newEntry = new CacheEntryListener(name)
this.cacheEntries.push(newEntry)
return newEntry
}
stringify(): string {
return JSON.stringify(this)
}
static rehydrate(stringRep: string): CacheListener {
const rehydrated: CacheListener = Object.assign(new CacheListener(), JSON.parse(stringRep))
const entries = rehydrated.cacheEntries
for (let index = 0; index < entries.length; index++) {
const rawEntry = entries[index]
entries[index] = Object.assign(new CacheEntryListener(rawEntry.entryName), rawEntry)
}
return rehydrated
}
}
/**
* Collects information on the state of a single cache entry.
*/
export class CacheEntryListener {
entryName: string
requestedKey: string | undefined
requestedRestoreKeys: string[] | undefined
restoredKey: string | undefined
restoredSize: number | undefined
savedKey: string | undefined
savedSize: number | undefined
constructor(entryName: string) {
this.entryName = entryName
}
wasRequestedButNotRestored(): boolean {
return this.requestedKey !== undefined && this.restoredKey === undefined
}
markRequested(key: string, restoreKeys: string[] = []): CacheEntryListener {
this.requestedKey = key
this.requestedRestoreKeys = restoreKeys
return this
}
markRestored(key: string, size: number | undefined): CacheEntryListener {
this.restoredKey = key
this.restoredSize = size
return this
}
markSaved(key: string, size: number | undefined): CacheEntryListener {
this.savedKey = key
this.savedSize = size
return this
}
}
export function logCachingReport(listener: CacheListener): void {
if (listener.cacheEntries.length === 0) {
return
}
core.info(`---------- Caching Summary -------------
Restored Entries Count: ${getCount(listener.cacheEntries, e => e.restoredSize)}
Size: ${getSum(listener.cacheEntries, e => e.restoredSize)}
Saved Entries Count: ${getCount(listener.cacheEntries, e => e.savedSize)}
Size: ${getSum(listener.cacheEntries, e => e.savedSize)}`)
core.startGroup('Cache Entry details')
for (const entry of listener.cacheEntries) {
core.info(`Entry: ${entry.entryName}
Requested Key : ${entry.requestedKey ?? ''}
Restored Key : ${entry.restoredKey ?? ''}
Size: ${formatSize(entry.restoredSize)}
Saved Key : ${entry.savedKey ?? ''}
Size: ${formatSize(entry.savedSize)}`)
}
core.endGroup()
}
function getCount(
cacheEntries: CacheEntryListener[],
predicate: (value: CacheEntryListener) => number | undefined
): number {
return cacheEntries.filter(e => predicate(e) !== undefined).length
}
function getSum(
cacheEntries: CacheEntryListener[],
predicate: (value: CacheEntryListener) => number | undefined
): string {
return formatSize(cacheEntries.map(e => predicate(e) ?? 0).reduce((p, v) => p + v, 0))
}
function formatSize(bytes: number | undefined): string {
if (bytes === undefined || bytes === 0) {
return ''
}
return `${Math.round(bytes / (1024 * 1024))} MB (${bytes} B)`
}

123
src/cache-utils.ts Normal file
View File

@@ -0,0 +1,123 @@
import * as core from '@actions/core'
import * as cache from '@actions/cache'
import * as crypto from 'crypto'
import * as path from 'path'
import * as fs from 'fs'
const JOB_CONTEXT_PARAMETER = 'workflow-job-context'
const CACHE_DISABLED_PARAMETER = 'cache-disabled'
const CACHE_READONLY_PARAMETER = 'cache-read-only'
const CACHE_DEBUG_VAR = 'GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED'
const CACHE_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX'
export function isCacheDisabled(): boolean {
return core.getBooleanInput(CACHE_DISABLED_PARAMETER)
}
export function isCacheReadOnly(): boolean {
return core.getBooleanInput(CACHE_READONLY_PARAMETER)
}
export function isCacheDebuggingEnabled(): boolean {
return process.env[CACHE_DEBUG_VAR] ? true : false
}
export function getCacheKeyPrefix(): string {
// Prefix can be used to force change all cache keys (defaults to cache protocol version)
return process.env[CACHE_PREFIX_VAR] || ''
}
export function determineJobContext(): string {
// By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
// The only way we can obtain the `matrix` data is via the `workflow-job-context` parameter in action.yml.
const workflowJobContext = core.getInput(JOB_CONTEXT_PARAMETER)
return hashStrings([workflowJobContext])
}
export function hashFileNames(fileNames: string[]): string {
return hashStrings(fileNames.map(x => x.replace(new RegExp(`\\${path.sep}`, 'g'), '/')))
}
export function hashStrings(values: string[]): string {
const hash = crypto.createHash('md5')
for (const value of values) {
hash.update(value)
}
return hash.digest('hex')
}
export async function restoreCache(
cachePath: string[],
cacheKey: string,
cacheRestoreKeys: string[] = []
): Promise<cache.CacheEntry | undefined> {
try {
return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys)
} catch (error) {
handleCacheFailure(error, `Failed to restore ${cacheKey}`)
return undefined
}
}
export async function saveCache(cachePath: string[], cacheKey: string): Promise<cache.CacheEntry | undefined> {
try {
return await cache.saveCache(cachePath, cacheKey)
} catch (error) {
handleCacheFailure(error, `Failed to save cache entry ${cacheKey}`)
}
return undefined
}
export function cacheDebug(message: string): void {
if (isCacheDebuggingEnabled()) {
core.info(message)
} else {
core.debug(message)
}
}
export function handleCacheFailure(error: unknown, message: string): void {
if (error instanceof cache.ValidationError) {
// Fail on cache validation errors
throw error
}
if (error instanceof cache.ReserveCacheError) {
// Reserve cache errors are expected if the artifact has been previously cached
if (isCacheDebuggingEnabled()) {
core.info(`${message}: ${error}`)
} else {
core.debug(`${message}: ${error}`)
}
} else {
// Warn on all other errors
core.warning(`${message}: ${error}`)
}
}
/**
* Attempt to delete a file or directory, waiting to allow locks to be released
*/
export async function tryDelete(file: string): Promise<void> {
const stat = fs.lstatSync(file)
for (let count = 0; count < 3; count++) {
try {
if (stat.isDirectory()) {
fs.rmdirSync(file, {recursive: true})
} else {
fs.unlinkSync(file)
}
return
} catch (error) {
if (count === 2) {
throw error
} else {
core.warning(String(error))
await delay(1000)
}
}
}
}
async function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}

View File

@@ -1,96 +0,0 @@
import * as core from '@actions/core'
import * as cache from '@actions/cache'
import * as path from 'path'
import * as fs from 'fs'
const WRAPPER_CACHE_KEY = 'WRAPPER_CACHE_KEY'
const WRAPPER_CACHE_PATH = 'WRAPPER_CACHE_PATH'
const WRAPPER_CACHE_RESULT = 'WRAPPER_CACHE_RESULT'
export async function restoreCachedWrapperDist(
executableDirectory: string
): Promise<void> {
const wrapperSlug = extractGradleWrapperSlugFrom(
path.join(
path.resolve(executableDirectory),
'gradle/wrapper/gradle-wrapper.properties'
)
)
if (!wrapperSlug) return
const wrapperCacheKey = `wrapper-${wrapperSlug}`
const wrapperCachePath = path.join(
process.env.HOME!,
`.gradle/wrapper/dists/gradle-${wrapperSlug}`
)
core.saveState(WRAPPER_CACHE_KEY, wrapperCacheKey)
core.saveState(WRAPPER_CACHE_PATH, wrapperCachePath)
const restoredKey = await cache.restoreCache(
[wrapperCachePath],
wrapperCacheKey
)
if (!restoredKey) {
core.info(
'Wrapper installation cache not found, expect a Gradle distribution download.'
)
return
}
core.saveState(WRAPPER_CACHE_RESULT, restoredKey)
core.info(`Wrapper installation restored from cache key: ${restoredKey}`)
return
}
export async function cacheWrapperDist(): Promise<void> {
const cacheKey = core.getState(WRAPPER_CACHE_KEY)
const cachePath = core.getState(WRAPPER_CACHE_PATH)
const cacheResult = core.getState(WRAPPER_CACHE_RESULT)
if (!cachePath) {
core.debug('No wrapper installation to cache.')
return
}
if (cacheResult && cacheKey === cacheResult) {
core.info(
`Wrapper installation cache hit occurred on the cache key ${cacheKey}, not saving cache.`
)
return
}
try {
await cache.saveCache([cachePath], cacheKey)
} catch (error) {
if (error.name === cache.ValidationError.name) {
throw error
} else if (error.name === cache.ReserveCacheError.name) {
core.info(error.message)
} else {
core.info(`[warning] ${error.message}`)
}
}
return
}
export function extractGradleWrapperSlugFrom(
wrapperProperties: string
): string | null {
const props = fs.readFileSync(wrapperProperties, {encoding: 'utf8'})
const distUrlLine = props
.split('\n')
.find(line => line.startsWith('distributionUrl'))
if (!distUrlLine) return null
return extractGradleWrapperSlugFromDistUri(distUrlLine.substr(16).trim())
}
export function extractGradleWrapperSlugFromDistUri(
distUri: string
): string | null {
const regex = /.*gradle-(.*-(bin|all))\.zip/
const match = distUri.match(regex)
return match ? match[1] : null
}

79
src/caches.ts Normal file
View File

@@ -0,0 +1,79 @@
import * as core from '@actions/core'
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
import {logCachingReport, CacheListener} from './cache-reporting'
import {GradleStateCache} from './cache-base'
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
const CACHE_LISTENER = 'CACHE_LISTENER'
export async function restore(gradleUserHome: string): Promise<void> {
if (!shouldRestoreCaches()) {
return
}
const gradleStateCache = new GradleStateCache(gradleUserHome)
gradleStateCache.init()
await core.group('Restore Gradle state from cache', async () => {
core.saveState(GRADLE_USER_HOME, gradleUserHome)
const cacheListener = new CacheListener()
await gradleStateCache.restore(cacheListener)
core.saveState(CACHE_LISTENER, cacheListener.stringify())
})
// Export var that is detected in all later restore steps
core.exportVariable(CACHE_RESTORED_VAR, true)
// Export state that is detected in corresponding post-action step
core.saveState(CACHE_RESTORED_VAR, true)
}
export async function save(): Promise<void> {
if (!shouldSaveCaches()) {
return
}
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
if (isCacheReadOnly()) {
core.info('Cache is read-only: will not save state for use in subsequent builds.')
logCachingReport(cacheListener)
return
}
await core.group('Caching Gradle state', async () => {
const gradleUserHome = core.getState(GRADLE_USER_HOME)
return new GradleStateCache(gradleUserHome).save(cacheListener)
})
logCachingReport(cacheListener)
}
function shouldRestoreCaches(): boolean {
if (isCacheDisabled()) {
core.info('Cache is disabled: will not restore state from previous builds.')
return false
}
if (process.env[CACHE_RESTORED_VAR]) {
core.info('Cache only restored on first action step.')
return false
}
return true
}
function shouldSaveCaches(): boolean {
if (isCacheDisabled()) {
core.info('Cache is disabled: will not save state for later builds.')
return false
}
if (!core.getState(CACHE_RESTORED_VAR)) {
core.info('Cache will only be saved in final post-action step.')
return false
}
return true
}

View File

@@ -1,40 +1,33 @@
import * as core from '@actions/core'
import * as exec from '@actions/exec'
import fs from 'fs'
import path from 'path'
import * as gradlew from './gradlew'
export async function execute(
executable: string,
root: string,
argv: string[]
): Promise<BuildResult> {
let publishing = false
export async function executeGradleBuild(executable: string | undefined, root: string, args: string[]): Promise<void> {
let buildScanUrl: string | undefined
const status: number = await exec.exec(executable, argv, {
const buildScanFile = path.resolve(root, 'gradle-build-scan.txt')
if (fs.existsSync(buildScanFile)) {
fs.unlinkSync(buildScanFile)
}
// Use the provided executable, or look for a Gradle wrapper script to run
const toExecute = executable ?? gradlew.locateGradleWrapperScript(root)
const status: number = await exec.exec(toExecute, args, {
cwd: root,
ignoreReturnCode: true,
listeners: {
stdline: (line: string) => {
if (line.startsWith('Publishing build scan...')) {
publishing = true
}
if (publishing && line.length === 0) {
publishing = false
}
if (publishing && line.startsWith('http')) {
buildScanUrl = line.trim()
publishing = false
}
}
}
ignoreReturnCode: true
})
return new BuildResultImpl(status, buildScanUrl)
}
if (fs.existsSync(buildScanFile)) {
buildScanUrl = fs.readFileSync(buildScanFile, 'utf-8')
}
export interface BuildResult {
readonly status: number
readonly buildScanUrl?: string
}
class BuildResultImpl implements BuildResult {
constructor(readonly status: number, readonly buildScanUrl?: string) {}
if (status !== 0) {
if (buildScanUrl) {
core.setFailed(`Gradle build failed: ${buildScanUrl}`)
} else {
core.setFailed(`Gradle build failed: process exited with status ${status}`)
}
}
}

View File

@@ -1,9 +1,26 @@
import * as path from 'path'
import fs from 'fs'
const IS_WINDOWS = process.platform === 'win32'
export function wrapperFilename(): string {
export function wrapperScriptFilename(): string {
return IS_WINDOWS ? 'gradlew.bat' : 'gradlew'
}
export function installScriptFilename(): string {
return IS_WINDOWS ? 'gradle.bat' : 'gradle'
}
export function locateGradleWrapperScript(buildRootDirectory: string): string {
validateGradleWrapper(buildRootDirectory)
return path.resolve(buildRootDirectory, wrapperScriptFilename())
}
function validateGradleWrapper(buildRootDirectory: string): void {
const wrapperProperties = path.resolve(buildRootDirectory, 'gradle/wrapper/gradle-wrapper.properties')
if (!fs.existsSync(wrapperProperties)) {
throw new Error(
`Cannot locate a Gradle wrapper properties file at '${wrapperProperties}'. Specify 'gradle-version' or 'gradle-executable' for projects without Gradle wrapper configured.`
)
}
}

View File

@@ -1,77 +1,75 @@
import * as core from '@actions/core'
import * as path from 'path'
import * as os from 'os'
import {parseArgsStringToArgv} from 'string-argv'
import * as cache from './cache'
import * as caches from './caches'
import * as execution from './execution'
import * as gradlew from './gradlew'
import * as provision from './provision'
// Invoked by GitHub Actions
/**
* The main entry point for the action, called by Github Actions for the step.
*/
export async function run(): Promise<void> {
try {
const baseDirectory = process.env[`GITHUB_WORKSPACE`] || ''
const workspaceDirectory = process.env[`GITHUB_WORKSPACE`] || ''
const buildRootDirectory = resolveBuildRootDirectory(workspaceDirectory)
const gradleUserHome = determineGradleUserHome(buildRootDirectory)
const result = await execution.execute(
await resolveGradleExecutable(baseDirectory),
resolveBuildRootDirectory(baseDirectory),
parseCommandLineArguments()
)
await caches.restore(gradleUserHome)
if (result.buildScanUrl) {
core.setOutput('build-scan-url', result.buildScanUrl)
const executable = await provisionGradle(workspaceDirectory)
// executable will be undefined if using Gradle wrapper
if (executable !== undefined) {
core.addPath(path.dirname(executable))
}
if (result.status !== 0) {
core.setFailed(`Gradle process exited with status ${result.status}`)
// Only execute if arguments have been provided
const args: string[] = parseCommandLineArguments()
if (args.length > 0) {
await execution.executeGradleBuild(executable, buildRootDirectory, args)
}
} catch (error) {
core.setFailed(error.message)
core.setFailed(String(error))
if (error instanceof Error && error.stack) {
core.info(error.stack)
}
}
}
run()
async function resolveGradleExecutable(baseDirectory: string): Promise<string> {
const gradleVersion = inputOrNull('gradle-version')
if (gradleVersion !== null && gradleVersion !== 'wrapper') {
async function provisionGradle(workspaceDirectory: string): Promise<string | undefined> {
const gradleVersion = core.getInput('gradle-version')
if (gradleVersion !== '' && gradleVersion !== 'wrapper') {
return path.resolve(await provision.gradleVersion(gradleVersion))
}
const gradleExecutable = inputOrNull('gradle-executable')
if (gradleExecutable !== null) {
return path.resolve(baseDirectory, gradleExecutable)
const gradleExecutable = core.getInput('gradle-executable')
if (gradleExecutable !== '') {
return path.resolve(workspaceDirectory, gradleExecutable)
}
const wrapperDirectory = inputOrNull('wrapper-directory')
const executableDirectory =
wrapperDirectory !== null
? path.join(baseDirectory, wrapperDirectory)
: baseDirectory
await cache.restoreCachedWrapperDist(executableDirectory)
return path.resolve(executableDirectory, gradlew.wrapperFilename())
return undefined
}
function resolveBuildRootDirectory(baseDirectory: string): string {
const buildRootDirectory = inputOrNull('build-root-directory')
const buildRootDirectory = core.getInput('build-root-directory')
const resolvedBuildRootDirectory =
buildRootDirectory === null
? path.resolve(baseDirectory)
: path.resolve(baseDirectory, buildRootDirectory)
buildRootDirectory === '' ? path.resolve(baseDirectory) : path.resolve(baseDirectory, buildRootDirectory)
return resolvedBuildRootDirectory
}
function parseCommandLineArguments(): string[] {
const input = inputOrNull('arguments')
return input === null ? [] : parseArgsStringToArgv(input)
function determineGradleUserHome(rootDir: string): string {
const customGradleUserHome = process.env['GRADLE_USER_HOME']
if (customGradleUserHome) {
return path.resolve(rootDir, customGradleUserHome)
}
return path.resolve(os.homedir(), '.gradle')
}
function inputOrNull(name: string): string | null {
const inputString = core.getInput(name)
if (inputString.length === 0) {
return null
}
return inputString
function parseCommandLineArguments(): string[] {
const input = core.getInput('arguments')
return parseArgsStringToArgv(input)
}

View File

@@ -1,8 +1,27 @@
import * as cache from './cache'
import * as core from '@actions/core'
import * as caches from './caches'
// Invoked by GitHub Actions
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
// throw an uncaught exception. Instead of failing this action, just warn.
process.on('uncaughtException', e => handleFailure(e))
/**
* The post-execution entry point for the action, called by Github Actions after completing all steps for the Job.
*/
export async function run(): Promise<void> {
await cache.cacheWrapperDist()
try {
await caches.save()
} catch (error) {
handleFailure(error)
}
}
function handleFailure(error: unknown): void {
core.warning(`Unhandled error saving cache - job will continue: ${error}`)
if (error instanceof Error && error.stack) {
core.info(error.stack)
}
}
run()

View File

@@ -1,14 +1,14 @@
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
import * as httpm from 'typed-rest-client/HttpClient'
import * as unzip from 'unzipper'
import * as httpm from '@actions/http-client'
import * as core from '@actions/core'
import * as io from '@actions/io'
import * as cache from '@actions/cache'
import * as toolCache from '@actions/tool-cache'
import * as gradlew from './gradlew'
import {handleCacheFailure, isCacheDisabled, isCacheReadOnly} from './cache-utils'
const httpc = new httpm.HttpClient('eskatos/gradle-command-action')
const gradleVersionsBaseUrl = 'https://services.gradle.org/versions'
/**
@@ -19,6 +19,9 @@ export async function gradleVersion(version: string): Promise<string> {
case 'current':
return gradleCurrent()
case 'rc':
core.warning(`Specifying gradle-version 'rc' has been deprecated. Use 'release-candidate' instead.`)
return gradleReleaseCandidate()
case 'release-candidate':
return gradleReleaseCandidate()
case 'nightly':
return gradleNightly()
@@ -30,34 +33,27 @@ export async function gradleVersion(version: string): Promise<string> {
}
async function gradleCurrent(): Promise<string> {
const versionInfo = await gradleVersionDeclaration(
`${gradleVersionsBaseUrl}/current`
)
return provisionGradle(versionInfo.version, versionInfo.downloadUrl)
const versionInfo = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/current`)
return provisionGradle(versionInfo)
}
async function gradleReleaseCandidate(): Promise<string> {
const versionInfo = await gradleVersionDeclaration(
`${gradleVersionsBaseUrl}/release-candidate`
)
if (versionInfo) {
return provisionGradle(versionInfo.version, versionInfo.downloadUrl)
const versionInfo = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/release-candidate`)
if (versionInfo && versionInfo.version && versionInfo.downloadUrl) {
return provisionGradle(versionInfo)
}
core.info('No current release-candidate found, will fallback to current')
return gradleCurrent()
}
async function gradleNightly(): Promise<string> {
const versionInfo = await gradleVersionDeclaration(
`${gradleVersionsBaseUrl}/nightly`
)
return provisionGradle(versionInfo.version, versionInfo.downloadUrl)
const versionInfo = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/nightly`)
return provisionGradle(versionInfo)
}
async function gradleReleaseNightly(): Promise<string> {
const versionInfo = await gradleVersionDeclaration(
`${gradleVersionsBaseUrl}/release-nightly`
)
return provisionGradle(versionInfo.version, versionInfo.downloadUrl)
const versionInfo = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/release-nightly`)
return provisionGradle(versionInfo)
}
async function gradle(version: string): Promise<string> {
@@ -65,62 +61,82 @@ async function gradle(version: string): Promise<string> {
if (!versionInfo) {
throw new Error(`Gradle version ${version} does not exists`)
}
return provisionGradle(versionInfo.version, versionInfo.downloadUrl)
return provisionGradle(versionInfo)
}
async function gradleVersionDeclaration(
url: string
): Promise<GradleVersionInfo> {
async function gradleVersionDeclaration(url: string): Promise<GradleVersionInfo> {
return await httpGetGradleVersion(url)
}
async function findGradleVersionDeclaration(
version: string
): Promise<GradleVersionInfo | undefined> {
const gradleVersions = await httpGetGradleVersions(
`${gradleVersionsBaseUrl}/all`
)
async function findGradleVersionDeclaration(version: string): Promise<GradleVersionInfo | undefined> {
const gradleVersions = await httpGetGradleVersions(`${gradleVersionsBaseUrl}/all`)
return gradleVersions.find((entry: GradleVersionInfo) => {
return entry.version === version
})
}
async function provisionGradle(version: string, url: string): Promise<string> {
const cachedInstall: string = toolCache.find('gradle', version)
if (cachedInstall.length > 0) {
const cachedExecutable = executableFrom(cachedInstall)
core.info(`Provisioned Gradle executable ${cachedExecutable}`)
return cachedExecutable
async function provisionGradle(versionInfo: GradleVersionInfo): Promise<string> {
return core.group(`Provision Gradle ${versionInfo.version}`, async () => {
return locateGradleAndDownloadIfRequired(versionInfo)
})
}
async function locateGradleAndDownloadIfRequired(versionInfo: GradleVersionInfo): Promise<string> {
const installsDir = path.join(os.homedir(), 'gradle-installations/installs')
const installDir = path.join(installsDir, `gradle-${versionInfo.version}`)
if (fs.existsSync(installDir)) {
core.info(`Gradle installation already exists at ${installDir}`)
return executableFrom(installDir)
}
const home = process.env['HOME'] || ''
const tmpdir = path.join(home, 'gradle-provision-tmpdir')
const downloadsDir = path.join(tmpdir, 'downloads')
const installsDir = path.join(tmpdir, 'installs')
await io.mkdirP(downloadsDir)
await io.mkdirP(installsDir)
core.info(`Downloading ${url}`)
const downloadPath = path.join(downloadsDir, `gradle-${version}-bin.zip`)
await httpDownload(url, downloadPath)
core.info(
`Downloaded at ${downloadPath}, size ${fs.statSync(downloadPath).size}`
)
await extractZip(downloadPath, installsDir)
const installDir = path.join(installsDir, `gradle-${version}`)
core.info(`Extracted in ${installDir}`)
const downloadPath = await downloadAndCacheGradleDistribution(versionInfo)
await toolCache.extractZip(downloadPath, installsDir)
core.info(`Extracted Gradle ${versionInfo.version} to ${installDir}`)
const executable = executableFrom(installDir)
fs.chmodSync(executable, '755')
core.info(`Provisioned Gradle executable ${executable}`)
toolCache.cacheDir(installDir, 'gradle', version)
return executable
}
async function downloadAndCacheGradleDistribution(versionInfo: GradleVersionInfo): Promise<string> {
const downloadPath = path.join(os.homedir(), `gradle-installations/downloads/gradle-${versionInfo.version}-bin.zip`)
if (isCacheDisabled()) {
await downloadGradleDistribution(versionInfo, downloadPath)
return downloadPath
}
const cacheKey = `gradle-${versionInfo.version}`
try {
const restoreKey = await cache.restoreCache([downloadPath], cacheKey)
if (restoreKey) {
core.info(`Restored Gradle distribution ${cacheKey} from cache to ${downloadPath}`)
return downloadPath
}
} catch (error) {
handleCacheFailure(error, `Restore Gradle distribution ${versionInfo.version} failed`)
}
core.info(`Gradle distribution ${versionInfo.version} not found in cache. Will download.`)
await downloadGradleDistribution(versionInfo, downloadPath)
if (!isCacheReadOnly()) {
try {
await cache.saveCache([downloadPath], cacheKey)
} catch (error) {
handleCacheFailure(error, `Save Gradle distribution ${versionInfo.version} failed`)
}
}
return downloadPath
}
async function downloadGradleDistribution(versionInfo: GradleVersionInfo, downloadPath: string): Promise<void> {
await toolCache.downloadTool(versionInfo.downloadUrl, downloadPath)
core.info(`Downloaded ${versionInfo.downloadUrl} to ${downloadPath} (size ${fs.statSync(downloadPath).size})`)
}
function executableFrom(installDir: string): string {
return path.join(installDir, 'bin', `${gradlew.installScriptFilename()}`)
}
@@ -129,45 +145,16 @@ async function httpGetGradleVersion(url: string): Promise<GradleVersionInfo> {
return JSON.parse(await httpGetString(url))
}
async function httpGetGradleVersions(
url: string
): Promise<GradleVersionInfo[]> {
async function httpGetGradleVersions(url: string): Promise<GradleVersionInfo[]> {
return JSON.parse(await httpGetString(url))
}
async function httpGetString(url: string): Promise<string> {
const response = await httpc.get(url)
const httpClient = new httpm.HttpClient('gradle/gradle-build-action')
const response = await httpClient.get(url)
return response.readBody()
}
async function httpDownload(url: string, localPath: string): Promise<void> {
const response = await httpc.get(url)
return new Promise<void>(function (resolve, reject) {
const writeStream = fs.createWriteStream(localPath)
response.message
.pipe(writeStream)
.on('close', () => {
resolve()
})
.on('error', err => {
reject(err)
})
})
}
async function extractZip(zip: string, destination: string): Promise<void> {
return new Promise<void>(function (resolve, reject) {
fs.createReadStream(zip)
.pipe(unzip.Extract({path: destination}))
.on('close', () => {
resolve()
})
.on('error', err => {
reject(err)
})
})
}
interface GradleVersionInfo {
version: string
downloadUrl: string