1#!/usr/bin/env nix-shell
2#!nix-shell -i runhaskell --packages 'haskellPackages.ghcWithPackages (ps: [ps.aeson ps.aeson-pretty ps.directory ps.process ps.optparse-applicative])'
3
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RecordWildCards #-}
6
7module Main where
8
9import Options.Applicative
10import System.FilePath (takeDirectory, (</>))
11import System.Process (readProcess)
12import Data.Aeson (object, (.=))
13import Data.Aeson.Encode.Pretty (encodePretty', Config(..), defConfig, Indent(..), keyOrder)
14import qualified Data.ByteString.Lazy.Char8 as BL
15import qualified Data.Map.Strict as Map
16import Control.Monad (forM)
17import qualified Data.Text as T
18
19data Options = Options
20 { scriptPath :: FilePath
21 , channel :: String
22 }
23
24optionsParser :: Parser Options
25optionsParser = Options
26 <$> argument str
27 ( metavar "SCRIPT_PATH"
28 <> help "The path to the script" )
29 <*> argument channelReader
30 ( metavar "CHANNEL"
31 <> help "The release channel (main or stable)" )
32
33channelReader :: ReadM String
34channelReader = eitherReader $ \arg ->
35 if arg `elem` ["main", "stable"]
36 then Right arg
37 else Left "CHANNEL must be one of: main, stable"
38
39-- Custom configuration for pretty-printing JSON
40prettyConfig :: Config
41prettyConfig = defConfig
42 { confIndent = Spaces 2
43 , confCompare = keyOrder ["archMap", "fetchurlAttrSet", "platformList", "version"]
44 }
45
46main :: IO ()
47main = do
48 let opts = info (optionsParser <**> helper)
49 ( fullDesc
50 <> progDesc "Updates the sources-{main|stable}.json files based on the upstream release channel"
51 <> header "update.hs - Haskell script with argument parsing" )
52
53 -- Parse the command-line arguments
54 Options {..} <- execParser opts
55
56 -- Process the arguments
57 let scriptDir = takeDirectory scriptPath
58 let outputFile = scriptDir </> ("sources-" ++ channel ++ ".json")
59
60 -- Fetch current version
61 let baseUrl = "https://cli.upbound.io/" ++ channel
62 currentVersion <- readProcess "curl" ["-s", baseUrl ++ "/current/version"] ""
63 let version = filter (\x -> x /= 'v' && x /= '\n') currentVersion -- Remove the leading 'v' and the new line char
64
65 -- Architecture mapping
66 let archMapping = Map.fromList
67 [ ("aarch64-darwin", "darwin_arm64")
68 , ("x86_64-darwin", "darwin_amd64")
69 , ("aarch64-linux", "linux_arm64")
70 , ("x86_64-linux", "linux_amd64")
71 ]
72
73 -- Build platformList
74 let platformList = Map.keys archMapping
75
76 -- Build fetchurlAttrSet
77 fetchurlAttrSet <- fmap Map.fromList $ forM ["docker-credential-up", "up"] $ \cmd -> do
78 attrs <- forM (Map.toList archMapping) $ \(key, arch) -> do
79 let url = baseUrl ++ "/v" ++ version ++ "/bundle/" ++ cmd ++ "/" ++ arch ++ ".tar.gz"
80 _hash <- readProcess "nix-prefetch-url" [url] ""
81 let hash = T.unpack $ T.strip $ T.pack _hash
82 _sha256Hash <- readProcess "nix" ["hash", "convert", hash, "--hash-algo", "sha256"] ""
83 let sha256Hash = T.unpack $ T.strip $ T.pack _sha256Hash
84 return (key, object ["hash" .= sha256Hash, "url" .= url])
85 return (cmd, object attrs)
86
87 -- Write output to JSON
88 let output = object
89 [ "version" .= version
90 , "platformList" .= platformList
91 , "archMap" .= archMapping
92 , "fetchurlAttrSet" .= fetchurlAttrSet
93 ]
94 BL.writeFile outputFile $ BL.snoc (encodePretty' prettyConfig output) '\n'
95
96 putStrLn $ "Output written to: " ++ outputFile