Add telemetry for TRAP caching
This commit is contained in:
parent
ca10a6d552
commit
4139682b64
63 changed files with 1195 additions and 126 deletions
|
|
@ -30,6 +30,7 @@ test("emptyPaths", async (t) => {
|
|||
queriesInputCombines: false,
|
||||
},
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
analysisPaths.includeAndExcludeAnalysisPaths(config);
|
||||
t.is(process.env["LGTM_INDEX_INCLUDE"], undefined);
|
||||
|
|
@ -60,6 +61,7 @@ test("nonEmptyPaths", async (t) => {
|
|||
queriesInputCombines: false,
|
||||
},
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
analysisPaths.includeAndExcludeAnalysisPaths(config);
|
||||
t.is(process.env["LGTM_INDEX_INCLUDE"], "path1\npath2");
|
||||
|
|
@ -93,6 +95,7 @@ test("exclude temp dir", async (t) => {
|
|||
queriesInputCombines: false,
|
||||
},
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
analysisPaths.includeAndExcludeAnalysisPaths(config);
|
||||
t.is(process.env["LGTM_INDEX_INCLUDE"], undefined);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { uploadDatabases } from "./database-upload";
|
|||
import { GitHubFeatureFlags } from "./feature-flags";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import { uploadTrapCaches } from "./trap-caching";
|
||||
import { getTotalCacheSize, uploadTrapCaches } from "./trap-caching";
|
||||
import * as upload_lib from "./upload-lib";
|
||||
import { UploadResult } from "./upload-lib";
|
||||
import * as util from "./util";
|
||||
|
|
@ -29,13 +29,18 @@ interface AnalysisStatusReport
|
|||
|
||||
interface FinishStatusReport
|
||||
extends actionsUtil.StatusReportBase,
|
||||
AnalysisStatusReport {}
|
||||
AnalysisStatusReport {
|
||||
trap_cache_upload_size_bytes: number;
|
||||
trap_cache_upload_duration_ms: number;
|
||||
}
|
||||
|
||||
export async function sendStatusReport(
|
||||
startedAt: Date,
|
||||
config: Config | undefined,
|
||||
stats: AnalysisStatusReport | undefined,
|
||||
error?: Error
|
||||
error?: Error,
|
||||
trapCacheUploadTime?: number,
|
||||
didUploadTrapCaches?: boolean
|
||||
) {
|
||||
const status = actionsUtil.getActionsStatus(
|
||||
error,
|
||||
|
|
@ -57,6 +62,11 @@ export async function sendStatusReport(
|
|||
}
|
||||
: {}),
|
||||
...(stats || {}),
|
||||
trap_cache_upload_duration_ms: trapCacheUploadTime || 0,
|
||||
trap_cache_upload_size_bytes:
|
||||
config && didUploadTrapCaches
|
||||
? await getTotalCacheSize(config.trapCaches)
|
||||
: 0,
|
||||
};
|
||||
await actionsUtil.sendStatusReport(statusReport);
|
||||
}
|
||||
|
|
@ -66,6 +76,8 @@ async function run() {
|
|||
let uploadResult: UploadResult | undefined = undefined;
|
||||
let runStats: QueriesStatusReport | undefined = undefined;
|
||||
let config: Config | undefined = undefined;
|
||||
let trapCacheUploadTime: number | undefined = undefined;
|
||||
let didUploadTrapCaches = false;
|
||||
util.initializeEnvironment(util.Mode.actions, pkg.version);
|
||||
await util.checkActionVersion(pkg.version);
|
||||
|
||||
|
|
@ -163,8 +175,10 @@ async function run() {
|
|||
await uploadDatabases(repositoryNwo, config, apiDetails, logger);
|
||||
|
||||
// Possibly upload the TRAP caches for later re-use
|
||||
const trapCacheUploadStartTime = Date.now();
|
||||
const codeql = await getCodeQL(config.codeQLCmd);
|
||||
await uploadTrapCaches(codeql, config, logger);
|
||||
trapCacheUploadTime = Date.now() - trapCacheUploadStartTime;
|
||||
didUploadTrapCaches = await uploadTrapCaches(codeql, config, logger);
|
||||
|
||||
// We don't upload results in test mode, so don't wait for processing
|
||||
if (util.isInTestMode()) {
|
||||
|
|
@ -188,23 +202,58 @@ async function run() {
|
|||
|
||||
if (error instanceof CodeQLAnalysisError) {
|
||||
const stats = { ...error.queriesStatusReport };
|
||||
await sendStatusReport(startedAt, config, stats, error);
|
||||
await sendStatusReport(
|
||||
startedAt,
|
||||
config,
|
||||
stats,
|
||||
error,
|
||||
trapCacheUploadTime,
|
||||
didUploadTrapCaches
|
||||
);
|
||||
} else {
|
||||
await sendStatusReport(startedAt, config, undefined, error);
|
||||
await sendStatusReport(
|
||||
startedAt,
|
||||
config,
|
||||
undefined,
|
||||
error,
|
||||
trapCacheUploadTime,
|
||||
didUploadTrapCaches
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (runStats && uploadResult) {
|
||||
await sendStatusReport(startedAt, config, {
|
||||
...runStats,
|
||||
...uploadResult.statusReport,
|
||||
});
|
||||
await sendStatusReport(
|
||||
startedAt,
|
||||
config,
|
||||
{
|
||||
...runStats,
|
||||
...uploadResult.statusReport,
|
||||
},
|
||||
undefined,
|
||||
trapCacheUploadTime,
|
||||
didUploadTrapCaches
|
||||
);
|
||||
} else if (runStats) {
|
||||
await sendStatusReport(startedAt, config, { ...runStats });
|
||||
await sendStatusReport(
|
||||
startedAt,
|
||||
config,
|
||||
{ ...runStats },
|
||||
undefined,
|
||||
trapCacheUploadTime,
|
||||
didUploadTrapCaches
|
||||
);
|
||||
} else {
|
||||
await sendStatusReport(startedAt, config, undefined);
|
||||
await sendStatusReport(
|
||||
startedAt,
|
||||
config,
|
||||
undefined,
|
||||
undefined,
|
||||
trapCacheUploadTime,
|
||||
didUploadTrapCaches
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ test("status report fields and search path setting", async (t) => {
|
|||
queriesInputCombines: false,
|
||||
},
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
fs.mkdirSync(util.getCodeQLDatabasePath(config, language), {
|
||||
recursive: true,
|
||||
|
|
@ -442,6 +443,7 @@ const stubConfig: Config = {
|
|||
queriesInputCombines: false,
|
||||
},
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
|
||||
for (const options of [
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ test.beforeEach(() => {
|
|||
queriesInputCombines: false,
|
||||
},
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@ test("load non-empty input", async (t) => {
|
|||
debugDatabaseName: "my-db",
|
||||
augmentationProperties: configUtils.defaultAugmentationProperties,
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
|
||||
const languages = "javascript";
|
||||
|
|
|
|||
|
|
@ -183,6 +183,11 @@ export interface Config {
|
|||
* If a key is omitted, then TRAP caching should not be used for that language.
|
||||
*/
|
||||
trapCaches: Partial<Record<Language, string>>;
|
||||
|
||||
/**
|
||||
* Time taken to download TRAP caches. Used for status reporting.
|
||||
*/
|
||||
trapCacheDownloadTime: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1015,6 +1020,13 @@ export async function getDefaultConfig(
|
|||
);
|
||||
}
|
||||
|
||||
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
|
||||
trapCachingEnabled,
|
||||
codeQL,
|
||||
languages,
|
||||
logger
|
||||
);
|
||||
|
||||
return {
|
||||
languages,
|
||||
queries,
|
||||
|
|
@ -1030,12 +1042,27 @@ export async function getDefaultConfig(
|
|||
debugArtifactName,
|
||||
debugDatabaseName,
|
||||
augmentationProperties,
|
||||
trapCaches: trapCachingEnabled
|
||||
? await downloadTrapCaches(codeQL, languages, logger)
|
||||
: {},
|
||||
trapCaches,
|
||||
trapCacheDownloadTime,
|
||||
};
|
||||
}
|
||||
|
||||
async function downloadCacheWithTime(
|
||||
trapCachingEnabled: boolean,
|
||||
codeQL: CodeQL,
|
||||
languages: Language[],
|
||||
logger: Logger
|
||||
) {
|
||||
let trapCaches = {};
|
||||
let trapCacheDownloadTime = 0;
|
||||
if (trapCachingEnabled) {
|
||||
const start = Date.now();
|
||||
trapCaches = await downloadTrapCaches(codeQL, languages, logger);
|
||||
trapCacheDownloadTime = Date.now() - start;
|
||||
}
|
||||
return { trapCaches, trapCacheDownloadTime };
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the config from the given file.
|
||||
*/
|
||||
|
|
@ -1204,6 +1231,13 @@ async function loadConfig(
|
|||
}
|
||||
}
|
||||
|
||||
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
|
||||
trapCachingEnabled,
|
||||
codeQL,
|
||||
languages,
|
||||
logger
|
||||
);
|
||||
|
||||
return {
|
||||
languages,
|
||||
queries,
|
||||
|
|
@ -1219,9 +1253,8 @@ async function loadConfig(
|
|||
debugArtifactName,
|
||||
debugDatabaseName,
|
||||
augmentationProperties,
|
||||
trapCaches: trapCachingEnabled
|
||||
? await downloadTrapCaches(codeQL, languages, logger)
|
||||
: {},
|
||||
trapCaches,
|
||||
trapCacheDownloadTime,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ function getTestConfig(tmpDir: string): Config {
|
|||
debugDatabaseName: DEFAULT_DEBUG_DATABASE_NAME,
|
||||
augmentationProperties: defaultAugmentationProperties,
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
import { Language } from "./languages";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import { getTotalCacheSize } from "./trap-caching";
|
||||
import {
|
||||
checkActionVersion,
|
||||
checkGitHubVersionInRange,
|
||||
|
|
@ -65,6 +66,12 @@ interface InitSuccessStatusReport extends StatusReportBase {
|
|||
tools_resolved_version: string;
|
||||
/** Comma-separated list of languages specified explicitly in the workflow file. */
|
||||
workflow_languages: string;
|
||||
/** Comma-separated list of languages for which we are using TRAP caching. */
|
||||
trap_cache_languages: string;
|
||||
/** Size of TRAP caches that we downloaded, in megabytes. */
|
||||
trap_cache_download_size_bytes: number;
|
||||
/** Time taken to download TRAP caches, in milliseconds. */
|
||||
trap_cache_download_duration_ms: number;
|
||||
}
|
||||
|
||||
async function sendSuccessStatusReport(
|
||||
|
|
@ -115,6 +122,9 @@ async function sendSuccessStatusReport(
|
|||
tools_input: getOptionalInput("tools") || "",
|
||||
tools_resolved_version: toolsVersion,
|
||||
workflow_languages: workflowLanguages || "",
|
||||
trap_cache_languages: Object.keys(config.trapCaches).join(","),
|
||||
trap_cache_download_size_bytes: await getTotalCacheSize(config.trapCaches),
|
||||
trap_cache_download_duration_ms: config.trapCacheDownloadTime,
|
||||
};
|
||||
|
||||
await sendStatusReport(statusReport);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ function getTestConfig(tmpDir: string): configUtils.Config {
|
|||
debugDatabaseName: util.DEFAULT_DEBUG_DATABASE_NAME,
|
||||
augmentationProperties: configUtils.defaultAugmentationProperties,
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ const testConfigWithoutTmpDir: Config = {
|
|||
trapCaches: {
|
||||
javascript: "/some/cache/dir",
|
||||
},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
|
||||
function getTestConfigWithTempDir(tmpDir: string): configUtils.Config {
|
||||
|
|
@ -113,6 +114,7 @@ function getTestConfigWithTempDir(tmpDir: string): configUtils.Config {
|
|||
javascript: path.resolve(tmpDir, "jsCache"),
|
||||
ruby: path.resolve(tmpDir, "rubyCache"),
|
||||
},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import * as fs from "fs";
|
|||
import * as path from "path";
|
||||
|
||||
import * as cache from "@actions/cache";
|
||||
import getFolderSize from "get-folder-size";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { CodeQL, CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES } from "./codeql";
|
||||
|
|
@ -118,12 +119,19 @@ export async function downloadTrapCaches(
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly upload TRAP caches to the Actions cache.
|
||||
* @param codeql The CodeQL instance to use.
|
||||
* @param config The configuration for this workflow.
|
||||
* @param logger A logger to record some informational messages to.
|
||||
* @returns Whether the TRAP caches were uploaded.
|
||||
*/
|
||||
export async function uploadTrapCaches(
|
||||
codeql: CodeQL,
|
||||
config: Config,
|
||||
logger: Logger
|
||||
): Promise<void> {
|
||||
if (!(await actionsUtil.isAnalyzingDefaultBranch())) return; // Only upload caches from the default branch
|
||||
): Promise<boolean> {
|
||||
if (!(await actionsUtil.isAnalyzingDefaultBranch())) return false; // Only upload caches from the default branch
|
||||
|
||||
const toAwait: Array<Promise<number>> = [];
|
||||
for (const language of config.languages) {
|
||||
|
|
@ -138,6 +146,7 @@ export async function uploadTrapCaches(
|
|||
toAwait.push(cache.saveCache([cacheDir], key));
|
||||
}
|
||||
await Promise.all(toAwait);
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function getLanguagesSupportingCaching(
|
||||
|
|
@ -175,6 +184,23 @@ export async function getLanguagesSupportingCaching(
|
|||
return result;
|
||||
}
|
||||
|
||||
export async function getTotalCacheSize(
|
||||
trapCaches: Partial<Record<Language, string>>
|
||||
): Promise<number> {
|
||||
const sizes = await Promise.all(
|
||||
Object.values(trapCaches).map(async (cacheDir) => {
|
||||
return new Promise<number>((resolve) => {
|
||||
getFolderSize(cacheDir, (err, size) => {
|
||||
// Ignore file system errors when getting the size. It's only used for telemetry anyway.
|
||||
if (err) resolve(0);
|
||||
resolve(size);
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
return sizes.reduce((a, b) => a + b, 0);
|
||||
}
|
||||
|
||||
async function cacheKey(
|
||||
codeql: CodeQL,
|
||||
language: Language,
|
||||
|
|
|
|||
|
|
@ -357,6 +357,7 @@ for (const [packs, expectedStatus] of ML_POWERED_JS_STATUS_TESTS) {
|
|||
queriesInputCombines: false,
|
||||
},
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
};
|
||||
|
||||
t.is(util.getMlPoweredJsQueriesStatus(config), expectedStatus);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue