You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
440 lines
18 KiB
440 lines
18 KiB
#addin nuget:?package=Cake.FileHelpers&version=5.0.0 |
|
#addin nuget:?package=Cake.AndroidAppManifest&version=1.1.2 |
|
#addin nuget:?package=Cake.Plist&version=0.7.0 |
|
#addin nuget:?package=Cake.Incubator&version=7.0.0 |
|
#tool dotnet:?package=GitVersion.Tool&version=5.10.3 |
|
using Path = System.IO.Path; |
|
using System.Text.RegularExpressions; |
|
|
|
var debugScript = Argument<bool>("debugScript", false); |
|
var target = Argument("target", "Default"); |
|
var configuration = Argument("configuration", "Release"); |
|
var variant = Argument("variant", "dev"); |
|
|
|
abstract record VariantConfig( |
|
string AppName, |
|
string AndroidPackageName, |
|
string iOSBundleId, |
|
string ApsEnvironment |
|
); |
|
|
|
const string BASE_BUNDLE_ID_DROID = "com.x8bit.bitwarden"; |
|
const string BASE_BUNDLE_ID_IOS = "com.8bit.bitwarden"; |
|
|
|
record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development"); |
|
record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development"); |
|
record Beta(): VariantConfig("Bitwarden Beta", $"{BASE_BUNDLE_ID_DROID}.beta", $"{BASE_BUNDLE_ID_IOS}.beta", "production"); |
|
record Prod(): VariantConfig("Bitwarden", $"{BASE_BUNDLE_ID_DROID}", $"{BASE_BUNDLE_ID_IOS}", "production"); |
|
|
|
VariantConfig GetVariant() => variant.ToLower() switch{ |
|
"qa" => new QA(), |
|
"beta" => new Beta(), |
|
"prod" => new Prod(), |
|
_ => new Dev() |
|
}; |
|
|
|
GitVersion _gitVersion; //will be set by GetGitInfo task |
|
var _slnPath = Path.Combine(""); //base path used to access files. If build.cake file is moved, just update this |
|
string _androidPackageName = string.Empty; //will be set by UpdateAndroidManifest task |
|
string _iOSVersionName = string.Empty; //will be set by UpdateiOSPlist task |
|
string CreateFeatureBranch(string prevVersionName, GitVersion git) => $"{prevVersionName}-{git.BranchName.Replace("/","-")}"; |
|
string GetVersionName(string prevVersionName, VariantConfig buildVariant, GitVersion git) => buildVariant is Prod? prevVersionName : CreateFeatureBranch(prevVersionName, git); |
|
int CreateBuildNumber(int previousNumber) => ++previousNumber; |
|
|
|
Task("GetGitInfo") |
|
.Does(()=> { |
|
_gitVersion = GitVersion(new GitVersionSettings()); |
|
|
|
if(debugScript) |
|
{ |
|
Information($"GitVersion Dump:\n{_gitVersion.Dump()}"); |
|
} |
|
|
|
Information("Git data Load successfully."); |
|
}); |
|
|
|
#region Android |
|
Task("UpdateAndroidAppIcon") |
|
.Does(()=>{ |
|
//TODO we'll implement variant icons later |
|
//manifest.ApplicationIcon = "@mipmap/ic_launcher"; |
|
Information($"Updated Androix App Icon with success"); |
|
}); |
|
|
|
|
|
Task("UpdateAndroidManifest") |
|
.IsDependentOn("GetGitInfo") |
|
.Does(()=> |
|
{ |
|
var buildVariant = GetVariant(); |
|
var manifestPath = Path.Combine(_slnPath, "src", "Android", "Properties", "AndroidManifest.xml"); |
|
|
|
// Cake.AndroidAppManifest doesn't currently enable us to access nested items so, quick (not ideal) fix: |
|
var manifestText = FileReadText(manifestPath); |
|
manifestText = manifestText.Replace("com.x8bit.bitwarden.", buildVariant.AndroidPackageName + "."); |
|
manifestText = manifestText.Replace("android:label=\"Bitwarden\"", $"android:label=\"{buildVariant.AppName}\""); |
|
FileWriteText(manifestPath, manifestText); |
|
|
|
var manifest = DeserializeAppManifest(manifestPath); |
|
|
|
var prevVersionCode = manifest.VersionCode; |
|
var prevVersionName = manifest.VersionName; |
|
_androidPackageName = manifest.PackageName; |
|
|
|
//manifest.VersionCode = CreateBuildNumber(prevVersionCode); |
|
manifest.VersionName = GetVersionName(prevVersionName, buildVariant, _gitVersion); |
|
manifest.PackageName = buildVariant.AndroidPackageName; |
|
manifest.ApplicationLabel = buildVariant.AppName; |
|
|
|
//Information($"AndroidManigest.xml VersionCode from {prevVersionCode} to {manifest.VersionCode}"); |
|
Information($"AndroidManigest.xml VersionName from {prevVersionName} to {manifest.VersionName}"); |
|
Information($"AndroidManigest.xml PackageName from {_androidPackageName} to {buildVariant.AndroidPackageName}"); |
|
Information($"AndroidManigest.xml ApplicationLabel to {buildVariant.AppName}"); |
|
|
|
SerializeAppManifest(manifestPath, manifest); |
|
|
|
Information("AndroidManifest updated with success!"); |
|
}); |
|
|
|
void ReplaceInFile(string filePath, string oldtext, string newtext) |
|
{ |
|
var fileText = FileReadText(filePath); |
|
|
|
if(string.IsNullOrEmpty(fileText) || !fileText.Contains(oldtext)) |
|
{ |
|
throw new Exception($"Couldn't find {filePath} or it didn't contain: {oldtext}"); |
|
} |
|
|
|
fileText = fileText.Replace(oldtext, newtext); |
|
|
|
FileWriteText(filePath, fileText); |
|
Information($"{filePath} modified successfully."); |
|
} |
|
|
|
Task("UpdateAndroidCodeFiles") |
|
.IsDependentOn("UpdateAndroidManifest") |
|
.Does(()=> { |
|
var buildVariant = GetVariant(); |
|
|
|
//We're not using _androidPackageName here because the codefile is currently slightly different string than the one in AndroidManifest.xml |
|
var keyName = "com.8bit.bitwarden"; |
|
var fixedPackageName = buildVariant.AndroidPackageName.Replace("x8bit", "8bit"); |
|
var filePath = Path.Combine(_slnPath, "src", "Android", "Services", "BiometricService.cs"); |
|
ReplaceInFile(filePath, keyName, fixedPackageName); |
|
|
|
var packageFileList = new string[] { |
|
Path.Combine(_slnPath, "src", "Android", "MainActivity.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "MainApplication.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Constants.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Accessibility", "AccessibilityService.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillHelpers.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Receivers", "EventUploadReceiver.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Receivers", "PackageReplacedReceiver.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Receivers", "RestrictionsChangedReceiver.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Services", "DeviceActionService.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Services", "FileService.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Tiles", "AutofillTileService.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Tiles", "GeneratorTileService.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "Tiles", "MyVaultTileService.cs"), |
|
Path.Combine(_slnPath, "src", "Android", "google-services.json"), |
|
Path.Combine(_slnPath, "store", "google", "Publisher", "Program.cs"), |
|
}; |
|
|
|
foreach(string path in packageFileList) |
|
{ |
|
ReplaceInFile(path, "com.x8bit.bitwarden", buildVariant.AndroidPackageName); |
|
} |
|
|
|
var labelFileList = new string[] { |
|
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"), |
|
}; |
|
|
|
foreach(string path in labelFileList) |
|
{ |
|
ReplaceInFile(path, "Bitwarden\"", $"{buildVariant.AppName}\""); |
|
} |
|
}); |
|
#endregion Android |
|
|
|
#region iOS |
|
enum iOSProjectType |
|
{ |
|
Null, |
|
MainApp, |
|
Autofill, |
|
Extension, |
|
ShareExtension, |
|
WatchApp |
|
} |
|
|
|
string GetiOSBundleId(VariantConfig buildVariant, iOSProjectType projectType) => projectType switch |
|
{ |
|
iOSProjectType.Autofill => $"{buildVariant.iOSBundleId}.autofill", |
|
iOSProjectType.Extension => $"{buildVariant.iOSBundleId}.find-login-action-extension", |
|
iOSProjectType.ShareExtension => $"{buildVariant.iOSBundleId}.share-extension", |
|
iOSProjectType.WatchApp => $"{buildVariant.iOSBundleId}.watchkitapp", |
|
_ => buildVariant.iOSBundleId |
|
}; |
|
|
|
string GetiOSBundleName(VariantConfig buildVariant, iOSProjectType projectType) => projectType switch |
|
{ |
|
iOSProjectType.Autofill => $"{buildVariant.AppName} Autofill", |
|
iOSProjectType.Extension => $"{buildVariant.AppName} Extension", |
|
iOSProjectType.ShareExtension => $"{buildVariant.AppName} Share Extension", |
|
_ => buildVariant.AppName |
|
}; |
|
|
|
private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, GitVersion git, iOSProjectType projectType = iOSProjectType.MainApp) |
|
{ |
|
var plistFile = File(plistPath); |
|
dynamic plist = DeserializePlist(plistFile); |
|
|
|
var prevVersionName = plist["CFBundleShortVersionString"]; |
|
var prevVersionString = plist["CFBundleVersion"]; |
|
var prevVersion = int.Parse(plist["CFBundleVersion"]); |
|
var prevBundleId = plist["CFBundleIdentifier"]; |
|
var prevBundleName = plist["CFBundleName"]; |
|
//var newVersion = CreateBuildNumber(prevVersion).ToString(); |
|
var newVersionName = GetVersionName(prevVersionName, buildVariant, git); |
|
var newBundleId = GetiOSBundleId(buildVariant, projectType); |
|
var newBundleName = GetiOSBundleName(buildVariant, projectType); |
|
|
|
plist["CFBundleName"] = newBundleName; |
|
plist["CFBundleDisplayName"] = newBundleName; |
|
//plist["CFBundleVersion"] = newVersion; |
|
plist["CFBundleShortVersionString"] = newVersionName; |
|
plist["CFBundleIdentifier"] = newBundleId; |
|
|
|
if(projectType == iOSProjectType.MainApp) |
|
{ |
|
_iOSVersionName = newVersionName; |
|
plist["CFBundleURLTypes"][0]["CFBundleURLName"] = $"{buildVariant.iOSBundleId}.url"; |
|
} |
|
|
|
if(projectType == iOSProjectType.Extension) |
|
{ |
|
var keyText = plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"]; |
|
plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"] = keyText.Replace("com.8bit.bitwarden", buildVariant.iOSBundleId); |
|
} |
|
|
|
SerializePlist(plistFile, plist); |
|
|
|
Information($"Changed app name from {prevBundleName} to {newBundleName}"); |
|
//Information($"Changed Bundle Version from {prevVersion} to {newVersion}"); |
|
Information($"Changed Bundle Short Version name from {prevVersionName} to {newVersionName}"); |
|
Information($"Changed Bundle Identifier from {prevBundleId} to {newBundleId}"); |
|
Information($"{plistPath} updated with success!"); |
|
} |
|
|
|
private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant) |
|
{ |
|
var EntitlementlistFile = File(entitlementsPath); |
|
dynamic Entitlements = DeserializePlist(EntitlementlistFile); |
|
|
|
Entitlements["aps-environment"] = buildVariant.ApsEnvironment; |
|
Entitlements["keychain-access-groups"] = new List<string>() { "$(AppIdentifierPrefix)" + buildVariant.iOSBundleId }; |
|
Entitlements["com.apple.security.application-groups"] = new List<string>() { $"group.{buildVariant.iOSBundleId}" };; |
|
|
|
Information($"Changed ApsEnvironment name to {buildVariant.ApsEnvironment}"); |
|
Information($"Changed keychain-access-groups bundleID to {buildVariant.iOSBundleId}"); |
|
|
|
SerializePlist(EntitlementlistFile, Entitlements); |
|
|
|
Information($"{entitlementsPath} updated with success!"); |
|
} |
|
|
|
private void UpdateWatchKitAppInfoPlist(string plistPath, VariantConfig buildVariant) |
|
{ |
|
var plistFile = File(plistPath); |
|
dynamic plist = DeserializePlist(plistFile); |
|
|
|
var prevBundleId = plist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"]; |
|
var newBundleId = GetiOSBundleId(buildVariant, iOSProjectType.WatchApp); |
|
|
|
plist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"] = newBundleId; |
|
|
|
SerializePlist(plistFile, plist); |
|
|
|
Information($"Changed Bundle Identifier from {prevBundleId} to {newBundleId}"); |
|
Information($"{plistPath} updated with success!"); |
|
} |
|
|
|
private void UpdateWatchPbxproj(string pbxprojPath, string newVersion) |
|
{ |
|
var fileText = FileReadText(pbxprojPath); |
|
if (string.IsNullOrEmpty(fileText)) |
|
{ |
|
throw new Exception($"Couldn't find {pbxprojPath}"); |
|
} |
|
|
|
const string pattern = @"MARKETING_VERSION = [^;]*;"; |
|
|
|
fileText = Regex.Replace(fileText, pattern, $"MARKETING_VERSION = {newVersion};"); |
|
|
|
FileWriteText(pbxprojPath, fileText); |
|
Information($"{pbxprojPath} modified successfully."); |
|
} |
|
|
|
/// <summary> |
|
/// Updates the target icons on the given appiconset target |
|
/// taking as source the icon in appIcons/iOS folder for the giving variant |
|
/// </summary> |
|
/// <param name="target">It can be <ios|watch|complication|macos></param> |
|
/// <param name="appiconsetTarget">Folder to copy the generated icons to</param> |
|
private void UpdateAppleIcons(string target, string appiconsetTarget) |
|
{ |
|
Information($"Updating {target} App Icons"); |
|
|
|
var iconsTempDirPath = Path.Combine(_slnPath, "appIcons", "temp"); |
|
CreateDirectory(iconsTempDirPath); |
|
|
|
var arguments = new ProcessArgumentBuilder(); |
|
arguments.Append(target); |
|
arguments.Append(Path.Combine(_slnPath, "appIcons", "iOS", $"{variant}.png")); |
|
arguments.Append(iconsTempDirPath); |
|
|
|
using(var process = StartAndReturnProcess(Path.Combine(_slnPath, "appIcons", "icongen.sh"), |
|
new ProcessSettings { Arguments = arguments })) |
|
{ |
|
process.WaitForExit(); |
|
Information("Exit code: {0}", process.GetExitCode()); |
|
} |
|
|
|
var generatedIconsPath = Path.Combine(iconsTempDirPath, "*.png"); |
|
CopyFiles(generatedIconsPath, appiconsetTarget); |
|
|
|
DeleteDirectory(iconsTempDirPath, new DeleteDirectorySettings { |
|
Recursive = true, |
|
Force = true |
|
}); |
|
|
|
Information($"{target} App Icons have been updated"); |
|
} |
|
|
|
Task("UpdateiOSIcons") |
|
.Does(()=>{ |
|
UpdateAppleIcons("ios", Path.Combine(_slnPath, "src", "iOS", "Resources", "Assets.xcassets", "AppIcons.appiconset")); |
|
UpdateAppleIcons("watch", Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit App", "Assets.xcassets", "AppIcon.appiconset")); |
|
// TODO: Update complication icons when they start working |
|
}); |
|
|
|
Task("UpdateiOSPlist") |
|
.IsDependentOn("GetGitInfo") |
|
.Does(()=> { |
|
var buildVariant = GetVariant(); |
|
var infoPath = Path.Combine(_slnPath, "src", "iOS", "Info.plist"); |
|
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS", "Entitlements.plist"); |
|
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.MainApp); |
|
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant); |
|
}); |
|
|
|
Task("UpdateiOSAutofillPlist") |
|
.IsDependentOn("GetGitInfo") |
|
.IsDependentOn("UpdateiOSPlist") |
|
.Does(()=> { |
|
var buildVariant = GetVariant(); |
|
var infoPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Info.plist"); |
|
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Entitlements.plist"); |
|
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Autofill); |
|
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant); |
|
}); |
|
|
|
Task("UpdateiOSExtensionPlist") |
|
.IsDependentOn("GetGitInfo") |
|
.IsDependentOn("UpdateiOSPlist") |
|
.Does(()=> { |
|
var buildVariant = GetVariant(); |
|
var infoPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Info.plist"); |
|
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Entitlements.plist"); |
|
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Extension); |
|
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant); |
|
}); |
|
|
|
Task("UpdateiOSShareExtensionPlist") |
|
.IsDependentOn("GetGitInfo") |
|
.IsDependentOn("UpdateiOSPlist") |
|
.Does(()=> { |
|
var buildVariant = GetVariant(); |
|
var infoPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Info.plist"); |
|
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Entitlements.plist"); |
|
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.ShareExtension); |
|
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant); |
|
}); |
|
|
|
Task("UpdateiOSCodeFiles") |
|
.IsDependentOn("UpdateiOSPlist") |
|
.Does(()=> { |
|
var buildVariant = GetVariant(); |
|
var fileList = new string[] { |
|
Path.Combine(_slnPath, "src", "iOS.Core", "Utilities", "iOSCoreHelpers.cs"), |
|
Path.Combine(_slnPath, "src", "iOS.Core", "Constants.cs"), |
|
Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj"), |
|
Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit Extension", "Helpers", "KeychainHelper.swift"), |
|
Path.Combine(".github", "resources", "export-options-ad-hoc.plist"), |
|
Path.Combine(".github", "resources", "export-options-app-store.plist") |
|
}; |
|
|
|
foreach(string path in fileList) |
|
{ |
|
ReplaceInFile(path, "com.8bit.bitwarden", buildVariant.iOSBundleId); |
|
} |
|
}); |
|
|
|
Task("UpdateWatchProject") |
|
.IsDependentOn("UpdateiOSPlist") |
|
.WithCriteria(() => !string.IsNullOrEmpty(_iOSVersionName)) |
|
.Does(()=> { |
|
var watchProjectPath = Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj"); |
|
UpdateWatchPbxproj(watchProjectPath, _iOSVersionName); |
|
}); |
|
|
|
Task("UpdateWatchKitAppInfoPlist") |
|
.Does(()=> { |
|
var buildVariant = GetVariant(); |
|
var infoPath = Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit Extension", "Info.plist"); |
|
UpdateWatchKitAppInfoPlist(infoPath, buildVariant); |
|
}); |
|
|
|
#endregion iOS |
|
|
|
#region Main Tasks |
|
Task("Android") |
|
//.IsDependentOn("UpdateAndroidAppIcon") |
|
.IsDependentOn("UpdateAndroidManifest") |
|
.IsDependentOn("UpdateAndroidCodeFiles") |
|
.Does(()=> |
|
{ |
|
Information("Android app updated"); |
|
}); |
|
|
|
Task("iOS") |
|
.IsDependentOn("UpdateiOSIcons") |
|
.IsDependentOn("UpdateiOSPlist") |
|
.IsDependentOn("UpdateiOSAutofillPlist") |
|
.IsDependentOn("UpdateiOSExtensionPlist") |
|
.IsDependentOn("UpdateiOSShareExtensionPlist") |
|
.IsDependentOn("UpdateiOSCodeFiles") |
|
.IsDependentOn("UpdateWatchProject") |
|
.IsDependentOn("UpdateWatchKitAppInfoPlist") |
|
.Does(()=> |
|
{ |
|
Information("iOS app updated"); |
|
}); |
|
|
|
Task("Default") |
|
.Does(() => { |
|
var usage = @"Missing target. |
|
|
|
Usage: |
|
dotnet cake build.cake --target (Android | iOS) --variant (dev | qa | beta | prod) |
|
|
|
Options: |
|
--debugScript=<bool> Script debug mode. |
|
"; |
|
Information(usage); |
|
}); |
|
#endregion Main Tasks |
|
|
|
RunTarget(target); |