Skip to content

Commit ed28440

Browse files
fix: workflow urls
1 parent 1e89b2b commit ed28440

3 files changed

Lines changed: 121 additions & 18 deletions

File tree

.github/workflows/clone-and-push.yml

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ jobs:
116116
env:
117117
SUPABASE_URL: ${{ secrets.SUPABASE_URL || secrets.POWERSYNC_SUPABASE_URL }}
118118
SUPABASE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY || secrets.POWERSYNC_SUPABASE_SERVICE_ROLE_KEY || secrets.SUPABASE_ANON_KEY || secrets.POWERSYNC_SUPABASE_ANON_KEY }}
119+
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY || secrets.POWERSYNC_SUPABASE_ANON_KEY }}
120+
POWERGIT_EMAIL: ${{ secrets.POWERGIT_EMAIL }}
121+
POWERGIT_PASSWORD: ${{ secrets.POWERGIT_PASSWORD }}
119122
JOB_ID: ${{ github.event.inputs.job_id }}
120123
ORG_ID: ${{ github.event.inputs.org }}
121124
REPO_ID: ${{ github.event.inputs.repo }}
@@ -131,25 +134,56 @@ jobs:
131134
}
132135
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY)
133136
const now = new Date().toISOString()
137+
138+
const isUuid = (value) =>
139+
typeof value === 'string' &&
140+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value.trim())
141+
134142
const requestedByRaw = (process.env.REQUESTED_BY ?? '').trim()
135-
const requestedBy = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(requestedByRaw)
136-
? requestedByRaw
137-
: null
138-
const payload = {
143+
const requestedBy = isUuid(requestedByRaw) ? requestedByRaw : null
144+
145+
const buildPayload = (requestedByValue) => ({
139146
id: process.env.JOB_ID,
140147
org_id: process.env.ORG_ID,
141148
repo_id: process.env.REPO_ID,
142149
repo_url: process.env.REPO_URL,
143150
status: 'running',
144151
created_at: now,
145152
updated_at: now,
146-
requested_by: requestedBy,
153+
requested_by: requestedByValue,
147154
source: 'actions',
148155
workflow_url: process.env.WORKFLOW_URL ?? null,
149-
}
156+
})
157+
150158
;(async () => {
151-
const { error } = await supabase.from('import_jobs').upsert(payload, { onConflict: 'id' })
152-
if (error) throw error
159+
const initial = await supabase.from('import_jobs').upsert(buildPayload(requestedBy), { onConflict: 'id' })
160+
if (!initial.error) return
161+
162+
if (initial.error.code !== '42501') throw initial.error
163+
164+
// RLS fallback: authenticate with a known user and record the job as that user.
165+
const anonKey = (process.env.SUPABASE_ANON_KEY ?? '').trim()
166+
const email = (process.env.POWERGIT_EMAIL ?? '').trim()
167+
const password = (process.env.POWERGIT_PASSWORD ?? '').trim()
168+
if (!anonKey || !email || !password) {
169+
throw initial.error
170+
}
171+
172+
const authed = createClient(process.env.SUPABASE_URL, anonKey, {
173+
auth: { persistSession: false, autoRefreshToken: false },
174+
})
175+
const { data, error: loginError } = await authed.auth.signInWithPassword({ email, password })
176+
if (loginError) throw new Error(`Supabase login failed: ${loginError.message}`)
177+
const userId = data?.user?.id ?? ''
178+
if (!isUuid(userId)) throw new Error('Supabase login did not return a valid user id')
179+
180+
// Use REQUESTED_BY only when it matches the authenticated user, otherwise
181+
// pin the job to the CI user so the insert/update RLS policies pass.
182+
const effectiveRequestedBy = requestedBy && requestedBy === userId ? requestedBy : userId
183+
const retry = await authed
184+
.from('import_jobs')
185+
.upsert(buildPayload(effectiveRequestedBy), { onConflict: 'id' })
186+
if (retry.error) throw retry.error
153187
})().catch((err) => {
154188
console.error('Failed to record import job running', err)
155189
process.exit(1)
@@ -254,6 +288,9 @@ jobs:
254288
env:
255289
SUPABASE_URL: ${{ secrets.SUPABASE_URL || secrets.POWERSYNC_SUPABASE_URL }}
256290
SUPABASE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY || secrets.POWERSYNC_SUPABASE_SERVICE_ROLE_KEY || secrets.SUPABASE_ANON_KEY || secrets.POWERSYNC_SUPABASE_ANON_KEY }}
291+
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY || secrets.POWERSYNC_SUPABASE_ANON_KEY }}
292+
POWERGIT_EMAIL: ${{ secrets.POWERGIT_EMAIL }}
293+
POWERGIT_PASSWORD: ${{ secrets.POWERGIT_PASSWORD }}
257294
JOB_ID: ${{ github.event.inputs.job_id }}
258295
WORKFLOW_STATUS: ${{ job.status }}
259296
run: |
@@ -267,9 +304,35 @@ jobs:
267304
updated_at: now,
268305
error: status === 'error' ? 'GitHub Actions import failed' : null,
269306
}
307+
308+
const isUuid = (value) =>
309+
typeof value === 'string' &&
310+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value.trim())
311+
270312
;(async () => {
271-
const { error } = await supabase.from('import_jobs').update(update).eq('id', process.env.JOB_ID)
272-
if (error) throw error
313+
const initial = await supabase.from('import_jobs').update(update).eq('id', process.env.JOB_ID)
314+
if (!initial.error) return
315+
316+
if (initial.error.code !== '42501') throw initial.error
317+
318+
// RLS fallback: authenticate with a known user and update the job as that user.
319+
const anonKey = (process.env.SUPABASE_ANON_KEY ?? '').trim()
320+
const email = (process.env.POWERGIT_EMAIL ?? '').trim()
321+
const password = (process.env.POWERGIT_PASSWORD ?? '').trim()
322+
if (!anonKey || !email || !password) {
323+
throw initial.error
324+
}
325+
326+
const authed = createClient(process.env.SUPABASE_URL, anonKey, {
327+
auth: { persistSession: false, autoRefreshToken: false },
328+
})
329+
const { data, error: loginError } = await authed.auth.signInWithPassword({ email, password })
330+
if (loginError) throw new Error(`Supabase login failed: ${loginError.message}`)
331+
const userId = data?.user?.id ?? ''
332+
if (!isUuid(userId)) throw new Error('Supabase login did not return a valid user id')
333+
334+
const retry = await authed.from('import_jobs').update(update).eq('id', process.env.JOB_ID)
335+
if (retry.error) throw retry.error
273336
})().catch((err) => {
274337
console.error('Failed to record import job result', err)
275338
process.exit(1)

packages/apps/explorer/src/routes/org.$orgId.repo.$repoId.commits.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,13 @@ function Commits() {
198198
id: j.id,
199199
status: j.status,
200200
error: j.error,
201+
workflow_url: j.workflow_url,
201202
updated_at: j.updated_at,
202203
})),
203204
[importJobs, orgId, repoId],
204-
) as { data: Array<{ id: string; status: string | null; error: string | null; updated_at: string | null }> }
205+
) as {
206+
data: Array<{ id: string; status: string | null; error: string | null; workflow_url: string | null; updated_at: string | null }>
207+
}
205208

