mirror of
https://github.com/github/codeql-action.git
synced 2025-12-24 08:10:06 +08:00
Add support for feature flagging via the GitHub API
This commit is contained in:
41
lib/database-upload.test.js
generated
41
lib/database-upload.test.js
generated
@@ -58,29 +58,6 @@ function getTestConfig(tmpDir) {
|
||||
debugMode: false,
|
||||
};
|
||||
}
|
||||
function getRecordingLogger(messages) {
|
||||
return {
|
||||
debug: (message) => {
|
||||
messages.push({ type: "debug", message });
|
||||
console.debug(message);
|
||||
},
|
||||
info: (message) => {
|
||||
messages.push({ type: "info", message });
|
||||
console.info(message);
|
||||
},
|
||||
warning: (message) => {
|
||||
messages.push({ type: "warning", message });
|
||||
console.warn(message);
|
||||
},
|
||||
error: (message) => {
|
||||
messages.push({ type: "error", message });
|
||||
console.error(message);
|
||||
},
|
||||
isDebug: () => true,
|
||||
startGroup: () => undefined,
|
||||
endGroup: () => undefined,
|
||||
};
|
||||
}
|
||||
function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatusCode) {
|
||||
// Passing an auth token is required, so we just use a dummy value
|
||||
const client = github.getOctokit("123");
|
||||
@@ -122,7 +99,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
.returns("false");
|
||||
sinon.stub(actionsUtil, "isAnalyzingDefaultBranch").resolves(true);
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message === "Database upload disabled in workflow. Skipping upload.") !== undefined);
|
||||
});
|
||||
@@ -138,7 +115,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
const config = getTestConfig(tmpDir);
|
||||
config.gitHubVersion = { type: util_1.GitHubVariant.GHES, version: "3.0" };
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, config, testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, config, testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message === "Not running against github.com. Skipping upload.") !== undefined);
|
||||
});
|
||||
@@ -154,7 +131,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
const config = getTestConfig(tmpDir);
|
||||
config.gitHubVersion = { type: util_1.GitHubVariant.GHAE };
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, config, testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, config, testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message === "Not running against github.com. Skipping upload.") !== undefined);
|
||||
});
|
||||
@@ -168,7 +145,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
.returns("true");
|
||||
sinon.stub(actionsUtil, "isAnalyzingDefaultBranch").resolves(false);
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message === "Not analyzing default branch. Skipping upload.") !== undefined);
|
||||
});
|
||||
@@ -188,7 +165,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
},
|
||||
});
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message ===
|
||||
"Repository is not opted in to database uploads. Skipping upload.") !== undefined);
|
||||
@@ -209,7 +186,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
},
|
||||
});
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "info" &&
|
||||
v.message ===
|
||||
"Skipping database upload due to unknown error: Error: some error message") !== undefined);
|
||||
@@ -230,7 +207,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
},
|
||||
});
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "warning" &&
|
||||
v.message ===
|
||||
"Failed to upload database for javascript: Error: some error message") !== undefined);
|
||||
@@ -251,7 +228,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
},
|
||||
});
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message === "Successfully uploaded database for javascript") !== undefined);
|
||||
});
|
||||
@@ -271,7 +248,7 @@ function mockHttpRequests(optInStatusCode, useUploadDomain, databaseUploadStatus
|
||||
},
|
||||
});
|
||||
const loggedMessages = [];
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, getRecordingLogger(loggedMessages));
|
||||
await (0, database_upload_1.uploadDatabases)(testRepoName, getTestConfig(tmpDir), testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message === "Successfully uploaded database for javascript") !== undefined);
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
79
lib/feature-flags.js
generated
Normal file
79
lib/feature-flags.js
generated
Normal file
@@ -0,0 +1,79 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (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;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GitHubFeatureFlags = void 0;
|
||||
const api_client_1 = require("./api-client");
|
||||
const repository_1 = require("./repository");
|
||||
const util = __importStar(require("./util"));
|
||||
class GitHubFeatureFlags {
|
||||
constructor(gitHubVersion, apiDetails, logger) {
|
||||
this.gitHubVersion = gitHubVersion;
|
||||
this.apiDetails = apiDetails;
|
||||
this.logger = logger;
|
||||
}
|
||||
getDatabaseUploadsEnabled() {
|
||||
return this.getFeatureFlag("database_uploads_enabled");
|
||||
}
|
||||
getMlPoweredQueriesEnabled() {
|
||||
return this.getFeatureFlag("ml_powered_queries_enabled");
|
||||
}
|
||||
getUploadsDomainEnabled() {
|
||||
return this.getFeatureFlag("uploads_domain_enabled");
|
||||
}
|
||||
async preloadFeatureFlags() {
|
||||
await this.getApiResponse();
|
||||
}
|
||||
async getFeatureFlag(name) {
|
||||
const response = (await this.getApiResponse())[name];
|
||||
if (response === undefined) {
|
||||
this.logger.debug(`Feature flag '${name}' undefined in API response, considering it disabled.`);
|
||||
}
|
||||
return response || false;
|
||||
}
|
||||
async getApiResponse() {
|
||||
const loadApiResponse = async () => {
|
||||
// Do nothing when not running against github.com
|
||||
if (this.gitHubVersion.type !== util.GitHubVariant.DOTCOM) {
|
||||
this.logger.debug("Not running against github.com. Disabling all feature flags.");
|
||||
return {};
|
||||
}
|
||||
const client = (0, api_client_1.getApiClient)(this.apiDetails);
|
||||
const repositoryNwo = (0, repository_1.parseRepositoryNwo)(util.getRequiredEnvParam("GITHUB_REPOSITORY"));
|
||||
try {
|
||||
const response = await client.request("GET /repos/:owner/:repo/code-scanning/codeql-action/features", {
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
this.logger.info(`Disabling all feature flags due to unknown error: ${e}`);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
const apiResponse = this.cachedApiResponse || (await loadApiResponse());
|
||||
this.cachedApiResponse = apiResponse;
|
||||
return apiResponse;
|
||||
}
|
||||
}
|
||||
exports.GitHubFeatureFlags = GitHubFeatureFlags;
|
||||
//# sourceMappingURL=feature-flags.js.map
|
||||
1
lib/feature-flags.js.map
Normal file
1
lib/feature-flags.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"feature-flags.js","sourceRoot":"","sources":["../src/feature-flags.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,6CAA8D;AAE9D,6CAAkD;AAClD,6CAA+B;AAgB/B,MAAa,kBAAkB;IAG7B,YACU,aAAiC,EACjC,UAA4B,EAC5B,MAAc;QAFd,kBAAa,GAAb,aAAa,CAAoB;QACjC,eAAU,GAAV,UAAU,CAAkB;QAC5B,WAAM,GAAN,MAAM,CAAQ;IACrB,CAAC;IAEJ,yBAAyB;QACvB,OAAO,IAAI,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC;IACzD,CAAC;IAED,0BAA0B;QACxB,OAAO,IAAI,CAAC,cAAc,CAAC,4BAA4B,CAAC,CAAC;IAC3D,CAAC;IAED,uBAAuB;QACrB,OAAO,IAAI,CAAC,cAAc,CAAC,wBAAwB,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;IAC9B,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,IAAY;QACvC,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,QAAQ,KAAK,SAAS,EAAE;YAC1B,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,iBAAiB,IAAI,uDAAuD,CAC7E,CAAC;SACH;QACD,OAAO,QAAQ,IAAI,KAAK,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,MAAM,eAAe,GAAG,KAAK,IAAI,EAAE;YACjC,iDAAiD;YACjD,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE;gBACzD,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,8DAA8D,CAC/D,CAAC;gBACF,OAAO,EAAE,CAAC;aACX;YACD,MAAM,MAAM,GAAG,IAAA,yBAAY,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC7C,MAAM,aAAa,GAAG,IAAA,+BAAkB,EACtC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAC9C,CAAC;YACF,IAAI;gBACF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CACnC,8DAA8D,EAC9D;oBACE,KAAK,EAAE,aAAa,CAAC,KAAK;oBAC1B,IAAI,EAAE,aAAa,CAAC,IAAI;iBACzB,CACF,CAAC;gBACF,OAAO,QAAQ,CAAC,IAAI,CAAC;aACtB;YAAC,OAAO,CAAC,EAAE;gBACV,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,qDAAqD,CAAC,EAAE,CACzD,CAAC;gBACF,OAAO,EAAE,CAAC;aACX;QACH,CAAC,CAAC;QAEF,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,IAAI,CAAC,MAAM,eAAe,EAAE,CAAC,CAAC;QACxE,IAAI,CAAC,iBAAiB,GAAG,WAAW,CAAC;QACrC,OAAO,WAAW,CAAC;IACrB,CAAC;CACF;AAtED,gDAsEC"}
|
||||
145
lib/feature-flags.test.js
generated
Normal file
145
lib/feature-flags.test.js
generated
Normal file
@@ -0,0 +1,145 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (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 __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const github = __importStar(require("@actions/github"));
|
||||
const ava_1 = __importDefault(require("ava"));
|
||||
const sinon = __importStar(require("sinon"));
|
||||
const apiClient = __importStar(require("./api-client"));
|
||||
const feature_flags_1 = require("./feature-flags");
|
||||
const testing_utils_1 = require("./testing-utils");
|
||||
const util = __importStar(require("./util"));
|
||||
const util_1 = require("./util");
|
||||
(0, testing_utils_1.setupTests)(ava_1.default);
|
||||
ava_1.default.beforeEach(() => {
|
||||
(0, util_1.initializeEnvironment)(util_1.Mode.actions, "1.2.3");
|
||||
sinon
|
||||
.stub(util, "getRequiredEnvParam")
|
||||
.withArgs("GITHUB_REPOSITORY")
|
||||
.returns("github/example");
|
||||
});
|
||||
const testApiDetails = {
|
||||
auth: "1234",
|
||||
url: "https://github.com",
|
||||
};
|
||||
function mockHttpRequests(responseStatusCode, flags) {
|
||||
// Passing an auth token is required, so we just use a dummy value
|
||||
const client = github.getOctokit("123");
|
||||
const requestSpy = sinon.stub(client, "request");
|
||||
const optInSpy = requestSpy.withArgs("GET /repos/:owner/:repo/code-scanning/codeql-action/features");
|
||||
if (responseStatusCode < 300) {
|
||||
optInSpy.resolves({
|
||||
status: responseStatusCode,
|
||||
data: flags,
|
||||
headers: {},
|
||||
url: "GET /repos/:owner/:repo/code-scanning/codeql-action/features",
|
||||
});
|
||||
}
|
||||
else {
|
||||
optInSpy.throws(new util_1.HTTPError("some error message", responseStatusCode));
|
||||
}
|
||||
sinon.stub(apiClient, "getApiClient").value(() => client);
|
||||
}
|
||||
const ALL_FEATURE_FLAGS_DISABLED_VARIANTS = [
|
||||
{
|
||||
description: "GHES",
|
||||
gitHubVersion: { type: util_1.GitHubVariant.GHES, version: "3.0.0" },
|
||||
},
|
||||
{ description: "GHAE", gitHubVersion: { type: util_1.GitHubVariant.GHAE } },
|
||||
];
|
||||
for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
|
||||
(0, ava_1.default)(`All feature flags are disabled if running against ${variant.description}`, async (t) => {
|
||||
await (0, util_1.withTmpDir)(async (tmpDir) => {
|
||||
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
|
||||
const loggedMessages = [];
|
||||
const featureFlags = new feature_flags_1.GitHubFeatureFlags(variant.gitHubVersion, testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
t.assert((await featureFlags.getDatabaseUploadsEnabled()) === false);
|
||||
t.assert((await featureFlags.getMlPoweredQueriesEnabled()) === false);
|
||||
t.assert((await featureFlags.getUploadsDomainEnabled()) === false);
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message ===
|
||||
"Not running against github.com. Disabling all feature flags.") !== undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
(0, ava_1.default)("Feature flags are disabled if they're not returned in API response", async (t) => {
|
||||
await (0, util_1.withTmpDir)(async (tmpDir) => {
|
||||
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
|
||||
const loggedMessages = [];
|
||||
const featureFlags = new feature_flags_1.GitHubFeatureFlags({ type: util_1.GitHubVariant.DOTCOM }, testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
mockHttpRequests(200, {});
|
||||
t.assert((await featureFlags.getDatabaseUploadsEnabled()) === false);
|
||||
t.assert((await featureFlags.getMlPoweredQueriesEnabled()) === false);
|
||||
t.assert((await featureFlags.getUploadsDomainEnabled()) === false);
|
||||
for (const featureFlag of [
|
||||
"database_uploads_enabled",
|
||||
"ml_powered_queries_enabled",
|
||||
"uploads_domain_enabled",
|
||||
]) {
|
||||
t.assert(loggedMessages.find((v) => v.type === "debug" &&
|
||||
v.message ===
|
||||
`Feature flag '${featureFlag}' undefined in API response, considering it disabled.`) !== undefined);
|
||||
}
|
||||
});
|
||||
});
|
||||
(0, ava_1.default)("All feature flags are disabled if the API request errors", async (t) => {
|
||||
await (0, util_1.withTmpDir)(async (tmpDir) => {
|
||||
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
|
||||
const loggedMessages = [];
|
||||
const featureFlags = new feature_flags_1.GitHubFeatureFlags({ type: util_1.GitHubVariant.DOTCOM }, testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
mockHttpRequests(500, {});
|
||||
t.assert((await featureFlags.getDatabaseUploadsEnabled()) === false);
|
||||
t.assert((await featureFlags.getMlPoweredQueriesEnabled()) === false);
|
||||
t.assert((await featureFlags.getUploadsDomainEnabled()) === false);
|
||||
t.assert(loggedMessages.find((v) => v.type === "info" &&
|
||||
v.message ===
|
||||
"Disabling all feature flags due to unknown error: Error: some error message") !== undefined);
|
||||
});
|
||||
});
|
||||
const FEATURE_FLAGS = [
|
||||
"database_uploads_enabled",
|
||||
"ml_powered_queries_enabled",
|
||||
"uploads_domain_enabled",
|
||||
];
|
||||
for (const featureFlag of FEATURE_FLAGS) {
|
||||
(0, ava_1.default)(`Feature flag '${featureFlag}' is enabled if enabled in the API response`, async (t) => {
|
||||
await (0, util_1.withTmpDir)(async (tmpDir) => {
|
||||
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
|
||||
const loggedMessages = [];
|
||||
const featureFlags = new feature_flags_1.GitHubFeatureFlags({ type: util_1.GitHubVariant.DOTCOM }, testApiDetails, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
|
||||
const expectedFeatureFlags = {};
|
||||
for (const f of FEATURE_FLAGS) {
|
||||
expectedFeatureFlags[f] = false;
|
||||
}
|
||||
expectedFeatureFlags[featureFlag] = true;
|
||||
mockHttpRequests(200, expectedFeatureFlags);
|
||||
const actualFeatureFlags = {
|
||||
database_uploads_enabled: await featureFlags.getDatabaseUploadsEnabled(),
|
||||
ml_powered_queries_enabled: await featureFlags.getMlPoweredQueriesEnabled(),
|
||||
uploads_domain_enabled: await featureFlags.getUploadsDomainEnabled(),
|
||||
};
|
||||
t.deepEqual(actualFeatureFlags, expectedFeatureFlags);
|
||||
});
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=feature-flags.test.js.map
|
||||
1
lib/feature-flags.test.js.map
Normal file
1
lib/feature-flags.test.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"feature-flags.test.js","sourceRoot":"","sources":["../src/feature-flags.test.ts"],"names":[],"mappings":"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"}
|
||||
26
lib/testing-utils.js
generated
26
lib/testing-utils.js
generated
@@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setupActionsVars = exports.setupTests = void 0;
|
||||
exports.getRecordingLogger = exports.setupActionsVars = exports.setupTests = void 0;
|
||||
const sinon = __importStar(require("sinon"));
|
||||
const CodeQL = __importStar(require("./codeql"));
|
||||
function wrapOutput(context) {
|
||||
@@ -89,4 +89,28 @@ function setupActionsVars(tempDir, toolsDir) {
|
||||
process.env["RUNNER_TOOL_CACHE"] = toolsDir;
|
||||
}
|
||||
exports.setupActionsVars = setupActionsVars;
|
||||
function getRecordingLogger(messages) {
|
||||
return {
|
||||
debug: (message) => {
|
||||
messages.push({ type: "debug", message });
|
||||
console.debug(message);
|
||||
},
|
||||
info: (message) => {
|
||||
messages.push({ type: "info", message });
|
||||
console.info(message);
|
||||
},
|
||||
warning: (message) => {
|
||||
messages.push({ type: "warning", message });
|
||||
console.warn(message);
|
||||
},
|
||||
error: (message) => {
|
||||
messages.push({ type: "error", message });
|
||||
console.error(message);
|
||||
},
|
||||
isDebug: () => true,
|
||||
startGroup: () => undefined,
|
||||
endGroup: () => undefined,
|
||||
};
|
||||
}
|
||||
exports.getRecordingLogger = getRecordingLogger;
|
||||
//# sourceMappingURL=testing-utils.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"testing-utils.js","sourceRoot":"","sources":["../src/testing-utils.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"testing-utils.js","sourceRoot":"","sources":["../src/testing-utils.ts"],"names":[],"mappings":"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"}
|
||||
@@ -11,9 +11,13 @@ import { setCodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import { uploadDatabases } from "./database-upload";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
import { setupActionsVars, setupTests } from "./testing-utils";
|
||||
import {
|
||||
getRecordingLogger,
|
||||
LoggedMessage,
|
||||
setupActionsVars,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import {
|
||||
GitHubVariant,
|
||||
HTTPError,
|
||||
@@ -51,35 +55,6 @@ function getTestConfig(tmpDir: string): Config {
|
||||
};
|
||||
}
|
||||
|
||||
interface LoggedMessage {
|
||||
type: "debug" | "info" | "warning" | "error";
|
||||
message: string | Error;
|
||||
}
|
||||
|
||||
function getRecordingLogger(messages: LoggedMessage[]): Logger {
|
||||
return {
|
||||
debug: (message: string) => {
|
||||
messages.push({ type: "debug", message });
|
||||
console.debug(message);
|
||||
},
|
||||
info: (message: string) => {
|
||||
messages.push({ type: "info", message });
|
||||
console.info(message);
|
||||
},
|
||||
warning: (message: string | Error) => {
|
||||
messages.push({ type: "warning", message });
|
||||
console.warn(message);
|
||||
},
|
||||
error: (message: string | Error) => {
|
||||
messages.push({ type: "error", message });
|
||||
console.error(message);
|
||||
},
|
||||
isDebug: () => true,
|
||||
startGroup: () => undefined,
|
||||
endGroup: () => undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function mockHttpRequests(
|
||||
optInStatusCode: number,
|
||||
useUploadDomain?: boolean,
|
||||
|
||||
202
src/feature-flags.test.ts
Normal file
202
src/feature-flags.test.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
import * as github from "@actions/github";
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import * as apiClient from "./api-client";
|
||||
import { GitHubApiDetails } from "./api-client";
|
||||
import { GitHubFeatureFlags } from "./feature-flags";
|
||||
import {
|
||||
getRecordingLogger,
|
||||
LoggedMessage,
|
||||
setupActionsVars,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import * as util from "./util";
|
||||
import {
|
||||
GitHubVariant,
|
||||
HTTPError,
|
||||
initializeEnvironment,
|
||||
Mode,
|
||||
withTmpDir,
|
||||
} from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test.beforeEach(() => {
|
||||
initializeEnvironment(Mode.actions, "1.2.3");
|
||||
|
||||
sinon
|
||||
.stub(util, "getRequiredEnvParam")
|
||||
.withArgs("GITHUB_REPOSITORY")
|
||||
.returns("github/example");
|
||||
});
|
||||
|
||||
const testApiDetails: GitHubApiDetails = {
|
||||
auth: "1234",
|
||||
url: "https://github.com",
|
||||
};
|
||||
|
||||
function mockHttpRequests(
|
||||
responseStatusCode: number,
|
||||
flags: { [flagName: string]: boolean }
|
||||
) {
|
||||
// Passing an auth token is required, so we just use a dummy value
|
||||
const client = github.getOctokit("123");
|
||||
|
||||
const requestSpy = sinon.stub(client, "request");
|
||||
|
||||
const optInSpy = requestSpy.withArgs(
|
||||
"GET /repos/:owner/:repo/code-scanning/codeql-action/features"
|
||||
);
|
||||
if (responseStatusCode < 300) {
|
||||
optInSpy.resolves({
|
||||
status: responseStatusCode,
|
||||
data: flags,
|
||||
headers: {},
|
||||
url: "GET /repos/:owner/:repo/code-scanning/codeql-action/features",
|
||||
});
|
||||
} else {
|
||||
optInSpy.throws(new HTTPError("some error message", responseStatusCode));
|
||||
}
|
||||
|
||||
sinon.stub(apiClient, "getApiClient").value(() => client);
|
||||
}
|
||||
|
||||
const ALL_FEATURE_FLAGS_DISABLED_VARIANTS: Array<{
|
||||
description: string;
|
||||
gitHubVersion: util.GitHubVersion;
|
||||
}> = [
|
||||
{
|
||||
description: "GHES",
|
||||
gitHubVersion: { type: GitHubVariant.GHES, version: "3.0.0" },
|
||||
},
|
||||
{ description: "GHAE", gitHubVersion: { type: GitHubVariant.GHAE } },
|
||||
];
|
||||
|
||||
for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
|
||||
test(`All feature flags are disabled if running against ${variant.description}`, async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
|
||||
const loggedMessages = [];
|
||||
const featureFlags = new GitHubFeatureFlags(
|
||||
variant.gitHubVersion,
|
||||
testApiDetails,
|
||||
getRecordingLogger(loggedMessages)
|
||||
);
|
||||
|
||||
t.assert((await featureFlags.getDatabaseUploadsEnabled()) === false);
|
||||
t.assert((await featureFlags.getMlPoweredQueriesEnabled()) === false);
|
||||
t.assert((await featureFlags.getUploadsDomainEnabled()) === false);
|
||||
|
||||
t.assert(
|
||||
loggedMessages.find(
|
||||
(v: LoggedMessage) =>
|
||||
v.type === "debug" &&
|
||||
v.message ===
|
||||
"Not running against github.com. Disabling all feature flags."
|
||||
) !== undefined
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test("Feature flags are disabled if they're not returned in API response", async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
|
||||
const loggedMessages = [];
|
||||
const featureFlags = new GitHubFeatureFlags(
|
||||
{ type: GitHubVariant.DOTCOM },
|
||||
testApiDetails,
|
||||
getRecordingLogger(loggedMessages)
|
||||
);
|
||||
|
||||
mockHttpRequests(200, {});
|
||||
|
||||
t.assert((await featureFlags.getDatabaseUploadsEnabled()) === false);
|
||||
t.assert((await featureFlags.getMlPoweredQueriesEnabled()) === false);
|
||||
t.assert((await featureFlags.getUploadsDomainEnabled()) === false);
|
||||
|
||||
for (const featureFlag of [
|
||||
"database_uploads_enabled",
|
||||
"ml_powered_queries_enabled",
|
||||
"uploads_domain_enabled",
|
||||
]) {
|
||||
t.assert(
|
||||
loggedMessages.find(
|
||||
(v: LoggedMessage) =>
|
||||
v.type === "debug" &&
|
||||
v.message ===
|
||||
`Feature flag '${featureFlag}' undefined in API response, considering it disabled.`
|
||||
) !== undefined
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test("All feature flags are disabled if the API request errors", async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
|
||||
const loggedMessages = [];
|
||||
const featureFlags = new GitHubFeatureFlags(
|
||||
{ type: GitHubVariant.DOTCOM },
|
||||
testApiDetails,
|
||||
getRecordingLogger(loggedMessages)
|
||||
);
|
||||
|
||||
mockHttpRequests(500, {});
|
||||
|
||||
t.assert((await featureFlags.getDatabaseUploadsEnabled()) === false);
|
||||
t.assert((await featureFlags.getMlPoweredQueriesEnabled()) === false);
|
||||
t.assert((await featureFlags.getUploadsDomainEnabled()) === false);
|
||||
|
||||
t.assert(
|
||||
loggedMessages.find(
|
||||
(v: LoggedMessage) =>
|
||||
v.type === "info" &&
|
||||
v.message ===
|
||||
"Disabling all feature flags due to unknown error: Error: some error message"
|
||||
) !== undefined
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
const FEATURE_FLAGS = [
|
||||
"database_uploads_enabled",
|
||||
"ml_powered_queries_enabled",
|
||||
"uploads_domain_enabled",
|
||||
];
|
||||
|
||||
for (const featureFlag of FEATURE_FLAGS) {
|
||||
test(`Feature flag '${featureFlag}' is enabled if enabled in the API response`, async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
|
||||
const loggedMessages = [];
|
||||
const featureFlags = new GitHubFeatureFlags(
|
||||
{ type: GitHubVariant.DOTCOM },
|
||||
testApiDetails,
|
||||
getRecordingLogger(loggedMessages)
|
||||
);
|
||||
|
||||
const expectedFeatureFlags = {};
|
||||
for (const f of FEATURE_FLAGS) {
|
||||
expectedFeatureFlags[f] = false;
|
||||
}
|
||||
expectedFeatureFlags[featureFlag] = true;
|
||||
mockHttpRequests(200, expectedFeatureFlags);
|
||||
|
||||
const actualFeatureFlags = {
|
||||
database_uploads_enabled:
|
||||
await featureFlags.getDatabaseUploadsEnabled(),
|
||||
ml_powered_queries_enabled:
|
||||
await featureFlags.getMlPoweredQueriesEnabled(),
|
||||
uploads_domain_enabled: await featureFlags.getUploadsDomainEnabled(),
|
||||
};
|
||||
|
||||
t.deepEqual(actualFeatureFlags, expectedFeatureFlags);
|
||||
});
|
||||
});
|
||||
}
|
||||
90
src/feature-flags.ts
Normal file
90
src/feature-flags.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { getApiClient, GitHubApiDetails } from "./api-client";
|
||||
import { Logger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import * as util from "./util";
|
||||
|
||||
interface FeatureFlags {
|
||||
getDatabaseUploadsEnabled(): Promise<boolean>;
|
||||
getMlPoweredQueriesEnabled(): Promise<boolean>;
|
||||
getUploadsDomainEnabled(): Promise<boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A response from the GitHub API that contains feature flag enablement information for the CodeQL
|
||||
* Action.
|
||||
*
|
||||
* It maps feature flags to whether they are enabled or not.
|
||||
*/
|
||||
type FeatureFlagsApiResponse = { [flagName: string]: boolean };
|
||||
|
||||
export class GitHubFeatureFlags implements FeatureFlags {
|
||||
private cachedApiResponse: FeatureFlagsApiResponse | undefined;
|
||||
|
||||
constructor(
|
||||
private gitHubVersion: util.GitHubVersion,
|
||||
private apiDetails: GitHubApiDetails,
|
||||
private logger: Logger
|
||||
) {}
|
||||
|
||||
getDatabaseUploadsEnabled(): Promise<boolean> {
|
||||
return this.getFeatureFlag("database_uploads_enabled");
|
||||
}
|
||||
|
||||
getMlPoweredQueriesEnabled(): Promise<boolean> {
|
||||
return this.getFeatureFlag("ml_powered_queries_enabled");
|
||||
}
|
||||
|
||||
getUploadsDomainEnabled(): Promise<boolean> {
|
||||
return this.getFeatureFlag("uploads_domain_enabled");
|
||||
}
|
||||
|
||||
async preloadFeatureFlags(): Promise<void> {
|
||||
await this.getApiResponse();
|
||||
}
|
||||
|
||||
private async getFeatureFlag(name: string): Promise<boolean> {
|
||||
const response = (await this.getApiResponse())[name];
|
||||
if (response === undefined) {
|
||||
this.logger.debug(
|
||||
`Feature flag '${name}' undefined in API response, considering it disabled.`
|
||||
);
|
||||
}
|
||||
return response || false;
|
||||
}
|
||||
|
||||
private async getApiResponse(): Promise<FeatureFlagsApiResponse> {
|
||||
const loadApiResponse = async () => {
|
||||
// Do nothing when not running against github.com
|
||||
if (this.gitHubVersion.type !== util.GitHubVariant.DOTCOM) {
|
||||
this.logger.debug(
|
||||
"Not running against github.com. Disabling all feature flags."
|
||||
);
|
||||
return {};
|
||||
}
|
||||
const client = getApiClient(this.apiDetails);
|
||||
const repositoryNwo = parseRepositoryNwo(
|
||||
util.getRequiredEnvParam("GITHUB_REPOSITORY")
|
||||
);
|
||||
try {
|
||||
const response = await client.request(
|
||||
"GET /repos/:owner/:repo/code-scanning/codeql-action/features",
|
||||
{
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this.logger.info(
|
||||
`Disabling all feature flags due to unknown error: ${e}`
|
||||
);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const apiResponse = this.cachedApiResponse || (await loadApiResponse());
|
||||
this.cachedApiResponse = apiResponse;
|
||||
return apiResponse;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { TestInterface } from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import * as CodeQL from "./codeql";
|
||||
import { Logger } from "./logging";
|
||||
|
||||
type TestContext = {
|
||||
stdoutWrite: any;
|
||||
@@ -89,3 +90,32 @@ export function setupActionsVars(tempDir: string, toolsDir: string) {
|
||||
process.env["RUNNER_TEMP"] = tempDir;
|
||||
process.env["RUNNER_TOOL_CACHE"] = toolsDir;
|
||||
}
|
||||
|
||||
export interface LoggedMessage {
|
||||
type: "debug" | "info" | "warning" | "error";
|
||||
message: string | Error;
|
||||
}
|
||||
|
||||
export function getRecordingLogger(messages: LoggedMessage[]): Logger {
|
||||
return {
|
||||
debug: (message: string) => {
|
||||
messages.push({ type: "debug", message });
|
||||
console.debug(message);
|
||||
},
|
||||
info: (message: string) => {
|
||||
messages.push({ type: "info", message });
|
||||
console.info(message);
|
||||
},
|
||||
warning: (message: string | Error) => {
|
||||
messages.push({ type: "warning", message });
|
||||
console.warn(message);
|
||||
},
|
||||
error: (message: string | Error) => {
|
||||
messages.push({ type: "error", message });
|
||||
console.error(message);
|
||||
},
|
||||
isDebug: () => true,
|
||||
startGroup: () => undefined,
|
||||
endGroup: () => undefined,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user