Skip to content

Add CI with oxlint and oxfmt - #2

Merged
rin2yh merged 18 commits into
claude/email-discord-webhook-worker-am5f2qfrom
claude/ci-oxlint-oxfmt-7ozg00
Jul 20, 2026
Merged

Add CI with oxlint and oxfmt#2
rin2yh merged 18 commits into
claude/email-discord-webhook-worker-am5f2qfrom
claude/ci-oxlint-oxfmt-7ozg00

Conversation

@rin2yh

@rin2yh rin2yh commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

概要

PR #1 のブランチから切って、CI を導入する。 oxc 系の oxlint / oxfmt を採用。

  • lint / test の2ジョブで実行
  • main への push と全 PR でトリガー
  • TypeScript の型ポリシーを lint で強制
    • any は全面禁止、unknown は基盤(信頼境界)のみ許可

設計判断

  • any は禁止、unknown は「基盤のみ」許可
    • any は型チェックを無効化するため typescript/no-explicit-any で全面禁止
    • unknown は素通しを避け、外部入力をパースする src/index.ts とテスト(部分モックの as unknown as)のみ overrides で許可

レビュー観点

  • GHAのプラクティス的に漏れがないか
  • あった方が良いLintのルール

補足

コードがない状態で導入してもルールの良さや動作確認がしづらいため、実装してから導入。

claude added 14 commits July 14, 2026 05:43
Introduce oxc-based linting (oxlint) and formatting (oxfmt) as the
project's lint/format toolchain, pinned to exact versions to match the
repo's supply-chain hardening.

Type policy enforced via oxlint beyond the correctness defaults:
- `any` is banned (typescript/no-explicit-any).
- `unknown` is banned as a type (typescript/no-restricted-types), except
  at the untrusted-input boundary (src/index.ts, which parses the
  DISCORD_WEBHOOK_MAP JSON) and in test files (partial-mock double casts),
  via config overrides.

buildDiscordPayload previously returned `unknown`; it now returns a
concrete DiscordWebhookPayload type. The rest of the codebase is
reformatted with oxfmt so `oxfmt --check` is clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
CI runs on pushes to main and on pull requests. It installs the
mise-pinned toolchain, then runs oxfmt --check, oxlint, tsc --noEmit, and
vitest — the same as `bun run check` locally. README documents the new
scripts, the CI pipeline, and the any/unknown policy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Reconcile with the rebased PR #1 base (test helpers moved to
src/test/fixtures.ts, comments trimmed, README condensed). Resolutions:
- discord.ts keeps the concrete DiscordWebhookPayload return type on top
  of PR #1's trimmed style.
- oxlint unknown-override path updated to src/test/fixtures.ts.
- README merged: kept PR #1's condensed prose, kept the CI/lint/format
  additions.
- Reformatted with oxfmt so `oxfmt --check` stays clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Split the single check job into two jobs named `lint` (oxfmt --check,
oxlint, tsc --noEmit) and `test` (vitest) so they run in parallel and
report independently.

Bump and pin third-party actions to commit SHAs (version in a trailing
comment) for supply-chain safety:
- actions/checkout v4 -> v7.0.0 (9c091bb)
- jdx/mise-action v2 -> v4.2.0 (e6a8b39)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Remove the CI workflow trigger/job writeup from the README and keep the
lint/format policy under a "Lint & format" heading.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Drop the file-level oxlint overrides that disabled no-restricted-types
for src/index.ts and test files. Instead allow `unknown` at its two
genuine sites with `// oxlint-disable-next-line typescript/no-restricted-types`
plus a reason, so the ban stays in force everywhere else in those files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Build the ForwardableEmailMessage mock as a fully typed object literal
(vi.fn() members satisfy the interface signatures), removing the
`as unknown as` double cast. `unknown` now appears only at the JSON.parse
boundary in src/index.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Drop the lint/format section and script lines from the README, and remove
the redundant tool name in parentheses from CI step names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Remove the toolchain/install explanation comments from the workflow (CI
just carries the --frozen-lockfile flag) and shorten the JSON-parse
boundary comment in src/index.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Remove the fmt:check and lint:fix scripts; pass the flag directly instead
(`bun run fmt --check`, `bun run lint --fix`). CI's format step and the
aggregate `check` script now use `bun run fmt --check`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Set timeout-minutes: 10 on both CI jobs, and remove the aggregate `check`
script (CI runs the steps individually and nothing else referenced it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Use oxlint-disable-line as a trailing comment instead of
oxlint-disable-next-line on the line above.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
- discord.test.ts: drop the local Embed interface and the cast; rely on
  buildDiscordPayload's now-concrete DiscordWebhookPayload return type.
- .oxlintrc.json: fix the rule message to reference oxlint-disable-line,
  matching the actual usage in src/index.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Take PR #1's refactored source and tests as canonical (typed Discord
payload, private internals, reorganized tests, spyFetch helpers). Re-apply
this branch's CI policy on top:
- src/index.ts: per-line disable for the JSON.parse boundary `unknown`.
- src/test/fixtures.ts: type the message mock instead of `as unknown as`.
- src/discord.test.ts: per-line disables for the two genuine test-only
  `unknown` uses (parsed JSON return, table expected value).
Reformatted with oxfmt; oxlint/typecheck/tests all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
@otakakot

Copy link
Copy Markdown

@rin2yh

どこまで CI ガチガチにするかですが、

とかで CI 実装を固めておくのはよいかもなーって思いました 👀

@rin2yh
rin2yh marked this pull request as ready for review July 14, 2026 09:57
@rin2yh

rin2yh commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

全然ありですねー!
ghalintとactionlintは使ったことあるので、zizmor?試してみましょうか

claude added 2 commits July 14, 2026 10:07
Add a separate gha-ci workflow that runs zizmor against the Actions
workflows, triggered only on changes under .github/workflows/** and
.github/actions/**. Set persist-credentials: false on the checkout steps
in ci.yml (jobs only read code and run tools) — this clears zizmor's
artipacked findings, and both workflows now audit clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
Drop the push trigger: the workflow audit is not a code gate, so PR-time
is sufficient. Still scoped to changes under .github/workflows/** and
.github/actions/**.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
@rin2yh

rin2yh commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

@otakakot
GHAを検証するCIも実装しました!
追加分のご確認お願いします🙏

pipxを必要とするpython製なのはあまり好みではありませんが、ghaのコンテナにしか入らないのでよしとしてます。

Comment thread .github/workflows/gha-ci.yml Outdated
claude added 2 commits July 14, 2026 10:55
Switch the gha-ci zizmor step to the official SHA-pinned action (per
review suggestion), replacing the manual pipx install + run. Pin the
zizmor version to 1.27.0 and disable SARIF upload (advanced-security:
false) since the repo has no code scanning — the job fails directly on
findings instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V
zizmor-action v0.5.7 resolves the version from a bundled digest file and
does not know zizmor 1.27.0 (released after the action), which failed with
"Unknown version". Rely on the action's default (latest known to v0.5.7);
the action is SHA-pinned, so that resolution stays deterministic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GKWrRcDKcg13Xqv5qrr1V

@otakakot otakakot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTMです〜!

@rin2yh
rin2yh merged commit a0bd25a into claude/email-discord-webhook-worker-am5f2q Jul 20, 2026
3 checks passed
@rin2yh
rin2yh deleted the claude/ci-oxlint-oxfmt-7ozg00 branch July 20, 2026 14:24
@rin2yh
rin2yh restored the claude/ci-oxlint-oxfmt-7ozg00 branch July 20, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants