Refactor with platform-specific constraints
This commit is contained in:
215
.github/workflows/release.yml
vendored
215
.github/workflows/release.yml
vendored
@@ -13,118 +13,115 @@ on:
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get latest tag
|
||||
id: get_tag
|
||||
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 and increment
|
||||
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 patch version
|
||||
PATCH=$((PATCH + 1))
|
||||
NEW_VERSION="v$MAJOR.$MINOR.$PATCH"
|
||||
|
||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "New version will be: $NEW_VERSION"
|
||||
|
||||
- name: Check if release already exists
|
||||
id: check_release
|
||||
run: |
|
||||
if gh release view ${{ steps.get_tag.outputs.new_version }} >/dev/null 2>&1; then
|
||||
echo "exists=true" >> $GITHUB_OUTPUT
|
||||
echo "Release ${{ steps.get_tag.outputs.new_version }} already exists, skipping..."
|
||||
else
|
||||
echo "exists=false" >> $GITHUB_OUTPUT
|
||||
echo "Release ${{ steps.get_tag.outputs.new_version }} does not exist, proceeding..."
|
||||
fi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create release package
|
||||
if: steps.check_release.outputs.exists == 'false'
|
||||
run: |
|
||||
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'
|
||||
id: release_notes
|
||||
run: |
|
||||
# Get commits since last tag
|
||||
LAST_TAG=${{ steps.get_tag.outputs.latest_tag }}
|
||||
if [ "$LAST_TAG" = "v0.0.0" ]; then
|
||||
# Check how many commits we have and use that as the limit
|
||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||
if [ "$COMMIT_COUNT" -gt 10 ]; then
|
||||
COMMITS=$(git log --oneline --pretty=format:"- %s" HEAD~10..HEAD)
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Get latest tag
|
||||
id: get_tag
|
||||
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 and increment
|
||||
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 patch version
|
||||
PATCH=$((PATCH + 1))
|
||||
NEW_VERSION="v$MAJOR.$MINOR.$PATCH"
|
||||
|
||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "New version will be: $NEW_VERSION"
|
||||
- name: Check if release already exists
|
||||
id: check_release
|
||||
run: |
|
||||
if gh release view ${{ steps.get_tag.outputs.new_version }} >/dev/null 2>&1; then
|
||||
echo "exists=true" >> $GITHUB_OUTPUT
|
||||
echo "Release ${{ steps.get_tag.outputs.new_version }} already exists, skipping..."
|
||||
else
|
||||
COMMITS=$(git log --oneline --pretty=format:"- %s" HEAD~$COMMIT_COUNT..HEAD 2>/dev/null || git log --oneline --pretty=format:"- %s")
|
||||
echo "exists=false" >> $GITHUB_OUTPUT
|
||||
echo "Release ${{ steps.get_tag.outputs.new_version }} does not exist, proceeding..."
|
||||
fi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Create release package variants
|
||||
if: steps.check_release.outputs.exists == 'false'
|
||||
run: |
|
||||
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'
|
||||
id: release_notes
|
||||
run: |
|
||||
# Get commits since last tag
|
||||
LAST_TAG=${{ steps.get_tag.outputs.latest_tag }}
|
||||
if [ "$LAST_TAG" = "v0.0.0" ]; then
|
||||
# Check how many commits we have and use that as the limit
|
||||
COMMIT_COUNT=$(git rev-list --count HEAD)
|
||||
if [ "$COMMIT_COUNT" -gt 10 ]; then
|
||||
COMMITS=$(git log --oneline --pretty=format:"- %s" HEAD~10..HEAD)
|
||||
else
|
||||
COMMITS=$(git log --oneline --pretty=format:"- %s" HEAD~$COMMIT_COUNT..HEAD 2>/dev/null || git log --oneline --pretty=format:"- %s")
|
||||
fi
|
||||
else
|
||||
COMMITS=$(git log --oneline --pretty=format:"- %s" $LAST_TAG..HEAD)
|
||||
fi
|
||||
|
||||
# Create release notes
|
||||
cat > release_notes.md << EOF
|
||||
Template release ${{ steps.get_tag.outputs.new_version }}
|
||||
|
||||
Updated specification-driven development templates for GitHub Copilot, Claude Code, and Gemini CLI.
|
||||
|
||||
Now includes per-script variants for POSIX shell (sh) and PowerShell (ps).
|
||||
|
||||
Download the template for your preferred AI assistant + script type:
|
||||
- spec-kit-template-copilot-sh-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-copilot-ps-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-claude-sh-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-claude-ps-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-gemini-sh-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-gemini-ps-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
EOF
|
||||
|
||||
echo "Generated release notes:"
|
||||
cat release_notes.md
|
||||
- name: Create GitHub Release
|
||||
if: steps.check_release.outputs.exists == 'false'
|
||||
run: |
|
||||
# Remove 'v' prefix from version for release title
|
||||
VERSION_NO_V=${{ steps.get_tag.outputs.new_version }}
|
||||
VERSION_NO_V=${VERSION_NO_V#v}
|
||||
|
||||
gh release create ${{ steps.get_tag.outputs.new_version }} \
|
||||
spec-kit-template-copilot-sh-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-copilot-ps-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-claude-sh-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-claude-ps-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-gemini-sh-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-gemini-ps-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
--title "Spec Kit Templates - $VERSION_NO_V" \
|
||||
--notes-file release_notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Update version in pyproject.toml (for release artifacts only)
|
||||
if: steps.check_release.outputs.exists == 'false'
|
||||
run: |
|
||||
# Update version in pyproject.toml (remove 'v' prefix for Python versioning)
|
||||
VERSION=${{ steps.get_tag.outputs.new_version }}
|
||||
PYTHON_VERSION=${VERSION#v}
|
||||
|
||||
if [ -f "pyproject.toml" ]; then
|
||||
sed -i "s/version = \".*\"/version = \"$PYTHON_VERSION\"/" pyproject.toml
|
||||
echo "Updated pyproject.toml version to $PYTHON_VERSION (for release artifacts only)"
|
||||
fi
|
||||
else
|
||||
COMMITS=$(git log --oneline --pretty=format:"- %s" $LAST_TAG..HEAD)
|
||||
fi
|
||||
|
||||
# Create release notes
|
||||
cat > release_notes.md << EOF
|
||||
Template release ${{ steps.get_tag.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.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
EOF
|
||||
|
||||
echo "Generated release notes:"
|
||||
cat release_notes.md
|
||||
|
||||
- name: Create GitHub Release
|
||||
if: steps.check_release.outputs.exists == 'false'
|
||||
run: |
|
||||
# Remove 'v' prefix from version for release title
|
||||
VERSION_NO_V=${{ steps.get_tag.outputs.new_version }}
|
||||
VERSION_NO_V=${VERSION_NO_V#v}
|
||||
|
||||
gh release create ${{ steps.get_tag.outputs.new_version }} \
|
||||
spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
--title "Spec Kit Templates - $VERSION_NO_V" \
|
||||
--notes-file release_notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update version in pyproject.toml (for release artifacts only)
|
||||
if: steps.check_release.outputs.exists == 'false'
|
||||
run: |
|
||||
# Update version in pyproject.toml (remove 'v' prefix for Python versioning)
|
||||
VERSION=${{ steps.get_tag.outputs.new_version }}
|
||||
PYTHON_VERSION=${VERSION#v}
|
||||
|
||||
if [ -f "pyproject.toml" ]; then
|
||||
sed -i "s/version = \".*\"/version = \"$PYTHON_VERSION\"/" pyproject.toml
|
||||
echo "Updated pyproject.toml version to $PYTHON_VERSION (for release artifacts only)"
|
||||
fi
|
||||
|
||||
# Note: No longer committing version changes back to main branch
|
||||
# The version is only updated in the release artifacts
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
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'.
|
||||
|
||||
@@ -18,10 +18,7 @@ fi
|
||||
|
||||
echo "Building release packages for $NEW_VERSION"
|
||||
|
||||
rm -rf sdd-package-base sdd-claude-package sdd-gemini-package sdd-copilot-package \
|
||||
spec-kit-template-claude-${NEW_VERSION}.zip \
|
||||
spec-kit-template-gemini-${NEW_VERSION}.zip \
|
||||
spec-kit-template-copilot-${NEW_VERSION}.zip || true
|
||||
rm -rf sdd-package-base* sdd-*-package-* spec-kit-template-*-${NEW_VERSION}.zip || true
|
||||
|
||||
mkdir -p sdd-package-base
|
||||
SPEC_DIR="sdd-package-base/.specify"
|
||||
@@ -29,7 +26,7 @@ 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"; }
|
||||
[[ -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 \
|
||||
@@ -39,54 +36,76 @@ 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 raw_body variant_line injected 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)
|
||||
raw_body=$(awk '/^---$/{if(++count==2) start=1; next} start' "$template")
|
||||
# Find single-line variant comment matching the variant: <!-- VARIANT:sh ... --> or <!-- VARIANT:ps ... -->
|
||||
variant_line=$(printf '%s\n' "$raw_body" | awk -v sv="$script_variant" '/<!--[[:space:]]+VARIANT:'sv'/ {match($0, /VARIANT:'"sv"'[[:space:]]+(.*)-->/, m); if (m[1]!="") {print m[1]; exit}}')
|
||||
if [[ -z $variant_line ]]; then
|
||||
echo "Warning: no variant line found for $script_variant in $template" >&2
|
||||
variant_line="(Missing variant command for $script_variant)"
|
||||
fi
|
||||
# Replace the token VARIANT-INJECT with the selected variant line
|
||||
injected=$(printf '%s\n' "$raw_body" | sed "s/VARIANT-INJECT/${variant_line//\//\/}/")
|
||||
# Remove all single-line variant comments
|
||||
injected=$(printf '%s\n' "$injected" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d')
|
||||
# Apply arg substitution and path rewrite
|
||||
body=$(printf '%s\n' "$injected" | 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"
|
||||
cp -r sdd-package-base/. "$base_dir"/
|
||||
# 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
|
||||
variant_line=$(awk -v sv="$script" '/<!--[[:space:]]*VARIANT:'"$script"'/ {match($0, /VARIANT:'"$script"'[[:space:]]+(.*)-->/, m); if(m[1]!=""){print m[1]; exit}}' "$plan_tpl")
|
||||
if [[ -n $variant_line ]]; then
|
||||
tmp_file=$(mktemp)
|
||||
sed "s/VARIANT-INJECT/${variant_line//\//\/}/" "$plan_tpl" | sed "/__AGENT__/s//${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" >&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" ;;
|
||||
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"
|
||||
|
||||
# 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"
|
||||
|
||||
( 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 . )
|
||||
# Build for each agent+script variant
|
||||
for agent in claude gemini copilot; do
|
||||
for script in sh ps; 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
|
||||
|
||||
Reference in New Issue
Block a user