chore!: modernize toolchain (esm-only, tsdown, vitest, eslint 10)#827
Conversation
BREAKING CHANGE: Package is now ESM-only. The CJS entry point
(dist/index.cjs / require('locter')) has been removed.
- Build: rollup + swc + tsc-for-types → tsdown (with split
build:types / build:js scripts so tsc still typechecks)
- Tests: jest + @swc/jest → vitest + @vitest/coverage-v8
- Lint: ESLint 8 + .eslintrc + @tada5hi/eslint-config-typescript
→ ESLint 10 flat config + @tada5hi/eslint-config
- Commitlint: .js (CJS) → .mjs (ESM)
- Release: workspaces-publish + ad-hoc tagging → tada5hi/monoship@v2
- TypeScript 5 → 6; tsconfig aligned with tada5hi/typescript-template
(ESNext/bundler/noEmit/allowImportingTsExtensions)
- CI: Node 22 → 24 (engines.node remains >=22.0.0)
Source adjustments:
- ModuleLoader uses createRequire(import.meta.url) so loadSync and
the Jest segfault workaround still function under ESM.
- for-of refactors and includes()/regex-group fixes to satisfy
noUncheckedIndexedAccess in the new @tada5hi/tsconfig.
Removed obsolete files: rollup.config.mjs, test/jest.config.js,
.eslintrc, tsconfig.eslint.json, commitlint.config.js,
release.config.js.
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis pull request migrates the locter project from a CommonJS/Jest/Rollup toolchain to an ESM-only/Vitest/tsdown setup. The package now uses ChangesToolchain and Module System Conversion
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR modernizes the repository’s Node/TypeScript toolchain to align with the tada5hi/typescript-template, moving the package to ESM-only distribution and updating build/test/lint/release automation accordingly (breaking require('locter') consumers).
Changes:
- Replace Rollup/SWC + Jest with tsdown (bundle + dts) and Vitest (coverage via v8).
- Adopt ESLint 10 flat config, TypeScript 6 + updated tsconfig settings, and update CI to Node 24.
- Update runtime/module loading to work under pure ESM (via
createRequire(import.meta.url)), plus small refactors to satisfy stricter TS settings.
Reviewed changes
Copilot reviewed 40 out of 43 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tsdown.config.ts | Adds tsdown bundler configuration (ESM + dts + sourcemaps). |
| tsconfig.json | Moves to ESNext/bundler resolution + noEmit typecheck setup. |
| tsconfig.eslint.json | Removes ESLint-specific TS project file. |
| test/vitest.config.ts | Adds Vitest configuration and coverage thresholds. |
| test/unit/utils.spec.ts | Migrates test to Vitest and updates Jest-env expectation. |
| test/unit/locator.spec.ts | Migrates to Vitest + ESM path handling (import.meta.dirname). |
| test/unit/loader/yaml.spec.ts | Migrates to Vitest. |
| test/unit/loader/module.spec.ts | Migrates to Vitest. |
| test/unit/loader/json.spec.ts | Migrates to Vitest. |
| test/unit/loader/esm.spec.ts | Migrates to Vitest. |
| test/unit/loader/conf.spec.ts | Migrates to Vitest. |
| test/unit/loader/cjs.spec.ts | Migrates to Vitest. |
| test/jest.config.js | Removes Jest configuration. |
| test/data/file.cts | Fixture formatting tweak. |
| test/data/file-default.mts | Fixture formatting tweak. |
| test/data/file-default.mjs | Fixture formatting tweak. |
| test/data/file-default.cts | Fixture formatting tweak. |
| test/data/file-default.cjs | Fixture formatting tweak. |
| src/utils/file-name.ts | Uses .includes() instead of indexOf. |
| src/locator/sync.ts | Refactors loops to for...of. |
| src/locator/async.ts | Refactors loops to for...of. |
| src/loader/module.ts | Refactors rule iteration + uses .includes(). |
| src/loader/built-in/module/utils.ts | Refactors module utils; compact property descriptors. |
| src/loader/built-in/module/module.ts | Uses createRequire(import.meta.url) for ESM-safe sync loading. |
| src/loader/built-in/conf/module.ts | Makes .conf parsing resilient to missing capture group. |
| rollup.config.mjs | Removes Rollup build configuration. |
| release.config.js | Removes semantic-release config. |
| package.json | Switches to ESM-only exports, updates scripts, tooling deps, and publishing config. |
| eslint.config.js | Adds ESLint 10 flat config using @tada5hi/eslint-config. |
| commitlint.config.mjs | Converts commitlint config to ESM. |
| commitlint.config.js | Removes CJS commitlint config. |
| AGENTS.md | Updates contributor guide for new toolchain + ESM-only note. |
| .gitignore | Updates ignore patterns (IDE/cache/tooling artifacts). |
| .github/workflows/release.yml | Updates release pipeline to release-please v5 + monoship publishing. |
| .github/workflows/main.yml | Updates CI triggers, concurrency, permissions, and Node version. |
| .github/dependabot.yml | Adjusts Dependabot target branch and grouping rules. |
| .github/actions/install/action.yml | Updates setup-node/cache actions and cache keying. |
| .github/actions/build/action.yml | Updates cache action version and formatting. |
| .eslintrc | Removes legacy ESLint config. |
| .agents/testing.md | Updates testing docs to Vitest + ESM patterns. |
| .agents/structure.md | Updates repo structure documentation for new config files/output. |
| .agents/conventions.md | Updates conventions/docs for ESM-only + tsdown/vitest/monoship flow. |
Comments suppressed due to low confidence (1)
src/loader/built-in/module/module.ts:25
- There is trailing whitespace after
isFilePath,in the utils import list. Please remove the trailing space to keep formatting consistent.
import {
handleException,
hasStringProperty,
isFilePath,
isJestRuntimeEnvironment,
isObject,
isTsNodeRuntimeEnvironment,
isTypeScriptError,
} from '../../../utils';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "scripts": { | ||
| "build:types": "tsc --emitDeclarationOnly", | ||
| "build:js": "rollup -c", | ||
| "build": "rimraf dist && npm run build:types && npm run build:js", | ||
| "commit": "npx git-cz", | ||
| "test": "cross-env NODE_ENV=test jest --config ./test/jest.config.js", | ||
| "test:coverage": "cross-env NODE_ENV=test jest --config ./test/jest.config.js --coverage", | ||
| "lint": "eslint --ext .js,.vue,.ts ./src", | ||
| "build:types": "tsc --noEmit", | ||
| "build:js": "tsdown", | ||
| "build": "npm run build:types && npm run build:js", | ||
| "test": "vitest --config test/vitest.config.ts --run", | ||
| "test:coverage": "vitest --config test/vitest.config.ts --run --coverage", |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/actions/build/action.yml:
- Around line 9-13: The workflow currently references actions/cache@v4 which is
mutable; update the uses entry for the actions/cache step (the line containing
"uses: actions/cache@v4") to pin to the full commit SHA of the actions/cache
repository (replace `@v4` with @<full-length-commit-sha>) to prevent supply-chain
tampering, and optionally append a human-readable comment with the semantic
version (e.g., "# v4") for clarity; no other changes to the step (path/key) are
required.
In @.github/actions/install/action.yml:
- Around line 15-22: The workflow currently pins GitHub Actions with mutable
tags (uses: actions/setup-node@v4 and uses: actions/cache@v4); replace those tag
pins with the corresponding immutable full commit SHAs from the official action
repos (e.g., uses: actions/setup-node@<full-commit-sha> and uses:
actions/cache@<full-commit-sha>) so the action versions cannot be
repointed—locate the correct commit SHAs in the actions/setup-node and
actions/cache GitHub repos/releases and update the two uses: lines accordingly.
In @.github/workflows/main.yml:
- Around line 50-52: The checkout steps using actions/checkout@v6 should be
hardened: replace the tag pin with the full commit SHA for actions/checkout and
add persist-credentials: false to the step configuration; apply this change to
both occurrences of the Checkout step (the actions/checkout usage shown twice)
so the workflow no longer uses a mutable tag and does not leave GITHUB_TOKEN in
the Git config.
In @.github/workflows/release.yml:
- Around line 25-31: Pin external actions to immutable commit SHAs instead of
mutable tags: replace uses: googleapis/release-please-action@v5 and uses:
actions/checkout@v6 with their full commit SHA equivalents, and for the Checkout
step (the job that runs when steps.release.outputs.releases_created == 'true')
add persist-credentials: false under the checkout action inputs to harden
credentials; update any other occurrences (the other checkout/use lines
mentioned around 46-47 and 55-56) similarly so all third-party actions are
SHA-pinned and all checkout steps include persist-credentials: false.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 058dd40c-4c0c-4f51-b15e-dc830159cb0c
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (42)
.agents/conventions.md.agents/structure.md.agents/testing.md.eslintrc.github/actions/build/action.yml.github/actions/install/action.yml.github/dependabot.yml.github/workflows/main.yml.github/workflows/release.yml.gitignoreAGENTS.mdcommitlint.config.jscommitlint.config.mjseslint.config.jspackage.jsonrelease.config.jsrollup.config.mjssrc/loader/built-in/conf/module.tssrc/loader/built-in/module/module.tssrc/loader/built-in/module/utils.tssrc/loader/module.tssrc/locator/async.tssrc/locator/sync.tssrc/utils/file-name.tstest/data/file-default.cjstest/data/file-default.ctstest/data/file-default.mjstest/data/file-default.mtstest/data/file.ctstest/jest.config.jstest/unit/loader/cjs.spec.tstest/unit/loader/conf.spec.tstest/unit/loader/esm.spec.tstest/unit/loader/json.spec.tstest/unit/loader/module.spec.tstest/unit/loader/yaml.spec.tstest/unit/locator.spec.tstest/unit/utils.spec.tstest/vitest.config.tstsconfig.eslint.jsontsconfig.jsontsdown.config.ts
💤 Files with no reviewable changes (6)
- commitlint.config.js
- .eslintrc
- tsconfig.eslint.json
- release.config.js
- test/jest.config.js
- rollup.config.mjs
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| **/dist/** | ||
| key: ${{ runner.os }}-build-${{ github.sha }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/actions/build/action.ymlRepository: tada5hi/locter
Length of output: 662
🌐 Web query:
GitHub Actions best practices pinning commit SHA vs tag 2024 2025
💡 Result:
As of May 2026, the consensus among security experts and official GitHub documentation is that pinning GitHub Actions to full-length commit SHAs is the industry best practice for production and high-security CI/CD environments [1][2][3][4]. Why Pinning to Commit SHA is Recommended: 1. Immutability and Security: Git tags are mutable, meaning they can be deleted, moved, or re-created by a repository maintainer to point to different commits [1][5]. If an action's repository or a maintainer's account is compromised, attackers can update existing tags to inject malicious code, which would then be automatically executed by your workflows [6][7][8][4]. Pinning to a full-length commit SHA ensures that your workflows always execute the exact, expected code, effectively mitigating this class of supply-chain attacks [1][5][6]. 2. Enforced Governance: GitHub now supports organization-level policies that administrators can enable to mandate full commit SHA pinning [1][9][10]. When this policy is active, any workflow attempting to use a tag or branch reference will fail, ensuring strict compliance across an organization [1][9]. 3. Controlled Updates: Pinning to a SHA eliminates the risk of silent, unexpected updates [1][3]. You can (and should) use automated tools like Dependabot, Renovate, or specialized CLI tools to track and open pull requests for action updates, allowing you to review changes before they are merged into your workflows [1][8][11][4]. Best Practices for Implementation: * Use Full-Length SHAs: Always use the full-length commit SHA, not a shortened version, to ensure uniqueness and security [2][3][12]. * Maintain Context: Since SHAs are not human-readable, append the version tag as a comment in your workflow file to maintain visibility into what version is being used [9][11]. Example: uses: actions/checkout@f43a0e5 # v4.2.1 * Leverage Automation: Configure Dependabot or similar dependency management tools to automatically monitor for updates to your pinned actions [1][8]. This balances security with the need to keep your tools up to date without manual overhead [1][5][4]. While tags remain convenient for development or low-risk, non-sensitive environments, they should be avoided for workflows handling secrets, production deployments, or any process where supply-chain integrity is a requirement [2][5][3][4].
Citations:
- 1: https://www.romainlespinasse.dev/posts/github-actions-commit-sha-pinning/
- 2: https://docs.github.com/en/actions/reference/security/secure-use
- 3: https://dev.to/nkuziev-sec/why-fixed-container-image-versions-matter-lessons-from-the-trivy-supply-chain-attack-29ke
- 4: https://www.aikido.dev/blog/checklist-github-actions
- 5: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 6: https://gbhackers.com/compromised-github-action/
- 7: https://undercodenews.com/github-action-supply-chain-attack-exposes-ci-cd-secrets-through-memory-scraping-malware/
- 8: Pin GitHub Actions to commit SHAs instead of version tags ankitects/anki#4722
- 9: https://dev.to/ciberconscientes/github-actions-why-you-should-pin-your-actions-to-a-specific-version-1513
- 10: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 11: Pin GitHub Actions to commit SHAs in release workflow github/gh-aw#10021
- 12: https://stackoverflow.com/questions/78903499/how-do-i-pin-an-action-to-a-specific-sha
Pin actions/cache by commit SHA.
Using @v4 keeps this action vulnerable to supply-chain attacks—tags are mutable and can be retargeted if the repository or maintainer account is compromised. Pin to the full-length commit SHA instead, and optionally append the version as a comment for readability (e.g., actions/cache@<full-length-commit-sha> # v4).
Suggested patch
- uses: actions/cache@v4
+ uses: actions/cache@<full-length-commit-sha> # v4🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/actions/build/action.yml around lines 9 - 13, The workflow currently
references actions/cache@v4 which is mutable; update the uses entry for the
actions/cache step (the line containing "uses: actions/cache@v4") to pin to the
full commit SHA of the actions/cache repository (replace `@v4` with
@<full-length-commit-sha>) to prevent supply-chain tampering, and optionally
append a human-readable comment with the semantic version (e.g., "# v4") for
clarity; no other changes to the step (path/key) are required.
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: ${{ inputs.node-version }} | ||
| registry-url: ${{ inputs.registry-url }} | ||
|
|
||
| - name: Use Node.js ${{ inputs.node-version }} | ||
| uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: ${{ inputs.node-version }} | ||
| registry-url: ${{ inputs.registry-url }} | ||
| - name: Use cache | ||
| uses: actions/cache@v4 | ||
| with: |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Read the action file to verify the current state
cat -n .github/actions/install/action.yml | head -30Repository: tada5hi/locter
Length of output: 1162
Pin setup-node and cache actions to commit SHAs instead of version tags.
Both action references use mutable tag pins (@v4) which can be repointed by a compromised repository. Replace with immutable full-length commit SHAs for security hardening.
Suggested patch
- uses: actions/setup-node@v4
+ uses: actions/setup-node@<FULL_LENGTH_COMMIT_SHA>
...
- uses: actions/cache@v4
+ uses: actions/cache@<FULL_LENGTH_COMMIT_SHA>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/actions/install/action.yml around lines 15 - 22, The workflow
currently pins GitHub Actions with mutable tags (uses: actions/setup-node@v4 and
uses: actions/cache@v4); replace those tag pins with the corresponding immutable
full commit SHAs from the official action repos (e.g., uses:
actions/setup-node@<full-commit-sha> and uses: actions/cache@<full-commit-sha>)
so the action versions cannot be repointed—locate the correct commit SHAs in the
actions/setup-node and actions/cache GitHub repos/releases and update the two
uses: lines accordingly.
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/main.yml | sed -n '45,75p'Repository: tada5hi/locter
Length of output: 1014
🌐 Web query:
GitHub Actions checkout action best practices SHA pinning vs tag pinning security
💡 Result:
For security and supply chain integrity, pinning GitHub Actions to a full-length commit SHA is the recommended best practice [1][2][3]. Unlike version tags (e.g., v4), which are mutable and can be moved or deleted by an action's maintainer or a malicious actor [1][4][5], a full-length SHA points to an immutable commit in the repository [6][1][4]. Security Benefits of SHA Pinning 1. Immutability: Once pinned to a SHA, the workflow will always execute the exact same code, regardless of changes to tags or branches in the source repository [4][7][3]. 2. Mitigation of Supply Chain Attacks: If an action's repository is compromised, attackers often target mutable tags to inject malicious code [8][5]. Workflows pinned to a specific SHA are protected from these silent, unauthorized updates [5][9]. 3. Reproducibility: SHA pinning ensures that build environments remain consistent over time, which is critical for debugging and preventing unexpected deployment issues [7][3]. Best Practices for Implementation - Use Full-Length SHAs: Always use the full 40-character commit SHA rather than a shortened version to ensure unique identification [1][3]. - Use Informative Comments: To maintain readability and ease of maintenance, include the version tag in a comment on the same line as the SHA [10][6][7]: uses: actions/checkout@a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 # v4.1.1 - Automate Updates: Because SHA pinning prevents automatic updates, use dependency management tools like Dependabot or Renovate to monitor for new releases and automate the creation of pull requests to update your pinned SHAs [10][1][11][3]. - Enforce Policies: Organizations can configure repository or organization-level policies to mandate full SHA pinning, causing any workflows that use mutable tags to fail [6][1][9]. - Verify Sources: Ensure the SHA comes from the official, trusted repository and not from a fork or untrusted source [1][3]. While tagging is more convenient for general use, it carries inherent trust risks. Only use tags for actions from creators you explicitly trust, and prioritize SHA pinning for critical, high-risk, or third-party actions [1][4]. Always be aware that pinning the top-level action does not automatically protect you from vulnerabilities in its transitive dependencies; you should audit those as well [11].
Citations:
- 1: https://docs.github.com/en/actions/reference/security/secure-use
- 2: https://dev.to/nkuziev-sec/why-fixed-container-image-versions-matter-lessons-from-the-trivy-supply-chain-attack-29ke
- 3: https://github.com/ihs7/action-sha-pinned-actions
- 4: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 5: https://gbhackers.com/compromised-github-action/
- 6: https://dev.to/ciberconscientes/github-actions-why-you-should-pin-your-actions-to-a-specific-version-1513
- 7: https://www.nearform.com/insights/why-you-should-pin-your-github-actions-by-commit-hash
- 8: https://undercodenews.com/github-actions-under-siege-imposter-commits-poisoned-tags-and-a-silent-supply-chain-nightmare-unfolding-in-real-time/
- 9: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 10: Pin all GitHub Actions to immutable SHA commits for supply chain security github/gh-aw#5691
- 11: https://medium.com/@adan.alvarez/github-actions-and-the-pinning-problem-what-100-security-projects-reveal-54a3a9dcc902
🌐 Web query:
GitHub Actions checkout action persist-credentials false security best practice
💡 Result:
In GitHub Actions, setting persist-credentials: false in the actions/checkout action is considered a security best practice because it prevents the automatic persistence of authentication credentials (such as the GITHUB_TOKEN) into the local Git configuration [1][2][3]. When persist-credentials is set to its default value of true, the checkout action configures the local Git environment with authentication tokens [1][3]. This makes those credentials easily accessible to any subsequent steps or scripts running within the same job, effectively widening the surface area for credential exposure if a downstream step is compromised [4][5][6]. By setting persist-credentials: false, you opt out of this behavior, ensuring that authentication is not implicitly stored in the repository's Git configuration [1][3]. This is recommended by security guidelines, such as the OWASP GitHub Actions Security Cheat Sheet, to reduce the risk of credential leakage within the workflow environment [2]. Recent updates to the actions/checkout action have improved credential security by moving the stored credentials from the repository's internal.git/config file to a separate, temporary configuration file within $RUNNER_TEMP [1][7][3]. Despite this architectural improvement, explicitly setting persist-credentials: false remains a recommended practice to adhere to the principle of least privilege, ensuring that credentials are only used when absolutely necessary and are not readily available to unrelated job steps [2][6]. Key points regarding this security practice: - It limits the exposure of authentication tokens within the runner environment [4][6]. - It is particularly important if your job executes untrusted code or third-party actions [2]. - It does not prevent you from performing authenticated Git operations; if you need to push or fetch later in the job, you can still manage authentication explicitly using other methods if necessary [1]. - It is a complementary measure to other security practices, such as using read-only GITHUB_TOKEN permissions where possible and pinning actions to immutable commit SHAs to prevent supply chain attacks [8][9][10].
Citations:
- 1: https://github.com/actions/checkout/blob/main/README.md
- 2: https://cheatsheetseries.owasp.org/cheatsheets/GitHub_Actions_Security_Cheat_Sheet.html
- 3: https://github.com/actions/checkout
- 4: Remove
persist-credentialsor change the default tofalseactions/checkout#485 - 5: Remove
persist-credentialsor change the default tofalseactions/checkout#485 - 6: https://pkg.go.dev/github.com/koki-develop/ghasec/rules/checkout-persist-credentials
- 7: Persist creds to a separate file actions/checkout#2286
- 8: https://www.aikido.dev/blog/checklist-github-actions
- 9: https://undercodenews.com/github-action-supply-chain-attack-exposes-ci-cd-secrets-through-memory-scraping-malware/
- 10: https://gbhackers.com/compromised-github-action/
Harden checkout steps: pin action SHAs and disable credential persistence.
The new actions/checkout steps at lines 50–51 and 70–71 use tag pinning (@v6) instead of full-length commit SHAs, which are mutable and vulnerable to supply chain attacks. Additionally, persist-credentials is not set to false, leaving the GITHUB_TOKEN accessible in the Git config for all subsequent steps.
Suggested fix
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@<FULL_LENGTH_COMMIT_SHA>
+ with:
+ persist-credentials: falseApply the same change to both checkout steps (lines 50–51 and 70–71).
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 50-51: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 51-51: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/main.yml around lines 50 - 52, The checkout steps using
actions/checkout@v6 should be hardened: replace the tag pin with the full commit
SHA for actions/checkout and add persist-credentials: false to the step
configuration; apply this change to both occurrences of the Checkout step (the
actions/checkout usage shown twice) so the workflow no longer uses a mutable tag
and does not leave GITHUB_TOKEN in the Git config.
| uses: googleapis/release-please-action@v5 | ||
| with: | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Checkout | ||
| if: steps.release.outputs.releases_created == 'true' | ||
| uses: actions/checkout@v6 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find and read the release.yml workflow file
find . -name "release.yml" -o -name "release.yaml" | head -20Repository: tada5hi/locter
Length of output: 90
🏁 Script executed:
cat -n ./.github/workflows/release.ymlRepository: tada5hi/locter
Length of output: 2091
🌐 Web query:
GitHub Actions security best practices pinning commit SHA vs version tag official
💡 Result:
GitHub Actions security best practice: pin third-party actions to a full-length commit SHA (immutable), not to a version tag. 1) Why commit-SHA pinning is the official “most secure” choice GitHub states that “Pinning an action to a full-length commit SHA is currently the only way to use an action as an immutable release” and that it mitigates the risk of malicious changes to the action’s repository because an attacker would need to generate a SHA-1 collision for a valid Git object payload [1]. GitHub also recommends verifying the SHA is from the action’s repository (not a fork) [1]. 2) Why version tag pinning is weaker GitHub explicitly says pinning to a tag is less secure and “only” do it if you trust the action’s creator [1]. GitHub notes that tags can be moved or deleted if a bad actor gains access to the repository holding the action [1]. GitHub changelog coverage of “Enforce SHA pinning” similarly describes that pinning to a specific commit SHA prevents malicious code added to a new or updated branch or tag from being automatically used, and that policies can require full SHAs (workflows that aren’t pinned fail) [2]. 3) Practical guidance (what to do in your workflows) - Prefer: uses: owner/repo@ (optionally with a comment indicating the intended version) [1][2]. - If you use tags (e.g., @v3) you are accepting mutability risk; GitHub’s docs say only do so if you trust the creator, and you still have residual risk if the action repo/tag is compromised [1]. - To keep SHA pinning maintainable, use automation such as Dependabot/Renovate to update pins, and validate workflows after updates (GitHub documents Dependabot workflow automation in general) [3]. Bottom line: For security, pin to full-length commit SHAs; tags are convenient but inherently mutable and therefore not the “official” strongest recommendation [1][2].
Citations:
- 1: https://docs.github.com/en/actions/reference/security/secure-use
- 2: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 3: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions?learn=dependency_version_updates&learnProduct=code-security
🌐 Web query:
GitHub Actions actions/checkout persist-credentials false security best practice
💡 Result:
Setting persist-credentials: false in the actions/checkout GitHub Action is considered a security best practice because it prevents the automatic persistence of Git credentials (such as the GITHUB_TOKEN or SSH keys) into the local Git configuration of the runner [1][2][3]. By default, actions/checkout configures the local.git/config file with these credentials to enable subsequent Git commands within the same workflow job [1][3]. If this setting is left as true (the default), any process or malicious action running within that workflow can potentially read these credentials from the configuration file [4][5]. Setting this input to false ensures that credentials are not persisted, which reduces the risk of credential exposure if the workflow is compromised [2][6]. Key points regarding this practice: - Security Impact: It limits the potential for unauthorized access to repository credentials by ensuring they are not stored on disk in an easily readable format [5][2]. - Operational Impact: If you require authenticated Git operations later in your workflow (e.g., pushing code back to the repository using a custom action), setting this to false will cause those operations to fail unless you manually reconfigure authentication or provide the necessary credentials to those subsequent steps [1][6]. - Context: While this setting hardens the checkout process, it is one layer of a broader security strategy [7]. It is also recommended to use granular permissions (e.g., setting the default GITHUB_TOKEN to read-only at the repository level), pin actions to specific commit SHAs rather than mutable tags to prevent supply chain attacks, and use OIDC for cloud provider authentication where possible [7][8][9]. In summary, you should use persist-credentials: false unless your workflow explicitly requires authenticated Git operations that rely on the automatically persisted configuration [1][2].
Citations:
- 1: https://github.com/actions/checkout/blob/main/README.md
- 2: https://cheatsheetseries.owasp.org/cheatsheets/GitHub_Actions_Security_Cheat_Sheet.html
- 3: https://github.com/actions/checkout
- 4: Remove
persist-credentialsor change the default tofalseactions/checkout#485 - 5: Remove
persist-credentialsor change the default tofalseactions/checkout#485 - 6: Reconfigure credentials when `persist-credentials: false` is set stefanzweifel/git-auto-commit-action#356
- 7: https://www.aikido.dev/blog/checklist-github-actions
- 8: https://undercodenews.com/github-actions-nightmare-malicious-tag-hijack-sparks-massive-supply-chain-breach-shock/
- 9: https://gbhackers.com/compromised-github-action/
Pin all third-party GitHub Actions to commit SHAs, and harden checkout credentials.
The release workflow references external actions by mutable version tags and omits credential hardening on checkout. GitHub recommends pinning actions to full-length commit SHAs (immutable) rather than tags, which can be moved, deleted, or compromised. The checkout step should include persist-credentials: false to prevent credential persistence on disk and reduce exposure risk if the workflow is compromised.
Suggested patch
- - name: Release Please
- id: release
- uses: googleapis/release-please-action@v5
+ - name: Release Please
+ id: release
+ uses: googleapis/release-please-action@<FULL_LENGTH_COMMIT_SHA>
...
- - name: Checkout
- if: steps.release.outputs.releases_created == 'true'
- uses: actions/checkout@v6
+ - name: Checkout
+ if: steps.release.outputs.releases_created == 'true'
+ uses: actions/checkout@<FULL_LENGTH_COMMIT_SHA>
+ with:
+ persist-credentials: false
...
- - name: Publish
- if: steps.release.outputs.releases_created == 'true'
- uses: tada5hi/monoship@v2
+ - name: Publish
+ if: steps.release.outputs.releases_created == 'true'
+ uses: tada5hi/monoship@<FULL_LENGTH_COMMIT_SHA>
...
- - name: Upload report
- if: steps.release.outputs.releases_created == 'true'
- uses: codecov/codecov-action@v5
+ - name: Upload report
+ if: steps.release.outputs.releases_created == 'true'
+ uses: codecov/codecov-action@<FULL_LENGTH_COMMIT_SHA>Also applies to: 46-47, 55-56
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 29-31: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 25-25: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 31-31: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 25 - 31, Pin external actions to
immutable commit SHAs instead of mutable tags: replace uses:
googleapis/release-please-action@v5 and uses: actions/checkout@v6 with their
full commit SHA equivalents, and for the Checkout step (the job that runs when
steps.release.outputs.releases_created == 'true') add persist-credentials: false
under the checkout action inputs to harden credentials; update any other
occurrences (the other checkout/use lines mentioned around 46-47 and 55-56)
similarly so all third-party actions are SHA-pinned and all checkout steps
include persist-credentials: false.
Three spots got stranded whitespace after the autofix split inline imports onto multiple lines. Surfaced by PR review.
Summary
Brings the locter repo onto the same toolchain used by
tada5hi/typescript-template. ESM-only output, faster build viatsdown, modern test runner viavitest, ESLint 10 flat config, andmonoshipfor publishing.This is a breaking change —
require('locter')no longer works.Tooling changes
rollup+@swc+tsc --emitDeclarationOnly→tsdown(kept thebuild:types/build:jssplit sotsc --noEmitstill gates the build on type errors).jest+@swc/jest→vitest+@vitest/coverage-v8. Coverage thresholds preserved (branches 65, functions/lines/statements 80)..eslintrc+@tada5hi/eslint-config-typescript→ ESLint 10 flat config +@tada5hi/eslint-config.tsconfig.eslint.jsonremoved.commitlint.config.js(CJS) →commitlint.config.mjs.workspaces-publish+ ad-hoc git-tag logic →tada5hi/monoship@v2.ESNext/bundler/noEmit/allowImportingTsExtensions).engines.nodestays>=22.0.0).rollup.config.mjs,test/jest.config.js,.eslintrc,tsconfig.eslint.json,commitlint.config.js,release.config.js,npm run commit.Source changes
ModuleLoadernow usescreateRequire(import.meta.url)soloadSyncand the Jest-segfault workaround keep working under pure ESM.for…ofrefactors inlocator/{async,sync}.ts,loader/module.ts,loader/built-in/conf/module.tsplus.includes()and optional-capture-group fixes — required bynoUncheckedIndexedAccessin the new@tada5hi/tsconfig.test/data/*fixtures — these are now ignored by ESLint going forward).Breaking changes
"type": "module";exportsonly exposesimport+types. Consumers onrequire('locter')must migrate.dist/index.cjsis gone;dist/index.d.ts→dist/index.d.mts.Test plan
npm install— 0 vulnerabilitiesnpm run build—tsc --noEmitclean,tsdownemitsdist/index.mjs(15.47 kB) +dist/index.d.mts(6.02 kB)npm test— 8 files, 38 tests passingnpm run lint— cleanimport { locate, load } from 'locter'works from a fresh ESM consumerSummary by CodeRabbit
Release Notes
Build & Tooling
Package Distribution
Documentation