@@ -7,13 +7,16 @@ import { fetchSupportedScanFileNames } from './fetch-supported-scan-file-names.m
77import { handleScanReport } from './handle-scan-report.mts'
88import { outputCreateNewScan } from './output-create-new-scan.mts'
99import constants from '../../constants.mts'
10+ import { handleApiCall } from '../../utils/api.mts'
1011import { checkCommandInput } from '../../utils/check-input.mts'
12+ import { spawnCoana } from '../../utils/coana.mts'
1113import { getPackageFilesForScan } from '../../utils/path-resolve.mts'
14+ import { setupSdk } from '../../utils/sdk.mts'
1215import { readOrDefaultSocketJson } from '../../utils/socketjson.mts'
1316import { detectManifestActions } from '../manifest/detect-manifest-actions.mts'
1417import { generateAutoManifest } from '../manifest/generate_auto_manifest.mts'
1518
16- import type { OutputKind } from '../../types.mts'
19+ import type { CResult , OutputKind } from '../../types.mts'
1720
1821export async function handleCreateNewScan ( {
1922 autoManifest,
@@ -28,6 +31,7 @@ export async function handleCreateNewScan({
2831 outputKind,
2932 pendingHead,
3033 pullRequest,
34+ reach,
3135 readOnly,
3236 repoName,
3337 report,
@@ -46,6 +50,7 @@ export async function handleCreateNewScan({
4650 pendingHead : boolean
4751 pullRequest : number
4852 outputKind : OutputKind
53+ reach : boolean
4954 readOnly : boolean
5055 repoName : string
5156 report : boolean
@@ -106,8 +111,30 @@ export async function handleCreateNewScan({
106111 return
107112 }
108113
114+ let scanPaths : string [ ] = packagePaths
115+
116+ // If reachability is enabled, perform reachability analysis
117+ if ( reach ) {
118+ const reachResult = await performReachabilityAnalysis ( {
119+ packagePaths,
120+ orgSlug,
121+ cwd,
122+ repoName,
123+ branchName,
124+ outputKind,
125+ interactive,
126+ } )
127+
128+ if ( ! reachResult . ok ) {
129+ await outputCreateNewScan ( reachResult , outputKind , interactive )
130+ return
131+ }
132+
133+ scanPaths = reachResult . data ?. scanPaths || [ ]
134+ }
135+
109136 const fullScanCResult = await fetchCreateOrgFullScan (
110- packagePaths ,
137+ scanPaths ,
111138 orgSlug ,
112139 {
113140 commitHash,
@@ -153,3 +180,101 @@ export async function handleCreateNewScan({
153180 await outputCreateNewScan ( fullScanCResult , outputKind , interactive )
154181 }
155182}
183+
184+ async function performReachabilityAnalysis ( {
185+ branchName,
186+ cwd,
187+ orgSlug,
188+ packagePaths,
189+ repoName,
190+ } : {
191+ packagePaths : string [ ]
192+ orgSlug : string
193+ cwd : string
194+ repoName : string
195+ branchName : string
196+ outputKind : OutputKind
197+ interactive : boolean
198+ } ) : Promise < CResult < { scanPaths ?: string [ ] } > > {
199+ logger . info ( 'Starting reachability analysis...' )
200+
201+ packagePaths = packagePaths . filter (
202+ p =>
203+ /* Exclude DOT_SOCKET_DOT_FACTS_JSON from previous runs */ ! p . includes (
204+ constants . DOT_SOCKET_DOT_FACTS_JSON ,
205+ ) ,
206+ )
207+
208+ // Lazily access constants.spinner.
209+ const { spinner } = constants
210+
211+ // Setup SDK for uploading manifests
212+ const sockSdkCResult = await setupSdk ( )
213+ if ( ! sockSdkCResult . ok ) {
214+ return sockSdkCResult
215+ }
216+ const sockSdk = sockSdkCResult . data
217+
218+ // Upload manifests to get tar hash
219+ spinner . start ( 'Uploading manifests for reachability analysis...' )
220+ const uploadCResult = await handleApiCall (
221+ sockSdk . uploadManifestFiles ( orgSlug , packagePaths ) ,
222+ { desc : 'upload manifests' } ,
223+ )
224+ spinner . stop ( )
225+
226+ if ( ! uploadCResult . ok ) {
227+ return uploadCResult
228+ }
229+
230+ const tarHash = ( uploadCResult . data as { tarHash ?: string } ) ?. tarHash
231+ if ( ! tarHash ) {
232+ return {
233+ ok : false ,
234+ message : 'Failed to get manifest tar hash' ,
235+ cause : 'Server did not return a tar hash for the uploaded manifests' ,
236+ }
237+ }
238+
239+ logger . success ( `Manifests uploaded successfully. Tar hash: ${ tarHash } ` )
240+
241+ // Run Coana with the manifests tar hash
242+ logger . info ( 'Running reachability analysis with Coana...' )
243+ const coanaResult = await spawnCoana (
244+ [
245+ 'run' ,
246+ cwd ,
247+ '--output-dir' ,
248+ cwd ,
249+ '--socket-mode' ,
250+ constants . DOT_SOCKET_DOT_FACTS_JSON ,
251+ '--disable-report-submission' ,
252+ '--manifests-tar-hash' ,
253+ tarHash ,
254+ ] ,
255+ {
256+ cwd,
257+ stdio : 'inherit' ,
258+ env : {
259+ ...process . env ,
260+ SOCKET_REPO_NAME : repoName ,
261+ SOCKET_BRANCH_NAME : branchName ,
262+ SOCKET_CLI_VERSION : constants . ENV . INLINED_SOCKET_CLI_VERSION ,
263+ } ,
264+ } ,
265+ )
266+
267+ if ( ! coanaResult . ok ) {
268+ return coanaResult
269+ }
270+
271+ logger . success ( 'Reachability analysis completed successfully' )
272+
273+ // Use the DOT_SOCKET_DOT_FACTS_JSON file for the scan
274+ return {
275+ ok : true ,
276+ data : {
277+ scanPaths : [ constants . DOT_SOCKET_DOT_FACTS_JSON ] ,
278+ } ,
279+ }
280+ }
0 commit comments