Skip to content

Commit

Permalink
Merge pull request #54 from imclerran/update-fail-messages
Browse files Browse the repository at this point in the history
Add failure messages when the local repository fails to update
  • Loading branch information
imclerran authored Jul 8, 2024
2 parents a888125 + cc5d2eb commit 1bfc23f
Showing 1 changed file with 167 additions and 73 deletions.
240 changes: 167 additions & 73 deletions src/main.roc
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,23 @@ runWith = \args ->
when args.subcommand is
Ok (Tui {}) ->
runTuiApp args.update
|> Task.onErr \_ -> Task.err (Exit 1 "")

Ok (Update { doPfs, doPkgs, doStubs }) ->
if doPfs == doPkgs && doPkgs == doStubs then
runUpdates Bool.true Bool.true Bool.true
|> Task.onErr \_ -> Task.err (Exit 1 "")
else
runUpdates doPfs doPkgs doStubs
|> Task.onErr \_ -> Task.err (Exit 1 "")

Ok (App { appName, platform, packages }) ->
runCliApp App appName platform packages args.update
runCliApp App appName platform packages args.update
|> Task.onErr \_ -> Task.err (Exit 1 "")

Ok (Pkg { packages }) ->
runCliApp Pkg "main" "" packages args.update
|> Task.onErr \_ -> Task.err (Exit 1 "")

Err NoSubcommand ->
Stdout.line! ArgParser.extendedUsage
Expand All @@ -74,15 +79,18 @@ runWith = \args ->
## Load the repository data, and create the roc file if it doesn't already exist.
runCliApp : [App, Pkg], Str, Str, List Str, Bool -> Task {} _
runCliApp = \type, fileName, platform, packages, forceUpdate ->
repos = loadRepoData! forceUpdate
getAppStubsIfNeeded! (Dict.keys repos.platforms) forceUpdate
fileExists = checkForFile! "$(fileName).roc"
if fileExists then
Stdout.line! "Error: $(fileName).roc already exists. $(redCross)"
Task.err (Exit 1 "")
else
createRocFile! { fileName, platform, packages, type } repos
Stdout.line! "Created $(fileName).roc $(greenCheck)"
reposRes <- loadRepoData forceUpdate |> Task.attempt
when reposRes is
Ok repos ->
getAppStubsIfNeeded! (Dict.keys repos.platforms) forceUpdate
fileExists = checkForFile! "$(fileName).roc"
if fileExists then
Stdout.line! "Error: $(fileName).roc already exists. $(redCross)"
Task.err (Exit 1 "")
else
createRocFile! { fileName, platform, packages, type } repos
Stdout.line! "Created $(fileName).roc $(greenCheck)"
Err e -> Task.err e

## Run the TUI application.
## Load the repository data, run the main tui loop, and create the roc file when the user confirms their selections.
Expand All @@ -105,7 +113,7 @@ runTuiApp = \forceUpdate ->
createRocFile! config repos
Stdout.line "Created $(config.fileName).roc $(greenCheck)"

_ -> Stdout.line "Oops! Something went wrong..."
_ -> Stdout.line ("Oops! Something went wrong..." |> Core.withFg (Standard Yellow))

## Run the update tasks for the platform, package, and app-stub repositories.
runUpdates : Bool, Bool, Bool -> Task {} _
Expand All @@ -114,7 +122,7 @@ runUpdates = \doPfs, doPkgs, doStubs ->
when List.first updateList is
Ok (doUpdate, updater) ->
if doUpdate then
updater!
_ <- updater |> Task.attempt
Task.ok (Step (List.dropFirst updateList 1))
else
Task.ok (Step (List.dropFirst updateList 1))
Expand Down Expand Up @@ -361,64 +369,123 @@ loadRepoData = \forceUpdate ->
## Load the latest repository data from the remote repository.
loadLatestRepoData : Task { packages : Dict Str RepositoryEntry, platforms : Dict Str RepositoryEntry } _
loadLatestRepoData =
doRepoUpdate!
dataDir = getAndCreateDataDir!
packageBytes = File.readBytes! "$(dataDir)/pkg-data.rvn" # consider using in-memory data structure
platformBytes = File.readBytes! "$(dataDir)/pf-data.rvn" # if the repo was loaded from remote
packages = getRepoDict packageBytes
platforms = getRepoDict platformBytes
Task.ok { packages, platforms }
updateRes <- doRepoUpdate |> Task.attempt
when updateRes is
Ok _ ->
dataDir = getAndCreateDataDir!
packageBytes = File.readBytes! "$(dataDir)/pkg-data.rvn" # consider using in-memory data structure
platformBytes = File.readBytes! "$(dataDir)/pf-data.rvn" # if the repo was loaded from remote
packages = getRepoDict packageBytes
platforms = getRepoDict platformBytes
Task.ok { packages, platforms }

