diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d2912ae..2705aca 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "name": "indigo", "source": "./", "description": "Indigo home automation development toolkit \u2014 plugin development, API integration, and control page building", - "version": "1.9.1", + "version": "1.9.2", "repository": "https://github.com/simons-plugins/indigo-claude-plugin", "license": "MIT", "keywords": [ diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 3a10393..cc07d82 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "indigo", - "version": "1.9.1", + "version": "1.9.2", "description": "Indigo home automation development toolkit \u2014 plugin development, API integration, and control page building", "repository": "https://github.com/simons-plugins/indigo-claude-plugin" } diff --git a/docs/plugin-dev/workflows/README.md b/docs/plugin-dev/workflows/README.md new file mode 100644 index 0000000..db269d6 --- /dev/null +++ b/docs/plugin-dev/workflows/README.md @@ -0,0 +1,30 @@ +# Canonical CI workflows for Indigo plugins + +Drop-in GitHub Actions workflows for any Indigo plugin in the workspace. They auto-detect the plugin bundle (`*.indigoPlugin`) so the same files work in every repo without edits. + +## Files + +- [`version-check.yml`](version-check.yml) — runs on PR. Extracts `PluginVersion` from `Info.plist` and fails the check if the resulting tag (`v$VERSION` or bare `$VERSION`) already exists. Enforces the workspace rule that every PR bumps the version. +- [`create-release.yml`](create-release.yml) — runs on push to `main`/`master`. If a tag for the current `PluginVersion` doesn't yet exist, it zips the plugin bundle (excluding `.pyc`, `__pycache__`, IDE files) and creates a GitHub release tagged `v$VERSION` with auto-generated release notes and the zip attached. + +## Conventions + +- **Tag prefix**: `v$VERSION` (e.g. `v2026.0.3`). The version-check is conservative and rejects either prefixed or bare tags clashing with the new version. +- **Plugin bundle detection**: `find . -maxdepth 1 -iname "*.indigoPlugin" -type d`. The bundle must live at the repo root. Case-insensitive (`-iname`) so legacy bundles like `HeatmiserNeo.IndigoPlugin` are matched. The detected bundle name is used verbatim throughout (no suffix-stripping with `basename`), so the asset zip preserves the exact directory case (`HeatmiserNeo.IndigoPlugin.zip`, `Domio.indigoPlugin.zip`). +- **Action versions**: `actions/checkout@v4`, `softprops/action-gh-release@v2`. Both run on Node 20. + +## Usage + +Copy both YAML files to `.github/workflows/` in the target repo. No edits required — the workflows discover the plugin name at runtime. + +```bash +mkdir -p .github/workflows +cp /path/to/indigo-claude-plugin/docs/plugin-dev/workflows/version-check.yml .github/workflows/ +cp /path/to/indigo-claude-plugin/docs/plugin-dev/workflows/create-release.yml .github/workflows/ +``` + +Bump `PluginVersion` in `*.indigoPlugin/Contents/Info.plist`, open a PR. CI will gate the merge on a fresh version; merge to `main` triggers the release. + +## Coexistence with other CI + +These workflows do not interact with linting/test workflows. Repos with `tests.yml`, `test.yml`, `lint.yml`, etc. can keep them — they run as independent jobs. diff --git a/docs/plugin-dev/workflows/create-release.yml b/docs/plugin-dev/workflows/create-release.yml new file mode 100644 index 0000000..0633303 --- /dev/null +++ b/docs/plugin-dev/workflows/create-release.yml @@ -0,0 +1,81 @@ +name: Create Release + +on: + push: + branches: + - main + - master + +jobs: + create-release: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine plugin bundle + id: get_plugin + run: | + PLUGIN_BUNDLE_PATH=$(find . -maxdepth 1 -iname "*.indigoPlugin" -type d | head -1) + if [ -z "$PLUGIN_BUNDLE_PATH" ]; then + echo "Error: No .indigoPlugin directory found in repository root." >&2 + exit 1 + fi + PLUGIN_BUNDLE=$(basename "$PLUGIN_BUNDLE_PATH") + echo "plugin_bundle=$PLUGIN_BUNDLE" >> $GITHUB_OUTPUT + echo "Detected plugin bundle: $PLUGIN_BUNDLE" + + - name: Extract version from Info.plist + id: get_version + run: | + PLUGIN_BUNDLE="${{ steps.get_plugin.outputs.plugin_bundle }}" + VERSION=$(grep -A1 'PluginVersion' "${PLUGIN_BUNDLE}/Contents/Info.plist" | grep '' | sed 's/.*\(.*\)<\/string>.*/\1/') + if [ -z "$VERSION" ]; then + echo "Error: Could not extract PluginVersion from Info.plist." >&2 + exit 1 + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version: $VERSION" + + - name: Check if release already exists + id: check_release + run: | + VERSION="${{ steps.get_version.outputs.version }}" + if git rev-parse "v$VERSION" >/dev/null 2>&1; then + echo "exists=true" >> $GITHUB_OUTPUT + echo "Release v$VERSION already exists, skipping." + else + echo "exists=false" >> $GITHUB_OUTPUT + echo "Release v$VERSION does not exist, will create." + fi + + - name: Create plugin bundle zip + if: steps.check_release.outputs.exists == 'false' + run: | + PLUGIN_BUNDLE="${{ steps.get_plugin.outputs.plugin_bundle }}" + zip -r "${PLUGIN_BUNDLE}.zip" "${PLUGIN_BUNDLE}" \ + -x "*.pyc" \ + -x "*/__pycache__" \ + -x "*/__pycache__/*" \ + -x "*.sublime-project" \ + -x "*.sublime-workspace" \ + -x "*/.idea" \ + -x "*/.idea/*" \ + -x "*.bbproject" \ + -x "*.bbproject/*" + echo "Created ${PLUGIN_BUNDLE}.zip" + + - name: Create GitHub Release + if: steps.check_release.outputs.exists == 'false' + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ steps.get_version.outputs.version }} + name: Release v${{ steps.get_version.outputs.version }} + generate_release_notes: true + draft: false + prerelease: false + files: ${{ steps.get_plugin.outputs.plugin_bundle }}.zip diff --git a/docs/plugin-dev/workflows/version-check.yml b/docs/plugin-dev/workflows/version-check.yml new file mode 100644 index 0000000..7e8d7cd --- /dev/null +++ b/docs/plugin-dev/workflows/version-check.yml @@ -0,0 +1,53 @@ +name: Version Check + +on: + pull_request: + branches: + - main + - master + +jobs: + check-version: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine plugin bundle + id: get_plugin + run: | + PLUGIN_BUNDLE_PATH=$(find . -maxdepth 1 -iname "*.indigoPlugin" -type d | head -1) + if [ -z "$PLUGIN_BUNDLE_PATH" ]; then + echo "Error: No .indigoPlugin directory found in repository root." >&2 + exit 1 + fi + PLUGIN_BUNDLE=$(basename "$PLUGIN_BUNDLE_PATH") + echo "plugin_bundle=$PLUGIN_BUNDLE" >> $GITHUB_OUTPUT + echo "Detected plugin bundle: $PLUGIN_BUNDLE" + + - name: Extract version from Info.plist + id: get_version + run: | + PLUGIN_BUNDLE="${{ steps.get_plugin.outputs.plugin_bundle }}" + VERSION=$(grep -A1 'PluginVersion' "${PLUGIN_BUNDLE}/Contents/Info.plist" | grep '' | sed 's/.*\(.*\)<\/string>.*/\1/') + if [ -z "$VERSION" ]; then + echo "Error: Could not extract PluginVersion from Info.plist." >&2 + exit 1 + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version: $VERSION" + + - name: Check if tag already exists + run: | + VERSION="${{ steps.get_version.outputs.version }}" + if git rev-parse "v$VERSION" >/dev/null 2>&1; then + echo "::error::Version v$VERSION already exists as a git tag. Please update the PluginVersion in Info.plist." + exit 1 + fi + if git rev-parse "$VERSION" >/dev/null 2>&1; then + echo "::error::Version $VERSION already exists as a git tag. Please update the PluginVersion in Info.plist." + exit 1 + fi + echo "✓ Version $VERSION is new and can be released."