├── .gitignore ├── bin ├── clean.js ├── update.js ├── clone.js ├── reset.js └── test.js ├── .github └── workflows │ ├── pr-checks.yml │ ├── oxfmt-ci.yml │ └── oxlint-ci.yml ├── oxfmt-matrix.json ├── package.json ├── README.md └── oxlint-matrix.json /.gitignore: -------------------------------------------------------------------------------- 1 | /repos 2 | -------------------------------------------------------------------------------- /bin/clean.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @file clean.js 4 | * @summary prune remote and local branches, then run git garbage collection 5 | */ 6 | const { execEachRepo } = require('../lib/exec'); 7 | 8 | execEachRepo([ 9 | 'git fetch', 10 | 'git remote prune origin', 11 | 'git gc', 12 | ]); 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/pr-checks.yml: -------------------------------------------------------------------------------- 1 | name: PR Checks 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | paths-ignore: 7 | - '**/*.md' 8 | 9 | jobs: 10 | lint: 11 | name: Lint 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 15 | - run: npx oxlint@latest . --format github 16 | -------------------------------------------------------------------------------- /oxfmt-matrix.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "repository": "eggjs/egg", 4 | "ref": "next", 5 | "path": "eggjs/egg", 6 | "command": "oxfmt" 7 | }, 8 | { 9 | "repository": "actualbudget/actual", 10 | "ref": "master", 11 | "path": "actual", 12 | "command": "oxfmt" 13 | }, 14 | { 15 | "repository": "AmanVarshney01/create-better-t-stack", 16 | "ref": "main", 17 | "path": "AmanVarshney01/create-better-t-stack", 18 | "command": "oxfmt" 19 | }, 20 | { 21 | "repository": "cnpm/cnpmcore", 22 | "ref": "master", 23 | "path": "cnpmcore", 24 | "command": "oxfmt" 25 | }, 26 | { 27 | "repository": "monkeytypegame/monkeytype", 28 | "ref": "master", 29 | "path": "monkeytype", 30 | "command": "oxfmt" 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oxc-ecosystem-ci", 3 | "version": "0.0.0", 4 | "description": "Integration tests against other projects using oxlint and oxfmt", 5 | "private": true, 6 | "main": "test.js", 7 | "type": "commonjs", 8 | "scripts": { 9 | "clone:oxlint": "node bin/clone.js", 10 | "test:oxlint": "RUST_BACKTRACE=1 node bin/test.js", 11 | "test:default:oxlint": "RUST_BACKTRACE=1 node bin/test.js $(which oxlint)", 12 | "update:oxlint": "node bin/update.js", 13 | "reset:oxlint": "node bin/reset.js", 14 | "clone:oxfmt": "node bin/clone.js --oxfmt", 15 | "test:oxfmt": "RUST_BACKTRACE=1 node bin/test.js --oxfmt", 16 | "update:oxfmt": "node bin/update.js --oxfmt", 17 | "reset:oxfmt": "node bin/reset.js --oxfmt", 18 | "clean": "node bin/clean.js", 19 | "lint": "npx oxlint@latest ." 20 | }, 21 | "packageManager": "pnpm@10.26.1" 22 | } 23 | -------------------------------------------------------------------------------- /bin/update.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const assert = require("node:assert"); 4 | const exec = require("node:child_process").exec; 5 | const { existsSync } = require("node:fs"); 6 | 7 | // Parse command-line arguments 8 | const args = process.argv.slice(2); 9 | let matrixFile = "../oxlint-matrix.json"; // default to oxlint 10 | 11 | if (args.includes("--oxfmt")) { 12 | matrixFile = "../oxfmt-matrix.json"; 13 | } else if (args.includes("--oxlint")) { 14 | matrixFile = "../oxlint-matrix.json"; 15 | } 16 | 17 | const matrix = require(matrixFile); 18 | 19 | assert( 20 | existsSync("repos"), 21 | "No repositories found, did you forget to run clone.js?", 22 | ); 23 | 24 | console.log(`Using matrix file: ${matrixFile}`); 25 | 26 | for (const item of matrix) { 27 | const command = `cd repos/${item.path} && git pull`; 28 | 29 | console.log(`Running ${command}`); 30 | exec(command, function (err) { 31 | if (err) { 32 | console.error(err); 33 | return; 34 | } 35 | console.log(`Updated ${item.repository}`); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /bin/clone.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const exec = require("child_process").exec; 4 | 5 | // Parse command-line arguments 6 | const args = process.argv.slice(2); 7 | let matrixFile = "../oxlint-matrix.json"; // default to oxlint 8 | 9 | if (args.includes("--oxfmt")) { 10 | matrixFile = "../oxfmt-matrix.json"; 11 | } else if (args.includes("--oxlint")) { 12 | matrixFile = "../oxlint-matrix.json"; 13 | } 14 | 15 | const matrix = require(matrixFile); 16 | 17 | /** 18 | * @typedef {Object} MatrixItem 19 | * @property {string} repository 20 | * @property {string} path 21 | * @property {string} ref 22 | * @property {string} command 23 | * @property {Object} [options] 24 | */ 25 | 26 | /** 27 | * @param {MatrixItem} item 28 | */ 29 | function cloneRepo(item) { 30 | const command = `git clone --depth=1 --filter=blob:none --no-tags -b ${item.ref} git@github.com:${item.repository}.git repos/${item.path}`; 31 | 32 | console.log(`Running ${command}`); 33 | exec(command, function (err) { 34 | if (err) { 35 | console.error(err); 36 | return; 37 | } 38 | console.log(`Cloned ${item.repository}`); 39 | }); 40 | } 41 | 42 | if (require.main === module) { 43 | console.log(`Using matrix file: ${matrixFile}`); 44 | for (const item of matrix) { 45 | cloneRepo(item); 46 | } 47 | } 48 | 49 | module.exports = { cloneRepo }; 50 | -------------------------------------------------------------------------------- /bin/reset.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * @file reset.js 4 | * @summary Forcibly reset all repositories to the latest commit on their 5 | * default branch. All local changes will be lost. 6 | */ 7 | 8 | const { cloneRepo } = require("./clone"); 9 | const fs = require("fs"); 10 | const cp = require("child_process"); 11 | 12 | // Parse command-line arguments 13 | const args = process.argv.slice(2); 14 | let matrixFile = "../oxlint-matrix.json"; // default to oxlint 15 | 16 | if (args.includes("--oxfmt")) { 17 | matrixFile = "../oxfmt-matrix.json"; 18 | } else if (args.includes("--oxlint")) { 19 | matrixFile = "../oxlint-matrix.json"; 20 | } 21 | 22 | const matrix = require(matrixFile); 23 | 24 | /** 25 | * @param {import("./clone").MatrixItem} item 26 | */ 27 | function syncAndResetRepo(item) { 28 | const cmd = [ 29 | `cd repos/${item.path}`, 30 | `git reset --hard`, 31 | `git checkout ${item.ref}`, 32 | `git pull`, 33 | ].join(" && "); 34 | 35 | console.log(`Running ${cmd}`); 36 | cp.execSync(cmd, { stdio: "inherit" }); 37 | } 38 | 39 | function reset() { 40 | // await fs.promises.mkdir("repos", { recursive: true }); 41 | fs.mkdirSync("repos", { recursive: true }); 42 | 43 | console.log(`Using matrix file: ${matrixFile}`); 44 | 45 | let failed = false; 46 | for (const item of matrix) { 47 | if (!fs.existsSync(`repos/${item.path}`)) { 48 | cloneRepo(item); 49 | } else { 50 | try { 51 | syncAndResetRepo(item); 52 | } catch (e) { 53 | console.error(e); 54 | failed = true; 55 | } 56 | } 57 | } 58 | 59 | if (failed) { 60 | process.exit(1); 61 | } 62 | } 63 | 64 | if (require.main === module) { 65 | reset(); 66 | } 67 | -------------------------------------------------------------------------------- /bin/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const assert = require("node:assert"); 4 | const { execSync } = require("node:child_process"); 5 | const { existsSync, writeFileSync } = require("node:fs"); 6 | const { resolve, join } = require("node:path"); 7 | 8 | // Parse command-line arguments 9 | const allArgs = process.argv.slice(2); 10 | let matrixFile = "../oxlint-matrix.json"; // default to oxlint 11 | let binary = null; 12 | let extraArgs = []; 13 | 14 | // Parse flags and arguments 15 | for (let i = 0; i < allArgs.length; i++) { 16 | const arg = allArgs[i]; 17 | if (arg === "--oxfmt") { 18 | matrixFile = "../oxfmt-matrix.json"; 19 | } else if (arg === "--oxlint") { 20 | matrixFile = "../oxlint-matrix.json"; 21 | } else if (!binary) { 22 | binary = arg; 23 | } else { 24 | extraArgs.push(arg); 25 | } 26 | } 27 | 28 | const matrix = require(matrixFile); 29 | 30 | if (!binary) { 31 | console.error( 32 | "USAGE: ./test.js [--oxlint|--oxfmt] PATH_TO_BINARY [EXTRA_ARGS...]", 33 | ); 34 | console.error(" --oxlint: Use matrix.json (default)"); 35 | console.error(" --oxfmt: Use oxfmt-matrix.json"); 36 | process.exit(0); 37 | } 38 | binary = resolve(binary); // normalize relative paths 39 | 40 | assert( 41 | existsSync("repos"), 42 | "No repositories found, did you forget to run clone.js?", 43 | ); 44 | 45 | console.log(`Using matrix file: ${matrixFile}`); 46 | console.log(`Binary: ${binary}`); 47 | 48 | for (const item of matrix) { 49 | const repoPath = join("repos", item.path); 50 | 51 | // Write .oxfmtrc.json config if options are provided (for oxfmt) 52 | if (item.options) { 53 | const configPath = join(repoPath, ".oxfmtrc.json"); 54 | writeFileSync(configPath, JSON.stringify(item.options, null, 2)); 55 | console.log(`Created config at ${configPath}`); 56 | } 57 | 58 | // Replace binary name in command 59 | const commandWithBinary = item.command.replace( 60 | /^(oxfmt|oxlint|\.\/oxlint)/, 61 | binary, 62 | ); 63 | const command = `cd ${repoPath} && ${commandWithBinary} ${extraArgs.join(" ")}`; 64 | console.log(command); 65 | execSync(command, { stdio: "inherit" }); 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Oxc Ecosystem CI 2 | 3 | This repository is used to run integration tests for `oxlint` and `oxfmt` ecosystem projects. The workflows will be run on a scheduled basis and can also be triggered manually. 4 | 5 | ## Oxlint 6 | 7 | ### Maintenance 8 | 9 | * [Boshen](https://github.com/Boshen) may submit a `oxlint` adjustment PR to your repository if it fails to run correctly. 10 | * Due to maintenance burden, a [sponsorship](https://github.com/sponsors/Boshen) will have a more likely hood of having the PR accepted. 11 | 12 | ### Manual github workflow 13 | 14 | * open [workflow](https://github.com/oxc-project/oxc-ecosystem-ci/actions/workflows/oxlint-ci.yml) 15 | * click 'Run workflow' button on top right of the list 16 | * change `ref` for a different branch 17 | 18 | ### Local 19 | 20 | - `pnpm run clone:oxlint` - clones all the repositories 21 | - `pnpm run update:oxlint` - updates (git pull) all the repositories 22 | - `pnpm run test:oxlint /path/to/oxc/target/release/oxlint ARGS` - run `oxlint` 23 | 24 | ### Integrated Repositories 25 | 26 | See [./oxlint-matrix.json](./oxlint-matrix.json). 27 | 28 | Notable repositories: 29 | 30 | * [rolldown/rolldown](https://github.com/rolldown-rs/rolldown) 31 | * [napi-rs/napi-rs](https://github.com/napi-rs/napi-rs) 32 | * [toeverything/affine](https://github.com/toeverything/affine) 33 | * [preactjs/preact](https://github.com/preactjs/preact) 34 | * [microsoft/vscode](https://github.com/microsoft/vscode) 35 | * [bbc/simorgh](https://github.com/bbc/simorgh) 36 | * [elastic/kibana](https://github.com/elastic/kibana) 37 | * [DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) 38 | 39 | ## Oxfmt 40 | 41 | ### What we are checking 42 | 43 | 1. Oxfmt can format the integrated repositories without any differences. 44 | 2. Oxfmt does not drop any code in the formatting process. 45 | 3. Oxfmt does not panic during the formatting process. 46 | 47 | ### Manual github workflow 48 | 49 | * open [workflow](https://github.com/oxc-project/oxc-ecosystem-ci/actions/workflows/oxfmt-ci.yml) 50 | * click 'Run workflow' button on top right of the list 51 | * change `ref` for a different branch 52 | 53 | ### Local 54 | - `pnpm run clone:oxfmt` - clones all the repositories 55 | - `pnpm run update:oxfmt` - updates (git pull) all the repositories 56 | - `pnpm run test:oxfmt /path/to/oxc/target/release/oxfmt ARGS` - run `oxfmt` 57 | 58 | ### Integrated Repositories 59 | 60 | See [./oxfmt-matrix.json](./oxfmt-matrix.json). 61 | 62 |

63 | 64 | My sponsors 65 | 66 |

67 | -------------------------------------------------------------------------------- /oxlint-matrix.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "repository": "rolldown/rolldown", 4 | "ref": "main", 5 | "path": "rolldown", 6 | "command": "oxlint --deny-warnings -c .oxlintrc.json --ignore-path=.oxlintignore --import-plugin --jsdoc-plugin" 7 | }, 8 | { 9 | "repository": "napi-rs/napi-rs", 10 | "ref": "main", 11 | "path": "napi-rs", 12 | "command": "oxlint --deny-warnings --ignore-path=.oxlintignore --import-plugin -D correctness -A no-export" 13 | }, 14 | { 15 | "repository": "toeverything/affine", 16 | "ref": "canary", 17 | "path": "affine", 18 | "command": "oxlint -c oxlint.json --deny-warnings" 19 | }, 20 | { 21 | "repository": "preactjs/preact", 22 | "ref": "main", 23 | "path": "preact", 24 | "command": "oxlint --deny-warnings -c oxlint.json src test/browser test/node test/shared debug compat hooks test-utils" 25 | }, 26 | { 27 | "repository": "microsoft/vscode", 28 | "ref": "main", 29 | "path": "vscode", 30 | "command": "oxlint -W all --ignore-pattern '**/fixtures' --ignore-pattern 'test-*.ts' --silent" 31 | }, 32 | { 33 | "repository": "calcom/cal.com", 34 | "ref": "main", 35 | "path": "cal", 36 | "command": "echo 'skipped'" 37 | }, 38 | { 39 | "repository": "bbc/simorgh", 40 | "ref": "latest", 41 | "path": "simorgh", 42 | "command": "oxlint -D no-unreachable" 43 | }, 44 | { 45 | "repository": "sidebase/nuxt-auth", 46 | "ref": "main", 47 | "path": "nuxt-auth", 48 | "command": "oxlint --deny-warnings -D correctness -D suspicious -D perf" 49 | }, 50 | { 51 | "repository": "elastic/kibana", 52 | "ref": "main", 53 | "path": "kibana", 54 | "command": "echo 'skipped'" 55 | }, 56 | { 57 | "repository": "DefinitelyTyped/DefinitelyTyped", 58 | "ref": "master", 59 | "path": "DefinitelyTyped", 60 | "command": "echo 'skipped'" 61 | }, 62 | { 63 | "repository": "Milkdown/milkdown", 64 | "ref": "main", 65 | "path": "milkdown", 66 | "command": "oxlint -c .oxlintrc.json" 67 | }, 68 | { 69 | "repository": "bluesky-social/social-app", 70 | "ref": "main", 71 | "path": "bluesky", 72 | "command": "echo 'skipped'" 73 | }, 74 | { 75 | "repository": "oven-sh/bun", 76 | "ref": "main", 77 | "path": "bun", 78 | "command": "oxlint -c oxlint.json src/js" 79 | }, 80 | { 81 | "repository": "nestjs/nest", 82 | "ref": "master", 83 | "path": "nest", 84 | "command": "echo 'skipped'" 85 | }, 86 | { 87 | "repository": "vercel/next.js", 88 | "ref": "canary", 89 | "path": "next", 90 | "command": "echo 'skipped'" 91 | }, 92 | { 93 | "repository": "monkeytypegame/monkeytype", 94 | "ref": "master", 95 | "path": "monkeytype", 96 | "command": "oxlint -c ./backend/.oxlintrc.json ./backend && oxlint -c ./frontend/.oxlintrc.json ./frontend" 97 | }, 98 | { 99 | "repository": "PostHog/posthog", 100 | "ref": "master", 101 | "path": "posthog", 102 | "command": "oxlint" 103 | }, 104 | { 105 | "repository": "outline/outline", 106 | "ref": "main", 107 | "path": "outline", 108 | "command": "oxlint" 109 | }, 110 | { 111 | "repository": "date-fns/date-fns", 112 | "ref": "main", 113 | "path": "date-fns", 114 | "command": "oxlint -c .oxlintrc.json" 115 | }, 116 | { 117 | "repository": "actualbudget/actual", 118 | "ref": "master", 119 | "path": "actual", 120 | "command": "oxlint -c .oxlintrc.json" 121 | } 122 | ] 123 | -------------------------------------------------------------------------------- /.github/workflows/oxfmt-ci.yml: -------------------------------------------------------------------------------- 1 | name: oxfmt ecosystem ci 2 | 3 | on: 4 | schedule: 5 | - cron: "30 1,7,13,19 * * *" # Every 6 hours, gaps in the oxlint-ci 6 | pull_request: 7 | types: [opened, synchronize] 8 | paths-ignore: 9 | - "**/*.md" 10 | push: 11 | branches: 12 | - main 13 | paths-ignore: 14 | - "**/*.md" 15 | workflow_dispatch: 16 | inputs: # for create comment action https://github.com/peter-evans/create-or-update-comment?tab=readme-ov-file#action-inputs 17 | issue-number: 18 | required: false 19 | type: string 20 | comment-id: 21 | required: false 22 | type: string 23 | ref: 24 | required: false 25 | type: string 26 | default: "main" 27 | 28 | jobs: 29 | reply: 30 | name: Reply 31 | if: ${{ inputs.issue-number && inputs.comment-id }} 32 | runs-on: ubuntu-latest 33 | permissions: 34 | pull-requests: write 35 | contents: write 36 | steps: 37 | - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 38 | with: 39 | token: ${{ secrets.OXC_BOT_PAT }} 40 | repository: oxc-project/oxc 41 | issue-number: ${{ inputs.issue-number }} 42 | comment-id: ${{ inputs.comment-id }} 43 | reactions: eyes 44 | 45 | build: 46 | name: Build 47 | timeout-minutes: 30 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 51 | with: 52 | repository: oxc-project/oxc 53 | ref: ${{ inputs.ref }} 54 | persist-credentials: false 55 | show-progress: false 56 | 57 | - uses: oxc-project/setup-rust@ecabb7322a2ba5aeedb3612d2a40b86a85cee235 # v1.0.11 58 | with: 59 | save-cache: ${{ github.ref_name == 'main' }} 60 | 61 | - uses: oxc-project/setup-node@141eb77546de6702f92d320926403fe3f9f6a6f2 # v1.0.5 62 | 63 | - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 64 | 65 | - run: pnpm install 66 | 67 | # Equivalent to `pnpm build` with `detect_code_removal` feature 68 | - run: pnpm --filter ./apps/oxfmt run build-napi --release --features allocator,detect_code_removal 69 | env: 70 | RUSTFLAGS: "-C debug-assertions=true" 71 | - run: pnpm --filter ./apps/oxfmt run build-js 72 | 73 | # `pnpm pack` does not work for `apps/oxfmt` because: 74 | # - `.node` is ignored, not being packed 75 | # - Also, `bin` field does not exist 76 | # Instead, using `npm/oxfmt` for global CLI install 77 | - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 78 | with: 79 | if-no-files-found: error 80 | name: oxfmt 81 | path: | 82 | ./apps/oxfmt/dist 83 | ./npm/oxfmt/package.json 84 | ./npm/oxfmt/bin 85 | 86 | checkout: 87 | name: Read oxfmt-matrix.json 88 | needs: build 89 | runs-on: ubuntu-latest 90 | outputs: 91 | matrix: ${{ steps.setmatrix.outputs.content }} 92 | steps: 93 | - uses: taiki-e/checkout-action@b13d20b7cda4e2f325ef19895128f7ff735c0b3d # v1.3.1 94 | 95 | - id: setmatrix 96 | uses: jaywcjlove/github-action-read-file@d7632d78b76cbfbe8841dc31dd08dd2020175c80 # main 97 | with: 98 | localfile: ./oxfmt-matrix.json 99 | 100 | test: 101 | needs: checkout 102 | timeout-minutes: 30 103 | runs-on: ubuntu-latest 104 | strategy: 105 | fail-fast: false 106 | matrix: 107 | include: ${{ fromJson(needs.checkout.outputs.matrix) }} 108 | name: Test ${{ matrix.repository }} 109 | steps: 110 | - name: Clone ${{ matrix.repository }} 111 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 112 | with: 113 | repository: ${{ matrix.repository }} 114 | ref: ${{ matrix.ref }} 115 | path: ${{ matrix.path }} 116 | persist-credentials: false 117 | show-progress: false 118 | 119 | - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 120 | with: 121 | name: oxfmt 122 | path: oxfmt-package 123 | 124 | - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 125 | with: 126 | node-version: 24 127 | 128 | - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 129 | with: 130 | version: 10 131 | 132 | - name: Install oxfmt globally 133 | run: | 134 | # Create package structure 135 | # ``` 136 | # oxfmt-install 137 | # |- package.json 138 | # |- bin/oxfmt 139 | # |- dist/cli.js, *.node, etc 140 | # ``` 141 | mkdir -p oxfmt-install 142 | cp oxfmt-package/npm/oxfmt/package.json oxfmt-install/ 143 | cp -r oxfmt-package/npm/oxfmt/bin oxfmt-install/ 144 | cp -r oxfmt-package/apps/oxfmt/dist oxfmt-install/ 145 | # Dependencies are not installed automatically by this way 146 | cd oxfmt-install && npm install 147 | cd .. 148 | # Install globally 149 | npm install -g ./oxfmt-install 150 | 151 | - name: Run oxfmt 152 | working-directory: ${{ matrix.path }} 153 | run: ${{ matrix.command }} 154 | 155 | - name: Check diff 156 | working-directory: ${{ matrix.path }} 157 | run: | 158 | if [ -n "$(git status --porcelain)" ]; then 159 | echo "Code is not properly formatted. See the diff below:"; 160 | git --no-pager diff; 161 | exit 1; 162 | else 163 | echo "Code is properly formatted."; 164 | fi 165 | 166 | comment: 167 | needs: test 168 | if: ${{ always() }} 169 | runs-on: ubuntu-latest 170 | name: Reply Comment 171 | permissions: 172 | pull-requests: write 173 | contents: write 174 | steps: 175 | - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 176 | id: script 177 | if: ${{ inputs.issue-number }} 178 | with: 179 | github-token: ${{ secrets.OXC_BOT_PAT }} 180 | result-encoding: string 181 | script: | 182 | const { 183 | data: { jobs }, 184 | } = await github.rest.actions.listJobsForWorkflowRun({ 185 | owner: context.repo.owner, 186 | repo: context.repo.repo, 187 | run_id: context.runId, 188 | per_page: 100, 189 | }); 190 | let result = jobs 191 | .filter((job) => job.name.startsWith("Test ")) 192 | .map((job) => { 193 | const suite = job.name.slice(5); 194 | return { suite, conclusion: job.conclusion, link: job.html_url }; 195 | }); 196 | const url = `${context.serverUrl}//${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; 197 | const urlLink = `[Open](${url})`; 198 | const conclusionEmoji = { 199 | success: ":white_check_mark:", 200 | failure: ":x:", 201 | cancelled: ":stop_button:", 202 | }; 203 | const body = ` 204 | ## [Oxfmt Ecosystem CI](${urlLink}) 205 | | suite | result | 206 | |-------|--------| 207 | ${result.map((r) => `| [${r.suite}](${r.link}) | ${conclusionEmoji[r.conclusion]} |`).join("\n")} 208 | `; 209 | return body; 210 | 211 | - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 212 | if: ${{ inputs.issue-number && inputs.comment-id }} 213 | with: 214 | token: ${{ secrets.OXC_BOT_PAT }} 215 | repository: oxc-project/oxc 216 | issue-number: ${{ inputs.issue-number }} 217 | comment-id: ${{ inputs.comment-id }} 218 | body: ${{ steps.script.outputs.result }} 219 | edit-mode: replace 220 | -------------------------------------------------------------------------------- /.github/workflows/oxlint-ci.yml: -------------------------------------------------------------------------------- 1 | name: oxlint ecosystem ci 2 | 3 | on: 4 | schedule: 5 | - cron: "0 */3 * * *" # Every 3 hours. 6 | pull_request: 7 | types: [opened, synchronize] 8 | paths-ignore: 9 | - "**/*.md" 10 | push: 11 | branches: 12 | - main 13 | paths-ignore: 14 | - "**/*.md" 15 | workflow_dispatch: 16 | inputs: # for create comment action https://github.com/peter-evans/create-or-update-comment?tab=readme-ov-file#action-inputs 17 | issue-number: 18 | required: false 19 | type: string 20 | comment-id: 21 | required: false 22 | type: string 23 | ref: 24 | required: false 25 | type: string 26 | default: "main" 27 | 28 | jobs: 29 | reply: 30 | name: Reply 31 | if: ${{ inputs.issue-number && inputs.comment-id }} 32 | runs-on: ubuntu-latest 33 | permissions: 34 | pull-requests: write 35 | contents: write 36 | steps: 37 | - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 38 | with: 39 | token: ${{ secrets.OXC_BOT_PAT }} 40 | repository: oxc-project/oxc 41 | issue-number: ${{ inputs.issue-number }} 42 | comment-id: ${{ inputs.comment-id }} 43 | reactions: eyes 44 | 45 | build: 46 | name: Build 47 | timeout-minutes: 30 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 51 | with: 52 | repository: oxc-project/oxc 53 | ref: ${{ inputs.ref }} 54 | persist-credentials: false 55 | show-progress: false 56 | 57 | - uses: oxc-project/setup-rust@ecabb7322a2ba5aeedb3612d2a40b86a85cee235 # v1.0.11 58 | with: 59 | save-cache: ${{ github.ref_name == 'main' }} 60 | 61 | - uses: oxc-project/setup-node@141eb77546de6702f92d320926403fe3f9f6a6f2 # v1.0.5 62 | 63 | - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 64 | 65 | - run: pnpm install 66 | working-directory: ./apps/oxlint 67 | 68 | - run: pnpm run build 69 | working-directory: ./apps/oxlint 70 | env: 71 | RUSTFLAGS: "-C debug-assertions=true" 72 | 73 | # `pnpm pack` does not work for `apps/oxlint` because: 74 | # - `.node` is ignored, not being packed 75 | # - Also, `bin` field does not exist 76 | # Instead, using `npm/oxlint` for global CLI install 77 | - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 78 | with: 79 | if-no-files-found: error 80 | name: oxlint 81 | path: | 82 | ./apps/oxlint/dist 83 | ./npm/oxlint/package.json 84 | ./npm/oxlint/bin 85 | 86 | checkout: 87 | name: Read oxlint-matrix.json 88 | needs: build 89 | runs-on: ubuntu-latest 90 | outputs: 91 | matrix: ${{ steps.setmatrix.outputs.content }} 92 | steps: 93 | - uses: taiki-e/checkout-action@b13d20b7cda4e2f325ef19895128f7ff735c0b3d # v1.3.1 94 | 95 | - id: setmatrix 96 | uses: jaywcjlove/github-action-read-file@d7632d78b76cbfbe8841dc31dd08dd2020175c80 # main 97 | with: 98 | localfile: ./oxlint-matrix.json 99 | 100 | test: 101 | needs: checkout 102 | timeout-minutes: 30 103 | runs-on: ubuntu-latest 104 | strategy: 105 | fail-fast: false 106 | matrix: 107 | include: ${{ fromJson(needs.checkout.outputs.matrix) }} 108 | name: Test ${{ matrix.repository }} 109 | steps: 110 | - name: Clone ${{ matrix.repository }} 111 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 112 | with: 113 | repository: ${{ matrix.repository }} 114 | ref: ${{ matrix.ref }} 115 | path: ${{ matrix.path }} 116 | persist-credentials: false 117 | show-progress: false 118 | 119 | - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 120 | with: 121 | name: oxlint 122 | path: oxlint-package 123 | 124 | - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 125 | with: 126 | node-version: 24 127 | 128 | - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 129 | with: 130 | version: 10 131 | 132 | - name: Install oxlint globally 133 | run: | 134 | # Create package structure 135 | # ``` 136 | # oxlint-install 137 | # |- package.json 138 | # |- bin/oxlint 139 | # |- dist/cli.js, *.node, etc 140 | # ``` 141 | mkdir -p oxlint-install 142 | cp oxlint-package/npm/oxlint/package.json oxlint-install/ 143 | cp -r oxlint-package/npm/oxlint/bin oxlint-install/ 144 | cp -r oxlint-package/apps/oxlint/dist oxlint-install/ 145 | # Install globally 146 | npm install -g ./oxlint-install 147 | 148 | - name: Run 149 | working-directory: ${{ matrix.path }} 150 | run: ${{ matrix.command }} 151 | 152 | - name: Check Panic 153 | working-directory: ${{ matrix.path }} 154 | run: | 155 | set +e # disable exit on run 156 | 157 | oxlint --fix --import-plugin --jest-plugin --jsdoc-plugin --jsx-a11y-plugin --nextjs-plugin --react-perf-plugin --node-plugin --promise-plugin --vitest-plugin --vue-plugin -D all --silent 158 | 159 | EXIT_CODE=$? 160 | 161 | # Panic returns exit code > 1, e.g. 101 when a Rust program panics 162 | if [ $EXIT_CODE -gt 1 ]; then 163 | echo "exitcode=$EXIT_CODE" >> $GITHUB_OUTPUT 164 | exit $EXIT_CODE 165 | fi 166 | 167 | echo "exitcode=0" >> $GITHUB_OUTPUT 168 | exit 0 169 | 170 | comment: 171 | needs: test 172 | if: ${{ always() }} 173 | runs-on: ubuntu-latest 174 | name: Reply Comment 175 | permissions: 176 | pull-requests: write 177 | contents: write 178 | steps: 179 | - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 180 | id: script 181 | if: ${{ inputs.issue-number }} 182 | with: 183 | github-token: ${{ secrets.OXC_BOT_PAT }} 184 | result-encoding: string 185 | script: | 186 | const { 187 | data: { jobs }, 188 | } = await github.rest.actions.listJobsForWorkflowRun({ 189 | owner: context.repo.owner, 190 | repo: context.repo.repo, 191 | run_id: context.runId, 192 | per_page: 100, 193 | }); 194 | let result = jobs 195 | .filter((job) => job.name.startsWith("Test ")) 196 | .map((job) => { 197 | const suite = job.name.slice(5); 198 | return { suite, conclusion: job.conclusion, link: job.html_url }; 199 | }); 200 | const url = `${context.serverUrl}//${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; 201 | const urlLink = `[Open](${url})`; 202 | const conclusionEmoji = { 203 | success: ":white_check_mark:", 204 | failure: ":x:", 205 | cancelled: ":stop_button:", 206 | }; 207 | const body = ` 208 | ## [Oxlint Ecosystem CI](${urlLink}) 209 | | suite | result | 210 | |-------|--------| 211 | ${result.map((r) => `| [${r.suite}](${r.link}) | ${conclusionEmoji[r.conclusion]} |`).join("\n")} 212 | `; 213 | return body; 214 | 215 | - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 216 | if: ${{ inputs.issue-number && inputs.comment-id }} 217 | with: 218 | token: ${{ secrets.OXC_BOT_PAT }} 219 | repository: oxc-project/oxc 220 | issue-number: ${{ inputs.issue-number }} 221 | comment-id: ${{ inputs.comment-id }} 222 | body: ${{ steps.script.outputs.result }} 223 | edit-mode: replace 224 | --------------------------------------------------------------------------------