Skip to content

ci(product-plugins): add publish-on-merge and version-bump CI#10

Merged
gjvis merged 6 commits into
mainfrom
opt-877
Jun 24, 2026
Merged

ci(product-plugins): add publish-on-merge and version-bump CI#10
gjvis merged 6 commits into
mainfrom
opt-877

Conversation

@tessl-manoj

@tessl-manoj tessl-manoj commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

What & why

product-plugins has no CI — plugin publishing is manual (tessl plugin publish). That let skill-optimizer 0.9.1 get published from an unmerged branch. This adds the two pieces from OPT-877:

  1. Publish on merge to mainpublish.yml publishes every plugin whose files changed in the pushed commit.
  2. PR check: version bump requiredversion-check.yml fails a PR if a changed plugin's published version wasn't bumped forward.

Together they close the gap: the PR gate guarantees a version bump rides along with any plugin change, so the publish on main always has a fresh version to publish.

Design

A shared script defines "what is a plugin" and "what changed" in one place; both workflows build on it.

File Role
.github/scripts/changed-plugins.sh Lists top-level dirs containing .tessl-plugin/plugin.json whose files changed between two git refs
.github/scripts/check-version-bump.sh For each changed plugin, requires .tessl-plugin/plugin.json version to be strictly greater than the base ref (new plugins pass)
.github/scripts/publish-changed.sh Runs tessl plugin publish <dir> for each changed plugin; falls back to <sha>^ when GitHub sends an all-zero before SHA
.github/workflows/version-check.yml pull_requestmain: runs the bump check
.github/workflows/publish.yml pushmain: installs the CLI via tesslio/setup-tessl@v2, then publishes changed plugins; concurrency guard serializes overlapping merges

Notes

  • Scope: plugins are auto-discovered (any top-level dir with .tessl-plugin/plugin.json), so this covers skill-optimizer today and any plugin added later — no per-plugin wiring.
  • Version source of truth is .tessl-plugin/plugin.json, not the tessl.json vendored-deps manifest.
  • Scripts use only bash + jq (preinstalled on ubuntu-latest); semver comparison via sort -V.

Required before this works

  • A TESSL_TOKEN repository secret with publish access to the tessl workspace — publish.yml authenticates with it. (Added by @gareth.)

Tested locally

  • only-.github change → no plugins detected
  • plugin file change → plugin detected
  • no bump → ❌ fail · forward bump → ✅ pass · backwards bump → ❌ fail · brand-new plugin → ✅ pass
  • both workflow YAMLs parse; all scripts pass bash -n

Follow-up to watch on first run

skill-optimizer/tessl.json declares a tessl-labs/tile-creator vendored dependency. No tessl install step precedes publish (it can pull unpublished deps and break). If the first real publish needs deps resolved, that's a one-line addition.

Closes OPT-877.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c1cb9060-d0a2-4084-84f5-c7e06753fbbc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Three Bash scripts and two GitHub Actions workflows are added to automate plugin CI/CD. A shared changed-plugins.sh helper identifies plugin directories changed between two Git refs. A check-version-bump.sh script enforces strictly increasing semver on PRs, and a publish-changed.sh script publishes only modified plugins on pushes to main.

Changes

Plugin CI/CD automation

Layer / File(s) Summary
Shared changed-plugin discovery helper
.github/scripts/changed-plugins.sh
New script that accepts base-ref/head-ref arguments, computes a deduplicated list of top-level changed directories from git diff, and outputs only those containing a .tessl-plugin/plugin.json manifest at the head ref.
Version bump enforcement script and PR workflow
.github/scripts/check-version-bump.sh, .github/workflows/version-check.yml
check-version-bump.sh delegates to changed-plugins.sh, extracts the version field at each ref via git show and jq, and exits non-zero if any plugin's head version is not strictly greater than its base version. version-check.yml runs this check on all PRs targeting main with full git history.
Selective publish script and push-to-main workflow
.github/scripts/publish-changed.sh, .github/workflows/publish.yml
publish-changed.sh handles the all-zero SHA edge case, discovers changed plugins, and runs tessl plugin publish for each inside a GitHub Actions log group. publish.yml triggers on pushes to main, serialises concurrent runs, installs the Tessl CLI, and invokes the script with the push event's before/after SHAs and a TESSL_TOKEN secret.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: new CI for publish-on-merge and version-bump enforcement.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
.github/scripts/check-version-bump.sh (1)

25-30: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Confirm sort -V matches the versioning contract.

sort -V is only semver-safe for bare X.Y.Z values. If plugin manifests can ever carry prereleases or build metadata, this check will compare them incorrectly. Please confirm .tessl-plugin/plugin.json is constrained to simple triples, or swap in a real semver comparator.

