Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright (c) JFrog Ltd. 2026
#
# Cuts a GitHub Release when a release marker is merged to main.
# Full flow and rationale: CONTRIBUTING.md#releasing
name: Release

on:
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
# Full history, so the tag check below can see existing tags.
- uses: actions/checkout@v5
with:
fetch-depth: 0

# Subject line only, not the whole message. MSG goes through env rather than string
# interpolation, so a crafted commit subject can't inject shell.
- name: Detect release marker in commit subject
id: detect
env:
MSG: ${{ github.event.head_commit.message }}
run: |
SUBJECT=$(printf '%s\n' "$MSG" | head -1)
if printf '%s' "$SUBJECT" | grep -qE '\[(major|minor|patch)\]'; then
echo "triggered=true" >> "$GITHUB_OUTPUT"
else
echo "triggered=false" >> "$GITHUB_OUTPUT"
fi

# plugin.json is canonical; marketplace.json carries its own copy, so the two are
# cross-checked here as well as by the validate-version PR check.
- name: Read version from the plugin manifest
if: steps.detect.outputs.triggered == 'true'
id: version
run: |
set -euo pipefail
VERSION=$(jq -er '.version' plugin/.claude-plugin/plugin.json)
if ! printf '%s' "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::error::plugin.json version '$VERSION' is not X.Y.Z — refusing to release"
exit 1
fi
MARKET_VERSION=$(jq -er '.plugins[] | select(.name == "jfrog") | .version' marketplace.json)
if [ "$VERSION" != "$MARKET_VERSION" ]; then
echo "::error::plugin.json is $VERSION but marketplace.json lists $MARKET_VERSION — sync them before releasing"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

# A tag exists only if that version was released, so this catches a marker that was merged
# without a manifest bump.
- name: Refuse to re-release an existing version
if: steps.detect.outputs.triggered == 'true'
run: |
TAG="v${{ steps.version.outputs.version }}"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "::error::$TAG already exists — bump the plugin manifests before merging a release marker"
exit 1
fi

# NOTE: no plugin-layout validator exists in this repo to gate the release on. If one
# lands, run it as a step here. See CONTRIBUTING.md#releasing.

# Tracked files at HEAD only, so nothing left on the runner can end up in the zip.
- name: Package release artifact
if: steps.detect.outputs.triggered == 'true'
run: git archive --format=zip --output=release.zip HEAD -- ':(exclude).github'

# --target creates the tag as part of the release, so a failure can't leave an orphan tag.
- name: Create GitHub Release
if: steps.detect.outputs.triggered == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "v${{ steps.version.outputs.version }}" \
release.zip \
--target "$GITHUB_SHA" \
--title "Release v${{ steps.version.outputs.version }}" \
--generate-notes
27 changes: 27 additions & 0 deletions .github/workflows/validate-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright (c) JFrog Ltd. 2026
name: Validate version

on:
pull_request:
branches: [main]

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

# This repo has two manifests that both carry the version: the plugin's own
# plugin/.claude-plugin/plugin.json, and marketplace.json which lists it for the
# marketplace. plugin.json is canonical; this check keeps marketplace.json in step with it
# so a release can't ship two different version numbers.
- name: Check version consistency
run: |
set -euo pipefail
VERSION=$(jq -er '.version' plugin/.claude-plugin/plugin.json)
MARKET_VERSION=$(jq -er '.plugins[] | select(.name == "jfrog") | .version' marketplace.json)
if [ "$VERSION" != "$MARKET_VERSION" ]; then
echo "::error::Version mismatch: plugin/.claude-plugin/plugin.json is $VERSION but marketplace.json lists $MARKET_VERSION"
exit 1
fi
echo "Versions consistent: $VERSION"
55 changes: 55 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Contributing to JFrog VS Code Plugin

Thank you for your interest in contributing! This project is maintained by JFrog and licensed under the [Apache License 2.0](LICENSE).

## Contributor License Agreement (CLA)

All contributors must sign the [JFrog CLA](https://jfrog.com/cla/) before contributions can be merged. A CLA check runs automatically on every pull request — follow the prompts to sign if you haven't already.

## How to Contribute

1. **Fork** the repository and create a feature branch from `main`.
2. Make your changes, ensuring they follow the existing code style and project conventions.
3. **Commit** with a clear, descriptive message.
4. Open a **pull request** against `main` with a summary of what changed and why.

## Releasing

To cut a release:

1. In your PR, bump `.version` in [`plugin/.claude-plugin/plugin.json`](plugin/.claude-plugin/plugin.json) and sync the matching entry in [`marketplace.json`](marketplace.json) to match. `plugin.json` is canonical; the `validate-version` PR check enforces that the two agree.
2. Merge to `main` with `[major]`, `[minor]`, or `[patch]` in the commit **subject** - the first
line. A marker further down in the body is ignored on purpose: this repo squash-merges, and
GitHub pre-fills the squash body from the branch commits or the PR description, either of
which may quote a marker while only documenting it.

The marker only decides *whether* to release; the version comes from the manifest either way, so the bump is reviewed in the PR that makes it. There is no bot push to `main`. Merging a marker without bumping the manifests fails the release rather than re-tagging a shipped version.

The workflow reads the version from `plugin.json`, confirms `marketplace.json` agrees, refuses to continue if that version is already tagged, packages the tracked files at `HEAD` (minus `.github/`) into `release.zip`, and creates the `vX.Y.Z` tag as part of publishing the GitHub Release.

Two things to know before changing it:

- There is no plugin-layout validator in this repo, so a release is gated only on the two manifests agreeing. If a validator is added, run it as a step inside the release job as well: a separate workflow triggered by the same push can be red while a release still goes out.
- The tag is created by the release, not before it. `gh release create --target` does both in one API call, so a failed run can't leave a tag behind with no release attached to it.

## Reporting Issues

Open a [GitHub issue](https://github.com/jfrog/vscode-plugin/issues) with:

- A clear title and description of the problem.
- Steps to reproduce (if applicable).
- Expected vs. actual behavior.

## Code Guidelines

- Keep changes focused — one logical change per PR.
- Follow existing patterns and naming conventions in the codebase.
- Do not commit secrets, credentials, or API keys.

## Code of Conduct

Be respectful and constructive. We are committed to providing a welcoming and inclusive experience for everyone.

## Questions?

Reach out to the JFrog DevRel team at devrel@jfrog.com.