Skip to content
Merged
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
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Build the image from source, rather than copying host build/interpreter caches.
.git
/build
**/*.egg-info
/venv
/venv-mcp
/.venv
/node_modules
**/__pycache__
84 changes: 82 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,82 @@ permissions:
contents: read

jobs:
core_compatibility:
name: Core compatibility (Python 3.9)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.9"
cache: pip
cache-dependency-path: |
requirements.txt
requirements-test.txt

- name: Install core dependencies
run: python -m pip install -r requirements.txt -r requirements-test.txt

- name: Test without optional MCP dependencies
run: python -m pytest tests -q

oauth_and_mcp:
name: OAuth and MCP (Python ${{ matrix.python }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python: ["3.11", "3.13"]
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python }}
cache: pip
cache-dependency-path: |
requirements.txt
requirements-test.txt
constraints-mcp.txt

- name: Install tested OAuth and MCP dependencies
run: python -m pip install -c constraints-mcp.txt -r requirements.txt -r requirements-test.txt mcp

- name: Require the MCP SDK
# Missing optional dependencies must fail this job instead of skipping MCP tests.
run: python -c "from mcp.server import MCPServer"

- name: Test OAuth, MCP, and core integration
run: |
python -m pytest tests -q \
--cov=wirecloud.oauth2provider \
--cov=wirecloud.mcp \
--cov=wirecloud.commons.auth \
--cov-branch \
--cov-fail-under=100 \
--cov-report=term-missing \
--cov-report=xml:coverage-oauth-mcp.xml \
--junitxml=oauth-mcp-results.xml

- name: Store OAuth and MCP test reports
if: always()
uses: actions/upload-artifact@v7
with:
name: oauth-mcp-python-${{ matrix.python }}
path: |
coverage-oauth-mcp.xml
oauth-mcp-results.xml
if-no-files-found: ignore

test_and_coverage:
name: Test and coverage
permissions:
Expand All @@ -30,11 +106,12 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.9"
python-version: "3.11"
cache: "pip"
cache-dependency-path: |
requirements.txt
requirements-test.txt
constraints-mcp.txt

- name: Set up Node.js
uses: actions/setup-node@v6
Expand All @@ -45,7 +122,10 @@ jobs:
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-test.txt
python -m pip install -c constraints-mcp.txt -r requirements.txt -r requirements-test.txt mcp

- name: Require the MCP SDK for complete coverage
run: python -c "from mcp.server import MCPServer"

- name: Install JavaScript dependencies
run: npm ci
Expand Down
63 changes: 43 additions & 20 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:

jobs:
build_wheel:
name: Build wheel
name: Build regular and MCP wheels
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -25,7 +25,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.9"
python-version: "3.11"
cache: "pip"

- name: Set up Node.js
Expand All @@ -38,49 +38,70 @@ jobs:
run: npm ci

- name: Install package builder
run: python -m pip install --upgrade build
run: python -m pip install --upgrade build packaging

- name: Build wheel
run: python -m build --wheel

