fix: preserve content when converting note types #103
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: Create Release | |
| on: | |
| push: | |
| branches: ['main'] | |
| tags: | |
| - 'v*.*.*' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| # ────────────────────────────────────────────────────────────── | |
| # Job 1: "latest" release — updated on every push to main | |
| # Always overwrites the same "latest" tag and release | |
| # ────────────────────────────────────────────────────────────── | |
| latest: | |
| if: github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| environment: github-pages | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build | |
| run: npm run build | |
| env: | |
| VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }} | |
| VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }} | |
| - name: Create ZIP archive | |
| run: | | |
| cd dist | |
| zip -r ../quicknotes-latest.zip . | |
| - name: Generate latest release notes | |
| id: notes | |
| run: | | |
| RELEASE_DATE=$(date -u +"%d-%m-%Y at %I:%M %p UTC") | |
| COMMIT_SHA=$(git rev-parse --short HEAD) | |
| COMMIT_MSG=$(git log -1 --format="%s") | |
| # Get the last versioned tag for commit range | |
| LATEST_TAG=$(git tag -l 'v*.*.*' --sort=-v:refname | head -n 1) | |
| if [ -n "$LATEST_TAG" ]; then | |
| RANGE="${LATEST_TAG}..HEAD" | |
| COMMIT_COUNT=$(git rev-list --count "$RANGE" 2>/dev/null || echo "0") | |
| SINCE_TEXT="**${COMMIT_COUNT} commit(s)** since last versioned release \`${LATEST_TAG}\`" | |
| else | |
| COMMIT_COUNT=$(git rev-list --count HEAD 2>/dev/null || echo "0") | |
| SINCE_TEXT="**${COMMIT_COUNT} commit(s)** total" | |
| fi | |
| # Collect recent changes (last 15 commits or since last tag) | |
| ITEMS_FILE=$(mktemp) | |
| if [ -n "$RANGE" ]; then | |
| HASHES=$(git log "$RANGE" --no-merges --format="%H" 2>/dev/null) | |
| else | |
| HASHES=$(git log --no-merges -15 --format="%H" 2>/dev/null) | |
| fi | |
| while read -r HASH; do | |
| [ -z "$HASH" ] && continue | |
| SUBJECT=$(git log -1 "$HASH" --format="%s") | |
| BODY=$(git log -1 "$HASH" --format="%b") | |
| case "$SUBJECT" in | |
| feat:*|feat\(*|add:*|add\(*|new:*|new\(*) | |
| CAT="FEAT" ;; | |
| fix:*|fix\(*|bug:*|bug\(*|patch:*|patch\(*|hotfix:*|hotfix\(*) | |
| CAT="FIX" ;; | |
| ci:*|ci\(*|build:*|build\(*|chore:*|chore\(*|refactor:*|refactor\(*|style:*|style\(*|docs:*|docs\(*) | |
| CAT="MAINT" ;; | |
| *) | |
| CAT="OTHER" ;; | |
| esac | |
| BULLETS=$(echo "$BODY" | grep -E '^\s*[-*•]\s+' | sed 's/^[[:space:]]*[-*•][[:space:]]*//' || true) | |
| if [ -n "$BULLETS" ]; then | |
| while IFS= read -r item; do | |
| [ -n "$item" ] && echo "${CAT}|${item}" >> "$ITEMS_FILE" | |
| done <<< "$BULLETS" | |
| else | |
| CLEAN=$(echo "$SUBJECT" | sed -E 's/^(feat|add|new|fix|bug|patch|hotfix|ci|build|chore|refactor|style|docs)(\([^)]*\))?[: ]*//') | |
| [ -n "$CLEAN" ] && echo "${CAT}|${CLEAN}" >> "$ITEMS_FILE" | |
| fi | |
| done <<< "$HASHES" | |
| FEATURES="" | |
| FIXES="" | |
| MAINTENANCE="" | |
| OTHER="" | |
| if [ -f "$ITEMS_FILE" ]; then | |
| while IFS='|' read -r category text; do | |
| [ -z "$text" ] && continue | |
| text="$(echo "${text:0:1}" | tr '[:lower:]' '[:upper:]')${text:1}" | |
| case "$category" in | |
| FEAT) FEATURES="${FEATURES}\n• \u2800${text}" ;; | |
| FIX) FIXES="${FIXES}\n• \u2800${text}" ;; | |
| MAINT) MAINTENANCE="${MAINTENANCE}\n• \u2800${text}" ;; | |
| OTHER) OTHER="${OTHER}\n• \u2800${text}" ;; | |
| esac | |
| done < "$ITEMS_FILE" | |
| fi | |
| rm -f "$ITEMS_FILE" | |
| { | |
| echo "body<<EOFMARKER" | |
| echo "> **⚠️ This is a rolling release that always contains the latest build from \`main\`.**" | |
| echo "> It is automatically updated on every push. For stable versions, use a [versioned release](https://github.com/BerndHagen/QuickNotes-Simple-Note-Manager/releases)." | |
| echo "" | |
| echo "**Last updated:** ${RELEASE_DATE}" | |
| echo "**Commit:** \`${COMMIT_SHA}\` — ${COMMIT_MSG}" | |
| echo "**Changes:** ${SINCE_TEXT}" | |
| echo "" | |
| if [ -n "$FEATURES" ]; then | |
| echo "**New Features & Improvements**" | |
| echo -e "$FEATURES" | |
| echo "" | |
| fi | |
| if [ -n "$FIXES" ]; then | |
| echo "**Bug Fixes**" | |
| echo -e "$FIXES" | |
| echo "" | |
| fi | |
| if [ -n "$MAINTENANCE" ]; then | |
| echo "**Maintenance**" | |
| echo -e "$MAINTENANCE" | |
| echo "" | |
| fi | |
| if [ -n "$OTHER" ]; then | |
| echo "**Other Changes**" | |
| echo -e "$OTHER" | |
| echo "" | |
| fi | |
| echo "**Note:** If you encounter any bugs or issues, please don't hesitate to open an [issue](https://github.com/BerndHagen/QuickNotes-Simple-Note-Manager/issues). For any questions or to start a discussion, feel free to initiate a [discussion](https://github.com/BerndHagen/QuickNotes-Simple-Note-Manager/discussions) on the GitHub repository." | |
| echo "EOFMARKER" | |
| } >> $GITHUB_OUTPUT | |
| - name: Delete existing latest release | |
| run: gh release delete latest --yes --cleanup-tag 2>/dev/null || true | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create latest release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: latest | |
| name: "Latest Build" | |
| body: ${{ steps.notes.outputs.body }} | |
| prerelease: true | |
| files: quicknotes-latest.zip | |
| # ────────────────────────────────────────────────────────────── | |
| # Job 2: Versioned release — only when a tag like v1.0.0 is pushed | |
| # These releases are permanent and represent stable milestones | |
| # ────────────────────────────────────────────────────────────── | |
| versioned: | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build | |
| run: npm run build | |
| env: | |
| VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }} | |
| VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }} | |
| - name: Extract version | |
| id: version | |
| run: | | |
| TAG="${GITHUB_REF#refs/tags/}" | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| echo "Version tag: $TAG" | |
| - name: Create ZIP archive | |
| run: | | |
| cd dist | |
| zip -r ../quicknotes-${{ steps.version.outputs.tag }}.zip . | |
| - name: Generate versioned release notes | |
| id: notes | |
| run: | | |
| RELEASE_DATE=$(date -u +"%d-%m-%Y at %I:%M %p UTC") | |
| CURRENT_TAG="${GITHUB_REF#refs/tags/}" | |
| # Find previous versioned tag (exclude "latest" and current tag) | |
| PREV_TAG=$(git tag -l 'v*.*.*' --sort=-v:refname | grep -v "^${CURRENT_TAG}$" | head -n 1) | |
| if [ -n "$PREV_TAG" ]; then | |
| RANGE="${PREV_TAG}..${CURRENT_TAG}" | |
| else | |
| RANGE="" | |
| fi | |
| ITEMS_FILE=$(mktemp) | |
| if [ -n "$RANGE" ]; then | |
| HASHES=$(git log "$RANGE" --no-merges --format="%H" 2>/dev/null) | |
| else | |
| HASHES=$(git log --no-merges -20 --format="%H" 2>/dev/null) | |
| fi | |
| while read -r HASH; do | |
| [ -z "$HASH" ] && continue | |
| SUBJECT=$(git log -1 "$HASH" --format="%s") | |
| BODY=$(git log -1 "$HASH" --format="%b") | |
| case "$SUBJECT" in | |
| feat:*|feat\(*|add:*|add\(*|new:*|new\(*) | |
| CAT="FEAT" ;; | |
| fix:*|fix\(*|bug:*|bug\(*|patch:*|patch\(*|hotfix:*|hotfix\(*) | |
| CAT="FIX" ;; | |
| ci:*|ci\(*|build:*|build\(*|chore:*|chore\(*|refactor:*|refactor\(*|style:*|style\(*|docs:*|docs\(*) | |
| CAT="MAINT" ;; | |
| *) | |
| CAT="OTHER" ;; | |
| esac | |
| BULLETS=$(echo "$BODY" | grep -E '^\s*[-*•]\s+' | sed 's/^[[:space:]]*[-*•][[:space:]]*//' || true) | |
| if [ -n "$BULLETS" ]; then | |
| while IFS= read -r item; do | |
| [ -n "$item" ] && echo "${CAT}|${item}" >> "$ITEMS_FILE" | |
| done <<< "$BULLETS" | |
| else | |
| CLEAN=$(echo "$SUBJECT" | sed -E 's/^(feat|add|new|fix|bug|patch|hotfix|ci|build|chore|refactor|style|docs)(\([^)]*\))?[: ]*//') | |
| [ -n "$CLEAN" ] && echo "${CAT}|${CLEAN}" >> "$ITEMS_FILE" | |
| fi | |
| done <<< "$HASHES" | |
| FEATURES="" | |
| FIXES="" | |
| MAINTENANCE="" | |
| OTHER="" | |
| if [ -f "$ITEMS_FILE" ]; then | |
| while IFS='|' read -r category text; do | |
| [ -z "$text" ] && continue | |
| text="$(echo "${text:0:1}" | tr '[:lower:]' '[:upper:]')${text:1}" | |
| case "$category" in | |
| FEAT) FEATURES="${FEATURES}\n• \u2800${text}" ;; | |
| FIX) FIXES="${FIXES}\n• \u2800${text}" ;; | |
| MAINT) MAINTENANCE="${MAINTENANCE}\n• \u2800${text}" ;; | |
| OTHER) OTHER="${OTHER}\n• \u2800${text}" ;; | |
| esac | |
| done < "$ITEMS_FILE" | |
| fi | |
| rm -f "$ITEMS_FILE" | |
| FEAT_COUNT=$(echo -e "$FEATURES" | grep -c "•" || true) | |
| FIX_COUNT=$(echo -e "$FIXES" | grep -c "•" || true) | |
| SUMMARY="Release of QuickNotes with" | |
| PARTS="" | |
| if [ "$FEAT_COUNT" -gt 0 ]; then | |
| PARTS="${PARTS} ${FEAT_COUNT} new feature(s)," | |
| fi | |
| if [ "$FIX_COUNT" -gt 0 ]; then | |
| PARTS="${PARTS} ${FIX_COUNT} bug fix(es)," | |
| fi | |
| if [ -n "$PARTS" ]; then | |
| PARTS=$(echo "$PARTS" | sed 's/,$//') | |
| SUMMARY="${SUMMARY}${PARTS}." | |
| else | |
| SUMMARY="Maintenance release of QuickNotes with internal improvements and updates." | |
| fi | |
| { | |
| echo "body<<EOFMARKER" | |
| echo "**Release: ${RELEASE_DATE}**" | |
| echo "${SUMMARY}" | |
| echo "" | |
| if [ -n "$FEATURES" ]; then | |
| echo "**New Features & Improvements**" | |
| echo -e "$FEATURES" | |
| echo "" | |
| fi | |
| if [ -n "$FIXES" ]; then | |
| echo "**Bug Fixes**" | |
| echo -e "$FIXES" | |
| echo "" | |
| fi | |
| if [ -n "$MAINTENANCE" ]; then | |
| echo "**Maintenance**" | |
| echo -e "$MAINTENANCE" | |
| echo "" | |
| fi | |
| if [ -n "$OTHER" ]; then | |
| echo "**Other Changes**" | |
| echo -e "$OTHER" | |
| echo "" | |
| fi | |
| echo "**Note:** If you encounter any bugs or issues, please don't hesitate to open an [issue](https://github.com/BerndHagen/QuickNotes-Simple-Note-Manager/issues). For any questions or to start a discussion, feel free to initiate a [discussion](https://github.com/BerndHagen/QuickNotes-Simple-Note-Manager/discussions) on the GitHub repository." | |
| echo "EOFMARKER" | |
| } >> $GITHUB_OUTPUT | |
| - name: Create versioned release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.version.outputs.tag }} | |
| name: ${{ steps.version.outputs.tag }} | |
| body: ${{ steps.notes.outputs.body }} | |
| files: quicknotes-${{ steps.version.outputs.tag }}.zip |