Browse Source

make the code more modularized, make it capable of building a bootstrap from scratch

Kevin Bradley 9 months ago
parent
commit
10a73b9156

+ 5 - 0
bootstrapTool/Classes/HelperClass.h

@@ -11,4 +11,9 @@
 + (void)runProcess:(NSString *)call environment:(NSDictionary *)env currentPath:(NSString *)currentPath completion:(void(^)(NSString *output, NSInteger returnStatus))block;
 + (int)runCommand:(NSString *)call environment:(NSDictionary *)env currentPath:(NSString *)currentPath verbose:(BOOL)verbose;
 + (NSString *)validLDIDPath;
++ (int)bumpPackageAtPath:(NSString *)debFile withPrefix:(NSString *)prefix sigChecks:(BOOL)skip;
++ (int)repackagePackageAtPath:(NSString *)debFile withPrefix:(NSString *)prefix sigChecks:(BOOL)skip;
++ (int)cleanBootstrapAtPath:(NSString *)bootstrapPath;
++ (int)deletePackage:(NSString *)deletePackage inBootstrap:(NSString *)bootstrapPath;
++ (int)createNewBootstrap:(NSString *)bootstrapPath withPackages:(NSString *)packagesFolder;
 @end

+ 183 - 0
bootstrapTool/Classes/HelperClass.m

@@ -4,6 +4,189 @@
 
 @implementation HelperClass
 
