Skip to content

Commit 098a518

Browse files
committed
feat: initial commit - penpot-mcp-server project
0 parents  commit 098a518

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+117711
-0
lines changed

.dockerignore

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Dependencies
2+
node_modules
3+
npm-debug.log
4+
yarn-debug.log*
5+
yarn-error.log*
6+
# package-lock.json is needed for npm ci in Docker
7+
8+
# Build output
9+
dist
10+
*.tsbuildinfo
11+
12+
# Environment files
13+
.env
14+
.env.local
15+
.env.*.local
16+
.env.example
17+
18+
# IDE / Editor
19+
.vscode
20+
.idea
21+
*.swp
22+
*.swo
23+
*~
24+
25+
# OS files
26+
.DS_Store
27+
Thumbs.db
28+
29+
# Git files
30+
.git
31+
.gitignore
32+
.gitattributes
33+
.github
34+
35+
# Git hooks
36+
lefthook.yml
37+
.husky
38+
39+
# Documentation (include only necessary docs)
40+
*.md
41+
!README.md
42+
CONTRIBUTING.md
43+
TESTING.md
44+
EXAMPLES.md
45+
VISUAL_PROPERTIES_EXAMPLES.md
46+
47+
# Logs
48+
logs
49+
*.log
50+
51+
# Test files
52+
test-*.ts
53+
check-*.ts
54+
*.test.ts
55+
*.spec.ts
56+
coverage
57+
.nyc_output
58+
59+
# Docker files (avoid nested docker builds)
60+
Dockerfile*
61+
docker-compose*.yml
62+
.dockerignore
63+
64+
# CI/CD
65+
.github
66+
.gitlab-ci.yml
67+
.travis.yml
68+
.circleci
69+
70+
# Development tools
71+
.editorconfig
72+
.prettierrc*
73+
.eslintrc*
74+
.eslintignore
75+
# tsconfig.json is needed for Docker build
76+
77+
# MCP configuration (contains sensitive tokens)
78+
.mcp.json
79+
80+
# NPM package files
81+
.npmignore
82+
83+
# Temporary files
84+
*.tmp
85+
.cache
86+
tmp

.env.example

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Penpot API Configuration
2+
PENPOT_API_URL=https://design.penpot.app
3+
PENPOT_ACCESS_TOKEN=your-access-token-here
4+
5+
# Transport Mode (stdio or http)
6+
TRANSPORT=stdio
7+
8+
# HTTP Mode Configuration (only used when TRANSPORT=http)
9+
HTTP_PORT=3000
10+
HTTP_HOST=0.0.0.0
11+
12+
# Security Options (optional, for HTTP mode)
13+
ALLOWED_ORIGINS=https://example.com,https://app.example.com
14+
ALLOWED_HOSTS=localhost,example.com
15+
ENABLE_DNS_REBINDING_PROTECTION=false

.gitattributes

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto
3+
4+
# Source code
5+
*.ts text eol=lf
6+
*.js text eol=lf
7+
*.json text eol=lf
8+
*.md text eol=lf
9+
*.yml text eol=lf
10+
*.yaml text eol=lf
11+
12+
# Shell scripts
13+
*.sh text eol=lf
14+
15+
# Docker
16+
Dockerfile text eol=lf
17+
docker-compose.yml text eol=lf
18+
19+
# Configuration files
20+
.env.example text eol=lf
21+
.gitignore text eol=lf
22+
.gitattributes text eol=lf
23+
.npmignore text eol=lf
24+
tsconfig.json text eol=lf
25+
package.json text eol=lf
26+
package-lock.json text eol=lf linguist-generated=true
27+
28+
# Binary files
29+
*.png binary
30+
*.jpg binary
31+
*.jpeg binary
32+
*.gif binary
33+
*.ico binary
34+
*.pdf binary
35+
*.zip binary
36+
*.tar binary
37+
*.gz binary

