@@ -19,7 +19,8 @@ import { ErrorMessage } from './parsing/quintParserFrontend'
19
19
import path from 'path'
20
20
import fs from 'fs'
21
21
import os from 'os'
22
- import semver from 'semver'
22
+ // TODO: used by GitHub api approach: https://github.com/informalsystems/quint/issues/1124
23
+ // import semver from 'semver'
23
24
import fetch from 'node-fetch'
24
25
import { pipeline } from 'stream/promises'
25
26
import child_process from 'child_process'
@@ -30,14 +31,16 @@ import * as protobufDescriptor from 'protobufjs/ext/descriptor'
30
31
import { setTimeout } from 'timers/promises'
31
32
import { promisify } from 'util'
32
33
import { ItfTrace } from './itf'
33
- import { request as octokitRequest } from '@octokit/request'
34
+ // TODO: used by GitHub api approach: https://github.com/informalsystems/quint/issues/1124
35
+ // import { request as octokitRequest } from '@octokit/request'
34
36
35
37
import type { Buffer } from 'buffer'
36
38
import type { PackageDefinition as ProtoPackageDefinition } from '@grpc/proto-loader'
37
39
38
40
const APALACHE_SERVER_URI = 'localhost:8822'
39
- const APALACHE_VERSION_TAG = '0.42.x'
40
- const APALACHE_TGZ = 'apalache.tgz'
41
+ const APALACHE_VERSION_TAG = '0.42.0'
42
+ // TODO: used by GitHub api approach: https://github.com/informalsystems/quint/issues/1124
43
+ // const APALACHE_TGZ = 'apalache.tgz'
41
44
42
45
// The structure used to report errors
43
46
type VerifyError = {
@@ -46,6 +49,14 @@ type VerifyError = {
46
49
traces ?: ItfTrace [ ]
47
50
}
48
51
52
+ function quintConfigDir ( ) : string {
53
+ return path . join ( os . homedir ( ) , '.quint' )
54
+ }
55
+
56
+ function apalacheDistDir ( ) : string {
57
+ return path . join ( quintConfigDir ( ) , `apalache-dist-${ APALACHE_VERSION_TAG } ` )
58
+ }
59
+
49
60
export type VerifyResult < T > = Either < VerifyError , T >
50
61
51
62
// An object representing the Apalache configuration
@@ -260,6 +271,21 @@ async function tryConnect(retry: boolean = false): Promise<VerifyResult<Apalache
260
271
return ( await loadProtoDefViaReflection ( retry ) ) . map ( loadGrpcClient ) . map ( apalache )
261
272
}
262
273
274
+ function downloadAndUnpackApalache ( ) : Promise < VerifyResult < null > > {
275
+ const url = `https://github.com/informalsystems/apalache/releases/download/v${ APALACHE_VERSION_TAG } /apalache.tgz`
276
+ console . log ( `Downloading Apalache distribution from ${ url } ...` )
277
+ return fetch ( url )
278
+ . then (
279
+ // unpack response body
280
+ res => pipeline ( res . body , tar . extract ( { cwd : apalacheDistDir ( ) , strict : true } ) ) ,
281
+ error => err ( `Error fetching ${ url } : ${ error } ` )
282
+ )
283
+ . then (
284
+ _ => right ( null ) ,
285
+ error => err ( `Error unpacking .tgz: ${ error } ` )
286
+ )
287
+ }
288
+
263
289
/**
264
290
* Fetch the latest Apalache release pinned by `APALACHE_VERSION_TAG` from Github.
265
291
*
@@ -268,43 +294,59 @@ async function tryConnect(retry: boolean = false): Promise<VerifyResult<Apalache
268
294
* - a `left<VerifyError>` indicating an error.
269
295
*/
270
296
async function fetchApalache ( ) : Promise < VerifyResult < string > > {
297
+ const apalacheBinary = path . join ( apalacheDistDir ( ) , 'apalache' , 'bin' , 'apalache-mc' )
298
+ if ( fs . existsSync ( apalacheBinary ) ) {
299
+ // Use existing download
300
+ console . log ( `Using existing Apalache distribution in ${ apalacheBinary } ` )
301
+ return right ( apalacheBinary )
302
+ } else {
303
+ fs . mkdirSync ( apalacheDistDir ( ) , { recursive : true } )
304
+ const res = await downloadAndUnpackApalache ( )
305
+ return res . map ( _ => apalacheBinary )
306
+ }
307
+
308
+ // TODO: This logic makes the CLI tool extremely sensitive to environment.
309
+ // See https://github.com/informalsystems/quint/issues/1124
271
310
// Fetch Github releases
272
- return octokitRequest ( 'GET /repos/informalsystems/apalache/releases' ) . then (
273
- async resp => {
274
- // Find latest that satisfies `APALACHE_VERSION_TAG`
275
- const versions = resp . data . map ( ( element : any ) => element . tag_name )
276
- const latestTaggedVersion = semver . maxSatisfying ( versions , APALACHE_VERSION_TAG )
277
- // Check if we have already downloaded this release
278
- const unpackPath = path . join ( os . homedir ( ) , '.quint' , `apalache-dist-${ latestTaggedVersion } ` )
279
- const apalacheBinary = path . join ( unpackPath , 'apalache' , 'bin' , 'apalache-mc' )
280
- if ( fs . existsSync ( apalacheBinary ) ) {
281
- // Use existing download
282
- console . log ( `Using existing Apalache distribution in ${ unpackPath } ` )
283
- return right ( unpackPath )
284
- } else {
285
- // No existing download, download Apalache dist
286
- fs . mkdirSync ( unpackPath , { recursive : true } )
287
-
288
- // Filter release response to get dist archive asset URL
289
- const taggedRelease = resp . data . find ( ( element : any ) => element . tag_name == latestTaggedVersion )
290
- const tgzAsset = taggedRelease . assets . find ( ( asset : any ) => asset . name == APALACHE_TGZ )
291
- const downloadUrl = tgzAsset . browser_download_url
292
-
293
- console . log ( `Downloading Apalache distribution from ${ downloadUrl } ...` )
294
- return fetch ( downloadUrl )
295
- . then (
296
- // unpack response body
297
- res => pipeline ( res . body , tar . extract ( { cwd : unpackPath , strict : true } ) ) ,
298
- error => err ( `Error fetching ${ downloadUrl } : ${ error } ` )
299
- )
300
- . then (
301
- _ => right ( unpackPath ) ,
302
- error => err ( `Error unpacking .tgz: ${ error } ` )
303
- )
304
- }
305
- } ,
306
- error => err ( `Error listing the Apalache releases: ${ error } ` )
307
- )
311
+ // return octokitRequest('GET /repos/informalsystems/apalache/releases').then(
312
+ // async resp => {
313
+ // // Find latest that satisfies `APALACHE_VERSION_TAG`
314
+ // const versions = resp.data.map((element: any) => element.tag_name)
315
+ // const latestTaggedVersion = semver.parse(semver.maxSatisfying(versions, APALACHE_VERSION_TAG))
316
+ // if (latestTaggedVersion === null) {
317
+ // return err(`Failed to deteremine a valid semver version vesion from ${versions} and ${APALACHE_VERSION_TAG}`)
318
+ // }
319
+ // // Check if we have already downloaded this release
320
+ // const unpackPath = apalacheDistDir()
321
+ // const apalacheBinary = path.join(unpackPath, 'apalache', 'bin', 'apalache-mc')
322
+ // if (fs.existsSync(apalacheBinary)) {
323
+ // // Use existing download
324
+ // console.log(`Using existing Apalache distribution in ${unpackPath}`)
325
+ // return right(unpackPath)
326
+ // } else {
327
+ // // No existing download, download Apalache dist
328
+ // fs.mkdirSync(unpackPath, { recursive: true })
329
+
330
+ // // Filter release response to get dist archive asset URL
331
+ // const taggedRelease = resp.data.find((element: any) => element.tag_name == latestTaggedVersion)
332
+ // const tgzAsset = taggedRelease.assets.find((asset: any) => asset.name == APALACHE_TGZ)
333
+ // const downloadUrl = tgzAsset.browser_download_url
334
+
335
+ // console.log(`Downloading Apalache distribution from ${downloadUrl}...`)
336
+ // return fetch(downloadUrl)
337
+ // .then(
338
+ // // unpack response body
339
+ // res => pipeline(res.body, tar.extract({ cwd: unpackPath, strict: true })),
340
+ // error => err(`Error fetching ${downloadUrl}: ${error}`)
341
+ // )
342
+ // .then(
343
+ // _ => right(unpackPath),
344
+ // error => err(`Error unpacking .tgz: ${error}`)
345
+ // )
346
+ // }
347
+ // },
348
+ // error => err(`Error listing the Apalache releases: ${error}`)
349
+ // )
308
350
}
309
351
310
352
/**
@@ -327,15 +369,14 @@ async function connect(): Promise<VerifyResult<Apalache>> {
327
369
328
370
// Connection or pinging failed, download Apalache
329
371
console . log ( "Couldn't connect to Apalache, checking for latest supported release" )
330
- const distDir = await fetchApalache ( )
372
+ const exeResult = await fetchApalache ( )
331
373
// Launch Apalache from download
332
- return distDir
374
+ return exeResult
333
375
. asyncChain (
334
- async distDir =>
376
+ async exe =>
335
377
new Promise < VerifyResult < void > > ( ( resolve , _ ) => {
336
378
console . log ( 'Launching Apalache server' )
337
- const apalacheBinary = path . join ( distDir , 'apalache' , 'bin' , 'apalache-mc' )
338
- const apalache = child_process . spawn ( apalacheBinary , [ 'server' ] )
379
+ const apalache = child_process . spawn ( exe , [ 'server' ] )
339
380
340
381
// Exit handler that kills Apalache if Quint exists
341
382
function exitHandler ( ) {
0 commit comments