Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(node|karma|express): Add support for a "versioned" ruleArchive configuration option #1687

Merged
merged 5 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion accessibility-checker/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ options for `accessibility-checker`. Following is the structure of the `.achecke
```yml
# optional - Specify the rule archive
# Default: latest
# Run `npx achecker archives` for a list of valid ruleArchive ids and policy ids
# Run `npx achecker archives` for a list of valid ruleArchive ids and policy ids.
# If "latest", will use the latest rule release
# If "versioned" (supported in 3.1.61+), will use latest rule release at
# the time this version of the tool was released
ruleArchive: latest

# optional - Specify one or many policies to scan.
Expand Down
69 changes: 67 additions & 2 deletions common/module/src/config/ACConfigManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import * as crypto from 'crypto';
import { IConfig, IConfigInternal } from "./IConfig";
import { fetch_get } from "../api-ext/Fetch";
import { ReporterManager } from "../report/ReporterManager";
import { IArchive } from "./IArchive";

/**
* This function is responsible converting policies into an Array based on string or Array.
Expand Down Expand Up @@ -62,6 +63,60 @@ function convertPolicies(policies: string | string[]) : string[] {
return policies;
}

/**
* negative if versionA is less than versionB, positive if versionA is greater than versionB, and zero if they are equal. NaN is treated as 0.
* @param versionA
* @param versionB
*/
export function compareVersions(versionA: string, versionB: string) : number {
const versionRE = /[0-9.]+(-rc\.[0-9]+)?/;
versionA = versionA.trim();
versionB = versionB.trim();
if (!versionRE.test(versionA)) throw new Error("Invalid version");
if (!versionRE.test(versionB)) throw new Error("Invalid version");
if (versionA === versionB) return 0;
// Get x.y.z-rc.a into [x.y.z, a]
// Get x.y.z into [x.y.z]
let split1A = versionA.split("-rc.");
let split1B = versionB.split("-rc.");
// Get x.y.z into [x,y,z]
let split2A = split1A[0].split(".");
let split2B = split1B[0].split(".");
// For the components of the shortest version - can only compare numbers we have
let minLength = Math.min(split2A.length, split2B.length);
for (let idx=0; idx<minLength; ++idx) {
if (split2A[idx] !== split2B[idx]) {
return parseInt(split2A[idx])-parseInt(split2B[idx]);
}
}
// Handle 4.0 vs 4.0.1 (longer string is later)
if (split2A.length !== split2B.length) {
return split2A.length-split2B.length;
}
// Handle 4.0.0 vs 4.0.0-rc.x (shorter string is later)
if (split1A.length !== split1B.length) {
return split1B.length-split1A.length;
}
return parseInt(split1A[1])-parseInt(split1B[1]);
}
/**
*
* @param archives
* @param toolVersion
*/
function findLatestArchiveId(archives: IArchive[], toolVersion: string) {
const validArchiveKeywords = ["latest", "preview", "versioned"];
for (const archive of archives) {
if (validArchiveKeywords.includes(archive.id)) continue;
// If the toolVersion is greater than or equal to the archive version we've found it
if (compareVersions(toolVersion, archive.version) >= 0) {
return archive.id;
}
}
// Something wrong, go with the latest
return "latest";
}

