{"record":{"id":"ac35120e73888010","repo":"paperclipai/paperclip","slug":"a-full-lowercase-source-sha-is-required","errorCode":null,"errorMessage":"A full lowercase source SHA is required.","messagePattern":"A full lowercase source SHA is required\\.","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-source-verification.mjs","lineNumber":9,"sourceCode":"import { appendFile } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\n\nconst repository = \"paperclipai/paperclip\";\nconst workflowPath = \".github/workflows/cloud-readiness.yml\";\nexport const sourceVerificationJob = \"Cloud source verified v1\";\n\nfunction assertSha(sha) {\n  if (!/^[a-f0-9]{40}$/.test(sha ?? \"\")) throw new Error(\"A full lowercase source SHA is required.\");\n}\n\nfunction trustedRun(run, sha, workflowId) {\n  return run.workflow_id === workflowId && run.path === workflowPath &&\n    run.repository?.full_name === repository && run.head_repository?.full_name === repository &&\n    run.head_sha === sha && run.head_branch === \"master\" && run.event === \"push\" &&\n    Number.isSafeInteger(run.id) && run.id > 0 &&\n    Number.isSafeInteger(run.run_attempt) && run.run_attempt > 0;\n}\n\n// Consume one versioned job, independent of image/migrator availability. A\n// failed image build must not invalidate source checks that already passed.\nexport async function readSourceVerification(sha, api) {\n  assertSha(sha);\n  const workflow = await api(`/repos/${repository}/actions/workflows/cloud-readiness.yml`);\n  if (workflow.path !== workflowPath || !Number.isSafeInteger(workflow.id) || workflow.id < 1) {\n    throw new Error(\"Cloud readiness workflow identity does not match.\");\n  }","sourceCodeStart":1,"sourceCodeEnd":27,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-source-verification.mjs#L1-L27","documentation":"assertSha in cloud-source-verification.mjs enforces that the commit SHA is a full 40-character lowercase hex string (a complete git SHA-1). Anything else — short SHAs, uppercase, 'HEAD', branch names — throws. All downstream GitHub API queries filter by head_sha and require the exact full SHA.","triggerScenarios":"Passing a short SHA (`abc1234`), an uppercase SHA, a branch/tag name, or an empty/undefined argv[2] to readSourceVerification, waitForSourceVerification, or the CLI `node scripts/cloud-source-verification.mjs <sha>`.","commonSituations":"Copy-pasting an abbreviated SHA from `git log --oneline`; CI providing github.sha as uppercase in some contexts; forgetting the CLI argument entirely; using `git rev-parse --short HEAD` output.","solutions":["Expand the SHA: `git rev-parse <short-sha>` and pass the full 40-char lowercase result.","In CI use the full github.sha context value.","If the input is user-supplied, normalize to lowercase and validate /^[a-f0-9]{40}$/ before calling."],"exampleFix":"// before\nconst sha = \"a1b2c3d\"; // short SHA\n// after\nconst sha = require(\"node:child_process\").execSync(\"git rev-parse a1b2c3d\").toString().trim(); // full 40-char SHA","handlingStrategy":"validation","validationCode":"const FULL_SHA = /^[a-f0-9]{40}$/;\nfunction resolveFullSha(input) {\n  const sha = String(input ?? \"\").trim().toLowerCase();\n  if (!FULL_SHA.test(sha)) throw new Error(`Provide a full 40-char lowercase SHA, got: ${input}`);\n  return sha;\n}","typeGuard":"const isFullSha = (v) => typeof v === \"string\" && /^[a-f0-9]{40}$/.test(v);","tryCatchPattern":"try {\n  await waitForSourceVerification(sha, { api });\n} catch (error) {\n  if (/full lowercase source SHA/.test(error.message)) {\n    console.error(\"Run: git rev-parse HEAD  # and pass the full SHA.\");\n    process.exitCode = 2;\n  } else throw error;\n}","preventionTips":["Always expand short SHAs with git rev-parse before use","In CI prefer the full github.sha context","Add the SHA regex check at the entrypoint of your release script"],"tags":["validation","git","sha"],"backgroundTag":"invalid-argument-format","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}