mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 00:02:10 +00:00
Each workflow is copied under a cloud- prefix, runs from cloud/ through a workflow-level defaults block, and resolves pnpm and the Node cache against cloud/package.json and cloud/pnpm-lock.yaml. Display names are unchanged because the recovery chain matches on them; every reusable call, gh dispatch, and jq run-path check was repointed to the prefixed filenames. Every job that can start on its own is gated on the repository variable ORCA_CLOUD_OPERATIONS_ENABLED, so both scheduled triggers and every manual dispatch skip without running a step until the owner enables them. Reusable jobs inherit the caller's gate rather than restating it. A new contract test pins the gate, the three chained display names, and the absence of any repository secret other than the automatic token.
82 lines
3.0 KiB
JavaScript
82 lines
3.0 KiB
JavaScript
import { spawn } from 'node:child_process'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { createAccessTokenSource } from './gcloud-access-token.mjs'
|
|
import { holderIdentity } from './holder-identity.mjs'
|
|
import { fail, input, notice, renewerLogPath, saveState, setOutput, warn } from './runner-state.mjs'
|
|
import { CloudSqlRolloutLease, LeaseConflict, describeHolder } from './storage-lease.mjs'
|
|
|
|
const bucket = input('bucket')
|
|
const objectName = input('object')
|
|
const release = input('release') !== 'false'
|
|
|
|
if (!bucket || !objectName) {
|
|
fail('cloud-sql-rollout-lease requires both `bucket` and `object`')
|
|
process.exit(1)
|
|
}
|
|
|
|
const holder = holderIdentity(input('holder-key'))
|
|
const lease = new CloudSqlRolloutLease({
|
|
bucket,
|
|
objectName,
|
|
accessToken: createAccessTokenSource()
|
|
})
|
|
|
|
let claim
|
|
try {
|
|
claim = await lease.acquire(holder)
|
|
} catch (error) {
|
|
if (error instanceof LeaseConflict) {
|
|
fail(
|
|
`${error.message}. Cloud SQL rollouts are serialized across repositories; this run will not queue or steal the lease. Wait for the holder to finish, then re-run.`
|
|
)
|
|
if (error.holder) {
|
|
console.log(`Lease holder repository: ${error.holder.repository}`)
|
|
console.log(`Lease holder workflow: ${error.holder.workflow}`)
|
|
console.log(`Lease holder run: ${error.holder.run_url}`)
|
|
}
|
|
} else {
|
|
// Bucket unreachable, permission denied, unreadable record: fail closed.
|
|
fail(`could not acquire ${lease.uri}: ${error.message}`)
|
|
}
|
|
process.exit(1)
|
|
}
|
|
|
|
// Persist before anything else can throw, so `post` always releases what we hold.
|
|
saveState('acquired', 'true')
|
|
saveState('bucket', bucket)
|
|
saveState('object', objectName)
|
|
saveState('holder_key', holder.holderKey)
|
|
saveState('release', release ? 'true' : 'false')
|
|
|
|
setOutput('holder-key', holder.holderKey)
|
|
setOutput('generation', claim.generation)
|
|
setOutput('expires-at', new Date(claim.record.expires_at).toISOString())
|
|
setOutput('reentrant', claim.state === 'reentrant' ? 'true' : 'false')
|
|
|
|
if (claim.state === 'reentrant') {
|
|
notice(
|
|
`Re-entered the Cloud SQL rollout lease on ${lease.uri} already held by this run; refreshed to ${new Date(claim.record.expires_at).toISOString()}.`
|
|
)
|
|
} else if (claim.state === 'takeover') {
|
|
notice(`Took over ${lease.uri} from ${describeHolder(claim.previous)}.`)
|
|
} else {
|
|
notice(
|
|
`Acquired ${lease.uri} until ${new Date(claim.record.expires_at).toISOString()} (holder ${holder.holderKey}).`
|
|
)
|
|
}
|
|
|
|
try {
|
|
const renewer = spawn(
|
|
process.execPath,
|
|
[fileURLToPath(new URL('./renew.mjs', import.meta.url)), bucket, objectName, holder.holderKey],
|
|
{ detached: true, stdio: 'ignore', env: process.env }
|
|
)
|
|
renewer.unref()
|
|
saveState('renewer_pid', String(renewer.pid))
|
|
const log = renewerLogPath()
|
|
notice(`Lease renewer running as pid ${renewer.pid}${log ? `, logging to ${log}` : ''}.`)
|
|
} catch (error) {
|
|
// A missing renewer is survivable for short jobs; the TTL still covers 35 minutes.
|
|
warn(`could not start the lease renewer: ${error.message}. The lease will expire on its TTL.`)
|
|
}
|