206209
const packRows = useLiveQuery(
207210
(q) =>
@@ -298,6 +301,7 @@ function Commits() {
298301
const [filterResetKey, setFilterResetKey] = React.useState(0)
299302
const [refreshStatus, setRefreshStatus] = React.useState<'idle' | 'loading' | 'queued' | 'running' | 'done' | 'error'>('idle')
300303
const [refreshJobId, setRefreshJobId] = React.useState<string | null>(null)
304+
const [refreshWorkflowUrl, setRefreshWorkflowUrl] = React.useState<string | null>(null)
301305
const [refreshMessage, setRefreshMessage] = React.useState<string | null>(null)
302306
const trackedJob = React.useMemo(() => {
303307
const job = latestImportJobs[0] ?? null
@@ -693,14 +697,22 @@ function Commits() {
693697
: 'inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-slate-300 bg-white px-3 py-1 text-xs font-medium text-slate-700 transition hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-200'
694698
}
695699
onClick={async () => {
696-
if (refreshBusy) return
700+
if (refreshBusy) {
701+
const workflowUrl =
702+
(trackedJob?.workflow_url ?? '').trim() || (refreshWorkflowUrl ?? '').trim()
703+
if (workflowUrl) {
704+
window.open(workflowUrl, '_blank', 'noopener,noreferrer')
705+
}
706+
return
707+
}
697708
if (!repoUrl) {
698709
setRefreshStatus('error')
699710
setRefreshMessage('Repository URL is unavailable.')
700711
return
701712
}
702713
setRefreshStatus('loading')
703714
setRefreshJobId(null)
715+
setRefreshWorkflowUrl(null)
704716
setRefreshMessage(null)
705717
try {
706718
const job = await requestGithubImport({
@@ -710,6 +722,9 @@ function Commits() {
710722
branch: branchFilter !== 'all' ? branchFilter : repoDefaultBranch,
711723
})
712724
setRefreshJobId(job.id ?? null)
725+
const workflowUrlRaw = (job as unknown as { workflowUrl?: unknown }).workflowUrl
726+
const workflowUrl = typeof workflowUrlRaw === 'string' ? workflowUrlRaw.trim() : ''
727+
setRefreshWorkflowUrl(workflowUrl.length > 0 ? workflowUrl : null)
713728
const initialStatus = (job.status ?? '').toLowerCase()
714729
if (initialStatus === 'error') {
715730
setRefreshStatus('error')
@@ -727,9 +742,14 @@ function Commits() {
727742
setRefreshMessage(message)
728743
}
729744
}}
730-
title="Re-run import for this repository"
745+
title={
746+
refreshBusy
747+
? (trackedJob?.workflow_url ?? '').trim() || (refreshWorkflowUrl ?? '').trim()
748+
? 'View GitHub Actions run'
749+
: 'Refresh already in progress'
750+
: 'Re-run import for this repository'
751+
}
731752
data-testid="commit-refresh"
732-
disabled={refreshBusy}
733753
>
734754
{refreshBusy ? (
735755
<>

packages/apps/explorer/src/routes/org.$orgId.repo.$repoId.files.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -428,10 +428,13 @@ function Files() {
428428
id: j.id,
429429
status: j.status,
430430
error: j.error,
431+
workflow_url: j.workflow_url,
431432
updated_at: j.updated_at,
432433
})),
433434
[importJobs, orgId, repoId],
434-
) as { data: Array<{ id: string; status: string | null; error: string | null; updated_at: string | null }> }
435+
) as {
436+
data: Array<{ id: string; status: string | null; error: string | null; workflow_url: string | null; updated_at: string | null }>
437+
}
435438

436439
const orgMenuOptions = React.useMemo(() => {
437440
const orgs = new Set<string>()
@@ -663,6 +666,7 @@ function Files() {
663666
const [readyCommits, setReadyCommits] = React.useState<Set<string>>(() => new Set())
664667
const [refreshStatus, setRefreshStatus] = React.useState<'idle' | 'loading' | 'queued' | 'running' | 'done' | 'error'>('idle')
665668
const [refreshJobId, setRefreshJobId] = React.useState<string | null>(null)
669+
const [refreshWorkflowUrl, setRefreshWorkflowUrl] = React.useState<string | null>(null)
666670
const [refreshMessage, setRefreshMessage] = React.useState<string | null>(null)
667671
const trackedJob = React.useMemo(() => {
668672
const job = latestImportJobs[0] ?? null
@@ -1301,14 +1305,22 @@ function Files() {
13011305
type="button"
13021306
className={refreshButtonClass}
13031307
onClick={async () => {
1304-
if (refreshBusy) return
1308+
if (refreshBusy) {
1309+
const workflowUrl =
1310+
(trackedJob?.workflow_url ?? '').trim() || (refreshWorkflowUrl ?? '').trim()
1311+
if (workflowUrl) {
1312+
window.open(workflowUrl, '_blank', 'noopener,noreferrer')
1313+
}
1314+
return
1315+
}
13051316
if (!repoUrl) {
13061317
setRefreshStatus('error')
13071318
setRefreshMessage('Repository URL is unavailable.')
13081319
return
13091320
}
13101321
setRefreshStatus('loading')
13111322
setRefreshJobId(null)
1323+
setRefreshWorkflowUrl(null)
13121324
setRefreshMessage(null)
13131325
try {
13141326
const job = await requestGithubImport({
@@ -1318,6 +1330,9 @@ function Files() {
13181330
branch: selectedBranch?.name ?? repoDefaultBranch ?? null,
13191331
})
13201332
setRefreshJobId(job.id ?? null)
1333+
const workflowUrlRaw = (job as unknown as { workflowUrl?: unknown }).workflowUrl
1334+
const workflowUrl = typeof workflowUrlRaw === 'string' ? workflowUrlRaw.trim() : ''
1335+
setRefreshWorkflowUrl(workflowUrl.length > 0 ? workflowUrl : null)
13211336
const initialStatus = (job.status ?? '').toLowerCase()
13221337
if (initialStatus === 'error') {
13231338
setRefreshStatus('error')
@@ -1335,9 +1350,14 @@ function Files() {
13351350
setRefreshMessage(message)
13361351
}
13371352
}}
1338-
title="Re-run import for this repository"
1353+
title={
1354+
refreshBusy
1355+
? (trackedJob?.workflow_url ?? '').trim() || (refreshWorkflowUrl ?? '').trim()
1356+
? 'View GitHub Actions run'
1357+
: 'Refresh already in progress'
1358+
: 'Re-run import for this repository'
1359+
}
13391360
data-testid="repo-refresh"
1340-
disabled={refreshBusy}
13411361
>
13421362
{refreshBusy ? (
13431363
<>

0 commit comments

Comments
 (0)