mirror of
https://github.com/gradle/gradle-build-action.git
synced 2025-10-21 00:08:55 +08:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b3afdc78a7 | ||
|
e0c2736e35 | ||
|
a63892c289 | ||
|
d432f2086c | ||
|
eaad2cd2bb | ||
|
a148b21183 | ||
|
e7422f245c | ||
|
c86093d76a | ||
|
a693ccda4b | ||
|
543cacb256 |
41
.github/workflows/prod.yml
vendored
41
.github/workflows/prod.yml
vendored
@@ -4,9 +4,25 @@ name: prod
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
basic-build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Build using Gradle wrapper
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/basic
|
||||||
|
arguments: test
|
||||||
|
|
||||||
gradle-execution:
|
gradle-execution:
|
||||||
|
needs: basic-build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
@@ -17,11 +33,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Test use Gradle wrapper
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
build-root-directory: __tests__/samples/basic
|
|
||||||
arguments: test
|
|
||||||
- name: Test use defined Gradle version
|
- name: Test use defined Gradle version
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -48,6 +59,7 @@ jobs:
|
|||||||
arguments: help
|
arguments: help
|
||||||
|
|
||||||
dependencies-cache:
|
dependencies-cache:
|
||||||
|
needs: basic-build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
@@ -63,6 +75,7 @@ jobs:
|
|||||||
dependencies-cache-enabled: true
|
dependencies-cache-enabled: true
|
||||||
|
|
||||||
configuration-cache:
|
configuration-cache:
|
||||||
|
needs: basic-build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
@@ -79,8 +92,26 @@ jobs:
|
|||||||
dependencies-cache-enabled: true
|
dependencies-cache-enabled: true
|
||||||
# Configuration cache requires dependencies cache, since it assumes dependencies are already downloaded.
|
# Configuration cache requires dependencies cache, since it assumes dependencies are already downloaded.
|
||||||
|
|
||||||
|
cache-read-only:
|
||||||
|
needs: basic-build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Test cache-read-only
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/basic
|
||||||
|
arguments: test --no-daemon
|
||||||
|
dependencies-cache-enabled: true
|
||||||
|
configuration-cache-enabled: true
|
||||||
|
cache-read-only: true
|
||||||
|
|
||||||
failures: # These build invocations are informational only, and are expected to fail
|
failures: # These build invocations are informational only, and are expected to fail
|
||||||
|
needs: basic-build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
|
14
README.md
14
README.md
@@ -144,7 +144,7 @@ The distributions cache is simple and can't be configured further.
|
|||||||
The dependencies and configuration cache will compute a cache key in a best effort manner.
|
The dependencies and configuration cache will compute a cache key in a best effort manner.
|
||||||
Keep reading to learn how to better control how they work.
|
Keep reading to learn how to better control how they work.
|
||||||
|
|
||||||
Note that enabling configuration cache without thee dependencies cache is not permitted, since a hit in the configuration cache assumes that dependencies are already present in the local dependencies cache.
|
Note that enabling configuration cache without the dependencies cache is not permitted, since a hit in the configuration cache assumes that dependencies are already present in the local dependencies cache.
|
||||||
|
|
||||||
### Configuring the dependencies and configuration caches
|
### Configuring the dependencies and configuration caches
|
||||||
|
|
||||||
@@ -187,6 +187,18 @@ dependencies-cache-key: gradle/dependency-locks/**
|
|||||||
dependencies-cache-exact: true
|
dependencies-cache-exact: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Using the caches read-only
|
||||||
|
|
||||||
|
Cache storage space is limited for GitHub actions, and writing new cache entries can trigger the deletion of exising entries.
|
||||||
|
In some circumstances, it makes sense for a Gradle invocation to use any existing cache entries but not to write and changes back.
|
||||||
|
For example, you may want to write cache entries for builds on your `main` branch, but not for any PR build invocations.
|
||||||
|
|
||||||
|
Use the following configuration to avoid writing cache entries for the action invocation:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
cache-read-only: true
|
||||||
|
```
|
||||||
|
|
||||||
## Build scans
|
## Build scans
|
||||||
|
|
||||||
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-build-action` action will emit the link to the published build scan as an output named `build-scan-url`.
|
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-build-action` action will emit the link to the published build scan as an output named `build-scan-url`.
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import * as cryptoUtils from '../src/crypto-utils'
|
import * as cacheUtils from '../src/cache-utils'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
|
||||||
describe('crypto-utils', () => {
|
describe('cacheUtils-utils', () => {
|
||||||
describe('can hash', () => {
|
describe('can hash', () => {
|
||||||
it('a directory', async () => {
|
it('a directory', async () => {
|
||||||
const hash = await cryptoUtils.hashFiles(
|
const hash = await cacheUtils.hashFiles(
|
||||||
path.resolve('__tests__/data/crypto-utils-test/gradle')
|
path.resolve('__tests__/data/crypto-utils-test/gradle')
|
||||||
)
|
)
|
||||||
expect(hash).toBe(
|
expect(hash).toBe(
|
||||||
@@ -14,7 +14,7 @@ describe('crypto-utils', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('a directory with a glob', async () => {
|
it('a directory with a glob', async () => {
|
||||||
const hash = await cryptoUtils.hashFiles(
|
const hash = await cacheUtils.hashFiles(
|
||||||
path.resolve('__tests__/data/crypto-utils-test/'),
|
path.resolve('__tests__/data/crypto-utils-test/'),
|
||||||
['gradle/**']
|
['gradle/**']
|
||||||
)
|
)
|
||||||
@@ -25,7 +25,7 @@ describe('crypto-utils', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('a directory with globs', async () => {
|
it('a directory with globs', async () => {
|
||||||
const hash = await cryptoUtils.hashFiles(
|
const hash = await cacheUtils.hashFiles(
|
||||||
path.resolve('__tests__/data/crypto-utils-test/'),
|
path.resolve('__tests__/data/crypto-utils-test/'),
|
||||||
['**/*.gradle', 'gradle/**']
|
['**/*.gradle', 'gradle/**']
|
||||||
)
|
)
|
||||||
@@ -36,4 +36,30 @@ describe('crypto-utils', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('can truncate args', () => {
|
||||||
|
test('handles zero-length string', () => {
|
||||||
|
expect(cacheUtils.truncateArgs('')).toBe('')
|
||||||
|
})
|
||||||
|
test('leaves short string untouched', () => {
|
||||||
|
expect(
|
||||||
|
cacheUtils.truncateArgs('short string that-should-be-untouched')
|
||||||
|
).toBe('short string that-should-be-untouched')
|
||||||
|
})
|
||||||
|
test('truncates long string', () => {
|
||||||
|
const longString = 'a'.repeat(500)
|
||||||
|
expect(cacheUtils.truncateArgs(longString)).toBe('a'.repeat(400))
|
||||||
|
})
|
||||||
|
test('trims leading and trailing whitespace', () => {
|
||||||
|
expect(cacheUtils.truncateArgs(' this is an arg ')).toBe(
|
||||||
|
'this is an arg'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
test('removes repeated whitespace', () => {
|
||||||
|
expect(
|
||||||
|
cacheUtils.truncateArgs(
|
||||||
|
' this one has long \t\n\t\r spaces '
|
||||||
|
)
|
||||||
|
).toBe('this one has long spaces')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
@@ -1,5 +1,5 @@
|
|||||||
name: "Gradle Build Action"
|
name: "Gradle Build Action"
|
||||||
description: 'Execute Gradle Build'
|
description: 'Executes a Gradle build, caching useful state in the GitHub actions cache'
|
||||||
|
|
||||||
# https://help.github.com/en/articles/metadata-syntax-for-github-actions
|
# https://help.github.com/en/articles/metadata-syntax-for-github-actions
|
||||||
|
|
||||||
@@ -51,6 +51,10 @@ inputs:
|
|||||||
description: Whether to restore only if exact match, default to 'false'
|
description: Whether to restore only if exact match, default to 'false'
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
|
cache-read-only:
|
||||||
|
description: When 'true', existing entries will be read from the cache but no entries will be written
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
build-scan-url:
|
build-scan-url:
|
||||||
|
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/post/index.js
vendored
2
dist/post/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,10 +1,10 @@
|
|||||||
import path from 'path'
|
import * as path from 'path'
|
||||||
import fs from 'fs'
|
import * as fs from 'fs'
|
||||||
|
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as cache from '@actions/cache'
|
import * as cache from '@actions/cache'
|
||||||
|
|
||||||
import * as crypto from './crypto-utils'
|
import * as cacheUtils from './cache-utils'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
inputCacheKeyGlobs,
|
inputCacheKeyGlobs,
|
||||||
@@ -32,17 +32,22 @@ export async function restoreCachedConfiguration(
|
|||||||
core.saveState(CONFIGURATION_CACHE_PATH, cachePath)
|
core.saveState(CONFIGURATION_CACHE_PATH, cachePath)
|
||||||
|
|
||||||
const inputCacheExact = core.getBooleanInput('configuration-cache-exact')
|
const inputCacheExact = core.getBooleanInput('configuration-cache-exact')
|
||||||
const cacheKeyGlobs = inputCacheKeyGlobs('configuration-cache-key')
|
const cacheKeyPrefix = 'configuration|'
|
||||||
|
|
||||||
|
const args = core.getInput('arguments')
|
||||||
|
const argsKey = cacheUtils.truncateArgs(args)
|
||||||
|
const cacheKeyWithArgs = `${cacheKeyPrefix}${argsKey}|`
|
||||||
|
|
||||||
|
const cacheKeyGlobs = inputCacheKeyGlobs('configuration-cache-key')
|
||||||
|
const hash = await cacheUtils.hashFiles(rootDir, cacheKeyGlobs)
|
||||||
|
const cacheKey = `${cacheKeyWithArgs}${hash}`
|
||||||
|
|
||||||
const hash = await crypto.hashFiles(rootDir, cacheKeyGlobs)
|
|
||||||
const cacheKeyPrefix = 'configuration-'
|
|
||||||
const cacheKey = `${cacheKeyPrefix}${hash}`
|
|
||||||
core.saveState(CONFIGURATION_CACHE_KEY, cacheKey)
|
core.saveState(CONFIGURATION_CACHE_KEY, cacheKey)
|
||||||
|
|
||||||
const cacheResult = await cache.restoreCache(
|
const cacheResult = await cache.restoreCache(
|
||||||
[cachePath],
|
[cachePath],
|
||||||
cacheKey,
|
cacheKey,
|
||||||
inputCacheExact ? [] : [cacheKeyPrefix]
|
inputCacheExact ? [] : [cacheKeyWithArgs, cacheKeyPrefix]
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!cacheResult) {
|
if (!cacheResult) {
|
||||||
@@ -86,6 +91,8 @@ export async function cacheConfiguration(): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core.info(`Will cache configuration with key ${cacheKey}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await cache.saveCache([cachePath], cacheKey)
|
await cache.saveCache([cachePath], cacheKey)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@@ -5,7 +5,7 @@ import * as os from 'os'
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as cache from '@actions/cache'
|
import * as cache from '@actions/cache'
|
||||||
|
|
||||||
import * as crypto from './crypto-utils'
|
import * as cacheUtils from './cache-utils'
|
||||||
|
|
||||||
const DEPENDENCIES_CACHE_PATH = 'DEPENDENCIES_CACHE_PATH'
|
const DEPENDENCIES_CACHE_PATH = 'DEPENDENCIES_CACHE_PATH'
|
||||||
const DEPENDENCIES_CACHE_KEY = 'DEPENDENCIES_CACHE_KEY'
|
const DEPENDENCIES_CACHE_KEY = 'DEPENDENCIES_CACHE_KEY'
|
||||||
@@ -21,17 +21,22 @@ export async function restoreCachedDependencies(
|
|||||||
core.saveState(DEPENDENCIES_CACHE_PATH, cachePath)
|
core.saveState(DEPENDENCIES_CACHE_PATH, cachePath)
|
||||||
|
|
||||||
const inputCacheExact = core.getBooleanInput('dependencies-cache-exact')
|
const inputCacheExact = core.getBooleanInput('dependencies-cache-exact')
|
||||||
const cacheKeyGlobs = inputCacheKeyGlobs('dependencies-cache-key')
|
const cacheKeyPrefix = 'dependencies|'
|
||||||
|
|
||||||
|
const args = core.getInput('arguments')
|
||||||
|
const argsKey = cacheUtils.truncateArgs(args)
|
||||||
|
const cacheKeyWithArgs = `${cacheKeyPrefix}${argsKey}|`
|
||||||
|
|
||||||
|
const cacheKeyGlobs = inputCacheKeyGlobs('dependencies-cache-key')
|
||||||
|
const hash = await cacheUtils.hashFiles(rootDir, cacheKeyGlobs)
|
||||||
|
const cacheKey = `${cacheKeyWithArgs}${hash}`
|
||||||
|
|
||||||
const hash = await crypto.hashFiles(rootDir, cacheKeyGlobs)
|
|
||||||
const cacheKeyPrefix = 'dependencies-'
|
|
||||||
const cacheKey = `${cacheKeyPrefix}${hash}`
|
|
||||||
core.saveState(DEPENDENCIES_CACHE_KEY, cacheKey)
|
core.saveState(DEPENDENCIES_CACHE_KEY, cacheKey)
|
||||||
|
|
||||||
const cacheResult = await cache.restoreCache(
|
const cacheResult = await cache.restoreCache(
|
||||||
[cachePath],
|
[cachePath],
|
||||||
cacheKey,
|
cacheKey,
|
||||||
inputCacheExact ? [] : [cacheKeyPrefix]
|
inputCacheExact ? [] : [cacheKeyWithArgs, cacheKeyPrefix]
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!cacheResult) {
|
if (!cacheResult) {
|
||||||
@@ -73,6 +78,8 @@ export async function cacheDependencies(): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core.info(`Will cache dependencies with key ${cacheKey}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await cache.saveCache([cachePath], cacheKey)
|
await cache.saveCache([cachePath], cacheKey)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@@ -11,3 +11,7 @@ export async function hashFiles(
|
|||||||
.join('\n')
|
.join('\n')
|
||||||
return glob.hashFiles(combinedPatterns, {followSymbolicLinks})
|
return glob.hashFiles(combinedPatterns, {followSymbolicLinks})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function truncateArgs(args: string): string {
|
||||||
|
return args.trim().replace(/\s+/g, ' ').substr(0, 400)
|
||||||
|
}
|
@@ -1,12 +1,20 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
import * as cacheWrapper from './cache-wrapper'
|
import * as cacheWrapper from './cache-wrapper'
|
||||||
import * as cacheDependencies from './cache-dependencies'
|
import * as cacheDependencies from './cache-dependencies'
|
||||||
import * as cacheConfiguration from './cache-configuration'
|
import * as cacheConfiguration from './cache-configuration'
|
||||||
|
|
||||||
// Invoked by GitHub Actions
|
// Invoked by GitHub Actions
|
||||||
export async function run(): Promise<void> {
|
export async function run(): Promise<void> {
|
||||||
|
if (isCacheReadOnly()) return
|
||||||
|
|
||||||
await cacheWrapper.cacheWrapperDist()
|
await cacheWrapper.cacheWrapperDist()
|
||||||
await cacheDependencies.cacheDependencies()
|
await cacheDependencies.cacheDependencies()
|
||||||
await cacheConfiguration.cacheConfiguration()
|
await cacheConfiguration.cacheConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isCacheReadOnly(): boolean {
|
||||||
|
return core.getBooleanInput('cache-read-only')
|
||||||
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
|
Reference in New Issue
Block a user