feat(new-scripts): add TREK, SoulSync, UpSnap, Anchor, Slink #283
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: Update script timestamp on .sh changes | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - "ct/**/*.sh" | |
| - "install/**/*.sh" | |
| - "tools/**/*.sh" | |
| - "turnkey/**/*.sh" | |
| - "vm/**/*.sh" | |
| jobs: | |
| update-script-timestamp: | |
| runs-on: self-hosted | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get changed .sh files and derive slugs | |
| id: slugs | |
| run: | | |
| changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.event.after }}" -- ct/ install/ tools/ turnkey/ vm/ | grep '\.sh$' || true) | |
| if [[ -z "$changed" ]]; then | |
| echo "No .sh files changed in ct/, install/, tools/, turnkey/, or vm/." | |
| echo "count=0" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| declare -A seen | |
| slugs="" | |
| for f in $changed; do | |
| [[ -f "$f" ]] || continue | |
| base="${f##*/}" | |
| base="${base%.sh}" | |
| if [[ "$f" == install/* && "$base" == *-install ]]; then | |
| slug="${base%-install}" | |
| else | |
| slug="$base" | |
| fi | |
| if [[ -z "${seen[$slug]:-}" ]]; then | |
| seen[$slug]=1 | |
| slugs="$slugs $slug" | |
| fi | |
| done | |
| slugs=$(echo $slugs | xargs -n1 | sort -u) | |
| if [[ -z "$slugs" ]]; then | |
| echo "No slugs to update." | |
| echo "count=0" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "$slugs" > changed_slugs.txt | |
| echo "count=$(echo "$slugs" | wc -w)" >> "$GITHUB_OUTPUT" | |
| - name: Parse PR number from merge commit | |
| id: pr | |
| run: | | |
| re='#([0-9]+)' | |
| if [[ "$COMMIT_MSG" =~ $re ]]; then | |
| echo "number=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "number=" >> "$GITHUB_OUTPUT" | |
| fi | |
| env: | |
| COMMIT_MSG: ${{ github.event.head_commit.message }} | |
| - name: Update script timestamps in PocketBase | |
| if: steps.slugs.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 }} | |
| COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} | |
| PR_URL: ${{ steps.pr.outputs.number != '' && format('{0}/{1}/pull/{2}', github.server_url, github.repository, steps.pr.outputs.number) || '' }} | |
| run: | | |
| node << 'ENDSCRIPT' | |
| (async function() { | |
| const fs = require('fs'); | |
| const https = require('https'); | |
| const http = require('http'); | |
| const url = require('url'); | |
| function request(fullUrl, opts, redirectCount) { | |
| redirectCount = redirectCount || 0; | |
| return new Promise(function(resolve, reject) { | |
| const u = url.parse(fullUrl); | |
| const isHttps = u.protocol === 'https:'; | |
| const body = opts.body; | |
| const options = { | |
| hostname: u.hostname, | |
| port: u.port || (isHttps ? 443 : 80), | |
| path: u.path, | |
| method: opts.method || 'GET', | |
| headers: opts.headers || {} | |
| }; | |
| if (body) options.headers['Content-Length'] = Buffer.byteLength(body); | |
| const lib = isHttps ? https : http; | |
| const req = lib.request(options, function(res) { | |
| if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { | |
| if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl)); | |
| const redirectUrl = url.resolve(fullUrl, res.headers.location); | |
| res.resume(); | |
| resolve(request(redirectUrl, opts, redirectCount + 1)); | |
| return; | |
| } | |
| let data = ''; | |
| res.on('data', function(chunk) { data += chunk; }); | |
| res.on('end', function() { | |
| resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, body: data }); | |
| }); | |
| }); | |
| req.on('error', reject); | |
| if (body) req.write(body); | |
| req.end(); | |
| }); | |
| } | |
| const raw = process.env.POCKETBASE_URL.replace(/\/$/, ''); | |
| const apiBase = /\/api$/i.test(raw) ? raw : raw + '/api'; | |
| const coll = process.env.POCKETBASE_COLLECTION; | |
| const slugsText = fs.readFileSync('changed_slugs.txt', 'utf8').trim(); | |
| const slugs = slugsText ? slugsText.split(/\s+/).filter(Boolean) : []; | |
| if (slugs.length === 0) { | |
| console.log('No slugs to update.'); | |
| return; | |
| } | |
| const authUrl = apiBase + '/collections/users/auth-with-password'; | |
| const authBody = JSON.stringify({ | |
| identity: process.env.POCKETBASE_ADMIN_EMAIL, | |
| password: process.env.POCKETBASE_ADMIN_PASSWORD | |
| }); | |
| const authRes = await request(authUrl, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: authBody | |
| }); | |
| if (!authRes.ok) { | |
| throw new Error('Auth failed: ' + authRes.body); | |
| } | |
| const token = JSON.parse(authRes.body).token; | |
| const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records'; | |
| for (const slug of slugs) { | |
| const filter = "(slug='" + slug.replace(/'/g, "''") + "')"; | |
| const listRes = await request(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', { | |
| headers: { 'Authorization': token } | |
| }); | |
| const list = JSON.parse(listRes.body); | |
| const record = list.items && list.items[0]; | |
| if (!record) { | |
| console.log('Slug not in DB, skipping: ' + slug); | |
| continue; | |
| } | |
| const patchRes = await request(recordsUrl + '/' + record.id, { | |
| method: 'PATCH', | |
| headers: { 'Authorization': token, 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| name: record.name || record.slug, | |
| last_update_commit: process.env.PR_URL || process.env.COMMIT_URL || '' | |
| }) | |
| }); | |
| if (!patchRes.ok) { | |
| console.warn('PATCH failed for slug ' + slug + ': ' + patchRes.body); | |
| continue; | |
| } | |
| console.log('Updated timestamp for slug: ' + slug); | |
| } | |
| console.log('Done.'); | |
| })().catch(e => { console.error(e); process.exit(1); }); | |
| ENDSCRIPT | |
| shell: bash |