Err e -> Task.err e

## Update the local repository cache with the latest data from the remote repository.
doRepoUpdate : Task {} _
doRepoUpdate =
repoLists = getRemoteRepoData!
Stdout.write! "Updating platform repository..."
updateRepoCache! repoLists.platformRepos "pf-data.rvn"
Stdout.line! " $(greenCheck)"
Stdout.write! "Updating package repository..."
updateRepoCache! repoLists.packageRepos "pkg-data.rvn"
Stdout.line " $(greenCheck)"
pfRes <- doPlatformUpdate |> Task.attempt
when pfRes is
Ok _ ->
pkgRes <- doPackageUpdate |> Task.attempt
when pkgRes is
Ok _ -> Task.ok {}
Err e -> Task.err e

Err e -> Task.err e

## Update the local package repository cache with the latest data from the remote repository.
doPackageUpdate : Task {} _
doPackageUpdate =
repoLists = getRemoteRepoData!
Stdout.write! "Updating package repository..."
updateRepoCache! repoLists.packageRepos "pkg-data.rvn"
Stdout.line " $(greenCheck)"
repoListRes <- getRemoteRepoData Packages |> Task.attempt
when repoListRes is
Ok repoList ->
Stdout.write! "Updating package repository... "
res <- updateRepoCache repoList "pkg-data.rvn" |> Task.attempt
when res is
Ok _ ->
Stdout.line! greenCheck
Err GhAuthError ->
Stdout.line! redCross
Stdout.line! ("Error: `gh` not authenticated." |> Core.withFg (Standard Yellow))
Task.err GhAuthError
Err GhNotInstalled ->
Stdout.line! redCross
Stdout.line! ("Error: `gh` not installed." |> Core.withFg (Standard Yellow))
Task.err GhNotInstalled
Err e ->
Stdout.line! redCross
Task.err e

Err e ->
Stdout.line! "Package update failed. $(redCross)"
when e is
NetworkErr _ ->
Stdout.line! ("Error: network error." |> Core.withFg (Standard Yellow))
Task.err e
_ ->
Task.err e


## Update the local platform repository cache with the latest data from the remote repository.
doPlatformUpdate : Task {} _
doPlatformUpdate =
repoLists = getRemoteRepoData!
Stdout.write! "Updating platform repository..."
updateRepoCache! repoLists.platformRepos "pf-data.rvn"
Stdout.line! " $(greenCheck)"
repoListRes <- getRemoteRepoData Platforms |> Task.attempt
when repoListRes is
Ok repoList ->
Stdout.write! "Updating platform repository... "
res <- updateRepoCache repoList "pf-data.rvn" |> Task.attempt
when res is
Ok _ ->
Stdout.line! greenCheck
Err GhAuthError ->
Stdout.line! redCross
Stdout.line! ("Error: `gh` not authenticated" |> Core.withFg (Standard Yellow))
Task.err GhAuthError
Err GhNotInstalled ->
Stdout.line! redCross
Stdout.line! ("Error: `gh` not installed" |> Core.withFg (Standard Yellow))
Task.err GhNotInstalled
Err e ->
Stdout.line! redCross
Task.err e

Err e ->
Stdout.line! "Platform update failed. $(redCross)"
when e is
NetworkErr _ ->
Stdout.line! ("Error: network error." |> Core.withFg (Standard Yellow))
Task.err e
_ ->
Task.err e

