@@ -8,19 +8,149 @@ const FS = require("fs").promises;
8
8
9
9
async function run ( ) {
10
10
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 ) ;
13
27
} catch ( error ) {
14
28
Core . setFailed ( error ) ;
29
+ process . exit ( 1 ) ;
15
30
}
16
31
}
17
32
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
+
18
147
async function setupCrystalForWindows ( path ) {
19
148
Core . info ( "Setting up environment" ) ;
20
149
const vars = await variablesForVCBuildTools ( ) ;
21
150
addPathToVars ( vars , "PATH" , path ) ;
22
151
addPathToVars ( vars , "LIB" , path ) ;
23
152
addPathToVars ( vars , "CRYSTAL_PATH" , Path . join ( path , "src" ) ) ;
153
+ addPathToVars ( vars , "CRYSTAL_PATH" , "lib" ) ;
24
154
for ( const [ k , v ] of vars . entries ( ) ) {
25
155
Core . exportVariable ( k , v ) ;
26
156
}
@@ -41,6 +171,7 @@ const vcvarsPath = String.raw`C:\Program Files (x86)\Microsoft Visual Studio\201
41
171
async function variablesForVCBuildTools ( ) {
42
172
const exec = Util . promisify ( ChildProcess . exec ) ;
43
173
const command = `set && echo ${ outputSep } && "${ vcvarsPath } " >nul && set` ;
174
+ Core . info ( `[command]cmd /c "${ command } "` ) ;
44
175
const { stdout} = await exec ( command , { shell : "cmd" } ) ;
45
176
Core . debug ( JSON . stringify ( stdout ) ) ;
46
177
return new Map ( getChangedVars ( stdout . trimEnd ( ) . split ( / \r ? \n / ) ) ) ;
@@ -66,7 +197,7 @@ function* getChangedVars(lines) {
66
197
}
67
198
}
68
199
69
- async function downloadCrystalForWindows ( ) {
200
+ async function downloadCrystalForWindows ( destination = null ) {
70
201
Core . info ( "Looking for latest Crystal build" ) ;
71
202
72
203
const runsResp = await githubGet ( {
@@ -91,9 +222,7 @@ async function downloadCrystalForWindows() {
91
222
const downloadedPath = await ToolCache . downloadTool ( downloadUrl ) ;
92
223
93
224
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 ) ) ;
97
226
} ;
98
227
99
228
const fetchExeTask = async ( ) => {
@@ -106,7 +235,6 @@ async function downloadCrystalForWindows() {
106
235
const artifactLinkResp = await githubGet ( {
107
236
url : "/repos/crystal-lang/crystal/actions/artifacts/:artifact_id/zip" ,
108
237
"artifact_id" : artifact . id ,
109
- headers : { "authorization" : "token " + Core . getInput ( "token" ) } ,
110
238
request : { redirect : "manual" } ,
111
239
} ) ;
112
240
const downloadUrl = artifactLinkResp . headers [ "location" ] ;
@@ -116,7 +244,7 @@ async function downloadCrystalForWindows() {
116
244
} ;
117
245
118
246
const [ srcPath , exeDownloadedPath ] = await Promise . all ( [
119
- fetchSrcTask ( Core . getInput ( " destination" ) ) ,
247
+ fetchSrcTask ( destination ) ,
120
248
fetchExeTask ( ) ,
121
249
] ) ;
122
250
@@ -126,7 +254,14 @@ async function downloadCrystalForWindows() {
126
254
127
255
function githubGet ( request ) {
128
256
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 ) ;
130
265
}
131
266
132
267
if ( require . main === module ) {
0 commit comments