diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 33ffff0..025779e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,10 @@ updates: schedule: interval: weekly open-pull-requests-limit: 5 + ignore: + # Node 26 is a preview runtime; keep the current Node 24 type contract. + - dependency-name: '@types/node' + versions: ['26.x'] groups: development: dependency-type: development diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 77502c6..019dd55 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,10 +23,10 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 (2026-09-13) + - uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 with: languages: ${{ matrix.language }} build-mode: none - - uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 (2026-09-13) + - uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 with: category: /language:${{ matrix.language }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index e6baa56..6496801 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -28,6 +28,6 @@ jobs: path: results.sarif if-no-files-found: error retention-days: 14 - - uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 (2026-09-13) + - uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0 with: sarif_file: results.sarif diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e7a09c9..e2d755b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,6 +21,14 @@ Start from an issue labeled `good first issue`. Comment on the issue before open For scoring-rule work, follow [docs/methodology.md](docs/methodology.md): every rule change needs an evidence class plus positive, negative, and false-positive fixtures. Pull requests that claim ranking, citation, or AI-visibility outcomes will not be merged. +### Adding a scoring rule + +Before changing scoring behavior, read [docs/methodology.md](docs/methodology.md) and choose the applicable documented A–D evidence class. Record a current primary source for evidence-backed behavior or an explicit heuristic rationale for a content heuristic, including the rule's scope and known false positives. Class D experiments are informational only and earn no readiness points; do not add zero-weight Class D experiments to the scored fixture corpus. + +Create a stable rule ID in [`src/core/rules.ts`](src/core/rules.ts), implement the rule there, and register it in the exported `allRules` array. In [`fixtures/v0.6/rule-corpus.ts`](fixtures/v0.6/rule-corpus.ts), add cases under the matching ID: `positive`, `negative`, and `boundary` for the false-positive boundary. Give each case a purpose and expected `score`, `issues`, and `suggestions`; the [release-contract tests](src/core/__tests__/release-contract.test.ts) cover every scored rule and enforce these expectations through the parser boundary. + +Record expected JSON/output changes, score-contract compatibility, and any migration impact in the pull request. Keep claims within the documented scope and do not present a rule or score as evidence of ranking, citation, indexing, or adoption outcomes. + ## Pull requests Keep each pull request focused. Include: diff --git a/docs/action-pins.md b/docs/action-pins.md index 2cace8a..4d4435b 100644 --- a/docs/action-pins.md +++ b/docs/action-pins.md @@ -3,6 +3,9 @@ Resolved through official GitHub repositories on 2026-09-13. Annotated tag objects were dereferenced to commits; tag-object SHAs are not used as Action pins. +CodeQL was updated to v4.38.0 on 2026-09-21 after checking its upstream tag +and [official release notes](https://github.com/github/codeql-action/releases/tag/v4.38.0). + | Action | Upstream tag | Full commit | | --- | --- | --- | | actions/checkout | v7.0.1 | 3d3c42e5aac5ba805825da76410c181273ba90b1 | @@ -10,7 +13,7 @@ were dereferenced to commits; tag-object SHAs are not used as Action pins. | actions/upload-artifact | v7.0.1 | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | | actions/download-artifact | v8.0.1 | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | | actions/dependency-review-action | v5.0.0 | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | -| github/codeql-action | v4.37.9 | cdf488f595d80d6e07e03d4674febd5ab45fa938 | +| github/codeql-action | v4.38.0 | b96794f015dfd88f77b49b1c93e0fa7110f94c63 | | ossf/scorecard-action | v2.4.4 | 2d1146689b8cda280b9bc96326124645441f03bc | | actions/attest-build-provenance | v4.2.2 | 4d101475d8b20a2381f78447822ac1eab6504dd8 | diff --git a/examples/github-action-sample/README.md b/examples/github-action-sample/README.md index cfd835e..640ee87 100644 --- a/examples/github-action-sample/README.md +++ b/examples/github-action-sample/README.md @@ -5,6 +5,7 @@ This directory is a copyable end-to-end sample for the v0.6 Action contract. - `.github/workflows/geoptimize.yml` checks the static site on pull requests and manual runs. - `site/index.html` is a deterministic public input. - The Action is advisory by default. The sample does not block a pull request on an unreviewed score threshold. +- [VitePress workflow example](vitepress/README.md) adapts the check for an existing npm-based VitePress project. The workflow pins `geoptimize@0.11.0` through the immutable `v0.11.0` Git tag. Change this exact version only when deliberately adopting another release. diff --git a/examples/github-action-sample/vitepress/.github/workflows/geoptimize.yml b/examples/github-action-sample/vitepress/.github/workflows/geoptimize.yml new file mode 100644 index 0000000..2f7cc69 --- /dev/null +++ b/examples/github-action-sample/vitepress/.github/workflows/geoptimize.yml @@ -0,0 +1,26 @@ +name: VitePress content readiness + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + readiness: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '24' + cache: npm + - run: npm ci + - run: npm run docs:build + - uses: cucuwang/geoptimize@v0.11.0 + with: + path: docs/.vitepress/dist + fail-on-low-score: 'false' diff --git a/examples/github-action-sample/vitepress/README.md b/examples/github-action-sample/vitepress/README.md new file mode 100644 index 0000000..a68c90b --- /dev/null +++ b/examples/github-action-sample/vitepress/README.md @@ -0,0 +1,16 @@ +# VitePress GitHub Action sample + +This is a copyable advisory-mode workflow for an existing npm-based VitePress project. It is not a standalone runnable site fixture. + +Copy the [sample workflow](.github/workflows/geoptimize.yml) to `.github/workflows/geoptimize.yml` at the repository root of your project. + +## Prerequisites + +- `package.json` at the repository root and a committed `package-lock.json`. +- VitePress installed as a local dependency. +- A `docs:build` script that runs `vitepress build docs`. +- VitePress sources in `docs/`, with the default output at `docs/.vitepress/dist`. + +The workflow runs `npm ci`, builds the site, and scans `docs/.vitepress/dist` with `cucuwang/geoptimize@v0.11.0`. Advisory mode lets a low score pass; installation, build, and scan errors still fail the job. + +For another site root or a custom `outDir`, adjust the `docs:build` command and the Action `path` together so they point to the same build output. See the [official VitePress deployment guide](https://vitepress.dev/guide/deploy) for deployment details. diff --git a/src/cli/__tests__/resolve-target.test.ts b/src/cli/__tests__/resolve-target.test.ts new file mode 100644 index 0000000..2d06063 --- /dev/null +++ b/src/cli/__tests__/resolve-target.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from 'vitest'; +import { resolveTarget } from '../interactive.js'; + +describe('resolveTarget', () => { + it('keeps HTTPS URLs unchanged', () => { + expect(resolveTarget('https://example.com')).toEqual({ + type: 'url', + path: 'https://example.com', + }); + }); + + it('normalizes a bare domain to HTTPS', () => { + expect(resolveTarget('example.com')).toEqual({ + type: 'url', + path: 'https://example.com', + }); + }); + + it('throws actionable guidance for relative paths without --dir', () => { + expect(() => resolveTarget('./dist')).toThrowError(/--dir/); + }); + + it('throws actionable guidance for absolute paths without --dir', () => { + expect(() => resolveTarget('/tmp/site')).toThrowError(/--dir/); + }); + + it('resolves paths as directory when --dir is set', () => { + expect(resolveTarget('./dist', true)).toEqual({ + type: 'directory', + path: './dist', + }); + }); +});