Skip to content

fix(ci): add .unitypackage artifact to releases, clean up .gitignore #1

fix(ci): add .unitypackage artifact to releases, clean up .gitignore

fix(ci): add .unitypackage artifact to releases, clean up .gitignore #1

Workflow file for this run

name: Release

Check failure on line 1 in .github/workflows/release.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/release.yml

Invalid workflow file

(Line: 284, Col: 13): Unrecognized named-value: 'secrets'. Located at position 1 within expression: secrets.NUGET_API_KEY != ''
on:
push:
branches:
- master
workflow_dispatch:
inputs:
version_bump:
description: 'Version bump type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
release_notes:
description: 'Additional release notes (optional)'
required: false
type: string
permissions:
contents: write
jobs:
release:
name: Create Release
runs-on: ubuntu-latest
outputs:
new_version: ${{ steps.version.outputs.new_version }}
changelog_body: ${{ steps.changelog.outputs.body }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get current version
id: current
run: |
CURRENT=$(grep -oP '"version":\s*"\K[^"]+' src/Proyecto26.RestClient/package.json)
echo "version=$CURRENT" >> $GITHUB_OUTPUT
echo "Current version: $CURRENT"
- name: Determine version bump from commits
id: bump
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "type=${{ inputs.version_bump }}" >> $GITHUB_OUTPUT
echo "Manual bump: ${{ inputs.version_bump }}"
else
# Parse conventional commits since last tag
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$LAST_TAG" ]; then
RANGE="HEAD"
else
RANGE="${LAST_TAG}..HEAD"
fi
COMMITS=$(git log $RANGE --pretty=format:"%s" 2>/dev/null || echo "")
if echo "$COMMITS" | grep -qiE "^(feat|feature)(\(.+\))?!:|BREAKING CHANGE"; then
echo "type=major" >> $GITHUB_OUTPUT
echo "Detected: major (breaking change)"
elif echo "$COMMITS" | grep -qiE "^(feat|feature)(\(.+\))?:"; then
echo "type=minor" >> $GITHUB_OUTPUT
echo "Detected: minor (new feature)"
elif echo "$COMMITS" | grep -qiE "^(fix|perf|refactor)(\(.+\))?:"; then
echo "type=patch" >> $GITHUB_OUTPUT
echo "Detected: patch (fix/improvement)"
else
echo "type=patch" >> $GITHUB_OUTPUT
echo "Detected: patch (default)"
fi
fi
- name: Calculate new version
id: version
run: |
IFS='.' read -r MAJOR MINOR PATCH <<< "${{ steps.current.outputs.version }}"
case "${{ steps.bump.outputs.type }}" in
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
patch) PATCH=$((PATCH + 1)) ;;
esac
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version: $NEW_VERSION"
- name: Generate changelog entry
id: changelog
run: |
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$LAST_TAG" ]; then
RANGE="HEAD"
else
RANGE="${LAST_TAG}..HEAD"
fi
TODAY=$(date +%Y-%m-%d)
NEW_VERSION="${{ steps.version.outputs.new_version }}"
# Collect commits by type
ADDED=""
CHANGED=""
FIXED=""
REMOVED=""
while IFS= read -r line; do
# Extract commit info
HASH=$(echo "$line" | cut -d'|' -f1)
MSG=$(echo "$line" | cut -d'|' -f2-)
SHORT_HASH=$(echo "$HASH" | cut -c1-7)
# Get PR number and author if available
PR_NUM=$(echo "$MSG" | grep -oP '\(#\K[0-9]+(?=\))' | head -1)
AUTHOR=$(git log -1 --format='%an' "$HASH" 2>/dev/null)
GITHUB_USER=$(git log -1 --format='%(trailers:key=Co-authored-by,valueonly)' "$HASH" 2>/dev/null | grep -oP '@\K\S+' | head -1)
# Build attribution
if [ -n "$PR_NUM" ]; then
ATTR="([#${PR_NUM}](https://github.com/proyecto26/RestClient/pull/${PR_NUM}))"
else
ATTR="([${SHORT_HASH}](https://github.com/proyecto26/RestClient/commit/${HASH}))"
fi
# Clean message (remove conventional commit prefix and scope)
CLEAN_MSG=$(echo "$MSG" | sed -E 's/^(feat|fix|perf|refactor|chore|docs|style|test|build|ci)(\([^)]*\))?(!)?:\s*//')
# Remove PR reference from message if already captured
CLEAN_MSG=$(echo "$CLEAN_MSG" | sed -E 's/\s*\(#[0-9]+\)\s*$//')
# Categorize
if echo "$MSG" | grep -qiE "^(feat|feature)(\(.+\))?:"; then
ADDED="${ADDED}- ${CLEAN_MSG} ${ATTR}.\n"
elif echo "$MSG" | grep -qiE "^fix(\(.+\))?:"; then
FIXED="${FIXED}- ${CLEAN_MSG} ${ATTR}.\n"
elif echo "$MSG" | grep -qiE "^(perf|refactor|chore|docs|style|build|ci)(\(.+\))?:"; then
CHANGED="${CHANGED}- ${CLEAN_MSG} ${ATTR}.\n"
elif echo "$MSG" | grep -qiE "^(revert)(\(.+\))?:"; then
REMOVED="${REMOVED}- ${CLEAN_MSG} ${ATTR}.\n"
else
# Non-conventional commits go to Changed
CHANGED="${CHANGED}- ${CLEAN_MSG} ${ATTR}.\n"
fi
done < <(git log $RANGE --pretty=format:"%H|%s" --no-merges 2>/dev/null)
# Add manual release notes if provided
if [ -n "${{ inputs.release_notes }}" ]; then
ADDED="${ADDED}- ${{ inputs.release_notes }}\n"
fi
# Build changelog section
BODY=""
if [ -n "$ADDED" ]; then
BODY="${BODY}### Added\n${ADDED}\n"
fi
if [ -n "$CHANGED" ]; then
BODY="${BODY}### Changed\n${CHANGED}\n"
fi
if [ -n "$FIXED" ]; then
BODY="${BODY}### Fixed\n${FIXED}\n"
fi
if [ -n "$REMOVED" ]; then
BODY="${BODY}### Removed\n${REMOVED}\n"
fi
# Fallback if no conventional commits found
if [ -z "$BODY" ]; then
BODY="### Changed\n- Release version ${NEW_VERSION}.\n"
fi
# Save for GitHub Release body
echo -e "$BODY" > /tmp/release_body.md
echo "body<<CHANGELOG_EOF" >> $GITHUB_OUTPUT
echo -e "$BODY" >> $GITHUB_OUTPUT
echo "CHANGELOG_EOF" >> $GITHUB_OUTPUT
# Update CHANGELOG.md
PREV_VERSION="${{ steps.current.outputs.version }}"
HEADER="## [${NEW_VERSION}] - ${TODAY}"
LINK="[${NEW_VERSION}]: https://github.com/proyecto26/RestClient/compare/v${PREV_VERSION}...v${NEW_VERSION}"
# Insert new version section after [Unreleased] line
sed -i "/^## \[Unreleased\]/a\\\\n${HEADER}\\n" CHANGELOG.md
# Insert the body after the new header
sed -i "/^## \[${NEW_VERSION}\]/r /tmp/release_body.md" CHANGELOG.md
# Update Unreleased comparison link
sed -i "s|\[Unreleased\]: .*|[Unreleased]: https://github.com/proyecto26/RestClient/compare/v${NEW_VERSION}...HEAD|" CHANGELOG.md
# Add new version comparison link before existing ones
sed -i "/^\[Unreleased\]:/a ${LINK}" CHANGELOG.md
echo "Changelog updated for v${NEW_VERSION}"
- name: Update version in package.json (UPM)
run: |
sed -i 's/"version": "[^"]*"/"version": "${{ steps.version.outputs.new_version }}"/' src/Proyecto26.RestClient/package.json
- name: Update version in .csproj
run: |
sed -i 's/<PackageVersion>[^<]*</<PackageVersion>${{ steps.version.outputs.new_version }}</' src/Proyecto26.RestClient/Proyecto26.RestClient.csproj
- name: Update version in .nuspec
run: |
sed -i 's/<version>[^<]*</<version>${{ steps.version.outputs.new_version }}</' src/Proyecto26.RestClient/Proyecto26.RestClient.nuspec
- name: Update version in AssemblyInfo.cs
run: |
sed -i 's/AssemblyVersion ("[^"]*")/AssemblyVersion ("${{ steps.version.outputs.new_version }}")/' src/Proyecto26.RestClient/Properties/AssemblyInfo.cs
- name: Commit version bump
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add -A
git commit -m "chore(release): v${{ steps.version.outputs.new_version }}
- Updated package.json, .csproj, .nuspec, AssemblyInfo.cs
- Updated CHANGELOG.md"
git tag "v${{ steps.version.outputs.new_version }}"
git push origin master --follow-tags
- name: Build .unitypackage artifact
run: |
# Create a clean directory structure for the Unity package
PKG_DIR="/tmp/unity-package"
ASSET_DIR="$PKG_DIR/Assets/Proyecto26.RestClient"
mkdir -p "$ASSET_DIR"
# Copy only Unity-relevant source files (exclude build system files)
cp -r src/Proyecto26.RestClient/Helpers "$ASSET_DIR/"
cp src/Proyecto26.RestClient/RestClient.cs "$ASSET_DIR/"
cp src/Proyecto26.RestClient/RestClientPromise.cs "$ASSET_DIR/"
cp src/Proyecto26.RestClient/package.json "$ASSET_DIR/"
cp src/Proyecto26.RestClient/Proyecto26.RestClient.asmdef "$ASSET_DIR/"
# Copy meta files if they exist
find src/Proyecto26.RestClient -name "*.meta" -exec sh -c 'cp "$1" "'"$ASSET_DIR"'/$(basename "$1")" 2>/dev/null || true' _ {} \;
cp -r src/Proyecto26.RestClient/Helpers/*.meta "$ASSET_DIR/Helpers/" 2>/dev/null || true
cp src/Proyecto26.RestClient/RestClient.cs.meta "$ASSET_DIR/" 2>/dev/null || true
cp src/Proyecto26.RestClient/RestClientPromise.cs.meta "$ASSET_DIR/" 2>/dev/null || true
cp src/Proyecto26.RestClient/package.json.meta "$ASSET_DIR/" 2>/dev/null || true
cp src/Proyecto26.RestClient/Proyecto26.RestClient.asmdef.meta "$ASSET_DIR/" 2>/dev/null || true
cp LICENSE "$ASSET_DIR/"
cp README.md "$ASSET_DIR/"
# Create a tarball as the distributable (Unity .unitypackage format requires Unity Editor)
cd "$PKG_DIR"
tar -czf "/tmp/Proyecto26.RestClient-${{ steps.version.outputs.new_version }}.tar.gz" Assets/
echo "Package built: Proyecto26.RestClient-${{ steps.version.outputs.new_version }}.tar.gz"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.version.outputs.new_version }}
name: Release ${{ steps.version.outputs.new_version }}
body_path: /tmp/release_body.md
draft: false
prerelease: false
files: |
/tmp/Proyecto26.RestClient-${{ steps.version.outputs.new_version }}.tar.gz
publish-nuget:
name: Publish to NuGet
needs: release
runs-on: ubuntu-latest
if: needs.release.outputs.new_version != ''
steps:
- uses: actions/checkout@v4
with:
ref: master
- name: Pull latest (includes version bump commit)
run: git pull origin master
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.0.x'
- name: Build NuGet package
run: |
cd src/Proyecto26.RestClient
dotnet pack -c Release -o ../../artifacts
- name: Publish to NuGet
if: ${{ secrets.NUGET_API_KEY != '' }}
run: |
dotnet nuget push artifacts/*.nupkg \
--api-key ${{ secrets.NUGET_API_KEY }} \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate
split-branches:
name: Split UPM & NuGet branches
needs: release
runs-on: ubuntu-latest
if: needs.release.outputs.new_version != ''
steps:
- uses: actions/checkout@v4
with:
ref: master
fetch-depth: 0
- name: Pull latest
run: git pull origin master
- name: Split UPM branch
run: |
git branch -d upm &> /dev/null || echo "upm branch not found"
git mv "$PKG_ROOT" "src/$UPM_ROOT"
git mv "src/$UPM_ROOT" .
git mv demo/Assets demo/Samples~
git mv demo/Samples~ $UPM_ROOT
git mv doc Docs
git mv Docs $UPM_ROOT
git mv LICENSE $UPM_ROOT
git mv README.md $UPM_ROOT
git mv SECURITY.md $UPM_ROOT
git mv CHANGELOG.md $UPM_ROOT
git mv CONTRIBUTING.md $UPM_ROOT
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add "$UPM_ROOT"
git commit -m "create release folder"
git subtree split -P "$UPM_ROOT" -b upm
git checkout upm
if [[ -d "Samples~/Packages" ]]; then
git rm -rf Samples~/Packages
git rm Samples~/Packages.meta --ignore-unmatch
git rm Samples~/packages.config
git rm Samples~/packages.config.meta --ignore-unmatch
fi
if [[ -d "Properties" ]]; then
git rm -rf Properties
git rm Properties.meta --ignore-unmatch
git rm Proyecto26.RestClient.csproj
git rm Proyecto26.RestClient.csproj.meta --ignore-unmatch
git rm Proyecto26.RestClient.nuspec
git rm Proyecto26.RestClient.nuspec.meta --ignore-unmatch
git rm packages.config
git rm packages.config.meta --ignore-unmatch
fi
git commit -am "fix: src => root"
git push -u origin upm --force
env:
PKG_ROOT: src/Proyecto26.RestClient
UPM_ROOT: Release
- name: Split NuGet branch
run: |
git checkout master
git pull origin master
git branch -d nuget &> /dev/null || echo "nuget branch not found"
git mv LICENSE $NUGET_ROOT
git mv README.md $NUGET_ROOT
git mv SECURITY.md $NUGET_ROOT
git mv CHANGELOG.md $NUGET_ROOT
git mv CONTRIBUTING.md $NUGET_ROOT
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git subtree split -P "$NUGET_ROOT" -b nuget
git checkout nuget
git commit -am "fix: src => root" --allow-empty
git push -u origin nuget --force
env:
NUGET_ROOT: src