diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index dc53687..842bcd5 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -19,6 +19,10 @@ jobs: run: node --test scripts/validate.test.mjs - name: Validate manifests + skills run: node scripts/validate.mjs + - name: Validate README has no plugin version + env: + PLUGIN_MANIFEST: .codex-plugin/plugin.json + run: node scripts/validate-readme-no-version.mjs - name: Version consistency (plugin.json == package.json) run: | PJ=$(jq -r .version .codex-plugin/plugin.json) diff --git a/README.md b/README.md index 5bf79a9..86a18bb 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Before installing, make sure you have: - **JFrog host URL and access token** — Your JFrog platform URL and a valid access token. - **OpenAI Codex** — Installed, with plugin support (`codex plugin` CLI commands available). - **Node.js** (≥ 18) — with `npx` on your `PATH` (used by the Agent Guard). -- **Skill runtime requirements** — `jf` CLI, `jq`, and `curl` on `PATH`, plus a configured JFrog instance. For the minimum versions, see the upstream skills [`Requirements`](https://github.com/jfrog/jfrog-skills/blob/v0.22.0/README.md#requirements). Configure the CLI with `jf config add` — see [Authentication](#authentication). +- **Skill runtime requirements** — `jf` CLI, `jq`, and `curl` on `PATH`, plus a configured JFrog instance. For the minimum versions, see the upstream skills [`Requirements`](https://github.com/jfrog/jfrog-skills/blob/main/README.md#requirements). Configure the CLI with `jf config add` — see [Authentication](#authentication). - **JFrog AI Catalog** (optional) — If you want to use the Agent Guard feature, your JFrog subscription needs to include the AI Catalog entitlement. Contact your JFrog account team if you're unsure whether it's enabled. - **JFrog CLI ≥ 2.105.0** (optional) — If you want the Agent Guard to auto-resolve the credentials/server ID from the JFrog CLI configuration. - **JFrog project** (optional) — If you want to use the Agent Guard feature. @@ -148,7 +148,7 @@ The `skills/` tree is vendored from pinned in [`scripts/sync-skills-vendor.json`](scripts/sync-skills-vendor.json). To pull a newer upstream release into this repo: -1. Bump `pin` in `scripts/sync-skills-vendor.json` to the new tag (e.g. `v0.23.0`). +1. Bump `pin` in `scripts/sync-skills-vendor.json` to the new upstream tag. 2. Re-sync and commit the refreshed tree: ```bash @@ -160,10 +160,7 @@ To pull a newer upstream release into this repo: 3. Bump `version` in both [`.codex-plugin/plugin.json`](.codex-plugin/plugin.json) and [`package.json`](package.json) (they must match — CI enforces this) so the published plugin reflects the new skills bundle. -4. Update the pinned-version link in the [Prerequisites](#prerequisites) section so the - skill runtime requirements point at the new tag. -5. Commit the pin bump, the regenerated `skills/` tree, the version bump, and the - README link bump together, and open a PR whose merge commit subject carries a +4. Commit the pin bump, the regenerated `skills/` tree, and the version bump together, and open a PR whose merge commit subject carries a `[patch]` / `[minor]` / `[major]` marker (see [Releasing](#releasing)). See [`VENDOR.md`](VENDOR.md) for the full picture. diff --git a/scripts/validate-readme-no-version.mjs b/scripts/validate-readme-no-version.mjs new file mode 100644 index 0000000..513ce95 --- /dev/null +++ b/scripts/validate-readme-no-version.mjs @@ -0,0 +1,78 @@ +#!/usr/bin/env node + +// Copyright (c) JFrog Ltd. 2026 +// Licensed under the Apache License, Version 2.0 +// https://www.apache.org/licenses/LICENSE-2.0 + +import { readFileSync, existsSync } from "node:fs"; +import process from "node:process"; + +const docPaths = (process.env.DOCS || process.env.README_PATH || "README.md") + .split(/\s+/) + .filter(Boolean); + +const errors = []; + +for (const docPath of docPaths) { + const content = readFileSync(docPath, "utf8"); + validateDoc(docPath, content, errors); +} + +function validateDoc(docPath, content, errors) { + +const bannedPatterns = [ + { + re: /current version/i, + msg: 'README must not include a "Current version" callout — use GitHub Releases/tags.', + }, + { + re: /^## Versioning\s*$/m, + msg: 'README must not include a "## Versioning" section — versions live in the manifest and GitHub Releases.', + }, + { + re: /then tag \(for example `v/i, + msg: "README must not include example release tags.", + }, + { + re: /github\.com\/jfrog\/jfrog-skills\/blob\/v\d+\.\d+\.\d+/i, + msg: "README must not pin jfrog-skills doc links to a release tag — use main README or sync-skills-vendor.json.", + }, + { + re: /codeload\.github\.com\/jfrog\/jfrog-skills\/(tar\.gz|zip)\/v\d+\.\d+\.\d+/i, + msg: "README must not embed jfrog-skills release tags in download URLs.", + }, +]; + + for (const { re, msg } of bannedPatterns) { + if (re.test(content)) { + errors.push(`${docPath}: ${msg}`); + } + } + + const manifestPath = process.env.PLUGIN_MANIFEST; + if (docPath.endsWith("README.md") && manifestPath && existsSync(manifestPath)) { + let version; + if (manifestPath.endsWith(".json")) { + version = JSON.parse(readFileSync(manifestPath, "utf8")).version; + } else if (manifestPath.endsWith("gradle.properties")) { + const match = readFileSync(manifestPath, "utf8").match(/^version\s*=\s*(.+)$/m); + version = match?.[1]?.trim(); + } + + if (version && content.includes(version)) { + errors.push( + `${docPath}: contains plugin version "${version}" — authoritative source is ${manifestPath}.` + ); + } + } +} + +if (errors.length > 0) { + console.error("README version validation failed:"); + for (const error of errors) { + console.error(`- ${error}`); + } + process.exit(1); +} + +console.log("README version validation passed.");