Compare commits

...

164 Commits

Author SHA1 Message Date
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
Paul Merlin
b995a7b937 Merge pull request #19 from eskatos/eskatos/june
Automatically cache wrapper installation
2020-06-13 16:31:47 +02:00
Paul Merlin
1c1db193aa Cache wrapper installation 2020-06-13 16:15:53 +02:00
Paul Merlin
9675f09de6 Compute wrapper cache key 2020-06-13 15:51:58 +02:00
Paul Merlin
392bcac1c1 Fix state 2020-06-13 15:03:30 +02:00
Paul Merlin
a2ba1beedb Debugging 2020-06-13 15:01:08 +02:00
Paul Merlin
42e2fed267 Cache wrapper dist, step 1 2020-06-13 14:48:54 +02:00
Paul Merlin
8e2fd532f1 Fix dist download 2020-06-13 14:37:12 +02:00
Paul Merlin
2bf5eec3b6 Test dist download on CI 2020-06-13 14:33:32 +02:00
Paul Merlin
43efc20423 Run lint on CI 2020-06-13 14:33:04 +02:00
Paul Merlin
4a9a0a05a3 Lint 3 2020-06-13 14:30:57 +02:00
Paul Merlin
1647b85e82 Lint 2 2020-06-13 14:21:54 +02:00
Paul Merlin
6cee865aea Lint 2020-06-13 13:58:25 +02:00
Paul Merlin
5c61ab77ec Add back CI check for uncommited changes 2020-06-13 13:46:53 +02:00
Paul Merlin
a31de8476d Format 2020-06-13 13:44:30 +02:00
Paul Merlin
b2c379621c Add @action/cache 2020-06-13 13:42:27 +02:00
Paul Merlin
04a5ee4df2 Cleanup 2020-06-13 13:40:38 +02:00
Paul Merlin
a188e7cd61 Debug logging 2020-06-13 13:36:11 +02:00
Paul Merlin
e0644c97f9 Split action, step 2 2020-06-13 13:34:07 +02:00
Paul Merlin
9cc76cdea7 Split action, step 1 2020-06-13 13:30:20 +02:00
Paul Merlin
b55d63f1f8 Refine CI workflows 2020-06-13 13:17:04 +02:00
Paul Merlin
8f6be44bf5 Split dev/prod CI workflows 2020-06-13 13:14:52 +02:00
Paul Merlin
469bf6123e Fix CI 2020-06-13 13:10:37 +02:00
Paul Merlin
9cbb22e130 Test something on CI 2020-06-13 13:09:06 +02:00
Paul Merlin
4c65920ab9 Fix build 2020-06-13 13:03:18 +02:00
Paul Merlin
bad0f7d376 Fix build 2020-06-13 13:00:27 +02:00
Paul Merlin
ab09ae8fad Refine CI 2020-06-13 12:48:35 +02:00
Paul Merlin
2f66fb5679 Add basic test Gradle build 2020-06-13 12:47:27 +02:00
Paul Merlin
1a6a8efda2 Refine CI 2020-06-13 12:47:09 +02:00
Paul Merlin
e5b01ed062 Add more npm scripts 2020-06-13 12:47:01 +02:00
Paul Merlin
3d5e31b7a4 Upgrade dependencies 2020-06-13 12:46:47 +02:00
Paul Merlin
4336c6b886 Add various js build configs 2020-06-13 12:46:29 +02:00
Paul Merlin
f25026ba74 Refine README 2020-06-13 12:45:21 +02:00
Paul Merlin
9f5ce3593a Fix typo 2020-04-18 17:10:08 +02:00
Ivan Shcherbak
a5009d1aec Fix typo
Get an error from Github: `pull-request is not a valid event name`

https://help.github.com/en/actions/reference/events-that-trigger-workflows#example-using-a-list-of-events
2020-04-18 17:09:21 +02:00
Frieder Bluemle
ae5079ecac Fix GitHub spelling 2020-03-17 22:36:04 +01:00
Paul Merlin
38fdc3f684 Upgrade dependencies 2020-01-12 11:39:57 +01:00
Paul Merlin
0821518fd9 Merge pull request #6 from musketyr/patch-1
ignore version called "wrapper"
2019-12-09 15:18:28 +01:00
Vladimir Orany
c8b76ea3f7 Update README.md 2019-12-09 10:46:30 +01:00
Vladimir Orany
bf26498bc4 ignore version called "wrapper"
fixes #5
2019-12-09 10:44:21 +01:00
67 changed files with 14849 additions and 3081 deletions

3
.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
dist/
lib/
node_modules/

52
.eslintrc.json Normal file
View File

@@ -0,0 +1,52 @@
{
"plugins": ["jest", "@typescript-eslint"],
"extends": ["plugin:github/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"eslint-comments/no-use": "off",
"import/no-namespace": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"camelcase": "off",
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@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", {"ignoreStringArrays": true}],
"@typescript-eslint/restrict-plus-operands": "error",
"semi": "off",
"@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error"
},
"env": {
"node": true,
"es6": true,
"jest/globals": true
}
}

View File

