diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 79d33ad..6aae5fb 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,20 @@ ## ๐Ÿ“Ž Related issues -- resolve # +- resolve # + +## ๐Ÿ“ฆ Scope + + + +- [ ] @patchlogr/core +- [ ] @patchlogr/cli +- [ ] @patchlogr/inspector +- [ ] @patchlogr/oas +- [ ] @patchlogr/types +- [ ] docs, examples +- [ ] tests +- [ ] ci / cd / infra +- [ ] other (์•„๋ž˜์— ๋ช…์‹œ) ## ๐Ÿ“ฆ Scope @@ -35,6 +49,6 @@ ## โœ… Checklist -- [ ] ์š”๊ตฌ์‚ฌํ•ญ ๋ช…์„ธ ์ถฉ์กฑ -- [ ] ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ / ์ˆ˜์ • -- [ ] deterministic output ํ™•์ธ +- [ ] ์š”๊ตฌ์‚ฌํ•ญ ๋ช…์„ธ ์ถฉ์กฑ +- [ ] ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ / ์ˆ˜์ • +- [ ] deterministic output ํ™•์ธ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1451d49 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,92 @@ +name: Continuous Integration + +on: + pull_request: + types: [opened, synchronize] + workflow_dispatch: + +permissions: + contents: read + checks: write + actions: read + pull-requests: write + +env: + NODE_VERSION: "24" + +jobs: + build-and-test: + name: Build & Test + timeout-minutes: 15 + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Enable Corepack (Yarn Berry) + run: corepack enable + + - name: Setup Yarn Berry + run: yarn set version 4.12.0 + + - name: Restore Yarn Cache + uses: actions/cache@v4 + with: + path: | + .yarn/cache + .yarn/unplugged + .yarn/build-state.yml + .pnp.cjs + key: yarn-${{runner.os}}-${{hashFiles('**/yarn.lock')}} + restore-keys: yarn-${{runner.os}}- + + - name: Install Dependencies (Yarn Berry) + run: yarn install --immutable + + - name: Restore Turborepo Cache + uses: actions/cache@v4 + with: + path: .turbo + key: turbo-${{runner.os}}-${{hashFiles('turbo.json', 'package.json', '**/package.json')}} + restore-keys: turbo-${{runner.os}}- + + - name: Run Typecheck + run: yarn typecheck + + - name: Run Lint + run: yarn lint + + - name: Run Tests + run: yarn test + + - name: Report Test Logs + uses: dorny/test-reporter@v1 + if: (!cancelled()) + with: + name: Test Logs + path: "**/.coverage/report.xml" + reporter: java-junit + + - name: Upload Test Artifacts + uses: actions/upload-artifact@v4 + if: (!cancelled()) + with: + name: Test Reports + path: | + **/.coverage/report.xml + **/coverage/**/* + retention-days: 7 + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: (!cancelled()) + with: + files: "**/.coverage/report.xml" diff --git a/.gitignore b/.gitignore index 6ad3ff7..19b2169 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,7 @@ node_modules .turbo # build files -dist \ No newline at end of file +dist + +# test files +.coverage \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 59f15a8..c096547 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,6 +13,7 @@ export default tseslint.config( "**/coverage/**", "**/.yarn/**", "**/.pnp.*", + "**/*.mjs", ], }, js.configs.recommended, diff --git a/package.json b/package.json index 179bc9b..06041c9 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "packages/*" ], "scripts": { - "dev": "turbo run dev", - "build": "turbo run build", - "test": "turbo run test", - "lint": "turbo run lint", - "typecheck": "turbo run typecheck", + "dev": "turbo run dev --cache-dir=.turbo", + "build": "turbo run build --cache-dir=.turbo", + "test": "turbo run test --cache-dir=.turbo", + "lint": "turbo run lint --cache-dir=.turbo", + "typecheck": "turbo run typecheck --cache-dir=.turbo", "clean": "turbo run clean && rm -rf node_modules .turbo" }, "devDependencies": { @@ -18,6 +18,7 @@ "@types/node": "24", "@typescript-eslint/eslint-plugin": "^8.51.0", "@typescript-eslint/parser": "^8.51.0", + "@vitest/ui": "^4.0.16", "esbuild": "0.27.2", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", diff --git a/packages/patchlogr-oas/package.json b/packages/patchlogr-oas/package.json index 0386eef..0375a7d 100644 --- a/packages/patchlogr-oas/package.json +++ b/packages/patchlogr-oas/package.json @@ -6,7 +6,8 @@ }, "scripts": { "lint": "eslint .", - "test": "vitest" + "test": "vitest", + "typecheck": "tsc --noEmit" }, "devDependencies": { "@types/node": "24", diff --git a/packages/patchlogr-oas/src/pipeline/OASCanonicalizeStage.ts b/packages/patchlogr-oas/src/pipeline/OASCanonicalizeStage.ts index 3882d2c..0fa9a95 100644 --- a/packages/patchlogr-oas/src/pipeline/OASCanonicalizeStage.ts +++ b/packages/patchlogr-oas/src/pipeline/OASCanonicalizeStage.ts @@ -3,7 +3,7 @@ import { OASStageContext } from "./OASStageContext"; import { PipelineStage } from "./PipelineExecutor"; import { canonicalizeOASV2 } from "../canonicalize/v2"; import { canonicalizeOASV3 } from "../canonicalize/v3"; -import { isOpenAPIV2, isOpenAPIV3 } from "../utils/OASVersionUtils"; +import { isOpenAPIV2, isOpenAPIV3 } from "../utils/oasVersionUtils"; /** * ํ‘œ์ค€ํ™”๋œ CanonicalSpec๋กœ ๋ณ€ํ™˜ diff --git a/packages/patchlogr-oas/src/pipeline/OASValidationStage.ts b/packages/patchlogr-oas/src/pipeline/OASValidationStage.ts index 8be8ff3..ba36ac1 100644 --- a/packages/patchlogr-oas/src/pipeline/OASValidationStage.ts +++ b/packages/patchlogr-oas/src/pipeline/OASValidationStage.ts @@ -2,7 +2,7 @@ import SwaggerParser from "@apidevtools/swagger-parser"; import { PipelineStage } from "./PipelineExecutor"; import { OASStageContext } from "./OASStageContext"; -import { getOASVersion } from "../utils/OASVersionUtils"; +import { getOASVersion } from "../utils/oasVersionUtils"; /** * OAS ๋ฌธ์„œ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ diff --git a/packages/patchlogr-oas/src/utils/OASVersionUtils.ts b/packages/patchlogr-oas/src/utils/OASVersionUtils.ts deleted file mode 100644 index 896d5bf..0000000 --- a/packages/patchlogr-oas/src/utils/OASVersionUtils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { OpenAPI, OpenAPIV2, OpenAPIV3 } from "openapi-types"; - -export function getOASVersion(doc: OpenAPI.Document): string | undefined { - if (isOpenAPIV3(doc)) { - return doc.openapi; - } else if (isOpenAPIV2(doc)) { - return doc.swagger; - } - return undefined; -} - -export function isOpenAPIV2(doc: OpenAPI.Document): doc is OpenAPIV2.Document { - return "swagger" in doc && doc.swagger === "2.0"; -} - -export function isOpenAPIV3(doc: OpenAPI.Document): doc is OpenAPIV3.Document { - return "openapi" in doc && doc.openapi.startsWith("3."); -} diff --git a/packages/patchlogr-oas/src/utils/__tests__/OASVersionUtils.test.ts b/packages/patchlogr-oas/src/utils/__tests__/OASVersionUtils.test.ts deleted file mode 100644 index b099544..0000000 --- a/packages/patchlogr-oas/src/utils/__tests__/OASVersionUtils.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { describe, expect, test } from "vitest"; -import { getOASVersion, isOpenAPIV2, isOpenAPIV3 } from "../OASVersionUtils"; - -describe("OASVersionUtils", () => { - test("getOASVersion should return correct version for 3.+", () => { - const oasVersion = getOASVersion({ - openapi: "3.0.1", - info: { - title: "Test API", - version: "1.0.0", - }, - paths: {}, - }); - - expect(oasVersion).toBe("3.0.1"); - }); - - test("getOASVersion should return correct version for 2.0", () => { - const oasVersion = getOASVersion({ - swagger: "2.0", - info: { - title: "Test API", - version: "1.0.0", - }, - paths: {}, - }); - - expect(oasVersion).toBe("2.0"); - }); - - test("getOASVersion should return undefined for unknown version", () => { - const oasVersion = getOASVersion({ - info: { - title: "Test API", - version: "1.0.0", - }, - paths: {}, - } as any); - - expect(oasVersion).toBeUndefined(); - }); - - test("isOpenAPIV3 should correctly identify OpenAPI v3 documents", () => { - const isV3 = isOpenAPIV3({ - openapi: "3.0.1", - info: { - title: "Test API", - version: "1.0.0", - }, - paths: {}, - }); - - expect(isV3).toBe(true); - }); - - test("isOpenAPIV2 should correctly identify OpenAPI v2 documents", () => { - const isV2 = isOpenAPIV2({ - swagger: "2.0", - info: { - title: "Test API", - version: "1.0.0", - }, - paths: {}, - }); - - expect(isV2).toBe(true); - }); -}); diff --git a/packages/patchlogr-oas/src/utils/__tests__/oasVersionUtils.test.ts b/packages/patchlogr-oas/src/utils/__tests__/oasVersionUtils.test.ts index b099544..09434fe 100644 --- a/packages/patchlogr-oas/src/utils/__tests__/oasVersionUtils.test.ts +++ b/packages/patchlogr-oas/src/utils/__tests__/oasVersionUtils.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vitest"; -import { getOASVersion, isOpenAPIV2, isOpenAPIV3 } from "../OASVersionUtils"; +import { getOASVersion, isOpenAPIV2, isOpenAPIV3 } from "../oasVersionUtils"; describe("OASVersionUtils", () => { test("getOASVersion should return correct version for 3.+", () => { diff --git a/packages/patchlogr-types/package.json b/packages/patchlogr-types/package.json index 117ed0b..9d2bc6e 100644 --- a/packages/patchlogr-types/package.json +++ b/packages/patchlogr-types/package.json @@ -11,7 +11,7 @@ "scripts": { "build": "tsc", "lint": "eslint .", - "test": "vitest" + "typecheck": "tsc --noEmit" }, "devDependencies": { "@types/node": "24", diff --git a/tsconfig.base.json b/tsconfig.base.json index c058029..e626f22 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -7,6 +7,7 @@ "allowSyntheticDefaultImports": true, "strict": true, + "noImplicitAny": false, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "useUnknownInCatchVariables": true, diff --git a/turbo.json b/turbo.json index 5b86ace..8722a4e 100644 --- a/turbo.json +++ b/turbo.json @@ -22,6 +22,7 @@ "outputs": [] }, "typecheck": { + "dependsOn": ["^build"], "inputs": ["src/**", "package.json", "tsconfig.json"], "outputs": [] }, diff --git a/vitest.config.ts b/vitest.config.ts index 691fb91..1e9844c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -21,5 +21,11 @@ export default defineConfig({ reporter: ["text", "html", "lcov"], reportsDirectory: "./coverage", }, + reporters: ["default", "junit", "json", "html"], + outputFile: { + junit: "./.coverage/report.xml", + json: "./.coverage/report.json", + html: "./.coverage/report.html", + }, }, }); diff --git a/yarn.lock b/yarn.lock index d5fe412..96fcecf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -455,6 +455,13 @@ __metadata: languageName: node linkType: hard +"@polka/url@npm:^1.0.0-next.24": + version: 1.0.0-next.29 + resolution: "@polka/url@npm:1.0.0-next.29" + checksum: 10c0/0d58e081844095cb029d3c19a659bfefd09d5d51a2f791bc61eba7ea826f13d6ee204a8a448c2f5a855c17df07b37517373ff916dd05801063c0568ae9937684 + languageName: node + linkType: hard + "@rollup/rollup-android-arm-eabi@npm:4.54.0": version: 4.54.0 resolution: "@rollup/rollup-android-arm-eabi@npm:4.54.0" @@ -647,12 +654,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:24": - version: 24.10.4 - resolution: "@types/node@npm:24.10.4" +"@types/node@npm:24.10.6": + version: 24.10.6 + resolution: "@types/node@npm:24.10.6" dependencies: undici-types: "npm:~7.16.0" - checksum: 10c0/069639cb7233ee747df1897b5e784f6b6c5da765c96c94773c580aac888fa1a585048d2a6e95eb8302d89c7a9df75801c8b5a0b7d0221d4249059cf09a5f4228 + checksum: 10c0/4c93fb9c4ad38ef0738a34815d84bb091cf6e7af7d51d58f20a8600a8a189edbf0fb54c4f86e344b2199db7bda04fecf17c9d171055ff7a9aa8333b3b96fce4c languageName: node linkType: hard @@ -861,6 +868,23 @@ __metadata: languageName: node linkType: hard +"@vitest/ui@npm:^4.0.16": + version: 4.0.16 + resolution: "@vitest/ui@npm:4.0.16" + dependencies: + "@vitest/utils": "npm:4.0.16" + fflate: "npm:^0.8.2" + flatted: "npm:^3.3.3" + pathe: "npm:^2.0.3" + sirv: "npm:^3.0.2" + tinyglobby: "npm:^0.2.15" + tinyrainbow: "npm:^3.0.3" + peerDependencies: + vitest: 4.0.16 + checksum: 10c0/54bf7fff050784865ad2acbd8b45422468e8d43164f3cd1b5f960d700513f532ee224202d1302a798d63bb5d8e5a68a417176a17ac6ad46cffa21c6b97c6ea32 + languageName: node + linkType: hard + "@vitest/utils@npm:4.0.16": version: 4.0.16 resolution: "@vitest/utils@npm:4.0.16" @@ -1454,6 +1478,13 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.8.2": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 10c0/03448d630c0a583abea594835a9fdb2aaf7d67787055a761515bf4ed862913cfd693b4c4ffd5c3f3b355a70cf1e19033e9ae5aedcca103188aaff91b8bd6e293 + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -1483,7 +1514,7 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.2.9": +"flatted@npm:^3.2.9, flatted@npm:^3.3.3": version: 3.3.3 resolution: "flatted@npm:3.3.3" checksum: 10c0/e957a1c6b0254aa15b8cce8533e24165abd98fadc98575db082b786b5da1b7d72062b81bfdcd1da2f4d46b6ed93bec2434e62333e9b4261d79ef2e75a10dd538 @@ -1882,6 +1913,13 @@ __metadata: languageName: node linkType: hard +"mrmime@npm:^2.0.0": + version: 2.0.1 + resolution: "mrmime@npm:2.0.1" + checksum: 10c0/af05afd95af202fdd620422f976ad67dc18e6ee29beb03dd1ce950ea6ef664de378e44197246df4c7cdd73d47f2e7143a6e26e473084b9e4aa2095c0ad1e1761 + languageName: node + linkType: hard + "ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" @@ -2013,6 +2051,7 @@ __metadata: "@types/node": "npm:24" "@typescript-eslint/eslint-plugin": "npm:^8.51.0" "@typescript-eslint/parser": "npm:^8.51.0" + "@vitest/ui": "npm:^4.0.16" esbuild: "npm:0.27.2" eslint: "npm:^9.39.2" eslint-config-prettier: "npm:^10.1.8" @@ -2273,6 +2312,17 @@ __metadata: languageName: node linkType: hard +"sirv@npm:^3.0.2": + version: 3.0.2 + resolution: "sirv@npm:3.0.2" + dependencies: + "@polka/url": "npm:^1.0.0-next.24" + mrmime: "npm:^2.0.0" + totalist: "npm:^3.0.0" + checksum: 10c0/5930e4397afdb14fbae13751c3be983af4bda5c9aadec832607dc2af15a7162f7d518c71b30e83ae3644b9a24cea041543cc969e5fe2b80af6ce8ea3174b2d04 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -2400,6 +2450,13 @@ __metadata: languageName: node linkType: hard +"totalist@npm:^3.0.0": + version: 3.0.1 + resolution: "totalist@npm:3.0.1" + checksum: 10c0/4bb1fadb69c3edbef91c73ebef9d25b33bbf69afe1e37ce544d5f7d13854cda15e47132f3e0dc4cafe300ddb8578c77c50a65004d8b6e97e77934a69aa924863 + languageName: node + linkType: hard + "ts-api-utils@npm:^2.2.0": version: 2.4.0 resolution: "ts-api-utils@npm:2.4.0"