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
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
@@ -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.
69 changes: 67 additions & 2 deletions common/module/src/config/ACConfigManager.ts
Original file line number Diff line number Diff line change
@@ -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.
@@ -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.
@@ -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.
@@ -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) {
@@ -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;
}
@@ -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.
2 changes: 1 addition & 1 deletion common/module/src/config/IArchive.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
*****************************************************************************/

export interface IArchive {
id: "latest" | "preview" | string
id: "latest" | "preview" | "versioned" | string
name: string
path: string
policies: Array<{
3 changes: 2 additions & 1 deletion common/module/src/config/IConfig.ts
Original file line number Diff line number Diff line change
@@ -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.
@@ -171,6 +171,7 @@ export type IConfigInternal = IConfig & {

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

scanID?: string

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
@@ -70,7 +70,8 @@ const myConfig : IConfigInternal = {
"Chrome",
"master",
"IBMa-Node-TeSt"
]
],
engineMode: "DEFAULT"
}

const rulesets = [
3 changes: 3 additions & 0 deletions karma-accessibility-checker/src/README.md
Original file line number Diff line number Diff line change
@@ -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