(client)
| 118 | } |
| 119 | |
| 120 | async function stepInstallation(client) { |
| 121 | const secret = need('RUNNING_SECRET'); |
| 122 | const org = await orgUuid(client); |
| 123 | const live = await listInstallations(); |
| 124 | const liveIds = new Set((Array.isArray(live) ? live : []).map((i) => String(i.id))); |
| 125 | |
| 126 | const existing = await client.query( |
| 127 | "SELECT encode(encrypted_installation_id,'hex') AS hex FROM github_app_installations WHERE organization_uuid=$1", [org]); |
| 128 | if (existing.rows.length) { |
| 129 | try { |
| 130 | const cur = decrypt(Buffer.from(existing.rows[0].hex, 'hex'), secret); |
| 131 | if (liveIds.has(String(cur))) { |
| 132 | console.log(`OK: installation row already valid (id=${cur}, decrypts, live)`); |
| 133 | return; |
| 134 | } |
| 135 | console.log(`reconcile: stored id ${cur} is stale (not a live installation) — refreshing`); |
| 136 | } catch (_) { |
| 137 | console.log('reconcile: stored installation id will not decrypt with the running secret — refreshing'); |
| 138 | } |
| 139 | } else { |
| 140 | console.log('reconcile: no installation row for this org — creating'); |
| 141 | } |
| 142 | |
| 143 | const inst = await pickInstallation(); |
| 144 | const id = String(inst.id); |
| 145 | const enc = encrypt(id, secret); |
| 146 | if (decrypt(enc, secret) !== id) throw new Error('round-trip check failed'); |
| 147 | |
| 148 | if (existing.rows.length) { |
| 149 | await client.query( |
| 150 | 'UPDATE github_app_installations SET encrypted_installation_id=$1, updated_at=now() WHERE organization_uuid=$2', |
| 151 | [enc, org]); |
| 152 | } else { |
| 153 | const u = await client.query( |
| 154 | "SELECT u.user_uuid FROM users u JOIN emails e ON e.user_id=u.user_id WHERE e.email='demo@lightdash.com' LIMIT 1"); |
| 155 | const userUuid = u.rows[0] && u.rows[0].user_uuid; |
| 156 | if (!userUuid) throw new Error('demo user not found for created_by'); |
| 157 | // auth_token/refresh_token are varchar (NOT NULL) and only affect PR authorship, |
| 158 | // never the token mint — a plain string placeholder is correct here. Binding an |
| 159 | // encrypted Buffer would send binary bytes into a UTF8 text column and fail. |
| 160 | const placeholder = 'placeholder'; |
| 161 | await client.query( |
| 162 | `INSERT INTO github_app_installations |
| 163 | (organization_uuid, encrypted_installation_id, auth_token, refresh_token, |
| 164 | created_by_user_uuid, updated_by_user_uuid, created_at, updated_at) |
| 165 | VALUES ($1,$2,$3,$3,$4,$4,now(),now())`, |
| 166 | [org, enc, placeholder, userUuid]); |
| 167 | } |
| 168 | console.log(`OK: installation row set to id=${id} (account=${inst.account && inst.account.login}, repos=${inst.repository_selection})`); |
| 169 | } |
| 170 | |
| 171 | async function stepOrgSettings(client, kvs) { |
| 172 | const org = await orgUuid(client); |
no test coverage detected