diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml deleted file mode 100644 index df6a474..0000000 --- a/.github/workflows/pr-title.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: 'LintPrTitle' - -on: - pull_request: - types: - - opened - - edited - - synchronize - -permissions: - pull-requests: write - -jobs: - ValidatePrTitle: - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d63535f..2f9879f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -21,6 +21,18 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.ref }} + # Format only — whether the version is actually newer is decided by release.yml against the + # tags on main. Catching a malformed version here keeps that failure out of the merge. + - name: Validate package.json version format + run: | + set -euo pipefail + VERSION=$(jq -er '.version' package.json) + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::package.json version '$VERSION' is not X.Y.Z — releases are cut from this field" + exit 1 + fi + echo "package.json version $VERSION is a releasable X.Y.Z" + - name: Setup Tooling uses: jdx/mise-action@d6e32c1796099e0f1f3ac741c220a8b7eae9e5dd with: diff --git a/.github/workflows/publish-as-is.yml b/.github/workflows/publish-as-is.yml index 6c0d391..70feb6e 100644 --- a/.github/workflows/publish-as-is.yml +++ b/.github/workflows/publish-as-is.yml @@ -5,6 +5,10 @@ on: repository_dispatch: types: [publish-package-as-is] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + permissions: id-token: write contents: read @@ -13,8 +17,11 @@ jobs: publish: runs-on: ubuntu-latest steps: + # repository_dispatch checks out the default branch by default; the payload from release.yml + # pins this to the released commit so the published version matches the release. - uses: actions/checkout@v4 with: + ref: ${{ github.event.client_payload.ref || github.sha }} fetch-depth: 0 fetch-tags: true @@ -32,4 +39,4 @@ jobs: # set npmjs.com as the registry npm config set registry https://registry.npmjs.org/ echo "Publishing version as is" - mise run publish + mise run publish diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6708145..0d3e163 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,30 +7,88 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false permissions: contents: write - pull-requests: write jobs: - process: + release: runs-on: ubuntu-latest outputs: - releases_created: ${{ steps.release-please.outputs.releases_created }} - prs_created: ${{ steps.release-please.outputs.prs_created }} + released: ${{ steps.release_gate.outputs.should_release }} steps: - - uses: google-github-actions/release-please-action@v4 - id: release-please + # fetch-depth: 0 here, plus `git fetch --tags` in the gate below, so the gate sees every + # release tag — including one created by a prior run that was still queued behind this one. + - uses: actions/checkout@v4 with: - token: ${{ secrets.GITHUB_TOKEN }} - release-type: node - skip-github-pull-request: false + fetch-depth: 0 + + # package.json is the only place the version lives. + - name: Read version from package.json + id: version + run: | + set -euo pipefail + VERSION=$(jq -er '.version' package.json) + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::package.json version '$VERSION' is not X.Y.Z — refusing to release" + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Compare version against latest release tag + id: release_gate + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + set -euo pipefail + git fetch --tags origin + # grep -E drops prerelease and suffixed tags that the glob still matches, so only + # X.Y.Z releases take part in the comparison. + LATEST=$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1 || true) + if [ -z "$LATEST" ]; then + echo "should_release=true" >> "$GITHUB_OUTPUT" + echo "No prior release tag — v${VERSION} will be the first release" + exit 0 + fi + LATEST_VERSION="${LATEST#v}" + if [[ "$VERSION" == "$LATEST_VERSION" ]]; then + echo "::error::v${VERSION} was already released — bump package.json before merging" + exit 1 + fi + TAG_FOR_VERSION="v${VERSION}" + LOWEST=$(printf '%s\n%s\n' "$TAG_FOR_VERSION" "$LATEST" | sort -V | head -1) + if [[ "$LOWEST" == "$TAG_FOR_VERSION" ]]; then + echo "::error::package.json version ${VERSION} is older than the latest release ${LATEST} — check for an accidental revert" + exit 1 + fi + echo "should_release=true" >> "$GITHUB_OUTPUT" + echo "Version ${VERSION} is newer than ${LATEST} — proceeding with release" + + # --target creates the tag as part of the release, so a failure can't leave an orphan tag. + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.version.outputs.version }} + run: | + gh release create "v${VERSION}" \ + --target "$GITHUB_SHA" \ + --title "Release v${VERSION}" \ + --generate-notes + + # Only when the gate passed: a gate failure means the version was already released, and that + # release belongs to an earlier run — deleting it here would destroy a shipped release. + - name: Roll back a partially published release + if: failure() && steps.release_gate.outcome == 'success' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.version.outputs.version }} + run: gh release delete "v${VERSION}" --yes --cleanup-tag || true dispatch-publish: - needs: process + needs: release runs-on: ubuntu-latest - # Only publish once a release is actually cut (release PR merged → tag + GitHub release). - if: needs.process.outputs.releases_created == 'true' + if: needs.release.outputs.released == 'true' steps: # Routes to publish-as-is.yml, the workflow npm authorizes for OIDC trusted publishing. # (publish.yml is not a configured trusted publisher and fails with ENEEDAUTH.) @@ -39,3 +97,6 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} event-type: publish-package-as-is + # repository_dispatch always runs the default branch, which may have moved on by then. + # The payload pins the publish to the exact commit that was released. + client-payload: '{"ref": "${{ github.sha }}"}' diff --git a/.mise/tasks/version b/.mise/tasks/version index 4ebaea6..b64d6c9 100755 --- a/.mise/tasks/version +++ b/.mise/tasks/version @@ -1,38 +1,23 @@ #!/usr/bin/env bash -#MISE description="Bump version - handles 'next' prerelease or syncs after 'latest'" -#USAGE flag "-t --tag " "Tag for versioning strategy (next|latest)" default="next" +#MISE description="Bump the package.json version that releases are cut from" +#USAGE flag "-b --bump " "Version part to bump (major|minor|patch)" default="patch" set -e -echo "Bumping version" -echo " > with tag: ${usage_tag}..." +case "${usage_bump}" in +major | minor | patch) ;; +*) + echo "❌ Unknown bump: ${usage_bump}" + echo " Use: --bump patch (default), --bump minor or --bump major" + exit 1 + ;; +esac -# When bumping "next": prerelease bump only -# When "latest": release-please handles it, but we sync "next" to be ahead -if [ "${usage_tag}" = "next" ]; then - echo "Bumping prerelease version for 'next' tag..." - bun pm version prerelease - echo " > Next version: $(jq -r '.version' package.json)" - -elif [ "${usage_tag}" = "latest" ]; then - echo "Syncing 'next' tag to be ahead of 'latest'..." - echo "(Note: 'latest' version should already be bumped by release-please)" - - # Get current version (should be latest after release-please) - LATEST_VERSION=$(jq -r '.version' package.json) - echo " > Latest version: $LATEST_VERSION" - - # Bump to next prerelease (X.Y.Z -> X.Y.(Z+1)-prerelease.0) - echo " > Bumping 'next' to be ahead of latest..." - bun pm version prerelease - - NEXT_VERSION=$(jq -r '.version' package.json) - echo " > Next version: $NEXT_VERSION" - -else - echo "❌ Unknown tag: ${usage_tag}" - echo " Use: --tag next (default) or --tag latest" - exit 1 -fi +echo "Bumping ${usage_bump} version" -echo "Version bump completed!" +# No git tag: release.yml compares package.json against the tags on main, so a local tag would +# look like an already-shipped release. The tag is created by the GitHub Release. +bun pm version "${usage_bump}" --no-git-tag-version + +echo " > New version: $(jq -r '.version' package.json)" +echo "Commit the bump and merge it to main to release it." diff --git a/.release-please-manifest.json b/.release-please-manifest.json deleted file mode 100644 index c5bd7d2..0000000 --- a/.release-please-manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - ".": "0.3.0" -} - diff --git a/CHANGELOG.md b/CHANGELOG.md index 168563a..7bf2e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,30 +2,28 @@ ## [0.3.0](https://github.com/jfrog/opencode-jfrog-plugin/compare/v0.2.0...v0.3.0) (2026-08-18) - ### ⚠ BREAKING CHANGES -* JFROG_URL/JFROG_ACCESS_TOKEN are no longer used for the JFrog Platform MCP; authentication is OAuth via JFROG_PLATFORM_URL. +- JFROG_URL/JFROG_ACCESS_TOKEN are no longer used for the JFrog Platform MCP; authentication is OAuth via JFROG_PLATFORM_URL. ### Features -* JFrog Platform MCP authenticates via OAuth ([#31](https://github.com/jfrog/opencode-jfrog-plugin/issues/31)) ([b2dcdbc](https://github.com/jfrog/opencode-jfrog-plugin/commit/b2dcdbce0ae5960a1cdfb96ce77c0ae67af9846f)) +- JFrog Platform MCP authenticates via OAuth ([#31](https://github.com/jfrog/opencode-jfrog-plugin/issues/31)) ([b2dcdbc](https://github.com/jfrog/opencode-jfrog-plugin/commit/b2dcdbce0ae5960a1cdfb96ce77c0ae67af9846f)) ## [0.2.0](https://github.com/jfrog/opencode-jfrog-plugin/compare/v0.1.0...v0.2.0) (2026-08-11) - ### Features -* **skills:** bump vendored jfrog-skills v0.14.0 -> v0.16.0 ([#22](https://github.com/jfrog/opencode-jfrog-plugin/issues/22)) ([0af4dd9](https://github.com/jfrog/opencode-jfrog-plugin/commit/0af4dd9bb1deb65cc96f14be7962a838246574f5)) -* **skills:** sync skills to v0.22.0 ([#25](https://github.com/jfrog/opencode-jfrog-plugin/issues/25)) ([016b99b](https://github.com/jfrog/opencode-jfrog-plugin/commit/016b99bbe0e18f446f57bd4cad3e9f1309ecf40c)) +- **skills:** bump vendored jfrog-skills v0.14.0 -> v0.16.0 ([#22](https://github.com/jfrog/opencode-jfrog-plugin/issues/22)) ([0af4dd9](https://github.com/jfrog/opencode-jfrog-plugin/commit/0af4dd9bb1deb65cc96f14be7962a838246574f5)) +- **skills:** sync skills to v0.22.0 ([#25](https://github.com/jfrog/opencode-jfrog-plugin/issues/25)) ([016b99b](https://github.com/jfrog/opencode-jfrog-plugin/commit/016b99bbe0e18f446f57bd4cad3e9f1309ecf40c)) ## [0.1.0](https://github.com/jfrog/opencode-jfrog-plugin/compare/v0.0.4...v0.1.0) (2026-06-29) - ### Features -* **mcp:** JFrog Platform remote MCP (token auth) + install hint ([#19](https://github.com/jfrog/opencode-jfrog-plugin/issues/19)) ([5948dae](https://github.com/jfrog/opencode-jfrog-plugin/commit/5948dae1a2bfabb02afbb07b06d60d1b255a7ecb)) +- **mcp:** JFrog Platform remote MCP (token auth) + install hint ([#19](https://github.com/jfrog/opencode-jfrog-plugin/issues/19)) ([5948dae](https://github.com/jfrog/opencode-jfrog-plugin/commit/5948dae1a2bfabb02afbb07b06d60d1b255a7ecb)) -## Changelog +--- -All notable changes to this project will be documented here by Release Please. +This file is frozen as of v0.3.0. Release notes for later versions are generated per release and +live on the [GitHub Releases](https://github.com/jfrog/opencode-jfrog-plugin/releases) page. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e7de40f..40e2c00 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,20 +20,27 @@ Thank you for your interest in contributing! mise run test mise run lint ``` -5. Commit using [Conventional Commits](https://www.conventionalcommits.org/) format: - - `feat: add new feature` - - `fix: resolve bug` - - `docs: update readme` - - `chore: update dependencies` -6. Push and open a Pull Request +5. Bump the `version` field in `package.json` — see [Releasing](#releasing) +6. Commit with a descriptive message ([Conventional Commits](https://www.conventionalcommits.org/) + style is welcome, but nothing enforces it) +7. Push and open a Pull Request ## Pull Request Guidelines -- PR titles must follow Conventional Commits format (enforced by CI) - Keep PRs focused on a single change - Include tests for new functionality - Ensure all checks pass before requesting review +## Releasing + +Releases are cut from the `version` field in `package.json`: merging a PR that bumps it to a +not-yet-released `X.Y.Z` creates the matching GitHub Release and publishes to npm. See +[RELEASE.md](./RELEASE.md) for the full flow. + +Merging to `main` without a version bump fails the Release workflow. That is by design — the +failure reads "already released", and it is how a missing bump gets noticed instead of silently +shipping nothing. + ## Code Style This project uses ESLint and Prettier. Run `mise run lint:fix` to auto-fix issues. diff --git a/README.md b/README.md index 4bbd823..9c8f717 100644 --- a/README.md +++ b/README.md @@ -262,12 +262,16 @@ Tasks are run with [mise](https://mise.jdx.dev/): ## Release -Releases are automated with [release-please](https://github.com/googleapis/release-please): -merge Conventional-Commit PRs (`feat:`, `fix:`, …) to `main`, and release-please opens a -release PR that bumps the version and updates the changelog. Merging that PR tags the -release and publishes to npm. See [RELEASE.md](RELEASE.md) for details. +Releases are cut automatically by [`.github/workflows/release.yml`](.github/workflows/release.yml) +when a commit lands on `main` with a `package.json` version newer than the latest release tag. +The workflow creates a GitHub Release (and tag), then dispatches npm publishing via +[`publish-as-is.yml`](.github/workflows/publish-as-is.yml) (OIDC trusted publishing). -> Do **not** hand-edit the `version` in `package.json` — release-please manages it. +1. In your PR, bump the `version` field in [`package.json`](package.json). +2. Merge to `main`. + +Release notes are generated from merged PRs/commits since the last tag (`gh release create +--generate-notes`). `CHANGELOG.md` is not auto-updated. --- diff --git a/RELEASE.md b/RELEASE.md index 8326287..820828c 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,218 +1,49 @@ # Release Process -This project uses Release Please and Npm Trusted Publishing for automated releases. +This project cuts releases from the `version` field in [`package.json`](package.json). +Every push to `main` compares that version against the latest `vX.Y.Z` tag: -It follows two release channels: +- **Newer than latest tag** → create a GitHub Release and dispatch npm publishing. +- **Equal to latest tag** → fail with a clear "already released" error. +- **Older than latest tag** → fail with a revert warning. -- **Pre-release**: Normal PRs merged to main create `x.x.x-next.J` versions published to the `next` npm dist-tag for testing and feedback. -- **Stable Releases**: Release PRs merged to main create computed version and publish to the `latest` npm dist-tag. +There is no commit-message marker, no manual tag push, and no release-please bot. -You can also trigger manual releases in the follow ways: +> **Note:** v0.3.1 is the first version released end to end through this path. Watch that run +> before trusting it unattended. -- Push a tag in the format `v{semver}` (e.g. `v1.2.3`) -- Run the `publish.yml` workflow manually from the GitHub Actions tab and supply a channel 'latest' or 'next'. +## Cutting a release +1. In your PR, bump the `version` field in `package.json` to a not-yet-released semver + (`mise run version` bumps the patch; `--bump minor` / `--bump major` for the rest). +2. Merge to `main`. -## First Release +[`.github/workflows/release.yml`](.github/workflows/release.yml) creates the GitHub Release +(`gh release create --target`, with `--generate-notes`), then dispatches +[`publish-as-is.yml`](.github/workflows/publish-as-is.yml) for npm OIDC trusted publishing. The +dispatch carries the released commit SHA, so the published tarball is built from exactly that +commit even if `main` has moved on. -Before automated releases will work, you need to perform the first release manually. +## NPM Trusted Publishing -Why: +This project uses [NPM Trusted Publishing](https://docs.npmjs.com/trusted-publishers) with GitHub +Actions. No npm tokens are needed — authentication is handled automatically via OIDC. -- This uses [Npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers). -- The first release creates the npm package on npmjs.com. -- This then allows you to setup trusted publishing with GitHub Actions for future releases. +**Trusted publisher configuration:** -### Steps +- **Workflow filename**: `publish-as-is.yml` (not `publish.yml`) -1. make sure the `package.json` is correct: - - is the version `0.0.1` ? - - is the pkg name correct? Did you forget to set the scope if needed? - - do you have the right keywords? - - do you have the right repository field? - - do you have the right author field? +When a release succeeds, the publish workflow: -2. run `npm login` to authenticate with npm. +1. Builds the module (`mise run build`) +2. Publishes to npm with OIDC authentication (`mise run publish`) -3. run `mise build` to build the module. +## First release / npm setup -4. run `mise publish --otp {your-2fa-code}` to publish the first version. +Before automated publishes work, the package must exist on npmjs.com and trusted publishing must +be configured. See npm's [Trusted Publishers](https://docs.npmjs.com/trusted-publishers) docs. -5. Go to your npm package settings on npmjs.com and add a trusted publisher for GitHub Actions with: - - **Organization or user**: Your GitHub username/org - - **Repository**: Your repository name - - **Workflow filename**: `publish.yml` (the release workflow filename) +## Changelog -6. [Restrict token access](https://docs.npmjs.com/trusted-publishers#recommended-restrict-token-access-when-using-trusted-publishers) for maximum security. - - -## Release Workflow - -### Conventional Commits - -We follow [Conventional Commits](https://www.conventionalcommits.org/) specification: - -- `fix:` patches -- `feat:` minor features -- `feat!:` or `fix!:` breaking changes - -### Pre-1.0 Versioning - -While version is `0.x.x`, breaking changes bump **minor** version. - -### Release Process - -1. Push commits to `main` branch -2. Release Please will: - - Analyze commits - - Determine version bump - - Update `package.json` - - Update `CHANGELOG.md` - - Create a release PR - -3. Review and merge the Release Please PR - -### Commit Message Examples - -- `fix: resolve task tracking issue` -- `feat: add global task support` -- `feat!: change task management API` -- `docs: improve README` -- `chore: update dependencies` - -## Advanced Release Features - -### Force a Specific Version - -Use the `Release-As` footer in your commit message to force a specific version, bypassing conventional commit analysis: - -```bash -git commit --allow-empty -m "chore: release 2.0.0" -m "Release-As: 2.0.0" -``` - -This creates a commit: - -``` -chore: release 2.0.0 - -Release-As: 2.0.0 -``` - -Release Please will open a PR for version `2.0.0` regardless of commit message types. - -### Update Extra Files During Release - -If you have version numbers in other files beyond `package.json`, configure them in `release-please-config.json`: - -```json -{ - "extra-files": [ - "src/version.ts", - { - "type": "generic", - "path": "docs/VERSION.md" - }, - { - "type": "yaml", - "path": ".tool-versions", - "jsonpath": "$.node" - } - ] -} -``` - -**Supported file types:** - -- Generic files (any type) -- JSON files (with JSONPath) -- YAML files (with JSONPath) -- XML files (with XPath) -- TOML files (with JSONPath) - -### Magic Comments for Version Markers - -Use inline comments to mark where versions should be updated: - -```javascript -// x-release-please-version -const VERSION = '1.0.0'; - -// x-release-please-major -const MAJOR = '1'; -``` - -Or use block markers: - -```markdown - - -- Current version: 1.0.0 - -``` - -Available markers: - -- `x-release-please-version` - Full semver -- `x-release-please-major` - Major number -- `x-release-please-minor` - Minor number -- `x-release-please-patch` - Patch number - -## Do Not - -- Manually edit Release Please PRs -- Manually create GitHub releases -- Modify version numbers directly - -## Publishing - -Releases are automatically published to NPM when the Release Please PR is merged. - -### NPM Trusted Publishing - -This project uses [NPM Trusted Publishing](https://docs.npmjs.com/trusted-publishers) with GitHub Actions. No npm tokens are needed - authentication is handled automatically via OIDC (OpenID Connect). - -**How it works:** - -- Each publish uses short-lived, cryptographically-signed tokens specific to your workflow -- Tokens cannot be extracted or reused -- No need to manage or rotate long-lived credentials -- Automatic provenance attestations prove where and how your package was built - -**Setup required:** - -1. Go to your npm package settings on npmjs.com -2. Add a trusted publisher for GitHub Actions with: - - **Organization or user**: Your GitHub username/org - - **Repository**: Your repository name - - **Workflow filename**: `publish.yml` (the release workflow filename) -3. Optionally, [restrict token access](https://docs.npmjs.com/trusted-publishers#recommended-restrict-token-access-when-using-trusted-publishers) for maximum security - -When you merge a release PR, the GitHub Actions workflow will automatically: - -1. Build the module -2. Publish to NPM with OIDC authentication -3. Generate and attach provenance attestations -4. Create a GitHub release - -### Manual Releases - -You can also manually trigger a release by pushing a tag in the format `v{semver}`: - -```bash -git tag v1.2.3 -git push origin v1.2.3 -``` - -This will: - -1. Trigger the release workflow -2. Build and publish to NPM using trusted publishing -3. Create a GitHub release - -Use manual releases for: - -- Hot-fixes outside the normal release cycle -- Bypassing Release Please when needed -- Direct version control over releases - -**Learn more:** See the [NPM Trusted Publishing documentation](https://docs.npmjs.com/trusted-publishers) for complete setup and best practices. +Release notes come from `gh release create --generate-notes` (merged PRs/commits since the last +tag). `CHANGELOG.md` is frozen as of v0.3.0 and is no longer updated. diff --git a/package.json b/package.json index 91f6dca..bcd0e6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jfrog/opencode-jfrog-plugin", - "version": "0.3.0", + "version": "0.3.1", "description": "JFrog Plugin for seamless integration to Opencode", "author": { "name": "JFrog", diff --git a/release-please-config.json b/release-please-config.json deleted file mode 100644 index 5a4050c..0000000 --- a/release-please-config.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "packages": { - ".": {} - }, - "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", - "include-v-in-tag": true, - "include-component-in-tag": false, - "versioning": "prerelease", - "prerelease": true, - "bump-minor-pre-major": true, - "release-type": "node" -}