Skip to content

Commit 8839369

Browse files
authored
Compute supported compiler versions for a single package (#632)
1 parent a8f0d97 commit 8839369

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed

scripts/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,5 @@ in {
5959
package-transferrer = build-script "registry-package-transferrer" "PackageTransferrer";
6060
solver = build-script "registry-solver" "Solver";
6161
verify-integrity = build-script "registry-verify-integrity" "VerifyIntegrity";
62+
compiler-versions = build-script "registry-compiler-versions" "CompilerVersions";
6263
}

scripts/src/CompilerVersions.purs

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
module Registry.Scripts.CompilerVersions where
2+
3+
import Registry.App.Prelude
4+
5+
import ArgParse.Basic (ArgParser)
6+
import ArgParse.Basic as Arg
7+
import Data.Array as Array
8+
import Data.Array.NonEmpty as NEA
9+
import Data.Formatter.DateTime as Formatter.DateTime
10+
import Data.List (filterM)
11+
import Data.Map as Map
12+
import Data.String as String
13+
import Data.Tuple (uncurry)
14+
import Effect.Class.Console as Console
15+
import Node.FS.Aff as FS.Aff
16+
import Node.Path as Path
17+
import Node.Process as Process
18+
import Registry.App.CLI.Git as Git
19+
import Registry.App.CLI.Purs as Purs
20+
import Registry.App.CLI.PursVersions as PursVersions
21+
import Registry.App.CLI.Tar as Tar
22+
import Registry.App.Effect.Cache as Cache
23+
import Registry.App.Effect.Env as Env
24+
import Registry.App.Effect.GitHub as GitHub
25+
import Registry.App.Effect.Log (LOG)
26+
import Registry.App.Effect.Log as Log
27+
import Registry.App.Effect.Registry (REGISTRY)
28+
import Registry.App.Effect.Registry as Registry
29+
import Registry.App.Effect.Storage (STORAGE)
30+
import Registry.App.Effect.Storage as Storage
31+
import Registry.Foreign.FSExtra as FS.Extra
32+
import Registry.Foreign.Octokit as Octokit
33+
import Registry.Foreign.Tmp as Tmp
34+
import Registry.Internal.Format as Internal.Format
35+
import Registry.Manifest (Manifest(..))
36+
import Registry.ManifestIndex as ManifestIndex
37+
import Registry.PackageName as PackageName
38+
import Registry.Range as Range
39+
import Registry.Version as Version
40+
import Run (AFF, EFFECT, Run)
41+
import Run as Run
42+
import Run.Except (EXCEPT)
43+
import Run.Except as Except
44+
45+
data InputMode
46+
= File FilePath
47+
| Package PackageName Version
48+
| AllPackages
49+
50+
parser :: ArgParser InputMode
51+
parser = Arg.choose "input (--file or --package or --all)"
52+
[ Arg.argument [ "--file" ]
53+
"""Compute supported compiler versions for packages from a JSON file like: [ "prelude", "console" ]"""
54+
# Arg.unformat "FILE_PATH" pure
55+
# map File
56+
, Arg.argument [ "--package" ]
57+
"Compute supported compiler versions for the indicated package"
58+
# Arg.unformat "NAME@VERSION" parsePackage
59+
# map (uncurry Package)
60+
, Arg.flag [ "--all" ] "Compute supported compiler versions for all packages" $> AllPackages
61+
]
62+
where
63+
parsePackage :: String -> Either String (Tuple PackageName Version)
64+
parsePackage input = do
65+
let split = String.split (String.Pattern "@") input
66+
case Array.length split of
67+
0 -> Left "Expected package@version but received nothing."
68+
2 -> do
69+
rawPackage <- note "Unexpected error" (Array.index split 0)
70+
package <- lmap (append ("Failed to parse package name '" <> rawPackage <> "': ")) (PackageName.parse rawPackage)
71+
rawVersion <- note "Unexpected error" (Array.index split 1)
72+
version <- lmap (append ("Failed to parse version '" <> rawVersion <> "': ")) (Version.parse rawVersion)
73+
pure $ Tuple package version
74+
_ -> Left $ "Expected package@version but received an invalid format: " <> input
75+
76+
main :: Effect Unit
77+
main = launchAff_ do
78+
args <- Array.drop 2 <$> liftEffect Process.argv
79+
let description = "A script for determining the supported compiler versions for packages."
80+
arguments <- case Arg.parseArgs "compiler-versions" description parser args of
81+
Left err -> Console.log (Arg.printArgError err) *> liftEffect (Process.exit 1)
82+
Right command -> pure command
83+
84+
-- Environment
85+
_ <- Env.loadEnvFile ".env"
86+
token <- Env.lookupRequired Env.githubToken
87+
88+
-- Caching
89+
let cache = Path.concat [ scratchDir, ".cache" ]
90+
FS.Extra.ensureDirectory cache
91+
githubCacheRef <- Cache.newCacheRef
92+
registryCacheRef <- Cache.newCacheRef
93+
94+
-- GitHub
95+
octokit <- Octokit.newOctokit token
96+
97+
-- Registry
98+
debouncer <- Registry.newDebouncer
99+
let
100+
registryEnv :: Registry.RegistryEnv
101+
registryEnv =
102+
{ write: Registry.ReadOnly
103+
, pull: Git.Autostash
104+
, repos: Registry.defaultRepos
105+
, workdir: scratchDir
106+
, debouncer
107+
, cacheRef: registryCacheRef
108+
}
109+
110+
-- Logging
111+
now <- nowUTC
112+
let logDir = Path.concat [ scratchDir, "logs" ]
113+
FS.Extra.ensureDirectory logDir
114+
let logFile = "compiler-versions-" <> String.take 19 (Formatter.DateTime.format Internal.Format.iso8601DateTime now) <> ".log"
115+
let logPath = Path.concat [ logDir, logFile ]
116+
Console.log $ "Logs available at " <> logPath
117+
118+
let
119+
interpret :: Run _ ~> Aff
120+
interpret =
121+
Except.catch (\error -> Run.liftEffect (Console.log error *> Process.exit 1))
122+
>>> Registry.interpret (Registry.handle registryEnv)
123+
>>> Storage.interpret (Storage.handleReadOnly cache)
124+
>>> GitHub.interpret (GitHub.handle { octokit, cache, ref: githubCacheRef })
125+
>>> Log.interpret (\log -> Log.handleTerminal Normal log *> Log.handleFs Verbose logPath log)
126+
>>> Run.runBaseAff'
127+
128+
case arguments of
129+
File _ -> Console.log "Unsupported at this time." *> liftEffect (Process.exit 1)
130+
Package package version -> interpret $ determineCompilerVersionsForPackage package version
131+
AllPackages -> Console.log "Unsupported at this time." *> liftEffect (Process.exit 1)
132+
133+
determineCompilerVersionsForPackage :: forall r. PackageName -> Version -> Run (AFF + EFFECT + REGISTRY + EXCEPT String + LOG + STORAGE + r) Unit
134+
determineCompilerVersionsForPackage package version = do
135+
allManifests <- map ManifestIndex.toMap Registry.readAllManifests
136+
compilerVersions <- PursVersions.pursVersions
137+
Log.debug $ "Checking Manifest Index for " <> formatPackageVersion package version
138+
Manifest { dependencies } <- Except.rethrow $ (note "Invalid Version" <<< Map.lookup version <=< note "Invalid PackageName" <<< Map.lookup package) allManifests
139+
unless (Map.isEmpty dependencies) do
140+
Log.error "Cannot check package that has dependencies."
141+
Except.throw "Cannot check package that has dependencies."
142+
tmp <- Run.liftAff Tmp.mkTmpDir
143+
let formattedName = formatPackageVersion package version
144+
let extractedName = PackageName.print package <> "-" <> Version.print version
145+
let tarballName = extractedName <> ".tar.gz"
146+
let tarballPath = Path.concat [ tmp, tarballName ]
147+
let extractedPath = Path.concat [ tmp, extractedName ]
148+
let installPath = Path.concat [ tmp, formattedName ]
149+
Log.debug $ "Installing " <> formattedName
150+
Storage.download package version tarballPath
151+
Run.liftAff do
152+
Tar.extract { cwd: tmp, archive: tarballName }
153+
FS.Extra.remove tarballPath
154+
FS.Aff.rename extractedPath installPath
155+
Log.debug $ "Installed " <> formatPackageVersion package version
156+
Log.debug $ "Finding supported compiler versions for " <> formatPackageVersion package version
157+
158+
let
159+
checkCompiler compiler = do
160+
Log.debug $ "Trying to compile " <> formatPackageVersion package version <> " with purs@" <> Version.print compiler
161+
162+
result <- Run.liftAff $ Purs.callCompiler
163+
{ command: Purs.Compile { globs: [ Path.concat [ formattedName, "src/**/*.purs" ] ] }
164+
, version: Just (Version.print compiler)
165+
, cwd: Just tmp
166+
}
167+
168+
case result of
169+
Left _ -> do
170+
Log.debug $ "Failed to compile " <> formatPackageVersion package version <> " with purs@" <> Version.print compiler
171+
pure false
172+
Right _ -> do
173+
Log.debug $ "Compiled " <> formatPackageVersion package version <> " with purs@" <> Version.print compiler
174+
pure true
175+
176+
goCompilerVersions supported compilers = case Array.uncons compilers of
177+
Nothing -> pure supported
178+
Just { head, tail } -> do
179+
success <- checkCompiler head
180+
if success then
181+
goCompilerVersions (supported <> [ head ]) tail
182+
else
183+
goCompilerVersions supported tail
184+
185+
supported <- goCompilerVersions [] (Array.sort (NEA.toArray compilerVersions))
186+
187+
if Array.null supported then do
188+
Log.error $ "Could not find supported compiler versions for " <> formatPackageVersion package version
189+
Run.liftEffect $ Process.exit 1
190+
else
191+
Log.info $ "Found supported compiler versions for " <> formatPackageVersion package version <> ": " <> Array.intercalate ", " (map Version.print supported)

0 commit comments

Comments
 (0)