From 91d60b16073cf46dc172c0d3e8550fda8eeb16c5 Mon Sep 17 00:00:00 2001 From: MrLuje Date: Sat, 14 Apr 2018 16:51:56 +0200 Subject: [PATCH 1/4] Create Fake.Api.HockeyApp project (migration) --- Fake.sln | 15 + build.fsx | 3 +- src/app/Fake.Api.HockeyApp/AssemblyInfo.fs | 17 + .../Fake.Api.HockeyApp.fsproj | 21 + src/app/Fake.Api.HockeyApp/HockeyApp.fs | 358 ++++++++++++++++++ src/app/Fake.Api.HockeyApp/paket.references | 6 + 6 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 src/app/Fake.Api.HockeyApp/AssemblyInfo.fs create mode 100644 src/app/Fake.Api.HockeyApp/Fake.Api.HockeyApp.fsproj create mode 100644 src/app/Fake.Api.HockeyApp/HockeyApp.fs create mode 100644 src/app/Fake.Api.HockeyApp/paket.references diff --git a/Fake.sln b/Fake.sln index 0a365a9e1ee..43b0bb7ec56 100644 --- a/Fake.sln +++ b/Fake.sln @@ -104,6 +104,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E09B72E4-D EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fake.Core.CommandLine.UnitTests", "src/test/Fake.Core.CommandLine.UnitTests/Fake.Core.CommandLine.UnitTests.fsproj", "{8561A35A-C2A4-43C7-A938-CB35A7747121}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fake.Api.HockeyApp", "src/app/Fake.Api.HockeyApp/Fake.Api.HockeyApp.fsproj", "{B636A082-4DB4-439D-8A37-E5214BDC00A3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -666,6 +668,18 @@ Global {8561A35A-C2A4-43C7-A938-CB35A7747121}.Release|x64.Build.0 = Release|x64 {8561A35A-C2A4-43C7-A938-CB35A7747121}.Release|x86.ActiveCfg = Release|x86 {8561A35A-C2A4-43C7-A938-CB35A7747121}.Release|x86.Build.0 = Release|x86 + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Debug|x64.ActiveCfg = Debug|x64 + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Debug|x64.Build.0 = Debug|x64 + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Debug|x86.ActiveCfg = Debug|x86 + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Debug|x86.Build.0 = Debug|x86 + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Release|Any CPU.Build.0 = Release|Any CPU + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Release|x64.ActiveCfg = Release|x64 + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Release|x64.Build.0 = Release|x64 + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Release|x86.ActiveCfg = Release|x86 + {B636A082-4DB4-439D-8A37-E5214BDC00A3}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -719,6 +733,7 @@ Global {CA6EB1B3-EB3A-4063-8A6C-DE099A53A8B1} = {901F162F-8925-4390-89C5-9EE2C343F744} {E09B72E4-D890-46A8-8D14-7367C2E23E9D} = {539D7B9A-18A1-4D79-86AB-C8B48090CA84} {8561A35A-C2A4-43C7-A938-CB35A7747121} = {E09B72E4-D890-46A8-8D14-7367C2E23E9D} + {B636A082-4DB4-439D-8A37-E5214BDC00A3} = {901F162F-8925-4390-89C5-9EE2C343F744} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {058A0C5E-2216-4306-8AFB-0AE28320C26A} diff --git a/build.fsx b/build.fsx index 74300cc3f22..b4a5bfbd85b 100644 --- a/build.fsx +++ b/build.fsx @@ -245,8 +245,9 @@ let common = [ // New FAKE libraries let dotnetAssemblyInfos = [ "dotnet-fake", "Fake dotnet-cli command line tool" - "Fake.Api.Slack", "Slack Integration Support" "Fake.Api.GitHub", "GitHub Client API Support via Octokit" + "Fake.Api.HockeyApp", "HockeyApp Integration Support" + "Fake.Api.Slack", "Slack Integration Support" "Fake.Azure.CloudServices", "Azure Cloud Services Support" "Fake.Azure.Emulators", "Azure Emulators Support" "Fake.Azure.Kudu", "Azure Kudu Support" diff --git a/src/app/Fake.Api.HockeyApp/AssemblyInfo.fs b/src/app/Fake.Api.HockeyApp/AssemblyInfo.fs new file mode 100644 index 00000000000..cfa6a1d8226 --- /dev/null +++ b/src/app/Fake.Api.HockeyApp/AssemblyInfo.fs @@ -0,0 +1,17 @@ +// Auto-Generated by FAKE; do not edit +namespace System +open System.Reflection + +[] +[] +[] +[] +[] +do () + +module internal AssemblyVersionInformation = + let [] AssemblyTitle = "FAKE - F# Make HockeyApp Integration Support" + let [] AssemblyProduct = "FAKE - F# Make" + let [] AssemblyVersion = "5.0.0" + let [] AssemblyInformationalVersion = "5.0.0-rc004" + let [] AssemblyFileVersion = "5.0.0" diff --git a/src/app/Fake.Api.HockeyApp/Fake.Api.HockeyApp.fsproj b/src/app/Fake.Api.HockeyApp/Fake.Api.HockeyApp.fsproj new file mode 100644 index 00000000000..e91b9002a4a --- /dev/null +++ b/src/app/Fake.Api.HockeyApp/Fake.Api.HockeyApp.fsproj @@ -0,0 +1,21 @@ + + + net46;netstandard1.6;netstandard2.0 + Fake.Api.HockeyApp + Library + + + $(DefineConstants);NETSTANDARD;USE_HTTPCLIENT + + + $(DefineConstants);RELEASE + + + + + + + + + + diff --git a/src/app/Fake.Api.HockeyApp/HockeyApp.fs b/src/app/Fake.Api.HockeyApp/HockeyApp.fs new file mode 100644 index 00000000000..7d2c2cd535a --- /dev/null +++ b/src/app/Fake.Api.HockeyApp/HockeyApp.fs @@ -0,0 +1,358 @@ +namespace Fake.Api + +#if NETSTANDARD +open System.Net.Http +#else +open System.Net +#endif + +open Microsoft.FSharp.Core +open System +open System.Collections.Generic +open System.IO +open System.Text +open System.Text.RegularExpressions +open Fake.Core +open Newtonsoft.Json + +/// Contains tasks to interact with [HockeyApp](http://hockeyapp.com) +module HockeyApp = + + /// The release type of the app + type ReleaseType = + | Beta = 0 + | Store = 1 + | Alpha = 2 + | Enterprise = 3 + + /// The notification options + type NotifyOption = + | None = 0 + | CanInstallApp = 1 + | All = 2 + + /// The note types + type NoteType = + | Textile = 0 + | Markdown = 1 + + /// The mandatory options + type MandatoryOption = + | NotMandatory = 0 + | Mandatory = 1 + + /// The release download status + type DownloadStatusOption = + | NotDownloadable = 1 + | Downloadable = 2 + + /// HockeyApp's success response + type HockeyResponse = { + Title : string + + [] + BundleIdentifier: string + + [] + PublicIdentifier: string + + [] + DeviceFamily: string + + [] + MinimumOSVersion: string + + [] + ReleaseType : ReleaseType + + Platform : string + + Status : int + + [] + ConfigUrl : string + + [] + PublicUrl : string + + AppSize : int64 + } + + /// HockeyAppVersion's success response + /// https://support.hockeyapp.net/kb/api/api-versions#create-version + type HockeyVersionResponse = { + Title: string + + Timestamp : int64 + + Id : string + + Version : string + + ShortVersion : string + + Status : int + + [] + ConfigUrl : string + + [] + PublicUrl : string + } + + type BaseHockeyAppParams = + /// (Required) API token + abstract ApiToken: string + + /// Set to your App Id (required for UWP apps targeting windows phone) + abstract AppId: string + + /// Set maximum upload delay + abstract UploadTimeout: TimeSpan + + /// The HockeyAppVersion parameter type + /// Based on https://support.hockeyapp.net/kb/api/api-versions#create-version + [] + type HockeyAppVersionParams = + { + /// (Required) API token + ApiToken: string + + /// Set to your App Id (required for UWP apps targeting windows phone) + AppId: string + + /// Human readable version + Version: string + + /// Set maximum upload delay + UploadTimeout: TimeSpan + } + interface BaseHockeyAppParams with + member this.ApiToken = this.ApiToken + member this.AppId = this.AppId + member this.UploadTimeout = this.UploadTimeout + + /// The HockeyApp parameter type + /// Based on http://support.hockeyapp.net/kb/api/api-apps#upload-app + [] + type HockeyAppUploadParams = + { + /// (Required) API token + ApiToken: string + + /// (Required) file data for the build (.ipa or .apk) + File: string + + /// file data for dsym (IOS: *.dysm.zip or Android: mapping.txt) + Dsym: string + + /// Release notes for the build + Notes: string + + /// Release notes type for the build + NotesType: NoteType + + /// Set the release type of the app + ReleaseType: ReleaseType + + /// Set the owner of the app + OwnerId: string + + /// Set the notify option + Notify: NotifyOption + + /// Set version as mandatory + Mandatory: MandatoryOption + + /// Set to true to enable the private download page (default is false) + Private: bool + + /// Set to the git commit sha for this build + CommitSHA: string + + /// Set to the URL of the build job on your build server + BuildServerUrl: string + + /// Set to your source repository + RepositoryUrl: string + + /// Release download status (can only be set with full-access tokens) + DownloadStatus: DownloadStatusOption + + /// Restrict download to specific teams + Teams: string[] + + /// Set maximum upload delay + UploadTimeout: TimeSpan + + /// Set to your App Id (required for UWP apps targeting windows phone) + AppId: string + + /// When uploading a build, specify to which version (hockeyapp version id) + VersionId: string + } + interface BaseHockeyAppParams with + member this.ApiToken: string = this.ApiToken + member this.AppId: string = this.AppId + member this.UploadTimeout = this.UploadTimeout + + /// The default HockeyApp parameters to upload a build + let HockeyAppUploadDefaults = { + ApiToken = String.Empty + File = String.Empty + Dsym = String.Empty + Notes = String.Empty + NotesType = NoteType.Textile + ReleaseType = ReleaseType.Beta + OwnerId = String.Empty + Notify = NotifyOption.None + Mandatory = MandatoryOption.NotMandatory + Private = false + CommitSHA = String.Empty + BuildServerUrl = String.Empty + RepositoryUrl = String.Empty + DownloadStatus = DownloadStatusOption.NotDownloadable + Teams = Array.empty + UploadTimeout = TimeSpan.FromMinutes 2. + AppId = String.Empty + VersionId = String.Empty + } + + /// The default HockeyAppVersion parameters to create a version + let HockeyAppVersionDefaults = { + ApiToken = String.Empty + AppId = String.Empty + Version = String.Empty + UploadTimeout = TimeSpan.FromMinutes 2. + } + + /// [omit] + let private nl = Environment.NewLine + + /// [omit] + let private validateParams param:HockeyAppUploadParams = + if param.ApiToken = "" then failwith "You must provide your API token" + if param.File = "" then failwith "You must provide an app file to upload" + if not <| File.Exists param.File then + failwithf "No such file: %s" param.File + + if not (String.IsNullOrEmpty param.Dsym) then + if not (param.Dsym.EndsWith(".dsym.zip", StringComparison.OrdinalIgnoreCase) + || param.Dsym.Equals("mapping.txt", StringComparison.OrdinalIgnoreCase)) then + failwith "DSYM files should only be: IOS: *.dsym.zip Android: mapping.txt" + + if param.File.EndsWith(".ipa") + && not (param.Dsym.EndsWith(".dsym.zip", StringComparison.OrdinalIgnoreCase)) then + failwith "DSYM for an .ipa file can only only be: *.dsym.zip" + + if param.File.EndsWith(".apk") + && not (param.Dsym.Equals("mapping.txt", StringComparison.OrdinalIgnoreCase)) then + failwith "DSYM for an .apk file can only only be: mapping.txt" + + param + + /// [omit] + let private validateVersionParams (param:HockeyAppVersionParams) = + if param.ApiToken = "" then failwith "You must provide your API token" + if param.Version = "" then failwith "You must provide a version" + if param.AppId = "" then failwith "You must provide an app id" + param + + /// [omit] + let private toCurlArgs (param:HockeyAppUploadParams) = seq { + yield (String.Format("-sL -w \"{0}%{{http_code}}{0}\"", Regex.Escape(nl))) + if not (String.IsNullOrEmpty param.VersionId) then yield "-X PUT" + yield sprintf "-H \"X-HockeyAppToken:%s\"" param.ApiToken + yield sprintf "-F \"ipa=@%s\"" param.File + if not (String.IsNullOrEmpty param.Dsym) then yield sprintf "-F \"dsym=@%s\"" param.Dsym + yield sprintf "-F \"notes=%s\"" param.Notes + yield sprintf "-F \"notes_type=%i\"" (int param.NotesType) + yield sprintf "-F \"release_type=%i\"" (int param.ReleaseType) + yield sprintf "-F \"notify=%i\"" (int param.Notify) + yield sprintf "-F \"mandatory=%i\"" (int param.Mandatory) + yield sprintf "-F \"status=%i\"" (int param.DownloadStatus) + yield sprintf "-F \"private=%b\"" param.Private + yield sprintf "-F \"teams=%s\"" (param.Teams |> String.concat ",") + if not (String.IsNullOrEmpty param.OwnerId) then yield sprintf "-F \"owner_id=%s\"" param.OwnerId + if not (String.IsNullOrEmpty param.CommitSHA) then yield sprintf "-F \"commit_sha=%s\"" param.CommitSHA + if not (String.IsNullOrEmpty param.BuildServerUrl) then yield sprintf "-F \"build_server_url=%s\"" param.BuildServerUrl + if not (String.IsNullOrEmpty param.RepositoryUrl) then yield sprintf "-F \"repository_url=%s\"" param.RepositoryUrl + + if not (String.IsNullOrEmpty param.AppId) && (String.IsNullOrEmpty param.VersionId) then + yield sprintf "https://rink.hockeyapp.net/api/2/apps/%s/app_versions/upload" param.AppId + else if not (String.IsNullOrEmpty param.AppId) && not (String.IsNullOrEmpty param.VersionId) then + yield sprintf "https://rink.hockeyapp.net/api/2/apps/%s/app_versions/%s" param.AppId param.VersionId + else + yield "https://rink.hockeyapp.net/api/2/apps/upload" + } + + /// [omit] + let private toVersionCurlArgs (param:HockeyAppVersionParams) = seq { + yield (String.Format("-sL -w \"{0}%{{http_code}}{0}\"", Regex.Escape(nl))) + yield "-X POST" + yield "-H \"Content-Type: application/json\"" + yield sprintf "-d '{\"bundle_version\":\"%s\"}'" param.Version + yield sprintf "-H \"X-HockeyAppToken:%s\"" param.ApiToken + yield sprintf "https://rink.hockeyapp.net/api/2/apps/%s/app_versions/new" param.AppId + } + + /// [omit] + let private processHockeyAppCmd<'TParam, 'TResponse when 'TParam :> BaseHockeyAppParams> defaults + (setParams: 'TParam -> 'TParam) + (validateParam: 'TParam -> 'TParam) + (toCurlArgs: 'TParam -> seq) = + let p = defaults + |> setParams + |> validateParam + + p + |> toCurlArgs + |> fun args -> + Process.execWithResult (fun p -> + { p with + FileName = "curl" + Arguments = (String.concat " " args) + }) p.UploadTimeout + |> fun response -> + let error = sprintf "Error while posting to HockeyApp.%sMessages: %s%sErrors: %s%s" nl (String.concat "; " response.Messages) nl (String.concat "; " response.Errors) nl + match response.ExitCode with + | 0 -> + match Int32.TryParse (response.Messages.[response.Messages.Length - 1].Trim()) with + | (false, _) -> failwith error + | (true, responseCode) -> + match responseCode with + | 201 -> JsonConvert.DeserializeObject<'TResponse>(response.Messages.[0]) + | _ -> failwith error + | _ -> failwith error + + + /// Uploads an app to HockeyApp + /// ## Parameters + /// - `setParams` - Function used to override the default parameters + /// + /// ## Sample + /// + /// uploadApp (fun defaults -> + /// {defaults with + /// AppId = ... + /// ApiToken = ... + /// ... + /// }) + + let uploadApp setParams = processHockeyAppCmd HockeyAppUploadDefaults setParams validateParams toCurlArgs + + /// Create a new version of an app on HockeyApp + /// ## Parameters + /// - `setParams` - Function used to override the default parameters + /// + /// ## Sample + /// + /// createAppVersion (fun defaults -> + /// {defaults with + /// AppId = ... + /// ApiToken = ... + /// Version = ... + /// ... + /// }) + + let createAppVersion setParams = processHockeyAppCmd HockeyAppVersionDefaults setParams validateVersionParams toVersionCurlArgs diff --git a/src/app/Fake.Api.HockeyApp/paket.references b/src/app/Fake.Api.HockeyApp/paket.references new file mode 100644 index 00000000000..7e3f02584b1 --- /dev/null +++ b/src/app/Fake.Api.HockeyApp/paket.references @@ -0,0 +1,6 @@ +group netcore + +FSharp.Core +NETStandard.Library +System.Net.Http +Newtonsoft.Json From 2aabf613290c6ac803828c9d0006198f42fe3d03 Mon Sep 17 00:00:00 2001 From: MrLuje Date: Sat, 14 Apr 2018 16:52:44 +0200 Subject: [PATCH 2/4] Obsolete Fake.HockeyAppHelper --- src/legacy/FakeLib/HockeyAppHelper.fs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/legacy/FakeLib/HockeyAppHelper.fs b/src/legacy/FakeLib/HockeyAppHelper.fs index 3033c8480a2..27f7145ee4a 100644 --- a/src/legacy/FakeLib/HockeyAppHelper.fs +++ b/src/legacy/FakeLib/HockeyAppHelper.fs @@ -1,4 +1,5 @@ /// Contains tasks to interact with [HockeyApp](http://hockeyapp.com) +[] module Fake.HockeyAppHelper open Microsoft.FSharp.Core @@ -12,6 +13,7 @@ open Fake open Newtonsoft.Json /// The release type of the app +[] type ReleaseType = | Beta = 0 | Store = 1 @@ -19,27 +21,32 @@ type ReleaseType = | Enterprise = 3 /// The notification options +[] type NotifyOption = | None = 0 | CanInstallApp = 1 | All = 2 /// The note types +[] type NoteType = | Textile = 0 | Markdown = 1 /// The mandatory options +[] type MandatoryOption = | NotMandatory = 0 | Mandatory = 1 /// The release download status +[] type DownloadStatusOption = | NotDownloadable = 1 | Downloadable = 2 /// HockeyApp's success response +[] type HockeyResponse = { Title : string @@ -73,6 +80,7 @@ type HockeyResponse = { /// HockeyAppVersion's success response /// https://support.hockeyapp.net/kb/api/api-versions#create-version +[] type HockeyVersionResponse = { Title: string @@ -93,6 +101,7 @@ type HockeyVersionResponse = { PublicUrl : string } +[] type BaseHockeyAppParams = /// (Required) API token abstract ApiToken: string @@ -105,6 +114,7 @@ type BaseHockeyAppParams = /// The HockeyAppVersion parameter type /// Based on https://support.hockeyapp.net/kb/api/api-versions#create-version +[] [] type HockeyAppVersionParams = { @@ -127,6 +137,7 @@ type HockeyAppVersionParams = /// The HockeyApp parameter type /// Based on http://support.hockeyapp.net/kb/api/api-apps#upload-app +[] [] type HockeyAppUploadParams = { @@ -331,6 +342,7 @@ let private processHockeyAppCmd<'TParam, 'TResponse when 'TParam :> BaseHockeyAp /// ... /// }) +[] let HockeyApp setParams = processHockeyAppCmd HockeyAppUploadDefaults setParams validateParams toCurlArgs /// Create a new version of an app on HockeyApp @@ -347,4 +359,5 @@ let HockeyApp setParams = processHockeyAppCmd] let HockeyAppVersion setParams = processHockeyAppCmd HockeyAppVersionDefaults setParams validateVersionParams toVersionCurlArgs From a3be0539f6d000bac861ec6bbd9ff767bdd2a60c Mon Sep 17 00:00:00 2001 From: MrLuje Date: Sat, 14 Apr 2018 16:53:08 +0200 Subject: [PATCH 3/4] Add module Fake.Api.HockeyApp in FakeLib --- src/legacy/FakeLib/FakeLib.fsproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/legacy/FakeLib/FakeLib.fsproj b/src/legacy/FakeLib/FakeLib.fsproj index 65ca3774257..498bb420573 100644 --- a/src/legacy/FakeLib/FakeLib.fsproj +++ b/src/legacy/FakeLib/FakeLib.fsproj @@ -370,6 +370,9 @@ Fake.BuildServer.TeamFoundation/TeamFoundation.fs + + Fake.Api.HockeyApp\HockeyApp.fs + From 5a7390db084c150862d08d5b62c8b4a7853259cd Mon Sep 17 00:00:00 2001 From: MrLuje Date: Sun, 15 Apr 2018 12:02:09 +0200 Subject: [PATCH 4/4] Remove CLIMutable --- src/app/Fake.Api.HockeyApp/HockeyApp.fs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/Fake.Api.HockeyApp/HockeyApp.fs b/src/app/Fake.Api.HockeyApp/HockeyApp.fs index 7d2c2cd535a..c53fd371a71 100644 --- a/src/app/Fake.Api.HockeyApp/HockeyApp.fs +++ b/src/app/Fake.Api.HockeyApp/HockeyApp.fs @@ -8,9 +8,7 @@ open System.Net open Microsoft.FSharp.Core open System -open System.Collections.Generic open System.IO -open System.Text open System.Text.RegularExpressions open Fake.Core open Newtonsoft.Json @@ -112,7 +110,6 @@ module HockeyApp = /// The HockeyAppVersion parameter type /// Based on https://support.hockeyapp.net/kb/api/api-versions#create-version - [] type HockeyAppVersionParams = { /// (Required) API token @@ -134,7 +131,6 @@ module HockeyApp = /// The HockeyApp parameter type /// Based on http://support.hockeyapp.net/kb/api/api-apps#upload-app - [] type HockeyAppUploadParams = { /// (Required) API token