diff --git a/modules/Octokit/Octokit.fsx b/modules/Octokit/Octokit.fsx index d9518363c00..acca0446d5a 100644 --- a/modules/Octokit/Octokit.fsx +++ b/modules/Octokit/Octokit.fsx @@ -15,6 +15,12 @@ type Draft = let private isRunningOnMono = System.Type.GetType ("Mono.Runtime") <> null +/// A version of 'reraise' that can work inside computation expressions +let private captureAndReraise ex = + System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw() + Unchecked.defaultof<_> + +/// Retry the Octokit action count times let rec private retry count asyncF = // This retry logic causes an exception on Mono: // https://github.com/fsharp/fsharp/issues/440 @@ -24,9 +30,25 @@ let rec private retry count asyncF = async { try return! asyncF - with _ when count > 0 -> return! retry (count - 1) asyncF + with ex -> + return! + match (ex, ex.InnerException) with + | (:? AggregateException, (:? AuthorizationException as ex)) -> captureAndReraise ex + | _ when count > 0 -> retry (count - 1) asyncF + | (ex, _) -> captureAndReraise ex } +/// Retry the Octokit action count times after input succeed +let private retryWithArg count input asycnF = + async { + let! choice = input |> Async.Catch + match choice with + | Choice1Of2 input' -> + return! (asycnF input') |> retry count + | Choice2Of2 ex -> + return captureAndReraise ex + } + let createClient user password = async { let github = new GitHubClient(new ProductHeaderValue("FAKE")) @@ -42,41 +64,39 @@ let createClientWithToken token = } let private makeRelease draft owner project version prerelease (notes:seq) (client : Async) = - async { + retryWithArg 5 client <| fun client' -> async { let data = new NewRelease(version) data.Name <- version data.Body <- String.Join(Environment.NewLine, notes) data.Draft <- draft data.Prerelease <- prerelease - let! client' = client let! draft = Async.AwaitTask <| client'.Release.Create(owner, project, data) let draftWord = if data.Draft then " draft" else "" printfn "Created%s release id %d" draftWord draft.Id - return { Client = client' - Owner = owner - Project = project - DraftRelease = draft } - } |> retry 5 + return { + Client = client' + Owner = owner + Project = project + DraftRelease = draft } + } let createDraft owner project version prerelease notes client = makeRelease true owner project version prerelease notes client let createRelease owner project version prerelease notes client = makeRelease false owner project version prerelease notes client let uploadFile fileName (draft : Async) = - async { + retryWithArg 5 draft <| fun draft' -> async { let fi = FileInfo(fileName) let archiveContents = File.OpenRead(fi.FullName) let assetUpload = new ReleaseAssetUpload(fi.Name,"application/octet-stream",archiveContents,Nullable()) - let! draft' = draft let! asset = Async.AwaitTask <| draft'.Client.Release.UploadAsset(draft'.DraftRelease, assetUpload) printfn "Uploaded %s" asset.Name return draft' - } |> retry 5 + } let releaseDraft (draft : Async) = - async { - let! draft' = draft + retryWithArg 5 draft <| fun draft' -> async { let update = draft'.DraftRelease.ToUpdate() update.Draft <- Nullable(false) let! released = Async.AwaitTask <| draft'.Client.Release.Edit(draft'.Owner, draft'.Project, draft'.DraftRelease.Id, update) printfn "Released %d on github" released.Id - } |> retry 5 + }