fix(ci): add .unitypackage artifact to releases, clean up .gitignore #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | ||
| 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 | ||