.github/workflows/docker.yml

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
name: Docker Build and Test
2+
3+
on:
4+
push:
5+
branches: [ main, develop ]
6+
tags: [ 'v*' ]
7+
pull_request:
8+
branches: [ main, develop ]
9+
10+
env:
11+
REGISTRY: ghcr.io
12+
IMAGE_NAME: ${{ github.repository }}
13+
14+
jobs:
15+
# Check which features should be enabled based on secrets/repository
16+
check-config:
17+
runs-on: ubuntu-latest
18+
outputs:
19+
should-push-docker: ${{ steps.check.outputs.should-push-docker }}
20+
should-publish-npm: ${{ steps.check.outputs.should-publish-npm }}
21+
steps:
22+
- id: check
23+
env:
24+
DOCKER_ENABLED: ${{ secrets.DOCKER_PUSH_ENABLED }}
25+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
26+
run: |
27+
# Check if Docker push should be enabled
28+
if [ "${{ github.repository }}" == "zcube/penpot-mcp-server" ] || [ "$DOCKER_ENABLED" == "true" ]; then
29+
echo "should-push-docker=true" >> $GITHUB_OUTPUT
30+
else
31+
echo "should-push-docker=false" >> $GITHUB_OUTPUT
32+
fi
33+
34+
# Check if NPM publish should be enabled
35+
if [ "${{ github.repository }}" == "zcube/penpot-mcp-server" ] || [ -n "$NPM_TOKEN" ]; then
36+
echo "should-publish-npm=true" >> $GITHUB_OUTPUT
37+
else
38+
echo "should-publish-npm=false" >> $GITHUB_OUTPUT
39+
fi
40+
41+
# Build and test the Docker image
42+
build-and-test:
43+
runs-on: ubuntu-latest
44+
needs: check-config
45+
permissions:
46+
contents: read
47+
packages: write
48+
49+
steps:
50+
- name: Checkout repository
51+
uses: actions/checkout@v4
52+
53+
- name: Set up Docker Buildx
54+
uses: docker/setup-buildx-action@v3
55+
56+
- name: Log in to Container Registry
57+
# Only login if: not a PR AND push is enabled
58+
if: |
59+
github.event_name != 'pull_request' &&
60+
needs.check-config.outputs.should-push-docker == 'true'
61+
uses: docker/login-action@v3
62+
with:
63+
registry: ${{ env.REGISTRY }}
64+
username: ${{ github.actor }}
65+
password: ${{ secrets.GITHUB_TOKEN }}
66+
67+
- name: Extract metadata
68+
id: meta
69+
uses: docker/metadata-action@v5
70+
with:
71+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
72+
tags: |
73+
# Production tags (for version tags like v1.0.0)
74+
type=semver,pattern={{version}}
75+
type=semver,pattern={{major}}.{{minor}}
76+
type=semver,pattern={{major}}
77+
type=semver,pattern=latest
78+
# Development tag (for main branch only)
79+
type=ref,event=branch,suffix=-dev
80+
81+
- name: Build builder stage (host platform only, for caching)
82+
uses: docker/build-push-action@v5
83+
with:
84+
context: .
85+
file: ./Dockerfile
86+
target: builder
87+
push: false
88+
cache-from: type=gha
89+
cache-to: type=gha,mode=max
90+
91+
- name: Build test image (host platform only)
92+
uses: docker/build-push-action@v5
93+
with:
94+
context: .
95+
file: ./Dockerfile
96+
push: false
97+
load: true
98+
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test
99+
cache-from: type=gha
100+
101+
- name: Test Docker image
102+
run: |
103+
docker run --rm \
104+
-e PENPOT_API_URL="${{ secrets.PENPOT_API_URL }}" \
105+
-e PENPOT_ACCESS_TOKEN="${{ secrets.PENPOT_ACCESS_TOKEN }}" \
106+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test \
107+
node -e "console.log('Container starts successfully')"
108+
109+
- name: Build and push production image (multi-arch)
110+
# Only push if: not a PR AND (main branch OR version tag) AND push is enabled
111+
if: |
112+
github.event_name != 'pull_request' &&
113+
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) &&
114+
needs.check-config.outputs.should-push-docker == 'true'
115+
uses: docker/build-push-action@v5
116+
with:
117+
context: .
118+
file: ./Dockerfile
119+
platforms: linux/amd64,linux/arm64
120+
push: true
121+
tags: ${{ steps.meta.outputs.tags }}
122+
labels: ${{ steps.meta.outputs.labels }}
123+
cache-from: type=gha
124+
125+
# Run integration tests with npm test against external Penpot instance
126+
# Requires penpot-test environment with PENPOT_API_URL and PENPOT_ACCESS_TOKEN secrets
127+
# Forks can create this environment to enable integration tests
128+
integration-test:
129+
runs-on: ubuntu-latest
130+
environment: penpot-test
131+
needs: build-and-test
132+
# Skip on forks without penpot-test environment configured
133+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
134+
135+
steps:
136+
- name: Check for Penpot credentials
137+
id: check-secrets
138+
env:
139+
PENPOT_URL: ${{ secrets.PENPOT_API_URL }}
140+
PENPOT_TOKEN: ${{ secrets.PENPOT_ACCESS_TOKEN }}
141+
run: |
142+
if [ -n "$PENPOT_URL" ] && [ -n "$PENPOT_TOKEN" ]; then
143+
echo "has-secrets=true" >> $GITHUB_OUTPUT
144+
echo "✅ Penpot credentials found - integration tests will run"
145+
else
146+
echo "has-secrets=false" >> $GITHUB_OUTPUT
147+
echo "⚠️ Penpot credentials not found - skipping integration tests"
148+
fi
149+
150+
- name: Checkout repository
151+
if: steps.check-secrets.outputs.has-secrets == 'true'
152+
uses: actions/checkout@v4
153+
154+
- name: Set up Node.js
155+
if: steps.check-secrets.outputs.has-secrets == 'true'
156+
uses: actions/setup-node@v4
157+
with:
158+
node-version: '20'
159+
cache: 'npm'
160+
161+
- name: Install dependencies
162+
if: steps.check-secrets.outputs.has-secrets == 'true'
163+
run: npm ci
164+
165+
- name: Build project
166+
if: steps.check-secrets.outputs.has-secrets == 'true'
167+
run: npm run build
168+
169+
- name: Run tests
170+
if: steps.check-secrets.outputs.has-secrets == 'true'
171+
env:
172+
PENPOT_API_URL: ${{ secrets.PENPOT_API_URL }}
173+
PENPOT_ACCESS_TOKEN: ${{ secrets.PENPOT_ACCESS_TOKEN }}
174+
run: npm test
175+
176+
- name: Upload test results
177+
if: always() && steps.check-secrets.outputs.has-secrets == 'true'
178+
uses: actions/upload-artifact@v4
179+
with:
180+
name: integration-test-results
181+
path: |
182+
logs/
183+
test-results/
184+
185+
# Security scanning - runs when images are pushed
186+
# Scans the published Docker image for vulnerabilities
187+
security-scan:
188+
runs-on: ubuntu-latest
189+
needs: [check-config, build-and-test]
190+
# Only scan when image is pushed (same condition as image push)
191+
if: |
192+
github.event_name != 'pull_request' &&
193+
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) &&
194+
needs.check-config.outputs.should-push-docker == 'true'
195+
196+
steps:
197+
- name: Checkout repository
198+
uses: actions/checkout@v4
199+
200+
- name: Determine image tag to scan
201+
id: image-tag
202+
run: |
203+
if [[ $GITHUB_REF == refs/tags/v* ]]; then
204+
# For version tags, extract version number (v1.0.0 -> 1.0.0)
205+
VERSION=${GITHUB_REF#refs/tags/v}
206+
echo "tag=$VERSION" >> $GITHUB_OUTPUT
207+
else
208+
# For main branch, use dev tag
209+
echo "tag=main-dev" >> $GITHUB_OUTPUT
210+
fi
211+
212+
- name: Run Trivy vulnerability scanner
213+
uses: aquasecurity/trivy-action@master
214+
with:
215+
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.image-tag.outputs.tag }}
216+
format: 'sarif'
217+
output: 'trivy-results.sarif'
218+
219+
- name: Upload Trivy results to GitHub Security
220+
uses: github/codeql-action/upload-sarif@v3
221+
if: always()
222+
with:
223+
sarif_file: 'trivy-results.sarif'
224+
225+
# Publish to npm when version tag is pushed
226+
# Only publishes from original repo or when NPM_TOKEN is explicitly configured
227+
publish-npm:
228+
runs-on: ubuntu-latest
229+
needs: [check-config, build-and-test]
230+
if: |
231+
startsWith(github.ref, 'refs/tags/v') &&
232+
needs.check-config.outputs.should-publish-npm == 'true'
233+
permissions:
234+
contents: read
235+
id-token: write # Required for npm provenance
236+
237+
steps:
238+
- name: Checkout repository
239+
uses: actions/checkout@v4
240+
241+
- name: Set up Node.js
242+
uses: actions/setup-node@v4
243+
with:
244+
node-version: '20'
245+
registry-url: 'https://registry.npmjs.org'
246+
cache: 'npm'
247+
248+
- name: Install dependencies
249+
run: npm ci
250+
251+
- name: Build project
252+
run: npm run build
253+
254+
- name: Publish to npm
255+
env:
256+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
257+
run: npm publish --provenance --access public

0 commit comments

Comments
 (0)