🤖 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/scripts/check-version-bump.sh around lines 25 - 30, The version_gt
function relies on sort -V for version comparison, which only correctly handles
plain X.Y.Z format and will fail with prerelease versions or build metadata.
Either verify that the .tessl-plugin/plugin.json file is constrained to only
simple X.Y.Z version triples through documentation or validation, or replace the
version comparison logic in the version_gt function with a proper semver
comparator that can handle full semantic versioning including prerelease and
build metadata components.
🤖 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/scripts/changed-plugins.sh:
- Around line 21-26: The for loop iterating over $changed_dirs will word-split
on whitespace, causing plugin directory names containing spaces or tabs to be
truncated and skipped. Replace the `for dir in $changed_dirs;` loop with a while
read loop that properly handles whitespace-containing directory names, using
`while IFS= read -r dir;` with appropriate input redirection or piping to safely
iterate over each directory without splitting on whitespace.

In @.github/workflows/publish.yml:
- Around line 13-32: In the publish job, replace the mutable version tags for
both actions/checkout and tesslio/setup-tessl with their specific commit SHAs
instead of `@v4` and `@v2` respectively to prevent tag-retargeting attacks. Add an
explicit permissions block at the job level after runs-on to limit access to
only what is required for the workflow. Finally, add persist-credentials: false
to the checkout action step to prevent credential persistence and improve
security when handling sensitive secrets like TESSL_TOKEN.

In @.github/workflows/version-check.yml:
- Around line 8-22: Pin the actions/checkout action to a full-length commit SHA
instead of the `@v4` version tag to prevent supply chain attacks. Replace
actions/checkout@v4 with the full commit SHA format. Additionally, add a
permissions section at the job level (check-version-bump) to restrict the
GITHUB_TOKEN to only contents: read access. Finally, add persist-credentials:
false to the checkout step's with configuration to reduce the attack surface by
preventing credential persistence.

---

Nitpick comments:
In @.github/scripts/check-version-bump.sh:
- Around line 25-30: The version_gt function relies on sort -V for version
comparison, which only correctly handles plain X.Y.Z format and will fail with
prerelease versions or build metadata. Either verify that the
.tessl-plugin/plugin.json file is constrained to only simple X.Y.Z version
triples through documentation or validation, or replace the version comparison
logic in the version_gt function with a proper semver comparator that can handle
full semantic versioning including prerelease and build metadata components.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 06892dc2-26d3-44fb-8813-67dabe0da703

📥 Commits

Reviewing files that changed from the base of the PR and between 35eab39 and f2a02b0.

📒 Files selected for processing (5)
  • .github/scripts/changed-plugins.sh
  • .github/scripts/check-version-bump.sh
  • .github/scripts/publish-changed.sh
  • .github/workflows/publish.yml
  • .github/workflows/version-check.yml

Comment thread .github/scripts/changed-plugins.sh Outdated
Comment thread .github/workflows/publish.yml
Comment thread .github/workflows/version-check.yml
…lugin discovery

Address CodeRabbit review on #10: SHA-pin actions/checkout and
tesslio/setup-tessl, add least-privilege contents:read permissions and
persist-credentials:false, and iterate changed dirs with while-read to
avoid word-splitting plugin names.
@tessl-manoj

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 24, 2026
@tessl-manoj
tessl-manoj requested a review from a team June 24, 2026 08:25
Gareth added a TESSL_TOKEN repo secret with publish access to the tessl
workspace; point the publish workflow at it.
Comment thread .github/scripts/changed-plugins.sh
@gjvis

gjvis commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Re CodeRabbit's sort -V nitpick on .github/scripts/check-version-bump.sh (versioning contract):

Confirmed — plugin manifest versions are bare X.Y.Z triples (skill-optimizer 0.14.0, review-plugin-creator 0.1.0), which sort -V orders correctly. No prerelease or build-metadata suffixes are used, so leaving sort -V as-is; tessl plugin publish would reject a malformed version downstream anyway.

(from Claude)

gjvis added 2 commits June 24, 2026 22:32
…e tip

changed-plugins.sh used a two-dot `git diff base head`. For a pull_request that
compares the head against the base branch's current tip, so once main advances
and touches a plugin while a PR is open, that plugin shows as "changed" by the
PR even when the PR never touched it - and version-check then demands a bump for
it. Switch to a three-dot range so detection is relative to the merge-base: only
what the branch actually changed. check-version-bump.sh still reads the base
version from the literal base ref, so a head version is still compared against
current main and accidental version collisions are still caught.

For the publish workflow (before/after on a linear main) the merge-base is the
before ref, so three-dot equals the previous two-dot range - no behaviour change
there.
… not base tip"

This reverts commit 1791501. The two-dot `git diff base head` it replaced was
already correct: github.event.pull_request.base.sha is the PR's merge-base, not
the base branch's current tip. Verified across all five version-check runs on
this PR (2026-06-22 to 2026-06-24, during which main advanced from 35eab39 to
df8fb2f): BASE_SHA was 35eab39 - the fork point - in every run, never the
moving main tip. So base..head already diffs against the merge-base and the
three-dot change was a behavioural no-op.
@gjvis
gjvis merged commit 0ae78b9 into main Jun 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants