Merge main into feature/add-qwen-support
Resolved conflicts in release workflow and CLI: - Integrated external script approach for release package creation - Added Qwen Code support to release script and CLI tool checking - Maintained all existing functionality for other AI assistants
This commit is contained in:
191
.github/workflows/manual-release.yml
vendored
191
.github/workflows/manual-release.yml
vendored
@@ -1,191 +0,0 @@
|
||||
name: Manual Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_bump:
|
||||
description: 'Version bump type'
|
||||
required: true
|
||||
default: 'patch'
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
|
||||
jobs:
|
||||
manual_release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Calculate new version
|
||||
id: version
|
||||
run: |
|
||||
# Get the latest tag, or use v0.0.0 if no tags exist
|
||||
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
|
||||
echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
# Extract version number
|
||||
VERSION=$(echo $LATEST_TAG | sed 's/v//')
|
||||
IFS='.' read -ra VERSION_PARTS <<< "$VERSION"
|
||||
MAJOR=${VERSION_PARTS[0]:-0}
|
||||
MINOR=${VERSION_PARTS[1]:-0}
|
||||
PATCH=${VERSION_PARTS[2]:-0}
|
||||
|
||||
# Increment based on input
|
||||
case "${{ github.event.inputs.version_bump }}" in
|
||||
"major")
|
||||
MAJOR=$((MAJOR + 1))
|
||||
MINOR=0
|
||||
PATCH=0
|
||||
;;
|
||||
"minor")
|
||||
MINOR=$((MINOR + 1))
|
||||
PATCH=0
|
||||
;;
|
||||
"patch")
|
||||
PATCH=$((PATCH + 1))
|
||||
;;
|
||||
esac
|
||||
|
||||
NEW_VERSION="v$MAJOR.$MINOR.$PATCH"
|
||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "New version will be: $NEW_VERSION (was $LATEST_TAG)"
|
||||
|
||||
- name: Create release package
|
||||
run: |
|
||||
# Create base package directory structure
|
||||
mkdir -p sdd-package-base
|
||||
|
||||
# Copy common folders to base
|
||||
echo "Packaging SDD common components..."
|
||||
|
||||
if [ -d "memory" ]; then
|
||||
cp -r memory sdd-package-base/
|
||||
echo "✓ Copied memory folder ($(find memory -type f | wc -l) files)"
|
||||
else
|
||||
echo "⚠️ memory folder not found"
|
||||
fi
|
||||
|
||||
if [ -d "scripts" ]; then
|
||||
cp -r scripts sdd-package-base/
|
||||
echo "✓ Copied scripts folder ($(find scripts -type f | wc -l) files)"
|
||||
else
|
||||
echo "⚠️ scripts folder not found"
|
||||
fi
|
||||
|
||||
# Create Claude Code package
|
||||
echo "Creating Claude Code package..."
|
||||
mkdir -p sdd-claude-package
|
||||
cp -r sdd-package-base/* sdd-claude-package/
|
||||
if [ -d "agent_templates/claude" ]; then
|
||||
cp -r agent_templates/claude sdd-claude-package/.claude
|
||||
echo "✓ Added Claude Code commands ($(find agent_templates/claude -type f | wc -l) files)"
|
||||
else
|
||||
echo "⚠️ agent_templates/claude folder not found"
|
||||
fi
|
||||
|
||||
# Create Gemini CLI package
|
||||
echo "Creating Gemini CLI package..."
|
||||
mkdir -p sdd-gemini-package
|
||||
cp -r sdd-package-base/* sdd-gemini-package/
|
||||
if [ -d "agent_templates/gemini" ]; then
|
||||
cp -r agent_templates/gemini sdd-gemini-package/.gemini
|
||||
# Move GEMINI.md to root for easier access
|
||||
if [ -f "sdd-gemini-package/.gemini/GEMINI.md" ]; then
|
||||
mv sdd-gemini-package/.gemini/GEMINI.md sdd-gemini-package/GEMINI.md
|
||||
echo "✓ Moved GEMINI.md to root of Gemini package"
|
||||
fi
|
||||
# Remove empty .gemini folder if it only contained GEMINI.md
|
||||
if [ -d "sdd-gemini-package/.gemini" ] && [ -z "$(find sdd-gemini-package/.gemini -type f)" ]; then
|
||||
rm -rf sdd-gemini-package/.gemini
|
||||
echo "✓ Removed empty .gemini folder"
|
||||
fi
|
||||
echo "✓ Added Gemini CLI commands ($(find agent_templates/gemini -type f | wc -l) files)"
|
||||
else
|
||||
echo "⚠️ agent_templates/gemini folder not found"
|
||||
fi
|
||||
|
||||
# Create GitHub Copilot package
|
||||
echo "Creating GitHub Copilot package..."
|
||||
mkdir -p sdd-copilot-package
|
||||
cp -r sdd-package-base/* sdd-copilot-package/
|
||||
if [ -d "agent_templates/copilot" ]; then
|
||||
mkdir -p sdd-copilot-package/.github
|
||||
cp -r agent_templates/copilot/* sdd-copilot-package/.github/
|
||||
echo "✓ Added Copilot instructions to .github ($(find agent_templates/copilot -type f | wc -l) files)"
|
||||
else
|
||||
echo "⚠️ agent_templates/copilot folder not found"
|
||||
fi
|
||||
|
||||
# Create archive files for each package
|
||||
echo "Creating archive files..."
|
||||
cd sdd-claude-package && zip -r ../spec-kit-template-claude-${{ steps.version.outputs.new_version }}.zip . && cd ..
|
||||
|
||||
cd sdd-gemini-package && zip -r ../spec-kit-template-gemini-${{ steps.version.outputs.new_version }}.zip . && cd ..
|
||||
|
||||
cd sdd-copilot-package && zip -r ../spec-kit-template-copilot-${{ steps.version.outputs.new_version }}.zip . && cd ..
|
||||
|
||||
echo ""
|
||||
echo "📦 Packages created:"
|
||||
echo "Claude: $(ls -lh spec-kit-template-claude-*.zip | awk '{print $5}')"
|
||||
echo "Gemini: $(ls -lh spec-kit-template-gemini-*.zip | awk '{print $5}')"
|
||||
echo "Copilot: $(ls -lh spec-kit-template-copilot-*.zip | awk '{print $5}')"
|
||||
echo "Copilot: $(ls -lh sdd-template-copilot-*.zip | awk '{print $5}')"
|
||||
|
||||
- name: Generate detailed release notes
|
||||
run: |
|
||||
LAST_TAG=${{ steps.version.outputs.latest_tag }}
|
||||
|
||||
# Get commit range
|
||||
if [ "$LAST_TAG" = "v0.0.0" ]; then
|
||||
COMMIT_RANGE="HEAD~10..HEAD"
|
||||
COMMITS=$(git log --oneline --pretty=format:"- %s" $COMMIT_RANGE 2>/dev/null || echo "- Initial release")
|
||||
else
|
||||
COMMIT_RANGE="$LAST_TAG..HEAD"
|
||||
COMMITS=$(git log --oneline --pretty=format:"- %s" $COMMIT_RANGE 2>/dev/null || echo "- No changes since last release")
|
||||
fi
|
||||
|
||||
# Count files in each directory
|
||||
CLAUDE_COUNT=$(find agent_templates/claude -type f 2>/dev/null | wc -l || echo "0")
|
||||
GEMINI_COUNT=$(find agent_templates/gemini -type f 2>/dev/null | wc -l || echo "0")
|
||||
COPILOT_COUNT=$(find agent_templates/copilot -type f 2>/dev/null | wc -l || echo "0")
|
||||
MEMORY_COUNT=$(find memory -type f 2>/dev/null | wc -l || echo "0")
|
||||
SCRIPTS_COUNT=$(find scripts -type f 2>/dev/null | wc -l || echo "0")
|
||||
|
||||
cat > release_notes.md << EOF
|
||||
Template release ${{ steps.version.outputs.new_version }}
|
||||
|
||||
Updated specification-driven development templates for GitHub Copilot, Claude Code, and Gemini CLI.
|
||||
|
||||
Download the template for your preferred AI assistant:
|
||||
- spec-kit-template-copilot-${{ steps.version.outputs.new_version }}.zip
|
||||
- spec-kit-template-claude-${{ steps.version.outputs.new_version }}.zip
|
||||
- spec-kit-template-gemini-${{ steps.version.outputs.new_version }}.zip
|
||||
|
||||
Changes since $LAST_TAG:
|
||||
$COMMITS
|
||||
EOF
|
||||
|
||||
- name: Create GitHub Release
|
||||
run: |
|
||||
# Remove 'v' prefix from version for release title
|
||||
VERSION_NO_V=${{ steps.version.outputs.new_version }}
|
||||
VERSION_NO_V=${VERSION_NO_V#v}
|
||||
|
||||
gh release create ${{ steps.version.outputs.new_version }} \
|
||||
spec-kit-template-copilot-${{ steps.version.outputs.new_version }}.zip \
|
||||
spec-kit-template-claude-${{ steps.version.outputs.new_version }}.zip \
|
||||
spec-kit-template-gemini-${{ steps.version.outputs.new_version }}.zip \
|
||||
--title "Spec Kit Templates - $VERSION_NO_V" \
|
||||
--notes-file release_notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
116
.github/workflows/release.yml
vendored
116
.github/workflows/release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
- 'memory/**'
|
||||
- 'scripts/**'
|
||||
- 'templates/**'
|
||||
- '.github/workflows/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -61,119 +62,8 @@ jobs:
|
||||
- name: Create release package
|
||||
if: steps.check_release.outputs.exists == 'false'
|
||||
run: |
|
||||
# Create base package directory structure
|
||||
mkdir -p sdd-package-base
|
||||
|
||||
# Copy common folders to base
|
||||
if [ -d "memory" ]; then
|
||||
cp -r memory sdd-package-base/
|
||||
echo "Copied memory folder"
|
||||
fi
|
||||
|
||||
if [ -d "scripts" ]; then
|
||||
cp -r scripts sdd-package-base/
|
||||
echo "Copied scripts folder"
|
||||
fi
|
||||
|
||||
if [ -d "templates" ]; then
|
||||
mkdir -p sdd-package-base/templates
|
||||
# Copy templates folder but exclude the commands directory
|
||||
find templates -type f -not -path "templates/commands/*" -exec cp --parents {} sdd-package-base/ \;
|
||||
echo "Copied templates folder (excluding commands directory)"
|
||||
fi
|
||||
|
||||
# Generate command files for each agent from source templates
|
||||
generate_commands() {
|
||||
local agent=$1
|
||||
local ext=$2
|
||||
local arg_format=$3
|
||||
local output_dir=$4
|
||||
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
for template in templates/commands/*.md; do
|
||||
if [[ -f "$template" ]]; then
|
||||
name=$(basename "$template" .md)
|
||||
description=$(awk '/^description:/ {gsub(/^description: *"?/, ""); gsub(/"$/, ""); print; exit}' "$template" | tr -d '\r')
|
||||
content=$(awk '/^---$/{if(++count==2) start=1; next} start' "$template" | sed "s/{ARGS}/$arg_format/g")
|
||||
|
||||
case $ext in
|
||||
"toml")
|
||||
{
|
||||
echo "description = \"$description\""
|
||||
echo ""
|
||||
echo "prompt = \"\"\""
|
||||
echo "$content"
|
||||
echo "\"\"\""
|
||||
} > "$output_dir/$name.$ext"
|
||||
;;
|
||||
"md")
|
||||
echo "$content" > "$output_dir/$name.$ext"
|
||||
;;
|
||||
"prompt.md")
|
||||
{
|
||||
echo "# $(echo "$description" | sed 's/\. .*//')"
|
||||
echo ""
|
||||
echo "$content"
|
||||
} > "$output_dir/$name.$ext"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Create Claude Code 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 Code package"
|
||||
|
||||
# Create Gemini CLI 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"
|
||||
if [ -f "agent_templates/gemini/GEMINI.md" ]; then
|
||||
cp agent_templates/gemini/GEMINI.md sdd-gemini-package/GEMINI.md
|
||||
fi
|
||||
echo "Created Gemini CLI package"
|
||||
|
||||
# Create GitHub 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 GitHub Copilot package"
|
||||
|
||||
# Create Qwen CLI 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"
|
||||
if [ -f "agent_templates/qwen/QWEN.md" ]; then
|
||||
cp agent_templates/qwen/QWEN.md sdd-qwen-package/QWEN.md
|
||||
fi
|
||||
echo "Created Qwen CLI package"
|
||||
|
||||
# Create archive files for each package
|
||||
cd sdd-claude-package && zip -r ../spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip . && cd ..
|
||||
|
||||
cd sdd-gemini-package && zip -r ../spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip . && cd ..
|
||||
|
||||
cd sdd-copilot-package && zip -r ../spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip . && cd ..
|
||||
|
||||
cd sdd-qwen-package && zip -r ../spec-kit-template-qwen-${{ steps.get_tag.outputs.new_version }}.zip . && cd ..
|
||||
|
||||
# List contents for verification
|
||||
echo "Claude package contents:"
|
||||
unzip -l spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip | head -10
|
||||
echo "Gemini package contents:"
|
||||
unzip -l spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip | head -10
|
||||
echo "Copilot package contents:"
|
||||
unzip -l spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip | head -10
|
||||
echo "Qwen package contents:"
|
||||
unzip -l spec-kit-template-qwen-${{ steps.get_tag.outputs.new_version }}.zip | head -10
|
||||
chmod +x .github/workflows/scripts/create-release-packages.sh
|
||||
.github/workflows/scripts/create-release-packages.sh ${{ steps.get_tag.outputs.new_version }}
|
||||
|
||||
- name: Generate release notes
|
||||
if: steps.check_release.outputs.exists == 'false'
|
||||
|
||||
104
.github/workflows/scripts/create-release-packages.sh
vendored
Normal file
104
.github/workflows/scripts/create-release-packages.sh
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# create-release-packages.sh (workflow-local)
|
||||
# Build Spec Kit template release archives for each supported AI assistant.
|
||||
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
|
||||
# Version argument should include leading 'v'.
|
||||
|
||||
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-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"; }
|
||||
|
||||
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
|
||||
mkdir -p "$output_dir"
|
||||
for template in templates/commands/*.md; do
|
||||
[[ -f "$template" ]] || continue
|
||||
local name description 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)
|
||||
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" ;;
|
||||
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"
|
||||
|
||||
# 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"
|
||||
|
||||
# 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"
|
||||
|
||||
# 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"
|
||||
|
||||
( 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 . )
|
||||
|
||||
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