## Download the app stubs for the currently cached platforms.
doAppStubUpdate : Task {} _
doAppStubUpdate =
dataDir = getAndCreateDataDir!
platformBytes = File.readBytes! "$(dataDir)/pf-data.rvn"
platforms = getRepoDict platformBytes
getAppStubsIfNeeded! (Dict.keys platforms) Bool.true

## Get the remote repository data, decode it, and split it into a list of package and platform repos.
getRemoteRepoData : Task { packageRepos : List RemoteRepoEntry, platformRepos : List RemoteRepoEntry } _
getRemoteRepoData =
platformBytesRes <- File.readBytes "$(dataDir)/pf-data.rvn" |> Task.attempt
when platformBytesRes is
Ok platformBytes ->
platforms = getRepoDict platformBytes
getAppStubs! (Dict.keys platforms)
Err _ ->
Stdout.line! "App-stub update failed. $(redCross)"
Stdout.line! ("Error: no platforms downloaded. Try updating platforms." |> Core.withFg (Standard Yellow))
Task.err ErrReadingPlatforms

getRemoteRepoData : [Packages, Platforms] -> Task (List RemoteRepoEntry) _
getRemoteRepoData = \type ->
request = getRequest "https://raw.githubusercontent.com/imclerran/roc-start/main/repository/roc-repo.rvn"
resp = Http.send request |> Task.onErr! \_ -> Task.ok { body: [], headers: [], statusCode: 0, statusText: "", url: "" }
when Decode.fromBytes resp.body Rvn.pretty is
Ok repos ->
repoLists = List.walk repos { packageRepos: [], platformRepos: [] } \state, repoItem ->
if repoItem.platform then
{ state & platformRepos: List.append state.platformRepos repoItem }
else
{ state & packageRepos: List.append state.packageRepos repoItem }
Task.ok repoLists
respRes <- Http.send request |> Task.attempt
when respRes is
Ok resp ->
when Decode.fromBytes resp.body Rvn.pretty is
Ok repos ->
Task.ok (List.keepIf repos \repo -> repo.platform == (type == Platforms))

Err _ -> Task.ok { packageRepos: [], platformRepos: [] }
Err e -> Task.err (DecodingErr e)

Err e -> Task.err (NetworkErr e)

## Create an Http.Request object with the given url.
getRequest : Str -> Http.Request
Expand All @@ -438,28 +505,39 @@ updateRepoCache = \repositoryList, filename ->
Task.ok {}
else
dataDir = getAndCreateDataDir!
pkgRvnStr = Task.loop! { repositoryList, rvnDataStr: "[\n" } reposToRvnStrLoop
File.writeBytes "$(dataDir)/$(filename)" (pkgRvnStr |> Str.toUtf8)
pkgRvnStrRes <- Task.loop { repositoryList, rvnDataStr: "[\n" } reposToRvnStrLoop |> Task.attempt
when pkgRvnStrRes is
Ok pkgRvnStr ->
File.writeBytes "$(dataDir)/$(filename)" (pkgRvnStr |> Str.toUtf8)

Err e ->
Task.err e

RepositoryLoopState : { repositoryList : List RemoteRepoEntry, rvnDataStr : Str }

## Loop function which processes each git repo in the platform or package list, gets the latest release for each,
## and creates a string in rvn format containing the data for each package or platform. Used with `Task.loop`.
reposToRvnStrLoop : RepositoryLoopState -> Task [Step RepositoryLoopState, Done Str] _
reposToRvnStrLoop : RepositoryLoopState -> Task [Step RepositoryLoopState, Done Str] [GhAuthError, GhNotInstalled]
reposToRvnStrLoop = \{ repositoryList, rvnDataStr } ->
when List.get repositoryList 0 is
when List.first repositoryList is
Ok { owner, repo, alias, platform, requires } ->
updatedList = List.dropFirst repositoryList 1
response = getLatestRelease! owner repo
releaseData = responseToReleaseData response
when releaseData is
Ok { tagName, browserDownloadUrl } ->
updatedStr = Str.concat rvnDataStr (repoDataToRvnEntry { repo, owner, alias, version: tagName, url: browserDownloadUrl, platform, requires })
Task.ok (Step { repositoryList: updatedList, rvnDataStr: updatedStr })

