Compare commits

...

14 Commits

Author SHA1 Message Date
Daz DeBoer
cba1833dde Run test builds on Ubuntu and Windows
- Remove MacOS since the agents are current flaky
- Add Windows to a few more test pipelines
2021-10-21 12:08:17 -06:00
Daz DeBoer
39db90e99b Include build scan link in failure notice 2021-10-21 12:04:25 -06:00
Daz DeBoer
947a893558 Merge pull request #97 from gradle/dd/configurable-cache
Add some internal options for fine-tuning the gradle-build-action cache

- Can specify cache-paths to override the default directories cached from Gradle User Home
- Can specify cache-exclude-paths to exclude files from the Gradle User Home cache
- Files under caches/<gradle-version>/kotlin-dsl are now cached as a bundle
2021-10-21 19:26:19 +02:00
Daz DeBoer
b99e9f0bc3 Build outputs 2021-10-21 11:13:10 -06:00
Daz DeBoer
4cf255df10 Move bundle metadata files out of caches directory
Instead, use a separate '.gradle-build-action' directory for bundle
metadata files. This directory is always part of the cache-path.
2021-10-21 11:13:09 -06:00
Daz DeBoer
614d8770a4 Add test for cache configuration 2021-10-21 11:13:09 -06:00
Daz DeBoer
69453dbfc5 Include 'kotlin-dsl' as a cache-artifact-bundle 2021-10-21 11:13:09 -06:00
Daz DeBoer
1113cb87cb Allow cache-exclude-paths to be configured for action
All excluded paths are deleted prior to caching the Gradle User Home.
2021-10-21 11:13:09 -06:00
Daz DeBoer
9c95294209 Allow cache-paths to be set via action config 2021-10-21 11:13:08 -06:00
Daz DeBoer
f901ec9c20 Bump cache-protocol version 2021-10-20 15:04:14 -06:00
Daz DeBoer
a94b9252d5 Improve cache logging 2021-10-16 10:15:40 -06:00
Daz DeBoer
25672bf196 Build outputs 2021-10-16 09:50:40 -06:00
Daz DeBoer
cb6a0acca4 Use precise matching for artifact bundles
This should fix the warnings issued when saving artifact bundles.
2021-10-16 09:49:15 -06:00
Daz DeBoer
aa2ed2e033 Use cache protocol version for bundle keys too 2021-10-16 09:49:14 -06:00
15 changed files with 218 additions and 45 deletions

View File

@@ -10,7 +10,10 @@ env:
jobs: jobs:
action-inputs: action-inputs:
runs-on: ubuntu-latest strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@@ -0,0 +1,71 @@
name: Test caching configuration
on:
pull_request:
push:
workflow_dispatch:
env:
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
CACHE_DEBUG_ENABLED: true
jobs:
# Run initial Gradle builds to push initial cache entries
# These builds should start fresh without cache hits, due to the seed injected into the cache key above.
seed-build:
strategy:
matrix:
os: [ubuntu-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/groovy-dsl
arguments: test
# Add "wrapper" to main cache entry and remove 'wrapper-zips' bundle
# Exclude build-cache from main cache entry
cache-paths: |
["caches", "notifications", "wrapper"]
cache-exclude-paths: |
["caches/build-cache-1"]
cache-artifact-bundles: |
[
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
["dependency-jars", "caches/modules-*/files-*/**/*.jar"],
["instrumented-jars", "caches/jars-*/*/"],
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
]
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
verify-build:
needs: seed-build
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Execute Gradle build with --offline
uses: ./
with:
build-root-directory: __tests__/samples/groovy-dsl
arguments: test --offline
cache-read-only: true
# Need the same configuration when restoring state from cache
cache-paths: |
["caches", "notifications", "wrapper"]
cache-exclude-paths: |
["caches/build-cache-1"]
cache-artifact-bundles: |
[
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
["dependency-jars", "caches/modules-*/files-*/**/*.jar"],
["instrumented-jars", "caches/jars-*/*/"],
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
]

View File

@@ -14,7 +14,7 @@ jobs:
seed-build: seed-build:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
@@ -35,7 +35,7 @@ jobs:
needs: seed-build needs: seed-build
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
@@ -52,7 +52,7 @@ jobs:
needs: seed-build needs: seed-build
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
@@ -69,7 +69,7 @@ jobs:
needs: seed-build needs: seed-build
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
@@ -86,7 +86,10 @@ jobs:
# Check that the build can run when no bundles are restored # Check that the build can run when no bundles are restored
no-bundles-restored: no-bundles-restored:
needs: seed-build needs: seed-build
runs-on: ubuntu-latest strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@@ -14,7 +14,7 @@ jobs:
gradle-execution: gradle-execution:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, windows-latest]
include: include:
- os: windows-latest - os: windows-latest
script-suffix: '.bat' script-suffix: '.bat'
@@ -44,7 +44,7 @@ jobs:
gradle-versions: gradle-versions:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, windows-latest]
include: include:
- os: windows-latest - os: windows-latest
script-suffix: '.bat' script-suffix: '.bat'

