Skip to content

Commit

Permalink
Merge pull request #7 from depot/backscroll
Browse files Browse the repository at this point in the history
Implement support for GitHub Actions backscroll
  • Loading branch information
jacobwgillespie authored Feb 10, 2024
2 parents 6bd866f + 2a68c8a commit 27fd057
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/durable-objects/Watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class Watcher implements DurableObject {
const lines = message.arguments.flatMap((arg) => arg.lines)
for (const code of this.challenges.keys()) {
if (lines.some((line) => line.includes(code))) {
console.log(`Challenge ${code} validated`)
console.log(`Challenge ${code} validated with websocket`)
this.challenges.set(code, true)
this.stopWatcher()
return
Expand Down Expand Up @@ -121,7 +121,7 @@ async function startWebsocketWatcher(
Accept: 'application/json',
Cookie: `user_session=${session}`,
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
},
})
const body = await res.json<{data?: {authenticated_url: string}}>()
Expand Down
91 changes: 90 additions & 1 deletion src/utils/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export async function validateClaim(
claimData: ClaimData,
challengeCode: string,
): Promise<TokenClaims> {
const session = await env.KEYS.get('github-session')
if (!session) throw new Error('no github session')

const {data: run} = await request('GET /repos/{owner}/{repo}/actions/runs/{run_id}', {
owner: claimData.owner,
repo: claimData.repo,
Expand Down Expand Up @@ -40,6 +43,21 @@ export async function validateClaim(
const headSHA = job.head_sha
if (!jobID || !headSHA) continue

const jobURL = `https://github.com/${claimData.owner}/${claimData.repo}/actions/runs/${claimData.runID}/jobs/${jobID}`

promises.push(
validateChallengeCodeWithBackscroll({
session,
org: claimData.owner,
repo: claimData.repo,
runID: claimData.runID,
jobID: jobID,
code: challengeCode,
}).then((validated) => {
return {jobID, validated}
}),
)

promises.push(
validateChallengeCode(
env,
Expand Down Expand Up @@ -95,7 +113,7 @@ export async function validateClaim(
return validatedClaims
}

export async function validateChallengeCode(env: Env['Bindings'], url: string, code: string): Promise<boolean> {
async function validateChallengeCode(env: Env['Bindings'], url: string, code: string): Promise<boolean> {
const stub = env.WATCHER.get(env.WATCHER.idFromName(url))
const res = await stub.fetch('http://watcher/validate', {
method: 'POST',
Expand All @@ -105,3 +123,74 @@ export async function validateChallengeCode(env: Env['Bindings'], url: string, c
const data = await res.json<{validated: boolean}>()
return data.validated
}

interface BackscrollArgs {
session: string
org: string
repo: string
runID: number
jobID: number
code: string
}

async function validateChallengeCodeWithBackscroll(args: BackscrollArgs): Promise<boolean> {
console.log('Validating challenge with backscroll', args)

try {
const {session, org, repo, runID, jobID, code} = args
const backscrollRegex = new RegExp(`/${org}/${repo}/actions/runs/${runID}/jobs/(\\w+)/steps/[\\w-]+/backscroll`)
const pageRes = await fetch(`https://github.com/${org}/${repo}/actions/runs/${runID}/job/${jobID}`, {
headers: {
Accept: 'text/html,*/*',
Cookie: `user_session=${session}`,
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
},
})
const pageText = await pageRes.text()
const match = pageText.match(backscrollRegex)
if (!match) {
console.log('No backscroll job ID found', args)
return false
}
const backscrollJobID = match[1]

const jobURL = `https://github.com/${org}/${repo}/actions/runs/${runID}/jobs/${backscrollJobID}`
const stepsRes = await fetch(`${jobURL}/steps`, {
headers: {
Accept: 'application/json',
Cookie: `user_session=${session}`,
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
},
})
const body = await stepsRes.json<{id: string; status: string}[]>()
const runningSteps = body.filter((step) => step.status === 'in_progress')

for (const step of runningSteps) {
try {
console.log('Fetching backscroll for', step)
const backscrollRes = await fetch(`${jobURL}/steps/${step.id}/backscroll`, {
headers: {
Accept: 'application/json',
Cookie: `user_session=${session}`,
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
},
})
const body = await backscrollRes.json<{lines: {line: string}[]}>()

if (body.lines.some((line) => line.line.includes(code))) {
console.log(`Challenge ${code} validated with backscroll`, args)
return true
}
} catch (e) {
console.log('Error checking backscroll for step', step, e)
}
}
} catch (e) {
console.log('Error fething backscroll', e, args)
}

return false
}

0 comments on commit 27fd057

Please sign in to comment.