responseRes <- getLatestRelease owner repo |> Task.attempt
when responseRes is
Ok response ->
releaseData = responseToReleaseData response
when releaseData is
Ok { tagName, browserDownloadUrl } ->
updatedStr = Str.concat rvnDataStr (repoDataToRvnEntry { repo, owner, alias, version: tagName, url: browserDownloadUrl, platform, requires })
Task.ok (Step { repositoryList: updatedList, rvnDataStr: updatedStr })

Err _ -> Task.ok (Step { repositoryList: updatedList, rvnDataStr })

Err (CmdOutputError (_, ExitCode 4)) -> Task.err GhAuthError
Err (CmdOutputError (_, IOError _)) -> Task.err GhNotInstalled
Err _ -> Task.ok (Step { repositoryList: updatedList, rvnDataStr })

Err OutOfBounds -> Task.ok (Done (Str.concat rvnDataStr "]"))
Err ListWasEmpty -> Task.ok (Done (Str.concat rvnDataStr "]"))

## Use the github cli to get the latest release for a given repository.
getLatestRelease : Str, Str -> Task { stdout : List U8, stderr : List U8 } _
Expand All @@ -472,7 +550,6 @@ getLatestRelease = \owner, repo ->
|> Cmd.arg "X-GitHub-Api-Version: 2022-11-28"
|> Cmd.arg "/repos/$(owner)/$(repo)/releases/latest"
|> Cmd.output
|> Task.onErr! \_ -> Task.ok { stdout: [], stderr: [] }

## Parse the response from the github cli to get the tagName and browserDownloadUrl for the tar.br file for a given release.
responseToReleaseData : { stdout : List U8 }* -> Result { tagName : Str, browserDownloadUrl : Str } [NoAssetsFound, ParsingError]
Expand Down Expand Up @@ -523,9 +600,22 @@ getAppStubs : List Str -> Task {} _
getAppStubs = \platforms ->
dataDir = getAndCreateDataDir!
appStubsDir = getAndCreateDir! "$(dataDir)/app-stubs"
Stdout.write! "Updating app-stubs..."
Task.loop! { platforms, dir: appStubsDir } getAppStubsLoop
Stdout.line " $(greenCheck)"
Stdout.write! "Updating app-stubs... "
if List.len platforms > 0 then
res <- Task.loop { platforms, dir: appStubsDir } getAppStubsLoop |> Task.attempt
when res is
Err (NetworkErr _) ->
Stdout.line! redCross
Stdout.line! ("Error: network error." |> Core.withFg (Standard Yellow))

Err _ ->
Stdout.line! redCross

Ok {} ->
Stdout.line! greenCheck
else
Stdout.line! redCross
Stdout.line! ("Error: no platforms downloaded. Try updating platforms." |> Core.withFg (Standard Yellow))

AppStubsLoopState : { platforms : List Str, dir : Str }

Expand All @@ -536,12 +626,16 @@ getAppStubsLoop = \{ platforms, dir } ->
Ok platform ->
updatedList = List.dropFirst platforms 1
request = getRequest "https://raw.githubusercontent.com/imclerran/roc-start/main/repository/app-stubs/$(platform).roc"
response = Http.send request |> Task.onErr! \_ -> Task.ok { body: [], headers: [], statusCode: 0, statusText: "", url: "" }
if response.statusCode == 200 && !(List.isEmpty response.body) then
File.writeBytes! "$(dir)/$(platform)" response.body
Task.ok (Step { platforms: updatedList, dir })
else
Task.ok (Step { platforms: updatedList, dir })
responseRes <- Http.send request |> Task.attempt
when responseRes is
Err e ->
when e is
HttpErr (BadStatus code ) if code == 404 -> Task.ok (Step { platforms: updatedList, dir })
_ -> Task.err (NetworkErr e)

Ok response ->
File.writeBytes! "$(dir)/$(platform)" response.body
Task.ok (Step { platforms: updatedList, dir })

Err OutOfBounds -> Task.ok (Done {})

Expand Down

0 comments on commit 1bfc23f

Please sign in to comment.