diff --git a/action.yml b/action.yml index 71a81dea..58a0c409 100644 --- a/action.yml +++ b/action.yml @@ -60,6 +60,10 @@ inputs: additional-ports: description: Ports the client needs to access the application in addition to the one in the app URL, as a list of comma-separated values required: false + head-sha: + description: | + Override the head commit SHA that we are analysing instead of using the one inferred automatically by Meticulous. This normally should not be set, but is useful in some scenarios. + required: false outputs: {} runs: @@ -80,6 +84,7 @@ runs: TEST_SUITE_ID: ${{ inputs.test-suite-id }} METICULOUS_TELEMETRY_SAMPLE_RATE: "0.01" ADDITIONAL_PORTS: ${{ inputs.additional-ports }} + HEAD_SHA: ${{ inputs.head-sha }} branding: color: purple icon: camera diff --git a/cloud-compute/action.yml b/cloud-compute/action.yml index 2fe00d11..6b6291e3 100644 --- a/cloud-compute/action.yml +++ b/cloud-compute/action.yml @@ -12,6 +12,10 @@ inputs: description: | The URL to execute the tests against. This URL should serve the code from the current commit (e.g. a localhost URL served up by a local server). required: true + head-sha: + description: | + Override the head commit SHA that we are analysing instead of using the one inferred automatically by Meticulous. This normally should not be set, but is useful in some scenarios. + required: false outputs: {} runs: diff --git a/out/cloud-compute.entrypoint.js b/out/cloud-compute.entrypoint.js index 89ad6792..88a593c4 100644 --- a/out/cloud-compute.entrypoint.js +++ b/out/cloud-compute.entrypoint.js @@ -44,16 +44,16 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``)},L. `);t.retry(o)||n(t.mainError())})})},pZ=async e=>{try{let t=await p_(e);return 502!==t.status}catch(e){return!1}};async function p_(e){let t=new AbortController,a=setTimeout(()=>t.abort(),5e3),i=await fetch(e,{signal:t.signal});return clearTimeout(a),i}var pG=O("7SZkG");const pT="https://app.meticulous.ai/docs/github-actions-v2",pR=e=>pV(e).toLowerCase().includes("resource not accessible by integration")||e?.status===403,pV=e=>{let t=e?.message??"";return"string"==typeof t?t:""},pE=`Please check www.githubstatus.com, and that you have setup the action correctly, including with the correct permissions: see ${pT} for the correct setup.`;var pG=(O("7SZkG"),O("7SZkG"));const pN=()=>{F(pG).getLogger(aK.METICULOUS_LOGGER_NAME).setDefaultLevel(F(pG).levels.INFO)},pW=e=>{let t=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);switch((e||"").toLocaleLowerCase()){case"trace":t.setLevel(F(pG).levels.TRACE,!1);break;case"debug":t.setLevel(F(pG).levels.DEBUG,!1);break;case"info":t.setLevel(F(pG).levels.INFO,!1);break;case"warn":t.setLevel(F(pG).levels.WARN,!1);break;case"error":t.setLevel(F(pG).levels.ERROR,!1);break;case"silent":t.setLevel(F(pG).levels.SILENT,!1)}},pk=e=>e.slice(0,7),pH=oC.fromObject({seconds:10}),pF=oC.fromObject({seconds:5}),pU=async({context:e,octokit:t})=>{let{owner:a,repo:i}=e.repo,n=e.runId,{data:o}=await t.rest.actions.getWorkflowRun({owner:a,repo:i,run_id:n});return{workflowId:o.workflow_id}},pY=async({owner:e,repo:t,workflowId:a,ref:i,commitSha:n,octokit:o})=>{try{await o.rest.actions.createWorkflowDispatch({owner:e,repo:t,workflow_id:a,ref:i})}catch(t){let e=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);if((t?.message??"").includes("Workflow does not have 'workflow_dispatch' trigger")){e.error(`Could not trigger a workflow run on commit ${pk(n)} of the base branch (${i}) to compare against, because there was no Meticulous workflow with the 'workflow_dispatch' trigger on the ${i} branch. Visual snapshots of the new flows will be taken, but no comparisons will be made. If you haven't merged the PR to setup Meticulous in Github Actions to the ${i} branch yet then this is expected. Otherwise please check that Meticulous is running on the ${i} branch, that it has a 'workflow_dispatch' trigger, and has the appropiate permissions. See ${pT} for the correct setup.`),e.debug(t);return}if(pR(t)){e.error(`Missing permission to trigger a workflow run on the base branch (${i}). Visual snapshots of the new flows will be taken, but no comparisons will be made. Please add the 'actions: write' permission to your workflow YAML file: see ${pT} for the correct setup.`),e.debug(t);return}e.error(`Could not trigger a workflow run on commit ${pk(n)} of the base branch (${i}) to compare against. Visual snapshots of the new flows will be taken, but no comparisons will be made. Please check that Meticulous is running on the ${i} branch, that it has a 'workflow_dispatch' trigger, and has the appropiate permissions. See ${pT} for the correct setup.`,t);return}return await pD(pH),await pX({owner:e,repo:t,workflowId:a,commitSha:n,octokit:o})},pO=async({owner:e,repo:t,workflowRunId:a,octokit:i,timeout:n})=>{let o=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME),s=null,r=sc.now();for(;(null==s||pL(s.status))&&sc.now().diff(r){let o=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME),s=await n.rest.actions.listWorkflowRuns({owner:e,repo:t,workflow_id:a,head_sha:i});o.debug(`Workflow runs list: ${JSON.stringify(s.data,null,2)}`);let r=s.data.workflow_runs.find(e=>pL(e.status));if(null!=r)return{...r,workflowRunId:r.id}},pL=e=>["in_progress","queued","requested","waiting"].some(t=>t===e),pD=async e=>new Promise(t=>setTimeout(t,e.toMillis())),pK=oC.fromObject({minutes:30}),pJ=oC.fromObject({minutes:10}),pz=async(...e)=>{let t=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);try{return await pj(...e)}catch(i){t.error(i);let a=`Error while running tests on base ${e[0].base}. No diffs will be reported for this run.`;return t.warn(a),(0,L.warning)(a),{shaToCompareAgainst:null}}},pj=async({event:e,apiToken:t,base:a,context:i,useCloudReplayEnvironmentVersion:n,octokit:o})=>{let s=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME),{owner:r,repo:p}=i.repo;if(!a)return{shaToCompareAgainst:null};let l=await (0,aI.getLatestTestRunResults)({client:(0,aI.createClient)({apiToken:t}),commitSha:a,...n?{useCloudReplayEnvironmentVersion:!0}:{logicalEnvironmentVersion:5}});if(null!=l)return s.info(`Tests already exist for commit ${a} (${l.id})`),{shaToCompareAgainst:a};let{workflowId:d}=await pU({context:i,octokit:o}),c=await pX({owner:r,repo:p,workflowId:d,commitSha:a,octokit:o});if(null!=c)return(s.info(`Waiting on workflow run on base commit (${a}) to compare against: ${c.html_url}`),"pull_request"===e.type)?(await pP({owner:r,repo:p,workflowRunId:c.workflowRunId,octokit:o,commitSha:a,timeout:pK}),{shaToCompareAgainst:a}):await pM({owner:r,repo:p,workflowRunId:c.workflowRunId,octokit:o,commitSha:a,timeout:pJ,logger:s});if("pull_request"!==e.type)return{shaToCompareAgainst:null};let u=e.payload.pull_request.base.ref;s.debug(JSON.stringify({base:a,baseRef:u},null,2));let m=await pQ({owner:r,repo:p,ref:u,octokit:o});if(s.debug(JSON.stringify({owner:r,repo:p,base:a,baseRef:u,currentBaseSha:m},null,2)),a!==m){let e=`Meticulous tests on base commit ${a} haven't started running so we have nothing to compare against. In addition we were not able to trigger a run on ${a} since the '${u}' branch is now pointing to ${m}. - Therefore no diffs will be reported for this run. Re-running the tests may fix this.`;return s.warn(e),(0,L.warning)(e),{shaToCompareAgainst:null}}let g=await pY({owner:r,repo:p,workflowId:d,ref:u,commitSha:a,octokit:o});if(null==g){let e=`Warning: Could not retrieve dispatched workflow run. Will not perform diffs against ${a}.`;return s.warn(e),(0,L.warning)(e),{shaToCompareAgainst:null}}return s.info(`Waiting on workflow run: ${g.html_url}`),await pP({owner:r,repo:p,workflowRunId:g.workflowRunId,octokit:o,commitSha:a,timeout:pK}),{shaToCompareAgainst:a}},pP=async({commitSha:e,...t})=>{let a=await pO(t);if(null==a||pL(a.status))throw Error(`Timed out while waiting for workflow run (${t.workflowRunId}) to complete.`);if("completed"!==a.status||"success"!==a.conclusion)throw Error(`Comparing against visual snapshots taken on ${e}, but the corresponding workflow run [${a.id}] did not complete successfully. See: ${a.html_url}`)},pM=async({commitSha:e,logger:t,...a})=>{let i=await pO(a);return null==i||pL(i.status)?(t.warn(`Timed out while waiting for workflow run (${a.workflowRunId}) to complete. Running without comparisons.`),{shaToCompareAgainst:null}):"completed"!==i.status||"success"!==i.conclusion?(t.warn(`Comparing against visual snapshots taken on ${e}, but the corresponding workflow run [${i.id}] did not complete successfully. See: ${i.html_url}. Running without comparisons.`),{shaToCompareAgainst:null}):{shaToCompareAgainst:e}},pQ=async({owner:e,repo:t,ref:a,octokit:i})=>{try{return(await i.rest.repos.getBranch({owner:e,repo:t,branch:a})).data.commit.sha}catch(e){if(pR(e))throw Error(`Missing permission to get the head commit of the branch '${a}'. This is required in order to correctly calculate the two commits to compare. Please add the 'contents: read' permission to your workflow YAML file: see ${pT} for the correct setup.`);throw F(pG).getLogger(aK.METICULOUS_LOGGER_NAME).error(`Unable to get head commit of branch '${a}'. This is required in order to correctly calculate the two commits to compare. ${pE}`),e}},pq=e=>e.substring(0,7);var pG=O("7SZkG");const p$=async(e,t)=>{if("pull_request"===e.type){let a=e.payload.pull_request.head.sha,i=e.payload.pull_request.base.sha,n=e.payload.pull_request.base.ref;return t.useDeploymentUrl?{base:await p0(a,i,n)??i,head:a}:{base:await p2(a,i)??i,head:a}}return"push"===e.type?{base:e.payload.before,head:e.payload.after}:"workflow_dispatch"===e.type?{base:null,head:eH.context.sha}:p1(e)},p1=e=>{throw Error("Unexpected event: "+JSON.stringify(e))},p0=(e,t,a)=>{let i=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);try{p4(),(0,R.execSync)(`git fetch origin ${e}`),(0,R.execSync)(`git fetch origin ${a}`);let n=(0,R.execSync)(`git merge-base ${e} origin/${a}`).toString().trim();if(!p9(n))return i.error(`Failed to get merge base of ${e} and ${a}: value returned by 'git merge-base' was not a valid git SHA ('${n}').Using the base of the pull request instead (${t}).`),null;return n}catch(n){return i.error(`Failed to get merge base of ${e} and ${a}. Error: ${n}. Using the base of the pull request instead (${t}).`),null}},p2=(e,t)=>{let a=process.env.GITHUB_SHA,i=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);if(null==a)return i.error(`No GITHUB_SHA environment var set, so can't work out true base of the merge commit. Using the base of the pull request instead (${t}).`),null;try{p4();let n=(0,R.execSync)("git rev-list --max-count=1 HEAD").toString().trim();if(n!==a)return i.info(`The head commit SHA (${n}) does not equal GITHUB_SHA environment variable (${a}). + Therefore no diffs will be reported for this run. Re-running the tests may fix this.`;return s.warn(e),(0,L.warning)(e),{shaToCompareAgainst:null}}let g=await pY({owner:r,repo:p,workflowId:d,ref:u,commitSha:a,octokit:o});if(null==g){let e=`Warning: Could not retrieve dispatched workflow run. Will not perform diffs against ${a}.`;return s.warn(e),(0,L.warning)(e),{shaToCompareAgainst:null}}return s.info(`Waiting on workflow run: ${g.html_url}`),await pP({owner:r,repo:p,workflowRunId:g.workflowRunId,octokit:o,commitSha:a,timeout:pK}),{shaToCompareAgainst:a}},pP=async({commitSha:e,...t})=>{let a=await pO(t);if(null==a||pL(a.status))throw Error(`Timed out while waiting for workflow run (${t.workflowRunId}) to complete.`);if("completed"!==a.status||"success"!==a.conclusion)throw Error(`Comparing against visual snapshots taken on ${e}, but the corresponding workflow run [${a.id}] did not complete successfully. See: ${a.html_url}`)},pM=async({commitSha:e,logger:t,...a})=>{let i=await pO(a);return null==i||pL(i.status)?(t.warn(`Timed out while waiting for workflow run (${a.workflowRunId}) to complete. Running without comparisons.`),{shaToCompareAgainst:null}):"completed"!==i.status||"success"!==i.conclusion?(t.warn(`Comparing against visual snapshots taken on ${e}, but the corresponding workflow run [${i.id}] did not complete successfully. See: ${i.html_url}. Running without comparisons.`),{shaToCompareAgainst:null}):{shaToCompareAgainst:e}},pQ=async({owner:e,repo:t,ref:a,octokit:i})=>{try{return(await i.rest.repos.getBranch({owner:e,repo:t,branch:a})).data.commit.sha}catch(e){if(pR(e))throw Error(`Missing permission to get the head commit of the branch '${a}'. This is required in order to correctly calculate the two commits to compare. Please add the 'contents: read' permission to your workflow YAML file: see ${pT} for the correct setup.`);throw F(pG).getLogger(aK.METICULOUS_LOGGER_NAME).error(`Unable to get head commit of branch '${a}'. This is required in order to correctly calculate the two commits to compare. ${pE}`),e}},pq=e=>e.substring(0,7);var pG=O("7SZkG");const p$=async(e,t)=>{if("pull_request"===e.type){let a=t.headSha||e.payload.pull_request.head.sha,i=e.payload.pull_request.base.sha,n=e.payload.pull_request.base.ref;return t.useDeploymentUrl?{base:await p0(a,i,n)??i,head:a}:{base:await p2(a,i)??i,head:a}}return"push"===e.type?{base:e.payload.before,head:t.headSha||e.payload.after}:"workflow_dispatch"===e.type?{base:null,head:t.headSha||eH.context.sha}:p1(e)},p1=e=>{throw Error("Unexpected event: "+JSON.stringify(e))},p0=(e,t,a)=>{let i=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);try{p4(),(0,R.execSync)(`git fetch origin ${e}`),(0,R.execSync)(`git fetch origin ${a}`);let n=(0,R.execSync)(`git merge-base ${e} origin/${a}`).toString().trim();if(!p9(n))return i.error(`Failed to get merge base of ${e} and ${a}: value returned by 'git merge-base' was not a valid git SHA ('${n}').Using the base of the pull request instead (${t}).`),null;return n}catch(n){return i.error(`Failed to get merge base of ${e} and ${a}. Error: ${n}. Using the base of the pull request instead (${t}).`),null}},p2=(e,t)=>{let a=process.env.GITHUB_SHA,i=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);if(null==a)return i.error(`No GITHUB_SHA environment var set, so can't work out true base of the merge commit. Using the base of the pull request instead (${t}).`),null;try{p4();let n=(0,R.execSync)("git rev-list --max-count=1 HEAD").toString().trim();if(n!==a)return i.info(`The head commit SHA (${n}) does not equal GITHUB_SHA environment variable (${a}). This is likely because a custom ref has been passed to the 'actions/checkout' action. We're assuming therefore that the head commit SHA is not a temporary merge commit, but rather the head of the branch. Therefore we're using the base of the pull request (${t}) to compare the visual snapshots against, and not the base of GitHub's temporary merge commit.`),null;let o=(0,R.execSync)(`git cat-file -p ${a}`).toString().split("\n").filter(e=>e.startsWith("parent ")).map(e=>e.substring(7).trim());if(2!==o.length)return i.error(`GITHUB_SHA (${a}) is not a merge commit, so can't work out true base of the merge commit. Using the base of the pull request instead.`),null;let s=o[0];if(o[1]!==e)return i.error(`The second parent (${o[1]}) of the GITHUB_SHA merge commit (${a}) is not equal to the head of the PR (${e}), so can not confidently determine the base of the merge commit to compare against. Using the base of the pull request instead (${t}).`),null;return s}catch(e){return i.error(`Error getting base of merge commit (${a}). Using the base of the pull request instead (${t}).`,e),null}},p4=()=>{(0,R.execSync)(`git config --global --add safe.directory "${process.cwd()}"`)},p9=e=>/^[a-f0-9]{40}$/.test(e),p6=(e,t)=>"push"===e?{type:"push",payload:t}:"pull_request"===e?{type:"pull_request",payload:t}:"workflow_dispatch"===e?{type:"workflow_dispatch",payload:t}:null,p3=e=>!!e&&"pull_request"===e.type&&e.payload.pull_request.title.toLowerCase().includes("[meticulous debug]");var pG=O("7SZkG");const p5=e=>{if(null==e)throw Error("github-token is required");try{return(0,eH.getOctokit)(e)}catch(e){throw F(pG).getLogger(aK.METICULOUS_LOGGER_NAME).error(e),Error("Error connecting to GitHub. Did you specify a valid 'github-token'?")}};var pG=O("7SZkG");const p8=e=>``,p7=async({octokit:e,event:t,owner:a,repo:i,body:n,shortHeadSha:o,testSuiteId:s,createIfDoesNotExist:r})=>{if("pull_request"===t.type)try{let p=await e.rest.issues.listComments({owner:a,repo:i,issue_number:t.payload.pull_request.number,per_page:1e3}),l=p8(s),d=p.data.find(e=>(e.body??"").indexOf(l)>-1),c=s?`Test suite: ${s}. `:"",u=`${n} -${c}Last updated for commit ${o}. This comment will update as new commits are pushed.${l}`;null!=d?await e.rest.issues.updateComment({owner:a,repo:i,comment_id:d.id,body:u}):r&&await e.rest.issues.createComment({owner:a,repo:i,issue_number:t.payload.pull_request.number,body:u})}catch(e){if(pR(e))throw Error(`Missing permission to list and post comments to the pull request #${t.payload.pull_request.number}. Please add the 'pull-requests: write' permission to your workflow YAML file: see ${pT} for the correct setup.`);throw F(pG).getLogger(aK.METICULOUS_LOGGER_NAME).error(`Unable to post / update comment on PR #${t.payload.pull_request.number}. ${pE}`),e}},le=()=>({apiToken:(0,L.getInput)("api-token",{required:!0}),githubToken:(0,L.getInput)("github-token",{required:!0}),appUrl:(0,L.getInput)("app-url",{required:!0})}),lt=oC.fromObject({minutes:45});(async()=>{pN();let e=await (0,s9.initSentry)("report-diffs-action-cloud-compute-v1",1),t=e.startTransaction({name:"report-diffs-action.runMeticulousTestsActionInCloud",description:"Run Meticulous tests action (in cloud)",op:"report-diffs-action.runMeticulousTestsActionInCloud"});+(process.env.RUNNER_DEBUG??"0")&&pW("trace");let{apiToken:a,githubToken:i,appUrl:n}=le(),{payload:o}=eH.context,s=p6(eH.context.eventName,o),{owner:r,repo:p}=eH.context.repo,l=p3(s),d=p5(i),c=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);if(null==s){c.warn(`Running report-diffs-action is only supported for 'push', \ +${c}Last updated for commit ${o}. This comment will update as new commits are pushed.${l}`;null!=d?await e.rest.issues.updateComment({owner:a,repo:i,comment_id:d.id,body:u}):r&&await e.rest.issues.createComment({owner:a,repo:i,issue_number:t.payload.pull_request.number,body:u})}catch(e){if(pR(e))throw Error(`Missing permission to list and post comments to the pull request #${t.payload.pull_request.number}. Please add the 'pull-requests: write' permission to your workflow YAML file: see ${pT} for the correct setup.`);throw F(pG).getLogger(aK.METICULOUS_LOGGER_NAME).error(`Unable to post / update comment on PR #${t.payload.pull_request.number}. ${pE}`),e}},le=()=>{let e=(0,L.getInput)("api-token",{required:!0});return{apiToken:e,githubToken:(0,L.getInput)("github-token",{required:!0}),appUrl:(0,L.getInput)("app-url",{required:!0}),headSha:(0,L.getInput)("head-sha")}},lt=oC.fromObject({minutes:45});(async()=>{pN();let e=await (0,s9.initSentry)("report-diffs-action-cloud-compute-v1",1),t=e.startTransaction({name:"report-diffs-action.runMeticulousTestsActionInCloud",description:"Run Meticulous tests action (in cloud)",op:"report-diffs-action.runMeticulousTestsActionInCloud"});+(process.env.RUNNER_DEBUG??"0")&&pW("trace");let{apiToken:a,githubToken:i,appUrl:n,headSha:o}=le(),{payload:s}=eH.context,r=p6(eH.context.eventName,s),{owner:p,repo:l}=eH.context.repo,d=p3(r),c=p5(i),u=F(pG).getLogger(aK.METICULOUS_LOGGER_NAME);if(null==r){u.warn(`Running report-diffs-action is only supported for 'push', \ 'pull_request' and 'workflow_dispatch' events, but was triggered \ - on a '${eH.context.eventName}' event. Skipping execution.`);return}let{base:u,head:m}=await p$(s,{useDeploymentUrl:!1}),{shaToCompareAgainst:g}=await pz({event:s,apiToken:a,base:u,useCloudReplayEnvironmentVersion:!0,context:eH.context,octokit:d});null!=g&&"pull_request"===s.type?c.info(`Comparing visual snapshots for the commit head of this PR, ${pk(m)}, against ${pk(g)}`):null!=g?c.info(`Comparing visual snapshots for commit ${pk(m)} against commit ${pk(g)}}`):c.info(`Generating visual snapshots for commit ${pk(m)}`);try{await pB(n);let i=l?(0,aK.defer)():null,o=null,u=0,g=process.env.GITHUB_SHA;if(!g)throw Error("GITHUB_SHA is not set.");await (0,sW.executeRemoteTestRun)({apiToken:a,appUrl:n,commitSha:g,environment:"github-actions",onTunnelCreated:({url:e,basicAuthUser:t,basicAuthPassword:a})=>{c.info(`Secure tunnel to ${n} created: ${e}, user: ${t}, password: ${a}`),l&&p7({octokit:d,event:s,owner:r,repo:p,body:`\u{1F916} Meticulous is running in debug mode. Secure tunnel to ${n} created: ${e} user: \`${t}\` password: \`${a}\`. + on a '${eH.context.eventName}' event. Skipping execution.`);return}let{base:m,head:g}=await p$(r,{useDeploymentUrl:!1,headSha:o}),{shaToCompareAgainst:h}=await pz({event:r,apiToken:a,base:m,useCloudReplayEnvironmentVersion:!0,context:eH.context,octokit:c});null!=h&&"pull_request"===r.type?u.info(`Comparing visual snapshots for the commit head of this PR, ${pk(g)}, against ${pk(h)}`):null!=h?u.info(`Comparing visual snapshots for commit ${pk(g)} against commit ${pk(h)}}`):u.info(`Generating visual snapshots for commit ${pk(g)}`);try{await pB(n);let i=d?(0,aK.defer)():null,o=null,s=0,m=process.env.GITHUB_SHA;if(!m)throw Error("GITHUB_SHA is not set.");await (0,sW.executeRemoteTestRun)({apiToken:a,appUrl:n,commitSha:m,environment:"github-actions",onTunnelCreated:({url:e,basicAuthUser:t,basicAuthPassword:a})=>{u.info(`Secure tunnel to ${n} created: ${e}, user: ${t}, password: ${a}`),d&&p7({octokit:c,event:r,owner:p,repo:l,body:`\u{1F916} Meticulous is running in debug mode. Secure tunnel to ${n} created: ${e} user: \`${t}\` password: \`${a}\`. -Tunnel will be live for up to ${lt.toHuman()}. Cancel the workflow run to close the tunnel early.`,testSuiteId:"__meticulous_debug__",shortHeadSha:pq(m),createIfDoesNotExist:!0}).catch(e=>{c.error(e)})},onTestRunCreated:e=>{c.info(`Test run created: ${e.url}`)},onProgressUpdate:e=>{(0,aI.IN_PROGRESS_TEST_RUN_STATUS).includes(e.status)||!i||o||(c.info(`Test run execution completed. Keeping tunnel open for ${lt.toHuman()}`),o=setTimeout(()=>{i.resolve()},lt.as("milliseconds")));let t=e.configData.testCases?.length||0,a=e.resultData?.results?.length||0;a!=u&&t&&(c.info(`Executed ${a}/${t} test cases`),u=a)},...i?{keepTunnelOpenPromise:i.promise}:{}}),t.setStatus("ok"),t.finish(),await e.getClient()?.close(5e3),process.exit(0)}catch(i){let a=i instanceof Error?i.message:`${i}`;(0,L.setFailed)(a),t.setStatus("unknown_error"),t.finish(),await e.getClient()?.close(5e3),process.exit(1)}})().catch(e=>{let t=e instanceof Error?e.message:`${e}`;(0,L.setFailed)(t),process.exit(1)}); \ No newline at end of file +Tunnel will be live for up to ${lt.toHuman()}. Cancel the workflow run to close the tunnel early.`,testSuiteId:"__meticulous_debug__",shortHeadSha:pq(g),createIfDoesNotExist:!0}).catch(e=>{u.error(e)})},onTestRunCreated:e=>{u.info(`Test run created: ${e.url}`)},onProgressUpdate:e=>{(0,aI.IN_PROGRESS_TEST_RUN_STATUS).includes(e.status)||!i||o||(u.info(`Test run execution completed. Keeping tunnel open for ${lt.toHuman()}`),o=setTimeout(()=>{i.resolve()},lt.as("milliseconds")));let t=e.configData.testCases?.length||0,a=e.resultData?.results?.length||0;a!=s&&t&&(u.info(`Executed ${a}/${t} test cases`),s=a)},...i?{keepTunnelOpenPromise:i.promise}:{}}),t.setStatus("ok"),t.finish(),await e.getClient()?.close(5e3),process.exit(0)}catch(i){let a=i instanceof Error?i.message:`${i}`;(0,L.setFailed)(a),t.setStatus("unknown_error"),t.finish(),await e.getClient()?.close(5e3),process.exit(1)}})().catch(e=>{let t=e instanceof Error?e.message:`${e}`;(0,L.setFailed)(t),process.exit(1)}); \ No newline at end of file diff --git a/src/actions/cloud-compute/cloud-compute.ts b/src/actions/cloud-compute/cloud-compute.ts index 3944f80e..0e102607 100644 --- a/src/actions/cloud-compute/cloud-compute.ts +++ b/src/actions/cloud-compute/cloud-compute.ts @@ -42,7 +42,7 @@ export const runMeticulousTestsCloudComputeAction = async (): Promise => { setLogLevel("trace"); } - const { apiToken, githubToken, appUrl } = getInCloudActionInputs(); + const { apiToken, githubToken, appUrl, headSha } = getInCloudActionInputs(); const { payload } = context; const event = getCodeChangeEvent(context.eventName, payload); const { owner, repo } = context.repo; @@ -61,6 +61,7 @@ export const runMeticulousTestsCloudComputeAction = async (): Promise => { const { base, head } = await getBaseAndHeadCommitShas(event, { useDeploymentUrl: false, + headSha, }); const { shaToCompareAgainst } = await safeEnsureBaseTestsExists({ diff --git a/src/actions/cloud-compute/get-inputs.ts b/src/actions/cloud-compute/get-inputs.ts index 486aa4e4..8342ec5e 100644 --- a/src/actions/cloud-compute/get-inputs.ts +++ b/src/actions/cloud-compute/get-inputs.ts @@ -5,10 +5,12 @@ export const getInCloudActionInputs = () => { const apiToken = getInput("api-token", { required: true }); const githubToken = getInput("github-token", { required: true }); const appUrl = getInput("app-url", { required: true }); + const headSha = getInput("head-sha"); return { apiToken, githubToken, appUrl, + headSha, }; }; diff --git a/src/actions/main/__tests__/get-inputs.spec.ts b/src/actions/main/__tests__/get-inputs.spec.ts index d6b3dbeb..2baed49d 100644 --- a/src/actions/main/__tests__/get-inputs.spec.ts +++ b/src/actions/main/__tests__/get-inputs.spec.ts @@ -14,6 +14,7 @@ const keys = [ "USE_DEPLOYMENT_URL", "ALLOWED_ENVIRONMENTS", "ADDITIONAL_PORTS", + "HEAD_SHA", ]; const EXPECTED_DEFAULT_VALUES = { @@ -30,6 +31,7 @@ const EXPECTED_DEFAULT_VALUES = { testsFile: null, testSuiteId: null, additionalPorts: null, + headSha: null, }; describe("getMainActionInputs", () => { diff --git a/src/actions/main/get-inputs.ts b/src/actions/main/get-inputs.ts index e771f59e..f486bfea 100644 --- a/src/actions/main/get-inputs.ts +++ b/src/actions/main/get-inputs.ts @@ -67,6 +67,11 @@ export const getMainActionInputs = () => { required: false, type: "string", }); + const headSha = getInputFromEnv({ + name: "head-sha", + required: false, + type: "string", + }); if (appUrl != null && appUrl != "" && useDeploymentUrl === true) { throw new Error("Cannot use both app-url and use-deployment-url"); @@ -99,5 +104,6 @@ export const getMainActionInputs = () => { testSuiteId, allowedEnvironments, additionalPorts, + headSha, }; }; diff --git a/src/actions/main/main.ts b/src/actions/main/main.ts index 04c76331..e37d8912 100644 --- a/src/actions/main/main.ts +++ b/src/actions/main/main.ts @@ -61,6 +61,7 @@ export const runMeticulousTestsAction = async (): Promise => { allowedEnvironments, testSuiteId, additionalPorts, + headSha, } = getMainActionInputs(); const { payload } = context; const event = getCodeChangeEvent(context.eventName, payload); @@ -79,6 +80,7 @@ export const runMeticulousTestsAction = async (): Promise => { const { base, head } = await getBaseAndHeadCommitShas(event, { useDeploymentUrl, + headSha, }); const environment = getEnvironment({ event, head }); diff --git a/src/common/get-base-and-head-commit-shas.ts b/src/common/get-base-and-head-commit-shas.ts index 7583778d..380279b8 100644 --- a/src/common/get-base-and-head-commit-shas.ts +++ b/src/common/get-base-and-head-commit-shas.ts @@ -11,10 +11,13 @@ interface BaseAndHeadCommitShas { export const getBaseAndHeadCommitShas = async ( event: CodeChangeEvent, - options: { useDeploymentUrl: boolean } + options: { + useDeploymentUrl: boolean; + headSha: string | null; + } ): Promise => { if (event.type === "pull_request") { - const head = event.payload.pull_request.head.sha; + const head = options.headSha || event.payload.pull_request.head.sha; const base = event.payload.pull_request.base.sha; const baseRef = event.payload.pull_request.base.ref; if (options.useDeploymentUrl) { @@ -34,13 +37,13 @@ export const getBaseAndHeadCommitShas = async ( if (event.type === "push") { return { base: event.payload.before, - head: event.payload.after, + head: options.headSha || event.payload.after, }; } if (event.type === "workflow_dispatch") { return { base: null, - head: context.sha, + head: options.headSha || context.sha, }; } return assertNever(event);