CEC-4854 Trunk-based deploy pipeline (#412)

* CEC-4855: fix manifest deselect (#410)

* fix manifest deselect

* adjusted blackduck pipeline to run the latest detect version

* added blackduck_rapid pipeline to run synopsys detect rapid scans

* adjusted deploy pipeline to trun-based model, adjusted test pipeline to use main branch

* test image builds

* clean up

* CEC-4563: add cancel and include results in promise (#411)

* splited build and deploy order according to each environment, test builds

* clean up

* clean up

* CEC-4635: prevent false 0 calculation (#413)

* prevent false 0 calculation

* refactor switch statement

---------

Co-authored-by: Tristan Timblin <ttimblin@fiskerinc.com>
This commit is contained in:
Milamary
2023-08-14 14:09:15 -05:00
committed by GitHub
parent 5b0d3a9437
commit e1f4da2232
9 changed files with 476 additions and 131 deletions

View File

@@ -127,8 +127,16 @@ export const CarUpdatesProvider = ({ children }) => {
};
const getDownloadProgress = (status) => {
if (status.package_total > 0)
const disabled = status.status === "install_succeeded";
if (disabled) {
return -1;
}
const calculated = status.package_total > 0;
if (calculated) {
return Math.floor((100 * status.package_current) / status.package_total);
}
return 0;
};

View File

@@ -274,12 +274,10 @@ const MainForm = () => {
const handleSelect = (event, manifest) => {
setUpdateManifestIds((selected) => {
if (event.target.checked && selected.find((id) => id === manifest.id)) {
return selected;
} else if (event.target.checked) {
if (event.target.checked) {
return [...selected, manifest.id];
}
return selected.filter(({ id }) => id !== manifest.id);
return selected.filter(id => id !== manifest.id);
});
};

View File

@@ -3,6 +3,7 @@ jest.mock("../../Contexts/UserContext");
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from '@testing-library/user-event';
import { BrowserRouter } from "react-router-dom";
import { UserProvider, setToken } from "../../Contexts/UserContext";
@@ -32,4 +33,14 @@ describe("Manifest List Component", () => {
fireEvent.click(screen.getByText("Archived"));
expect(archiveActionEl.innerHTML).toBe("Activate");
});
it("properly selects and deselects a manifest", () => {
const { queryAllByRole } = render(Page);
const checkbox = queryAllByRole("checkbox")[0];
expect(checkbox).not.toBeChecked();
userEvent.click(checkbox);
expect(checkbox).toBeChecked();
userEvent.click(checkbox);
expect(checkbox).not.toBeChecked();
})
});

View File

@@ -8,7 +8,7 @@ export default class TaskRunner {
if (total) {
this._total = total;
this._responses = new Array(total);
this._responses = new Array(total).fill(undefined);
}
this._onComplete = new Promise((resolve, reject) => {
@@ -18,7 +18,8 @@ export default class TaskRunner {
}
execute() {
if (this._running >= this._concurrencyLimit || this._queue.length === 0) {
const isBusy = this._running >= this._concurrencyLimit || this._queue.length === 0;
if (isBusy || this._paused) {
return;
}
@@ -38,10 +39,16 @@ export default class TaskRunner {
}
resolve(response);
} catch (error) {
if (this._responses) {
this._responses[index] = new Error(`Task was rejected: ${error}`);
}
reject(error);
} finally {
this._running -= 1;
this.#progress();
this._complete += 1;
if (this._complete === this._total) {
this._onCompleteResolve(this._responses);
}
this.execute();
}
}
@@ -51,17 +58,15 @@ export default class TaskRunner {
});
}
#progress() {
this._complete += 1;
if (this._complete === this._total) {
this._onCompleteResolve(this._responses);
}
}
async onComplete() {
if (!this._total) {
this._onCompleteReject(new Error("Total is required to determine onComplete."));
}
return this._onComplete;
}
cancel() {
this._concurrencyLimit = 0;
this._onCompleteReject(this._responses);
}
}

View File

@@ -4,6 +4,9 @@ const mockPromise = async (id, ms) => {
await new Promise(resolve => setTimeout(resolve, ms));
return id;
}
const mockPromiseReject = async (id, ms) => {
return new Promise((_, reject) => setTimeout(reject(id), ms));
}
const mockPromiseError = async (id, ms) => {
await new Promise(resolve => setTimeout(resolve, ms));
return new Error(`Task ${id} had an error`);
@@ -44,19 +47,10 @@ describe("TaskRunner", () => {
it("runs tasks in order", async () => {
const actual = [];
const taskRunner = new TaskRunner(2);
taskRunner.push(asyncFn1)
.then((id) => {
actual.push(id);
});
taskRunner.push(asyncFn2)
.then((id) => {
actual.push(id);
});
taskRunner.push(asyncFn3)
.then((id) => {
actual.push(id);
});
await new Promise(resolve => setTimeout(resolve, 500));
taskRunner.push(asyncFn1).then(id => actual.push(id));
taskRunner.push(asyncFn2).then(id => actual.push(id));
taskRunner.push(asyncFn3).then(id => actual.push(id));
await new Promise(resolve => setTimeout(resolve, 200));
expect(actual).toEqual([2, 3, 1]);
});
@@ -72,6 +66,20 @@ describe("TaskRunner", () => {
});
});
it("resolves a promise when all tasks are complete, even if some are rejected", async () => {
const error = new Error(`Task was rejected: 3`);
const taskRunner = new TaskRunner(2, 5);
taskRunner.push(() => mockPromise(1, 600));
taskRunner.push(() => mockPromise(2, 300));
taskRunner.push(() => mockPromiseReject(3, 200)).catch(payload => expect(payload).toBe(3));
taskRunner.push(() => mockPromise(4, 600));
taskRunner.push(() => mockPromise(5, 100));
await taskRunner.onComplete().then((response) => {
expect(response).toStrictEqual([1, 2, error, 4, 5]);
});
});
it("resolves a promise when all tasks are complete, even if some fail", async () => {
const error = new Error(`Task 3 had an error`);
const taskRunner = new TaskRunner(2, 5);
@@ -85,7 +93,7 @@ describe("TaskRunner", () => {
});
});
it("rejects a promise when the total number of tasks is unknown", async () => {
it("immediately rejects onComplete when the total number of tasks is unknown", async () => {
const taskRunner = new TaskRunner(2);
taskRunner.push(() => mockPromise(1, 600));
taskRunner.push(() => mockPromise(2, 300));
@@ -96,4 +104,21 @@ describe("TaskRunner", () => {
expect(error.message).toBe("Total is required to determine onComplete.");
});
});
it("cancels a task runner and returns progress", async () => {
const taskRunner = new TaskRunner(2, 5);
taskRunner.push(() => mockPromise(1, 600));
taskRunner.push(() => mockPromise(2, 300));
taskRunner.push(() => mockPromise(3, 200));
taskRunner.push(() => mockPromise(4, 600));
taskRunner.push(() => mockPromise(5, 100));
setTimeout(() => {
taskRunner.cancel();
}, 550);
await taskRunner.onComplete().catch((response) => {
expect(response).toStrictEqual([undefined, 2, 3, undefined, undefined]);
});
});
});