@@ -8,19 +8,149 @@ const FS = require("fs").promises;
88
99async function run ( ) {
1010 try {
11- const path = await downloadCrystalForWindows ( ) ;
12- await setupCrystalForWindows ( path ) ;
11+ const params = { } ;
12+ for ( const key of [ "crystal" , "arch" , "destination" ] ) {
13+ let value ;
14+ if ( ( value = Core . getInput ( key ) ) ) {
15+ params [ key ] = value ;
16+ }
17+ }
18+ const func = {
19+ [ Linux ] : installCrystalForLinux ,
20+ [ Mac ] : installCrystalForMac ,
21+ [ Windows ] : installCrystalForWindows ,
22+ } [ getPlatform ( ) ] ;
23+ if ( ! func ) {
24+ throw `Platform "${ getPlatform ( ) } " is not supported` ;
25+ }
26+ await func ( params ) ;
1327 } catch ( error ) {
1428 Core . setFailed ( error ) ;
29+ process . exit ( 1 ) ;
1530 }
1631}
1732
33+ const Linux = "Linux" , Mac = "macOS" , Windows = "Windows" ;
34+
35+ function getPlatform ( ) {
36+ const platform = process . env [ "INSTALL_CRYSTAL_PLATFORM" ] || process . platform ;
37+ return { "linux" : Linux , "darwin" : Mac , "win32" : Windows } [ platform ] || platform ;
38+ }
39+
40+ function getArch ( ) {
41+ return { "ia32" : "x86" , "x64" : "x86_64" } [ process . arch ] || process . arch ;
42+ }
43+
44+ function checkArch ( arch , allowed ) {
45+ if ( ! allowed . includes ( arch ) ) {
46+ throw `Architecture "${ arch } " is not supported on ${ getPlatform ( ) } ` ;
47+ }
48+ }
49+
50+ const Latest = "latest" ;
51+ const Nightly = "nightly" ;
52+ const NumericVersion = / ^ \d [ . \d ] + \d $ / ;
53+
54+ function checkVersion ( version , allowed ) {
55+ const numericVersion = version . match ( NumericVersion ) && version ;
56+ allowed [ allowed . indexOf ( NumericVersion ) ] = numericVersion ;
57+
58+ if ( allowed . includes ( version ) ) {
59+ return version ;
60+ }
61+ if ( [ Latest , Nightly , numericVersion ] . includes ( version ) ) {
62+ throw `Version "${ version } " is not supported on ${ getPlatform ( ) } ` ;
63+ }
64+ throw `Version "${ version } " is invalid` ;
65+ }
66+
67+ async function installCrystalForLinux ( {
68+ crystal = Latest ,
69+ arch = getArch ( ) ,
70+ destination = null ,
71+ } ) {
72+ checkVersion ( crystal , [ Latest , NumericVersion ] ) ;
73+ const suffixes = { "x86_64" : "linux-x86_64" , "x86" : "linux-i686" } ;
74+ checkArch ( arch , Object . keys ( suffixes ) ) ;
75+
76+ return Promise . all ( [
77+ installAptPackages (
78+ "libevent-dev libgmp-dev libpcre3-dev libssl-dev libxml2-dev libyaml-dev" . split ( " " ) ,
79+ ) ,
80+ installBinaryRelease ( { crystal, suffix : suffixes [ arch ] , destination} ) ,
81+ ] ) ;
82+ }
83+
84+ async function installCrystalForMac ( {
85+ crystal = Latest ,
86+ arch = "x86_64" ,
87+ destination = null ,
88+ } ) {
89+ checkVersion ( crystal , [ Latest , NumericVersion ] ) ;
90+ checkArch ( arch , [ "x86_64" ] ) ;
91+ return installBinaryRelease ( { crystal, suffix : "darwin-x86_64" , destination} ) ;
92+ }
93+
94+ async function installAptPackages ( packages ) {
95+ const execFile = Util . promisify ( ChildProcess . execFile ) ;
96+ Core . info ( "Installing package dependencies" ) ;
97+ const args = [
98+ "-n" , "apt-get" , "install" , "-qy" , "--no-install-recommends" , "--no-upgrade" , "--" ,
99+ ] . concat ( packages ) ;
100+ Core . info ( "[command]sudo " + args . join ( " " ) ) ;
101+ const { stdout} = await execFile ( "sudo" , args ) ;
102+ Core . startGroup ( "Finished installing package dependencies" ) ;
103+ Core . info ( stdout ) ;
104+ Core . endGroup ( ) ;
105+ }
106+
107+ async function installBinaryRelease ( { crystal, suffix, destination} ) {
108+ if ( crystal === Latest ) {
109+ crystal = null ;
110+ }
111+ const path = await downloadCrystalRelease ( { suffix, tag : crystal , destination} ) ;
112+
113+ Core . info ( "Setting up environment" ) ;
114+ Core . addPath ( Path . join ( path , "bin" ) ) ;
115+ }
116+
117+ async function downloadCrystalRelease ( { suffix, tag = null , destination = null } ) {
118+ Core . info ( "Looking for latest Crystal release" ) ;
119+
120+ const releasesResp = await githubGet ( {
121+ url : "/repos/crystal-lang/crystal/releases/" + ( tag ? "tags/" + tag : "latest" ) ,
122+ } ) ;
123+ const release = releasesResp . data ;
124+ Core . info ( "Found " + release [ "html_url" ] ) ;
125+ Core . setOutput ( "crystal" , release [ "tag_name" ] ) ;
126+
127+ const asset = release [ "assets" ] . find ( ( a ) => a [ "name" ] . endsWith ( [ `-${ suffix } .tar.gz` ] ) ) ;
128+
129+ Core . info ( "Downloading Crystal build" ) ;
130+ const downloadedPath = await ToolCache . downloadTool ( asset [ "browser_download_url" ] ) ;
131+
132+ Core . info ( "Extracting Crystal build" ) ;
133+ return onlySubdir ( await ToolCache . extractTar ( downloadedPath , destination ) ) ;
134+ }
135+
136+ async function installCrystalForWindows ( {
137+ crystal = Nightly ,
138+ arch = "x86_64" ,
139+ destination = null ,
140+ } ) {
141+ checkVersion ( crystal , [ Nightly ] ) ;
142+ checkArch ( arch , [ "x86_64" ] ) ;
143+ const path = await downloadCrystalForWindows ( destination ) ;
144+ await setupCrystalForWindows ( path ) ;
145+ }
146+
18147async function setupCrystalForWindows ( path ) {
19148 Core . info ( "Setting up environment" ) ;
20149 const vars = await variablesForVCBuildTools ( ) ;
21150 addPathToVars ( vars , "PATH" , path ) ;
22151 addPathToVars ( vars , "LIB" , path ) ;
23152 addPathToVars ( vars , "CRYSTAL_PATH" , Path . join ( path , "src" ) ) ;
153+ addPathToVars ( vars , "CRYSTAL_PATH" , "lib" ) ;
24154 for ( const [ k , v ] of vars . entries ( ) ) {
25155 Core . exportVariable ( k , v ) ;
26156 }
@@ -41,6 +171,7 @@ const vcvarsPath = String.raw`C:\Program Files (x86)\Microsoft Visual Studio\201
41171async function variablesForVCBuildTools ( ) {
42172 const exec = Util . promisify ( ChildProcess . exec ) ;
43173 const command = `set && echo ${ outputSep } && "${ vcvarsPath } " >nul && set` ;
174+ Core . info ( `[command]cmd /c "${ command } "` ) ;
44175 const { stdout} = await exec ( command , { shell : "cmd" } ) ;
45176 Core . debug ( JSON . stringify ( stdout ) ) ;
46177 return new Map ( getChangedVars ( stdout . trimEnd ( ) . split ( / \r ? \n / ) ) ) ;
@@ -66,7 +197,7 @@ function* getChangedVars(lines) {
66197 }
67198}
68199
69- async function downloadCrystalForWindows ( ) {
200+ async function downloadCrystalForWindows ( destination = null ) {
70201 Core . info ( "Looking for latest Crystal build" ) ;
71202
72203 const runsResp = await githubGet ( {
@@ -91,9 +222,7 @@ async function downloadCrystalForWindows() {
91222 const downloadedPath = await ToolCache . downloadTool ( downloadUrl ) ;
92223
93224 Core . info ( "Extracting Crystal source" ) ;
94- const extractedPath = await ToolCache . extractZip ( downloadedPath , destDir ) ;
95- const [ subDir ] = await FS . readdir ( extractedPath ) ;
96- return Path . join ( extractedPath , subDir ) ;
225+ return onlySubdir ( await ToolCache . extractZip ( downloadedPath , destDir ) ) ;
97226 } ;
98227
99228 const fetchExeTask = async ( ) => {
@@ -106,7 +235,6 @@ async function downloadCrystalForWindows() {
106235 const artifactLinkResp = await githubGet ( {
107236 url : "/repos/crystal-lang/crystal/actions/artifacts/:artifact_id/zip" ,
108237 "artifact_id" : artifact . id ,
109- headers : { "authorization" : "token " + Core . getInput ( "token" ) } ,
110238 request : { redirect : "manual" } ,
111239 } ) ;
112240 const downloadUrl = artifactLinkResp . headers [ "location" ] ;
@@ -116,7 +244,7 @@ async function downloadCrystalForWindows() {
116244 } ;
117245
118246 const [ srcPath , exeDownloadedPath ] = await Promise . all ( [
119- fetchSrcTask ( Core . getInput ( " destination" ) ) ,
247+ fetchSrcTask ( destination ) ,
120248 fetchExeTask ( ) ,
121249 ] ) ;
122250
@@ -126,7 +254,14 @@ async function downloadCrystalForWindows() {
126254
127255function githubGet ( request ) {
128256 Core . debug ( request ) ;
129- return Octokit . request ( request ) ;
257+ return Octokit . request . defaults ( {
258+ headers : { "authorization" : "token " + Core . getInput ( "token" ) } ,
259+ } ) ( request ) ;
260+ }
261+
262+ async function onlySubdir ( path ) {
263+ const [ subDir ] = await FS . readdir ( path ) ;
264+ return Path . join ( path , subDir ) ;
130265}
131266
132267if ( require . main === module ) {
0 commit comments