-
Notifications
You must be signed in to change notification settings - Fork 449
Expand file tree
/
Copy pathanalyses.ts
More file actions
170 lines (150 loc) · 6.07 KB
/
analyses.ts
File metadata and controls
170 lines (150 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import {
fixCodeQualityCategory,
getOptionalInput,
getRequiredInput,
} from "./actions-util";
import { Logger } from "./logging";
import { ConfigurationError } from "./util";
export enum AnalysisKind {
CodeScanning = "code-scanning",
CodeQuality = "code-quality",
}
// Exported for testing. A set of all known analysis kinds.
export const supportedAnalysisKinds = new Set(Object.values(AnalysisKind));
/**
* Parses a comma-separated string into a list of unique analysis kinds.
* Throws a configuration error if the input contains unknown analysis kinds
* or doesn't contain at least one element.
*
* @param input The comma-separated string to parse.
* @returns The array of unique analysis kinds that were parsed from the input string.
*/
export async function parseAnalysisKinds(
input: string,
): Promise<AnalysisKind[]> {
const components = input.split(",");
if (components.length < 1) {
throw new ConfigurationError(
"At least one analysis kind must be configured.",
);
}
for (const component of components) {
if (!supportedAnalysisKinds.has(component as AnalysisKind)) {
throw new ConfigurationError(`Unknown analysis kind: ${component}`);
}
}
// Return all unique elements.
return Array.from(
new Set(components.map((component) => component as AnalysisKind)),
);
}
// Used to avoid re-parsing the input after we have done it once.
let cachedAnalysisKinds: AnalysisKind[] | undefined;
/**
* Initialises the analysis kinds for the analysis based on the `analysis-kinds` input.
* This function will also use the deprecated `quality-queries` input as an indicator to enable `code-quality`.
* If the `analysis-kinds` input cannot be parsed, a `ConfigurationError` is thrown.
*
* @param logger The logger to use.
* @param skipCache For testing, whether to ignore the cached values (default: false).
*
* @returns The array of enabled analysis kinds.
* @throws A `ConfigurationError` if the `analysis-kinds` input cannot be parsed.
*/
export async function getAnalysisKinds(
logger: Logger,
skipCache: boolean = false,
): Promise<AnalysisKind[]> {
if (!skipCache && cachedAnalysisKinds !== undefined) {
return cachedAnalysisKinds;
}
cachedAnalysisKinds = await parseAnalysisKinds(
getRequiredInput("analysis-kinds"),
);
// Warn that `quality-queries` is deprecated if there is an argument for it.
const qualityQueriesInput = getOptionalInput("quality-queries");
if (qualityQueriesInput !== undefined) {
logger.warning(
"The `quality-queries` input is deprecated and will be removed in a future version of the CodeQL Action. " +
"Use the `analysis-kinds` input to configure different analysis kinds instead.",
);
}
// For backwards compatibility, add Code Quality to the enabled analysis kinds
// if an input to `quality-queries` was specified. We should remove this once
// `quality-queries` is no longer used.
if (
!cachedAnalysisKinds.includes(AnalysisKind.CodeQuality) &&
qualityQueriesInput !== undefined
) {
cachedAnalysisKinds.push(AnalysisKind.CodeQuality);
}
return cachedAnalysisKinds;
}
/** The queries to use for Code Quality analyses. */
export const codeQualityQueries: string[] = ["code-quality"];
// Enumerates API endpoints that accept SARIF files.
export enum SARIF_UPLOAD_ENDPOINT {
CODE_SCANNING = "PUT /repos/:owner/:repo/code-scanning/analysis",
CODE_QUALITY = "PUT /repos/:owner/:repo/code-quality/analysis",
}
// Represents configurations for different analysis kinds.
export interface AnalysisConfig {
/** The analysis kind the configuration is for. */
kind: AnalysisKind;
/** A display friendly name for logs. */
name: string;
/** The API endpoint to upload SARIF files to. */
target: SARIF_UPLOAD_ENDPOINT;
/** The file extension for SARIF files generated by this kind of analysis. */
sarifExtension: string;
/** A predicate on filenames to decide whether a SARIF file
* belongs to this kind of analysis. */
sarifPredicate: (name: string) => boolean;
/** Analysis-specific adjustment of the category. */
fixCategory: (logger: Logger, category?: string) => string | undefined;
/** A prefix for environment variables used to track the uniqueness of SARIF uploads. */
sentinelPrefix: string;
}
// Represents the Code Scanning analysis configuration.
export const CodeScanning: AnalysisConfig = {
kind: AnalysisKind.CodeScanning,
name: "code scanning",
target: SARIF_UPLOAD_ENDPOINT.CODE_SCANNING,
sarifExtension: ".sarif",
sarifPredicate: (name) =>
name.endsWith(CodeScanning.sarifExtension) &&
!CodeQuality.sarifPredicate(name),
fixCategory: (_, category) => category,
sentinelPrefix: "CODEQL_UPLOAD_SARIF_",
};
// Represents the Code Quality analysis configuration.
export const CodeQuality: AnalysisConfig = {
kind: AnalysisKind.CodeQuality,
name: "code quality",
target: SARIF_UPLOAD_ENDPOINT.CODE_QUALITY,
sarifExtension: ".quality.sarif",
sarifPredicate: (name) => name.endsWith(CodeQuality.sarifExtension),
fixCategory: fixCodeQualityCategory,
sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_",
};
/**
* Gets the `AnalysisConfig` corresponding to `kind`.
* @param kind The analysis kind to get the `AnalysisConfig` for.
* @returns The `AnalysisConfig` corresponding to `kind`.
*/
export function getAnalysisConfig(kind: AnalysisKind): AnalysisConfig {
// Using a switch statement here accomplishes two things:
// 1. The type checker believes us that we have a case for every `AnalysisKind`.
// 2. If we ever add another member to `AnalysisKind`, the type checker will alert us that we have to add a case.
switch (kind) {
case AnalysisKind.CodeScanning:
return CodeScanning;
case AnalysisKind.CodeQuality:
return CodeQuality;
}
}
// Since we have overlapping extensions (i.e. ".sarif" includes ".quality.sarif"),
// we want to scan a folder containing SARIF files in an order that finds the more
// specific extensions first. This constant defines an array in the order of analyis
// configurations with more specific extensions to less specific extensions.
export const SarifScanOrder = [CodeQuality, CodeScanning];