diff --git a/.github/workflows/push-json-to-pocketbase.yml b/.github/workflows/push-json-to-pocketbase.yml new file mode 100644 index 000000000..6d8708c0f --- /dev/null +++ b/.github/workflows/push-json-to-pocketbase.yml @@ -0,0 +1,94 @@ +name: Push JSON changes to PocketBase + +on: + push: + branches: + - main + paths: + - "frontend/public/json/**" + +jobs: + push-json: + runs-on: self-hosted + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get changed JSON files with slug + id: changed + run: | + changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.event.after }}" -- frontend/public/json/ | grep '\.json$' || true) + with_slug="" + for f in $changed; do + [[ -f "$f" ]] || continue + jq -e '.slug' "$f" >/dev/null 2>&1 && with_slug="$with_slug $f" + done + with_slug=$(echo $with_slug | xargs -n1) + if [[ -z "$with_slug" ]]; then + echo "No app JSON files changed (or no files with slug)." + echo "count=0" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "$with_slug" > changed_app_jsons.txt + echo "count=$(echo "$with_slug" | wc -w)" >> "$GITHUB_OUTPUT" + + - name: Push to PocketBase + if: steps.changed.outputs.count != '0' + env: + POCKETBASE_URL: ${{ secrets.POCKETBASE_URL }} + POCKETBASE_COLLECTION: ${{ secrets.POCKETBASE_COLLECTION }} + POCKETBASE_ADMIN_EMAIL: ${{ secrets.POCKETBASE_ADMIN_EMAIL }} + POCKETBASE_ADMIN_PASSWORD: ${{ secrets.POCKETBASE_ADMIN_PASSWORD }} + run: | + node << 'ENDSCRIPT' + (async () => { + const fs = require('fs'); + const base = process.env.POCKETBASE_URL.replace(/\/$/, '') + '/api'; + const coll = process.env.POCKETBASE_COLLECTION; + const files = fs.readFileSync('changed_app_jsons.txt', 'utf8').trim().split(/\s+/).filter(Boolean); + const authRes = await fetch(base + '/admins/auth-with-password', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + identity: process.env.POCKETBASE_ADMIN_EMAIL, + password: process.env.POCKETBASE_ADMIN_PASSWORD + }) + }); + if (!authRes.ok) throw new Error('Auth failed: ' + await authRes.text()); + const { token } = await authRes.json(); + const recordsUrl = base + '/collections/' + encodeURIComponent(coll) + '/records'; + for (const file of files) { + if (!fs.existsSync(file)) continue; + const data = JSON.parse(fs.readFileSync(file, 'utf8')); + if (!data.slug) { console.log('Skipping', file, '(no slug)'); continue; } + const payload = { ...data, is_dev: false }; + const filter = "(slug='" + data.slug + "')"; + const listRes = await fetch(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', { + headers: { 'Authorization': token } + }); + const list = await listRes.json(); + const existingId = list.items && list.items[0] && list.items[0].id; + if (existingId) { + console.log('Updating', file, '(slug=' + data.slug + ')'); + const r = await fetch(recordsUrl + '/' + existingId, { + method: 'PATCH', + headers: { 'Authorization': token, 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + if (!r.ok) throw new Error('PATCH failed: ' + await r.text()); + } else { + console.log('Creating', file, '(slug=' + data.slug + ')'); + const r = await fetch(recordsUrl, { + method: 'POST', + headers: { 'Authorization': token, 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + if (!r.ok) throw new Error('POST failed: ' + await r.text()); + } + } + console.log('Done.'); + })().catch(e => { console.error(e); process.exit(1); }); + ENDSCRIPT + shell: bash