mirror of
https://github.com/gradle/gradle-build-action.git
synced 2024-11-26 18:21:05 -05:00
Merge pull request #131 from gradle/cache-jdks
- Cache downloaded Java Toolchains - Treat configuration-cache entries as extracted entries
This commit is contained in:
commit
f804351b51
23 changed files with 1037 additions and 588 deletions
24
.github/workflows/integTest-caching-config.yml
vendored
24
.github/workflows/integTest-caching-config.yml
vendored
|
@ -22,21 +22,13 @@ jobs:
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
# Add "wrapper" to main cache entry and remove 'wrapper-zips' cache entry
|
# Add "enterprise" to main cache entry but omit "notifications"
|
||||||
# Exclude build-cache from main cache entry
|
|
||||||
gradle-home-cache-includes: |
|
gradle-home-cache-includes: |
|
||||||
caches
|
caches
|
||||||
notifications
|
enterprise
|
||||||
wrapper
|
# Exclude build-cache from main cache entry
|
||||||
gradle-home-cache-excludes: |
|
gradle-home-cache-excludes: |
|
||||||
caches/build-cache-1
|
caches/build-cache-1
|
||||||
gradle-home-extracted-cache-entries: |
|
|
||||||
[
|
|
||||||
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
|
|
||||||
["dependencies", "caches/modules-*/files-*/*/*/*/*/"],
|
|
||||||
["instrumented-jars", "caches/jars-*/*/"],
|
|
||||||
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
|
|
||||||
]
|
|
||||||
- name: Build using Gradle wrapper
|
- name: Build using Gradle wrapper
|
||||||
working-directory: __tests__/samples/groovy-dsl
|
working-directory: __tests__/samples/groovy-dsl
|
||||||
run: ./gradlew test
|
run: ./gradlew test
|
||||||
|
@ -57,17 +49,9 @@ jobs:
|
||||||
# Use the same configuration when restoring state from cache
|
# Use the same configuration when restoring state from cache
|
||||||
gradle-home-cache-includes: |
|
gradle-home-cache-includes: |
|
||||||
caches
|
caches
|
||||||
notifications
|
enterprise
|
||||||
wrapper
|
|
||||||
gradle-home-cache-excludes: |
|
gradle-home-cache-excludes: |
|
||||||
caches/build-cache-1
|
caches/build-cache-1
|
||||||
gradle-home-extracted-cache-entries: |
|
|
||||||
[
|
|
||||||
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
|
|
||||||
["dependencies", "caches/modules-*/files-*/*/*/*/*/"],
|
|
||||||
["instrumented-jars", "caches/jars-*/*/"],
|
|
||||||
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
|
|
||||||
]
|
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
- name: Execute Gradle build with --offline
|
- name: Execute Gradle build with --offline
|
||||||
working-directory: __tests__/samples/groovy-dsl
|
working-directory: __tests__/samples/groovy-dsl
|
||||||
|
|
|
@ -7,13 +7,14 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
|
||||||
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Run initial Gradle builds to push initial cache entries
|
# 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.
|
# These builds should start fresh without cache hits, due to the seed injected into the cache key above.
|
||||||
seed-build:
|
seed-build-groovy:
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-groovy-
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
@ -26,12 +27,11 @@ jobs:
|
||||||
- name: Groovy build with configuration-cache enabled
|
- name: Groovy build with configuration-cache enabled
|
||||||
working-directory: __tests__/samples/groovy-dsl
|
working-directory: __tests__/samples/groovy-dsl
|
||||||
run: ./gradlew test --configuration-cache
|
run: ./gradlew test --configuration-cache
|
||||||
- name: Kotlin build with configuration-cache enabled
|
|
||||||
working-directory: __tests__/samples/kotlin-dsl
|
|
||||||
run: ./gradlew test --configuration-cache
|
|
||||||
|
|
||||||
configuration-cache-groovy:
|
configuration-cache-groovy:
|
||||||
needs: seed-build
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-groovy-
|
||||||
|
needs: seed-build-groovy
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
@ -49,29 +49,11 @@ jobs:
|
||||||
working-directory: __tests__/samples/groovy-dsl
|
working-directory: __tests__/samples/groovy-dsl
|
||||||
run: ./gradlew test --configuration-cache
|
run: ./gradlew test --configuration-cache
|
||||||
|
|
||||||
# Test restore configuration-cache from the second build invocation
|
|
||||||
configuration-cache-kotlin:
|
|
||||||
needs: seed-build
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, windows-latest]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Setup Gradle
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
cache-read-only: true
|
|
||||||
- name: Execute Gradle build and verify cached configuration
|
|
||||||
env:
|
|
||||||
VERIFY_CACHED_CONFIGURATION: true
|
|
||||||
working-directory: __tests__/samples/kotlin-dsl
|
|
||||||
run: ./gradlew test --configuration-cache
|
|
||||||
|
|
||||||
# Check that the build can run when no extracted cache entries are restored
|
# Check that the build can run when no extracted cache entries are restored
|
||||||
no-extracted-cache-entries-restored:
|
no-extracted-cache-entries-restored:
|
||||||
needs: seed-build
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-groovy-
|
||||||
|
needs: seed-build-groovy
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
@ -79,11 +61,67 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Gradle with no cache extracted cache entries restored
|
- name: Setup Gradle with no extracted cache entries restored
|
||||||
uses: ./
|
uses: ./
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl"
|
||||||
with:
|
with:
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
gradle-home-extracted-cache-entries: '[]'
|
|
||||||
- name: Check execute Gradle build with configuration cache enabled (but not restored)
|
- name: Check execute Gradle build with configuration cache enabled (but not restored)
|
||||||
working-directory: __tests__/samples/groovy-dsl
|
working-directory: __tests__/samples/groovy-dsl
|
||||||
run: ./gradlew test --configuration-cache
|
run: ./gradlew test --configuration-cache
|
||||||
|
|
||||||
|
seed-build-kotlin:
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-kotlin-
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
- name: Kotlin build with configuration-cache enabled
|
||||||
|
working-directory: __tests__/samples/kotlin-dsl
|
||||||
|
run: ./gradlew help --configuration-cache
|
||||||
|
|
||||||
|
modify-build-kotlin:
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-kotlin-
|
||||||
|
needs: seed-build-kotlin
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
- name: Kotlin build with configuration-cache enabled
|
||||||
|
working-directory: __tests__/samples/kotlin-dsl
|
||||||
|
run: ./gradlew test --configuration-cache
|
||||||
|
|
||||||
|
# Test restore configuration-cache from the third build invocation
|
||||||
|
configuration-cache-kotlin:
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-kotlin-
|
||||||
|
needs: modify-build-kotlin
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
cache-read-only: true
|
||||||
|
- name: Execute Gradle build and verify cached configuration
|
||||||
|
env:
|
||||||
|
VERIFY_CACHED_CONFIGURATION: true
|
||||||
|
working-directory: __tests__/samples/kotlin-dsl
|
||||||
|
run: ./gradlew test --configuration-cache
|
||||||
|
|
|
@ -72,9 +72,10 @@ jobs:
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Gradle with no extracted cache entries restored
|
- name: Setup Gradle with no extracted cache entries restored
|
||||||
uses: ./
|
uses: ./
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl"
|
||||||
with:
|
with:
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
gradle-home-extracted-cache-entries: '[]'
|
|
||||||
- name: Check executee Gradle build
|
- name: Check executee Gradle build
|
||||||
working-directory: __tests__/samples/groovy-dsl
|
working-directory: __tests__/samples/groovy-dsl
|
||||||
run: ./gradlew test
|
run: ./gradlew test
|
||||||
|
|
43
.github/workflows/integTest-caching-java-toolchain.yml
vendored
Normal file
43
.github/workflows/integTest-caching-java-toolchain.yml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
name: Test save/restore java toolchains
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [assigned, review_requested]
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
seed-build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
- name: Build using Gradle wrapper
|
||||||
|
working-directory: __tests__/samples/java-toolchain
|
||||||
|
run: ./gradlew test --info
|
||||||
|
|
||||||
|
# Test that the gradle-user-home cache will cache the toolchain, by running build with --offline
|
||||||
|
toolchain-cache:
|
||||||
|
needs: seed-build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
cache-read-only: true
|
||||||
|
- name: Execute Gradle build with --offline
|
||||||
|
working-directory: __tests__/samples/java-toolchain
|
||||||
|
run: ./gradlew test --info --offline
|
17
__tests__/samples/java-toolchain/build.gradle
Normal file
17
__tests__/samples/java-toolchain/build.gradle
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation('junit:junit:4.12')
|
||||||
|
}
|
1
__tests__/samples/java-toolchain/gradle.properties
Normal file
1
__tests__/samples/java-toolchain/gradle.properties
Normal file
|
@ -0,0 +1 @@
|
||||||
|
org.gradle.caching=true
|
BIN
__tests__/samples/java-toolchain/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
__tests__/samples/java-toolchain/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
__tests__/samples/java-toolchain/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
__tests__/samples/java-toolchain/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
185
__tests__/samples/java-toolchain/gradlew
vendored
Executable file
185
__tests__/samples/java-toolchain/gradlew
vendored
Executable 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/java-toolchain/gradlew.bat
vendored
Normal file
104
__tests__/samples/java-toolchain/gradlew.bat
vendored
Normal 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
|
1
__tests__/samples/java-toolchain/settings.gradle
Normal file
1
__tests__/samples/java-toolchain/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = 'basic'
|
|
@ -0,0 +1,10 @@
|
||||||
|
package basic;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class BasicTest {
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
assert true;
|
||||||
|
}
|
||||||
|
}
|
12
action.yml
12
action.yml
|
@ -55,17 +55,6 @@ inputs:
|
||||||
description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users (INTERNAL).
|
description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users (INTERNAL).
|
||||||
required: false
|
required: false
|
||||||
default: ${{ toJSON(matrix) }}
|
default: ${{ toJSON(matrix) }}
|
||||||
gradle-home-extracted-cache-entries:
|
|
||||||
description: Names and patterns of artifacts in Gradle User Home to cache separately. (EXPERIMENTAL - may be changed/removed without notice)
|
|
||||||
required: false
|
|
||||||
default: |
|
|
||||||
[
|
|
||||||
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
|
|
||||||
["wrapper-zips", "wrapper/dists/*/*/*.zip"],
|
|
||||||
["dependencies", "caches/modules-*/files-*/*/*/*/*/"],
|
|
||||||
["instrumented-jars", "caches/jars-*/*/"],
|
|
||||||
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
|
|
||||||
]
|
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
build-scan-url:
|
build-scan-url:
|
||||||
|
@ -75,7 +64,6 @@ runs:
|
||||||
using: 'node12'
|
using: 'node12'
|
||||||
main: 'dist/main/index.js'
|
main: 'dist/main/index.js'
|
||||||
post: 'dist/post/index.js'
|
post: 'dist/post/index.js'
|
||||||
post-if: success()
|
|
||||||
|
|
||||||
branding:
|
branding:
|
||||||
icon: 'box'
|
icon: 'box'
|
||||||
|
|
2
dist/main/index.js
vendored
2
dist/main/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/main/index.js.map
vendored
2
dist/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/post/index.js
vendored
2
dist/post/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/post/index.js.map
vendored
2
dist/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
|
@ -1,15 +1,26 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as cache from '@actions/cache'
|
import * as exec from '@actions/exec'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import {CacheListener} from './cache-reporting'
|
import {CacheListener} from './cache-reporting'
|
||||||
import {isCacheDebuggingEnabled, getCacheKeyPrefix, determineJobContext, handleCacheFailure} from './cache-utils'
|
import {
|
||||||
|
getCacheKeyPrefix,
|
||||||
|
determineJobContext,
|
||||||
|
saveCache,
|
||||||
|
restoreCache,
|
||||||
|
cacheDebug,
|
||||||
|
isCacheDebuggingEnabled,
|
||||||
|
tryDelete
|
||||||
|
} from './cache-utils'
|
||||||
|
import {ConfigurationCacheEntryExtractor, GradleHomeEntryExtractor} from './cache-extract-entries'
|
||||||
|
|
||||||
const CACHE_PROTOCOL_VERSION = 'v5-'
|
const CACHE_PROTOCOL_VERSION = 'v5-'
|
||||||
|
|
||||||
export const META_FILE_DIR = '.gradle-build-action'
|
export const META_FILE_DIR = '.gradle-build-action'
|
||||||
export const PROJECT_ROOTS_FILE = 'project-roots.txt'
|
export const PROJECT_ROOTS_FILE = 'project-roots.txt'
|
||||||
|
const INCLUDE_PATHS_PARAMETER = 'gradle-home-cache-includes'
|
||||||
|
const EXCLUDE_PATHS_PARAMETER = 'gradle-home-cache-excludes'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a key used to restore a cache entry.
|
* Represents a key used to restore a cache entry.
|
||||||
|
@ -62,22 +73,20 @@ function generateCacheKey(cacheName: string): CacheKey {
|
||||||
return new CacheKey(cacheKey, [cacheKeyForJobContext, cacheKeyForJob, cacheKeyForOs])
|
return new CacheKey(cacheKey, [cacheKeyForJobContext, cacheKeyForJob, cacheKeyForOs])
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class AbstractCache {
|
export class GradleStateCache {
|
||||||
private cacheName: string
|
private cacheName: string
|
||||||
private cacheDescription: string
|
private cacheDescription: string
|
||||||
private cacheKeyStateKey: string
|
private cacheKeyStateKey: string
|
||||||
private cacheResultStateKey: string
|
private cacheResultStateKey: string
|
||||||
|
|
||||||
protected readonly gradleUserHome: string
|
protected readonly gradleUserHome: string
|
||||||
protected readonly cacheDebuggingEnabled: boolean
|
|
||||||
|
|
||||||
constructor(gradleUserHome: string, cacheName: string, cacheDescription: string) {
|
constructor(gradleUserHome: string) {
|
||||||
this.gradleUserHome = gradleUserHome
|
this.gradleUserHome = gradleUserHome
|
||||||
this.cacheName = cacheName
|
this.cacheName = 'gradle'
|
||||||
this.cacheDescription = cacheDescription
|
this.cacheDescription = 'Gradle User Home'
|
||||||
this.cacheKeyStateKey = `CACHE_KEY_${cacheName}`
|
this.cacheKeyStateKey = `CACHE_KEY_gradle`
|
||||||
this.cacheResultStateKey = `CACHE_RESULT_${cacheName}`
|
this.cacheResultStateKey = `CACHE_RESULT_gradle`
|
||||||
this.cacheDebuggingEnabled = isCacheDebuggingEnabled()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(): void {
|
init(): void {
|
||||||
|
@ -96,15 +105,16 @@ export abstract class AbstractCache {
|
||||||
async restore(listener: CacheListener): Promise<void> {
|
async restore(listener: CacheListener): Promise<void> {
|
||||||
const entryListener = listener.entry(this.cacheDescription)
|
const entryListener = listener.entry(this.cacheDescription)
|
||||||
|
|
||||||
const cacheKey = this.prepareCacheKey()
|
const cacheKey = generateCacheKey(this.cacheName)
|
||||||
|
core.saveState(this.cacheKeyStateKey, cacheKey.key)
|
||||||
|
|
||||||
this.debug(
|
cacheDebug(
|
||||||
`Requesting ${this.cacheDescription} with
|
`Requesting ${this.cacheDescription} with
|
||||||
key:${cacheKey.key}
|
key:${cacheKey.key}
|
||||||
restoreKeys:[${cacheKey.restoreKeys}]`
|
restoreKeys:[${cacheKey.restoreKeys}]`
|
||||||
)
|
)
|
||||||
|
|
||||||
const cacheResult = await this.restoreCache(this.getCachePath(), cacheKey.key, cacheKey.restoreKeys)
|
const cacheResult = await restoreCache(this.getCachePath(), cacheKey.key, cacheKey.restoreKeys)
|
||||||
entryListener.markRequested(cacheKey.key, cacheKey.restoreKeys)
|
entryListener.markRequested(cacheKey.key, cacheKey.restoreKeys)
|
||||||
|
|
||||||
if (!cacheResult) {
|
if (!cacheResult) {
|
||||||
|
@ -124,27 +134,16 @@ export abstract class AbstractCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareCacheKey(): CacheKey {
|
/**
|
||||||
const cacheKey = generateCacheKey(this.cacheName)
|
* Restore any extracted cache entries after the main Gradle User Home entry is restored.
|
||||||
core.saveState(this.cacheKeyStateKey, cacheKey.key)
|
*/
|
||||||
return cacheKey
|
async afterRestore(listener: CacheListener): Promise<void> {
|
||||||
|
await this.debugReportGradleUserHomeSize('as restored from cache')
|
||||||
|
await new GradleHomeEntryExtractor(this.gradleUserHome).restore(listener)
|
||||||
|
await new ConfigurationCacheEntryExtractor(this.gradleUserHome).restore(listener)
|
||||||
|
await this.debugReportGradleUserHomeSize('after restoring common artifacts')
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async restoreCache(
|
|
||||||
cachePath: string[],
|
|
||||||
cacheKey: string,
|
|
||||||
cacheRestoreKeys: string[] = []
|
|
||||||
): Promise<cache.CacheEntry | undefined> {
|
|
||||||
try {
|
|
||||||
return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys)
|
|
||||||
} catch (error) {
|
|
||||||
handleCacheFailure(error, `Failed to restore ${cacheKey}`)
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async afterRestore(_listener: CacheListener): Promise<void> {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the cache entry based on the current cache key unless the cache was restored with the exact key,
|
* Saves the cache entry based on the current cache key unless the cache was restored with the exact key,
|
||||||
* in which case we cannot overwrite it.
|
* in which case we cannot overwrite it.
|
||||||
|
@ -171,7 +170,7 @@ export abstract class AbstractCache {
|
||||||
|
|
||||||
core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKeyFromRestore}`)
|
core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKeyFromRestore}`)
|
||||||
const cachePath = this.getCachePath()
|
const cachePath = this.getCachePath()
|
||||||
const savedEntry = await this.saveCache(cachePath, cacheKeyFromRestore)
|
const savedEntry = await saveCache(cachePath, cacheKeyFromRestore)
|
||||||
|
|
||||||
if (savedEntry) {
|
if (savedEntry) {
|
||||||
listener.entry(this.cacheDescription).markSaved(savedEntry.key, savedEntry.size)
|
listener.entry(this.cacheDescription).markSaved(savedEntry.key, savedEntry.size)
|
||||||
|
@ -180,25 +179,155 @@ export abstract class AbstractCache {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async beforeSave(_listener: CacheListener): Promise<void> {}
|
/**
|
||||||
|
* Extract and save any defined extracted cache entries prior to the main Gradle User Home entry being saved.
|
||||||
protected async saveCache(cachePath: string[], cacheKey: string): Promise<cache.CacheEntry | undefined> {
|
*/
|
||||||
try {
|
async beforeSave(listener: CacheListener): Promise<void> {
|
||||||
return await cache.saveCache(cachePath, cacheKey)
|
await this.debugReportGradleUserHomeSize('before saving common artifacts')
|
||||||
} catch (error) {
|
this.deleteExcludedPaths()
|
||||||
handleCacheFailure(error, `Failed to save cache entry ${cacheKey}`)
|
await Promise.all([
|
||||||
}
|
new GradleHomeEntryExtractor(this.gradleUserHome).extract(listener),
|
||||||
return undefined
|
new ConfigurationCacheEntryExtractor(this.gradleUserHome).extract(listener)
|
||||||
|
])
|
||||||
|
await this.debugReportGradleUserHomeSize(
|
||||||
|
"after extracting common artifacts (only 'caches' and 'notifications' will be stored)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected debug(message: string): void {
|
/**
|
||||||
if (this.cacheDebuggingEnabled) {
|
* Delete any file paths that are excluded by the `gradle-home-cache-excludes` parameter.
|
||||||
core.info(message)
|
*/
|
||||||
} else {
|
private deleteExcludedPaths(): void {
|
||||||
core.debug(message)
|
const rawPaths: string[] = core.getMultilineInput(EXCLUDE_PATHS_PARAMETER)
|
||||||
|
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
|
||||||
|
|
||||||
|
for (const p of resolvedPaths) {
|
||||||
|
cacheDebug(`Deleting excluded path: ${p}`)
|
||||||
|
tryDelete(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract getCachePath(): string[]
|
/**
|
||||||
protected abstract initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void
|
* Determines the paths within Gradle User Home to cache.
|
||||||
|
* By default, this is the 'caches' and 'notifications' directories,
|
||||||
|
* but this can be overridden by the `gradle-home-cache-includes` parameter.
|
||||||
|
*/
|
||||||
|
protected getCachePath(): string[] {
|
||||||
|
const rawPaths: string[] = core.getMultilineInput(INCLUDE_PATHS_PARAMETER)
|
||||||
|
rawPaths.push(META_FILE_DIR)
|
||||||
|
const resolvedPaths = rawPaths.map(x => this.resolveCachePath(x))
|
||||||
|
cacheDebug(`Using cache paths: ${resolvedPaths}`)
|
||||||
|
return resolvedPaths
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveCachePath(rawPath: string): string {
|
||||||
|
if (rawPath.startsWith('!')) {
|
||||||
|
const resolved = this.resolveCachePath(rawPath.substring(1))
|
||||||
|
return `!${resolved}`
|
||||||
|
}
|
||||||
|
return path.resolve(this.gradleUserHome, rawPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void {
|
||||||
|
const propertiesFile = path.resolve(gradleUserHome, 'gradle.properties')
|
||||||
|
fs.writeFileSync(propertiesFile, 'org.gradle.daemon=false')
|
||||||
|
|
||||||
|
const buildScanCapture = path.resolve(initScriptsDir, 'build-scan-capture.init.gradle')
|
||||||
|
fs.writeFileSync(
|
||||||
|
buildScanCapture,
|
||||||
|
`import org.gradle.util.GradleVersion
|
||||||
|
|
||||||
|
// Only run again root build. Do not run against included builds.
|
||||||
|
def isTopLevelBuild = gradle.getParent() == null
|
||||||
|
if (isTopLevelBuild) {
|
||||||
|
def version = GradleVersion.current().baseVersion
|
||||||
|
def atLeastGradle4 = version >= GradleVersion.version("4.0")
|
||||||
|
def atLeastGradle6 = version >= GradleVersion.version("6.0")
|
||||||
|
|
||||||
|
if (atLeastGradle6) {
|
||||||
|
settingsEvaluated { settings ->
|
||||||
|
if (settings.pluginManager.hasPlugin("com.gradle.enterprise")) {
|
||||||
|
registerCallbacks(settings.extensions["gradleEnterprise"].buildScan, settings.rootProject.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (atLeastGradle4) {
|
||||||
|
projectsEvaluated { gradle ->
|
||||||
|
if (gradle.rootProject.pluginManager.hasPlugin("com.gradle.build-scan")) {
|
||||||
|
registerCallbacks(gradle.rootProject.extensions["buildScan"], gradle.rootProject.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerCallbacks(buildScanExtension, rootProjectName) {
|
||||||
|
buildScanExtension.with {
|
||||||
|
def buildOutcome = ""
|
||||||
|
def scanFile = new File("gradle-build-scan.txt")
|
||||||
|
|
||||||
|
buildFinished { result ->
|
||||||
|
buildOutcome = result.failure == null ? " succeeded" : " failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildScanPublished { buildScan ->
|
||||||
|
scanFile.text = buildScan.buildScanUri
|
||||||
|
|
||||||
|
// Send commands directly to GitHub Actions via STDOUT.
|
||||||
|
def message = "Build '\${rootProjectName}'\${buildOutcome} - \${buildScan.buildScanUri}"
|
||||||
|
println("::notice ::\${message}")
|
||||||
|
println("::set-output name=build-scan-url::\${buildScan.buildScanUri}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
const projectRootCapture = path.resolve(initScriptsDir, 'project-root-capture.init.gradle')
|
||||||
|
fs.writeFileSync(
|
||||||
|
projectRootCapture,
|
||||||
|
`
|
||||||
|
// Only run again root build. Do not run against included builds.
|
||||||
|
def isTopLevelBuild = gradle.getParent() == null
|
||||||
|
if (isTopLevelBuild) {
|
||||||
|
settingsEvaluated { settings ->
|
||||||
|
def projectRootEntry = settings.rootDir.absolutePath + "\\n"
|
||||||
|
def projectRootList = new File(settings.gradle.gradleUserHomeDir, "${PROJECT_ROOTS_FILE}")
|
||||||
|
if (!projectRootList.exists() || !projectRootList.text.contains(projectRootEntry)) {
|
||||||
|
projectRootList << projectRootEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When cache debugging is enabled, this method will give a detailed report
|
||||||
|
* of the Gradle User Home contents.
|
||||||
|
*/
|
||||||
|
private async debugReportGradleUserHomeSize(label: string): Promise<void> {
|
||||||
|
if (!isCacheDebuggingEnabled()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(this.gradleUserHome)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const result = await exec.getExecOutput('du', ['-h', '-c', '-t', '5M'], {
|
||||||
|
cwd: this.gradleUserHome,
|
||||||
|
silent: true,
|
||||||
|
ignoreReturnCode: true
|
||||||
|
})
|
||||||
|
|
||||||
|
core.info(`Gradle User Home (directories >5M): ${label}`)
|
||||||
|
|
||||||
|
core.info(
|
||||||
|
result.stdout
|
||||||
|
.trimEnd()
|
||||||
|
.replace(/\t/g, ' ')
|
||||||
|
.split('\n')
|
||||||
|
.map(it => {
|
||||||
|
return ` ${it}`
|
||||||
|
})
|
||||||
|
.join('\n')
|
||||||
|
)
|
||||||
|
|
||||||
|
core.info('-----------------------')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
376
src/cache-extract-entries.ts
Normal file
376
src/cache-extract-entries.ts
Normal file
|
@ -0,0 +1,376 @@
|
||||||
|
import path from 'path'
|
||||||
|
import fs from 'fs'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import * as glob from '@actions/glob'
|
||||||
|
|
||||||
|
import {META_FILE_DIR, PROJECT_ROOTS_FILE} from './cache-base'
|
||||||
|
import {CacheEntryListener, CacheListener} from './cache-reporting'
|
||||||
|
import {
|
||||||
|
cacheDebug,
|
||||||
|
getCacheKeyPrefix,
|
||||||
|
hashFileNames,
|
||||||
|
isCacheDebuggingEnabled,
|
||||||
|
restoreCache,
|
||||||
|
saveCache,
|
||||||
|
tryDelete
|
||||||
|
} from './cache-utils'
|
||||||
|
|
||||||
|
const SKIP_RESTORE_VAR = 'GRADLE_BUILD_ACTION_SKIP_RESTORE'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of attempting to load or store an extracted cache entry.
|
||||||
|
* An undefined cacheKey indicates that the operation did not succeed.
|
||||||
|
* The collected results are then used to populate the `cache-metadata.json` file for later use.
|
||||||
|
*/
|
||||||
|
class ExtractedCacheEntry {
|
||||||
|
artifactType: string
|
||||||
|
pattern: string
|
||||||
|
cacheKey: string | undefined
|
||||||
|
|
||||||
|
constructor(artifactType: string, pattern: string, cacheKey: string | undefined) {
|
||||||
|
this.artifactType = artifactType
|
||||||
|
this.pattern = pattern
|
||||||
|
this.cacheKey = cacheKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of all of the extracted cache entries for this Gradle User Home.
|
||||||
|
* This object is persisted to JSON file in the Gradle User Home directory for storing,
|
||||||
|
* and subsequently used to restore the Gradle User Home.
|
||||||
|
*/
|
||||||
|
class ExtractedCacheEntryMetadata {
|
||||||
|
entries: ExtractedCacheEntry[] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The specification for a type of extracted cache entry.
|
||||||
|
*/
|
||||||
|
class ExtractedCacheEntryDefinition {
|
||||||
|
artifactType: string
|
||||||
|
pattern: string
|
||||||
|
bundle: boolean
|
||||||
|
uniqueFileNames = true
|
||||||
|
|
||||||
|
constructor(artifactType: string, pattern: string, bundle: boolean) {
|
||||||
|
this.artifactType = artifactType
|
||||||
|
this.pattern = pattern
|
||||||
|
this.bundle = bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
withNonUniqueFileNames(): ExtractedCacheEntryDefinition {
|
||||||
|
this.uniqueFileNames = false
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches and restores the entire Gradle User Home directory, extracting entries containing common artifacts
|
||||||
|
* for more efficient storage.
|
||||||
|
*/
|
||||||
|
abstract class AbstractEntryExtractor {
|
||||||
|
protected readonly gradleUserHome: string
|
||||||
|
private extractorName: string
|
||||||
|
|
||||||
|
constructor(gradleUserHome: string, extractorName: string) {
|
||||||
|
this.gradleUserHome = gradleUserHome
|
||||||
|
this.extractorName = extractorName
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores any artifacts that were cached separately, based on the information in the `cache-metadata.json` file.
|
||||||
|
* Each extracted cache entry is restored in parallel, except when debugging is enabled.
|
||||||
|
*/
|
||||||
|
async restore(listener: CacheListener): Promise<void> {
|
||||||
|
const previouslyExtractedCacheEntries = this.loadExtractedCacheEntries()
|
||||||
|
|
||||||
|
const processes: Promise<ExtractedCacheEntry>[] = []
|
||||||
|
|
||||||
|
for (const cacheEntry of previouslyExtractedCacheEntries) {
|
||||||
|
const artifactType = cacheEntry.artifactType
|
||||||
|
const entryListener = listener.entry(cacheEntry.pattern)
|
||||||
|
|
||||||
|
// Handle case where the extracted-cache-entry definitions have been changed
|
||||||
|
const skipRestore = process.env[SKIP_RESTORE_VAR] || ''
|
||||||
|
if (skipRestore.includes(artifactType)) {
|
||||||
|
core.info(`Not restoring extracted cache entry for ${artifactType}`)
|
||||||
|
entryListener.markRequested('SKIP_RESTORE')
|
||||||
|
} else {
|
||||||
|
processes.push(
|
||||||
|
this.awaitForDebugging(
|
||||||
|
this.restoreExtractedCacheEntry(
|
||||||
|
artifactType,
|
||||||
|
cacheEntry.cacheKey!,
|
||||||
|
cacheEntry.pattern,
|
||||||
|
entryListener
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveMetadataForCacheResults(await Promise.all(processes))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async restoreExtractedCacheEntry(
|
||||||
|
artifactType: string,
|
||||||
|
cacheKey: string,
|
||||||
|
pattern: string,
|
||||||
|
listener: CacheEntryListener
|
||||||
|
): Promise<ExtractedCacheEntry> {
|
||||||
|
listener.markRequested(cacheKey)
|
||||||
|
|
||||||
|
const restoredEntry = await restoreCache([pattern], cacheKey)
|
||||||
|
if (restoredEntry) {
|
||||||
|
core.info(`Restored ${artifactType} with key ${cacheKey} to ${pattern}`)
|
||||||
|
listener.markRestored(restoredEntry.key, restoredEntry.size)
|
||||||
|
return new ExtractedCacheEntry(artifactType, pattern, cacheKey)
|
||||||
|
} else {
|
||||||
|
core.info(`Did not restore ${artifactType} with key ${cacheKey} to ${pattern}`)
|
||||||
|
return new ExtractedCacheEntry(artifactType, pattern, undefined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves any artifacts that are configured to be cached separately, based on the extracted cache entry definitions.
|
||||||
|
* Each entry is extracted and saved in parallel, except when debugging is enabled.
|
||||||
|
*/
|
||||||
|
async extract(listener: CacheListener): Promise<void> {
|
||||||
|
// Load the cache entry definitions (from config) and the previously restored entries (from persisted metadata file)
|
||||||
|
const cacheEntryDefinitions = this.getExtractedCacheEntryDefinitions()
|
||||||
|
cacheDebug(
|
||||||
|
`Extracting cache entries for ${this.extractorName}: ${JSON.stringify(cacheEntryDefinitions, null, 2)}`
|
||||||
|
)
|
||||||
|
|
||||||
|
const previouslyRestoredEntries = this.loadExtractedCacheEntries()
|
||||||
|
const cacheActions: Promise<ExtractedCacheEntry>[] = []
|
||||||
|
|
||||||
|
// For each cache entry definition, determine if it has already been restored, and if not, extract it
|
||||||
|
for (const cacheEntryDefinition of cacheEntryDefinitions) {
|
||||||
|
const artifactType = cacheEntryDefinition.artifactType
|
||||||
|
const pattern = cacheEntryDefinition.pattern
|
||||||
|
|
||||||
|
// Find all matching files for this cache entry definition
|
||||||
|
const globber = await glob.create(pattern, {
|
||||||
|
implicitDescendants: false,
|
||||||
|
followSymbolicLinks: false
|
||||||
|
})
|
||||||
|
const matchingFiles = await globber.glob()
|
||||||
|
|
||||||
|
if (matchingFiles.length === 0) {
|
||||||
|
cacheDebug(`No files found to cache for ${artifactType}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheEntryDefinition.bundle) {
|
||||||
|
// For an extracted "bundle", use the defined pattern and cache all matching files in a single entry.
|
||||||
|
cacheActions.push(
|
||||||
|
this.awaitForDebugging(
|
||||||
|
this.saveExtractedCacheEntry(
|
||||||
|
matchingFiles,
|
||||||
|
artifactType,
|
||||||
|
pattern,
|
||||||
|
cacheEntryDefinition.uniqueFileNames,
|
||||||
|
previouslyRestoredEntries,
|
||||||
|
listener.entry(pattern)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Otherwise cache each matching file in a separate entry, using the complete file path as the cache pattern.
|
||||||
|
for (const cacheFile of matchingFiles) {
|
||||||
|
cacheActions.push(
|
||||||
|
this.awaitForDebugging(
|
||||||
|
this.saveExtractedCacheEntry(
|
||||||
|
[cacheFile],
|
||||||
|
artifactType,
|
||||||
|
cacheFile,
|
||||||
|
cacheEntryDefinition.uniqueFileNames,
|
||||||
|
previouslyRestoredEntries,
|
||||||
|
listener.entry(cacheFile)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveMetadataForCacheResults(await Promise.all(cacheActions))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveExtractedCacheEntry(
|
||||||
|
matchingFiles: string[],
|
||||||
|
artifactType: string,
|
||||||
|
pattern: string,
|
||||||
|
uniqueFileNames: boolean,
|
||||||
|
previouslyRestoredEntries: ExtractedCacheEntry[],
|
||||||
|
entryListener: CacheEntryListener
|
||||||
|
): Promise<ExtractedCacheEntry> {
|
||||||
|
const cacheKey = uniqueFileNames
|
||||||
|
? this.createCacheKeyFromFileNames(artifactType, matchingFiles)
|
||||||
|
: await this.createCacheKeyFromFileContents(artifactType, pattern)
|
||||||
|
const previouslyRestoredKey = previouslyRestoredEntries.find(
|
||||||
|
x => x.artifactType === artifactType && x.pattern === pattern
|
||||||
|
)?.cacheKey
|
||||||
|
|
||||||
|
if (previouslyRestoredKey === cacheKey) {
|
||||||
|
cacheDebug(`No change to previously restored ${artifactType}. Not saving.`)
|
||||||
|
} else {
|
||||||
|
core.info(`Caching ${artifactType} with path '${pattern}' and cache key: ${cacheKey}`)
|
||||||
|
const savedEntry = await saveCache([pattern], cacheKey)
|
||||||
|
if (savedEntry !== undefined) {
|
||||||
|
entryListener.markSaved(savedEntry.key, savedEntry.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of matchingFiles) {
|
||||||
|
tryDelete(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExtractedCacheEntry(artifactType, pattern, cacheKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createCacheKeyFromFileNames(artifactType: string, files: string[]): string {
|
||||||
|
const cacheKeyPrefix = getCacheKeyPrefix()
|
||||||
|
const relativeFiles = files.map(x => path.relative(this.gradleUserHome, x))
|
||||||
|
const key = hashFileNames(relativeFiles)
|
||||||
|
|
||||||
|
cacheDebug(`Generating cache key for ${artifactType} from file names: ${relativeFiles}`)
|
||||||
|
|
||||||
|
return `${cacheKeyPrefix}${artifactType}-${key}`
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async createCacheKeyFromFileContents(artifactType: string, pattern: string): Promise<string> {
|
||||||
|
const cacheKeyPrefix = getCacheKeyPrefix()
|
||||||
|
const key = await glob.hashFiles(pattern)
|
||||||
|
|
||||||
|
cacheDebug(`Generating cache key for ${artifactType} from files matching: ${pattern}`)
|
||||||
|
|
||||||
|
return `${cacheKeyPrefix}${artifactType}-${key}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run actions sequentially if debugging is enabled
|
||||||
|
private async awaitForDebugging(p: Promise<ExtractedCacheEntry>): Promise<ExtractedCacheEntry> {
|
||||||
|
if (isCacheDebuggingEnabled()) {
|
||||||
|
await p
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load information about the extracted cache entries previously restored/saved. This is loaded from the 'cache-metadata.json' file.
|
||||||
|
*/
|
||||||
|
protected loadExtractedCacheEntries(): ExtractedCacheEntry[] {
|
||||||
|
const cacheMetadataFile = this.getCacheMetadataFile()
|
||||||
|
if (!fs.existsSync(cacheMetadataFile)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const filedata = fs.readFileSync(cacheMetadataFile, 'utf-8')
|
||||||
|
cacheDebug(`Loaded cache metadata: ${filedata}`)
|
||||||
|
const extractedCacheEntryMetadata = JSON.parse(filedata) as ExtractedCacheEntryMetadata
|
||||||
|
return extractedCacheEntryMetadata.entries
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves information about the extracted cache entries into the 'cache-metadata.json' file.
|
||||||
|
*/
|
||||||
|
private saveMetadataForCacheResults(results: ExtractedCacheEntry[]): void {
|
||||||
|
const extractedCacheEntryMetadata = new ExtractedCacheEntryMetadata()
|
||||||
|
extractedCacheEntryMetadata.entries = results.filter(x => x.cacheKey !== undefined)
|
||||||
|
|
||||||
|
const filedata = JSON.stringify(extractedCacheEntryMetadata)
|
||||||
|
cacheDebug(`Saving cache metadata: ${filedata}`)
|
||||||
|
|
||||||
|
fs.writeFileSync(this.getCacheMetadataFile(), filedata, 'utf-8')
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCacheMetadataFile(): string {
|
||||||
|
const actionMetadataDirectory = path.resolve(this.gradleUserHome, META_FILE_DIR)
|
||||||
|
fs.mkdirSync(actionMetadataDirectory, {recursive: true})
|
||||||
|
|
||||||
|
return path.resolve(actionMetadataDirectory, `${this.extractorName}-entry-metadata.json`)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GradleHomeEntryExtractor extends AbstractEntryExtractor {
|
||||||
|
constructor(gradleUserHome: string) {
|
||||||
|
super(gradleUserHome, 'gradle-home')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the extracted cache entry definitions, which determine which artifacts will be cached
|
||||||
|
* separately from the rest of the Gradle User Home cache entry.
|
||||||
|
*/
|
||||||
|
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
|
||||||
|
const entryDefinition = (
|
||||||
|
artifactType: string,
|
||||||
|
patterns: string[],
|
||||||
|
bundle: boolean
|
||||||
|
): ExtractedCacheEntryDefinition => {
|
||||||
|
const resolvedPattern = patterns.map(x => path.resolve(this.gradleUserHome, x)).join('\n')
|
||||||
|
return new ExtractedCacheEntryDefinition(artifactType, resolvedPattern, bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
entryDefinition('generated-gradle-jars', ['caches/*/generated-gradle-jars/*.jar'], false),
|
||||||
|
entryDefinition('wrapper-zips', ['wrapper/dists/*/*/*.zip'], false),
|
||||||
|
entryDefinition('java-toolchains', ['jdks/*.zip', 'jdks/*.tar.gz'], false),
|
||||||
|
entryDefinition('dependencies', ['caches/modules-*/files-*/*/*/*/*'], true),
|
||||||
|
entryDefinition('instrumented-jars', ['caches/jars-*/*'], true),
|
||||||
|
entryDefinition('kotlin-dsl', ['caches/*/kotlin-dsl/*/*'], true)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
|
||||||
|
constructor(gradleUserHome: string) {
|
||||||
|
super(gradleUserHome, 'configuration-cache')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the case where Gradle User Home has not been fully restored, so that the configuration-cache
|
||||||
|
* entry is not reusable.
|
||||||
|
*/
|
||||||
|
async restore(listener: CacheListener): Promise<void> {
|
||||||
|
if (listener.fullyRestored) {
|
||||||
|
return super.restore(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info('Not restoring configuration-cache state, as Gradle User Home was not fully restored')
|
||||||
|
for (const cacheEntry of this.loadExtractedCacheEntries()) {
|
||||||
|
listener.entry(cacheEntry.pattern).markRequested('NOT_RESTORED')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract cache entries for the configuration cache in each project.
|
||||||
|
*/
|
||||||
|
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
|
||||||
|
return this.getProjectRoots().map(projectRoot => {
|
||||||
|
const configCachePath = path.resolve(projectRoot, '.gradle/configuration-cache')
|
||||||
|
return new ExtractedCacheEntryDefinition(
|
||||||
|
'configuration-cache',
|
||||||
|
configCachePath,
|
||||||
|
true
|
||||||
|
).withNonUniqueFileNames()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For every Gradle invocation, we record the project root directory. This method returns the entire
|
||||||
|
* set of project roots, to allow saving of configuration-cache entries for each.
|
||||||
|
*/
|
||||||
|
private getProjectRoots(): string[] {
|
||||||
|
const projectList = path.resolve(this.gradleUserHome, PROJECT_ROOTS_FILE)
|
||||||
|
if (!fs.existsSync(projectList)) {
|
||||||
|
core.info(`Missing project list file ${projectList}`)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const projectRoots = fs.readFileSync(projectList, 'utf-8')
|
||||||
|
core.info(`Found project roots '${projectRoots}' in ${projectList}`)
|
||||||
|
return projectRoots.trim().split('\n')
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,395 +0,0 @@
|
||||||
import path from 'path'
|
|
||||||
import fs from 'fs'
|
|
||||||
import * as core from '@actions/core'
|
|
||||||
import * as glob from '@actions/glob'
|
|
||||||
import * as exec from '@actions/exec'
|
|
||||||
|
|
||||||
import {AbstractCache, META_FILE_DIR} from './cache-base'
|
|
||||||
import {CacheEntryListener, CacheListener} from './cache-reporting'
|
|
||||||
import {getCacheKeyPrefix, hashFileNames, tryDelete} from './cache-utils'
|
|
||||||
|
|
||||||
const META_FILE = 'cache-metadata.json'
|
|
||||||
|
|
||||||
const INCLUDE_PATHS_PARAMETER = 'gradle-home-cache-includes'
|
|
||||||
const EXCLUDE_PATHS_PARAMETER = 'gradle-home-cache-excludes'
|
|
||||||
const EXTRACTED_CACHE_ENTRIES_PARAMETER = 'gradle-home-extracted-cache-entries'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the result of attempting to load or store an extracted cache entry.
|
|
||||||
* An undefined cacheKey indicates that the operation did not succeed.
|
|
||||||
* The collected results are then used to populate the `cache-metadata.json` file for later use.
|
|
||||||
*/
|
|
||||||
class ExtractedCacheEntry {
|
|
||||||
artifactType: string
|
|
||||||
pattern: string
|
|
||||||
cacheKey: string | undefined
|
|
||||||
|
|
||||||
constructor(artifactType: string, pattern: string, cacheKey: string | undefined) {
|
|
||||||
this.artifactType = artifactType
|
|
||||||
this.pattern = pattern
|
|
||||||
this.cacheKey = cacheKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Representation of all of the extracted cache entries for this Gradle User Home.
|
|
||||||
* This object is persisted to JSON file in the Gradle User Home directory for storing,
|
|
||||||
* and subsequently used to restore the Gradle User Home.
|
|
||||||
*/
|
|
||||||
class ExtractedCacheEntryMetadata {
|
|
||||||
entries: ExtractedCacheEntry[] = []
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Caches and restores the entire Gradle User Home directory, extracting entries containing common artifacts
|
|
||||||
* for more efficient storage.
|
|
||||||
*/
|
|
||||||
export class GradleUserHomeCache extends AbstractCache {
|
|
||||||
constructor(gradleUserHome: string) {
|
|
||||||
super(gradleUserHome, 'gradle', 'Gradle User Home')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore any extracted cache entries after the main Gradle User Home entry is restored.
|
|
||||||
*/
|
|
||||||
async afterRestore(listener: CacheListener): Promise<void> {
|
|
||||||
await this.debugReportGradleUserHomeSize('as restored from cache')
|
|
||||||
await this.restoreExtractedCacheEntries(listener)
|
|
||||||
await this.debugReportGradleUserHomeSize('after restoring common artifacts')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores any artifacts that were cached separately, based on the information in the `cache-metadata.json` file.
|
|
||||||
* Each extracted cache entry is restored in parallel, except when debugging is enabled.
|
|
||||||
*/
|
|
||||||
private async restoreExtractedCacheEntries(listener: CacheListener): Promise<void> {
|
|
||||||
const extractedCacheEntryDefinitions = this.getExtractedCacheEntryDefinitions()
|
|
||||||
const previouslyExtractedCacheEntries = this.loadExtractedCacheEntries()
|
|
||||||
|
|
||||||
const processes: Promise<ExtractedCacheEntry>[] = []
|
|
||||||
|
|
||||||
for (const cacheEntry of previouslyExtractedCacheEntries) {
|
|
||||||
const artifactType = cacheEntry.artifactType
|
|
||||||
const entryListener = listener.entry(cacheEntry.pattern)
|
|
||||||
|
|
||||||
// Handle case where the extracted-cache-entry definitions have been changed
|
|
||||||
if (extractedCacheEntryDefinitions.get(artifactType) === undefined) {
|
|
||||||
core.info(`Found extracted cache entry for ${artifactType} but no such entry defined`)
|
|
||||||
entryListener.markRequested('EXTRACTED_ENTRY_NOT_DEFINED')
|
|
||||||
} else {
|
|
||||||
processes.push(
|
|
||||||
this.restoreExtractedCacheEntry(
|
|
||||||
artifactType,
|
|
||||||
cacheEntry.cacheKey!,
|
|
||||||
cacheEntry.pattern,
|
|
||||||
entryListener
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.saveMetadataForCacheResults(await this.collectCacheResults(processes))
|
|
||||||
}
|
|
||||||
|
|
||||||
private async restoreExtractedCacheEntry(
|
|
||||||
artifactType: string,
|
|
||||||
cacheKey: string,
|
|
||||||
pattern: string,
|
|
||||||
listener: CacheEntryListener
|
|
||||||
): Promise<ExtractedCacheEntry> {
|
|
||||||
listener.markRequested(cacheKey)
|
|
||||||
|
|
||||||
const restoredEntry = await this.restoreCache([pattern], cacheKey)
|
|
||||||
if (restoredEntry) {
|
|
||||||
core.info(`Restored ${artifactType} with key ${cacheKey} to ${pattern}`)
|
|
||||||
listener.markRestored(restoredEntry.key, restoredEntry.size)
|
|
||||||
return new ExtractedCacheEntry(artifactType, pattern, cacheKey)
|
|
||||||
} else {
|
|
||||||
core.info(`Did not restore ${artifactType} with key ${cacheKey} to ${pattern}`)
|
|
||||||
return new ExtractedCacheEntry(artifactType, pattern, undefined)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract and save any defined extracted cache entries prior to the main Gradle User Home entry being saved.
|
|
||||||
*/
|
|
||||||
async beforeSave(listener: CacheListener): Promise<void> {
|
|
||||||
await this.debugReportGradleUserHomeSize('before saving common artifacts')
|
|
||||||
this.removeExcludedPaths()
|
|
||||||
await this.saveExtractedCacheEntries(listener)
|
|
||||||
await this.debugReportGradleUserHomeSize(
|
|
||||||
"after saving common artifacts (only 'caches' and 'notifications' will be stored)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete any file paths that are excluded by the `gradle-home-cache-excludes` parameter.
|
|
||||||
*/
|
|
||||||
private removeExcludedPaths(): void {
|
|
||||||
const rawPaths: string[] = core.getMultilineInput(EXCLUDE_PATHS_PARAMETER)
|
|
||||||
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
|
|
||||||
|
|
||||||
for (const p of resolvedPaths) {
|
|
||||||
this.debug(`Deleting excluded path: ${p}`)
|
|
||||||
tryDelete(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves any artifacts that are configured to be cached separately, based on the extracted cache entry definitions.
|
|
||||||
* These definitions are normally fixed, but can be overridden by the `gradle-home-extracted-cache-entries` parameter.
|
|
||||||
* Each entry is extracted and saved in parallel, except when debugging is enabled.
|
|
||||||
*/
|
|
||||||
private async saveExtractedCacheEntries(listener: CacheListener): Promise<void> {
|
|
||||||
// Load the cache entry definitions (from config) and the previously restored entries (from filesystem)
|
|
||||||
const cacheEntryDefinitions = this.getExtractedCacheEntryDefinitions()
|
|
||||||
const previouslyRestoredEntries = this.loadExtractedCacheEntries()
|
|
||||||
const cacheActions: Promise<ExtractedCacheEntry>[] = []
|
|
||||||
|
|
||||||
for (const [artifactType, pattern] of cacheEntryDefinitions) {
|
|
||||||
// Find all matching files for this cache entry definition
|
|
||||||
const globber = await glob.create(pattern, {
|
|
||||||
implicitDescendants: false,
|
|
||||||
followSymbolicLinks: false
|
|
||||||
})
|
|
||||||
const matchingFiles = await globber.glob()
|
|
||||||
|
|
||||||
if (matchingFiles.length === 0) {
|
|
||||||
this.debug(`No files found to cache for ${artifactType}`)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isBundlePattern(pattern)) {
|
|
||||||
// For an extracted "bundle", use the defined pattern and cache all matching files in a single entry.
|
|
||||||
cacheActions.push(
|
|
||||||
this.saveExtractedCacheEntry(
|
|
||||||
matchingFiles,
|
|
||||||
artifactType,
|
|
||||||
pattern,
|
|
||||||
previouslyRestoredEntries,
|
|
||||||
listener.entry(pattern)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Otherwise cache each matching file in a separate entry, using the complete file path as the cache pattern.
|
|
||||||
for (const cacheFile of matchingFiles) {
|
|
||||||
cacheActions.push(
|
|
||||||
this.saveExtractedCacheEntry(
|
|
||||||
[cacheFile],
|
|
||||||
artifactType,
|
|
||||||
cacheFile,
|
|
||||||
previouslyRestoredEntries,
|
|
||||||
listener.entry(cacheFile)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.saveMetadataForCacheResults(await this.collectCacheResults(cacheActions))
|
|
||||||
}
|
|
||||||
|
|
||||||
private async saveExtractedCacheEntry(
|
|
||||||
matchingFiles: string[],
|
|
||||||
artifactType: string,
|
|
||||||
pattern: string,
|
|
||||||
previouslyRestoredEntries: ExtractedCacheEntry[],
|
|
||||||
entryListener: CacheEntryListener
|
|
||||||
): Promise<ExtractedCacheEntry> {
|
|
||||||
const cacheKey = this.createCacheKeyForArtifacts(artifactType, matchingFiles)
|
|
||||||
const previouslyRestoredKey = previouslyRestoredEntries.find(
|
|
||||||
x => x.artifactType === artifactType && x.pattern === pattern
|
|
||||||
)?.cacheKey
|
|
||||||
|
|
||||||
if (previouslyRestoredKey === cacheKey) {
|
|
||||||
this.debug(`No change to previously restored ${artifactType}. Not saving.`)
|
|
||||||
} else {
|
|
||||||
core.info(`Caching ${artifactType} with path '${pattern}' and cache key: ${cacheKey}`)
|
|
||||||
const savedEntry = await this.saveCache([pattern], cacheKey)
|
|
||||||
if (savedEntry !== undefined) {
|
|
||||||
entryListener.markSaved(savedEntry.key, savedEntry.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const file of matchingFiles) {
|
|
||||||
tryDelete(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ExtractedCacheEntry(artifactType, pattern, cacheKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createCacheKeyForArtifacts(artifactType: string, files: string[]): string {
|
|
||||||
const cacheKeyPrefix = getCacheKeyPrefix()
|
|
||||||
const relativeFiles = files.map(x => path.relative(this.gradleUserHome, x))
|
|
||||||
const key = hashFileNames(relativeFiles)
|
|
||||||
|
|
||||||
this.debug(`Generating cache key for ${artifactType} from files: ${relativeFiles}`)
|
|
||||||
|
|
||||||
return `${cacheKeyPrefix}${artifactType}-${key}`
|
|
||||||
}
|
|
||||||
|
|
||||||
private isBundlePattern(pattern: string): boolean {
|
|
||||||
return pattern.endsWith('*')
|
|
||||||
}
|
|
||||||
|
|
||||||
private async collectCacheResults(processes: Promise<ExtractedCacheEntry>[]): Promise<ExtractedCacheEntry[]> {
|
|
||||||
// Run cache actions sequentially when debugging enabled
|
|
||||||
if (this.cacheDebuggingEnabled) {
|
|
||||||
for (const p of processes) {
|
|
||||||
await p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(processes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load information about the extracted cache entries previously restored/saved. This is loaded from the 'cache-metadata.json' file.
|
|
||||||
*/
|
|
||||||
private loadExtractedCacheEntries(): ExtractedCacheEntry[] {
|
|
||||||
const cacheMetadataFile = path.resolve(this.gradleUserHome, META_FILE_DIR, META_FILE)
|
|
||||||
if (!fs.existsSync(cacheMetadataFile)) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const filedata = fs.readFileSync(cacheMetadataFile, 'utf-8')
|
|
||||||
core.debug(`Loaded cache metadata: ${filedata}`)
|
|
||||||
const extractedCacheEntryMetadata = JSON.parse(filedata) as ExtractedCacheEntryMetadata
|
|
||||||
return extractedCacheEntryMetadata.entries
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves information about the extracted cache entries into the 'cache-metadata.json' file.
|
|
||||||
*/
|
|
||||||
private saveMetadataForCacheResults(results: ExtractedCacheEntry[]): void {
|
|
||||||
const extractedCacheEntryMetadata = new ExtractedCacheEntryMetadata()
|
|
||||||
extractedCacheEntryMetadata.entries = results.filter(x => x.cacheKey !== undefined)
|
|
||||||
|
|
||||||
const filedata = JSON.stringify(extractedCacheEntryMetadata)
|
|
||||||
core.debug(`Saving cache metadata: ${filedata}`)
|
|
||||||
|
|
||||||
const actionMetadataDirectory = path.resolve(this.gradleUserHome, META_FILE_DIR)
|
|
||||||
const cacheMetadataFile = path.resolve(actionMetadataDirectory, META_FILE)
|
|
||||||
|
|
||||||
fs.mkdirSync(actionMetadataDirectory, {recursive: true})
|
|
||||||
fs.writeFileSync(cacheMetadataFile, filedata, 'utf-8')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the paths within Gradle User Home to cache.
|
|
||||||
* By default, this is the 'caches' and 'notifications' directories,
|
|
||||||
* but this can be overridden by the `gradle-home-cache-includes` parameter.
|
|
||||||
*/
|
|
||||||
protected getCachePath(): string[] {
|
|
||||||
const rawPaths: string[] = core.getMultilineInput(INCLUDE_PATHS_PARAMETER)
|
|
||||||
rawPaths.push(META_FILE_DIR)
|
|
||||||
const resolvedPaths = rawPaths.map(x => this.resolveCachePath(x))
|
|
||||||
this.debug(`Using cache paths: ${resolvedPaths}`)
|
|
||||||
return resolvedPaths
|
|
||||||
}
|
|
||||||
|
|
||||||
private resolveCachePath(rawPath: string): string {
|
|
||||||
if (rawPath.startsWith('!')) {
|
|
||||||
const resolved = this.resolveCachePath(rawPath.substring(1))
|
|
||||||
return `!${resolved}`
|
|
||||||
}
|
|
||||||
return path.resolve(this.gradleUserHome, rawPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the extracted cache entry definitions, which determine which artifacts will be cached
|
|
||||||
* separately from the rest of the Gradle User Home cache entry.
|
|
||||||
* This is normally a fixed set, but can be overridden by the `gradle-home-extracted-cache-entries` parameter.
|
|
||||||
*/
|
|
||||||
private getExtractedCacheEntryDefinitions(): Map<string, string> {
|
|
||||||
const rawDefinitions = core.getInput(EXTRACTED_CACHE_ENTRIES_PARAMETER)
|
|
||||||
const parsedDefinitions = JSON.parse(rawDefinitions)
|
|
||||||
return new Map(Array.from(parsedDefinitions, ([key, value]) => [key, path.resolve(this.gradleUserHome, value)]))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When cache debugging is enabled, this method will give a detailed report
|
|
||||||
* of the Gradle User Home contents.
|
|
||||||
*/
|
|
||||||
private async debugReportGradleUserHomeSize(label: string): Promise<void> {
|
|
||||||
if (!this.cacheDebuggingEnabled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!fs.existsSync(this.gradleUserHome)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const result = await exec.getExecOutput('du', ['-h', '-c', '-t', '5M'], {
|
|
||||||
cwd: this.gradleUserHome,
|
|
||||||
silent: true,
|
|
||||||
ignoreReturnCode: true
|
|
||||||
})
|
|
||||||
|
|
||||||
core.info(`Gradle User Home (directories >5M): ${label}`)
|
|
||||||
|
|
||||||
core.info(
|
|
||||||
result.stdout
|
|
||||||
.trimEnd()
|
|
||||||
.replace(/\t/g, ' ')
|
|
||||||
.split('\n')
|
|
||||||
.map(it => {
|
|
||||||
return ` ${it}`
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
)
|
|
||||||
|
|
||||||
core.info('-----------------------')
|
|
||||||
}
|
|
||||||
|
|
||||||
protected initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void {
|
|
||||||
const propertiesFile = path.resolve(gradleUserHome, 'gradle.properties')
|
|
||||||
fs.writeFileSync(propertiesFile, 'org.gradle.daemon=false')
|
|
||||||
|
|
||||||
const buildScanCapture = path.resolve(initScriptsDir, 'build-scan-capture.init.gradle')
|
|
||||||
fs.writeFileSync(
|
|
||||||
buildScanCapture,
|
|
||||||
`import org.gradle.util.GradleVersion
|
|
||||||
|
|
||||||
// Only run again root build. Do not run against included builds.
|
|
||||||
def isTopLevelBuild = gradle.getParent() == null
|
|
||||||
if (isTopLevelBuild) {
|
|
||||||
def version = GradleVersion.current().baseVersion
|
|
||||||
def atLeastGradle4 = version >= GradleVersion.version("4.0")
|
|
||||||
def atLeastGradle6 = version >= GradleVersion.version("6.0")
|
|
||||||
|
|
||||||
if (atLeastGradle6) {
|
|
||||||
settingsEvaluated { settings ->
|
|
||||||
if (settings.pluginManager.hasPlugin("com.gradle.enterprise")) {
|
|
||||||
registerCallbacks(settings.extensions["gradleEnterprise"].buildScan, settings.rootProject.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (atLeastGradle4) {
|
|
||||||
projectsEvaluated { gradle ->
|
|
||||||
if (gradle.rootProject.pluginManager.hasPlugin("com.gradle.build-scan")) {
|
|
||||||
registerCallbacks(gradle.rootProject.extensions["buildScan"], gradle.rootProject.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def registerCallbacks(buildScanExtension, rootProjectName) {
|
|
||||||
buildScanExtension.with {
|
|
||||||
def buildOutcome = ""
|
|
||||||
def scanFile = new File("gradle-build-scan.txt")
|
|
||||||
|
|
||||||
buildFinished { result ->
|
|
||||||
buildOutcome = result.failure == null ? " succeeded" : " failed"
|
|
||||||
}
|
|
||||||
|
|
||||||
buildScanPublished { buildScan ->
|
|
||||||
scanFile.text = buildScan.buildScanUri
|
|
||||||
|
|
||||||
// Send commands directly to GitHub Actions via STDOUT.
|
|
||||||
def message = "Build '\${rootProjectName}'\${buildOutcome} - \${buildScan.buildScanUri}"
|
|
||||||
println("::notice ::\${message}")
|
|
||||||
println("::set-output name=build-scan-url::\${buildScan.buildScanUri}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
import * as core from '@actions/core'
|
|
||||||
import path from 'path'
|
|
||||||
import fs from 'fs'
|
|
||||||
import {AbstractCache, META_FILE_DIR, PROJECT_ROOTS_FILE} from './cache-base'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple cache that saves and restores the '.gradle/configuration-cache' directory in the project root.
|
|
||||||
*/
|
|
||||||
export class ProjectDotGradleCache extends AbstractCache {
|
|
||||||
constructor(gradleUserHome: string) {
|
|
||||||
super(gradleUserHome, 'project', 'Project configuration cache')
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getCachePath(): string[] {
|
|
||||||
return this.getProjectRoots().map(x => path.resolve(x, '.gradle/configuration-cache'))
|
|
||||||
}
|
|
||||||
|
|
||||||
protected initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void {
|
|
||||||
const projectRootCapture = path.resolve(initScriptsDir, 'project-root-capture.init.gradle')
|
|
||||||
fs.writeFileSync(
|
|
||||||
projectRootCapture,
|
|
||||||
`
|
|
||||||
// Only run again root build. Do not run against included builds.
|
|
||||||
def isTopLevelBuild = gradle.getParent() == null
|
|
||||||
if (isTopLevelBuild) {
|
|
||||||
settingsEvaluated { settings ->
|
|
||||||
def projectRootEntry = settings.rootDir.absolutePath + "\\n"
|
|
||||||
def projectRootList = new File(settings.gradle.gradleUserHomeDir, "${META_FILE_DIR}/${PROJECT_ROOTS_FILE}")
|
|
||||||
println "Adding " + projectRootEntry + " to " + projectRootList
|
|
||||||
if (!projectRootList.exists() || !projectRootList.text.contains(projectRootEntry)) {
|
|
||||||
projectRootList << projectRootEntry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For every Gradle invocation, we record the project root directory. This method returns the entire
|
|
||||||
* set of project roots, to allow saving of configuration-cache entries for each.
|
|
||||||
*/
|
|
||||||
private getProjectRoots(): string[] {
|
|
||||||
const projectList = path.resolve(this.gradleUserHome, META_FILE_DIR, PROJECT_ROOTS_FILE)
|
|
||||||
if (!fs.existsSync(projectList)) {
|
|
||||||
core.info(`Missing project list file ${projectList}`)
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
const projectRoots = fs.readFileSync(projectList, 'utf-8')
|
|
||||||
core.info(`Found project roots '${projectRoots}' in ${projectList}`)
|
|
||||||
return projectRoots.trim().split('\n')
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -46,6 +46,36 @@ export function hashStrings(values: string[]): string {
|
||||||
return hash.digest('hex')
|
return hash.digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function restoreCache(
|
||||||
|
cachePath: string[],
|
||||||
|
cacheKey: string,
|
||||||
|
cacheRestoreKeys: string[] = []
|
||||||
|
): Promise<cache.CacheEntry | undefined> {
|
||||||
|
try {
|
||||||
|
return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys)
|
||||||
|
} catch (error) {
|
||||||
|
handleCacheFailure(error, `Failed to restore ${cacheKey}`)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveCache(cachePath: string[], cacheKey: string): Promise<cache.CacheEntry | undefined> {
|
||||||
|
try {
|
||||||
|
return await cache.saveCache(cachePath, cacheKey)
|
||||||
|
} catch (error) {
|
||||||
|
handleCacheFailure(error, `Failed to save cache entry ${cacheKey}`)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cacheDebug(message: string): void {
|
||||||
|
if (isCacheDebuggingEnabled()) {
|
||||||
|
core.info(message)
|
||||||
|
} else {
|
||||||
|
core.debug(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function handleCacheFailure(error: unknown, message: string): void {
|
export function handleCacheFailure(error: unknown, message: string): void {
|
||||||
if (error instanceof cache.ValidationError) {
|
if (error instanceof cache.ValidationError) {
|
||||||
// Fail on cache validation errors
|
// Fail on cache validation errors
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {GradleUserHomeCache} from './cache-gradle-user-home'
|
|
||||||
import {ProjectDotGradleCache} from './cache-project-dot-gradle'
|
|
||||||
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
||||||
import {logCachingReport, CacheListener} from './cache-reporting'
|
import {logCachingReport, CacheListener} from './cache-reporting'
|
||||||
|
import {GradleStateCache} from './cache-base'
|
||||||
|
|
||||||
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
|
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
|
||||||
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
||||||
|
@ -13,29 +12,22 @@ export async function restore(gradleUserHome: string): Promise<void> {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const gradleUserHomeCache = new GradleUserHomeCache(gradleUserHome)
|
const gradleStateCache = new GradleStateCache(gradleUserHome)
|
||||||
gradleUserHomeCache.init()
|
gradleStateCache.init()
|
||||||
|
|
||||||
const projectDotGradleCache = new ProjectDotGradleCache(gradleUserHome)
|
|
||||||
projectDotGradleCache.init()
|
|
||||||
|
|
||||||
await core.group('Restore Gradle state from cache', async () => {
|
await core.group('Restore Gradle state from cache', async () => {
|
||||||
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
||||||
|
|
||||||
const cacheListener = new CacheListener()
|
const cacheListener = new CacheListener()
|
||||||
await gradleUserHomeCache.restore(cacheListener)
|
await gradleStateCache.restore(cacheListener)
|
||||||
|
|
||||||
if (cacheListener.fullyRestored) {
|
|
||||||
// Only restore the configuration-cache if the Gradle Home is fully restored
|
|
||||||
await projectDotGradleCache.restore(cacheListener)
|
|
||||||
} else {
|
|
||||||
// Otherwise, prepare the cache key for later save()
|
|
||||||
core.info('Gradle Home cache not fully restored: not restoring configuration-cache state')
|
|
||||||
projectDotGradleCache.prepareCacheKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Export var that is detected in all later restore steps
|
||||||
|
core.exportVariable(CACHE_RESTORED_VAR, true)
|
||||||
|
// Export state that is detected in corresponding post-action step
|
||||||
|
core.saveState(CACHE_RESTORED_VAR, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(): Promise<void> {
|
export async function save(): Promise<void> {
|
||||||
|
@ -53,10 +45,7 @@ export async function save(): Promise<void> {
|
||||||
|
|
||||||
await core.group('Caching Gradle state', async () => {
|
await core.group('Caching Gradle state', async () => {
|
||||||
const gradleUserHome = core.getState(GRADLE_USER_HOME)
|
const gradleUserHome = core.getState(GRADLE_USER_HOME)
|
||||||
return Promise.all([
|
return new GradleStateCache(gradleUserHome).save(cacheListener)
|
||||||
new GradleUserHomeCache(gradleUserHome).save(cacheListener),
|
|
||||||
new ProjectDotGradleCache(gradleUserHome).save(cacheListener)
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
logCachingReport(cacheListener)
|
logCachingReport(cacheListener)
|
||||||
|
@ -72,11 +61,6 @@ function shouldRestoreCaches(): boolean {
|
||||||
core.info('Cache only restored on first action step.')
|
core.info('Cache only restored on first action step.')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export var that is detected in all later restore steps
|
|
||||||
core.exportVariable(CACHE_RESTORED_VAR, true)
|
|
||||||
// Export state that is detected in corresponding post-action step
|
|
||||||
core.saveState(CACHE_RESTORED_VAR, true)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue