@@ -25,6 +25,13 @@ import type {
2525} from "./types" ;
2626import { isSome , unwrapOrDefault } from "./option" ;
2727
28+ /**
29+ * Check if a file is a zip archive based on its extension
30+ */
31+ function isZipFile ( filename : string ) : boolean {
32+ return filename . toLowerCase ( ) . endsWith ( '.zip' ) ;
33+ }
34+
2835function getDestinationDirectory (
2936 storageDirectory : string ,
3037 slug : RepositorySlug ,
@@ -80,33 +87,45 @@ async function installGitHubReleaseBinary(
8087 releaseAsset . binaryName ,
8188 targetRelease . slug . repository ,
8289 ) ;
83- const destinationFilename = path . join (
84- destinationDirectory ,
85- destinationBasename ,
86- ) ;
8790
91+ // Create the destination directory
8892 fs . mkdirSync ( destinationDirectory , { recursive : true } ) ;
8993
94+ // Determine if we're dealing with a zip file based on the asset name
95+ const assetName = releaseAsset . name || '' ;
96+ core . debug ( `Asset name: ${ assetName } ` ) ;
97+ const isZip = isZipFile ( assetName ) ;
98+
99+ // If it's a standard binary, use the existing destination path
100+ // If it's a zip, we'll create a temporary file path for the download
101+ const destinationFilename = isZip
102+ ? path . join ( destinationDirectory , `${ destinationBasename } .zip` )
103+ : path . join ( destinationDirectory , destinationBasename ) ;
104+
105+ // Final binary path to check/add to PATH (same for non-zip, different for zip)
106+ const finalBinaryPath = path . join ( destinationDirectory , destinationBasename ) ;
107+
90108 // Check if file already exists and skip if ignoreExisting is true
91- if ( fs . existsSync ( destinationFilename ) ) {
109+ if ( fs . existsSync ( finalBinaryPath ) ) {
92110 if ( ignoreExisting ) {
93- core . info ( `Binary already exists at ${ destinationFilename } , ignoring and leaving system as-is` ) ;
111+ core . info ( `Binary already exists at ${ finalBinaryPath } , ignoring and leaving system as-is` ) ;
94112 // Still add the directory to PATH so the binary can be found
95113 core . addPath ( destinationDirectory ) ;
96114 return ;
97115 }
98116 }
99117
100- await tc . downloadTool (
118+ // Download the file
119+ const downloadedFilePath = await tc . downloadTool (
101120 releaseAsset . url ,
102121 destinationFilename ,
103122 `token ${ token } ` ,
104123 { accept : "application/octet-stream" } ,
105124 ) ;
106125
107- // Ensure the binary matches the expected checksum
126+ // Ensure the downloaded file matches the expected checksum
108127 if ( isSome ( targetRelease . checksum ) ) {
109- const fileBuffer = fs . readFileSync ( destinationFilename ) ;
128+ const fileBuffer = fs . readFileSync ( downloadedFilePath ) ;
110129 const hash = createHash ( "sha256" ) ;
111130 hash . update ( fileBuffer ) ;
112131 const calculatedChecksum = hash . digest ( "hex" ) ;
@@ -124,9 +143,56 @@ async function installGitHubReleaseBinary(
124143 }
125144 }
126145
146+ // Process the file based on type
147+ if ( isZip ) {
148+ core . info ( `Detected zip archive based on filename: ${ assetName } ` ) ;
149+ core . info ( `Extracting zip file: ${ downloadedFilePath } ` ) ;
150+
151+ // Extract the zip file
152+ const extractedDirectory = await tc . extractZip ( downloadedFilePath , destinationDirectory ) ;
153+ core . debug ( `Files extracted to ${ extractedDirectory } ` ) ;
154+
155+ // We assume there's exactly one binary file in the archive
156+ // If there's more than one file or if it's a directory, throw an error
157+ const extractedFiles = fs . readdirSync ( extractedDirectory ) ;
158+
159+ // Filter out hidden files and directories
160+ const visibleFiles = extractedFiles . filter ( file =>
161+ ! file . startsWith ( '.' ) && ! fs . statSync ( path . join ( extractedDirectory , file ) ) . isDirectory ( )
162+ ) ;
163+
164+ if ( visibleFiles . length !== 1 ) {
165+ throw new Error ( `Expected exactly one binary in the zip archive, but found ${ visibleFiles . length } files: ${ visibleFiles . join ( ', ' ) } ` ) ;
166+ }
167+
168+ // Use the single binary file - we've already checked that there's exactly one file
169+ // TypeScript needs a non-null assertion here to know it's safe
170+ const binaryName : string = visibleFiles [ 0 ] ! ;
171+ core . debug ( `Found single binary in zip: ${ binaryName } ` ) ;
172+
173+ // Move the binary to the destination
174+ fs . renameSync (
175+ path . join ( extractedDirectory , binaryName ) ,
176+ finalBinaryPath
177+ ) ;
178+
179+ // Clean up the extracted directory
180+ if ( extractedDirectory !== destinationDirectory ) {
181+ core . debug ( `Removing temporary extraction directory: ${ extractedDirectory } ` ) ;
182+ fs . rmSync ( extractedDirectory , { recursive : true , force : true } ) ;
183+ }
184+
185+ // Clean up the zip file
186+ core . debug ( `Removing zip file: ${ downloadedFilePath } ` ) ;
187+ fs . unlinkSync ( downloadedFilePath ) ;
188+ } else {
189+ // For regular binaries, the downloaded file is already at the right location
190+ core . debug ( `Downloaded binary file: ${ downloadedFilePath } ` ) ;
191+ }
192+
127193 // Permissions are an attribute of the filesystem, not the file.
128194 // Set the executable permission on the binary no matter where it came from.
129- fs . chmodSync ( destinationFilename , "755" ) ;
195+ fs . chmodSync ( finalBinaryPath , "755" ) ;
130196 core . addPath ( destinationDirectory ) ;
131197}
132198
0 commit comments