View File

@@ -13,7 +13,10 @@ 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:
runs-on: ubuntu-latest strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -26,7 +29,10 @@ jobs:
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline # Test that the gradle-user-home cache will cache dependencies, by running build with --offline
dependencies-cache: dependencies-cache:
needs: seed-build needs: seed-build
runs-on: ubuntu-latest strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -40,7 +46,10 @@ jobs:
# Test that the gradle-user-home cache will cache and restore local build-cache # Test that the gradle-user-home cache will cache and restore local build-cache
build-cache: build-cache:
needs: seed-build needs: seed-build
runs-on: ubuntu-latest strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@@ -12,7 +12,10 @@ env:
jobs: jobs:
# Use kotlin-dsl project to verify caching of generated jars and compiled scripts # Use kotlin-dsl project to verify caching of generated jars and compiled scripts
seed-build: seed-build:
runs-on: ubuntu-latest strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -25,7 +28,10 @@ jobs:
# Check that the build can run --offline # Check that the build can run --offline
verify-build: verify-build:
needs: seed-build needs: seed-build
runs-on: ubuntu-latest strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@@ -7,38 +7,63 @@ inputs:
gradle-executable: gradle-executable:
description: Path to the Gradle executable description: Path to the Gradle executable
required: false required: false
gradle-version: gradle-version:
description: Gradle version to use description: Gradle version to use
required: false required: false
build-root-directory: build-root-directory:
description: Path to the root directory of the build description: Path to the root directory of the build
required: false required: false
arguments: arguments:
description: Gradle command line arguments (supports multi-line input) description: Gradle command line arguments (supports multi-line input)
required: false required: false
cache-disabled: cache-disabled:
description: When 'true', all caching is disabled. No entries will be written to or read from the cache. description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
required: false required: false
default: false default: false
cache-read-only: cache-read-only:
description: When 'true', existing entries will be read from the cache but no entries will be written description: When 'true', existing entries will be read from the cache but no entries will be written
required: false required: false
# TODO: It might be useful to default to read-only for PRs, or non-main branch. # TODO: It might be useful to default to read-only for PRs, or non-main branch.
default: false default: false
# EXPERIMENTAL & INTERNAL CONFIGURATION PROPERTIES
# The following action properties allow fine-grained tweaking of the action caching behaviour.
# These properties are not designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.
# Use at your own risk!
workflow-job-context: 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. 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) }}
cache-paths:
description: Paths in Gradle User Home to cache. (EXPERIMENTAL - may be changed/removed without notice)
required: false
default: |
["caches", "notifications"]
cache-exclude-paths:
description: Paths in Gradle User Home to exclude from cache. (EXPERIMENTAL - may be changed/removed without notice)
required: false
# eg ["caches/build-cache-1"] will prevent the local build cache from being saved/restored.
default: |
[]
cache-artifact-bundles: cache-artifact-bundles:
description: Names and patterns of artifact bundles to cache separately. For internal use only. description: Names and patterns of artifact bundles to cache separately. (EXPERIMENTAL - may be changed/removed without notice)
required: false required: false
default: | default: |
[ [
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"], ["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
["wrapper-zips", "wrapper/dists/*/*/*.zip"], ["wrapper-zips", "wrapper/dists/*/*/*.zip"],
["dependency-jars", "caches/modules-*/files-*/**/*.jar"], ["dependency-jars", "caches/modules-*/files-*/**/*.jar"],
["instrumented-jars", "caches/jars-*/*"] ["instrumented-jars", "caches/jars-*/*/"],
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
] ]
outputs: outputs:

