-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from teaxyz/refactor
refactor into multiple categories/files
- Loading branch information
Showing
9 changed files
with
1,083 additions
and
1,053 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
#import "teaBASE.h" | ||
|
||
@implementation teaBASE (DevTools) | ||
|
||
- (IBAction)installBrew:(NSSwitch *)sender { | ||
if (![NSFileManager.defaultManager isExecutableFileAtPath:@"/Library/Developer/CommandLineTools/usr/bin/git"]) { | ||
NSAlert *alert = [NSAlert new]; | ||
alert.messageText = @"Prerequisite Unsatisfied"; | ||
alert.informativeText = @"Homebrew requires the Xcode Command Line Tools (CLT) to be installed first"; | ||
[alert runModal]; | ||
|
||
[sender setState:NSControlStateValueOff]; | ||
return; | ||
} | ||
|
||
if (sender.state == NSControlStateValueOn) { | ||
[self.brewManualInstallInstructions setEditable:YES]; | ||
[self.brewManualInstallInstructions checkTextInDocument:sender]; | ||
[self.brewManualInstallInstructions setEditable:NO]; | ||
|
||
[self.mainView.window beginSheet:self.brewInstallWindow completionHandler:^(NSModalResponse returnCode) { | ||
if (returnCode != NSModalResponseOK) { | ||
[self.homebrewSwitch setState:NSControlStateValueOff]; | ||
} else { | ||
[self updateVersions]; | ||
} | ||
[self.brewInstallWindowSpinner stopAnimation:sender]; | ||
}]; | ||
} else { | ||
#if __arm64 | ||
// Get the contents of the directory | ||
NSError *error = nil; | ||
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/opt/homebrew" error:&error]; | ||
|
||
if (error) { | ||
[[NSAlert alertWithError:error] runModal]; | ||
return; | ||
} | ||
|
||
// Iterate over each item in the directory | ||
for (NSString *item in contents) { | ||
NSString *itemPath = [@"/opt/homebrew" stringByAppendingPathComponent:item]; | ||
|
||
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:itemPath error:&error]; | ||
if (!success) { | ||
[[NSAlert alertWithError:error] runModal]; | ||
return; | ||
} | ||
} | ||
|
||
[self updateVersions]; | ||
#else | ||
NSAlert *alert = [NSAlert new]; | ||
alert.informativeText = @"Please manually run the Homebrew uninstall script"; | ||
[alert runModal]; | ||
[sender setState:NSControlStateValueOn]; | ||
#endif | ||
} | ||
} | ||
|
||
static BOOL installer(NSURL *url) { | ||
NSURL *newurl = [[url URLByDeletingPathExtension] URLByAppendingPathExtension:@".pkg"]; | ||
[NSFileManager.defaultManager moveItemAtURL:url toURL:newurl error:nil]; | ||
|
||
char *arguments[] = {"-pkg", (char*)newurl.fileSystemRepresentation, "-target", "/", NULL}; | ||
|
||
return sudo_run_cmd("/usr/sbin/installer", arguments, @"Homebrew install failed"); | ||
} | ||
|
||
static NSString* fetchLatestBrewVersion(void) { | ||
NSURL *url = [NSURL URLWithString:@"https://api.github.com/repos/Homebrew/brew/releases/latest"]; | ||
NSData *data = [NSData dataWithContentsOfURL:url]; | ||
if (!data) return nil; | ||
|
||
NSError *error = nil; | ||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; | ||
if (error || !json[@"tag_name"]) return nil; | ||
|
||
NSString *version = json[@"tag_name"]; | ||
if ([version hasPrefix:@"v"]) { | ||
version = [version substringFromIndex:1]; | ||
} | ||
return version; | ||
} | ||
|
||
- (IBAction)installBrewStep2:(NSButton *)sender { | ||
[sender setEnabled:NO]; | ||
[self.brewInstallWindowSpinner startAnimation:sender]; | ||
|
||
NSString *version = fetchLatestBrewVersion(); | ||
if (!version) { | ||
NSAlert *alert = [NSAlert new]; | ||
alert.messageText = @"Failed to fetch latest Homebrew version"; | ||
alert.informativeText = @"Please try again later or install manually."; | ||
[alert runModal]; | ||
[NSApp endSheet:self.brewInstallWindow returnCode:NSModalResponseAbort]; | ||
[sender setEnabled:YES]; | ||
return; | ||
} | ||
|
||
NSString *urlstr = [NSString stringWithFormat:@"https://github.com/Homebrew/brew/releases/download/%@/Homebrew-%@.pkg", version, version]; | ||
NSURL *url = [NSURL URLWithString:urlstr]; | ||
|
||
[[[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { | ||
if (error) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
[[NSAlert alertWithError:error] runModal]; | ||
[NSApp endSheet:self.brewInstallWindow returnCode:NSModalResponseAbort]; | ||
[sender setEnabled:YES]; | ||
}); | ||
} else if (installer(location)) { | ||
// ^^ runs the installer on the NSURLSession queue as the download | ||
// is deleted when it exits. afaict this is fine. | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
|
||
if (self.setupBrewShellEnvCheckbox.state == NSControlStateValueOn) { | ||
NSString *zprofilePath = [NSHomeDirectory() stringByAppendingPathComponent:@".zprofile"]; | ||
NSString *cmdline = [NSString stringWithFormat:@"eval \"$(%@ shellenv)\"", brewPath()]; | ||
|
||
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:zprofilePath]; | ||
|
||
// Check if the file exists, if not create it | ||
if (!exists) { | ||
[[NSFileManager defaultManager] createFileAtPath:zprofilePath contents:nil attributes:nil]; | ||
} | ||
if (!file_contains(zprofilePath, cmdline)) { | ||
// Open the file for appending | ||
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:zprofilePath]; | ||
if (fileHandle) { | ||
[fileHandle seekToEndOfFile]; | ||
if (exists) { | ||
[fileHandle writeData:[@"\n" dataUsingEncoding:NSUTF8StringEncoding]]; | ||
} | ||
[fileHandle writeData:[cmdline dataUsingEncoding:NSUTF8StringEncoding]]; | ||
[fileHandle writeData:[@"\n" dataUsingEncoding:NSUTF8StringEncoding]]; | ||
[fileHandle closeFile]; | ||
} else { | ||
//TODO | ||
} | ||
} | ||
} | ||
|
||
[NSApp endSheet:self.brewInstallWindow returnCode:NSModalResponseOK]; | ||
[sender setEnabled:YES]; | ||
}); | ||
} else { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
NSAlert *alert = [NSAlert new]; | ||
alert.messageText = @"Installation Error"; | ||
alert.informativeText = @"Unknown error occurred. Please install Homebrew manually."; | ||
[alert runModal]; | ||
[NSApp endSheet:self.brewInstallWindow returnCode:NSModalResponseAbort]; | ||
[sender setEnabled:YES]; | ||
}); | ||
} | ||
}] resume]; | ||
} | ||
|
||
- (IBAction)installPkgx:(NSSwitch *)sender { | ||
if (sender.state == NSControlStateValueOn) { | ||
[self installSubexecutable:@"pkgx"]; | ||
[self updateVersions]; | ||
} else { | ||
char *args[] = {"/usr/local/bin/pkgx", NULL}; | ||
sudo_run_cmd("/bin/rm", args, @"Couldn’t delete /usr/local/bin/pkgx"); | ||
} | ||
} | ||
|
||
- (IBAction)installDocker:(NSSwitch *)sender { | ||
// using Terminal as the install steps requires `sudo` | ||
// using script as you can only pass a single arg to Terminal.app apparently | ||
id path = [[[NSBundle bundleForClass:[self class]] bundlePath] stringByAppendingPathComponent:@"Contents/Scripts/install-docker.sh"]; | ||
run(@"/usr/bin/open", @[@"-a", @"Terminal.app", path], nil); | ||
} | ||
|
||
- (IBAction)openDockerHome:(id)sender { | ||
NSURL *url = [NSURL URLWithString:@"https://docker.com"]; | ||
[[NSWorkspace sharedWorkspace] openURL:url]; | ||
} | ||
|
||
- (IBAction)openPkgxHome:(id)sender { | ||
NSURL *url = [NSURL URLWithString:@"https://pkgx.sh"]; | ||
[[NSWorkspace sharedWorkspace] openURL:url]; | ||
} | ||
|
||
- (IBAction)openHomebrewHome:(id)sender { | ||
NSURL *url = [NSURL URLWithString:@"https://brew.sh"]; | ||
[[NSWorkspace sharedWorkspace] openURL:url]; | ||
} | ||
|
||
- (IBAction)openXcodeCLTHome:(id)sender { | ||
NSURL *url = [NSURL URLWithString:@"https://developer.apple.com/xcode/resources/"]; | ||
[[NSWorkspace sharedWorkspace] openURL:url]; | ||
} | ||
|
||
- (IBAction)gitAddOnsHelpButton:(id)sender { | ||
NSURL *url = [NSURL URLWithString:@"https://github.com/pkgxdev/git-gud"]; | ||
[[NSWorkspace sharedWorkspace] openURL:url]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
#import "teaBASE.h" | ||
|
||
@implementation teaBASE (dotfileSync) | ||
|
||
- (BOOL)dotfileSyncEnabled { | ||
return run(@"/bin/launchctl", @[@"list", @"xyz.tea.BASE.dotfile-sync"], nil); | ||
} | ||
|
||
- (BOOL)dotfileDirThere { | ||
BOOL isdir = NO; | ||
id path = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/teaBASE/dotfiles.git"]; | ||
if (![NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isdir]) return NO; | ||
if (!isdir) return NO; | ||
return YES; | ||
} | ||
|
||
- (IBAction)onDotfileSyncToggled:(NSSwitch *)sender { | ||
|
||
id dst = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/LaunchAgents/xyz.tea.BASE.dotfile-sync.plist"]; | ||
|
||
if (sender.state == NSControlStateValueOn) { | ||
NSBundle *bundle = [NSBundle bundleForClass:[self class]]; | ||
id src = [bundle pathForResource:@"xyz.tea.BASE.dotfile-sync" ofType:@"plist"]; | ||
NSString *contents = [NSString stringWithContentsOfFile:src encoding:NSUTF8StringEncoding error:nil]; | ||
id prefpane_path = [bundle bundlePath]; | ||
contents = [contents stringByReplacingOccurrencesOfString:@"$PREFPANE" withString:prefpane_path]; | ||
contents = [contents stringByReplacingOccurrencesOfString:@"$HOME" withString:NSHomeDirectory()]; | ||
[NSFileManager.defaultManager createDirectoryAtPath:[dst stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil]; | ||
[contents writeToFile:dst atomically:NO encoding:NSUTF8StringEncoding error:nil]; | ||
run(@"/bin/launchctl", @[@"load", dst], nil); | ||
|
||
//TODO need to wait for the above launchctl job to finish lol | ||
|
||
NSLog(@"teaBASE: %@", self.dotfileDirThere ? @"YES" :@"NO"); | ||
|
||
if (!self.dotfileDirThere) { | ||
NSString *script_path = [[[NSBundle bundleForClass:[self class]] bundlePath] stringByAppendingPathComponent:@"Contents/Scripts/dotfile-sync.sh"]; | ||
|
||
run(@"/usr/bin/open", @[ | ||
@"-a", @"Terminal.app", script_path | ||
], nil); | ||
} | ||
|
||
} else { | ||
run(@"/bin/launchctl", @[@"unload", dst], nil); | ||
[[NSFileManager defaultManager] removeItemAtPath:dst error:nil]; | ||
} | ||
|
||
//FIXME need to know when the below script finishes before loading us into launchctl | ||
|
||
BOOL worked = [self dotfileSyncEnabled]; | ||
|
||
[self.dotfileSyncEditWhitelistButton setEnabled:worked]; | ||
[self.dotfileSyncViewRepoButton setEnabled:worked]; | ||
[self.dotfileSyncSwitch setState:worked ? NSControlStateValueOn : NSControlStateValueOff]; | ||
} | ||
|
||
- (IBAction)viewDotfilesRepo:(id)sender { | ||
//TODO use the origin remote URL to figure this out instead | ||
id pkgx = [[[NSBundle bundleForClass:[self class]] bundlePath] stringByAppendingPathComponent:@"Contents/MacOS/pkgx"]; | ||
run(pkgx, @[@"gh", @"repo", @"view", @"--web", @"dotfiles"], nil); | ||
} | ||
|
||
- (IBAction)editWhitelist:(id)sender { | ||
id url = @"https://github.com/teaxyz/teaBASE/blob/main/Scripts/dotfile-sync.sh"; | ||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]]; | ||
} | ||
|
||
@end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
#import "teaBASE.h" | ||
|
||
@implementation teaBASE (GPG) | ||
|
||
- (BOOL)gpgSignEnabled { | ||
id pkgx = [[[NSBundle bundleForClass:[self class]] bundlePath] stringByAppendingPathComponent:@"Contents/MacOS/pkgx"]; | ||
return [output(pkgx, @[@"git", @"config", @"--global", @"commit.gpgsign"]) isEqualToString:@"true"]; | ||
} | ||
|
||
- (IBAction)signCommits:(NSSwitch *)sender { | ||
NSString *git = which(@"git"); | ||
NSArray *config = @[@"config", @"--global"]; | ||
|
||
if ([git isEqualToString:@"/usr/bin/git"] && ![self xcodeCLTInstalled]) { | ||
git = [[[NSBundle bundleForClass:[self class]] bundlePath] stringByAppendingPathComponent:@"Contents/MacOS/pkgx"]; | ||
config = @[@"git", @"config", @"--global"]; | ||
} | ||
|
||
if (sender.state == NSControlStateValueOn) { | ||
|
||
//FIXME if XDG_* vars set uses that which requires us to run a script in a login shell to extract | ||
id configfile = [NSHomeDirectory() stringByAppendingPathComponent:@".config/pkgx/bpb.toml"]; | ||
if (![NSFileManager.defaultManager isReadableFileAtPath:configfile]) { | ||
configfile = [NSHomeDirectory() stringByAppendingPathComponent:@".local/share/pkgx/bpb.toml"]; | ||
} | ||
|
||
if ([NSFileManager.defaultManager isReadableFileAtPath:configfile]) { | ||
run(git, [config arrayByAddingObjectsFromArray:@[@"commit.gpgsign", @"true"]], nil); | ||
run(git, [config arrayByAddingObjectsFromArray:@[@"gpg.program", @"bpb"]], nil); | ||
|
||
if (![NSFileManager.defaultManager isExecutableFileAtPath:@"/usr/local/bin/bpb"]) { | ||
[self installSubexecutable:@"bpb"]; | ||
} | ||
|
||
[self calculateSecurityRating]; | ||
} | ||
else [self.mainView.window beginSheet:self.gpgPassphraseWindow completionHandler:^(NSModalResponse returnCode) { | ||
if (returnCode != NSModalResponseOK) { | ||
[self.gpgSignSwitch setState:NSControlStateValueOff]; | ||
} else { | ||
id username = self.setupGPGWindowUsername.stringValue; | ||
id email = self.setupGPGWindowEmail.stringValue; | ||
[self installBPB:username email:email]; | ||
|
||
//TODO need to have a git installed first | ||
run(git, [config arrayByAddingObjectsFromArray:@[@"commit.gpgsign", @"true"]], nil); | ||
run(git, [config arrayByAddingObjectsFromArray:@[@"gpg.program", @"bpb"]], nil); | ||
[self calculateSecurityRating]; | ||
} | ||
}]; | ||
} else { | ||
run(git, [config arrayByAddingObjectsFromArray:@[@"commit.gpgsign", @"false"]], nil); | ||
[self calculateSecurityRating]; | ||
} | ||
} | ||
|
||
- (void)installBPB:(id)username email:(id)email { | ||
if (![NSFileManager.defaultManager isExecutableFileAtPath:@"/usr/local/bin/bpb"]) { | ||
[self installSubexecutable:@"bpb"]; | ||
} | ||
|
||
if (![NSFileManager.defaultManager isReadableFileAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@".bpb_keys.toml"]]) { | ||
id initstr = [NSString stringWithFormat:@"%@ <%@>", username, email]; | ||
run(@"/usr/local/bin/bpb", @[@"init", initstr], nil); | ||
} | ||
} | ||
|
||
- (IBAction)printGPGEmergencyKit:(id)sender { | ||
NSString *pubkey = output(@"/usr/local/bin/bpb", @[@"print"]); | ||
NSString *privkey = output(@"/usr/bin/security", @[@"find-generic-password", @"-s", @"xyz.tea.BASE.bpb", @"-w"]); | ||
|
||
if (!pubkey || !privkey) { | ||
NSAlert *alert = [NSAlert new]; | ||
alert.informativeText = @"An error occurred trying to obtain your GPG keypair"; | ||
[alert runModal]; | ||
return; | ||
} | ||
|
||
id content = @"Public Key:\n\n"; | ||
content = [content stringByAppendingString:pubkey]; | ||
content = [content stringByAppendingString:@"\n\nPrivate Key:\n\n"]; | ||
content = [content stringByAppendingString:privkey]; | ||
|
||
// Create an NSTextView and set the document content | ||
NSTextView *textView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, 612, 612)]; // Typical page size | ||
[textView setString:content]; | ||
|
||
// Set font to Menlo (monospace) and make it smaller in order to fit the page | ||
NSFont *monoFont = [NSFont fontWithName:@"Menlo" size:9.6]; | ||
[textView setFont:monoFont]; | ||
[[textView textStorage] setFont:monoFont]; // Ensure the entire text storage uses the font | ||
|
||
// Configure the print operation for the text view | ||
NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:textView]; | ||
[printOperation setShowsPrintPanel:YES]; | ||
[printOperation setShowsProgressPanel:YES]; | ||
[printOperation setJobTitle:@"GPG_Emergency_Kit.pdf"]; | ||
|
||
// Run the print operation (this will display the print dialog) | ||
[printOperation runOperationModalForWindow:[sender window] delegate:self didRunSelector:@selector(didPrintGPGEmergencyKit:success:) contextInfo:nil]; | ||
} | ||
|
||
- (void)didPrintGPGEmergencyKit:(NSPrintOperation *)op success:(BOOL)success { | ||
if (success) { | ||
self.greenCheckGPGBackup.hidden = NO; | ||
[self.defaultsController.defaults setValue:@YES forKey:@"xyz.tea.BASE.printed-GPG-emergency-kit"]; | ||
[self calculateSecurityRating]; | ||
} | ||
} | ||
|
||
@end |
Oops, something went wrong.