From 867b72e4dbc5b73ef483b9d1102121bcf22ab347 Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Mon, 2 Sep 2024 12:50:21 -0400 Subject: [PATCH] gh-actions: Add lint workflow for new-log-viewer. (#61) --- .github/workflows/lint.yaml | 44 +++++++++++++++++++ new-log-viewer/package.json | 4 ++ .../components/Editor/MonacoInstance/utils.ts | 1 + .../src/contexts/StateContextProvider.tsx | 33 +++++++++----- .../src/services/decoders/JsonlDecoder.ts | 1 + .../services/formatters/LogbackFormatter.ts | 1 + 6 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/lint.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 00000000..73b7542e --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,44 @@ +name: "lint" + +on: + pull_request: + types: ["opened", "reopened"] + push: + schedule: + # Run at midnight UTC every day with 15 minutes delay added to avoid high load periods + - cron: "15 0 * * *" + workflow_dispatch: + +permissions: + # So the workflow can cancel in-progress jobs + actions: "write" + +concurrency: + group: "${{github.workflow}}-${{github.ref}}" + # Cancel in-progress jobs for efficiency + cancel-in-progress: true + +jobs: + lint-check: + runs-on: "ubuntu-latest" + permissions: + # So `eslint-annotate-action` can create / update status checks + checks: "write" + # So `eslint-annotate-action` can get pull request files + contents: "read" + steps: + - uses: "actions/checkout@v4" + with: + submodules: "recursive" + - uses: "actions/setup-node@v4" + with: + node-version: 22 + - run: "npm --prefix new-log-viewer/ clean-install" + - run: "npm --prefix new-log-viewer/ run lint:ci" + continue-on-error: true + - uses: "ataylorme/eslint-annotate-action@v3" + with: + fail-on-error: true + fail-on-warning: true + only-pr-files: true + report-json: "./new-log-viewer/eslint-report.json" diff --git a/new-log-viewer/package.json b/new-log-viewer/package.json index 607f0756..ee1cfae1 100644 --- a/new-log-viewer/package.json +++ b/new-log-viewer/package.json @@ -5,6 +5,10 @@ "main": "src/index.tsx", "scripts": { "build": "webpack --config webpack.prod.js", + "lint": "npm run lint:check", + "lint:ci": "npm run lint:check -- --output-file eslint-report.json --format json", + "lint:check": "eslint src webpack.*.js", + "lint:fix": "npm run lint:check -- --fix", "start": "webpack serve --open --config webpack.dev.js" }, "repository": { diff --git a/new-log-viewer/src/components/Editor/MonacoInstance/utils.ts b/new-log-viewer/src/components/Editor/MonacoInstance/utils.ts index 45d8e24e..16b8e42c 100644 --- a/new-log-viewer/src/components/Editor/MonacoInstance/utils.ts +++ b/new-log-viewer/src/components/Editor/MonacoInstance/utils.ts @@ -41,6 +41,7 @@ const createMonacoEditor = ( const editor = monaco.editor.create( editorContainer, { + // eslint-disable-next-line no-warning-comments // TODO: Add custom observer to debounce automatic layout automaticLayout: true, maxTokenizationLineLength: 30_000, diff --git a/new-log-viewer/src/contexts/StateContextProvider.tsx b/new-log-viewer/src/contexts/StateContextProvider.tsx index e84e46bf..bf3d4ec0 100644 --- a/new-log-viewer/src/contexts/StateContextProvider.tsx +++ b/new-log-viewer/src/contexts/StateContextProvider.tsx @@ -99,6 +99,23 @@ const getLastLogEventNum = (beginLineNumToLogEventNum: BeginLineNumToLogEventNum return lastLogEventNum; }; +/** + * Sends a post message to a worker with the given code and arguments. This wrapper around + * `worker.postMessage()` ensures type safety for both the request code and its corresponding + * arguments. + * + * @param worker + * @param code + * @param args + */ +const workerPostReq = ( + worker: Worker, + code: T, + args: WorkerReq +) => { + worker.postMessage({code, args}); +}; + /** * Provides state management for the application. This provider must be wrapped by * UrlContextProvider to function correctly. @@ -121,13 +138,6 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { const mainWorkerRef = useRef(null); - const mainWorkerPostReq = useCallback(( - code: T, - args: WorkerReq - ) => { - mainWorkerRef.current?.postMessage({code, args}); - }, []); - const handleMainWorkerResp = useCallback((ev: MessageEvent) => { const {code, args} = ev.data; console.log(`[MainWorker -> Renderer] code=${code}`); @@ -144,6 +154,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { break; } case WORKER_RESP_CODE.NOTIFICATION: + // eslint-disable-next-line no-warning-comments // TODO: notifications should be shown in the UI when the NotificationProvider // is added console.error(args.logLevel, args.message); @@ -165,7 +176,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { new URL("../services/MainWorker.ts", import.meta.url) ); mainWorkerRef.current.onmessage = handleMainWorkerResp; - mainWorkerPostReq(WORKER_REQ_CODE.LOAD_FILE, { + workerPostReq(mainWorkerRef.current, WORKER_REQ_CODE.LOAD_FILE, { fileSrc: fileSrc, pageSize: getConfig(CONFIG_KEY.PAGE_SIZE), cursor: cursor, @@ -173,7 +184,6 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { }); }, [ handleMainWorkerResp, - mainWorkerPostReq, ]); // Synchronize `logEventNumRef` with `logEventNum`. @@ -192,7 +202,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { // On `logEventNum` update, clamp it then switch page if necessary or simply update the URL. useEffect(() => { - if (URL_HASH_PARAMS_DEFAULT.logEventNum === logEventNum) { + if (null === mainWorkerRef.current || URL_HASH_PARAMS_DEFAULT.logEventNum === logEventNum) { return; } @@ -211,7 +221,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { // NOTE: We don't need to call `updateLogEventNumInUrl()` since it's called when // handling the `WORKER_RESP_CODE.PAGE_DATA` response (the response to // `WORKER_REQ_CODE.LOAD_PAGE` requests) . - mainWorkerPostReq(WORKER_REQ_CODE.LOAD_PAGE, { + workerPostReq(mainWorkerRef.current, WORKER_REQ_CODE.LOAD_PAGE, { cursor: {code: CURSOR_CODE.PAGE_NUM, args: {pageNum: newPageNum}}, decoderOptions: getConfig(CONFIG_KEY.DECODER_OPTIONS), }); @@ -222,7 +232,6 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { }, [ numEvents, logEventNum, - mainWorkerPostReq, ]); // On `filePath` update, load file. diff --git a/new-log-viewer/src/services/decoders/JsonlDecoder.ts b/new-log-viewer/src/services/decoders/JsonlDecoder.ts index d1ca1469..62a5b21e 100644 --- a/new-log-viewer/src/services/decoders/JsonlDecoder.ts +++ b/new-log-viewer/src/services/decoders/JsonlDecoder.ts @@ -82,6 +82,7 @@ class JsonlDecoder implements Decoder { return null; } + // eslint-disable-next-line no-warning-comments // TODO We could probably optimize this to avoid checking `#invalidLogEventIdxToRawLine` on // every iteration. const results: DecodeResultType[] = []; diff --git a/new-log-viewer/src/services/formatters/LogbackFormatter.ts b/new-log-viewer/src/services/formatters/LogbackFormatter.ts index 6f827c23..5d310df1 100644 --- a/new-log-viewer/src/services/formatters/LogbackFormatter.ts +++ b/new-log-viewer/src/services/formatters/LogbackFormatter.ts @@ -154,6 +154,7 @@ class LogbackFormatter implements Formatter { * @return The formatted string. */ #formatVariables (formatString: string, logEvent: JsonObject): string { + // eslint-disable-next-line no-warning-comments // TODO These don't handle the case where a variable value may contain a '%' itself for (const key of this.#keys) { if (false === (key in logEvent)) {