From 4a42b947bcacf668076e8b6d47145477f766da89 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Fri, 19 Dec 2025 18:06:39 +0100 Subject: [PATCH 1/4] feat(astrolabe): add Nextcloud App Store deployment automation Add complete CI/CD pipeline for automated Astrolabe app releases: - GitHub Actions workflow for build, sign, and publish - Makefile for app store package creation - Version synchronization between info.xml and package.json - CHANGELOG.md with v0.1.0 release notes feat: configure commitizen monorepo with independent versioning Enable independent versioning for three components using scope-based commits: - MCP Server (feat(mcp) or unscoped): v* tags, updates pyproject.toml + Chart.yaml:appVersion - Helm Chart (feat(helm)): nextcloud-mcp-server-* tags, updates Chart.yaml:version - Astrolabe App (feat(astrolabe)): astrolabe-v* tags, updates info.xml + package.json Changes: - Add .cz.toml configs for Astrolabe and Helm chart - Update root pyproject.toml with scope filtering and tag ignores - Create bump helper scripts (bump-mcp.sh, bump-helm.sh, bump-astrolabe.sh) - Add CONTRIBUTING.md with version management documentation - Create component-specific changelogs - Configure appVersion/version separation for Helm chart This allows each component to release independently while maintaining proper version tracking and changelog generation. --- .github/workflows/appstore-build-publish.yml | 84 ++++++++++++++ CHANGELOG.md | 7 ++ CONTRIBUTING.md | 116 +++++++++++++++++++ charts/nextcloud-mcp-server/.cz.toml | 24 ++++ charts/nextcloud-mcp-server/CHANGELOG.md | 18 +++ pyproject.toml | 15 ++- scripts/bump-astrolabe.sh | 14 +++ scripts/bump-helm.sh | 15 +++ scripts/bump-mcp.sh | 10 ++ third_party/astrolabe/.cz.toml | 25 ++++ third_party/astrolabe/.gitignore | 1 + third_party/astrolabe/CHANGELOG.md | 25 +++- third_party/astrolabe/Makefile | 81 +++++++++++++ third_party/astrolabe/package.json | 2 +- 14 files changed, 429 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/appstore-build-publish.yml create mode 100644 CONTRIBUTING.md create mode 100644 charts/nextcloud-mcp-server/.cz.toml create mode 100644 charts/nextcloud-mcp-server/CHANGELOG.md create mode 100755 scripts/bump-astrolabe.sh create mode 100755 scripts/bump-helm.sh create mode 100755 scripts/bump-mcp.sh create mode 100644 third_party/astrolabe/.cz.toml create mode 100644 third_party/astrolabe/Makefile diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml new file mode 100644 index 0000000..63b20f0 --- /dev/null +++ b/.github/workflows/appstore-build-publish.yml @@ -0,0 +1,84 @@ +name: Build and Publish Astrolabe App Release + +on: + release: + types: [published] + +env: + APP_NAME: astrolabe + APP_DIR: third_party/astrolabe + +jobs: + build-and-publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get version from tag + id: tag + run: | + echo "TAG=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Validate version in info.xml matches tag + working-directory: ${{ env.APP_DIR }} + run: | + INFO_VERSION=$(sed -n 's/.*\(.*\)<\/version>.*/\1/p' appinfo/info.xml | tr -d '\t') + if [ "$INFO_VERSION" != "${{ steps.tag.outputs.TAG }}" ]; then + echo "Version mismatch: info.xml has $INFO_VERSION but tag is ${{ steps.tag.outputs.TAG }}" + exit 1 + fi + echo "Version validated: $INFO_VERSION" + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + coverage: none + + - name: Checkout Nextcloud server (for signing) + uses: actions/checkout@v4 + with: + repository: nextcloud/server + ref: stable30 + path: server + + - name: Install dependencies and build + working-directory: ${{ env.APP_DIR }} + run: | + composer install --no-dev --optimize-autoloader + npm ci + npm run build + + - name: Setup signing certificate + run: | + mkdir -p $HOME/.nextcloud/certificates + echo "${{ secrets.APP_PRIVATE_KEY }}" > $HOME/.nextcloud/certificates/${{ env.APP_NAME }}.key + echo "${{ secrets.APP_PUBLIC_CRT }}" > $HOME/.nextcloud/certificates/${{ env.APP_NAME }}.crt + + - name: Build app store package + working-directory: ${{ env.APP_DIR }} + run: make appstore + + - name: Attach tarball to GitHub release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ env.APP_DIR }}/build/artifacts/${{ env.APP_NAME }}.tar.gz + asset_name: ${{ env.APP_NAME }}-${{ steps.tag.outputs.TAG }}.tar.gz + tag: ${{ github.ref }} + + - name: Upload to Nextcloud App Store + uses: R0Wi/nextcloud-appstore-push-action@v1.0.6 + with: + app_name: ${{ env.APP_NAME }} + appstore_token: ${{ secrets.APPSTORE_TOKEN }} + download_url: ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ github.ref_name }}/${{ env.APP_NAME }}-${{ steps.tag.outputs.TAG }}.tar.gz + app_private_key: ${{ secrets.APP_PRIVATE_KEY }} + nightly: ${{ github.event.release.prerelease }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 72b7c0d..842c6da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# Changelog - MCP Server + +All notable changes to the Nextcloud MCP Server will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [PEP 440](https://peps.python.org/pep-0440/). + ## v0.53.0 (2025-12-19) ### Feat diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..463962b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,116 @@ +# Contributing to Nextcloud MCP Server + +## Version Management + +This monorepo uses commitizen for version management with **independent versioning** for three components: + +### Components + +| Component | Scope | Bump Command | Tag Example | +|-----------|-------|--------------|-------------| +| MCP Server | `mcp` or none | `./scripts/bump-mcp.sh` | `v0.54.0` | +| Helm Chart | `helm` | `./scripts/bump-helm.sh` | `nextcloud-mcp-server-0.54.0` | +| Astrolabe App | `astrolabe` | `./scripts/bump-astrolabe.sh` | `astrolabe-v0.2.0` | + +### Commit Message Format + +Use conventional commits with **scopes** to target specific components: + +```bash +# MCP server changes +feat(mcp): add calendar sync API +fix(mcp): resolve authentication bug + +# Helm chart changes +feat(helm): add resource limits +docs(helm): update values documentation + +# Astrolabe app changes +feat(astrolabe): add dark mode toggle +fix(astrolabe): resolve search UI bug +``` + +**Unscoped commits** default to the MCP server: +```bash +feat: add new feature # → MCP server (v0.54.0) +``` + +### Release Workflow + +#### 1. Make Changes with Scoped Commits + +```bash +git commit -m "feat(astrolabe): add dark mode toggle" +git commit -m "feat(helm): add ingress annotations" +git commit -m "feat(mcp): add calendar sync" +``` + +#### 2. Bump Component Versions + +```bash +# Bump MCP server (reads commits with scope=mcp or unscoped) +./scripts/bump-mcp.sh +# → Creates tag: v0.54.0 +# → Updates: pyproject.toml, Chart.yaml:appVersion + +# Bump Helm chart (reads commits with scope=helm) +./scripts/bump-helm.sh +# → Creates tag: nextcloud-mcp-server-0.54.0 +# → Updates: Chart.yaml:version + +# Bump Astrolabe (reads commits with scope=astrolabe) +./scripts/bump-astrolabe.sh +# → Creates tag: astrolabe-v0.2.0 +# → Updates: info.xml, package.json +``` + +#### 3. Push Tags + +```bash +git push --follow-tags +``` + +### Changelog Filtering + +Each component maintains its own `CHANGELOG.md`: + +- **MCP Server**: `CHANGELOG.md` (root) - includes `feat(mcp):` and unscoped commits +- **Helm Chart**: `charts/nextcloud-mcp-server/CHANGELOG.md` - includes `feat(helm):` only +- **Astrolabe**: `third_party/astrolabe/CHANGELOG.md` - includes `feat(astrolabe):` only + +### Manual Version Bumps + +For specific increments: + +```bash +# Patch bump (0.53.0 → 0.53.1) +uv run cz bump --increment PATCH + +# Minor bump (0.53.0 → 0.54.0) +uv run cz bump --increment MINOR + +# Major bump (0.53.0 → 1.0.0) +uv run cz bump --increment MAJOR + +# For non-MCP components, use --config +cd charts/nextcloud-mcp-server +uv run cz --config .cz.toml bump --increment MINOR +``` + +### Versioning Philosophy + +- **MCP Server**: Follows PEP 440, `major_version_zero = true` (0.x.x for pre-1.0) +- **Helm Chart**: Follows PEP 440, starts at 0.53.0 (continues from current) +- **Astrolabe**: Follows PEP 440, `major_version_zero = true` (0.x.x for alpha/beta) + +### Chart.yaml Version vs appVersion + +The Helm chart has TWO version fields: + +- **`version`**: Chart packaging version (bumped by `feat(helm):`) + - Example: `0.53.0` → `0.54.0` when adding resource limits + +- **`appVersion`**: MCP server version being deployed (bumped by `feat(mcp):`) + - Example: `"0.53.0"` → `"0.54.0"` when MCP server releases + +This allows the chart to evolve independently from the application. diff --git a/charts/nextcloud-mcp-server/.cz.toml b/charts/nextcloud-mcp-server/.cz.toml new file mode 100644 index 0000000..c71bcdd --- /dev/null +++ b/charts/nextcloud-mcp-server/.cz.toml @@ -0,0 +1,24 @@ +[tool.commitizen] +name = "cz_conventional_commits" +version = "0.53.0" +tag_format = "nextcloud-mcp-server-$version" +version_scheme = "pep440" +update_changelog_on_bump = true +major_version_zero = true + +# Update chart version only (NOT appVersion) +version_files = [ + "Chart.yaml:^version:" +] + +# Ignore tags from other components +ignored_tag_formats = [ + "v*", # MCP server tags + "astrolabe-v*", # Astrolabe tags +] + +# Filter commits by scope +[tool.commitizen.customize] +changelog_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)\\(helm\\)(!)?:" +schema_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)\\(helm\\)(!)?:\\s.+" +message_template = "{{change_type}}(helm): {{message}}" diff --git a/charts/nextcloud-mcp-server/CHANGELOG.md b/charts/nextcloud-mcp-server/CHANGELOG.md new file mode 100644 index 0000000..4fb9d32 --- /dev/null +++ b/charts/nextcloud-mcp-server/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog - Helm Chart + +All notable changes to the Helm chart will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.53.0] - 2024-12-19 + +### Added +- Initial independent versioning release +- Support for Nextcloud MCP server deployment +- Qdrant subchart integration +- Ollama subchart integration +- Configurable resource limits +- Grafana dashboard annotations diff --git a/pyproject.toml b/pyproject.toml index 60b728b..cb142ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,14 +89,23 @@ version_scheme = "pep440" version_provider = "uv" update_changelog_on_bump = true major_version_zero = true + +# MCP server version files + Helm appVersion version_files = [ - "charts/nextcloud-mcp-server/Chart.yaml:appVersion", - "charts/nextcloud-mcp-server/Chart.yaml:version" + "charts/nextcloud-mcp-server/Chart.yaml:^appVersion:", ] + +# Ignore tags from other components ignored_tag_formats = [ - "nextcloud-mcp-server-*" + "nextcloud-mcp-server-*", # Helm chart tags + "astrolabe-v*", # Astrolabe tags ] +# Filter commits by scope (mcp or unscoped) +[tool.commitizen.customize] +changelog_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)(\\(mcp\\))?(!)?:" +schema_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)(\\(mcp\\))?(!)?:\\s.+" + [tool.ruff.lint] extend-select = ["I"] diff --git a/scripts/bump-astrolabe.sh b/scripts/bump-astrolabe.sh new file mode 100755 index 0000000..79e61cc --- /dev/null +++ b/scripts/bump-astrolabe.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Bump Astrolabe app version +set -e + +cd third_party/astrolabe + +echo "Bumping Astrolabe version..." +uv run cz --config .cz.toml bump --yes + +echo "✓ Astrolabe version bumped" +echo " - Updated: appinfo/info.xml, package.json" +echo " - Tag format: astrolabe-v\${version}" + +cd ../.. diff --git a/scripts/bump-helm.sh b/scripts/bump-helm.sh new file mode 100755 index 0000000..75d08e7 --- /dev/null +++ b/scripts/bump-helm.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Bump Helm chart version +set -e + +cd charts/nextcloud-mcp-server + +echo "Bumping Helm chart version..." +uv run cz --config .cz.toml bump --yes + +echo "✓ Helm chart version bumped" +echo " - Updated: Chart.yaml:version" +echo " - Tag format: nextcloud-mcp-server-\${version}" +echo " - Note: appVersion stays at MCP server version" + +cd ../.. diff --git a/scripts/bump-mcp.sh b/scripts/bump-mcp.sh new file mode 100755 index 0000000..29bb045 --- /dev/null +++ b/scripts/bump-mcp.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Bump MCP server version +set -e + +echo "Bumping MCP server version..." +uv run cz bump --yes + +echo "✓ MCP server version bumped" +echo " - Updated: pyproject.toml, Chart.yaml:appVersion" +echo " - Tag format: v\${version}" diff --git a/third_party/astrolabe/.cz.toml b/third_party/astrolabe/.cz.toml new file mode 100644 index 0000000..d16c32a --- /dev/null +++ b/third_party/astrolabe/.cz.toml @@ -0,0 +1,25 @@ +[tool.commitizen] +name = "cz_conventional_commits" +version = "0.1.0" +tag_format = "astrolabe-v$version" +version_scheme = "pep440" +update_changelog_on_bump = true +major_version_zero = true + +# Update Astrolabe-specific files only +version_files = [ + "appinfo/info.xml:version", + "package.json:version" +] + +# Ignore tags from other components +ignored_tag_formats = [ + "v*", # MCP server tags + "nextcloud-mcp-server-*", # Helm chart tags +] + +# Filter commits by scope +[tool.commitizen.customize] +changelog_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)\\(astrolabe\\)(!)?:" +schema_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)\\(astrolabe\\)(!)?:\\s.+" +message_template = "{{change_type}}(astrolabe): {{message}}" diff --git a/third_party/astrolabe/.gitignore b/third_party/astrolabe/.gitignore index 6f827c5..afd5e5d 100644 --- a/third_party/astrolabe/.gitignore +++ b/third_party/astrolabe/.gitignore @@ -8,6 +8,7 @@ /tests/.phpunit.cache dist/ +build/ node_modules/ js/ css/ diff --git a/third_party/astrolabe/CHANGELOG.md b/third_party/astrolabe/CHANGELOG.md index ab830f7..40fee5b 100644 --- a/third_party/astrolabe/CHANGELOG.md +++ b/third_party/astrolabe/CHANGELOG.md @@ -1,12 +1,29 @@ -# Changelog +# Changelog - Astrolabe -All notable changes to this project will be documented in this file. +All notable changes to the Astrolabe Nextcloud app will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.1.0] - 2024-12-19 + ### Added -- First release +- Initial alpha release +- Semantic search across Notes, Files, Calendar, Deck, and Contacts +- Integration with Nextcloud Unified Search +- Personal settings UI for MCP server configuration +- Admin settings for global MCP server URL +- OAuth PKCE authentication flow +- Vector visualization of semantic relationships +- Hybrid search combining semantic and keyword matching +- Background content indexing +- Support for Nextcloud 30-32 + +### Notes + +- This is an alpha release intended for early adopters and testing +- Requires external MCP server deployment +- See documentation for setup: https://github.com/cbcoutinho/nextcloud-mcp-server diff --git a/third_party/astrolabe/Makefile b/third_party/astrolabe/Makefile new file mode 100644 index 0000000..bf9f188 --- /dev/null +++ b/third_party/astrolabe/Makefile @@ -0,0 +1,81 @@ +# Nextcloud App Store Release Makefile for Astrolabe +# +# Based on: https://nextcloudappstore.readthedocs.io/en/latest/developer.html + +app_name=astrolabe +project_dir=$(CURDIR) +build_dir=$(project_dir)/build +appstore_dir=$(build_dir)/artifacts +package_name=$(appstore_dir)/$(app_name) +cert_dir=$(HOME)/.nextcloud/certificates + +# Signing +private_key=$(cert_dir)/$(app_name).key +certificate=$(cert_dir)/$(app_name).crt +sign_cmd=php ../../server/occ integrity:sign-app --privateKey=$(private_key) --certificate=$(certificate) + +# Clean build artifacts +.PHONY: clean +clean: + rm -rf $(build_dir) + +# Install PHP and Node dependencies +.PHONY: install-deps +install-deps: + composer install --no-dev --optimize-autoloader + npm ci + +# Build production frontend assets +.PHONY: build-frontend +build-frontend: + npm run build + +# Run all linters +.PHONY: lint +lint: + composer lint + composer cs:check + npm run lint + npm run stylelint + +# Assemble app files into build directory (exclude dev files) +.PHONY: assemble +assemble: clean install-deps build-frontend + mkdir -p $(package_name) + # Copy app files + rsync -av \ + --exclude='.git*' \ + --exclude='build/' \ + --exclude='tests/' \ + --exclude='node_modules/' \ + --exclude='*.log' \ + --exclude='.github/' \ + --exclude='composer.json' \ + --exclude='composer.lock' \ + --exclude='package.json' \ + --exclude='package-lock.json' \ + --exclude='vite.config.js' \ + --exclude='.eslintrc.js' \ + --exclude='.php-cs-fixer.*' \ + --exclude='psalm.xml' \ + --exclude='*.iml' \ + --exclude='.idea' \ + --exclude='src/' \ + ./ $(package_name)/ + +# Create signed release tarball for App Store +.PHONY: appstore +appstore: assemble + # Sign the app + $(sign_cmd) --path=$(package_name) + # Create tarball + cd $(appstore_dir) && \ + tar -czf $(app_name).tar.gz $(app_name) + # Show package info + @echo "=========================================" + @echo "App package created:" + @echo " $(appstore_dir)/$(app_name).tar.gz" + @echo "" + @echo "Signature:" + @cat $(package_name)/appinfo/signature.json | head -n 5 + @echo "=========================================" diff --git a/third_party/astrolabe/package.json b/third_party/astrolabe/package.json index d3fe6cc..1e6b4ac 100644 --- a/third_party/astrolabe/package.json +++ b/third_party/astrolabe/package.json @@ -1,6 +1,6 @@ { "name": "astrolabe", - "version": "1.0.0", + "version": "0.1.0", "license": "AGPL-3.0-or-later", "engines": { "node": "^22.0.0", From c5f7221fb2fc66cd17e45667df80678411416b9f Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Fri, 19 Dec 2025 18:34:14 +0100 Subject: [PATCH 2/4] fix(astrolabe): address code review feedback CRITICAL FIXES: - Fix tag parsing in workflow to strip "astrolabe-v" instead of "v" For tag astrolabe-v0.1.0, now correctly extracts "0.1.0" - Add workflow filtering to only run on astrolabe-v* tags Prevents wasting CI resources on MCP/Helm releases RECOMMENDED IMPROVEMENTS: - Make Nextcloud server path configurable in Makefile Can now override: make appstore server_dir=/path/to/server - Add dependency validation to Makefile Checks for composer, npm, php before building - Add signing prerequisite validation Verifies server/occ, private key, and certificate exist - Add dependency checks to all bump scripts Validates uv is installed before running cz bump These changes improve local build experience and prevent common errors with clear error messages and installation guidance. --- .github/workflows/appstore-build-publish.yml | 4 ++- scripts/bump-astrolabe.sh | 3 +++ scripts/bump-helm.sh | 3 +++ scripts/bump-mcp.sh | 3 +++ third_party/astrolabe/Makefile | 26 +++++++++++++++++--- 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml index 63b20f0..bd1165d 100644 --- a/.github/workflows/appstore-build-publish.yml +++ b/.github/workflows/appstore-build-publish.yml @@ -11,6 +11,8 @@ env: jobs: build-and-publish: runs-on: ubuntu-latest + # Only run on Astrolabe releases + if: startsWith(github.ref, 'refs/tags/astrolabe-v') steps: - name: Checkout code @@ -19,7 +21,7 @@ jobs: - name: Get version from tag id: tag run: | - echo "TAG=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + echo "TAG=${GITHUB_REF#refs/tags/astrolabe-v}" >> $GITHUB_OUTPUT - name: Validate version in info.xml matches tag working-directory: ${{ env.APP_DIR }} diff --git a/scripts/bump-astrolabe.sh b/scripts/bump-astrolabe.sh index 79e61cc..4a320d7 100755 --- a/scripts/bump-astrolabe.sh +++ b/scripts/bump-astrolabe.sh @@ -2,6 +2,9 @@ # Bump Astrolabe app version set -e +# Validate dependencies +command -v uv >/dev/null 2>&1 || { echo "Error: uv not found. Install from https://docs.astral.sh/uv/"; exit 1; } + cd third_party/astrolabe echo "Bumping Astrolabe version..." diff --git a/scripts/bump-helm.sh b/scripts/bump-helm.sh index 75d08e7..dd5411a 100755 --- a/scripts/bump-helm.sh +++ b/scripts/bump-helm.sh @@ -2,6 +2,9 @@ # Bump Helm chart version set -e +# Validate dependencies +command -v uv >/dev/null 2>&1 || { echo "Error: uv not found. Install from https://docs.astral.sh/uv/"; exit 1; } + cd charts/nextcloud-mcp-server echo "Bumping Helm chart version..." diff --git a/scripts/bump-mcp.sh b/scripts/bump-mcp.sh index 29bb045..6cc9a47 100755 --- a/scripts/bump-mcp.sh +++ b/scripts/bump-mcp.sh @@ -2,6 +2,9 @@ # Bump MCP server version set -e +# Validate dependencies +command -v uv >/dev/null 2>&1 || { echo "Error: uv not found. Install from https://docs.astral.sh/uv/"; exit 1; } + echo "Bumping MCP server version..." uv run cz bump --yes diff --git a/third_party/astrolabe/Makefile b/third_party/astrolabe/Makefile index bf9f188..f96e6e0 100644 --- a/third_party/astrolabe/Makefile +++ b/third_party/astrolabe/Makefile @@ -9,19 +9,31 @@ appstore_dir=$(build_dir)/artifacts package_name=$(appstore_dir)/$(app_name) cert_dir=$(HOME)/.nextcloud/certificates +# Nextcloud server path (configurable via environment variable) +server_dir?=../../server +occ=$(server_dir)/occ + # Signing private_key=$(cert_dir)/$(app_name).key certificate=$(cert_dir)/$(app_name).crt -sign_cmd=php ../../server/occ integrity:sign-app --privateKey=$(private_key) --certificate=$(certificate) +sign_cmd=php $(occ) integrity:sign-app --privateKey=$(private_key) --certificate=$(certificate) # Clean build artifacts .PHONY: clean clean: rm -rf $(build_dir) +# Validate required dependencies +.PHONY: validate-deps +validate-deps: + @command -v composer >/dev/null 2>&1 || { echo "Error: composer not found. Install from https://getcomposer.org/"; exit 1; } + @command -v npm >/dev/null 2>&1 || { echo "Error: npm not found. Install Node.js from https://nodejs.org/"; exit 1; } + @command -v php >/dev/null 2>&1 || { echo "Error: php not found. Install PHP 8.1 or higher."; exit 1; } + @echo "✓ All dependencies found" + # Install PHP and Node dependencies .PHONY: install-deps -install-deps: +install-deps: validate-deps composer install --no-dev --optimize-autoloader npm ci @@ -63,9 +75,17 @@ assemble: clean install-deps build-frontend --exclude='src/' \ ./ $(package_name)/ +# Validate signing prerequisites +.PHONY: validate-signing +validate-signing: + @test -f $(occ) || { echo "Error: Nextcloud server not found at $(server_dir)"; echo "Set server_dir variable: make appstore server_dir=/path/to/server"; exit 1; } + @test -f $(private_key) || { echo "Error: Private key not found at $(private_key)"; exit 1; } + @test -f $(certificate) || { echo "Error: Certificate not found at $(certificate)"; exit 1; } + @echo "✓ Signing prerequisites validated" + # Create signed release tarball for App Store .PHONY: appstore -appstore: assemble +appstore: assemble validate-signing # Sign the app $(sign_cmd) --path=$(package_name) # Create tarball From 2f3a3e0be4b6ced17fe7f5932ead6c25baeb6401 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Fri, 19 Dec 2025 19:34:21 +0100 Subject: [PATCH 3/4] fix(ci): address critical workflow and validation issues Addresses code review feedback on PR #418: CRITICAL FIXES: 1. Workflow trigger: Changed from release:published to push:tags - Enables "tag and publish in one step" workflow as intended - Automatically creates GitHub release on tag push - Removed redundant if condition (filtering now via trigger) - Added prerelease detection based on tag (-alpha, -beta, -rc) 2. Server path: Explicitly pass server_dir to make command - Fixes path mismatch between local (../../server) and CI - Uses absolute path: server_dir=${{ github.workspace }}/server - Prevents signing failures in GitHub Actions 3. Regex validation: Added test script for commitizen patterns - Validates scope filtering works correctly - Tests all three components: mcp, helm, astrolabe - Tests unscoped commits (default to mcp) - Tests breaking changes and invalid commits - Location: scripts/test-commitizen-scopes.sh WORKFLOW IMPROVEMENTS: - Release creation now automatic on tag push - Better step naming for clarity - Consistent prerelease handling across GitHub and App Store - Explicit server_dir prevents reliance on fragile relative paths All 16 test cases pass for scope filtering patterns. --- .github/workflows/appstore-build-publish.yml | 15 +-- scripts/test-commitizen-scopes.sh | 102 +++++++++++++++++++ 2 files changed, 110 insertions(+), 7 deletions(-) create mode 100755 scripts/test-commitizen-scopes.sh diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml index bd1165d..faefd73 100644 --- a/.github/workflows/appstore-build-publish.yml +++ b/.github/workflows/appstore-build-publish.yml @@ -1,8 +1,9 @@ name: Build and Publish Astrolabe App Release on: - release: - types: [published] + push: + tags: + - 'astrolabe-v*' env: APP_NAME: astrolabe @@ -11,8 +12,6 @@ env: jobs: build-and-publish: runs-on: ubuntu-latest - # Only run on Astrolabe releases - if: startsWith(github.ref, 'refs/tags/astrolabe-v') steps: - name: Checkout code @@ -66,15 +65,17 @@ jobs: - name: Build app store package working-directory: ${{ env.APP_DIR }} - run: make appstore + run: make appstore server_dir=${{ github.workspace }}/server - - name: Attach tarball to GitHub release + - name: Create GitHub release and attach tarball uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ${{ env.APP_DIR }}/build/artifacts/${{ env.APP_NAME }}.tar.gz asset_name: ${{ env.APP_NAME }}-${{ steps.tag.outputs.TAG }}.tar.gz tag: ${{ github.ref }} + release_name: Astrolabe ${{ steps.tag.outputs.TAG }} + prerelease: ${{ contains(steps.tag.outputs.TAG, '-alpha') || contains(steps.tag.outputs.TAG, '-beta') || contains(steps.tag.outputs.TAG, '-rc') }} - name: Upload to Nextcloud App Store uses: R0Wi/nextcloud-appstore-push-action@v1.0.6 @@ -83,4 +84,4 @@ jobs: appstore_token: ${{ secrets.APPSTORE_TOKEN }} download_url: ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ github.ref_name }}/${{ env.APP_NAME }}-${{ steps.tag.outputs.TAG }}.tar.gz app_private_key: ${{ secrets.APP_PRIVATE_KEY }} - nightly: ${{ github.event.release.prerelease }} + nightly: ${{ contains(steps.tag.outputs.TAG, '-alpha') || contains(steps.tag.outputs.TAG, '-beta') || contains(steps.tag.outputs.TAG, '-rc') }} diff --git a/scripts/test-commitizen-scopes.sh b/scripts/test-commitizen-scopes.sh new file mode 100755 index 0000000..9f2c072 --- /dev/null +++ b/scripts/test-commitizen-scopes.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# Test commitizen scope filtering patterns +set -uo pipefail + +echo "Testing commitizen scope filtering patterns..." +echo + +# Regex patterns from configs +MCP_PATTERN='^(feat|fix|docs|refactor|perf|test|build|ci|chore)(\(mcp\))?(!)?:' +HELM_PATTERN='^(feat|fix|docs|refactor|perf|test|build|ci|chore)\(helm\)(!)?:' +ASTROLABE_PATTERN='^(feat|fix|docs|refactor|perf|test|build|ci|chore)\(astrolabe\)(!)?:' + +test_pattern() { + local message="$1" + local pattern="$2" + + if echo "$message" | grep -qE "$pattern"; then + return 0 + else + return 1 + fi +} + +run_test() { + local message="$1" + local expected="$2" + local matched_components=() + + # Check which components match + if test_pattern "$message" "$MCP_PATTERN"; then + matched_components+=("mcp") + fi + if test_pattern "$message" "$HELM_PATTERN"; then + matched_components+=("helm") + fi + if test_pattern "$message" "$ASTROLABE_PATTERN"; then + matched_components+=("astrolabe") + fi + + # Convert array to space-separated string, or "none" if empty + local matched + if [ ${#matched_components[@]} -eq 0 ]; then + matched="none" + else + matched="${matched_components[*]}" + fi + + # Validate expectation + if [ "$matched" = "$expected" ]; then + echo "✓ PASS: '$message'" + echo " → Matched: $matched" + return 0 + else + echo "✗ FAIL: '$message'" + echo " → Matched: $matched (expected: $expected)" + return 1 + fi +} + +# Run all test cases +failed=0 +passed=0 + +# MCP server commits (scope=mcp or unscoped) +run_test "feat: add new feature" "mcp" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "feat(mcp): add API endpoint" "mcp" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "fix(mcp): resolve authentication bug" "mcp" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "docs: update README" "mcp" && passed=$((passed+1)) || failed=$((failed+1)) + +# Helm chart commits +run_test "feat(helm): add resource limits" "helm" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "fix(helm): correct values schema" "helm" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "docs(helm): update deployment guide" "helm" && passed=$((passed+1)) || failed=$((failed+1)) + +# Astrolabe commits +run_test "feat(astrolabe): add dark mode" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "fix(astrolabe): resolve UI bug" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "perf(astrolabe): optimize rendering" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1)) + +# Breaking changes +run_test "feat(mcp)!: breaking API change" "mcp" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "feat(helm)!: rename values" "helm" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "feat(astrolabe)!: remove deprecated feature" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1)) + +# Invalid commits (should not match any) +run_test "feat(invalid): test" "none" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "random commit message" "none" && passed=$((passed+1)) || failed=$((failed+1)) +run_test "feat (mcp): space before scope" "none" && passed=$((passed+1)) || failed=$((failed+1)) + +# Summary +echo +echo "==========================================" +echo "Results: $passed passed, $failed failed" +echo "==========================================" + +if [ $failed -gt 0 ]; then + echo "❌ Some tests failed - scope patterns may need adjustment" + exit 1 +else + echo "✅ All tests passed - scope patterns working correctly" + exit 0 +fi From e2e0ffce4487334f203673a64a05e7d43b412893 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Fri, 19 Dec 2025 19:38:24 +0100 Subject: [PATCH 4/4] fix(ci): improve versioning and error handling Addresses remaining high-priority code review feedback: VERSIONING SCHEME FIXES: - Helm chart: Changed from pep440 to semver (correct for Helm) - Astrolabe: Changed from pep440 to semver (correct for Nextcloud apps) - MCP server: Remains pep440 (correct for Python packages) Helm charts must use semantic versioning per Helm specification. Nextcloud apps use semantic versioning in info.xml and package.json. ENHANCED ERROR HANDLING IN BUMP SCRIPTS: All three bump scripts now include: - Comprehensive validation checks * Tool availability (uv) * Directory structure (must run from repo root) * Required files exist (Chart.yaml, info.xml, package.json) - Better error messages * Stderr output for errors * Captured commitizen output on failure * Common failure causes listed - Success confirmation * Clear indication of what was updated * Next steps guidance (git push --follow-tags) - Robust shell options (set -euo pipefail) Scripts now provide helpful guidance when: - No conventional commits found - No commits with required scope - Git working directory not clean - Required dependencies missing --- charts/nextcloud-mcp-server/.cz.toml | 2 +- scripts/bump-astrolabe.sh | 53 ++++++++++++++++++++++++---- scripts/bump-helm.sh | 50 +++++++++++++++++++++----- scripts/bump-mcp.sh | 38 ++++++++++++++++---- third_party/astrolabe/.cz.toml | 2 +- 5 files changed, 122 insertions(+), 23 deletions(-) diff --git a/charts/nextcloud-mcp-server/.cz.toml b/charts/nextcloud-mcp-server/.cz.toml index c71bcdd..8f4504c 100644 --- a/charts/nextcloud-mcp-server/.cz.toml +++ b/charts/nextcloud-mcp-server/.cz.toml @@ -2,7 +2,7 @@ name = "cz_conventional_commits" version = "0.53.0" tag_format = "nextcloud-mcp-server-$version" -version_scheme = "pep440" +version_scheme = "semver" update_changelog_on_bump = true major_version_zero = true diff --git a/scripts/bump-astrolabe.sh b/scripts/bump-astrolabe.sh index 4a320d7..c51b8eb 100755 --- a/scripts/bump-astrolabe.sh +++ b/scripts/bump-astrolabe.sh @@ -1,17 +1,56 @@ #!/bin/bash # Bump Astrolabe app version -set -e +set -euo pipefail # Validate dependencies -command -v uv >/dev/null 2>&1 || { echo "Error: uv not found. Install from https://docs.astral.sh/uv/"; exit 1; } +command -v uv >/dev/null 2>&1 || { + echo "❌ Error: uv not found" >&2 + echo " Install from https://docs.astral.sh/uv/" >&2 + exit 1 +} + +# Validate Astrolabe directory exists +if [ ! -d "third_party/astrolabe" ]; then + echo "❌ Error: Must run from repository root (third_party/astrolabe not found)" >&2 + exit 1 +fi cd third_party/astrolabe -echo "Bumping Astrolabe version..." -uv run cz --config .cz.toml bump --yes +# Validate required files exist +if [ ! -f "appinfo/info.xml" ]; then + echo "❌ Error: appinfo/info.xml not found" >&2 + exit 1 +fi -echo "✓ Astrolabe version bumped" -echo " - Updated: appinfo/info.xml, package.json" -echo " - Tag format: astrolabe-v\${version}" +if [ ! -f "package.json" ]; then + echo "❌ Error: package.json not found" >&2 + exit 1 +fi + +echo "Bumping Astrolabe version..." + +# Run commitizen bump and capture output +if ! output=$(uv run cz --config .cz.toml bump --yes 2>&1); then + cd ../.. + echo "❌ Error: Version bump failed" >&2 + echo "$output" >&2 + echo "" >&2 + echo "Common causes:" >&2 + echo " - No commits with scope 'astrolabe' since last version" >&2 + echo " - No conventional commits found (use feat(astrolabe):, fix(astrolabe):, etc.)" >&2 + echo " - Git working directory not clean" >&2 + exit 1 +fi + +echo "$output" +echo "" +echo "✓ Astrolabe version bumped successfully" +echo " Updated: appinfo/info.xml, package.json" +echo " Tag format: astrolabe-v\${version}" +echo "" +echo "Next steps:" +echo " cd ../.." +echo " git push --follow-tags" cd ../.. diff --git a/scripts/bump-helm.sh b/scripts/bump-helm.sh index dd5411a..d57aef7 100755 --- a/scripts/bump-helm.sh +++ b/scripts/bump-helm.sh @@ -1,18 +1,52 @@ #!/bin/bash # Bump Helm chart version -set -e +set -euo pipefail # Validate dependencies -command -v uv >/dev/null 2>&1 || { echo "Error: uv not found. Install from https://docs.astral.sh/uv/"; exit 1; } +command -v uv >/dev/null 2>&1 || { + echo "❌ Error: uv not found" >&2 + echo " Install from https://docs.astral.sh/uv/" >&2 + exit 1 +} + +# Validate Helm chart directory exists +if [ ! -d "charts/nextcloud-mcp-server" ]; then + echo "❌ Error: Must run from repository root (charts/ not found)" >&2 + exit 1 +fi cd charts/nextcloud-mcp-server -echo "Bumping Helm chart version..." -uv run cz --config .cz.toml bump --yes +# Validate Chart.yaml exists +if [ ! -f "Chart.yaml" ]; then + echo "❌ Error: Chart.yaml not found" >&2 + exit 1 +fi -echo "✓ Helm chart version bumped" -echo " - Updated: Chart.yaml:version" -echo " - Tag format: nextcloud-mcp-server-\${version}" -echo " - Note: appVersion stays at MCP server version" +echo "Bumping Helm chart version..." + +# Run commitizen bump and capture output +if ! output=$(uv run cz --config .cz.toml bump --yes 2>&1); then + cd ../.. + echo "❌ Error: Version bump failed" >&2 + echo "$output" >&2 + echo "" >&2 + echo "Common causes:" >&2 + echo " - No commits with scope 'helm' since last version" >&2 + echo " - No conventional commits found (use feat(helm):, fix(helm):, etc.)" >&2 + echo " - Git working directory not clean" >&2 + exit 1 +fi + +echo "$output" +echo "" +echo "✓ Helm chart version bumped successfully" +echo " Updated: Chart.yaml:version" +echo " Tag format: nextcloud-mcp-server-\${version}" +echo " Note: appVersion stays at MCP server version" +echo "" +echo "Next steps:" +echo " cd ../.." +echo " git push --follow-tags" cd ../.. diff --git a/scripts/bump-mcp.sh b/scripts/bump-mcp.sh index 6cc9a47..d6b618e 100755 --- a/scripts/bump-mcp.sh +++ b/scripts/bump-mcp.sh @@ -1,13 +1,39 @@ #!/bin/bash # Bump MCP server version -set -e +set -euo pipefail # Validate dependencies -command -v uv >/dev/null 2>&1 || { echo "Error: uv not found. Install from https://docs.astral.sh/uv/"; exit 1; } +command -v uv >/dev/null 2>&1 || { + echo "❌ Error: uv not found" >&2 + echo " Install from https://docs.astral.sh/uv/" >&2 + exit 1 +} + +# Validate we're in the repository root +if [ ! -f "pyproject.toml" ]; then + echo "❌ Error: Must run from repository root (pyproject.toml not found)" >&2 + exit 1 +fi echo "Bumping MCP server version..." -uv run cz bump --yes -echo "✓ MCP server version bumped" -echo " - Updated: pyproject.toml, Chart.yaml:appVersion" -echo " - Tag format: v\${version}" +# Run commitizen bump and capture output +if ! output=$(uv run cz bump --yes 2>&1); then + echo "❌ Error: Version bump failed" >&2 + echo "$output" >&2 + echo "" >&2 + echo "Common causes:" >&2 + echo " - No commits since last version" >&2 + echo " - No conventional commits found (use feat:, fix:, etc.)" >&2 + echo " - Git working directory not clean" >&2 + exit 1 +fi + +echo "$output" +echo "" +echo "✓ MCP server version bumped successfully" +echo " Updated: pyproject.toml, Chart.yaml:appVersion" +echo " Tag format: v\${version}" +echo "" +echo "Next steps:" +echo " git push --follow-tags" diff --git a/third_party/astrolabe/.cz.toml b/third_party/astrolabe/.cz.toml index d16c32a..6e7c9b3 100644 --- a/third_party/astrolabe/.cz.toml +++ b/third_party/astrolabe/.cz.toml @@ -2,7 +2,7 @@ name = "cz_conventional_commits" version = "0.1.0" tag_format = "astrolabe-v$version" -version_scheme = "pep440" +version_scheme = "semver" update_changelog_on_bump = true major_version_zero = true