@@ -1,25 +1,24 @@
name: "PR Checks"
on: [pull_request, push]
# make sure the build works and doesn't produce spurious changes
name: dev
on:
pull_request:
push:
jobs:
check_pr:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: "npm ci"
run: npm ci
- name: "npm run build"
run: npm run build
- name: "npm run test"
run: npm run test
- name: "check for uncommitted changes"
- name: Checkout sources
uses: actions/checkout@v2
- name: Build
run: |
npm install
npm run all
- 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 build && npm run format'" \
|| (echo "##[error] found changed files after build. please 'npm run all'" \
"and check in all changes" \
&& exit 1)

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

@@ -0,0 +1,38 @@
# Run builds under certain failure conditions to allow the output to be manually inspected.
# These build invocations are informational only, and are expected to fail
name: failure-cases
on:
pull_request:
push:
workflow_dispatch:
env:
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
jobs:
wrapper-missing:
continue-on-error: true
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Test wrapper missing
uses: ./
with:
build-root-directory: __tests__/samples/no-wrapper
arguments: help
bad-configuration:
continue-on-error: true
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Test bad config value
uses: ./
with:
build-root-directory: __tests__/samples/no-wrapper
arguments: help
cache-disabled: yes

View File

@@ -0,0 +1,37 @@
# Make sure the action works on a clean machine without building
name: integration-testing-kotlin-dsl
on:
pull_request:
push:
workflow_dispatch:
env:
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
CACHE_DEBUG_ENABLED: true
jobs:
# Use kotlin-dsl project to verify caching of generated jars and compiled scripts
seed-build:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Build kotlin-dsl project
uses: ./
with:
build-root-directory: __tests__/samples/kotlin-dsl
arguments: test
# Check that the build can run --offline
verify-build:
needs: seed-build
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Build kotlin-dsl project
uses: ./
with:
build-root-directory: __tests__/samples/kotlin-dsl
arguments: test --offline

View File

@@ -0,0 +1,121 @@
# Verify the functionality works as expected
name: integration-testing
on:
pull_request:
push:
workflow_dispatch:
env:
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
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, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Build using Gradle wrapper
uses: ./
with:
build-root-directory: __tests__/samples/basic
arguments: test
- name: Build with configuration-cache enabled
uses: ./
with:
build-root-directory: __tests__/samples/basic
arguments: test --configuration-cache
# Tests for executing with different Gradle versions.
# Each build verifies that it is executed with the expected Gradle version.
gradle-execution:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, macos-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
cache-read-only: true
- name: Test use Gradle version alias
uses: ./
with:
gradle-version: release-candidate
build-root-directory: __tests__/samples/no-wrapper
arguments: help -DgradleVersionCheck=7.2
cache-read-only: true
- name: Test use defined Gradle executable
uses: ./
with:
gradle-executable: __tests__/samples/basic/gradlew${{ matrix.script-suffix }}
build-root-directory: __tests__/samples/no-wrapper
arguments: help -DgradleVersionCheck=7.1.1
cache-read-only: true
# 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, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Execute Gradle build with --offline
uses: ./
with:
build-root-directory: __tests__/samples/basic
arguments: test --offline
cache-read-only: true
# Test that the gradle-user-home cache will cache and restore local build-cache
build-cache:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Execute Gradle build and verify tasks from cache
uses: ./
with:
build-root-directory: __tests__/samples/basic
arguments: test -DverifyCachedBuild=true
cache-read-only: true
# Test that the project-dot-gradle cache will cache and restore configuration-cache
configuration-cache:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Execute Gradle build and verify cached configuration
uses: ./
env:
VERIFY_CACHED_CONFIGURATION: true
with:
build-root-directory: __tests__/samples/basic
arguments: test --configuration-cache
cache-read-only: true

21
.gitignore vendored
View File

@@ -1,10 +1,6 @@
lib/
# Dependency directory
node_modules
.idea
*.iml
__tests__/runner/*
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs
logs
@@ -93,3 +89,18 @@ typings/
# DynamoDB Local files
.dynamodb/
# OS metadata
.DS_Store
Thumbs.db
# Ignore built ts files
__tests__/runner/*
# lib/**/*
# IntelliJ IDEA config files
.idea/
*.iml
# ASDF tool configuration
.tool-versions

3
.prettierignore Normal file
View File

@@ -0,0 +1,3 @@
dist/
lib/
node_modules/

11
.prettierrc.json Normal file
View File

@@ -0,0 +1,11 @@
{
"printWidth": 80,
"tabWidth": 4,
"useTabs": false,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid",
"parser": "typescript"
}

124
README.md
View File