2
dist/main/index.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/post/index.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,10 +5,14 @@ import * as core from '@actions/core'
import * as glob from '@actions/glob' import * as glob from '@actions/glob'
import * as exec from '@actions/exec' import * as exec from '@actions/exec'
import {AbstractCache, hashFileNames, tryDelete} from './cache-utils' import {
AbstractCache,
getCacheKeyPrefix,
hashFileNames,
tryDelete
} from './cache-utils'
// Which paths under Gradle User Home should be cached const META_FILE_DIR = '.gradle-build-action'
const CACHE_PATH = ['caches', 'notifications']
export class GradleUserHomeCache extends AbstractCache { export class GradleUserHomeCache extends AbstractCache {
private gradleUserHome: string private gradleUserHome: string
@@ -63,17 +67,30 @@ export class GradleUserHomeCache extends AbstractCache {
} }
private getBundleMetaFile(name: string): string { private getBundleMetaFile(name: string): string {
return path.resolve( return path.resolve(this.gradleUserHome, META_FILE_DIR, `${name}.cache`)
this.gradleUserHome,
'caches',
`.gradle-build-action.${name}.cache`
)
} }
async beforeSave(): Promise<void> { async beforeSave(): Promise<void> {
await this.reportGradleUserHomeSize('before saving common artifacts') await this.reportGradleUserHomeSize('before saving common artifacts')
this.removeExcludedPaths()
await this.saveArtifactBundles() await this.saveArtifactBundles()
await this.reportGradleUserHomeSize('after saving common artifacts') await this.reportGradleUserHomeSize(
"after saving common artifacts (only 'caches' and 'notifications' will be stored)"
)
}
private removeExcludedPaths(): void {
const rawPaths: string[] = JSON.parse(
core.getInput('cache-exclude-paths')
)
const resolvedPaths = rawPaths.map(x =>
path.resolve(this.gradleUserHome, x)
)
for (const p of resolvedPaths) {
this.debug(`Deleting excluded path: ${p}`)
tryDelete(p)
}
} }
private async saveArtifactBundles(): Promise<void> { private async saveArtifactBundles(): Promise<void> {
@@ -96,7 +113,10 @@ export class GradleUserHomeCache extends AbstractCache {
): Promise<void> { ): Promise<void> {
const bundleMetaFile = this.getBundleMetaFile(bundle) const bundleMetaFile = this.getBundleMetaFile(bundle)
const globber = await glob.create(artifactPath) const globber = await glob.create(artifactPath, {
implicitDescendants: false,
followSymbolicLinks: false
})
const bundleFiles = await globber.glob() const bundleFiles = await globber.glob()
// Handle no matching files // Handle no matching files
@@ -120,9 +140,7 @@ export class GradleUserHomeCache extends AbstractCache {
} else { } else {
core.info(`Caching ${bundle} with cache key: ${cacheKey}`) core.info(`Caching ${bundle} with cache key: ${cacheKey}`)
await this.saveCache([artifactPath], cacheKey) await this.saveCache([artifactPath], cacheKey)
this.writeBundleMetaFile(bundleMetaFile, cacheKey)
this.debug(`Writing cache metafile: ${bundleMetaFile}`)
fs.writeFileSync(bundleMetaFile, cacheKey)
} }
for (const file of bundleFiles) { for (const file of bundleFiles) {
@@ -131,7 +149,7 @@ export class GradleUserHomeCache extends AbstractCache {
} }
protected createCacheKey(bundle: string, files: string[]): string { protected createCacheKey(bundle: string, files: string[]): string {
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || '' const cacheKeyPrefix = getCacheKeyPrefix()
const relativeFiles = files.map(x => const relativeFiles = files.map(x =>
path.relative(this.gradleUserHome, x) path.relative(this.gradleUserHome, x)
) )
@@ -144,6 +162,17 @@ export class GradleUserHomeCache extends AbstractCache {
return `${cacheKeyPrefix}${bundle}-${key}` return `${cacheKeyPrefix}${bundle}-${key}`
} }
private writeBundleMetaFile(metaFile: string, cacheKey: string): void {
this.debug(`Writing bundle metafile: ${metaFile}`)
const dirName = path.dirname(metaFile)
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName)
}
fs.writeFileSync(metaFile, cacheKey)
}
protected determineGradleUserHome(rootDir: string): string { protected determineGradleUserHome(rootDir: string): string {
const customGradleUserHome = process.env['GRADLE_USER_HOME'] const customGradleUserHome = process.env['GRADLE_USER_HOME']
if (customGradleUserHome) { if (customGradleUserHome) {
@@ -160,7 +189,19 @@ export class GradleUserHomeCache extends AbstractCache {
} }
protected getCachePath(): string[] { protected getCachePath(): string[] {
return CACHE_PATH.map(x => path.resolve(this.gradleUserHome, x)) const rawPaths: string[] = JSON.parse(core.getInput('cache-paths'))
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)
} }
private getArtifactBundles(): Map<string, string> { private getArtifactBundles(): Map<string, string> {
@@ -194,7 +235,7 @@ export class GradleUserHomeCache extends AbstractCache {
} }
) )
core.info(`Gradle User Home cache entry (directories >5M): ${label}`) core.info(`Gradle User Home (directories >5M): ${label}`)
core.info( core.info(
result.stdout result.stdout

View File

@@ -17,9 +17,13 @@ export function isCacheDebuggingEnabled(): boolean {
return process.env['CACHE_DEBUG_ENABLED'] ? true : false return process.env['CACHE_DEBUG_ENABLED'] ? true : false
} }
function generateCacheKey(cacheName: string): CacheKey { export function getCacheKeyPrefix(): string {
// Prefix can be used to force change all cache keys (defaults to cache protocol version) // Prefix can be used to force change all cache keys (defaults to cache protocol version)
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || 'v2-' return process.env['CACHE_KEY_PREFIX'] || 'v3-'
}
function generateCacheKey(cacheName: string): CacheKey {
const cacheKeyPrefix = getCacheKeyPrefix()
// At the most general level, share caches for all executions on the same OS // At the most general level, share caches for all executions on the same OS
const runnerOs = process.env['RUNNER_OS'] || '' const runnerOs = process.env['RUNNER_OS'] || ''

View File

@@ -7,7 +7,9 @@ const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
export async function restore(buildRootDirectory: string): Promise<void> { export async function restore(buildRootDirectory: string): Promise<void> {
if (isCacheDisabled()) { if (isCacheDisabled()) {
core.debug('Cache read disabled') core.info(
'Cache is disabled: will not restore state from previous builds.'
)
return return
} }
@@ -22,7 +24,9 @@ export async function restore(buildRootDirectory: string): Promise<void> {
export async function save(): Promise<void> { export async function save(): Promise<void> {
if (isCacheReadOnly()) { if (isCacheReadOnly()) {
core.debug('Cache is read-only: not saving cache entry') core.info(
'Cache is read-only: will not save state for use in subsequent builds.'
)
return return
} }

View File

@@ -28,13 +28,20 @@ export async function run(): Promise<void> {
if (result.buildScanUrl) { 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}`) if (result.buildScanUrl) {
core.setFailed(`Gradle build failed: ${result.buildScanUrl}`)
} else {
core.setFailed(
`Gradle build failed: process exited with status ${result.status}`
)
}
} else {
if (result.buildScanUrl) {
core.notice(`Gradle build succeeded: ${result.buildScanUrl}`)
}
} }
} catch (error) { } catch (error) {
core.setFailed(String(error)) core.setFailed(String(error))