Merge pull request #297 from gradle/extract-init-scripts

Extract init scripts and state tracking
This commit is contained in:
Daz DeBoer 2022-06-02 12:43:26 -06:00 committed by GitHub
commit 5fe4df6233
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 360 additions and 223 deletions

View file

@ -1 +1,13 @@
rootProject.name = 'basic'
plugins {
id "com.gradle.enterprise" version "3.10.1"
}
gradleEnterprise {
buildScan {
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
publishAlways()
uploadInBackground = false
}
}
rootProject.name = 'groovy-dsl'

37
.github/workflows/demo-job-summary.yml vendored Normal file
View file

@ -0,0 +1,37 @@
name: Demo Job Summary for Gradle builds
on:
workflow_dispatch:
push:
env:
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
jobs:
run-gradle-builds:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Build distribution
shell: bash
run: |
npm install
npm run build
- name: Setup Gradle
uses: ./
- name: Build kotlin-dsl project
working-directory: .github/workflow-samples/kotlin-dsl
run: ./gradlew assemble
- name: Build groovy-dsl project
working-directory: .github/workflow-samples/groovy-dsl
run: ./gradlew assemble
- name: Build kotlin-dsl project again
working-directory: .github/workflow-samples/kotlin-dsl
run: |
./gradlew tasks --no-daemon
./gradlew help check
- name: Fail groovy-dsl project
working-directory: .github/workflow-samples/groovy-dsl
continue-on-error: true
run: ./gradlew not-a-real-task

164
dist/main/index.js vendored
View file

@ -64964,65 +64964,16 @@ class GradleStateCache {
initializeGradleUserHome(gradleUserHome, initScriptsDir) {
const propertiesFile = path_1.default.resolve(gradleUserHome, 'gradle.properties');
fs_1.default.appendFileSync(propertiesFile, 'org.gradle.daemon=false');
const buildScanCapture = path_1.default.resolve(initScriptsDir, 'build-scan-capture.init.gradle');
fs_1.default.writeFileSync(buildScanCapture, `import org.gradle.util.GradleVersion
// Only run against 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)
const initScriptFilenames = ['build-result-capture.init.gradle', 'project-root-capture.init.gradle'];
for (const initScriptFilename of initScriptFilenames) {
const initScriptContent = this.readResourceAsString(initScriptFilename);
const initScriptPath = path_1.default.resolve(initScriptsDir, initScriptFilename);
fs_1.default.writeFileSync(initScriptPath, initScriptContent);
}
}
} 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 buildFailed = false
buildFinished { result ->
buildFailed = (result.failure != null)
}
buildScanPublished { buildScan ->
// Send commands directly to GitHub Actions via STDOUT.
def gradleCommand = rootProjectName + " " + gradle.startParameter.taskNames.join(" ")
def githubSummaryFile = new File(System.getenv("GITHUB_STEP_SUMMARY"))
if (buildFailed) {
githubSummaryFile << ":x: Gradle Build \`\${gradleCommand}\` [![Gradle Enterprise Build Scan](https://img.shields.io/badge/Gradle%20Enterprise%20Build%20Scan%E2%84%A2-FAILED-red?logo=Gradle)](\${buildScan.buildScanUri})"
} else {
githubSummaryFile << ":white_check_mark: Gradle Build \`\${gradleCommand}\` [![Gradle Enterprise Build Scan](https://img.shields.io/badge/Gradle%20Enterprise%20Build%20Scan%E2%84%A2-SUCCESS-brightgreen?logo=Gradle)](\${buildScan.buildScanUri})"
}
println("::set-output name=build-scan-url::\${buildScan.buildScanUri}")
}
}
}`);
const projectRootCapture = path_1.default.resolve(initScriptsDir, 'project-root-capture.init.gradle');
fs_1.default.writeFileSync(projectRootCapture, `
// Only run against 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, "${exports.PROJECT_ROOTS_FILE}")
if (!projectRootList.exists() || !projectRootList.text.contains(projectRootEntry)) {
projectRootList << projectRootEntry
}
}
}`);
readResourceAsString(resource) {
const absolutePath = path_1.default.resolve(__dirname, '..', '..', 'src', 'resources', resource);
return fs_1.default.readFileSync(absolutePath, 'utf8');
}
debugReportGradleUserHomeSize(label) {
return __awaiter(this, void 0, void 0, function* () {
@ -65748,7 +65699,6 @@ const cache_utils_1 = __nccwpck_require__(1678);
const cache_reporting_1 = __nccwpck_require__(6674);
const cache_base_1 = __nccwpck_require__(7591);
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED';
const GRADLE_USER_HOME = 'GRADLE_USER_HOME';
const CACHE_LISTENER = 'CACHE_LISTENER';
function restore(gradleUserHome) {
return __awaiter(this, void 0, void 0, function* () {
@ -65770,7 +65720,6 @@ function restore(gradleUserHome) {
}
gradleStateCache.init();
core.saveState(CACHE_RESTORED_VAR, true);
core.saveState(GRADLE_USER_HOME, gradleUserHome);
if ((0, cache_utils_1.isCacheWriteOnly)()) {
core.info('Cache is write-only: will not restore from cache.');
return;
@ -65783,7 +65732,7 @@ function restore(gradleUserHome) {
});
}
exports.restore = restore;
function save() {
function save(gradleUserHome) {
return __awaiter(this, void 0, void 0, function* () {
if (!shouldSaveCaches()) {
return;
@ -65795,7 +65744,6 @@ function save() {
return;
}
yield core.group('Caching Gradle state', () => __awaiter(this, void 0, void 0, function* () {
const gradleUserHome = core.getState(GRADLE_USER_HOME);
return new cache_base_1.GradleStateCache(gradleUserHome).save(cacheListener);
}));
(0, cache_reporting_1.logCachingReport)(cacheListener);
@ -65989,9 +65937,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.run = void 0;
const core = __importStar(__nccwpck_require__(2186));
const path = __importStar(__nccwpck_require__(1017));
const os = __importStar(__nccwpck_require__(2037));
const string_argv_1 = __nccwpck_require__(9453);
const caches = __importStar(__nccwpck_require__(3800));
const setupGradle = __importStar(__nccwpck_require__(8652));
const execution = __importStar(__nccwpck_require__(3584));
const provision = __importStar(__nccwpck_require__(2501));
function run() {
@ -65999,8 +65946,7 @@ function run() {
try {
const workspaceDirectory = process.env[`GITHUB_WORKSPACE`] || '';
const buildRootDirectory = resolveBuildRootDirectory(workspaceDirectory);
const gradleUserHome = determineGradleUserHome(buildRootDirectory);
yield caches.restore(gradleUserHome);
yield setupGradle.setup(buildRootDirectory);
const executable = yield provisionGradle(workspaceDirectory);
if (executable !== undefined) {
core.addPath(path.dirname(executable));
@ -66038,13 +65984,6 @@ function resolveBuildRootDirectory(baseDirectory) {
const resolvedBuildRootDirectory = buildRootDirectory === '' ? path.resolve(baseDirectory) : path.resolve(baseDirectory, buildRootDirectory);
return resolvedBuildRootDirectory;
}
function determineGradleUserHome(rootDir) {
const customGradleUserHome = process.env['GRADLE_USER_HOME'];
if (customGradleUserHome) {
return path.resolve(rootDir, customGradleUserHome);
}
return path.resolve(os.homedir(), '.gradle');
}
function parseCommandLineArguments() {
const input = core.getInput('arguments');
return (0, string_argv_1.parseArgsStringToArgv)(input);
@ -66255,6 +66194,87 @@ function httpGetString(url) {
}
/***/ }),
/***/ 8652:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.complete = exports.setup = void 0;
const core = __importStar(__nccwpck_require__(2186));
const path = __importStar(__nccwpck_require__(1017));
const os = __importStar(__nccwpck_require__(2037));
const caches = __importStar(__nccwpck_require__(3800));
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED';
const GRADLE_USER_HOME = 'GRADLE_USER_HOME';
function setup(buildRootDirectory) {
return __awaiter(this, void 0, void 0, function* () {
const gradleUserHome = determineGradleUserHome(buildRootDirectory);
if (process.env[GRADLE_SETUP_VAR]) {
core.info('Gradle setup only performed on first gradle-build-action step in workflow.');
return;
}
core.exportVariable(GRADLE_SETUP_VAR, true);
core.saveState(GRADLE_SETUP_VAR, true);
core.saveState(GRADLE_USER_HOME, gradleUserHome);
yield caches.restore(gradleUserHome);
});
}
exports.setup = setup;
function complete() {
return __awaiter(this, void 0, void 0, function* () {
if (!core.getState(GRADLE_SETUP_VAR)) {
core.info('Gradle setup post-action only performed for first gradle-build-action step in workflow.');
return;
}
const gradleUserHome = core.getState(GRADLE_USER_HOME);
yield caches.save(gradleUserHome);
});
}
exports.complete = complete;
function determineGradleUserHome(rootDir) {
const customGradleUserHome = process.env['GRADLE_USER_HOME'];
if (customGradleUserHome) {
return path.resolve(rootDir, customGradleUserHome);
}
return path.resolve(os.homedir(), '.gradle');
}
/***/ }),
/***/ 2877:

File diff suppressed because one or more lines are too long

157
dist/post/index.js vendored
View file

@ -64015,65 +64015,16 @@ class GradleStateCache {
initializeGradleUserHome(gradleUserHome, initScriptsDir) {
const propertiesFile = path_1.default.resolve(gradleUserHome, 'gradle.properties');
fs_1.default.appendFileSync(propertiesFile, 'org.gradle.daemon=false');
const buildScanCapture = path_1.default.resolve(initScriptsDir, 'build-scan-capture.init.gradle');
fs_1.default.writeFileSync(buildScanCapture, `import org.gradle.util.GradleVersion
// Only run against 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)
const initScriptFilenames = ['build-result-capture.init.gradle', 'project-root-capture.init.gradle'];
for (const initScriptFilename of initScriptFilenames) {
const initScriptContent = this.readResourceAsString(initScriptFilename);
const initScriptPath = path_1.default.resolve(initScriptsDir, initScriptFilename);
fs_1.default.writeFileSync(initScriptPath, initScriptContent);
}
}
} 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 buildFailed = false
buildFinished { result ->
buildFailed = (result.failure != null)
}
buildScanPublished { buildScan ->
// Send commands directly to GitHub Actions via STDOUT.
def gradleCommand = rootProjectName + " " + gradle.startParameter.taskNames.join(" ")
def githubSummaryFile = new File(System.getenv("GITHUB_STEP_SUMMARY"))
if (buildFailed) {
githubSummaryFile << ":x: Gradle Build \`\${gradleCommand}\` [![Gradle Enterprise Build Scan](https://img.shields.io/badge/Gradle%20Enterprise%20Build%20Scan%E2%84%A2-FAILED-red?logo=Gradle)](\${buildScan.buildScanUri})"
} else {
githubSummaryFile << ":white_check_mark: Gradle Build \`\${gradleCommand}\` [![Gradle Enterprise Build Scan](https://img.shields.io/badge/Gradle%20Enterprise%20Build%20Scan%E2%84%A2-SUCCESS-brightgreen?logo=Gradle)](\${buildScan.buildScanUri})"
}
println("::set-output name=build-scan-url::\${buildScan.buildScanUri}")
}
}
}`);
const projectRootCapture = path_1.default.resolve(initScriptsDir, 'project-root-capture.init.gradle');
fs_1.default.writeFileSync(projectRootCapture, `
// Only run against 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, "${exports.PROJECT_ROOTS_FILE}")
if (!projectRootList.exists() || !projectRootList.text.contains(projectRootEntry)) {
projectRootList << projectRootEntry
}
}
}`);
readResourceAsString(resource) {
const absolutePath = path_1.default.resolve(__dirname, '..', '..', 'src', 'resources', resource);
return fs_1.default.readFileSync(absolutePath, 'utf8');
}
debugReportGradleUserHomeSize(label) {
return __awaiter(this, void 0, void 0, function* () {
@ -64799,7 +64750,6 @@ const cache_utils_1 = __nccwpck_require__(1678);
const cache_reporting_1 = __nccwpck_require__(6674);
const cache_base_1 = __nccwpck_require__(7591);
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED';
const GRADLE_USER_HOME = 'GRADLE_USER_HOME';
const CACHE_LISTENER = 'CACHE_LISTENER';
function restore(gradleUserHome) {
return __awaiter(this, void 0, void 0, function* () {
@ -64821,7 +64771,6 @@ function restore(gradleUserHome) {
}
gradleStateCache.init();
core.saveState(CACHE_RESTORED_VAR, true);
core.saveState(GRADLE_USER_HOME, gradleUserHome);
if ((0, cache_utils_1.isCacheWriteOnly)()) {
core.info('Cache is write-only: will not restore from cache.');
return;
@ -64834,7 +64783,7 @@ function restore(gradleUserHome) {
});
}
exports.restore = restore;
function save() {
function save(gradleUserHome) {
return __awaiter(this, void 0, void 0, function* () {
if (!shouldSaveCaches()) {
return;
@ -64846,7 +64795,6 @@ function save() {
return;
}
yield core.group('Caching Gradle state', () => __awaiter(this, void 0, void 0, function* () {
const gradleUserHome = core.getState(GRADLE_USER_HOME);
return new cache_base_1.GradleStateCache(gradleUserHome).save(cacheListener);
}));
(0, cache_reporting_1.logCachingReport)(cacheListener);
@ -64908,12 +64856,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.run = void 0;
const core = __importStar(__nccwpck_require__(2186));
const caches = __importStar(__nccwpck_require__(3800));
const setupGradle = __importStar(__nccwpck_require__(8652));
process.on('uncaughtException', e => handleFailure(e));
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield caches.save();
yield setupGradle.complete();
}
catch (error) {
handleFailure(error);
@ -64922,7 +64870,7 @@ function run() {
}
exports.run = run;
function handleFailure(error) {
core.warning(`Unhandled error saving cache - job will continue: ${error}`);
core.warning(`Unhandled error in Gradle post-action - job will continue: ${error}`);
if (error instanceof Error && error.stack) {
core.info(error.stack);
}
@ -64930,6 +64878,87 @@ function handleFailure(error) {
run();
/***/ }),
/***/ 8652:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.complete = exports.setup = void 0;
const core = __importStar(__nccwpck_require__(2186));
const path = __importStar(__nccwpck_require__(1017));
const os = __importStar(__nccwpck_require__(2037));
const caches = __importStar(__nccwpck_require__(3800));
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED';
const GRADLE_USER_HOME = 'GRADLE_USER_HOME';
function setup(buildRootDirectory) {
return __awaiter(this, void 0, void 0, function* () {
const gradleUserHome = determineGradleUserHome(buildRootDirectory);
if (process.env[GRADLE_SETUP_VAR]) {
core.info('Gradle setup only performed on first gradle-build-action step in workflow.');
return;
}
core.exportVariable(GRADLE_SETUP_VAR, true);
core.saveState(GRADLE_SETUP_VAR, true);
core.saveState(GRADLE_USER_HOME, gradleUserHome);
yield caches.restore(gradleUserHome);
});
}
exports.setup = setup;
function complete() {
return __awaiter(this, void 0, void 0, function* () {
if (!core.getState(GRADLE_SETUP_VAR)) {
core.info('Gradle setup post-action only performed for first gradle-build-action step in workflow.');
return;
}
const gradleUserHome = core.getState(GRADLE_USER_HOME);
yield caches.save(gradleUserHome);
});
}
exports.complete = complete;
function determineGradleUserHome(rootDir) {
const customGradleUserHome = process.env['GRADLE_USER_HOME'];
if (customGradleUserHome) {
return path.resolve(rootDir, customGradleUserHome);
}
return path.resolve(os.homedir(), '.gradle');
}
/***/ }),
/***/ 2877:

File diff suppressed because one or more lines are too long

View file

@ -10,6 +10,7 @@
"lint": "eslint src/**/*.ts",
"build": "ncc build src/main.ts --out dist/main --source-map && ncc build src/post.ts --out dist/post --source-map",
"test": "jest",
"check": "npm run format && npm run lint",
"all": "npm run format && npm run lint && npm run build && npm test"
},
"repository": {

View file

@ -169,72 +169,18 @@ export class GradleStateCache {
const propertiesFile = path.resolve(gradleUserHome, 'gradle.properties')
fs.appendFileSync(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 against 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)
}
}
const initScriptFilenames = ['build-result-capture.init.gradle', 'project-root-capture.init.gradle']
for (const initScriptFilename of initScriptFilenames) {
const initScriptContent = this.readResourceAsString(initScriptFilename)
const initScriptPath = path.resolve(initScriptsDir, initScriptFilename)
fs.writeFileSync(initScriptPath, initScriptContent)
}
}
def registerCallbacks(buildScanExtension, rootProjectName) {
buildScanExtension.with {
def buildFailed = false
buildFinished { result ->
buildFailed = (result.failure != null)
}
buildScanPublished { buildScan ->
// Send commands directly to GitHub Actions via STDOUT.
def gradleCommand = rootProjectName + " " + gradle.startParameter.taskNames.join(" ")
def githubSummaryFile = new File(System.getenv("GITHUB_STEP_SUMMARY"))
if (buildFailed) {
githubSummaryFile << ":x: Gradle Build \`\${gradleCommand}\` [![Gradle Enterprise Build Scan](https://img.shields.io/badge/Gradle%20Enterprise%20Build%20Scan%E2%84%A2-FAILED-red?logo=Gradle)](\${buildScan.buildScanUri})"
} else {
githubSummaryFile << ":white_check_mark: Gradle Build \`\${gradleCommand}\` [![Gradle Enterprise Build Scan](https://img.shields.io/badge/Gradle%20Enterprise%20Build%20Scan%E2%84%A2-SUCCESS-brightgreen?logo=Gradle)](\${buildScan.buildScanUri})"
}
println("::set-output name=build-scan-url::\${buildScan.buildScanUri}")
}
}
}`
)
const projectRootCapture = path.resolve(initScriptsDir, 'project-root-capture.init.gradle')
fs.writeFileSync(
projectRootCapture,
`
// Only run against 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
}
}
}`
)
private readResourceAsString(resource: string): string {
// Resolving relative to __dirname will force the compiler to inline the content in the distribution
const absolutePath = path.resolve(__dirname, '..', '..', 'src', 'resources', resource)
return fs.readFileSync(absolutePath, 'utf8')
}
/**

View file

@ -4,7 +4,6 @@ import {logCachingReport, CacheListener} from './cache-reporting'
import {GradleStateCache} from './cache-base'
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
const CACHE_LISTENER = 'CACHE_LISTENER'
export async function restore(gradleUserHome: string): Promise<void> {
@ -34,8 +33,6 @@ export async function restore(gradleUserHome: string): Promise<void> {
gradleStateCache.init()
// Mark the state as restored so that post-action will perform save.
core.saveState(CACHE_RESTORED_VAR, true)
// Save the Gradle User Home for the post-action step.
core.saveState(GRADLE_USER_HOME, gradleUserHome)
if (isCacheWriteOnly()) {
core.info('Cache is write-only: will not restore from cache.')
@ -50,7 +47,7 @@ export async function restore(gradleUserHome: string): Promise<void> {
})
}
export async function save(): Promise<void> {
export async function save(gradleUserHome: string): Promise<void> {
if (!shouldSaveCaches()) {
return
}
@ -64,7 +61,6 @@ export async function save(): Promise<void> {
}
await core.group('Caching Gradle state', async () => {
const gradleUserHome = core.getState(GRADLE_USER_HOME)
return new GradleStateCache(gradleUserHome).save(cacheListener)
})

View file

@ -1,9 +1,8 @@
import * as core from '@actions/core'
import * as path from 'path'
import * as os from 'os'
import {parseArgsStringToArgv} from 'string-argv'
import * as caches from './caches'
import * as setupGradle from './setup-gradle'
import * as execution from './execution'
import * as provision from './provision'
@ -14,9 +13,8 @@ export async function run(): Promise<void> {
try {
const workspaceDirectory = process.env[`GITHUB_WORKSPACE`] || ''
const buildRootDirectory = resolveBuildRootDirectory(workspaceDirectory)
const gradleUserHome = determineGradleUserHome(buildRootDirectory)
await caches.restore(gradleUserHome)
await setupGradle.setup(buildRootDirectory)
const executable = await provisionGradle(workspaceDirectory)
// executable will be undefined if using Gradle wrapper
@ -60,15 +58,6 @@ function resolveBuildRootDirectory(baseDirectory: string): string {
return resolvedBuildRootDirectory
}
function determineGradleUserHome(rootDir: string): string {
const customGradleUserHome = process.env['GRADLE_USER_HOME']
if (customGradleUserHome) {
return path.resolve(rootDir, customGradleUserHome)
}
return path.resolve(os.homedir(), '.gradle')
}
function parseCommandLineArguments(): string[] {
const input = core.getInput('arguments')
return parseArgsStringToArgv(input)

View file

@ -1,5 +1,5 @@
import * as core from '@actions/core'
import * as caches from './caches'
import * as setupGradle from './setup-gradle'
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
@ -11,14 +11,14 @@ process.on('uncaughtException', e => handleFailure(e))
*/
export async function run(): Promise<void> {
try {
await caches.save()
await setupGradle.complete()
} catch (error) {
handleFailure(error)
}
}
function handleFailure(error: unknown): void {
core.warning(`Unhandled error saving cache - job will continue: ${error}`)
core.warning(`Unhandled error in Gradle post-action - job will continue: ${error}`)
if (error instanceof Error && error.stack) {
core.info(error.stack)
}

View file

@ -0,0 +1,50 @@
/*
* Capture information for each executed Gradle build to display in the job summary.
*/
import org.gradle.util.GradleVersion
// Only run against 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 buildFailed = false
buildFinished { result ->
buildFailed = (result.failure != null)
}
buildScanPublished { buildScan ->
def gradleCommand = rootProjectName + " " + gradle.startParameter.taskNames.join(" ")
// Write job summary to magic file defined by GitHub Actions.
def githubSummaryFile = new File(System.getenv("GITHUB_STEP_SUMMARY"))
if (buildFailed) {
githubSummaryFile << ":x: Gradle Build `${gradleCommand}` [![Gradle Enterprise Build Scan](https://img.shields.io/badge/Gradle%20Enterprise%20Build%20Scan%E2%84%A2-FAILED-red?logo=Gradle)](${buildScan.buildScanUri})"
} else {
githubSummaryFile << ":white_check_mark: Gradle Build `${gradleCommand}` [![Gradle Enterprise Build Scan](https://img.shields.io/badge/Gradle%20Enterprise%20Build%20Scan%E2%84%A2-SUCCESS-brightgreen?logo=Gradle)](${buildScan.buildScanUri})"
}
// Send 'set-output' command directly to GitHub Actions via STDOUT.
println("::set-output name=build-scan-url::${buildScan.buildScanUri}")
}
}
}

View file

@ -0,0 +1,12 @@
// Capture the build root directory for each executed Gradle build.
// Only run against 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.txt")
if (!projectRootList.exists() || !projectRootList.text.contains(projectRootEntry)) {
projectRootList << projectRootEntry
}
}
}

45
src/setup-gradle.ts Normal file
View file

@ -0,0 +1,45 @@
import * as core from '@actions/core'
import * as path from 'path'
import * as os from 'os'
import * as caches from './caches'
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
export async function setup(buildRootDirectory: string): Promise<void> {
const gradleUserHome = determineGradleUserHome(buildRootDirectory)
// Bypass setup on all but first action step in workflow.
if (process.env[GRADLE_SETUP_VAR]) {
core.info('Gradle setup only performed on first gradle-build-action step in workflow.')
return
}
// Record setup complete: visible to all subsequent actions and prevents duplicate setup
core.exportVariable(GRADLE_SETUP_VAR, true)
// Record setup complete: visible in post-action, to control action completion
core.saveState(GRADLE_SETUP_VAR, true)
// Save the Gradle User Home for use in the post-action step.
core.saveState(GRADLE_USER_HOME, gradleUserHome)
await caches.restore(gradleUserHome)
}
export async function complete(): Promise<void> {
if (!core.getState(GRADLE_SETUP_VAR)) {
core.info('Gradle setup post-action only performed for first gradle-build-action step in workflow.')
return
}
const gradleUserHome = core.getState(GRADLE_USER_HOME)
await caches.save(gradleUserHome)
}
function determineGradleUserHome(rootDir: string): string {
const customGradleUserHome = process.env['GRADLE_USER_HOME']
if (customGradleUserHome) {
return path.resolve(rootDir, customGradleUserHome)
}
return path.resolve(os.homedir(), '.gradle')
}