@@ -1,19 +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.
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.
This GitHub Action can be used to execute a Gradle build on any platform supported by GitHub Actions.
## Usage
The following workflow will run `gradle 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.
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 define the version of Java you need to run the build using the `actions/setup-java` action.
```yaml
# .github/workflows/gradle-build-pr.yml
name: Run Gradle on PRs
on: pull-request
on: pull_request
jobs:
gradle:
strategy:
@@ -21,11 +17,11 @@ 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
- uses: gradle/gradle-build-action@v1
with:
arguments: build
```
@@ -46,10 +42,10 @@ arguments: build -DsystemProperty=bar
See `gradle --help` for more information.
If you need to pass environment variables, simply use the Github Actions workflow syntax:
If you need to pass environment variables, simply use the GitHub Actions workflow syntax:
```yaml
- uses: eskatos/gradle-command-action@v1
- uses: gradle/gradle-build-action@v1
env:
CI: true
```
@@ -57,33 +53,33 @@ If you need to pass environment variables, simply use the Github Actions workflo
## Run a build from a different directory
```yaml
- uses: eskatos/gradle-command-action@v1
- uses: gradle/gradle-build-action@v1
with:
build-root-directory: some/subdirectory
```
## Use a specific `gradle` executable
```yaml
- uses: gradle/gradle-build-action@v1
with:
gradle-executable: path/to/gradle
```
## Use a Gradle wrapper from a different directory
```yaml
- uses: eskatos/gradle-command-action@v1
- uses: gradle/gradle-build-action@v1
with:
wrapper-directory: path/to/wrapper-directory
gradle-executable: path/to/gradlew
```
## Use a specific `gradle` executable
```yaml
- uses: eskatos/gradle-command-action@v1
with:
gradle-executable: path/to/gradle
```
## Setup and use a declared Gradle version
```yaml
- uses: eskatos/gradle-command-action@v1
- uses: gradle/gradle-build-action@v1
with:
gradle-version: 5.6.2
gradle-version: 6.5
```
`gradle-version` can be set to any valid Gradle version.
@@ -92,10 +88,11 @@ Moreover, you can use the following aliases:
| Alias | Selects |
| --- |---|
| `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:
@@ -109,19 +106,58 @@ 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@v1
with:
gradle-version: rc
gradle-version: release-candidate
arguments: build --dry-run # just test build configuration
```
# Build scans
## Caching
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`.
This action provides 3 levels of caching to help speed up your GitHub Actions:
- `distributions` caches any distributions downloaded to satisfy a `gradle-version` parameter ;
- `gradle-user-home` caches downloaded dependencies, wrapper distributions, and other stuff from the Gradle User home directory ;
- `project-dot-gradle` caches stored [configuration-cache](https://docs.gradle.org/nightly/userguide/configuration_cache.html) state, saving time configuring the build.
Each of these are enabled by default. To save caching space, you can disable any of them as follows:
```yaml
distributions-cache-enabled: true
gradle-user-home-cache-enabled: true
project-dot-gradle-cache-enabled: true
```
The distributions cache uses a cache key that is unique to the downloaded distribution. This will not change over time.
The `gradle-user-home` and `project-dot-gradle` caches compute a cache key based on the current commit and the Gradle invocation.
As such, these are likely to change on each subsequent run of GitHub actions, allowing the most recent state to always be available in the GitHub actions cache.
By default, this action aims to cache any and all reusable state that may be speed up a subsequent build invocation.
At this time it is not possible to fine-tune this caching. If you have a legitimate use case for fine-grained caching or restricting which files are cached, please raise an issue.
### Using the caches read-only
Cache storage space is limited for GitHub actions, and writing new cache entries can trigger the deletion of exising entries.
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 asfollows:
```yaml
distributions-cache-enabled: read-only
gradle-user-home-cache-enabled: read-only
project-dot-gradle-cache-enabled: read-only
```
## Build scans
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-build-action` action will emit the link to the published build scan as an output named `build-scan-url`.
You can then use that link in subsequent actions of your workflow.
@@ -130,7 +166,7 @@ For example:
```yaml
# .github/workflows/gradle-build-pr.yml
name: Run Gradle on PRs
on: pull-request
on: pull_request
jobs:
gradle:
strategy:
@@ -138,16 +174,24 @@ 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
- uses: gradle/gradle-build-action@v1
with:
arguments: build
id: gradle
- uses: example/action-that-comments-on-the-pr@v0
if: failure()
- name: "Comment build scan url"
uses: actions/github-script@v3
if: github.event_name == 'pull_request' && failure()
with:
comment: Build failed ${{ steps.gradle.outputs.build-scan-url }}
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,15 @@
import * as cacheUtils from '../src/cache-utils'
import * as path from 'path'
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')
})
})
})

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,6 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf

View File

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

View File

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

Binary file not shown.

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

185
__tests__/data/crypto-utils-test/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,10 @@
/*
* 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/6.5/userguide/multi_project_builds.html
*/
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

@@ -1,3 +0,0 @@
describe('TODO - Add a test suite', () => {
it('TODO - Add a test', async () => {});
});

View File

@@ -0,0 +1,6 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf

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

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

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/basic/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/basic/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,6 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf

View File

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

View File

@@ -0,0 +1,18 @@
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()
}

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.6.4")
}
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,6 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf

View File

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

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

@@ -1,13 +1,9 @@
name: "Gradle Command"
description: 'Execute Gradle Command Line'
author: 'Paul Merlin <paul@nospere.org>'
name: "Gradle Build Action"
description: 'Executes a Gradle build, 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
@@ -20,6 +16,20 @@ inputs:
arguments:
description: Gradle command line arguments, see gradle --help
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
# TODO: It might be useful to default to read-only for PRs, or non-main branch.
default: false
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.
required: false
default: ${{ toJSON(matrix) }}
outputs:
build-scan-url:
@@ -27,7 +37,9 @@ outputs:
runs:
using: 'node12'
main: 'lib/main.js'
main: 'dist/main/index.js'
post: 'dist/post/index.js'
post-if: success()
branding:
icon: 'box'

