96 lines
3.8 KiB
JavaScript
96 lines
3.8 KiB
JavaScript
function createReleaseReceipt({ storage, getLogin, fetchJson, launcher = null, dialog = null, statusNode = null, checksNode = null, releaseNode = null }) {
|
|
const prefix = 'stackchain.release-receipt.v1:';
|
|
let receipt = null;
|
|
|
|
const login = () => String(getLogin?.() || '').trim().toLowerCase();
|
|
const key = () => prefix + login();
|
|
const path = value => 'api/v1/repos/' + String(value.repository || '').split('/')
|
|
.map(encodeURIComponent).join('/') + '/release-receipt/' + encodeURIComponent(value.commit_sha);
|
|
|
|
function restore() {
|
|
const account = login();
|
|
if (!account) return null;
|
|
try {
|
|
const parsed = JSON.parse(storage?.getItem(prefix + account) || 'null');
|
|
receipt = parsed && parsed.account === account && parsed.repository && parsed.commit_sha ? parsed : null;
|
|
} catch (_error) { receipt = null; }
|
|
render(receipt?.status || null);
|
|
return receipt;
|
|
}
|
|
|
|
function capture(item, mergeResult) {
|
|
const account = login();
|
|
const commitSha = String(mergeResult?.merge_commit_sha || '').trim();
|
|
if (!account || !item?.repository || !commitSha) throw new Error('The exact merge commit is unavailable.');
|
|
receipt = {
|
|
account,
|
|
repository: item.repository,
|
|
number: Number(item.number),
|
|
key: item.key || item.repository + '#' + item.number,
|
|
commit_sha: commitSha,
|
|
status: null,
|
|
};
|
|
storage?.setItem(key(), JSON.stringify(receipt));
|
|
render(null);
|
|
return receipt;
|
|
}
|
|
|
|
function summarize(payload) {
|
|
const checks = Array.isArray(payload?.checks) ? payload.checks : [];
|
|
const failing = checks.filter(check => ['failure', 'error'].includes(check.state)).map(check => check.name).filter(Boolean);
|
|
const pending = checks.filter(check => check.state === 'pending').map(check => check.name).filter(Boolean);
|
|
if (payload?.release) return { ...payload, label: 'Released · ' + payload.release.tag, checks: [] };
|
|
if (failing.length) return { ...payload, label: 'Checks failed', checks: failing };
|
|
if (payload?.ci_state === 'success') return { ...payload, label: 'Checks passed · waiting for release', checks: [] };
|
|
return { ...payload, label: 'Checks running', checks: pending };
|
|
}
|
|
|
|
function render(status) {
|
|
if (launcher) {
|
|
launcher.hidden = !receipt;
|
|
launcher.textContent = status?.label || (receipt ? 'Merged · tracking release' : '');
|
|
}
|
|
if (statusNode) statusNode.textContent = status?.label || 'Checking the exact merge commit…';
|
|
if (checksNode) checksNode.textContent = (status?.checks || []).join(', ');
|
|
if (releaseNode) {
|
|
releaseNode.hidden = !status?.release?.url;
|
|
if (status?.release?.url) {
|
|
releaseNode.href = status.release.url;
|
|
releaseNode.textContent = 'Open release ' + status.release.tag;
|
|
}
|
|
}
|
|
}
|
|
|
|
async function refresh() {
|
|
if (!receipt) restore();
|
|
if (!receipt) return null;
|
|
const payload = await fetchJson(path(receipt), { headers: { Accept: 'application/json' } });
|
|
if (payload?.commit_sha !== receipt.commit_sha) throw new Error('Release evidence did not match the merged commit.');
|
|
const status = summarize(payload);
|
|
receipt.status = status;
|
|
storage?.setItem(key(), JSON.stringify(receipt));
|
|
render(status);
|
|
return status;
|
|
}
|
|
|
|
function dismiss() {
|
|
const accountKey = key();
|
|
storage?.removeItem(accountKey);
|
|
receipt = null;
|
|
render(null);
|
|
if (dialog?.open) dialog.close();
|
|
}
|
|
|
|
function bind() {
|
|
launcher?.addEventListener('click', async () => {
|
|
dialog?.showModal?.();
|
|
try { await refresh(); }
|
|
catch (error) { if (statusNode) statusNode.textContent = error.message + ' Retry when connected.'; }
|
|
});
|
|
}
|
|
|
|
return { capture, restore, refresh, dismiss, bind, fetchJson };
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = createReleaseReceipt;
|