├── .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 |
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 |
--------------------------------------------------------------------------------