Publish Android apk
This module helps android developers to automatically publish their APKs
Prerequisite
Before using this module, you will need an android keystore for apk signing.
Next, you will need a Google service account described here:
https://developers.google.com/accounts/docs/OAuth2ServiceAccount#creatinganaccount
and here:
https://developers.google.com/android-publisher/getting_started
Usage example:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
|
#r "packages/FAKE/tools/FakeLib.dll"
let androidBuildDir = "./build/"
let androidProdDir = "./pack/"
androidProdDir |> ensureDirectory
//Clean old apk
Target "Clean" (fun _ ->
CleanDir androidBuildDir
CleanDir androidProdDir
)
Target "Android-Package" (fun () ->
AndroidPackage(fun defaults ->
{ defaults with
ProjectPath = "Path to my project Droid.csproj"
Configuration = "Release"
OutputPath = androidBuildDir
Properties = ["MSBuild property", "MSBuild property value"]
})
|> AndroidSignAndAlign (fun defaults ->
{ defaults with
KeystorePath = @"path to my file.keystore"
KeystorePassword = "my password"
KeystoreAlias = "my key alias"
})
|> fun file -> file.CopyTo(Path.Combine(androidProdDir, file.Name)) |> ignore
)
// You can also build one APK per ABI
Target "Android-MultiPackages" (fun () ->
let versionStepper = (fun v t -> match t with
| AndroidAbiTarget.X86 c -> v + 1
| AndroidAbiTarget.X86And64 c -> v + 2
| AndroidAbiTarget.ArmEabi c -> v + 3
| AndroidAbiTarget.ArmEabiV7a c -> v + 4
| AndroidAbiTarget.Arm64V8a c -> v + 5
| _ -> v)
let abis = AndroidPackageAbiParam.SpecificAbis
([ AndroidAbiTarget.X86({ SuffixAndExtension="-x86.apk"; })
AndroidAbiTarget.ArmEabi({ SuffixAndExtension="-armeabi.apk"; })
AndroidAbiTarget.ArmEabiV7a({ SuffixAndExtension="-armeabi-v7a.apk"; })
AndroidAbiTarget.X86And64({ SuffixAndExtension="-x86_64.apk"; })
])
let files = AndroidBuildPackages(fun defaults ->
{ defaults with
ProjectPath = "Path to my project Droid.csproj"
Configuration = "Release"
OutputPath = androidBuildDir
PackageAbiTargets = abis
VersionStepper = Some(versionStepper)
})
for f in files do
printfn "- apk: %s" f.Name
files
|> Seq.iter (fun file -> file.CopyTo(Path.Combine(androidProdDir, file.Name)) |> ignore)
)
Target "Publish" (fun _ ->
// I like verbose script
trace "publishing Android App"
let apk = androidProdDir
|> directoryInfo
|> filesInDir
|> Seq.filter(fun f -> f.Name.EndsWith(".apk"))
|> Seq.exactlyOne
let apkPath = apk.FullName
tracefn "Apk found: %s" apkPath
let mail = "my service account [email protected]"
// Path to the certificate file probably named 'Google Play Android Developer-xxxxxxxxxxxx.p12'
let certificate = new X509Certificate2
(
@"Google Play Android Developer-xxxxxxxxxxxx.p12",
"notasecret",
X509KeyStorageFlags.Exportable
)
let packageName = "my Android package name"
// to publish an alpha version:
PublishApk
{ AlphaSettings with
Config =
{
Certificate = certificate;
PackageName = packageName;
AccountId = mail;
Apk = apkPath;
}
}
// to publish a beta version:
//
//PublishApk
// { BetaSettings with
// Config =
// {
// Certificate = certificate;
// PackageName = packageName;
// AccountId = mail;
// Apk = apkPath;
// }
// }
// to publish a production version:
//
//PublishApk
// { ProductionSettings with
// Config =
// {
// Certificate = certificate;
// PackageName = packageName;
// AccountId = mail;
// Apk = apkPath;
// }
// }
)
Target "Android-Build" (fun _ ->
!! "**/my project Droid.csproj"
|> MSBuildRelease androidBuildDir "Build"
|> Log "BuildAndroidLib-Output: "
)
Target "Default" (fun _ ->
trace "Building default target"
RestorePackages()
)
"Clean"
==> "Android-Package"
==> "Default"
RunTargetOrDefault "Default"
|
Default target will not start "Publish" target because apps do not need to be updated too frequently (as explained here: https://developers.google.com/android-publisher/api_usage)
To publish your app, you can run
val androidBuildDir : string
Full name: androidpublisher.androidBuildDir
val androidProdDir : string
Full name: androidpublisher.androidProdDir
val file : obj
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
union case Option.Some: Value: 'T -> Option<'T>
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
module Seq
from Microsoft.FSharp.Collections
val iter : action:('T -> unit) -> source:seq<'T> -> unit
Full name: Microsoft.FSharp.Collections.Seq.iter
val filter : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Collections.Seq.filter
val exactlyOne : source:seq<'T> -> 'T
Full name: Microsoft.FSharp.Collections.Seq.exactlyOne