-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
204 lines (184 loc) · 6.82 KB
/
action.yml
File metadata and controls
204 lines (184 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# yamllint disable rule:line-length
---
name: MCP Bundle Packager
description: Package MCP server projects into MCPB (MCP Bundle) archives with manifest validation, checksums, and release asset support.
author: Robert Allen <zircote@gmail.com>
branding:
icon: package
color: orange
inputs:
# Bundles the working directory via mcpb or zip; use .mcpbignore to exclude files.
# For source-files/config-files/additional-artifacts, use the reusable workflow.
manifest-path:
description: Path to the bundle manifest.json
required: false
default: manifest.json
bundle-name:
description: Override bundle output filename (defaults to name from manifest.json)
required: false
default: ''
upload-artifact:
description: Whether to upload the bundle as a GitHub Actions artifact
required: false
default: 'true'
create-release-asset:
description: Whether to attach the bundle to a GitHub Release (only on tag pushes)
required: false
default: 'false'
mcpb-version:
description: Version of mcpb toolchain to use
required: false
default: latest
outputs:
bundle-path:
description: Path to the generated bundle file
value: ${{ steps.pack.outputs.bundle-path }}
bundle-sha256:
description: SHA-256 checksum of the bundle for integrity verification
value: ${{ steps.checksum.outputs.sha256 }}
manifest-valid:
description: Whether manifest validation passed (true/false)
value: ${{ steps.validate.outputs.valid }}
runs:
using: composite
steps:
- name: Detect server type
id: detect
shell: bash
env:
MANIFEST: ${{ inputs.manifest-path }}
run: |
if [ ! -f "$MANIFEST" ]; then
echo "::error::Manifest not found: $MANIFEST"
exit 1
fi
SERVER_TYPE=$(jq -r '.server.type' "$MANIFEST")
echo "server-type=$SERVER_TYPE" >> "$GITHUB_OUTPUT"
- name: Validate manifest.json
id: validate
shell: bash
env:
MANIFEST: ${{ inputs.manifest-path }}
run: |
# shellcheck source=scripts/validate-manifest.sh
source "$GITHUB_ACTION_PATH/scripts/validate-manifest.sh"
ENTRY=$(jq -r '.server.entry_point // empty' "$MANIFEST")
if [ -n "$ENTRY" ] && [ ! -f "$ENTRY" ]; then
echo "::warning::Entry point not found: ${ENTRY} (expected if build produces it)"
fi
if ! ERRORS=$(validate_manifest "$MANIFEST"); then
echo "::error::Manifest validation failed:"
printf "%b" "$ERRORS" | while IFS= read -r line; do
echo "::error:: $line"
done
echo "valid=false" >> "$GITHUB_OUTPUT"
exit 1
fi
echo "Manifest validation passed"
echo "valid=true" >> "$GITHUB_OUTPUT"
- name: Install mcpb CLI
if: steps.detect.outputs.server-type == 'node'
shell: bash
env:
MCPB_VER: ${{ inputs.mcpb-version }}
run: |
if [ "$MCPB_VER" = "latest" ]; then
npm install -g @anthropic-ai/mcpb
else
npm install -g "@anthropic-ai/mcpb@${MCPB_VER}"
fi
- name: Package bundle
id: pack
shell: bash
env:
BUNDLE_NAME_INPUT: ${{ inputs.bundle-name }}
MANIFEST: ${{ inputs.manifest-path }}
run: |
if [ -n "$BUNDLE_NAME_INPUT" ]; then
BNAME="$BUNDLE_NAME_INPUT"
else
BNAME=$(jq -r '.name' "$MANIFEST")
fi
# Strip path separators and leading dots to prevent path traversal
BNAME=$(echo "$BNAME" | tr -d '/' | sed 's/^\.\+//')
if [ -z "$BNAME" ]; then
echo "::error::bundle-name resolves to empty after sanitization"
exit 1
fi
VERSION=$(jq -r '.version' "$MANIFEST")
VERSION=$(echo "$VERSION" | tr -cd 'a-zA-Z0-9._+-')
BUNDLE_FILE="${BNAME}-${VERSION}.mcpb"
# Pack from working directory using mcpb (preferred) or zip (fallback).
# Exclusions come from .mcpbignore and zip -x patterns.
EXCLUDES=("*.git*" "node_modules/*")
if [ -f ".mcpbignore" ]; then
while IFS= read -r pattern || [ -n "$pattern" ]; do
pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -z "$pattern" ] && continue
[[ "$pattern" == \#* ]] && continue
if [[ "$pattern" == \!* ]]; then
echo "::warning::.mcpbignore negation pattern not supported (skipped): ${pattern}"
continue
fi
if [[ "$pattern" == */ ]]; then
EXCLUDES+=("${pattern}*")
else
EXCLUDES+=("$pattern")
fi
done < ".mcpbignore"
fi
EXCLUDE_ARGS=()
for pat in "${EXCLUDES[@]}"; do
EXCLUDE_ARGS+=(-x "$pat")
done
if command -v mcpb &>/dev/null; then
mcpb pack --output "$BUNDLE_FILE" || \
zip -ry "$BUNDLE_FILE" . "${EXCLUDE_ARGS[@]}"
else
zip -ry "$BUNDLE_FILE" . "${EXCLUDE_ARGS[@]}"
fi
# Validate bundle structure
BUNDLE_CONTENTS=$(unzip -Z1 "$BUNDLE_FILE" 2>/dev/null || true)
if ! echo "$BUNDLE_CONTENTS" | grep -q '^manifest\.json$'; then
echo "::error::Bundle missing manifest.json — archive may be malformed"
exit 1
fi
ENTRY_POINT=$(jq -r '.server.entry_point // empty' "$MANIFEST")
if [ -n "$ENTRY_POINT" ]; then
ENTRY_REL="${ENTRY_POINT#./}"
if ! echo "$BUNDLE_CONTENTS" | grep -qF "$ENTRY_REL"; then
echo "::warning::Bundle does not contain declared entry_point: ${ENTRY_REL}"
fi
fi
echo "bundle-path=$BUNDLE_FILE" >> "$GITHUB_OUTPUT"
- name: Generate SHA-256 checksum
id: checksum
shell: bash
env:
BUNDLE_FILE: ${{ steps.pack.outputs.bundle-path }}
run: |
if command -v sha256sum &>/dev/null; then
SHA256=$(sha256sum "$BUNDLE_FILE" | awk '{print $1}')
else
SHA256=$(shasum -a 256 "$BUNDLE_FILE" | awk '{print $1}')
fi
echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"
echo "$SHA256 $BUNDLE_FILE" > "${BUNDLE_FILE}.sha256"
# NOTE: Pinned to mutable @v6 tag; see workflow for SHA guidance.
- name: Upload bundle artifact
if: inputs.upload-artifact == 'true'
uses: actions/upload-artifact@v6
with:
name: mcpb-bundle
path: |
${{ steps.pack.outputs.bundle-path }}
${{ steps.pack.outputs.bundle-path }}.sha256
- name: Attach to GitHub Release
if: inputs.create-release-asset == 'true' && startsWith(github.ref, 'refs/tags/')
shell: bash
env:
GH_TOKEN: ${{ github.token }}
BUNDLE_FILE: ${{ steps.pack.outputs.bundle-path }}
run: |
TAG="${GITHUB_REF#refs/tags/}"
gh release upload "$TAG" "$BUNDLE_FILE" "${BUNDLE_FILE}.sha256" --clobber