+/*
+ tar cpJ -v -p --exclude .DS_Store --exclude docs --exclude share/doc --exclude share/info --exclude share/man --exclude man --exclude share/readline -f bs.tar.xz fs private var
+ */
+
++ (NSArray *)excludeArgs {
+    return @[@".DS_Store", @"docs", @"share/doc", @"share/info", @"share/man", @"man", @"share/readline"];
+}
+
++ (NSInteger)createBootstrapTar:(NSString *)tarFile withBootstrap:(NSString *)bootstrap {
+
+    __block NSMutableArray *excludeArgs = [NSMutableArray new];
+    [excludeArgs addObject:@"--exclude"];
+    [[self excludeArgs] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        [excludeArgs addObject:obj];
+        DLog(@"idx: %lu count: %lu", idx, excludeArgs.count);
+        if (idx < excludeArgs.count-1){
+            [excludeArgs addObject:@"--exclude"];
+        }
+    }];
+    return 0;
+}
+
++ (int)createNewBootstrap:(NSString *)bootstrapPath withPackages:(NSString *)packagesFolder {
+    [FM createDirectoryAtPath:bootstrapPath withIntermediateDirectories:true attributes:nil error:nil];
+    NSArray *folders = @[@"var/lib/dpkg/info",
+                         @"fs/jb/usr"];
+    [folders enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        NSString *path = [bootstrapPath stringByAppendingPathComponent:obj];
+        [FM createDirectoryAtPath:path withIntermediateDirectories:true attributes:nil error:nil];
+    }];
+    NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"var/lib/dpkg/status"];
+    if (![FM fileExistsAtPath:statusFile]){
+        [@"" writeToFile:statusFile atomically:true encoding:NSUTF8StringEncoding error:nil];
+    }
+    NSArray *packagesArray = [FM contentsOfDirectoryAtPath:packagesFolder error:nil];
+    [packagesArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        NSString *debFile = [packagesFolder stringByAppendingPathComponent:obj];
+        DLog(@"processing package: %@", debFile);
+        InputPackage *output = [HelperClass packageForDeb:debFile];
+        output.forceOverwrite = true;
+        [output installToBootstrapPath:bootstrapPath];
+    }];
+    return 0;
+}
+
++ (int)deletePackage:(NSString *)deletePackage inBootstrap:(NSString *)bootstrapPath {
+    DLog(@"deleting package: %@ from bootstrap: %@", deletePackage, bootstrapPath);
+    __block NSString *postRmFile = nil;
+    NSString *statusFile = [bootstrapPath relativeStatusFilePath];
+    NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
+    StatusPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", deletePackage]] lastObject];
+    if (model) {
+        NSString *listName = [NSString stringWithFormat:@"%@.list", deletePackage];
+        NSString *infoPath = [bootstrapPath relativeInfoFolderPath];
+        NSString *listFile = [infoPath stringByAppendingPathComponent:listName];
+        NSArray *listFiles = [FM contentsOfDirectoryAtPath:infoPath error:nil];
+        NSPredicate *blockPred = [NSPredicate predicateWithBlock:^BOOL(id  _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
+            if ([evaluatedObject containsString:deletePackage]){
+                return TRUE;
+            }
+            return FALSE;
+        }];
+        NSArray *relatedFiles = [listFiles filteredArrayUsingPredicate:blockPred]; //any files in dpkg/info/deletePackage.*
+        DLog(@"\nWe have found the package, determining list details from: %@", listFile);
+        NSString *listContents = [NSString stringWithContentsOfFile:listFile encoding:NSUTF8StringEncoding error:nil];
+        DLog(@"\nContents: %@\n", listContents);
+        DLog(@"\nFound package info files: %@\n", relatedFiles);
+        NSArray *files = [listContents componentsSeparatedByString:@"\n"];
+        __block NSMutableArray *filesToProcess = [NSMutableArray new];
+        [relatedFiles enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+            NSString *fullPath = [infoPath stringByAppendingPathComponent:obj];
+            if([FM fileExistsAtPath:fullPath]){
+                if ([[obj pathExtension] isEqualToString:@"postrm"]){
+                    postRmFile = fullPath;
+                }
+                [filesToProcess addObject:fullPath];
+            }
+        }];
+        [files enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+            NSString *fullPath = [bootstrapPath stringByAppendingPathComponent:obj];
+            BOOL isDir = FALSE;
+            if([FM fileExistsAtPath:fullPath isDirectory:&isDir]){
+                if (!isDir){
+                    DLog(@"found file: %@", fullPath);
+                    [filesToProcess addObject:fullPath];
+                }
+            }
+        }];
+        DLog(@"\nFiles to process: %@\n", filesToProcess);
+        NSString *error = [NSString stringWithFormat:@" [WARNING] We are about to delete '%@' from the bootstrap, this cannot be undone!", deletePackage];
+        if(![HelperClass shouldContinueWithError:error]){
+            return -1;
+        }
+        [filesToProcess enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+            [FM removeItemAtPath:obj error:nil];
+        }];
+        
+        NSString *statusFile = [bootstrapPath relativeStatusFilePath];
+        [FM copyItemAtPath:statusFile toPath:[statusFile stringByAppendingPathExtension:@"bak"] error:nil];
+        __block NSMutableString *newStatusFile = [NSMutableString new];
+        [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+            if (obj != model){
+                [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
+            } else {
+                DLog(@"skipping the model we want to delete: %@", obj);
+            }
+        }];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        [newStatusFile writeToFile:statusFile atomically:TRUE];
+#pragma clang diagnostic pop
+        if (postRmFile) {
+            DLog(@"\n [WARNING] We found a postrm file, will not run this due to potential unexpected consequences off of the target device, displaying contents so you can determine if any additional steps are necessary\n");
+            DLog(@"\n\n%@ contents:\n%@\n\n", postRmFile, [NSString stringWithContentsOfFile:postRmFile encoding:NSUTF8StringEncoding error:nil]);
+        }
+    }
+    return 0;
+}
+
++ (int)cleanBootstrapAtPath:(NSString *)bootstrapPath {
+    NSString *statusFile = [bootstrapPath relativeStatusFilePath];
+    NSString *listContents = [NSString stringWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
+    //clean up any errant spaces
+    listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
+    listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n\n" withString:@"\n\n"];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    [listContents writeToFile:statusFile atomically:TRUE];
+#pragma clang diagnostic pop
+    DLog(@"\n [INFO] Cleaning status file: %@", statusFile);
+    
+    NSString *testStatusFile = [bootstrapPath relativeStatusFilePath];
+    NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
+    __block NSMutableString *newStatusFile = [NSMutableString new];
+    __block NSMutableArray *alreadyAdded = [NSMutableArray new];
+    
+    [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        DLog(@"\n [INFO] Processing package: %@", obj.package);
+        if (![alreadyAdded containsObject:obj.package]){
+            //duplicate check first
+            NSArray *doubleCheck = [installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", obj.package]];
+            if (doubleCheck.count > 1){
+                NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:@"version" ascending:FALSE];
+                StatusPackageModel *latestVersion = [[doubleCheck sortedArrayUsingDescriptors:@[sortDesc]] firstObject];
+                DLog(@"\n [INFO] We found a duplicate package, choosing latestVersion: %@", latestVersion);
+                [newStatusFile appendFormat:@"%@\n\n", latestVersion.rawString];
+                [alreadyAdded addObject:obj.package];
+            } else {
+                [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
+            }
+        } else {
+            DLog(@"\n [INFO] We already had this package, skipping older duplicate: %@\n", obj.package);
+        }
+    }];
+    testStatusFile = [testStatusFile stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
+    DLog(@"\n Writing new output file: %@\n", testStatusFile);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    [newStatusFile writeToFile:testStatusFile atomically:TRUE];
+#pragma clang diagnostic pop
+    DLog(@"\n Done!\n\n");
+    return 0;
+    
+}
+
++ (int)repackagePackageAtPath:(NSString *)debFile withPrefix:(NSString *)prefix sigChecks:(BOOL)skip {
+    DLog(@"\n [INFO] Repackaging file: %@...\n", debFile);
+    InputPackage *output = [HelperClass packageForDeb:debFile];
+    output.appendedPrefix = prefix;
+    output.skipSignatureChecks = skip;
+    [output repackageInCurrentDirectoryWithArch:@"appletvos-arm64"];
+    return 0;
+}
+
++ (int)bumpPackageAtPath:(NSString *)debFile withPrefix:(NSString *)prefix sigChecks:(BOOL)skip {
+    DLog(@"\n [INFO] Bumping version number for file: %@...\n", debFile);
+    InputPackage *output = [HelperClass packageForDeb:debFile];
+    output.appendedPrefix = prefix;
+    output.skipSignatureChecks = skip;
+    [output bumpVersionInCurrentDirectory];
+    return 0;
+}
+
 + (NSArray <StatusPackageModel*>*)statusInstalledPackagesFromFile:(NSString *)statusFile {
     
     if (![FM fileExistsAtPath:statusFile]) {

+ 1 - 1
bootstrapTool/Classes/NSString+Additions.m

@@ -24,7 +24,7 @@
     if (![FM fileExistsAtPath:firstTry]){
         firstTry = [[self stringByExpandingTildeInPath] stringByAppendingPathComponent:@"var/lib/dpkg/info"];
         if (![FM fileExistsAtPath:firstTry]) {
-            DLog(@"no relative status file found!");
+            DLog(@"no relative info file found!");
             return nil;
         }
     }

+ 21 - 126
bootstrapTool/main.m

@@ -16,7 +16,7 @@
 #import <sys/utsname.h>
 #import "HelperClass.h"
 
-#define OPTION_FLAGS "o:i:ld:hcrbp:su:y"
+#define OPTION_FLAGS "o:i:ld:hcrbp:su:yn:"
 
 char *progname;
 char *path;
@@ -34,6 +34,7 @@ static struct option longopts[] = {
     { "skip",                      no_argument,            NULL,   's' },
     { "unique",                    required_argument,      NULL,   'u' },
     { "yes",                       no_argument,            NULL,   'y' },
+    { "new",                       required_argument,      NULL,   'n' },
     { NULL,                        0,                      NULL,    0  }
 };
 
@@ -42,6 +43,7 @@ void cmd_help(void){
     printf("Makes various modifications to a raw bootstrap folder\n\n");
     
     printf("  -h, --help\t\t\tprints usage information\n");
+    printf("  -n, --new\t\t\tcreates a brand new bootstrap with debs specified by this folder\n");
     printf("  -i, --input\t\t\tthe deb package to process\n");
     printf("  -d, --delete\t\t\tthe package to delete\n");
     printf("  -l, --list\t\t\tlist all the packages installed on this bootstrap\n");
@@ -68,12 +70,14 @@ int main(int argc, char **argv) {
         NSString *bootstrapPath = nil;
         NSString *deletePackage = nil;
         NSString *prefix = nil;
+        NSString *packagesFolder = nil;
         BOOL listPackage = FALSE;
         BOOL repackage = FALSE;
         BOOL skip = FALSE;
         BOOL bump = FALSE;
         BOOL clean = FALSE;
         BOOL forceYes = FALSE;
+        BOOL new = FALSE;
         /*
         NSString *searchPathString = @"/Users/bradleyk/.rbenv/shims:/Users/bradleyk/Library/Python/3.9/bin:/Users/bradleyk/Library/Python/3.8/bin:/usr/local/lib/ruby/gems/3.0.0/bin:/usr/local/lib/ruby/gems/bin:/usr/local/opt/ruby/bin:/opt/local/bin:/opt/local/sbin:/Users/bradleyk/local/bin:/usr/local/mysql/bin:/opt/local/bin:/opt/local/sbin:/usr/bin:/Users/bradleyk/Projects/goprojects/bin:/Users/bradleyk/Projects/theos/bin:/usr/local/bin/:/usr/local/bin:/bin/:/sbin/:/usr/sbin/";
         NSString *target = @"dpkg-deb";
@@ -126,6 +130,10 @@ int main(int argc, char **argv) {
                 case 'u':
                     up = [NSString stringWithUTF8String:optarg];
                     break;
+                case 'n':
+                    new = TRUE;
+                    packagesFolder = [NSString stringWithUTF8String:optarg];
+                    break;
                     
                 default:
                     cmd_help();
@@ -152,143 +160,22 @@ int main(int argc, char **argv) {
         }
         
         if (bump == TRUE && debFile) {
-            DLog(@"\n [INFO] Bumping version number for file: %@...\n", debFile);
-            InputPackage *output = [HelperClass packageForDeb:debFile];
-            output.appendedPrefix = prefix;
-            output.skipSignatureChecks = skip;
-            [output bumpVersionInCurrentDirectory];
-            return 0;
+            return [HelperClass bumpPackageAtPath:debFile withPrefix:prefix sigChecks:skip];
         }
         
         if (repackage == TRUE && debFile) {
-            DLog(@"\n [INFO] Repackaging file: %@...\n", debFile);
-            InputPackage *output = [HelperClass packageForDeb:debFile];
-            output.appendedPrefix = prefix;
-            output.skipSignatureChecks = skip;
-            [output repackageInCurrentDirectoryWithArch:@"appletvos-arm64"];
-            return 0;
+            return [HelperClass repackagePackageAtPath:debFile withPrefix:prefix sigChecks:skip];
         }
         
         //DLog(@"folder: %@ project: %@", folder, project);
         if (clean && bootstrapPath) {
-            NSString *statusFile = [bootstrapPath relativeStatusFilePath];
-            NSString *listContents = [NSString stringWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
-            //clean up any errant spaces
-            listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
-            listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n\n" withString:@"\n\n"];
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-            [listContents writeToFile:statusFile atomically:TRUE];
-#pragma clang diagnostic pop
-            DLog(@"\n [INFO] Cleaning status file: %@", statusFile);
-            
-            NSString *testStatusFile = [bootstrapPath relativeStatusFilePath];
-            NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
-            __block NSMutableString *newStatusFile = [NSMutableString new];
-            __block NSMutableArray *alreadyAdded = [NSMutableArray new];
-            
-            [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
-                DLog(@"\n [INFO] Processing package: %@", obj.package);
-                if (![alreadyAdded containsObject:obj.package]){
-                    //duplicate check first
-                    NSArray *doubleCheck = [installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", obj.package]];
-                    if (doubleCheck.count > 1){
-                        NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:@"version" ascending:FALSE];
-                        StatusPackageModel *latestVersion = [[doubleCheck sortedArrayUsingDescriptors:@[sortDesc]] firstObject];
-                        DLog(@"\n [INFO] We found a duplicate package, choosing latestVersion: %@", latestVersion);
-                        [newStatusFile appendFormat:@"%@\n\n", latestVersion.rawString];
-                        [alreadyAdded addObject:obj.package];
-                    } else {
-                        [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
-                    }
-                } else {
-                    DLog(@"\n [INFO] We already had this package, skipping older duplicate: %@\n", obj.package);
-                }
-            }];
-            testStatusFile = [testStatusFile stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
-            DLog(@"\n Writing new output file: %@\n", testStatusFile);
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-            [newStatusFile writeToFile:testStatusFile atomically:TRUE];
-#pragma clang diagnostic pop
-            DLog(@"\n Done!\n\n");
-            return 0;
-            
+            return [HelperClass cleanBootstrapAtPath:bootstrapPath];
         } else if (clean) {
             DLog(@"\nYou need to choose a bootstrap path to clean!\n\n");
             return 1;
         }
         if (deletePackage && bootstrapPath) {
-            __block NSString *postRmFile = nil;
-            NSString *statusFile = [bootstrapPath relativeStatusFilePath];
-            NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
-            StatusPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", deletePackage]] lastObject];
-            if (model) {
-                NSString *listName = [NSString stringWithFormat:@"%@.list", deletePackage];
-                NSString *infoPath = [bootstrapPath relativeInfoFolderPath];
-                NSString *listFile = [infoPath stringByAppendingPathComponent:listName];
-                NSArray *listFiles = [FM contentsOfDirectoryAtPath:infoPath error:nil];
-                NSPredicate *blockPred = [NSPredicate predicateWithBlock:^BOOL(id  _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
-                    if ([evaluatedObject containsString:deletePackage]){
-                        return TRUE;
-                    }
-                    return FALSE;
-                }];
-                NSArray *relatedFiles = [listFiles filteredArrayUsingPredicate:blockPred]; //any files in dpkg/info/deletePackage.*
-                DLog(@"\nWe have found the package, determining list details from: %@", listFile);
-                NSString *listContents = [NSString stringWithContentsOfFile:listFile encoding:NSUTF8StringEncoding error:nil];
-                DLog(@"\nContents: %@\n", listContents);
-                DLog(@"\nFound package info files: %@\n", relatedFiles);
-                NSArray *files = [listContents componentsSeparatedByString:@"\n"];
-                __block NSMutableArray *filesToProcess = [NSMutableArray new];
-                [relatedFiles enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
-                    NSString *fullPath = [infoPath stringByAppendingPathComponent:obj];
-                    if([FM fileExistsAtPath:fullPath]){
-                        if ([[obj pathExtension] isEqualToString:@"postrm"]){
-                            postRmFile = fullPath;
-                        }
-                        [filesToProcess addObject:fullPath];
-                    }
-                }];
-                [files enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
-                    NSString *fullPath = [bootstrapPath stringByAppendingPathComponent:obj];
-                    BOOL isDir = FALSE;
-                    if([FM fileExistsAtPath:fullPath isDirectory:&isDir]){
-                        if (!isDir){
-                            DLog(@"found file: %@", fullPath);
-                            [filesToProcess addObject:fullPath];
-                        }
-                    }
-                }];
-                DLog(@"\nFiles to process: %@\n", filesToProcess);
-                NSString *error = [NSString stringWithFormat:@" [WARNING] We are about to delete '%@' from the bootstrap, this cannot be undone!", deletePackage];
-                if(![HelperClass shouldContinueWithError:error]){
-                    return -1;
-                }
-                [filesToProcess enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
-                    [FM removeItemAtPath:obj error:nil];
-                }];
-                
-                NSString *statusFile = [bootstrapPath relativeStatusFilePath];
-                [FM copyItemAtPath:statusFile toPath:[statusFile stringByAppendingPathExtension:@"bak"] error:nil];
-                __block NSMutableString *newStatusFile = [NSMutableString new];
-                [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
-                    if (obj != model){
-                        [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
-                    } else {
-                        DLog(@"skipping the model we want to delete: %@", obj);
-                    }
-                }];
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-                [newStatusFile writeToFile:statusFile atomically:TRUE];
-#pragma clang diagnostic pop
-                if (postRmFile) {
-                    DLog(@"\n [WARNING] We found a postrm file, will not run this due to potential unexpected consequences off of the target device, displaying contents so you can determine if any additional steps are necessary\n");
-                    DLog(@"\n\n%@ contents:\n%@\n\n", postRmFile, [NSString stringWithContentsOfFile:postRmFile encoding:NSUTF8StringEncoding error:nil]);
-                }
-            }
-            return 0;
+            return [HelperClass deletePackage:deletePackage inBootstrap:bootstrapPath];
         } else if (deletePackage) {
             DLog(@"\nYou need to choose a bootstrap path to delete from!\n\n");
             return 1;
@@ -317,6 +204,14 @@ int main(int argc, char **argv) {
             return 1;
         }
         
+        if (new && bootstrapPath && packagesFolder) {
+            return [HelperClass createNewBootstrap:bootstrapPath withPackages:packagesFolder];
+        } else if (new && bootstrapPath) {
+            DLog(@"you need to choose a packages folder to install from when creating a new bootstrap!");
+        } else if (new) {
+            DLog(@"you need to choose a packages folder to install from when creating a new bootstrap, and a bootstrap folder needs to be specified as well.");
+        }
+        
         if (debFile && bootstrapPath) {
             DLog(@"\nProcessing file: %@\n", debFile);
             InputPackage *output = [HelperClass packageForDeb:debFile];