2
dist/main/index.js vendored Normal file

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

2
dist/post/index.js vendored Normal file

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

View File

@@ -1,11 +1,12 @@
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
moduleFileExtensions: ['js', 'ts', 'json'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}
verbose: true,
setupFilesAfterEnv: ['./jest.setup.js']
}

1
jest.setup.js Normal file
View File

@@ -0,0 +1 @@
jest.setTimeout(10000) // in milliseconds

7537
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,19 @@
{
"name": "gradle-command-action",
"name": "gradle-build-action",
"version": "1.0.0",
"description": "Execute Gradle Command Line",
"main": "lib/main.js",
"private": true,
"description": "Execute Gradle Build",
"scripts": {
"build": "tsc",
"test": "jest"
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts",
"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",
@@ -20,21 +24,29 @@
"author": "Paul Merlin <paul@nosphere.org>",
"license": "MIT",
"dependencies": {
"@actions/core": "1.1.1",
"@actions/exec": "1.0.1",
"@actions/io": "1.0.1",
"@actions/tool-cache": "1.1.1",
"string-argv": "0.3.1",
"typed-rest-client": "1.5.0",
"unzipper": "0.10.5"
"@actions/cache": "1.0.7",
"@actions/core": "1.5.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",
"string-argv": "0.3.1"
},
"devDependencies": {
"@types/jest": "24.0.18",
"@types/node": "12.7.5",
"@types/unzipper": "0.10.0",
"jest": "24.9.0",
"jest-circus": "24.9.0",
"ts-jest": "24.1.0",
"typescript": "3.6.3"
"@types/jest": "26.0.23",
"@types/node": "14.17.3",
"@types/unzipper": "0.10.4",
"@typescript-eslint/parser": "4.28.2",
"@zeit/ncc": "0.22.3",
"eslint": "7.30.0",
"eslint-plugin-github": "4.1.3",
"eslint-plugin-jest": "24.3.6",
"jest": "26.6.3",
"jest-circus": "26.6.3",
"js-yaml": "3.14.1",
"prettier": "2.3.2",
"ts-jest": "26.5.6",
"typescript": "4.3.5"
}
}

View File

@@ -0,0 +1,192 @@
import path from 'path'
import fs from 'fs'
import os from 'os'
import * as core from '@actions/core'
import * as glob from '@actions/glob'
import * as exec from '@actions/exec'
import {AbstractCache, hashStrings} from './cache-utils'
// Which paths under Gradle User Home should be cached
// TODO: This should adapt for the `GRADLE_USER_HOME` environment variable
// TODO: Allow the user to override / tweak this set
const CACHE_PATH = ['~/.gradle/caches', '~/.gradle/notifications']
const COMMON_ARTIFACT_CACHES = new Map([
['generated-gradle-jars', '~/.gradle/caches/*/generated-gradle-jars/*.jar'],
['wrapper-zips', '~/.gradle/wrapper/dists/*/*/*.zip'],
['dependency-jars', '~/.gradle/caches/modules-*/files-*/**/*.jar'],
['instrumented-jars', '~/.gradle/caches/jars-*/*/*.jar']
])
export class GradleUserHomeCache extends AbstractCache {
constructor() {
super('gradle', 'Gradle User Home')
}
async afterRestore(): Promise<void> {
await this.reportCacheEntrySize('as restored from cache')
await this.restoreCommonArtifacts()
await this.reportCacheEntrySize('after restoring common artifacts')
}
private async restoreCommonArtifacts(): Promise<void> {
const processes: Promise<void>[] = []
for (const [bundle, pattern] of COMMON_ARTIFACT_CACHES) {
const p = this.restoreCommonArtifactBundle(bundle, pattern)
// Run sequentially when debugging enabled
if (this.cacheDebuggingEnabled) {
await p
}
processes.push(p)
}
await Promise.all(processes)
}
private async restoreCommonArtifactBundle(
bundle: string,
pattern: string
): Promise<void> {
const cacheMetaFile = this.getCacheMetaFile(bundle)
if (fs.existsSync(cacheMetaFile)) {
const cacheKey = fs.readFileSync(cacheMetaFile, 'utf-8').trim()
const restoreKey = await this.restoreCache([pattern], cacheKey)
if (restoreKey) {
core.info(
`Restored ${bundle} with key ${cacheKey} to ${pattern}`
)
} else {
this.debug(
`Failed to restore ${bundle} with key ${cacheKey} to ${pattern}`
)
}
} else {
this.debug(
`No metafile found to restore ${bundle}: ${cacheMetaFile}`
)
}
}
private getCacheMetaFile(name: string): string {
return path.resolve(
this.getGradleUserHome(),
'caches',
`.gradle-build-action.${name}.cache`
)
}
private async reportCacheEntrySize(label: string): Promise<void> {
if (!this.cacheDebuggingEnabled) {
return
}
const gradleUserHome = path.resolve(os.homedir(), '.gradle')
if (!fs.existsSync(gradleUserHome)) {
return
}
const result = await exec.getExecOutput(
'du',
['-h', '-c', '-t', '5M'],
{
cwd: gradleUserHome,
silent: true,
ignoreReturnCode: true
}
)
core.info(`Gradle User Home cache entry (directories >5M): ${label}`)
core.info(
result.stdout
.trimEnd()
.replace(/\t/g, ' ')
.split('\n')
.map(it => {
return ` ${it}`
})
.join('\n')
)
core.info('-----------------------')
}
async beforeSave(): Promise<void> {
await this.saveCommonArtifacts()
}
private async saveCommonArtifacts(): Promise<void> {
const processes: Promise<void>[] = []
for (const [bundle, pattern] of COMMON_ARTIFACT_CACHES) {
const p = this.saveCommonArtifactBundle(bundle, pattern)
// Run sequentially when debugging enabled
if (this.cacheDebuggingEnabled) {
await p
}
processes.push(p)
}
await Promise.all(processes)
}
private async saveCommonArtifactBundle(
bundle: string,
pattern: string
): Promise<void> {
const cacheMetaFile = this.getCacheMetaFile(bundle)
const globber = await glob.create(pattern)
const commonArtifactFiles = await globber.glob()
// Handle no matching files
if (commonArtifactFiles.length === 0) {
this.debug(`No files found to cache for ${bundle}`)
if (fs.existsSync(cacheMetaFile)) {
fs.unlinkSync(cacheMetaFile)
}
return
}
const previouslyRestoredKey = fs.existsSync(cacheMetaFile)
? fs.readFileSync(cacheMetaFile, 'utf-8').trim()
: ''
const cacheKey = this.createCacheKey(
bundle,
hashStrings(commonArtifactFiles)
)
if (previouslyRestoredKey === cacheKey) {
this.debug(
`No change to previously restored ${bundle}. Not caching.`
)
} else {
core.info(`Caching ${bundle} with cache key: ${cacheKey}`)
await this.saveCache([pattern], cacheKey)
this.debug(`Writing cache metafile: ${cacheMetaFile}`)
fs.writeFileSync(cacheMetaFile, cacheKey)
}
for (const file of commonArtifactFiles) {
fs.unlinkSync(file)
}
}
protected createCacheKey(bundle: string, key: string): string {
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
return `${cacheKeyPrefix}${bundle}-${key}`
}
protected getGradleUserHome(): string {
return path.resolve(os.homedir(), '.gradle')
}
protected cacheOutputExists(): boolean {
// Need to check for 'caches' directory to avoid incorrect detection on MacOS agents
const dir = path.resolve(this.getGradleUserHome(), 'caches')
return fs.existsSync(dir)
}
protected getCachePath(): string[] {
return CACHE_PATH
}
}

View File

@@ -0,0 +1,30 @@
import path from 'path'
import fs from 'fs'
import {AbstractCache} from './cache-utils'
// TODO: Maybe allow the user to override / tweak this set
const PATHS_TO_CACHE = [
'configuration-cache' // Only configuration-cache is stored at present
]
export class ProjectDotGradleCache extends AbstractCache {
private rootDir: string
constructor(rootDir: string) {
super('project', 'Project .gradle directory')
this.rootDir = rootDir
}
protected cacheOutputExists(): boolean {
const dir = this.getProjectDotGradleDir()
return fs.existsSync(dir)
}
protected getCachePath(): string[] {
const dir = this.getProjectDotGradleDir()
return PATHS_TO_CACHE.map(x => path.resolve(dir, x))
}
private getProjectDotGradleDir(): string {
return path.resolve(this.rootDir, '.gradle')
}
}

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

@@ -0,0 +1,208 @@
import * as core from '@actions/core'
import * as cache from '@actions/cache'
import * as github from '@actions/github'
import * as crypto from 'crypto'
export function isCacheDisabled(): boolean {
return core.getBooleanInput('cache-disabled')
}
export function isCacheReadOnly(): boolean {
return core.getBooleanInput('cache-read-only')
}
export function isCacheDebuggingEnabled(): boolean {
return process.env['CACHE_DEBUG_ENABLED'] ? true : false
}
function generateCacheKey(cacheName: string): CacheKey {
// Prefix can be used to force change all cache keys
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
// At the most general level, share caches for all executions on the same OS
const runnerOs = process.env['RUNNER_OS'] || ''
const cacheKeyForOs = `${cacheKeyPrefix}${cacheName}|${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
])
}
function determineJobContext(): string {
// By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
const workflowJobContext = core.getInput('workflow-job-context')
return hashStrings([workflowJobContext])
}
export function hashStrings(values: string[]): string {
const hash = crypto.createHash('md5')
for (const value of values) {
hash.update(value)
}
return hash.digest('hex')
}
class CacheKey {
key: string
restoreKeys: string[]
constructor(key: string, restoreKeys: string[]) {
this.key = key
this.restoreKeys = restoreKeys
}
}
export abstract class AbstractCache {
private cacheName: string
private cacheDescription: string
private cacheKeyStateKey: string
private cacheResultStateKey: string
protected readonly cacheDebuggingEnabled: boolean
constructor(cacheName: string, cacheDescription: string) {
this.cacheName = cacheName
this.cacheDescription = cacheDescription
this.cacheKeyStateKey = `CACHE_KEY_${cacheName}`
this.cacheResultStateKey = `CACHE_RESULT_${cacheName}`
this.cacheDebuggingEnabled = isCacheDebuggingEnabled()
}
async restore(): Promise<void> {
if (this.cacheOutputExists()) {
core.info(
`${this.cacheDescription} already exists. Not restoring from cache.`
)
return
}
const cacheKey = generateCacheKey(this.cacheName)
core.saveState(this.cacheKeyStateKey, cacheKey.key)
const cacheResult = await this.restoreCache(
this.getCachePath(),
cacheKey.key,
cacheKey.restoreKeys
)
if (!cacheResult) {
core.info(
`${this.cacheDescription} cache not found. Will start with empty.`
)
return
}
core.saveState(this.cacheResultStateKey, cacheResult)
core.info(
`Restored ${this.cacheDescription} from cache key: ${cacheResult}`
)
await this.afterRestore()
return
}
protected async restoreCache(
cachePath: string[],
cacheKey: string,
cacheRestoreKeys: string[] = []
): Promise<string | undefined> {
try {
return await cache.restoreCache(
cachePath,
cacheKey,
cacheRestoreKeys
)
} catch (error) {
if (error instanceof cache.ValidationError) {
// Validation errors should fail the build action
throw error
}
// Warn about any other error and continue
core.warning(`Failed to restore ${cacheKey}: ${error}`)
return undefined
}
}
protected async afterRestore(): Promise<void> {}
async save(): Promise<void> {
if (!this.cacheOutputExists()) {
this.debug(`No ${this.cacheDescription} to cache.`)
return
}
const cacheKey = core.getState(this.cacheKeyStateKey)
const cacheResult = core.getState(this.cacheResultStateKey)
if (!cacheKey) {
this.debug(
`${this.cacheDescription} existed prior to cache restore. Not saving.`
)
return
}
if (cacheResult && cacheKey === cacheResult) {
core.info(
`Cache hit occurred on the cache key ${cacheKey}, not saving cache.`
)
return
}
await this.beforeSave()
core.info(
`Caching ${this.cacheDescription} with cache key: ${cacheKey}`
)
const cachePath = this.getCachePath()
await this.saveCache(cachePath, cacheKey)
return
}
protected async beforeSave(): Promise<void> {}
protected async saveCache(
cachePath: string[],
cacheKey: string
): Promise<void> {
try {
await cache.saveCache(cachePath, cacheKey)
} catch (error) {
if (error instanceof cache.ValidationError) {
// Validation errors should fail the build action
throw error
} else if (error instanceof cache.ReserveCacheError) {
// Reserve cache errors are expected if the artifact has been previously cached
this.debug(error.message)
} else {
// Warn about any other error and continue
core.warning(String(error))
}
}
}
protected debug(message: string): void {
if (this.cacheDebuggingEnabled) {
core.info(message)
} else {
core.debug(message)
}
}
protected abstract cacheOutputExists(): boolean
protected abstract getCachePath(): string[]
}

36
src/caches.ts Normal file
View File

@@ -0,0 +1,36 @@
import {GradleUserHomeCache} from './cache-gradle-user-home'
import {ProjectDotGradleCache} from './cache-project-dot-gradle'
import * as core from '@actions/core'
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
export async function restore(buildRootDirectory: string): Promise<void> {
if (isCacheDisabled()) {
core.debug('Cache read disabled')
return
}
await core.group('Restore Gradle state from cache', async () => {
core.saveState(BUILD_ROOT_DIR, buildRootDirectory)
return Promise.all([
new GradleUserHomeCache().restore(),
new ProjectDotGradleCache(buildRootDirectory).restore()
])
})
}
export async function save(): Promise<void> {
if (isCacheReadOnly()) {
core.debug('Cache is read-only: not saving cache entry')
return
}
await core.group('Caching Gradle state', async () => {
const buildRootDirectory = core.getState(BUILD_ROOT_DIR)
return Promise.all([
new GradleUserHomeCache().save(),
new ProjectDotGradleCache(buildRootDirectory).save()
])
})
}

View File

@@ -1,31 +1,30 @@
import * as exec from "@actions/exec";
import * as exec from '@actions/exec'
export async function execute(executable: string, root: string, argv: string[]): Promise<BuildResult> {
let publishing = false;
let buildScanUrl: string | undefined;
export async function execute(
executable: string,
root: string,
argv: string[]
): Promise<BuildResult> {
let publishing = false
let buildScanUrl: string | undefined
const status: number = await exec.exec(executable, argv, {
cwd: root,
ignoreReturnCode: true,
listeners: {
stdline: (line: string) => {
if (line.startsWith("Publishing build scan...")) {
publishing = true;
if (line.includes('Publishing build scan...')) {
publishing = true
}
if (publishing && line.length == 0) {
publishing = false
}
if (publishing && line.startsWith("http")) {
buildScanUrl = line.trim();
if (publishing && line.startsWith('http')) {
buildScanUrl = line.trim()
publishing = false
}
}
}
});
})
return new BuildResultImpl(status, buildScanUrl);
return new BuildResultImpl(status, buildScanUrl)
}
export interface BuildResult {
@@ -34,9 +33,5 @@ export interface BuildResult {
}
class BuildResultImpl implements BuildResult {
constructor(
readonly status: number,
readonly buildScanUrl?: string
) {
}
constructor(readonly status: number, readonly buildScanUrl?: string) {}
}

View File

@@ -1,11 +1,29 @@
const IS_WINDOWS = process.platform === "win32";
import * as path from 'path'
import fs from 'fs'
const IS_WINDOWS = process.platform === 'win32'
export function wrapperFilename(): string {
return IS_WINDOWS ? "gradlew.bat" : "gradlew";
export function wrapperScriptFilename(): string {
return IS_WINDOWS ? 'gradlew.bat' : 'gradlew'
}
export function installScriptFilename(): string {
return IS_WINDOWS ? "gradle.bat" : "gradle";
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,80 +1,80 @@
import * as core from "@actions/core";
import * as path from "path";
import {parseArgsStringToArgv} from "string-argv";
import * as core from '@actions/core'
import * as path from 'path'
import {parseArgsStringToArgv} from 'string-argv'
import * as execution from "./execution";
import * as gradlew from "./gradlew";
import * as provision from "./provision";
import * as caches from './caches'
import * as execution from './execution'
import * as gradlew from './gradlew'
import * as provision from './provision'
// Invoked by Github Actions
export async function run() {
// Invoked by GitHub Actions
export async function run(): Promise<void> {
try {
const workspaceDirectory = process.env[`GITHUB_WORKSPACE`] || ''
const buildRootDirectory = resolveBuildRootDirectory(workspaceDirectory)
const baseDirectory = process.env[`GITHUB_WORKSPACE`] || "";
await caches.restore(buildRootDirectory)
let result = await execution.execute(
await resolveGradleExecutable(baseDirectory),
resolveBuildRootDirectory(baseDirectory),
parseCommandLineArguments()
);
const args: string[] = parseCommandLineArguments()
// TODO: instead of running with no-daemon, run `--stop` in post action.
args.push('--no-daemon')
const result = await execution.execute(
await resolveGradleExecutable(
workspaceDirectory,
buildRootDirectory
),
buildRootDirectory,
args
)
if (result.buildScanUrl) {
core.setOutput("build-scan-url", result.buildScanUrl);
core.setOutput('build-scan-url', result.buildScanUrl)
// TODO Include context about the invocation (eg step name) in this message
// Unfortunately it doesn't seem possible to access the current step name here
core.notice(`Gradle build scan: ${result.buildScanUrl}`)
}
if (result.status != 0) {
if (result.status !== 0) {
core.setFailed(`Gradle process exited with status ${result.status}`)
}
} catch (error) {
core.setFailed(error.message);
core.setFailed(String(error))
if (error instanceof Error && error.stack) {
core.info(error.stack)
}
}
}
run()
run();
async function resolveGradleExecutable(baseDirectory: string): Promise<string> {
const gradleVersion = inputOrNull("gradle-version");
if (gradleVersion != null) {
async function resolveGradleExecutable(
workspaceDirectory: string,
buildRootDirectory: string
): Promise<string> {
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;
return path.resolve(executableDirectory, gradlew.wrapperFilename());
return gradlew.locateGradleWrapperScript(buildRootDirectory)
}
function resolveBuildRootDirectory(baseDirectory: string): string {
let buildRootDirectory = inputOrNull("build-root-directory");
return buildRootDirectory == null
? path.resolve(baseDirectory)
: path.resolve(baseDirectory, buildRootDirectory);
const buildRootDirectory = core.getInput('build-root-directory')
const resolvedBuildRootDirectory =
buildRootDirectory === ''
? path.resolve(baseDirectory)
: path.resolve(baseDirectory, buildRootDirectory)
return resolvedBuildRootDirectory
}
function parseCommandLineArguments(): string[] {
const input = inputOrNull("arguments");
return input == null ? [] : parseArgsStringToArgv(input)
}
function inputOrNull(name: string): string | null {
const inputString = core.getInput(name);
if (inputString.length == 0) {
return null;
}
return inputString
const input = core.getInput('arguments')
return parseArgsStringToArgv(input)
}

16
src/post.ts Normal file
View File

@@ -0,0 +1,16 @@
import * as core from '@actions/core'
import * as caches from './caches'
// Invoked by GitHub Actions
export async function run(): Promise<void> {
try {
await caches.save()
} catch (error) {
core.setFailed(String(error))
if (error instanceof Error && error.stack) {
core.info(error.stack)
}
}
}
run()

View File

@@ -1,163 +1,201 @@
import * as fs from "fs";
import * as path from "path";
import * as httpm from 'typed-rest-client/HttpClient';
import * as unzip from "unzipper"
import * as core from "@actions/core";
import * as io from '@actions/io';
import * as toolCache from "@actions/tool-cache";
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
import * as httpm from '@actions/http-client'
import * as core from '@actions/core'
import * as cache from '@actions/cache'
import * as toolCache from '@actions/tool-cache'
import * as gradlew from "./gradlew";
const httpc = new httpm.HttpClient("eskatos/gradle-command-action");
const gradleVersionsBaseUrl = "https://services.gradle.org/versions";
import * as gradlew from './gradlew'
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
const gradleVersionsBaseUrl = 'https://services.gradle.org/versions'
/**
* @return Gradle executable path
*/
export async function gradleVersion(gradleVersion: string): Promise<string> {
switch (gradleVersion) {
case "current":
return gradleCurrent();
case "rc":
return gradleReleaseCandidate();
case "nightly":
return gradleNightly();
case "release-nightly":
return gradleReleaseNightly();
export async function gradleVersion(version: string): Promise<string> {
switch (version) {
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()
case 'release-nightly':
return gradleReleaseNightly()
default:
return gradle(gradleVersion);
return gradle(version)
}
}
async function gradleCurrent(): Promise<string> {
const json = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/current`);
return provisionGradle(json.version, json.downloadUrl);
const versionInfo = await gradleVersionDeclaration(
`${gradleVersionsBaseUrl}/current`
)
return provisionGradle(versionInfo)
}
async function gradleReleaseCandidate(): Promise<string> {
const json = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/release-candidate`);
if (json) {
return provisionGradle(json.version, json.downloadUrl);
const versionInfo = await gradleVersionDeclaration(
`${gradleVersionsBaseUrl}/release-candidate`
)
if (versionInfo && versionInfo.version && versionInfo.downloadUrl) {
return provisionGradle(versionInfo)
}
return gradleCurrent();
core.info('No current release-candidate found, will fallback to current')
return gradleCurrent()
}
async function gradleNightly(): Promise<string> {
const json = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/nightly`);
return provisionGradle(json.version, json.downloadUrl);
const versionInfo = await gradleVersionDeclaration(
`${gradleVersionsBaseUrl}/nightly`
)
return provisionGradle(versionInfo)
}
async function gradleReleaseNightly(): Promise<string> {
const json = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/release-nightly`);
return provisionGradle(json.version, json.downloadUrl);
const versionInfo = await gradleVersionDeclaration(
`${gradleVersionsBaseUrl}/release-nightly`
)
return provisionGradle(versionInfo)
}
async function gradle(version: string): Promise<string> {
const declaration = await findGradleVersionDeclaration(version);
if (!declaration) {
throw new Error(`Gradle version ${version} does not exists`);
const versionInfo = await findGradleVersionDeclaration(version)
if (!versionInfo) {
throw new Error(`Gradle version ${version} does not exists`)
}
return provisionGradle(declaration.version, declaration.downloadUrl);
return provisionGradle(versionInfo)
}
async function gradleVersionDeclaration(url: string): Promise<any | undefined> {
const json: any = await httpGetJson(url);
return (json.version && json.version.length > 0) ? json : undefined
async function gradleVersionDeclaration(
url: string
): Promise<GradleVersionInfo> {
return await httpGetGradleVersion(url)
}
async function findGradleVersionDeclaration(version: string): Promise<any | undefined> {
const json: any = await httpGetJson(`${gradleVersionsBaseUrl}/all`);
const found: any = json.find((entry: any) => {
return entry.version === version;
});
return found ? found : undefined
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(
versionInfo: GradleVersionInfo
): Promise<string> {
return core.group(`Provision Gradle ${versionInfo.version}`, async () => {
return locateGradleAndDownloadIfRequired(versionInfo)
})
}
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 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);
const downloadPath = await downloadAndCacheGradleDistribution(versionInfo)
await toolCache.extractZip(downloadPath, installsDir)
core.info(`Extracted Gradle ${versionInfo.version} to ${installDir}`)
core.info(`Downloading ${url}`);
const executable = executableFrom(installDir)
fs.chmodSync(executable, '755')
core.info(`Provisioned Gradle executable ${executable}`)
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 executable = executableFrom(installDir);
fs.chmodSync(executable, "755");
core.info(`Provisioned Gradle executable ${executable}`);
toolCache.cacheDir(installDir, "gradle", version);
return executable;
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}`
const restoreKey = await cache.restoreCache([downloadPath], cacheKey)
if (restoreKey) {
core.info(
`Restored Gradle distribution ${cacheKey} from cache to ${downloadPath}`
)
return downloadPath
}
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) {
// Fail on validation errors or non-errors (the latter to keep Typescript happy)
if (
error instanceof cache.ValidationError ||
!(error instanceof Error)
) {
throw error
}
core.warning(error.message)
}
}
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()}`);
return path.join(installDir, 'bin', `${gradlew.installScriptFilename()}`)
}
async function httpGetJson(url: string): Promise<any> {
const response = await httpc.get(url);
const body = await response.readBody();
return JSON.parse(body);
async function httpGetGradleVersion(url: string): Promise<GradleVersionInfo> {
return JSON.parse(await httpGetString(url))
}
async function httpDownload(url: string, path: string): Promise<void> {
return new Promise<void>(function (resolve, reject) {
const writeStream = fs.createWriteStream(path);
httpc.get(url).then(response => {
response.message.pipe(writeStream)
.on("close", () => {
resolve();
})
.on("error", err => {
reject(err)
});
}).catch(reason => {
reject(reason);
});
});
async function httpGetGradleVersions(
url: string
): Promise<GradleVersionInfo[]> {
return JSON.parse(await httpGetString(url))
}
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)
});
});
async function httpGetString(url: string): Promise<string> {
const httpClient = new httpm.HttpClient('gradle/gradle-build-action')
const response = await httpClient.get(url)
return response.readBody()
}
interface GradleVersionInfo {
version: string
downloadUrl: string
}

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
/* Basic Options */
"incremental": true, /* Enable incremental compilation */
"incremental": false, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "allowJs": true, /* Allow javascript files to be compiled. */