mcp-synology 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. mcp_synology-0.4.0/.github/workflows/ci.yml +50 -0
  2. mcp_synology-0.4.0/.github/workflows/pr-labels-ci.yml +120 -0
  3. mcp_synology-0.4.0/.github/workflows/pr-labels.yml +151 -0
  4. mcp_synology-0.4.0/.github/workflows/publish.yml +67 -0
  5. mcp_synology-0.4.0/.github/workflows/qa-gate.yml +30 -0
  6. mcp_synology-0.4.0/.github/workflows/test-publish.yml +49 -0
  7. mcp_synology-0.4.0/.gitignore +213 -0
  8. mcp_synology-0.4.0/CHANGELOG.md +198 -0
  9. mcp_synology-0.4.0/CLAUDE.md +144 -0
  10. mcp_synology-0.4.0/DEVELOPMENT.md +131 -0
  11. mcp_synology-0.4.0/LICENSE +190 -0
  12. mcp_synology-0.4.0/PKG-INFO +314 -0
  13. mcp_synology-0.4.0/README.md +273 -0
  14. mcp_synology-0.4.0/docs/credentials.md +110 -0
  15. mcp_synology-0.4.0/docs/specs/architecture.md +401 -0
  16. mcp_synology-0.4.0/docs/specs/config-schema-spec.md +536 -0
  17. mcp_synology-0.4.0/docs/specs/filestation-module-spec.md +738 -0
  18. mcp_synology-0.4.0/docs/specs/project-scaffolding-spec.md +506 -0
  19. mcp_synology-0.4.0/examples/config-docker.yaml +8 -0
  20. mcp_synology-0.4.0/examples/config-minimal.yaml +12 -0
  21. mcp_synology-0.4.0/examples/config-power-user.yaml +42 -0
  22. mcp_synology-0.4.0/pyproject.toml +90 -0
  23. mcp_synology-0.4.0/scripts/migrate-from-synology-mcp.py +210 -0
  24. mcp_synology-0.4.0/scripts/vdsm_setup.py +152 -0
  25. mcp_synology-0.4.0/src/mcp_synology/__init__.py +5 -0
  26. mcp_synology-0.4.0/src/mcp_synology/__main__.py +5 -0
  27. mcp_synology-0.4.0/src/mcp_synology/cli/__init__.py +25 -0
  28. mcp_synology-0.4.0/src/mcp_synology/cli/check.py +65 -0
  29. mcp_synology-0.4.0/src/mcp_synology/cli/logging_.py +35 -0
  30. mcp_synology-0.4.0/src/mcp_synology/cli/main.py +128 -0
  31. mcp_synology-0.4.0/src/mcp_synology/cli/setup.py +416 -0
  32. mcp_synology-0.4.0/src/mcp_synology/cli/version.py +186 -0
  33. mcp_synology-0.4.0/src/mcp_synology/core/__init__.py +1 -0
  34. mcp_synology-0.4.0/src/mcp_synology/core/auth.py +212 -0
  35. mcp_synology-0.4.0/src/mcp_synology/core/client.py +589 -0
  36. mcp_synology-0.4.0/src/mcp_synology/core/config.py +319 -0
  37. mcp_synology-0.4.0/src/mcp_synology/core/errors.py +202 -0
  38. mcp_synology-0.4.0/src/mcp_synology/core/formatting.py +188 -0
  39. mcp_synology-0.4.0/src/mcp_synology/core/state.py +62 -0
  40. mcp_synology-0.4.0/src/mcp_synology/icons/favicon.ico +0 -0
  41. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-dark-128.png +0 -0
  42. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-dark-16.png +0 -0
  43. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-dark-256.png +0 -0
  44. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-dark-32.png +0 -0
  45. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-dark-64.png +0 -0
  46. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-light-128.png +0 -0
  47. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-light-16.png +0 -0
  48. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-light-256.png +0 -0
  49. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-light-32.png +0 -0
  50. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-light-64.png +0 -0
  51. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-logo-dark.svg +54 -0
  52. mcp_synology-0.4.0/src/mcp_synology/icons/mcp-synology-logo-light.svg +54 -0
  53. mcp_synology-0.4.0/src/mcp_synology/instructions/server.md +73 -0
  54. mcp_synology-0.4.0/src/mcp_synology/modules/__init__.py +191 -0
  55. mcp_synology-0.4.0/src/mcp_synology/modules/filestation/__init__.py +588 -0
  56. mcp_synology-0.4.0/src/mcp_synology/modules/filestation/helpers.py +142 -0
  57. mcp_synology-0.4.0/src/mcp_synology/modules/filestation/listing.py +213 -0
  58. mcp_synology-0.4.0/src/mcp_synology/modules/filestation/metadata.py +206 -0
  59. mcp_synology-0.4.0/src/mcp_synology/modules/filestation/operations.py +452 -0
  60. mcp_synology-0.4.0/src/mcp_synology/modules/filestation/search.py +222 -0
  61. mcp_synology-0.4.0/src/mcp_synology/modules/filestation/transfer.py +197 -0
  62. mcp_synology-0.4.0/src/mcp_synology/modules/system/__init__.py +86 -0
  63. mcp_synology-0.4.0/src/mcp_synology/modules/system/info.py +126 -0
  64. mcp_synology-0.4.0/src/mcp_synology/modules/system/utilization.py +159 -0
  65. mcp_synology-0.4.0/src/mcp_synology/py.typed +0 -0
  66. mcp_synology-0.4.0/src/mcp_synology/server.py +263 -0
  67. mcp_synology-0.4.0/tests/__init__.py +0 -0
  68. mcp_synology-0.4.0/tests/conftest.py +72 -0
  69. mcp_synology-0.4.0/tests/core/__init__.py +0 -0
  70. mcp_synology-0.4.0/tests/core/test_auth.py +402 -0
  71. mcp_synology-0.4.0/tests/core/test_cli.py +628 -0
  72. mcp_synology-0.4.0/tests/core/test_client.py +200 -0
  73. mcp_synology-0.4.0/tests/core/test_config.py +213 -0
  74. mcp_synology-0.4.0/tests/core/test_errors.py +91 -0
  75. mcp_synology-0.4.0/tests/core/test_formatting.py +146 -0
  76. mcp_synology-0.4.0/tests/core/test_server.py +117 -0
  77. mcp_synology-0.4.0/tests/core/test_state.py +43 -0
  78. mcp_synology-0.4.0/tests/integration_config.yaml.example +40 -0
  79. mcp_synology-0.4.0/tests/modules/__init__.py +0 -0
  80. mcp_synology-0.4.0/tests/modules/filestation/__init__.py +0 -0
  81. mcp_synology-0.4.0/tests/modules/filestation/test_helpers.py +145 -0
  82. mcp_synology-0.4.0/tests/modules/filestation/test_listing.py +193 -0
  83. mcp_synology-0.4.0/tests/modules/filestation/test_metadata.py +118 -0
  84. mcp_synology-0.4.0/tests/modules/filestation/test_operations.py +245 -0
  85. mcp_synology-0.4.0/tests/modules/filestation/test_search.py +165 -0
  86. mcp_synology-0.4.0/tests/modules/filestation/test_transfer.py +390 -0
  87. mcp_synology-0.4.0/tests/modules/test_module_system.py +152 -0
  88. mcp_synology-0.4.0/tests/test_e2e.py +162 -0
  89. mcp_synology-0.4.0/tests/test_integration.py +953 -0
  90. mcp_synology-0.4.0/tests/vdsm/__init__.py +0 -0
  91. mcp_synology-0.4.0/tests/vdsm/config.py +95 -0
  92. mcp_synology-0.4.0/tests/vdsm/conftest.py +203 -0
  93. mcp_synology-0.4.0/tests/vdsm/container.py +216 -0
  94. mcp_synology-0.4.0/tests/vdsm/golden_image.py +119 -0
  95. mcp_synology-0.4.0/tests/vdsm/setup_dsm.py +578 -0
  96. mcp_synology-0.4.0/tests/vdsm/test_vdsm_integration.py +46 -0
  97. mcp_synology-0.4.0/uv.lock +1609 -0
