-
Notifications
You must be signed in to change notification settings - Fork 7.5k
Expand file tree
/
Copy pathcreate-release-packages.sh
More file actions
208 lines (182 loc) · 8.39 KB
/
create-release-packages.sh
File metadata and controls
208 lines (182 loc) · 8.39 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
205
206
207
208
#!/usr/bin/env bash
set -euo pipefail
# create-release-packages.sh (workflow-local)
# Build Spec Kit template release archives for each supported AI assistant and script type.
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
# Version argument should include leading 'v'.
# Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built.
# AGENTS : space or comma separated subset of: claude gemini copilot qwen opencode auggie (default: all)
# SCRIPTS : space or comma separated subset of: sh ps (default: both)
# Examples:
# AGENTS=claude SCRIPTS=sh $0 v0.2.0
# AGENTS="copilot,gemini" $0 v0.2.0
# SCRIPTS=ps $0 v0.2.0
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <version-with-v-prefix>" >&2
exit 1
fi
NEW_VERSION="$1"
if [[ ! $NEW_VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Version must look like v0.0.0" >&2
exit 1
fi
echo "Building release packages for $NEW_VERSION"
rm -rf sdd-package-base* sdd-*-package-* spec-kit-template-*-${NEW_VERSION}.zip || true
rewrite_paths() {
sed -E \
-e 's@(/?)memory/@.specify/memory/@g' \
-e 's@(/?)scripts/@.specify/scripts/@g' \
-e 's@(/?)templates/@.specify/templates/@g'
}
generate_commands() {
local agent=$1 ext=$2 arg_format=$3 output_dir=$4 script_variant=$5
mkdir -p "$output_dir"
for template in templates/commands/*.md; do
[[ -f "$template" ]] || continue
local name description script_command body
name=$(basename "$template" .md)
# Normalize line endings
file_content=$(tr -d '\r' < "$template")
# Extract description and script command from YAML frontmatter
description=$(printf '%s\n' "$file_content" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}')
script_command=$(printf '%s\n' "$file_content" | awk -v sv="$script_variant" '/^[[:space:]]*'"$script_variant"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, ""); print; exit}')
if [[ -z $script_command ]]; then
echo "Warning: no script command found for $script_variant in $template" >&2
script_command="(Missing script command for $script_variant)"
fi
# Replace {SCRIPT} placeholder with the script command
body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g")
# Remove the scripts: section from frontmatter while preserving YAML structure
body=$(printf '%s\n' "$body" | awk '
/^---$/ { print; if (++dash_count == 1) in_frontmatter=1; else in_frontmatter=0; next }
in_frontmatter && /^scripts:$/ { skip_scripts=1; next }
in_frontmatter && /^[a-zA-Z].*:/ && skip_scripts { skip_scripts=0 }
in_frontmatter && skip_scripts && /^[[:space:]]/ { next }
{ print }
')
# Apply other substitutions
body=$(printf '%s\n' "$body" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths)
case $ext in
toml)
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/$name.$ext" ;;
md)
echo "$body" > "$output_dir/$name.$ext" ;;
prompt.md)
echo "$body" > "$output_dir/$name.$ext" ;;
esac
done
}
build_variant() {
local agent=$1 script=$2
local base_dir="sdd-${agent}-package-${script}"
echo "Building $agent ($script) package..."
mkdir -p "$base_dir"
# Copy base structure but filter scripts by variant
SPEC_DIR="$base_dir/.specify"
mkdir -p "$SPEC_DIR"
[[ -d memory ]] && { cp -r memory "$SPEC_DIR/"; echo "Copied memory -> .specify"; }
# Only copy the relevant script variant directory
if [[ -d scripts ]]; then
mkdir -p "$SPEC_DIR/scripts"
case $script in
sh)
[[ -d scripts/bash ]] && { cp -r scripts/bash "$SPEC_DIR/scripts/"; echo "Copied scripts/bash -> .specify/scripts"; }
# Copy any script files that aren't in variant-specific directories
find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
;;
ps)
[[ -d scripts/powershell ]] && { cp -r scripts/powershell "$SPEC_DIR/scripts/"; echo "Copied scripts/powershell -> .specify/scripts"; }
# Copy any script files that aren't in variant-specific directories
find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
;;
esac
fi
[[ -d templates ]] && { mkdir -p "$SPEC_DIR/templates"; find templates -type f -not -path "templates/commands/*" -exec cp --parents {} "$SPEC_DIR"/ \; ; echo "Copied templates -> .specify/templates"; }
# Inject variant into plan-template.md within .specify/templates if present
local plan_tpl="$base_dir/.specify/templates/plan-template.md"
if [[ -f "$plan_tpl" ]]; then
plan_norm=$(tr -d '\r' < "$plan_tpl")
# Extract script command from YAML frontmatter
script_command=$(printf '%s\n' "$plan_norm" | awk -v sv="$script" '/^[[:space:]]*'"$script"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script"':[[:space:]]*/, ""); print; exit}')
if [[ -n $script_command ]]; then
# Always prefix with .specify/ for plan usage
script_command=".specify/$script_command"
tmp_file=$(mktemp)
# Replace {SCRIPT} placeholder with the script command and __AGENT__ with agent name
substituted=$(sed "s|{SCRIPT}|${script_command}|g" "$plan_tpl" | tr -d '\r' | sed "s|__AGENT__|${agent}|g")
# Strip YAML frontmatter from plan template output (keep body only)
stripped=$(printf '%s\n' "$substituted" | awk 'BEGIN{fm=0;dash=0} /^---$/ {dash++; if(dash==1){fm=1; next} else if(dash==2){fm=0; next}} {if(!fm) print}')
printf '%s\n' "$stripped" > "$plan_tpl"
else
echo "Warning: no plan-template script command found for $script in YAML frontmatter" >&2
fi
fi
case $agent in
claude)
mkdir -p "$base_dir/.claude/commands"
generate_commands claude md "\$ARGUMENTS" "$base_dir/.claude/commands" "$script" ;;
gemini)
mkdir -p "$base_dir/.gemini/commands"
generate_commands gemini toml "{{args}}" "$base_dir/.gemini/commands" "$script"
[[ -f agent_templates/gemini/GEMINI.md ]] && cp agent_templates/gemini/GEMINI.md "$base_dir/GEMINI.md" ;;
copilot)
mkdir -p "$base_dir/.github/prompts"
generate_commands copilot prompt.md "\$ARGUMENTS" "$base_dir/.github/prompts" "$script" ;;
cursor)
mkdir -p "$base_dir/.cursor/commands"
generate_commands cursor md "\$ARGUMENTS" "$base_dir/.cursor/commands" "$script" ;;
qwen)
mkdir -p "$base_dir/.qwen/commands"
generate_commands qwen md "\$ARGUMENTS" "$base_dir/.qwen/commands" "$script"
[[ -f agent_templates/qwen/QWEN.md ]] && cp agent_templates/qwen/QWEN.md "$base_dir/QWEN.md" ;;
opencode)
mkdir -p "$base_dir/.opencode/command"
generate_commands opencode md "\$ARGUMENTS" "$base_dir/.opencode/command" "$script" ;;
auggie)
mkdir -p "$base_dir/.augment/commands"
generate_commands auggie md "\$ARGUMENTS" "$base_dir/.augment/commands" "$script" ;;
esac
( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
echo "Created spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
}
# Determine agent list
ALL_AGENTS=(claude gemini copilot cursor qwen opencode auggie)
ALL_SCRIPTS=(sh ps)
norm_list() {
# convert comma+space separated -> space separated unique while preserving order of first occurrence
tr ',\n' ' ' | awk '{for(i=1;i<=NF;i++){if(!seen[$i]++){printf((out?" ":"") $i)}}}END{printf("\n")}'
}
validate_subset() {
local type=$1; shift; local -n allowed=$1; shift; local items=($@)
local ok=1
for it in "${items[@]}"; do
local found=0
for a in "${allowed[@]}"; do [[ $it == $a ]] && { found=1; break; }; done
if [[ $found -eq 0 ]]; then
echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2
ok=0
fi
done
return $ok
}
if [[ -n ${AGENTS:-} ]]; then
AGENT_LIST=($(printf '%s' "$AGENTS" | norm_list))
validate_subset agent ALL_AGENTS "${AGENT_LIST[@]}" || exit 1
else
AGENT_LIST=(${ALL_AGENTS[@]})
fi
if [[ -n ${SCRIPTS:-} ]]; then
SCRIPT_LIST=($(printf '%s' "$SCRIPTS" | norm_list))
validate_subset script ALL_SCRIPTS "${SCRIPT_LIST[@]}" || exit 1
else
SCRIPT_LIST=(${ALL_SCRIPTS[@]})
fi
echo "Agents: ${AGENT_LIST[*]}"
echo "Scripts: ${SCRIPT_LIST[*]}"
for agent in "${AGENT_LIST[@]}"; do
for script in "${SCRIPT_LIST[@]}"; do
build_variant "$agent" "$script"
done
done
echo "Archives:"
ls -1 spec-kit-template-*-${NEW_VERSION}.zip