/**
* This function is responsible processing the achecker config which was initialized to make sure it contains,
* information which matches what the engine reads.
Expand All @@ -78,8 +133,9 @@ function convertPolicies(policies: string | string[]) : string[] {
*
* @memberOf this
*/
async function processACConfig(ACConfig) {
async function processACConfig(ACConfig: IConfigInternal) {
ACConstants.DEBUG && console.log("START 'processACConfig' function");
const validArchiveKeywords = ["latest", "preview", "versioned"];

// Convert the reportLevels and failLevels to match with what the engine provides
// Don't need to convert the levels from the input as we are going to compare with out the level.
Expand Down Expand Up @@ -111,6 +167,14 @@ async function processACConfig(ACConfig) {
ACConstants.DEBUG && console.log("Found archiveFile: " + ruleArchiveFile);
ACConfig.ruleArchiveSet = ruleArchiveParse;
let ruleArchive = ACConfig.ruleArchive;
// If the user asked us to sync the rule version with the tool version, we need to figure out what the last rule version was
if (ruleArchive === "versioned") {
if (!ACConfig.toolVersion) {
ruleArchive = "latest";
} else {
ruleArchive = findLatestArchiveId(ACConfig.ruleArchiveSet, ACConfig.toolVersion);
}
}
ACConfig.ruleArchiveLabel = ACConfig.ruleArchive;
for (let i = 0; i < ACConfig.ruleArchiveSet.length; i++) {
if (ruleArchive === ACConfig.ruleArchiveSet[i].id && !ACConfig.ruleArchiveSet[i].sunset) {
Expand All @@ -126,7 +190,7 @@ async function processACConfig(ACConfig) {
throw new Error(errStr);
}
for (let i = 0; i < ACConfig.ruleArchiveSet.length; i++) {
if (ACConfig.ruleArchiveVersion === ACConfig.ruleArchiveSet[i].version && ACConfig.ruleArchiveSet[i].id !== "latest" && ACConfig.ruleArchiveSet[i].id !== "preview") {
if (ACConfig.ruleArchiveVersion === ACConfig.ruleArchiveSet[i].version && !validArchiveKeywords.includes(ACConfig.ruleArchiveSet[i].id)) {
ACConfig.ruleArchivePath = ACConfig.ruleArchiveSet[i].path;
break;
}
Expand Down Expand Up @@ -192,6 +256,7 @@ function initializeDefaults(config: IConfigInternal) {
// Build the toolID based on name and version
config.toolID = packageObject.name + "-v" + packageObject.version;
config.toolName = packageObject.name;
config.toolVersion = packageObject.version;

// Using the uuid module generate a uuid number which is used to assoiciate to the scans that
// are done for a single run of karma.
Expand Down
2 changes: 1 addition & 1 deletion common/module/src/config/IArchive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*****************************************************************************/

export interface IArchive {
id: "latest" | "preview" | string
id: "latest" | "preview" | "versioned" | string
name: string
path: string
policies: Array<{
Expand Down
3 changes: 2 additions & 1 deletion common/module/src/config/IConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export interface IConfig {
* Run `npx achecker archives` for a list of valid ruleArchive ids and policy ids
* Default: "latest"
*/
ruleArchive?: "latest" | "preview" | string
ruleArchive?: "latest" | "preview" | "versioned" | string

/**
* (optional) Specify one or many policies to scan.
Expand Down Expand Up @@ -171,6 +171,7 @@ export type IConfigInternal = IConfig & {

toolID?: string
toolName?: string
toolVersion?: string

scanID?: string

Expand Down
17 changes: 16 additions & 1 deletion common/module/test/config/ACConfigManager.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { ACConfigManager } from "../../src/config/ACConfigManager"
import { ACConfigManager, compareVersions } from "../../src/config/ACConfigManager"
test("Configuration loads", async () => {
const config = await ACConfigManager.getConfig();
// console.log(config);
})

test("Version compare", async () => {
expect(compareVersions("0.0.0", "0.0.0")).toBe(0);
expect(compareVersions("0.0.0", "0.0.1")).toBeLessThan(0);
expect(compareVersions("0.0.0", "0.1.0")).toBeLessThan(0);
expect(compareVersions("0.0.0", "1.0.0")).toBeLessThan(0);
expect(compareVersions("0.0.1", "0.0.0")).toBeGreaterThan(0);
expect(compareVersions("0.1.0", "0.0.0")).toBeGreaterThan(0);
expect(compareVersions("1.0.0", "0.0.0")).toBeGreaterThan(0);
expect(compareVersions("1.0.0", "1.0")).toBeGreaterThan(0);
expect(compareVersions("1.0.0", "1.0.0-rc.0")).toBeGreaterThan(0);
expect(compareVersions("1.0.0-rc.0", "1.0.0")).toBeLessThan(0);
expect(compareVersions("1.0.0-rc.0", "1.0.0-rc.1")).toBeLessThan(0);
expect(compareVersions("1.0.0-rc.1", "1.0.0-rc.0")).toBeGreaterThan(0);
})
3 changes: 2 additions & 1 deletion common/module/test/report/ReportManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ const myConfig : IConfigInternal = {
"Chrome",
"master",
"IBMa-Node-TeSt"
]
],
engineMode: "DEFAULT"
}

const rulesets = [
Expand Down
3 changes: 3 additions & 0 deletions karma-accessibility-checker/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ options for `karma-accessibility-checker`. This is the structure of the `.acheck
# optional - Specify the rule archive
# i.e. For march rule archive use ruleArchive: 2017MayDeploy
# Default: latest
# If "latest", will use the latest rule release
# If "versioned" (supported in 3.1.61+), will use latest rule release at
# the time this version of the tool was released
# Refer to README.md FAQ section below to get the rule archive ID.
ruleArchive: latest

Expand Down
Loading