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
2 changes: 1 addition & 1 deletion .claude/skills/deploy-plugin/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ New plugins start at `1.0.0`. Use semver:
| New stream, new optional config field, new default content | MINOR (`1.x.0`) |
| Deleted/renamed stream, breaking config change | MAJOR (`x.0.0`) |

Every PR that modifies plugin files must include a version bump in `metadata.json`.
Versions only matter at merge, so they are compared against `main`. A PR that modifies an existing plugin must end with a `version` higher than the one on `main` — that is **one bump for the whole PR, not one per commit**, so don't bump again on each review round. A brand-new plugin has nothing on `main` to compare against: leave it at `1.0.0` until it merges.

**Breaking (MAJOR) changes — do not create a new major version without asking the user first.** It is often possible to avoid the break entirely. If a major version is genuinely needed:
- Create a new versioned folder (e.g. `v2/`) rather than modifying `v1/`
Expand Down
15 changes: 4 additions & 11 deletions .github/PULL_REQUEST_TEMPLATE/Add a new plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
Thanks for contributing a new community-authored SquaredUp plugin!
-->
## 🔌 Plugin overview

- **Plugin name**:
- **Purpose / problem solved**:
- **Primary audience** (e.g. platform teams, SREs, product teams):
- **Authentication method(s)** (e.g. OAuth, Username/Password, API Key):
---

## 🖼️ Plugin screenshots
<!--
Expand All @@ -16,25 +14,20 @@
### Plugin configuration
### Default dashboards

---

## 🧪 Testing

## 🧪 Test plan
<!--
Please describe the testing you have carried out for this new plugin
Validation is not testing. Describe how you tested this plugin against a live,
authenticated deployment in a SquaredUp organization.
-->

---

## ⚠️ Known limitations
<!--
Are there any known gaps, constraints, or follow-up ideas?
-->

---

## 📚 Checklist

- [ ] This PR adds a single plugin only
- [ ] Plugin, datastream and UI naming follow SquaredUp guidelines
- [ ] Logo added
- [ ] One or more dashboards added
Expand Down
19 changes: 6 additions & 13 deletions .github/PULL_REQUEST_TEMPLATE/Change to an existing plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
> Fixes an issue when authenticating with OAuth
-->

---

## 🔗 Related issue(s)
<!--
Link any related issues or discussions.
Expand All @@ -17,10 +15,7 @@
- Relates to #
-->

---

## 🧩 Plugin details

- **Plugin name**:
- **Type of change**:
- [ ] Bug fix
Expand All @@ -30,12 +25,14 @@
- [ ] Documentation / metadata / logo
- [ ] Other (please describe):

---
## 🧪 Testing
<!--
Validation is not testing. Describe how you tested this plugin against a live,
authenticated deployment in a SquaredUp organization.
-->

## ⚠️ Breaking changes

Does this PR introduce any breaking changes?

- [ ] No
- [ ] Yes (please describe):

Expand All @@ -44,17 +41,13 @@ If yes, describe:
- Who is impacted
- Any migration steps

---

## 📚 Documentation

- [ ] Documentation updated
- [ ] No documentation changes needed

---

## ✅ Checklist

- [ ] This PR changes a single plugin only
- [ ] No secrets or credentials included
- [ ] Plugin, datastream and UI naming follow SquaredUp guidelines
- [ ] I agree to the [Code of Conduct](CODE_OF_CONDUCT.md)
4 changes: 0 additions & 4 deletions .github/PULL_REQUEST_TEMPLATE/Miscellaneous change.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@
Describe what this PR changes.
-->

---

## 🔍 Scope of change

- [ ] Documentation only
- [ ] Repository metadata or configuration
- [ ] CI / automation
- [ ] Other (please describe):

---

## 📚 Checklist