- name: Verify wheel installation
run: |
python -m pip install --no-deps --target /tmp/wirecloud-wheel-check dist/*.whl
PYTHONPATH=/tmp/wirecloud-wheel-check python -c "import wirecloud"
- name: Build MCP wheel
run: python scripts/build-mcp-wheel.py dist/*.whl

- name: Store wheel
- name: Verify both wheel installations
run: |
for wheel in dist/*.whl; do
check_dir=$(mktemp -d)
python -m venv "$check_dir"
"$check_dir/bin/python" -m pip install "$wheel"
"$check_dir/bin/python" -m pip check
"$check_dir/bin/python" -c "import wirecloud"
case "$wheel" in
*+mcp-*) "$check_dir/bin/python" -c "from mcp.server import MCPServer; import wirecloud.mcp" ;;
esac
done

- name: Store both wheels
uses: actions/upload-artifact@v7
with:
name: wirecloud-wheel
name: wirecloud-wheels
path: dist/*.whl
if-no-files-found: error

attach_to_release:
name: Attach wheel to release
name: Attach regular and MCP wheels to release
if: github.event_name == 'release'
needs: build_wheel
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Download built wheel
- name: Download built wheels
uses: actions/download-artifact@v8
with:
name: wirecloud-wheel
name: wirecloud-wheels
path: dist

- name: Upload wheel to GitHub release
- name: Upload both wheels to GitHub release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: gh release upload "$RELEASE_TAG" dist/*.whl --clobber --repo "$GITHUB_REPOSITORY"

publish_docker_image:
name: Publish Docker image
name: Publish Docker image (${{ matrix.variant }})
if: github.event_name == 'release'
needs: build_wheel
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- variant: regular
install_mcp: "false"
tag_suffix: ""
- variant: mcp
install_mcp: "true"
tag_suffix: "-mcp"
permissions:
contents: read
env:
Expand Down Expand Up @@ -152,19 +173,21 @@ jobs:
images: ${{ env.DOCKER_IMAGE }}
flavor: latest=false
tags: |
type=semver,pattern={{version}},value=${{ github.event.release.tag_name }}
type=semver,pattern={{major}}.{{minor}},value=${{ github.event.release.tag_name }},enable=${{ !github.event.release.prerelease }}
type=semver,pattern={{major}},value=${{ github.event.release.tag_name }},enable=${{ !github.event.release.prerelease }}
type=raw,value=latest,enable=${{ !github.event.release.prerelease }}
type=semver,pattern={{version}},value=${{ github.event.release.tag_name }},suffix=${{ matrix.tag_suffix }}
type=semver,pattern={{major}}.{{minor}},value=${{ github.event.release.tag_name }},enable=${{ !github.event.release.prerelease }},suffix=${{ matrix.tag_suffix }}
type=semver,pattern={{major}},value=${{ github.event.release.tag_name }},enable=${{ !github.event.release.prerelease }},suffix=${{ matrix.tag_suffix }}
type=raw,value=latest,enable=${{ !github.event.release.prerelease }},suffix=${{ matrix.tag_suffix }}

- name: Build and publish Docker image
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
build-args: |
WIRECLOUD_INSTALL_MCP=${{ matrix.install_mcp }}
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=gha,scope=wirecloud-${{ matrix.variant }}
cache-to: type=gha,mode=max,scope=wirecloud-${{ matrix.variant }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
venv/
venv-mcp/
__pycache__/
catalogue/
!src/wirecloud/catalogue/
Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ include src/settings.py
include src/manage.py
include src/generate_webpack_settings.py
include README.md
include constraints-mcp.txt
include scripts/build-mcp-wheel.py

global-exclude __pycache__/*
global-exclude *.py[cod]
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ follow the complete [getting-started guide](docs/getting-started.md).
| Operate, back up, and restore WireCloud | [Administration](docs/administration.md) |
| Build widgets, operators, or mashups | [Component development](docs/components.md) |
| Integrate with WireCloud over HTTP | [HTTP API](docs/api.md) |
| Connect an AI assistant | [MCP plugin](docs/mcp.md) |
| Offer OAuth connections from a plugin | [OAuth module](docs/oauth2.md) |
| Customize the interface or backend | [Themes](docs/themes.md) and [plugins](docs/plugins.md) |
| Migrate an existing deployment | [Migration guide](docs/migration.md) |

Expand Down
6 changes: 6 additions & 0 deletions constraints-mcp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Tested MCP deployment constraints; install with:
# python -m pip install -c constraints-mcp.txt '.[mcp]'
mcp==2.3.0
fastapi==0.115.8
starlette==0.45.3
sse-starlette==3.0.3
8 changes: 8 additions & 0 deletions docker/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ WIRECLOUD_JWT_KEY=change-this-jwt-key-in-production-123456
WIRECLOUD_SECRET_KEY=change-this-secret-key-in-production-123456
WIRECLOUD_SESSION_AGE=1209600

# Optional OAuth and MCP (build MCP with --build-arg WIRECLOUD_INSTALL_MCP=true;
# add wirecloud.oauth2provider and wirecloud.mcp to WIRECLOUD_INSTALLED_APPS)
WIRECLOUD_MCP_PUBLIC_URL=
WIRECLOUD_AUTH_PUBLIC_URL=
WIRECLOUD_OAUTH2_PUBLIC_URL=
WIRECLOUD_MCP_ALLOWED_ORIGINS=
WIRECLOUD_OAUTH2_DYNAMIC_REGISTRATION=true

# Database (MongoDB)
WIRECLOUD_DB_DRIVER=mongodb
WIRECLOUD_DB_NAME=wirecloud
Expand Down
8 changes: 7 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ RUN python -m pip install --upgrade pip \
&& python -m pip wheel . -w /wheels --no-deps

FROM python:3.11-slim AS runtime
ARG WIRECLOUD_INSTALL_MCP=false

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
Expand All @@ -27,7 +28,12 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
WORKDIR /app

COPY --from=builder /wheels /wheels
RUN python -m pip install --no-cache-dir /wheels/wirecloud-*.whl \
COPY constraints-mcp.txt /app/constraints-mcp.txt
RUN if [ "$WIRECLOUD_INSTALL_MCP" = "true" ]; then \
python -m pip install --no-cache-dir -c /app/constraints-mcp.txt /wheels/wirecloud-*.whl 'mcp==2.3.0'; \
else \
python -m pip install --no-cache-dir /wheels/wirecloud-*.whl; \
fi \
&& rm -rf /wheels

COPY docker/settings.py /app/docker/settings.py
Expand Down
20 changes: 17 additions & 3 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,33 @@ docker compose -f docker/docker-compose.yml exec wirecloud python -m manage crea

## Published images

Release images for `linux/amd64` and `linux/arm64` are published to one
configurable repository. The default is Docker Hub:
Each published release builds regular and MCP variants for `linux/amd64` and
`linux/arm64` in one configurable repository. The default is Docker Hub:

```bash
docker pull fiware/wirecloud:2.0.0
docker pull fiware/wirecloud:2.0.0-mcp
```

The regular image installs the base application. The `-mcp` image also installs
the MCP SDK and its tested dependency constraints using
`WIRECLOUD_INSTALL_MCP=true`. Enable `wirecloud.oauth2provider` and `wirecloud.mcp`
in the deployment's `WIRECLOUD_INSTALLED_APPS` and configure their public URLs as
described in the [MCP guide](../docs/mcp.md#docker).

Stable releases also update major/minor aliases and `latest` for the regular
image, with matching `-mcp` aliases for the MCP image (for example, `2.0-mcp`,
`2-mcp`, and `latest-mcp`). Prereleases publish only their full version tags,
with and without `-mcp`; they do not replace stable aliases. CI keeps the two
variants' build caches separate.

Check the release notes for the canonical location when the repository's
publishing configuration overrides this default.

For a deployment based on a published image, remove the `build` block from the
`wirecloud` service in `docker-compose.yml` and replace its image with a pinned
release, for example `image: fiware/wirecloud:2.0.0`. Keep the supplied
release, for example `image: fiware/wirecloud:2.0.0` or
`image: fiware/wirecloud:2.0.0-mcp`. Keep the supplied
environment, ports, dependencies, and volumes.

## Notes
Expand Down
5 changes: 5 additions & 0 deletions docker/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ def _env_languages(name: str, default: list[tuple[str, str]]) -> list[tuple[str,


DEBUG = _env_bool("WIRECLOUD_DEBUG", False)
MCP_PUBLIC_URL = _env_str("WIRECLOUD_MCP_PUBLIC_URL", "")
AUTH_PUBLIC_URL = _env_str("WIRECLOUD_AUTH_PUBLIC_URL", "")
MCP_ALLOWED_ORIGINS = _env_csv("WIRECLOUD_MCP_ALLOWED_ORIGINS", [])
OAUTH2_PUBLIC_URL = _env_str("WIRECLOUD_OAUTH2_PUBLIC_URL", "")
OAUTH2_DYNAMIC_REGISTRATION = _env_bool("WIRECLOUD_OAUTH2_DYNAMIC_REGISTRATION", True)

BASEDIR = _env_str("WIRECLOUD_BASEDIR", "/var/lib/wirecloud")
ALLOW_ANONYMOUS_ACCESS = _env_bool("WIRECLOUD_ALLOW_ANONYMOUS_ACCESS", True)
Expand Down
Loading
Loading