@@ -18,15 +18,15 @@ import {
1818 parsePyprojectDependencies ,
1919 resolvePythonEntryPoint
2020} from '../utils/python-helpers' ;
21+ import {
22+ parseGitHubUrl ,
23+ resolvePackageEntry ,
24+ detectRuntime ,
25+ type GitHubInfo
26+ } from '../utils/node-helpers' ;
2127
22- /**
23- * Parsed GitHub repository information
24- */
25- export interface GitHubInfo {
26- owner : string ;
27- repo : string ;
28- ref : string ;
29- }
28+ // Re-export GitHubInfo for backward compatibility
29+ export type { GitHubInfo } ;
3030
3131/**
3232 * Helper to check if a file exists
@@ -58,52 +58,6 @@ export class GitHubDeploymentHandler {
5858 this . tmpfsManager = new TmpfsManager ( logger ) ;
5959 }
6060
61- /**
62- * Parse GitHub URL from package manager arguments
63- * Supports:
64- * - github:owner/repo#ref (for npx)
65- * - git+https://github.com/owner/repo.git@ref (for uvx/pip)
66- */
67- parseGitHubUrl ( command : string , args : string [ ] ) : GitHubInfo | null {
68- // Check if this is a supported package manager command
69- const supportedCommands = [ 'npx' , 'uvx' ] ;
70- if ( ! supportedCommands . includes ( command ) ) {
71- return null ;
72- }
73-
74- // Try npm/npx format: github:owner/repo#ref
75- const githubArg = args . find ( arg => arg . startsWith ( 'github:' ) ) ;
76- if ( githubArg ) {
77- const match = githubArg . match ( / ^ g i t h u b : ( [ ^ / ] + ) \/ ( [ ^ # ] + ) # ( .+ ) $ / ) ;
78- if ( match ) {
79- return {
80- owner : match [ 1 ] ,
81- repo : match [ 2 ] ,
82- ref : match [ 3 ]
83- } ;
84- }
85- }
86-
87- // Try Python/uvx format: git+https://github.com/owner/repo.git@ref
88- const gitPlusArg = args . find ( arg => arg . startsWith ( 'git+https://github.com/' ) ) ;
89- if ( gitPlusArg ) {
90- const match = gitPlusArg . match ( / ^ g i t \+ h t t p s : \/ \/ g i t h u b \. c o m \/ ( [ ^ / ] + ) \/ ( [ ^ . ] + ) \. g i t @ ( .+ ) $ / ) ;
91- if ( match ) {
92- return {
93- owner : match [ 1 ] ,
94- repo : match [ 2 ] ,
95- ref : match [ 3 ]
96- } ;
97- }
98- }
99-
100- this . logger . warn ( {
101- operation : 'github_url_parse_failed' ,
102- args,
103- command
104- } , `Failed to parse GitHub URL from ${ command } arguments` ) ;
105- return null ;
106- }
10761
10862 /**
10963 * Check if a config requires GitHub deployment
@@ -554,79 +508,6 @@ export class GitHubDeploymentHandler {
554508 }
555509 }
556510
557- /**
558- * Resolve package entry point from package.json
559- */
560- async resolvePackageEntry ( tempDir : string ) : Promise < string > {
561- this . logger . debug ( {
562- operation : 'package_entry_resolve_start' ,
563- temp_dir : tempDir
564- } , 'Resolving package entry point from package.json' ) ;
565-
566- try {
567- const packageJsonPath = path . join ( tempDir , 'package.json' ) ;
568- const packageJsonContent = await fs . promises . readFile ( packageJsonPath , 'utf8' ) ;
569- const packageJson = JSON . parse ( packageJsonContent ) ;
570-
571- // Check for bin field (can be string or object)
572- if ( packageJson . bin ) {
573- let entryPoint : string ;
574-
575- if ( typeof packageJson . bin === 'string' ) {
576- // bin: "dist/index.js"
577- entryPoint = packageJson . bin ;
578- } else if ( typeof packageJson . bin === 'object' ) {
579- // bin: { "server-name": "dist/index.js" }
580- // Use the first entry
581- const binEntries = Object . values ( packageJson . bin ) ;
582- if ( binEntries . length === 0 ) {
583- throw new Error ( 'bin field is empty object' ) ;
584- }
585- entryPoint = binEntries [ 0 ] as string ;
586- } else {
587- throw new Error ( `Invalid bin field type: ${ typeof packageJson . bin } ` ) ;
588- }
589-
590- const absolutePath = path . join ( tempDir , entryPoint ) ;
591-
592- this . logger . info ( {
593- operation : 'package_entry_resolved' ,
594- temp_dir : tempDir ,
595- entry_point : entryPoint ,
596- absolute_path : absolutePath
597- } , `Resolved package entry point: ${ entryPoint } ` ) ;
598-
599- return absolutePath ;
600- }
601-
602- // Fallback to main field
603- if ( packageJson . main ) {
604- const absolutePath = path . join ( tempDir , packageJson . main ) ;
605-
606- this . logger . info ( {
607- operation : 'package_entry_resolved_main' ,
608- temp_dir : tempDir ,
609- main : packageJson . main ,
610- absolute_path : absolutePath
611- } , `Using main field as entry point: ${ packageJson . main } ` ) ;
612-
613- return absolutePath ;
614- }
615-
616- throw new Error ( 'No bin or main field found in package.json' ) ;
617-
618- } catch ( error ) {
619- const errorMessage = error instanceof Error ? error . message : String ( error ) ;
620-
621- this . logger . error ( {
622- operation : 'package_entry_resolve_failed' ,
623- temp_dir : tempDir ,
624- error : errorMessage
625- } , 'Failed to resolve package entry point' ) ;
626-
627- throw new Error ( `Failed to resolve package entry point: ${ errorMessage } ` ) ;
628- }
629- }
630511
631512
632513 /**
@@ -1054,23 +935,6 @@ export class GitHubDeploymentHandler {
1054935 return result ;
1055936 }
1056937
1057- /**
1058- * Detect runtime from extracted repository files
1059- */
1060- async detectRuntime ( tempDir : string ) : Promise < 'node' | 'python' | 'unknown' > {
1061- // Check for Node.js
1062- if ( await fileExists ( path . join ( tempDir , 'package.json' ) ) ) {
1063- return 'node' ;
1064- }
1065-
1066- // Check for Python
1067- if ( await fileExists ( path . join ( tempDir , 'pyproject.toml' ) ) ||
1068- await fileExists ( path . join ( tempDir , 'requirements.txt' ) ) ) {
1069- return 'python' ;
1070- }
1071-
1072- return 'unknown' ;
1073- }
1074938
1075939 /**
1076940 * Prepare a GitHub deployment - downloads, extracts, builds, and updates config
@@ -1095,7 +959,7 @@ export class GitHubDeploymentHandler {
1095959 } , `GitHub deployment detected (${ config . runtime || 'unknown' } runtime), downloading repository via Octokit` ) ;
1096960
1097961 // Parse GitHub URL from package manager arguments
1098- const githubInfo = this . parseGitHubUrl ( config . command , config . args || [ ] ) ;
962+ const githubInfo = parseGitHubUrl ( config . command , config . args || [ ] , this . logger ) ;
1099963 if ( ! githubInfo ) {
1100964 throw new Error ( 'Failed to parse GitHub URL from package manager arguments' ) ;
1101965 }
@@ -1178,7 +1042,7 @@ export class GitHubDeploymentHandler {
11781042 await this . extractTarball ( tarballBuffer , deploymentDir ) ;
11791043
11801044 // Detect runtime from extracted files or use config runtime
1181- const runtime = config . runtime || await this . detectRuntime ( deploymentDir ) ;
1045+ const runtime = config . runtime || await detectRuntime ( deploymentDir ) ;
11821046
11831047 this . logger . info ( {
11841048 operation : 'github_deployment_runtime_detected' ,
@@ -1214,7 +1078,7 @@ export class GitHubDeploymentHandler {
12141078 await this . buildPackage ( deploymentDir , config . installation_id , config . team_id , config . user_id ) ;
12151079
12161080 // Resolve Node.js package entry point
1217- const entryPoint = await this . resolvePackageEntry ( deploymentDir ) ;
1081+ const entryPoint = await resolvePackageEntry ( deploymentDir , this . logger ) ;
12181082
12191083 // Make entry point relative to deployment dir for nsjail mounting
12201084 const relativeEntryPoint = path . relative ( deploymentDir , entryPoint ) ;
0 commit comments