- [ ] I agree to the [Code of Conduct](CODE_OF_CONDUCT.md)
147 changes: 130 additions & 17 deletions .github/workflows/pr-run.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,116 @@ jobs:
if [ -z "$plugin_paths" ]; then
echo "No plugins were modified in this PR."
echo "plugins_modified=false" >> $GITHUB_OUTPUT
else
echo "Found modified plugin(s):"
echo "$plugin_paths"
echo "plugins_modified=true" >> $GITHUB_OUTPUT
echo "plugin_paths<<EOF" >> $GITHUB_OUTPUT
echo "$plugin_paths" >> $GITHUB_OUTPUT
echo "existing_modified=false" >> $GITHUB_OUTPUT
exit 0
fi

echo "Found modified plugin(s):"
echo "$plugin_paths"

# A PR that deletes a plugin version folder still lists it as touched, but it
# cannot be validated or deployed - the CLI reports "Path not found". Split the
# paths that still exist so only those reach the version, validate and deploy
# steps; the full list stays for the one-plugin-per-PR check, which must still
# count a deleted plugin as touched.
existing_paths=$(while IFS= read -r p; do if [ -d "$p" ]; then printf '%s\n' "$p"; fi; done <<< "$plugin_paths")
removed_paths=$(while IFS= read -r p; do if [ ! -d "$p" ]; then printf '%s\n' "$p"; fi; done <<< "$plugin_paths")

if [ -n "$removed_paths" ]; then
echo "Removed in this PR (skipped for validate and deploy):"
echo "$removed_paths"
fi

echo "plugins_modified=true" >> $GITHUB_OUTPUT
echo "plugin_paths<<EOF" >> $GITHUB_OUTPUT
echo "$plugin_paths" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

if [ -n "$existing_paths" ]; then
echo "existing_modified=true" >> $GITHUB_OUTPUT
echo "existing_paths<<EOF" >> $GITHUB_OUTPUT
echo "$existing_paths" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "existing_modified=false" >> $GITHUB_OUTPUT
fi

- name: Install & Configure SquaredUp CLI
# Plugin paths come from the contributor's branch, so they are untrusted input.
# They are passed through env rather than interpolated into the script: GitHub
# expands ${{ }} before Bash parses it, so a crafted directory name would
# otherwise run as shell code on the runner.
- name: Check PR scope & version bumps
id: checks
if: steps.detect.outputs.plugins_modified == 'true'
env:
PLUGIN_PATHS: ${{ steps.detect.outputs.plugin_paths }}
EXISTING_PATHS: ${{ steps.detect.outputs.existing_paths }}
run: |
checks_failed=false

# One plugin per PR. Several version folders of the same plugin are fine,
# so that deprecating in v1 alongside adding v2 stays a single PR.
plugin_names=$(printf '%s\n' "$PLUGIN_PATHS" | cut -d/ -f2 | sort -u)
plugin_count=$(echo "$plugin_names" | wc -l | tr -d ' ')

if [ "$plugin_count" -gt 1 ]; then
echo "[FAIL] This PR modifies ${plugin_count} plugins:"
echo "$plugin_names" | sed 's/^/ - /'
echo "Raise one pull request per plugin - see REVIEW.md."
checks_failed=true
else
echo "[PASS] Single plugin modified: ${plugin_names}"
fi
echo ""

# Every change to an existing plugin must increase metadata.json version.
while IFS= read -r plugin_path; do
if [ -z "$plugin_path" ]; then
continue
fi

if [ ! -f "${plugin_path}/metadata.json" ]; then
echo "[FAIL] ${plugin_path} has no metadata.json"
checks_failed=true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
continue
fi

new_version=$(jq -r '.version' "${plugin_path}/metadata.json")

# The major version must match the version folder: v2/ holds 2.x.y
folder_major=$(basename "${plugin_path}")
folder_major=${folder_major#v}
if [ "${new_version%%.*}" != "$folder_major" ]; then
echo "[FAIL] ${plugin_path} is version ${new_version} - a plugin in v${folder_major}/ must be ${folder_major}.x.y"
checks_failed=true
fi

if ! old_metadata=$(git show "origin/main:${plugin_path}/metadata.json" 2>/dev/null); then
echo "[SKIP] ${plugin_path} is new - no previous version to compare"
continue
fi

old_version=$(echo "$old_metadata" | jq -r '.version')

if [ "$new_version" = "$old_version" ]; then
echo "[FAIL] ${plugin_path} version is unchanged (${old_version}) - bump it in metadata.json"
checks_failed=true
elif [ "$(printf '%s\n%s\n' "$old_version" "$new_version" | sort -V | head -1)" != "$old_version" ]; then
echo "[FAIL] ${plugin_path} version decreased (${old_version} -> ${new_version}) - it can never decrease"
checks_failed=true
else
echo "[PASS] ${plugin_path} version bumped ${old_version} -> ${new_version}"
fi
done <<< "$EXISTING_PATHS"

if [ "$checks_failed" = "true" ]; then
echo ""
echo "One or more repository checks failed."
exit 1
fi

- name: Install & Configure SquaredUp CLI
if: steps.detect.outputs.existing_modified == 'true'
env:
SQUAREDUP_API_KEY: ${{ secrets.SQUAREDUP_API_KEY }}
run: |
Expand All @@ -44,7 +143,9 @@ jobs:

- name: Validate modified plugins
id: validate
if: steps.detect.outputs.plugins_modified == 'true'
if: steps.detect.outputs.existing_modified == 'true'
env:
EXISTING_PATHS: ${{ steps.detect.outputs.existing_paths }}
run: |
validation_failed=false

Expand All @@ -66,7 +167,7 @@ jobs:
validation_failed=true
fi
echo ""
done <<< "${{ steps.detect.outputs.plugin_paths }}"
done <<< "$EXISTING_PATHS"

if [ "$validation_failed" = "true" ]; then
echo "One or more plugins failed validation."
Expand All @@ -75,17 +176,21 @@ jobs:

- name: Deploy modified plugins
id: deploy
if: steps.detect.outputs.plugins_modified == 'true'
if: steps.detect.outputs.existing_modified == 'true'
env:
EXISTING_PATHS: ${{ steps.detect.outputs.existing_paths }}
run: |
while IFS= read -r plugin_path; do
echo "Deploying ${plugin_path}..."
squaredup deploy "${plugin_path}" --suffix "${{ github.event.pull_request.number }}" --force
echo "Deployed ${plugin_path} successfully."
echo ""
done <<< "${{ steps.detect.outputs.plugin_paths }}"
done <<< "$EXISTING_PATHS"

- name: Summary
if: always()
env:
PLUGIN_PATHS: ${{ steps.detect.outputs.plugin_paths }}
run: |
OUT=/tmp/summary.md

Expand All @@ -101,20 +206,28 @@ jobs:
echo "### 📦 Modified Plugins" >> $OUT
while IFS= read -r plugin_path; do
echo "- \`${plugin_path}\`" >> $OUT
done <<< "${{ steps.detect.outputs.plugin_paths }}"
done <<< "$PLUGIN_PATHS"
echo "" >> $OUT

echo "### 📋 Results" >> $OUT
echo "| Step | Status |" >> $OUT
echo "|------|--------|" >> $OUT

validate_conclusion="${{ steps.validate.conclusion }}"
deploy_conclusion="${{ steps.deploy.conclusion }}"
# A failed earlier step skips the later ones, so report skipped as skipped
# rather than folding it into a failure.
status_of() {
case "$1" in
success) echo "✅ Passed" ;;
skipped|"") echo "⏭️ Skipped" ;;
*) echo "❌ Failed" ;;
esac
}

[ "$validate_conclusion" = "success" ] && v_status="✅ Passed" || v_status="❌ Failed"
[ "$deploy_conclusion" = "success" ] && d_status="🚀 Deployed" || d_status="⏭️ Skipped"
deploy_conclusion="${{ steps.deploy.conclusion }}"
[ "$deploy_conclusion" = "success" ] && d_status="🚀 Deployed" || d_status="$(status_of "$deploy_conclusion")"

echo "| Validation | ${v_status} |" >> $OUT
echo "| Scope & version | $(status_of "${{ steps.checks.conclusion }}") |" >> $OUT
echo "| Validation | $(status_of "${{ steps.validate.conclusion }}") |" >> $OUT
echo "| Deployment | ${d_status} |" >> $OUT
echo "" >> $OUT

Expand Down
Loading
Loading