Merge main into feature/add-qwen-support
Combines Qwen Code support with new script variant structure from main. Key additions: - Qwen Code AI assistant support - PowerShell script variants alongside Bash - Updated release packaging for per-script variants - Maintains all existing Cursor support from main Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
203
.github/workflows/scripts/create-release-packages.sh
vendored
203
.github/workflows/scripts/create-release-packages.sh
vendored
@@ -2,9 +2,16 @@
|
||||
set -euo pipefail
|
||||
|
||||
# create-release-packages.sh (workflow-local)
|
||||
# Build Spec Kit template release archives for each supported AI assistant.
|
||||
# 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'.
|
||||
# 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 (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
|
||||
@@ -18,19 +25,7 @@ fi
|
||||
|
||||
echo "Building release packages for $NEW_VERSION"
|
||||
|
||||
rm -rf sdd-package-base sdd-claude-package sdd-gemini-package sdd-copilot-package sdd-qwen-package \
|
||||
spec-kit-template-claude-${NEW_VERSION}.zip \
|
||||
spec-kit-template-gemini-${NEW_VERSION}.zip \
|
||||
spec-kit-template-copilot-${NEW_VERSION}.zip \
|
||||
spec-kit-template-qwen-${NEW_VERSION}.zip || true
|
||||
|
||||
mkdir -p sdd-package-base
|
||||
SPEC_DIR="sdd-package-base/.specify"
|
||||
mkdir -p "$SPEC_DIR"
|
||||
|
||||
[[ -d memory ]] && { cp -r memory "$SPEC_DIR/"; echo "Copied memory -> .specify"; }
|
||||
[[ -d scripts ]] && { cp -r scripts "$SPEC_DIR/"; echo "Copied scripts -> .specify/scripts"; }
|
||||
[[ -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"; }
|
||||
rm -rf sdd-package-base* sdd-*-package-* spec-kit-template-*-${NEW_VERSION}.zip || true
|
||||
|
||||
rewrite_paths() {
|
||||
sed -E \
|
||||
@@ -40,65 +35,161 @@ rewrite_paths() {
|
||||
}
|
||||
|
||||
generate_commands() {
|
||||
local agent=$1 ext=$2 arg_format=$3 output_dir=$4
|
||||
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 body
|
||||
local name description script_command body
|
||||
name=$(basename "$template" .md)
|
||||
description=$(awk '/^description:/ {gsub(/^description: *"?/, ""); gsub(/"$/, ""); print; exit}' "$template" | tr -d '\r')
|
||||
body=$(awk '/^---$/{if(++count==2) start=1; next} start' "$template" | sed "s/{ARGS}/$arg_format/g" | rewrite_paths)
|
||||
|
||||
# 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)
|
||||
sed "s/{ARGS}/$arg_format/g" "$template" | rewrite_paths > "$output_dir/$name.$ext" ;;
|
||||
echo "$body" > "$output_dir/$name.$ext" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Create Claude package
|
||||
echo "Building Claude package..."
|
||||
mkdir -p sdd-claude-package
|
||||
cp -r sdd-package-base/. sdd-claude-package/
|
||||
mkdir -p sdd-claude-package/.claude/commands
|
||||
generate_commands claude md "\$ARGUMENTS" sdd-claude-package/.claude/commands
|
||||
echo "Created Claude package"
|
||||
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")
|
||||
variant_line=$(printf '%s\n' "$plan_norm" | grep -E "<!--[[:space:]]*VARIANT:$script" | head -1 | sed -E "s/.*VARIANT:$script[[:space:]]+//; s/-->.*//; s/^[[:space:]]+//; s/[[:space:]]+$//")
|
||||
if [[ -n $variant_line ]]; then
|
||||
tmp_file=$(mktemp)
|
||||
sed "s|VARIANT-INJECT|${variant_line}|" "$plan_tpl" | tr -d '\r' | sed "s|__AGENT__|${agent}|g" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d' > "$tmp_file" && mv "$tmp_file" "$plan_tpl"
|
||||
else
|
||||
echo "Warning: no plan-template variant for $script (pattern not matched)" >&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" ;;
|
||||
esac
|
||||
( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
|
||||
echo "Created spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
|
||||
}
|
||||
|
||||
# Create Gemini package
|
||||
echo "Building Gemini package..."
|
||||
mkdir -p sdd-gemini-package
|
||||
cp -r sdd-package-base/. sdd-gemini-package/
|
||||
mkdir -p sdd-gemini-package/.gemini/commands
|
||||
generate_commands gemini toml "{{args}}" sdd-gemini-package/.gemini/commands
|
||||
[[ -f agent_templates/gemini/GEMINI.md ]] && cp agent_templates/gemini/GEMINI.md sdd-gemini-package/GEMINI.md
|
||||
echo "Created Gemini package"
|
||||
# Determine agent list
|
||||
ALL_AGENTS=(claude gemini copilot cursor qwen)
|
||||
ALL_SCRIPTS=(sh ps)
|
||||
|
||||
# Create Copilot package
|
||||
echo "Building Copilot package..."
|
||||
mkdir -p sdd-copilot-package
|
||||
cp -r sdd-package-base/. sdd-copilot-package/
|
||||
mkdir -p sdd-copilot-package/.github/prompts
|
||||
generate_commands copilot prompt.md "\$ARGUMENTS" sdd-copilot-package/.github/prompts
|
||||
echo "Created Copilot package"
|
||||
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")}'
|
||||
}
|
||||
|
||||
# Create Qwen package
|
||||
echo "Building Qwen package..."
|
||||
mkdir -p sdd-qwen-package
|
||||
cp -r sdd-package-base/. sdd-qwen-package/
|
||||
mkdir -p sdd-qwen-package/.qwen/commands
|
||||
generate_commands qwen md "\$ARGUMENTS" sdd-qwen-package/.qwen/commands
|
||||
[[ -f agent_templates/qwen/QWEN.md ]] && cp agent_templates/qwen/QWEN.md sdd-qwen-package/QWEN.md
|
||||
echo "Created Qwen package"
|
||||
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
|
||||
}
|
||||
|
||||
( cd sdd-claude-package && zip -r ../spec-kit-template-claude-${NEW_VERSION}.zip . )
|
||||
( cd sdd-gemini-package && zip -r ../spec-kit-template-gemini-${NEW_VERSION}.zip . )
|
||||
( cd sdd-copilot-package && zip -r ../spec-kit-template-copilot-${NEW_VERSION}.zip . )
|
||||
( cd sdd-qwen-package && zip -r ../spec-kit-template-qwen-${NEW_VERSION}.zip . )
|
||||
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
|
||||
unzip -l spec-kit-template-copilot-${NEW_VERSION}.zip | head -10 || true
|
||||
unzip -l spec-kit-template-qwen-${NEW_VERSION}.zip | head -10 || true
|
||||
|
||||
Reference in New Issue
Block a user