fix: migration 010 β clean up group_members RLS policies to minimal set #26
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: | |
| - main | |
| tags: | |
| - 'v*.*.*' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| release: | |
| name: Create or Update GitHub Release | |
| runs-on: ubuntu-latest | |
| concurrency: | |
| group: release-${{ github.ref_name }} | |
| cancel-in-progress: false | |
| steps: | |
| - name: β¬οΈ Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: π·οΈ Get tag info | |
| id: tag | |
| run: | | |
| set -euo pipefail | |
| if [ "${GITHUB_REF_TYPE}" = "tag" ]; then | |
| TAG="${GITHUB_REF_NAME}" | |
| VERSION="${TAG#v}" | |
| else | |
| VERSION="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' package.json | head -1)" | |
| TAG="v${VERSION}" | |
| fi | |
| if ! printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([.-][A-Za-z0-9.-]+)?$'; then | |
| echo "β Invalid package version: $VERSION" | |
| exit 1 | |
| fi | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: π’ Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: β Release syntax checks | |
| run: | | |
| set -euo pipefail | |
| node --check server.mjs | |
| node --check server/backup-manager.mjs | |
| node --check public/sync/backup-normalizer.js | |
| node --check public/sync/local-data-adapter.js | |
| node --check scripts/validate-backup-files.mjs | |
| node --check scripts/repair-user-backup.mjs | |
| node --check scripts/prove-new-browser-restore.mjs | |
| node --check scripts/validate-storage-cleanup.mjs | |
| node --check scripts/verify-supabase-security.mjs | |
| node --check scripts/compare-remote-assets.mjs | |
| npm run docs:validate | |
| - name: π Extract changelog section | |
| id: changelog | |
| run: | | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| # Extract section for this version from CHANGELOG.md | |
| NOTES=$(awk "/^## \[$VERSION\]/{found=1; next} found && /^## /{exit} found{print}" CHANGELOG.md) | |
| if [ -z "$NOTES" ]; then | |
| NOTES="See [CHANGELOG.md](./CHANGELOG.md) for details." | |
| fi | |
| # Write to a file to avoid multi-line/special-char issues in GITHUB_OUTPUT | |
| echo "$NOTES" > /tmp/changelog_notes.txt | |
| { | |
| echo "notes<<CHANGELOG_EOF" | |
| cat /tmp/changelog_notes.txt | |
| echo "CHANGELOG_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: π€ Enhance release notes with AI | |
| id: ai_notes | |
| continue-on-error: true | |
| env: | |
| YEPAPI_KEY: ${{ secrets.YEPAPI_KEY }} | |
| run: | | |
| if [ -z "$YEPAPI_KEY" ]; then | |
| echo "No YEPAPI_KEY β using raw changelog notes" | |
| { | |
| echo "notes<<CHANGELOG_EOF" | |
| cat /tmp/changelog_notes.txt | |
| echo "CHANGELOG_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| # Write excerpt to a file β avoids shell injection via changelog content | |
| head -50 /tmp/changelog_notes.txt > /tmp/changelog_excerpt.txt | |
| # Build JSON payload using node to handle escaping safely | |
| node -e " | |
| const fs = require('fs'); | |
| const excerpt = fs.readFileSync('/tmp/changelog_excerpt.txt', 'utf8').trim(); | |
| const payload = { | |
| model: 'gpt-4o-mini', | |
| messages: [{ | |
| role: 'user', | |
| content: 'Polish these GitHub release notes for IsotopeAI v${VERSION} into a clean, friendly, emoji-enhanced format for students. Keep it concise (max 300 words). Original:\n' + excerpt | |
| }] | |
| }; | |
| fs.writeFileSync('/tmp/ai_payload.json', JSON.stringify(payload)); | |
| " | |
| RESPONSE=$(curl -s -X POST "https://api.yepapi.com/v1/ai/chat" \ | |
| -H "x-api-key: $YEPAPI_KEY" \ | |
| -H "Content-Type: application/json" \ | |
| -d @/tmp/ai_payload.json) | |
| AI_NOTES=$(echo "$RESPONSE" | node -e " | |
| let d=''; | |
| process.stdin.on('data', c => d += c); | |
| process.stdin.on('end', () => { | |
| try { | |
| const p = JSON.parse(d); | |
| console.log(p.data?.message?.content || ''); | |
| } catch { | |
| console.log(''); | |
| } | |
| }); | |
| ") | |
| if [ -n "$AI_NOTES" ]; then | |
| echo "$AI_NOTES" > /tmp/ai_notes.txt | |
| { | |
| echo "notes<<CHANGELOG_EOF" | |
| cat /tmp/ai_notes.txt | |
| echo "CHANGELOG_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| else | |
| echo "AI returned empty β falling back to raw changelog" | |
| { | |
| echo "notes<<CHANGELOG_EOF" | |
| cat /tmp/changelog_notes.txt | |
| echo "CHANGELOG_EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: π¦ Package release archive | |
| run: | | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| zip -qr "isotope-v${VERSION}.zip" . \ | |
| --exclude ".git/*" \ | |
| --exclude "node_modules/*" \ | |
| --exclude ".env" \ | |
| --exclude ".isotope/*" \ | |
| --exclude "artifacts/*" \ | |
| --exclude "*.log" \ | |
| --exclude ".github/*" \ | |
| --exclude "screenshots/*" | |
| echo "Archive: $(wc -c < isotope-v${VERSION}.zip) bytes" | |
| - name: π Verify release archive | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| TMPDIR="$(mktemp -d)" | |
| unzip -q "isotope-v${VERSION}.zip" -d "$TMPDIR" | |
| required=( | |
| server.mjs | |
| package.json | |
| public/sync/backup-normalizer.js | |
| public/sync/local-data-adapter.js | |
| server/backup-manager.mjs | |
| scripts/prove-new-browser-restore.mjs | |
| scripts/repair-user-backup.mjs | |
| scripts/validate-backup-files.mjs | |
| scripts/validate-storage-cleanup.mjs | |
| scripts/verify-supabase-security.mjs | |
| scripts/compare-remote-assets.mjs | |
| sql/backup_manifests.sql | |
| sql/006_security_policy_cleanup.sql | |
| docs/sync-system.md | |
| docs/storage-backup-system.md | |
| docs/supabase-connection-map.md | |
| docs/index.html | |
| docs/install.html | |
| docs/sync.html | |
| docs/admin.html | |
| docs/gallery.html | |
| docs/motion.html | |
| docs/assets/site.css | |
| docs/assets/site.js | |
| docs/logo.svg | |
| README.md | |
| ADMIN.md | |
| .env.example | |
| ) | |
| for f in "${required[@]}"; do | |
| test -f "$TMPDIR/$f" || { echo "β Missing from release: $f"; exit 1; } | |
| echo "β $f" | |
| done | |
| if [ -f "$TMPDIR/.env" ]; then | |
| echo "β .env included in release" | |
| exit 1 | |
| fi | |
| if find "$TMPDIR" -maxdepth 1 -name '.env.*' ! -name '.env.example' | grep -q .; then | |
| echo "β private .env.* included in release" | |
| exit 1 | |
| fi | |
| PORT=3099 \ | |
| SUPABASE_URL=https://placeholder.supabase.co \ | |
| SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYW5vbiJ9.signature \ | |
| SESSION_SECRET=release_smoke_test_secret_not_real \ | |
| node "$TMPDIR/server.mjs" & | |
| PID=$! | |
| trap 'kill "$PID" 2>/dev/null || true; rm -rf "$TMPDIR"' EXIT | |
| for i in $(seq 1 30); do | |
| STATUS="$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3099/__isotope/ping 2>/dev/null || echo 000)" | |
| if [ "$STATUS" = "200" ]; then | |
| echo "Release smoke HTTP $STATUS" | |
| exit 0 | |
| fi | |
| sleep 0.5 | |
| done | |
| echo "β Release archive server ping did not return 200 (got: $STATUS)" | |
| exit 1 | |
| - name: π Build release notes | |
| run: | | |
| cat > /tmp/release_notes.md <<'EOF' | |
| ## IsotopeAI ${{ steps.tag.outputs.tag }} | |
| ${{ steps.ai_notes.outputs.notes || steps.changelog.outputs.notes }} | |
| --- | |
| ### π₯ Quick Install | |
| ```bash | |
| git clone https://github.com/Suydev/isotope-code.git | |
| cd isotope-code | |
| bash setup.sh | |
| ``` | |
| ### π What's Changed | |
| See [CHANGELOG.md](https://github.com/Suydev/isotope-code/blob/main/CHANGELOG.md) for the full history. | |
| ### β€οΈ Support | |
| UPI: `9699393886@fam` Β· [isotopeai.in](https://isotopeai.in) | |
| EOF | |
| - name: π·οΈ Ensure release tag points to this commit | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| TAG="${{ steps.tag.outputs.tag }}" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag -f "$TAG" "$GITHUB_SHA" | |
| git push --force origin "refs/tags/$TAG" | |
| - name: π Create or update GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| TAG="${{ steps.tag.outputs.tag }}" | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| ARCHIVE="isotope-v${VERSION}.zip" | |
| PRERELEASE_FLAG=() | |
| if printf '%s' "$TAG" | grep -Eq -- '(-beta|-rc)'; then | |
| PRERELEASE_FLAG=(--prerelease) | |
| fi | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| gh release edit "$TAG" \ | |
| --title "IsotopeAI ${TAG} π§ͺ" \ | |
| --notes-file /tmp/release_notes.md \ | |
| "${PRERELEASE_FLAG[@]}" | |
| else | |
| gh release create "$TAG" \ | |
| --target "$GITHUB_SHA" \ | |
| --title "IsotopeAI ${TAG} π§ͺ" \ | |
| --notes-file /tmp/release_notes.md \ | |
| "${PRERELEASE_FLAG[@]}" | |
| fi | |
| gh release upload "$TAG" "$ARCHIVE" --clobber | |
| echo "β Release ${TAG} published with ${ARCHIVE}" |