@@ -0,0 +1,50 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ env:
10
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v5
18
+ with:
19
+ python-version: "3.12"
20
+ - run: uv sync --extra dev
21
+ - run: uv run ruff check src/ tests/
22
+ - run: uv run ruff format --check src/ tests/
23
+
24
+ typecheck:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - uses: astral-sh/setup-uv@v5
29
+ with:
30
+ python-version: "3.12"
31
+ - run: uv sync --extra dev
32
+ - run: uv run mypy src/
33
+
34
+ test:
35
+ runs-on: ubuntu-latest
36
+ strategy:
37
+ fail-fast: false
38
+ matrix:
39
+ python-version: ["3.11", "3.12", "3.13"]
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+ - uses: astral-sh/setup-uv@v5
43
+ with:
44
+ python-version: ${{ matrix.python-version }}
45
+ - run: uv sync --extra dev
46
+ - run: uv run pytest --cov=mcp_synology --cov-report=xml
47
+ - uses: codecov/codecov-action@v4
48
+ if: matrix.python-version == '3.12'
49
+ with:
50
+ files: coverage.xml
@@ -0,0 +1,120 @@
1
+ name: PR Label Automation (CI)
2
+
3
+ # Handles label transitions when CI completes (pass or fail).
4
+ # Split from pr-labels.yml because workflow_run fires on every CI
5
+ # completion (including merges to main), which caused noisy failures
6
+ # when there was no associated PR.
7
+ #
8
+ # NOTE: References the CI workflow by name ("CI").
9
+ # If the CI workflow in ci.yml is ever renamed, update the name here too.
10
+ #
11
+ # LIMITATION: workflow_run triggers always run from the default branch
12
+ # (main), not the PR branch. Changes to this file cannot be tested
13
+ # on a PR — they take effect only after merge.
14
+ on:
15
+ workflow_run:
16
+ workflows: [CI]
17
+ types: [completed]
18
+
19
+ permissions:
20
+ pull-requests: write
21
+ checks: read
22
+
23
+ jobs:
24
+ # When CI passes on a PR branch, promote from Awaiting CI to Ready for QA.
25
+ # Skips if Dev Active is present (dev isn't done yet).
26
+ on-ci-pass:
27
+ if: >-
28
+ github.event.workflow_run.conclusion == 'success'
29
+ && github.event.workflow_run.event == 'pull_request'
30
+ && github.event.workflow_run.head_branch != github.event.repository.default_branch
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - name: Promote to Ready for QA
34
+ env:
35
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36
+ run: |
37
+ REPO=${{ github.repository }}
38
+
39
+ # Look up the associated PR.
40
+ # The API can 404 after a force-push (orphaned run).
41
+ # Capture output only on success so 404 body doesn't leak into $PR.
42
+ PR=""
43
+ API_OUT=$(gh api "repos/$REPO/actions/runs/${{ github.event.workflow_run.id }}/pull_requests" \
44
+ --jq '.[0].number // empty' 2>&1) && PR="$API_OUT" || true
45
+
46
+ # Fallback: pull_requests array is empty for dependabot PRs.
47
+ # Search by head branch instead.
48
+ if [ -z "$PR" ]; then
49
+ HEAD_BRANCH=${{ github.event.workflow_run.head_branch }}
50
+ PR=$(gh pr list --repo "$REPO" --head "$HEAD_BRANCH" --state open \
51
+ --json number --jq '.[0].number // empty' 2>/dev/null) || true
52
+ fi
53
+
54
+ if [ -z "$PR" ]; then
55
+ echo "No PR associated with this workflow run — exiting"
56
+ exit 0
57
+ fi
58
+
59
+ LABELS=$(gh pr view "$PR" --repo "$REPO" --json labels --jq '.labels[].name')
60
+
61
+ # Dev Active means dev isn't done — don't promote.
62
+ # on-unlabel will handle promotion when Dev Active is removed.
63
+ if echo "$LABELS" | grep -q "^Dev Active$"; then
64
+ echo "Dev Active present — skipping promotion"
65
+ exit 0
66
+ fi
67
+
68
+ # Promote if Awaiting CI is present
69
+ if echo "$LABELS" | grep -q "^Awaiting CI$"; then
70
+ REMOVE="Awaiting CI"
71
+ if echo "$LABELS" | grep -q "^CI Failed$"; then
72
+ REMOVE="Awaiting CI,CI Failed"
73
+ fi
74
+ gh pr edit "$PR" --repo "$REPO" --remove-label "$REMOVE"
75
+ gh pr edit "$PR" --repo "$REPO" --add-label "Ready for QA"
76
+ fi
77
+
78
+ # When CI fails on a PR branch, set CI Failed.
79
+ # Skips if Dev Active is present (dev is already working).
80
+ on-ci-fail:
81
+ if: >-
82
+ github.event.workflow_run.conclusion == 'failure'
83
+ && github.event.workflow_run.event == 'pull_request'
84
+ && github.event.workflow_run.head_branch != github.event.repository.default_branch
85
+ runs-on: ubuntu-latest
86
+ steps:
87
+ - name: Set CI Failed
88
+ env:
89
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
90
+ run: |
91
+ REPO=${{ github.repository }}
92
+
93
+ PR=""
94
+ API_OUT=$(gh api "repos/$REPO/actions/runs/${{ github.event.workflow_run.id }}/pull_requests" \
95
+ --jq '.[0].number // empty' 2>&1) && PR="$API_OUT" || true
96
+
97
+ if [ -z "$PR" ]; then
98
+ HEAD_BRANCH=${{ github.event.workflow_run.head_branch }}
99
+ PR=$(gh pr list --repo "$REPO" --head "$HEAD_BRANCH" --state open \
100
+ --json number --jq '.[0].number // empty' 2>/dev/null) || true
101
+ fi
102
+
103
+ if [ -z "$PR" ]; then
104
+ echo "No PR associated with this workflow run — exiting"
105
+ exit 0
106
+ fi
107
+
108
+ LABELS=$(gh pr view "$PR" --repo "$REPO" --json labels --jq '.labels[].name')
109
+
110
+ # Dev Active means dev is already working — don't add CI Failed
111
+ if echo "$LABELS" | grep -q "^Dev Active$"; then
112
+ echo "Dev Active present — skipping CI Failed"
113
+ exit 0
114
+ fi
115
+
116
+ # Set CI Failed if Awaiting CI is present (normal flow)
117
+ if echo "$LABELS" | grep -q "^Awaiting CI$"; then
118
+ gh pr edit "$PR" --repo "$REPO" --remove-label "Awaiting CI"
119
+ gh pr edit "$PR" --repo "$REPO" --add-label "CI Failed"
120
+ fi
@@ -0,0 +1,151 @@
1
+ name: PR Label Automation
2
+
3
+ # Handles label transitions triggered by PR events (push, label, unlabel).
4
+ # CI-completion promotion lives in pr-labels-ci.yml (separate workflow_run trigger).
5
+ on:
6
+ pull_request:
7
+ branches: [main]
8
+ types: [opened, synchronize, labeled, unlabeled]
9
+
10
+ permissions:
11
+ pull-requests: write
12
+ checks: read
13
+
14
+ jobs:
15
+ # When a PR is opened or new commits are pushed, reset to Awaiting CI.
16
+ # Removes stale QA/workflow labels. Keeps Dev Active if present (coexists
17
+ # with Awaiting CI — dev is still working, CI runs but won't promote).
18
+ on-push:
19
+ if: github.event.action == 'opened' || github.event.action == 'synchronize'
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Reset labels on new push
23
+ env:
24
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25
+ PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }}
26
+ run: |
27
+ PR=${{ github.event.pull_request.number }}
28
+ REPO=${{ github.repository }}
29
+
30
+ HAD_QA_ACTIVE=false
31
+ if echo "$PR_LABELS" | grep -q '"QA Active"'; then
32
+ HAD_QA_ACTIVE=true
33
+ fi
34
+
35
+ # Remove stale workflow labels (coalesced into one API call)
36
+ REMOVE=""
37
+ for LABEL in "Ready for QA" "Ready for QA Signoff" "QA Approved" "QA Active" "QA Failed" "CI Failed"; do
38
+ if echo "$PR_LABELS" | grep -q "\"$LABEL\""; then
39
+ if [ -n "$REMOVE" ]; then
40
+ REMOVE="$REMOVE,$LABEL"
41
+ else
42
+ REMOVE="$LABEL"
43
+ fi
44
+ fi
45
+ done
46
+ if [ -n "$REMOVE" ]; then
47
+ gh pr edit "$PR" --repo "$REPO" --remove-label "$REMOVE"
48
+ fi
49
+
50
+ # Add Awaiting CI (unless already present).
51
+ # Dev Active is NOT removed — it coexists with Awaiting CI.
52
+ if ! echo "$PR_LABELS" | grep -q '"Awaiting CI"'; then
53
+ gh pr edit "$PR" --repo "$REPO" --add-label "Awaiting CI"
54
+ fi
55
+
56
+ # If QA was actively reviewing, notify via comment
57
+ if [ "$HAD_QA_ACTIVE" = true ]; then
58
+ gh pr comment "$PR" --repo "$REPO" \
59
+ --body "New commits pushed while QA was active. QA review invalidated — resetting to Awaiting CI."
60
+ fi
61
+
62
+ # When Dev Active is removed manually, check if CI already passed.
63
+ # If yes, promote straight to Ready for QA. If no, Awaiting CI is
64
+ # already present and on-ci-pass will promote when CI completes.
65
+ on-unlabel:
66
+ if: >-
67
+ github.event.action == 'unlabeled'
68
+ && github.event.label.name == 'Dev Active'
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - name: Transition after Dev Active removed
72
+ env:
73
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74
+ PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }}
75
+ run: |
76
+ PR=${{ github.event.pull_request.number }}
77
+ REPO=${{ github.repository }}
78
+
79
+ # Check if CI already passed for the head commit
80
+ HEAD_SHA=${{ github.event.pull_request.head.sha }}
81
+ CI_CONCLUSION=$(gh api "repos/$REPO/actions/workflows/ci.yml/runs?head_sha=$HEAD_SHA" \
82
+ --jq '.workflow_runs[0].conclusion // empty' 2>/dev/null) || true
83
+
84
+ if [ "$CI_CONCLUSION" = "success" ]; then
85
+ # CI passed — promote to Ready for QA
86
+ REMOVE=""
87
+ if echo "$PR_LABELS" | grep -q '"Awaiting CI"'; then
88
+ REMOVE="Awaiting CI"
89
+ fi
90
+ if echo "$PR_LABELS" | grep -q '"CI Failed"'; then
91
+ REMOVE="${REMOVE:+$REMOVE,}CI Failed"
92
+ fi
93
+ if [ -n "$REMOVE" ]; then
94
+ gh pr edit "$PR" --repo "$REPO" --remove-label "$REMOVE"
95
+ fi
96
+ gh pr edit "$PR" --repo "$REPO" --add-label "Ready for QA"
97
+ else
98
+ # CI hasn't passed — Awaiting CI should already be present
99
+ if ! echo "$PR_LABELS" | grep -q '"Awaiting CI"'; then
100
+ gh pr edit "$PR" --repo "$REPO" --add-label "Awaiting CI"
101
+ fi
102
+ fi
103
+
104
+ # When a workflow label is added, clean up labels that no longer apply.
105
+ # Only runs for labels that have cleanup rules.
106
+ on-label:
107
+ if: >-
108
+ github.event.action == 'labeled'
109
+ && contains(fromJSON('["Ready for QA","QA Active","Dev Active","Ready for QA Signoff","QA Failed","QA Approved"]'), github.event.label.name)
110
+ runs-on: ubuntu-latest
111
+ steps:
112
+ - name: Clean up stale labels
113
+ env:
114
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
115
+ ADDED_LABEL: ${{ github.event.label.name }}
116
+ PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }}
117
+ run: |
118
+ PR=${{ github.event.pull_request.number }}
119
+ REPO=${{ github.repository }}
120
+
121
+ remove_if_present() {
122
+ if echo "$PR_LABELS" | grep -q "\"$1\""; then
123
+ gh pr edit "$PR" --repo "$REPO" --remove-label "$1"
124
+ fi
125
+ }
126
+
127
+ case "$ADDED_LABEL" in
128
+ "Ready for QA")
129
+ remove_if_present "Awaiting CI"
130
+ remove_if_present "CI Failed"
131
+ ;;
132
+ "QA Active")
133
+ remove_if_present "Ready for QA"
134
+ ;;
135
+ "Dev Active")
136
+ remove_if_present "QA Failed"
137
+ remove_if_present "CI Failed"
138
+ remove_if_present "Awaiting CI"
139
+ remove_if_present "Ready for QA"
140
+ ;;
141
+ "Ready for QA Signoff")
142
+ remove_if_present "QA Active"
143
+ ;;
144
+ "QA Failed")
145
+ remove_if_present "QA Active"
146
+ remove_if_present "Ready for QA Signoff"
147
+ ;;
148
+ "QA Approved")
149
+ remove_if_present "Ready for QA Signoff"
150
+ ;;
151
+ esac
@@ -0,0 +1,67 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ env:
9
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: astral-sh/setup-uv@v5
18
+ with:
19
+ python-version: "3.12"
20
+
21
+ - name: Run tests
22
+ run: |
23
+ uv sync --extra dev
24
+ uv run pytest
25
+
26
+ - name: Build distributions
27
+ run: uv build
28
+
29
+ - name: Upload distributions
30
+ uses: actions/upload-artifact@v4
31
+ with:
32
+ name: dist
33
+ path: dist/
34
+
35
+ publish-pypi:
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+ environment: pypi
39
+ permissions:
40
+ id-token: write
41
+ steps:
42
+ - name: Download distributions
43
+ uses: actions/download-artifact@v4
44
+ with:
45
+ name: dist
46
+ path: dist/
47
+
48
+ - name: Publish to PyPI
49
+ uses: pypa/gh-action-pypi-publish@release/v1
50
+
51
+ github-release:
52
+ needs: publish-pypi
53
+ runs-on: ubuntu-latest
54
+ permissions:
55
+ contents: write
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+ with:
59
+ fetch-depth: 0
60
+
61
+ - name: Create GitHub Release
62
+ env:
63
+ GH_TOKEN: ${{ github.token }}
64
+ run: |
65
+ gh release create "${{ github.ref_name }}" \
66
+ --title "${{ github.ref_name }}" \
67
+ --generate-notes
@@ -0,0 +1,30 @@
1
+ name: QA Gate
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ types: [opened, synchronize, reopened, labeled, unlabeled]
7
+
8
+ permissions:
9
+ statuses: write
10
+
11
+ jobs:
12
+ qa-approved:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Set QA status
16
+ env:
17
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18
+ run: |
19
+ if echo '${{ toJSON(github.event.pull_request.labels.*.name) }}' | grep -q '"QA Approved"'; then
20
+ STATE="success"
21
+ DESC="QA testing completed"
22
+ else
23
+ STATE="pending"
24
+ DESC="Waiting for QA — add 'QA Approved' label when testing is complete"
25
+ fi
26
+ gh api repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }} \
27
+ --method POST \
28
+ -f state="$STATE" \
29
+ -f description="$DESC" \
30
+ -f context="QA Gate"
@@ -0,0 +1,49 @@
1
+ name: Test Publish
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ env:
7
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: astral-sh/setup-uv@v5
16
+ with:
17
+ python-version: "3.12"
18
+
19
+ - name: Run tests
20
+ run: |
21
+ uv sync --extra dev
22
+ uv run pytest
23
+
24
+ - name: Build distributions
25
+ run: uv build
26
+
27
+ - name: Upload distributions
28
+ uses: actions/upload-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+
33
+ publish:
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ environment: testpypi
37
+ permissions:
38
+ id-token: write
39
+ steps:
40
+ - name: Download distributions
41
+ uses: actions/download-artifact@v4
42
+ with:
43
+ name: dist
44
+ path: dist/
45
+
46
+ - name: Publish to TestPyPI
47
+ uses: pypa/gh-action-pypi-publish@release/v1
48
+ with:
49
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,213 @@
1
+ # mcp-synology
2
+ tests/integration_config.yaml
3
+
4
+ # Virtual DSM test infrastructure (golden images ~2GB each)
5
+ .vdsm/
6
+
7
+ # Byte-compiled / optimized / DLL files
8
+ __pycache__/
9
+ *.py[codz]
10
+ *$py.class
11
+
12
+ # C extensions
13
+ *.so
14
+
15
+ # Distribution / packaging
16
+ .Python
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ downloads/
21
+ eggs/
22
+ .eggs/
23
+ lib/
24
+ lib64/
25
+ parts/
26
+ sdist/
27
+ var/
28
+ wheels/
29
+ share/python-wheels/
30
+ *.egg-info/
31
+ .installed.cfg
32
+ *.egg
33
+ MANIFEST
34
+
35
+ # PyInstaller
36
+ # Usually these files are written by a python script from a template
37
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
38
+ *.manifest
39
+ *.spec
40
+
41
+ # Installer logs
42
+ pip-log.txt
43
+ pip-delete-this-directory.txt
44
+
45
+ # Unit test / coverage reports
46
+ htmlcov/
47
+ .tox/
48
+ .nox/
49
+ .coverage
50
+ .coverage.*
51
+ .cache
52
+ nosetests.xml
53
+ coverage.xml
54
+ *.cover
55
+ *.py.cover
56
+ .hypothesis/
57
+ .pytest_cache/
58
+ cover/
59
+
60
+ # Translations
61
+ *.mo
62
+ *.pot
63
+
64
+ # Django stuff:
65
+ *.log
66
+ local_settings.py
67
+ db.sqlite3
68
+ db.sqlite3-journal
69
+
70
+ # Flask stuff:
71
+ instance/
72
+ .webassets-cache
73
+
74
+ # Scrapy stuff:
75
+ .scrapy
76
+
77
+ # Sphinx documentation
78
+ docs/_build/
79
+
80
+ # PyBuilder
81
+ .pybuilder/
82
+ target/
83
+
84
+ # Jupyter Notebook
85
+ .ipynb_checkpoints
86
+
87
+ # IPython
88
+ profile_default/
89
+ ipython_config.py
90
+
91
+ # pyenv
92
+ # For a library or package, you might want to ignore these files since the code is
93
+ # intended to run in multiple environments; otherwise, check them in:
94
+ # .python-version
95
+
96
+ # pipenv
97
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
98
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
99
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
100
+ # install all needed dependencies.
101
+ #Pipfile.lock
102
+
103
+ # UV
104
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ #uv.lock
108
+
109
+ # poetry
110
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
111
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
112
+ # commonly ignored for libraries.
113
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
114
+ #poetry.lock
115
+ #poetry.toml
116
+
117
+ # pdm
118
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
119
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
120
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
121
+ #pdm.lock
122
+ #pdm.toml
123
+ .pdm-python
124
+ .pdm-build/
125
+
126
+ # pixi
127
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
128
+ #pixi.lock
129
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
130
+ # in the .venv directory. It is recommended not to include this directory in version control.
131
+ .pixi
132
+
133
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
134
+ __pypackages__/
135
+
136
+ # Celery stuff
137
+ celerybeat-schedule
138
+ celerybeat.pid
139
+
140
+ # SageMath parsed files
141
+ *.sage.py
142
+
143
+ # Environments
144
+ .env
145
+ .envrc
146
+ .venv
147
+ env/
148
+ venv/
149
+ ENV/
150
+ env.bak/
151
+ venv.bak/
152
+
153
+ # Spyder project settings
154
+ .spyderproject
155
+ .spyproject
156
+
157
+ # Rope project settings
158
+ .ropeproject
159
+
160
+ # mkdocs documentation
161
+ /site
162
+
163
+ # mypy
164
+ .mypy_cache/
165
+ .dmypy.json
166
+ dmypy.json
167
+
168
+ # Pyre type checker
169
+ .pyre/
170
+
171
+ # pytype static type analyzer
172
+ .pytype/
173
+
174
+ # Cython debug symbols
175
+ cython_debug/
176
+
177
+ # PyCharm
178
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
179
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
180
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
181
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
182
+ #.idea/
183
+
184
+ # Abstra
185
+ # Abstra is an AI-powered process automation framework.
186
+ # Ignore directories containing user credentials, local state, and settings.
187
+ # Learn more at https://abstra.io/docs
188
+ .abstra/
189
+
190
+ # Visual Studio Code
191
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
192
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
193
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
194
+ # you could uncomment the following to ignore the entire vscode folder
195
+ # .vscode/
196
+
197
+ # Ruff stuff:
198
+ .ruff_cache/
199
+
200
+ # PyPI configuration file
201
+ .pypirc
202
+
203
+ # Cursor
204
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
205
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
206
+ # refer to https://docs.cursor.com/context/ignore-files
207
+ .cursorignore
208
+ .cursorindexingignore
209
+
210
+ # Marimo
211
+ marimo/_static/
212
+ marimo/_lsp/
213
+ __marimo__/