| @ -0,0 +1,6 @@ | |||||
| *.db | |||||
| *.tar | |||||
| *.tar.gz | |||||
| tjpkg | |||||
| test.go | |||||
| lfs_packages | |||||
| @ -1,129 +1,107 @@ | |||||
| package Archive | package Archive | ||||
| import ( | import ( | ||||
| "PackageManager/Variables" | |||||
| "archive/tar" | "archive/tar" | ||||
| "compress/gzip" | "compress/gzip" | ||||
| "io" | "io" | ||||
| "os" | "os" | ||||
| "path/filepath" | |||||
| "strings" | "strings" | ||||
| ) | ) | ||||
| func Gzip(source, target string) error { | |||||
| func CreateArchive(files []string, target string) error { | |||||
| var ( | var ( | ||||
| reader, writer *os.File | |||||
| archiver *gzip.Writer | |||||
| filename string | |||||
| e error | |||||
| outFile *os.File | |||||
| gzipWriter *gzip.Writer | |||||
| tarWriter *tar.Writer | |||||
| file string | |||||
| err error | |||||
| ) | ) | ||||
| reader, e = os.Open(source) | |||||
| if e != nil { | |||||
| return e | |||||
| // Create output file | |||||
| outFile, err = os.Create(target) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| defer outFile.Close() | |||||
| //filename = filepath.Base(source) | |||||
| //target = filepath.Join(target, fmt.Sprintf("%s.gz", filename)) | |||||
| gzipWriter = gzip.NewWriter(outFile) | |||||
| defer gzipWriter.Close() | |||||
| tarWriter = tar.NewWriter(gzipWriter) | |||||
| defer tarWriter.Close() | |||||
| writer, e = os.Create(target) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| defer writer.Close() | |||||
| // Iterate over files and add them to the tar archive | |||||
| archiver = gzip.NewWriter(writer) | |||||
| archiver.Name = filename | |||||
| defer archiver.Close() | |||||
| for _, file = range files { | |||||
| err = addToArchive(tarWriter, file) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| _, e = io.Copy(archiver, reader) | |||||
| return e | |||||
| return nil | |||||
| } | } | ||||
| func Tar(source, target string) error { | |||||
| func addToArchive(tarWriter *tar.Writer, filename string) error { | |||||
| var ( | var ( | ||||
| tarfile, file *os.File | |||||
| tarball *tar.Writer | |||||
| info os.FileInfo | |||||
| header *tar.Header | |||||
| e error | |||||
| file *os.File | |||||
| info os.FileInfo | |||||
| header *tar.Header | |||||
| err error | |||||
| ) | ) | ||||
| tarfile, e = os.Create(target) | |||||
| if e != nil { | |||||
| return e | |||||
| // Get FileInfo about our file providing file size, mode, etc. | |||||
| info, err = os.Lstat(filename) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| defer tarfile.Close() | |||||
| tarball = tar.NewWriter(tarfile) | |||||
| defer tarball.Close() | |||||
| info, e = os.Stat(source) | |||||
| if e != nil { | |||||
| return e | |||||
| // Create a tar Header from the FileInfo data | |||||
| header, err = tar.FileInfoHeader(info, info.Name()) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| var baseDir string | |||||
| if info.IsDir() { | |||||
| baseDir = filepath.Base(source) | |||||
| isSymlink := info.Mode()&os.ModeSymlink != 0 | |||||
| if isSymlink { | |||||
| header.Typeflag = tar.TypeSymlink | |||||
| } | } | ||||
| return filepath.Walk(source, | |||||
| func(path string, info os.FileInfo, e error) error { | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| header, e = tar.FileInfoHeader(info, info.Name()) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| if baseDir != "" { | |||||
| header.Name = filepath.Join( | |||||
| baseDir, | |||||
| strings.TrimPrefix( | |||||
| strings.Join(strings.Split(path, "/")[1:], "/"), | |||||
| source, | |||||
| ), | |||||
| ) | |||||
| } | |||||
| e = tarball.WriteHeader(header) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| if info.IsDir() { | |||||
| return nil | |||||
| } | |||||
| file, e = os.Open(path) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| defer file.Close() | |||||
| _, e = io.Copy(tarball, file) | |||||
| return e | |||||
| }) | |||||
| } | |||||
| func TarGzip(source, target string) error { | |||||
| var ( | |||||
| tarPath string = strings.ReplaceAll(target, ".gz", "") | |||||
| e error | |||||
| // Use full path as name (FileInfoHeader only takes the basename) | |||||
| // If we don't do this the directory strucuture would | |||||
| // not be preserved | |||||
| // https://golang.org/src/archive/tar/common.go?#L626 | |||||
| header.Name = strings.TrimPrefix( | |||||
| strings.TrimPrefix( | |||||
| filename, | |||||
| Variables.RootDir, | |||||
| ), | |||||
| "/", | |||||
| ) | ) | ||||
| e = Tar(source, tarPath) | |||||
| if e != nil { | |||||
| return e | |||||
| // Write file header to the tar archive | |||||
| err = tarWriter.WriteHeader(header) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| // Do not write file if it is a symlink | |||||
| if isSymlink { | |||||
| return nil | |||||
| } | |||||
| // Open the file which will be written into the archive | |||||
| file, err = os.Open(filename) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| defer file.Close() | |||||
| e = Gzip(tarPath, target) | |||||
| if e != nil { | |||||
| return e | |||||
| // Copy file content to tar archive | |||||
| _, err = io.Copy(tarWriter, file) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| return os.Remove(tarPath) | |||||
| return nil | |||||
| } | } | ||||
| @ -0,0 +1,6 @@ | |||||
| # TJPKG CHANGELOG | |||||
| [Unreleased] | |||||
| ## Added | |||||
| ## Changed | |||||
| ## Fixed | |||||
| @ -0,0 +1,62 @@ | |||||
| package Database | |||||
| import ( | |||||
| "database/sql" | |||||
| "time" | |||||
| _ "github.com/mattn/go-sqlite3" | |||||
| ) | |||||
| func IsPackageInstalled(name, version string) (int64, error) { | |||||
| var ( | |||||
| row *sql.Row | |||||
| id int64 | |||||
| err error | |||||
| ) | |||||
| row = DB.QueryRow(` | |||||
| SELECT id FROM installed_packages WHERE name = ? AND version = ?; | |||||
| `, name, version) | |||||
| err = row.Scan(&id) | |||||
| return id, err | |||||
| } | |||||
| func InsertPackage(name, version string, depIds []int64) error { | |||||
| var ( | |||||
| stmt *sql.Stmt | |||||
| result sql.Result | |||||
| pkgId int64 | |||||
| depId int64 | |||||
| err error | |||||
| ) | |||||
| stmt, err = DB.Prepare("INSERT INTO installed_packages(name, version, installed_at) VALUES(?,?,?)") | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| result, err = stmt.Exec(name, version, time.Now().Unix()) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| pkgId, err = result.LastInsertId() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| for _, depId = range depIds { | |||||
| stmt, err = DB.Prepare("INSERT INTO dependancy_linker(package_id, dependancy_id) VALUES(?,?)") | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| _, err = stmt.Exec(pkgId, depId) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| return err | |||||
| } | |||||
| @ -0,0 +1,51 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| "PackageManager/Client/Database" | |||||
| "PackageManager/Client/ProgressBar" | |||||
| "PackageManager/Variables" | |||||
| "github.com/vbauerster/mpb" | |||||
| bolt "go.etcd.io/bbolt" | |||||
| ) | |||||
| func CommitFiles() error { | |||||
| var ( | |||||
| indexBucket *bolt.Bucket | |||||
| picksBucket *bolt.Bucket | |||||
| bar *mpb.Bar | |||||
| pickedFiles []string | |||||
| f string | |||||
| err error | |||||
| ) | |||||
| err = Database.FsDB.Batch(func(tx *bolt.Tx) error { | |||||
| indexBucket = tx.Bucket(Variables.FsHashIndexBucket) | |||||
| picksBucket = tx.Bucket(Variables.FsHashPicksBucket) | |||||
| picksBucket.ForEach(func(k, v []byte) error { | |||||
| pickedFiles = append(pickedFiles, string(k)) | |||||
| return nil | |||||
| }) | |||||
| if len(pickedFiles) > 0 { | |||||
| bar = ProgressBar.InitBar("Commiting...", len(pickedFiles)) | |||||
| for _, f = range pickedFiles { | |||||
| bar.Increment() | |||||
| err = AddFileToBucket(indexBucket, f) | |||||
| if err != nil { | |||||
| return nil | |||||
| } | |||||
| } | |||||
| err = tx.DeleteBucket(Variables.FsHashPicksBucket) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| return nil | |||||
| }) | |||||
| return err | |||||
| } | |||||
| @ -0,0 +1,74 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| "regexp" | |||||
| "strings" | |||||
| "PackageManager/Variables" | |||||
| ) | |||||
| var ( | |||||
| PruneRegex []*regexp.Regexp | |||||
| IgnoreRegex []*regexp.Regexp | |||||
| ) | |||||
| func init() { | |||||
| var err error | |||||
| err = InitPruneRegex() | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| err = InitIgnoreRegex() | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| } | |||||
| func InitPruneRegex() error { | |||||
| var ( | |||||
| r *regexp.Regexp | |||||
| s string | |||||
| err error | |||||
| ) | |||||
| for _, s = range Variables.PruneRegexPaths { | |||||
| r, err = regexp.Compile(strings.Replace(s, "^/", "^"+Variables.RootDir, 1)) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| PruneRegex = append(PruneRegex, r) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func InitIgnoreRegex() error { | |||||
| var ( | |||||
| r *regexp.Regexp | |||||
| s string | |||||
| err error | |||||
| ) | |||||
| for _, s = range Variables.IgnoreRegexPaths { | |||||
| r, err = regexp.Compile(strings.Replace(s, "^/", "^"+Variables.RootDir, 1)) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| IgnoreRegex = append(IgnoreRegex, r) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func matchAny(p string, a []*regexp.Regexp) bool { | |||||
| var ( | |||||
| regex *regexp.Regexp | |||||
| ) | |||||
| for _, regex = range a { | |||||
| if regex.MatchString(p) { | |||||
| return true | |||||
| } | |||||
| } | |||||
| return false | |||||
| } | |||||
| @ -0,0 +1,48 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| "io/fs" | |||||
| "io/ioutil" | |||||
| "os" | |||||
| "path/filepath" | |||||
| ) | |||||
| func CopyFile(src, dest string) error { | |||||
| var ( | |||||
| input []byte | |||||
| fileInfo fs.FileInfo | |||||
| srcBasePath string | |||||
| destBasePath string | |||||
| err error | |||||
| ) | |||||
| srcBasePath = filepath.Dir(src) | |||||
| destBasePath = filepath.Dir(dest) | |||||
| fileInfo, err = os.Stat(srcBasePath) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| err = os.MkdirAll(destBasePath, fileInfo.Mode()) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| fileInfo, err = os.Stat(src) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| input, err = ioutil.ReadFile(src) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| err = ioutil.WriteFile(dest, input, fileInfo.Mode()) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @ -0,0 +1,214 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| "bytes" | |||||
| "crypto/sha1" | |||||
| "encoding/gob" | |||||
| "encoding/hex" | |||||
| "errors" | |||||
| "fmt" | |||||
| "hash" | |||||
| "io" | |||||
| "os" | |||||
| "path/filepath" | |||||
| ) | |||||
| var ( | |||||
| // TODO: Where do I put this | |||||
| Data string | |||||
| ) | |||||
| type Package struct { | |||||
| Name string | |||||
| Version string | |||||
| } | |||||
| type FileObject struct { | |||||
| FileMode os.FileMode | |||||
| Size int64 | |||||
| Package Package | |||||
| Ref string | |||||
| Sha1 []byte | |||||
| } | |||||
| type ByName []Package | |||||
| func (f FileObject) IsLink() bool { | |||||
| return f.FileMode&os.ModeSymlink != 0 | |||||
| } | |||||
| func (f FileObject) objFile() string { | |||||
| return filepath.Join(f.objDir(), hex.EncodeToString(f.Sha1)) | |||||
| } | |||||
| func (f FileObject) objDir() string { | |||||
| return filepath.Join(Data, hex.EncodeToString(f.Sha1[:2])) | |||||
| } | |||||
| func (f FileObject) Reset(dst string) error { | |||||
| var err error | |||||
| if f.IsLink() { | |||||
| _, err = os.Lstat(dst) | |||||
| if !os.IsNotExist(err) { | |||||
| err = os.Remove(dst) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| err = os.Symlink(f.Ref, dst) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| f.cp(f.objFile(), dst) | |||||
| err = os.Chmod(dst, f.FileMode) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (f FileObject) Stov(src string) error { | |||||
| var err error | |||||
| if f.IsLink() { | |||||
| return nil | |||||
| } | |||||
| err = os.MkdirAll(f.objDir(), 0744) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| f.cp(src, f.objFile()) | |||||
| return nil | |||||
| } | |||||
| func (f FileObject) cp(src string, dst string) error { | |||||
| var ( | |||||
| srcFile, dstFile *os.File | |||||
| err error | |||||
| ) | |||||
| fmt.Println("cp ", src, dst) | |||||
| srcFile, err = os.Open(src) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| defer srcFile.Close() | |||||
| dstFile, err = os.Create(dst) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| defer dstFile.Close() | |||||
| _, err = io.Copy(dstFile, srcFile) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return dstFile.Sync() | |||||
| } | |||||
| func (f FileObject) IsDifferent(fn FileObject) error { | |||||
| if f.FileMode != fn.FileMode { | |||||
| return errors.New("mode does not match") | |||||
| } | |||||
| if f.IsLink() { | |||||
| if f.Ref != fn.Ref { | |||||
| return errors.New("ref does not match") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| if f.Size != fn.Size { | |||||
| return errors.New("size does not match") | |||||
| } | |||||
| if !bytes.Equal(f.Sha1, fn.Sha1) { | |||||
| return errors.New("sha1 does not match") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func CreateFileObject(f string) (FileObject, error) { | |||||
| var ( | |||||
| sha1Hash hash.Hash = sha1.New() | |||||
| fo FileObject | |||||
| fi os.FileInfo | |||||
| file *os.File | |||||
| err error | |||||
| ) | |||||
| fi, err = os.Lstat(f) | |||||
| if err != nil { | |||||
| return fo, err | |||||
| } | |||||
| fo = FileObject{ | |||||
| FileMode: fi.Mode(), | |||||
| Size: fi.Size(), | |||||
| } | |||||
| if fo.IsLink() { | |||||
| fo.Ref, err = os.Readlink(f) | |||||
| if err != nil { | |||||
| return fo, err | |||||
| } | |||||
| return fo, nil | |||||
| } | |||||
| file, err = os.Open(f) | |||||
| if err != nil { | |||||
| return fo, err | |||||
| } | |||||
| defer file.Close() | |||||
| io.Copy(sha1Hash, file) | |||||
| fo.Sha1 = sha1Hash.Sum(nil) | |||||
| return fo, nil | |||||
| } | |||||
| func (f FileObject) ToBytes() ([]byte, error) { | |||||
| var ( | |||||
| encoder *gob.Encoder | |||||
| buf bytes.Buffer | |||||
| err error | |||||
| ) | |||||
| encoder = gob.NewEncoder(&buf) | |||||
| err = encoder.Encode(f) | |||||
| return buf.Bytes(), err | |||||
| } | |||||
| func FromBytes(v []byte) (FileObject, error) { | |||||
| var ( | |||||
| buf *bytes.Buffer | |||||
| decoder *gob.Decoder | |||||
| fo FileObject | |||||
| err error | |||||
| ) | |||||
| buf = bytes.NewBuffer(v) | |||||
| decoder = gob.NewDecoder(buf) | |||||
| err = decoder.Decode(&fo) | |||||
| if err != nil { | |||||
| return fo, err | |||||
| } | |||||
| return fo, nil | |||||
| } | |||||
| @ -0,0 +1,239 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| "context" | |||||
| "fmt" | |||||
| "os" | |||||
| "path/filepath" | |||||
| "runtime" | |||||
| "sync" | |||||
| "github.com/vbauerster/mpb" | |||||
| bolt "go.etcd.io/bbolt" | |||||
| "golang.org/x/sync/semaphore" | |||||
| "PackageManager/Client/Database" | |||||
| "PackageManager/Client/ProgressBar" | |||||
| "PackageManager/Color" | |||||
| "PackageManager/Variables" | |||||
| ) | |||||
| var ( | |||||
| fsStatusWG sync.WaitGroup | |||||
| FsWalkWG sync.WaitGroup | |||||
| ) | |||||
| type FilesystemStatus struct { | |||||
| NewFiles []string | |||||
| PickedFiles []string | |||||
| ModifiedFiles []string | |||||
| MissingFiles []string | |||||
| } | |||||
| func ShowFilesystemDiff(root string) error { | |||||
| var ( | |||||
| fsStatus FilesystemStatus | |||||
| picksBucket *bolt.Bucket | |||||
| pickedFiles []string | |||||
| err error | |||||
| ) | |||||
| fsStatus, err = GetFilesystemDiff(root) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| err = Database.FsDB.View(func(tx *bolt.Tx) error { | |||||
| picksBucket = tx.Bucket(Variables.FsHashPicksBucket) | |||||
| return picksBucket.ForEach(func(key, _ []byte) error { | |||||
| pickedFiles = append(pickedFiles, string(key)) | |||||
| return nil | |||||
| }) | |||||
| }) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| fmt.Println("New files:") | |||||
| PrintFilesOrLength(fsStatus.NewFiles, Color.Green) | |||||
| fmt.Println("Added files:") | |||||
| PrintFilesOrLength(pickedFiles, Color.Green) | |||||
| fmt.Println("Modified files:") | |||||
| PrintFilesOrLength(fsStatus.ModifiedFiles, Color.Warning) | |||||
| fmt.Println("Deleted files:") | |||||
| PrintFilesOrLength(fsStatus.MissingFiles, Color.Fatal) | |||||
| return nil | |||||
| } | |||||
| func GetFilesystemLength(root string) (int, error) { | |||||
| var ( | |||||
| rootStat os.FileInfo | |||||
| fsCount int = 0 | |||||
| err error | |||||
| ) | |||||
| rootStat, err = os.Stat(root) | |||||
| if err != nil { | |||||
| return fsCount, err | |||||
| } | |||||
| if rootStat.IsDir() && root[len(root)-1:] != "/" { | |||||
| root = root + "/" | |||||
| } | |||||
| err = filepath.Walk(root, func(p string, i os.FileInfo, _ error) error { | |||||
| // Ignore path in Variables.PruneRegexPaths | |||||
| if i.IsDir() && matchAny(p, PruneRegex) { | |||||
| return filepath.SkipDir | |||||
| } | |||||
| // Ignore path in Variables.IgnoreRegexPaths | |||||
| if matchAny(p, IgnoreRegex) { | |||||
| return nil | |||||
| } | |||||
| if !i.Mode().IsRegular() && (i.Mode()&os.ModeSymlink == 0) { | |||||
| return nil | |||||
| } | |||||
| fsCount++ | |||||
| return nil | |||||
| }) | |||||
| return fsCount, err | |||||
| } | |||||
| func (fsStatus *FilesystemStatus) parseFile(indexBucket, picksBucket *bolt.Bucket, p string, bar *mpb.Bar) { | |||||
| var ( | |||||
| newFileObject FileObject | |||||
| knownFileObject FileObject | |||||
| pick, known []byte | |||||
| err error | |||||
| ) | |||||
| defer func() { | |||||
| bar.Increment() | |||||
| }() | |||||
| pick = picksBucket.Get([]byte(p)) | |||||
| known = indexBucket.Get([]byte(p)) | |||||
| if pick != nil { | |||||
| fsStatusWG.Wait() | |||||
| fsStatusWG.Add(1) | |||||
| fsStatus.PickedFiles = append(fsStatus.PickedFiles, p) | |||||
| fsStatusWG.Done() | |||||
| return | |||||
| } | |||||
| if known != nil { | |||||
| newFileObject, err = CreateFileObject(p) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| knownFileObject, err = FromBytes(known) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| err = newFileObject.IsDifferent(knownFileObject) | |||||
| if err != nil { | |||||
| fsStatusWG.Wait() | |||||
| fsStatusWG.Add(1) | |||||
| fsStatus.ModifiedFiles = append(fsStatus.ModifiedFiles, p) | |||||
| fsStatusWG.Done() | |||||
| } | |||||
| return | |||||
| } | |||||
| fsStatusWG.Wait() | |||||
| fsStatusWG.Add(1) | |||||
| fsStatus.NewFiles = append(fsStatus.NewFiles, p) | |||||
| fsStatusWG.Done() | |||||
| } | |||||
| func GetFilesystemDiff(root string) (FilesystemStatus, error) { | |||||
| var ( | |||||
| fsStatus FilesystemStatus = FilesystemStatus{} | |||||
| sem *semaphore.Weighted | |||||
| picksBucket *bolt.Bucket | |||||
| indexBucket *bolt.Bucket | |||||
| rootStat os.FileInfo | |||||
| bar *mpb.Bar | |||||
| fsCount int | |||||
| poolSize int | |||||
| err error | |||||
| ) | |||||
| poolSize = runtime.NumCPU() | |||||
| sem = semaphore.NewWeighted(int64(poolSize)) | |||||
| rootStat, err = os.Stat(root) | |||||
| if err != nil { | |||||
| return fsStatus, err | |||||
| } | |||||
| if rootStat.IsDir() && root[len(root)-1:] != "/" { | |||||
| root = root + "/" | |||||
| } | |||||
| fsCount, err = GetFilesystemLength(root) | |||||
| if err != nil { | |||||
| return fsStatus, err | |||||
| } | |||||
| bar = ProgressBar.InitBar("Scanning...", fsCount) | |||||
| err = Database.FsDB.View(func(tx *bolt.Tx) error { | |||||
| picksBucket = tx.Bucket(Variables.FsHashPicksBucket) | |||||
| indexBucket = tx.Bucket(Variables.FsHashIndexBucket) | |||||
| filepath.Walk(root, func(p string, i os.FileInfo, _ error) error { | |||||
| // Ignore path in Variables.PruneRegexPaths | |||||
| if i.IsDir() && matchAny(p, PruneRegex) { | |||||
| return filepath.SkipDir | |||||
| } | |||||
| // Ignore path in Variables.IgnoreRegexPaths | |||||
| if matchAny(p, IgnoreRegex) { | |||||
| return nil | |||||
| } | |||||
| if !i.Mode().IsRegular() && (i.Mode()&os.ModeSymlink == 0) { | |||||
| return nil | |||||
| } | |||||
| FsWalkWG.Add(1) | |||||
| sem.Acquire(context.Background(), 1) | |||||
| go func() { | |||||
| fsStatus.parseFile(indexBucket, picksBucket, p, bar) | |||||
| sem.Release(1) | |||||
| FsWalkWG.Done() | |||||
| }() | |||||
| FsWalkWG.Wait() | |||||
| return nil | |||||
| }) | |||||
| indexBucket.ForEach(func(k, v []byte) error { | |||||
| _, err = os.Lstat(string(k)) | |||||
| if os.IsNotExist(err) { | |||||
| fsStatus.MissingFiles = append(fsStatus.MissingFiles, string(k)) | |||||
| } | |||||
| return nil | |||||
| }) | |||||
| return nil | |||||
| }) | |||||
| return fsStatus, err | |||||
| } | |||||
| @ -0,0 +1,28 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| bolt "go.etcd.io/bbolt" | |||||
| ) | |||||
| func AddFileToBucket(bucket *bolt.Bucket, filePath string) error { | |||||
| var ( | |||||
| fileObject FileObject | |||||
| fileObjectBytes []byte | |||||
| err error | |||||
| ) | |||||
| fileObject, err = CreateFileObject(filePath) | |||||
| if err != nil { | |||||
| return nil | |||||
| } | |||||
| fileObjectBytes, err = fileObject.ToBytes() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return bucket.Put([]byte(filePath), fileObjectBytes) | |||||
| } | |||||
| func RemoveFileFromBucket(bucket *bolt.Bucket, filePath string) error { | |||||
| return bucket.Delete([]byte(filePath)) | |||||
| } | |||||
| @ -0,0 +1,130 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| "os" | |||||
| "path/filepath" | |||||
| "github.com/vbauerster/mpb" | |||||
| bolt "go.etcd.io/bbolt" | |||||
| "PackageManager/Client/Database" | |||||
| "PackageManager/Client/ProgressBar" | |||||
| "PackageManager/Variables" | |||||
| ) | |||||
| func pickFilesSingle(filePath string) error { | |||||
| var ( | |||||
| indexBucket *bolt.Bucket | |||||
| picksBucket *bolt.Bucket | |||||
| err error | |||||
| ) | |||||
| err = Database.FsDB.Batch(func(tx *bolt.Tx) error { | |||||
| indexBucket = tx.Bucket(Variables.FsHashIndexBucket) | |||||
| picksBucket = tx.Bucket(Variables.FsHashPicksBucket) | |||||
| err = AddFileToBucket(picksBucket, filePath) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return RemoveFileFromBucket(indexBucket, filePath) | |||||
| }) | |||||
| return err | |||||
| } | |||||
| func pickFilesRecursive(rootPath string) error { | |||||
| var ( | |||||
| fsStatus FilesystemStatus | |||||
| indexBucket *bolt.Bucket | |||||
| picksBucket *bolt.Bucket | |||||
| bar *mpb.Bar | |||||
| totalLen int | |||||
| f string | |||||
| err error | |||||
| ) | |||||
| fsStatus, err = GetFilesystemDiff(rootPath) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| totalLen = len(fsStatus.NewFiles) + len(fsStatus.ModifiedFiles) + len(fsStatus.MissingFiles) | |||||
| if totalLen == 0 { | |||||
| return nil | |||||
| } | |||||
| bar = ProgressBar.InitBar("Adding...", totalLen) | |||||
| err = Database.FsDB.Batch(func(tx *bolt.Tx) error { | |||||
| indexBucket = tx.Bucket(Variables.FsHashIndexBucket) | |||||
| picksBucket = tx.Bucket(Variables.FsHashPicksBucket) | |||||
| if len(fsStatus.NewFiles) > 0 { | |||||
| for _, f = range fsStatus.NewFiles { | |||||
| bar.Increment() | |||||
| err = AddFileToBucket(picksBucket, f) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| if len(fsStatus.ModifiedFiles) > 0 { | |||||
| for _, f = range fsStatus.ModifiedFiles { | |||||
| bar.Increment() | |||||
| err = AddFileToBucket(picksBucket, f) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| if len(fsStatus.MissingFiles) > 0 { | |||||
| for _, f = range fsStatus.MissingFiles { | |||||
| bar.Increment() | |||||
| err = RemoveFileFromBucket(indexBucket, f) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| err = RemoveFileFromBucket(picksBucket, f) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| }) | |||||
| return err | |||||
| } | |||||
| func PickFiles(rootPath string) error { | |||||
| var ( | |||||
| realRootPath string | |||||
| rootStat os.FileInfo | |||||
| err error | |||||
| ) | |||||
| realRootPath = filepath.Join(Variables.RootDir, rootPath) | |||||
| rootStat, err = os.Stat(realRootPath) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if !rootStat.IsDir() { | |||||
| return pickFilesSingle(realRootPath) | |||||
| } | |||||
| return pickFilesRecursive(realRootPath) | |||||
| } | |||||
| func ResetAllPickedFiles() error { | |||||
| var err error = Database.FsDB.Batch(func(tx *bolt.Tx) error { | |||||
| return tx.DeleteBucket(Variables.FsHashPicksBucket) | |||||
| }) | |||||
| return err | |||||
| } | |||||
| @ -0,0 +1,32 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| "PackageManager/Variables" | |||||
| "fmt" | |||||
| ) | |||||
| func PrintFiles(files []string, color func(...interface{}) string, showInt bool) { | |||||
| var ( | |||||
| f string | |||||
| i int | |||||
| ) | |||||
| for i, f = range files { | |||||
| if showInt { | |||||
| fmt.Printf("\t%d - %s\n", i, color(f)) | |||||
| continue | |||||
| } | |||||
| fmt.Printf("\t%s\n", color(f)) | |||||
| } | |||||
| } | |||||
| func PrintFilesLength(files []string) { | |||||
| fmt.Printf("\t%d files found\n", len(files)) | |||||
| } | |||||
| func PrintFilesOrLength(files []string, color func(...interface{}) string) { | |||||
| if (Variables.VerboseOutput && len(files) != 0) || (len(files) < 25 && len(files) > 0) { | |||||
| PrintFiles(files, color, false) | |||||
| return | |||||
| } | |||||
| PrintFilesLength(files) | |||||
| } | |||||
| @ -0,0 +1,18 @@ | |||||
| package Filesystem | |||||
| import ( | |||||
| "path/filepath" | |||||
| "strings" | |||||
| "PackageManager/Variables" | |||||
| ) | |||||
| func StripRootDir(path string) string { | |||||
| return filepath.Join( | |||||
| "/", | |||||
| strings.TrimPrefix( | |||||
| path, | |||||
| Variables.RootDir, | |||||
| ), | |||||
| ) | |||||
| } | |||||
| @ -0,0 +1,158 @@ | |||||
| package Package | |||||
| import ( | |||||
| "errors" | |||||
| "fmt" | |||||
| "io/ioutil" | |||||
| "os" | |||||
| "os/exec" | |||||
| "path/filepath" | |||||
| "strconv" | |||||
| "strings" | |||||
| bolt "go.etcd.io/bbolt" | |||||
| "PackageManager/Archive" | |||||
| "PackageManager/Client/Database" | |||||
| "PackageManager/Client/Filesystem" | |||||
| "PackageManager/Color" | |||||
| "PackageManager/Helper" | |||||
| "PackageManager/Variables" | |||||
| ) | |||||
| func editManifestFile(filePath string) error { | |||||
| var ( | |||||
| cmd *exec.Cmd | |||||
| ) | |||||
| cmd = exec.Command(Variables.Editor, filePath) | |||||
| cmd.Stdin = os.Stdin | |||||
| cmd.Stdout = os.Stdout | |||||
| return cmd.Run() | |||||
| } | |||||
| func writeManifestFile(path, name, version string) error { | |||||
| var ( | |||||
| manifest string | |||||
| filePath string | |||||
| e error | |||||
| ) | |||||
| manifest, e = Manifest{ | |||||
| Name: name, | |||||
| Version: version, | |||||
| Dependancies: make(map[string]string), | |||||
| }.CreateManifestString() | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| filePath = filepath.Join(path, "manifest.yml") | |||||
| return ioutil.WriteFile(filePath, []byte(manifest), 0644) | |||||
| } | |||||
| func CreatePackage() error { | |||||
| var ( | |||||
| picksBucket *bolt.Bucket | |||||
| pickedFiles []string | |||||
| pkgFiles []string | |||||
| choices string | |||||
| choicesSplit []string | |||||
| pkgName string | |||||
| pkgVersion string | |||||
| pkgNameVersion string | |||||
| index int | |||||
| err error | |||||
| ) | |||||
| fmt.Println("Initialising package creation...") | |||||
| err = Database.FsDB.View(func(tx *bolt.Tx) error { | |||||
| picksBucket = tx.Bucket(Variables.FsHashPicksBucket) | |||||
| picksBucket.ForEach(func(k, v []byte) error { | |||||
| pickedFiles = append(pickedFiles, string(k)) | |||||
| return nil | |||||
| }) | |||||
| return nil | |||||
| }) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| fmt.Println("Added files:") | |||||
| Filesystem.PrintFiles(pickedFiles, Color.Green, true) | |||||
| fmt.Println("Please select the files you would like to use to create the package. Leave empty for all.") | |||||
| choices = Helper.Input() | |||||
| if choices == "" { | |||||
| pkgFiles = append(pkgFiles, pickedFiles...) | |||||
| } else { | |||||
| choicesSplit = strings.Split(choices, ",") | |||||
| for _, i := range choicesSplit { | |||||
| index, err = strconv.Atoi(i) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if len(pickedFiles) < index { | |||||
| return errors.New("invalid choice") | |||||
| } | |||||
| pkgFiles = append(pkgFiles, pickedFiles[index]) | |||||
| } | |||||
| } | |||||
| fmt.Println("Please enter the package name:") | |||||
| pkgName = Helper.Input() | |||||
| if pkgName == "" { | |||||
| return errors.New("invalid package name") | |||||
| } | |||||
| fmt.Println("Please enter the package version:") | |||||
| pkgVersion = Helper.Input() | |||||
| if pkgVersion == "" { | |||||
| return errors.New("invalid package name") | |||||
| } | |||||
| fmt.Printf("Package Name: %s\n", pkgName) | |||||
| fmt.Printf("Package Version: %s\n", pkgVersion) | |||||
| fmt.Println("Files to be added") | |||||
| Filesystem.PrintFiles(pkgFiles, Color.Green, false) | |||||
| // TODO: Move this check to below manifest file | |||||
| fmt.Println("Is this correct? [y/N]") | |||||
| if strings.ToLower(Helper.Input()) != "y" { | |||||
| return errors.New("user aborted") | |||||
| } | |||||
| pkgNameVersion = fmt.Sprintf("%s-%s", pkgName, pkgVersion) | |||||
| err = writeManifestFile("/tmp/", pkgName, pkgVersion) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| err = editManifestFile(filepath.Join("/tmp/", "manifest.yml")) | |||||
| if err != nil { | |||||
| fmt.Println(Color.Fatal(err)) | |||||
| } | |||||
| // TODO: Write this file to a better spot? | |||||
| pkgFiles = append(pkgFiles, "/tmp/manifest.yml") | |||||
| err = Archive.CreateArchive(pkgFiles, pkgNameVersion+".tar.gz") | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| fmt.Printf( | |||||
| Color.Green("\nSuccessfully created package %s\n"), | |||||
| pkgNameVersion, | |||||
| ) | |||||
| return nil | |||||
| } | |||||
| @ -0,0 +1,124 @@ | |||||
| package Package | |||||
| import ( | |||||
| "fmt" | |||||
| "os" | |||||
| "os/exec" | |||||
| "PackageManager/Archive" | |||||
| "PackageManager/Client/Database" | |||||
| "PackageManager/Variables" | |||||
| ) | |||||
| func runInstallCmd(cmdStrs []string) error { | |||||
| var ( | |||||
| cmd *exec.Cmd | |||||
| cmdStr string | |||||
| err error | |||||
| ) | |||||
| if len(cmdStrs) == 0 { | |||||
| return nil | |||||
| } | |||||
| for _, cmdStr = range cmdStrs { | |||||
| cmd = exec.Command("/bin/sh", "-c", cmdStr) | |||||
| cmd.Dir = Variables.RootDir | |||||
| cmd.Stdin = os.Stdin | |||||
| cmd.Stdout = os.Stdout | |||||
| cmd.Stderr = os.Stderr | |||||
| err = cmd.Run() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func CheckPackageDependancies(deps map[string]string) ([]int64, error) { | |||||
| var ( | |||||
| name, version string | |||||
| depIds []int64 | |||||
| id int64 | |||||
| e error | |||||
| ) | |||||
| for name, version = range deps { | |||||
| id, e = Database.IsPackageInstalled(name, version) | |||||
| if e != nil { | |||||
| return depIds, e | |||||
| } | |||||
| depIds = append(depIds, id) | |||||
| } | |||||
| return depIds, e | |||||
| } | |||||
| func InstallPackage(pkgs []string) error { | |||||
| var ( | |||||
| manifest Manifest | |||||
| depIds []int64 | |||||
| pkg string | |||||
| mStr string | |||||
| e error | |||||
| ) | |||||
| for _, pkg = range pkgs { | |||||
| _, e = os.Stat(pkg) | |||||
| if os.IsNotExist(e) { | |||||
| return fmt.Errorf("invalid package %s", pkg) | |||||
| } | |||||
| } | |||||
| for _, pkg = range pkgs { | |||||
| fmt.Printf( | |||||
| "Installing %s...\n", | |||||
| pkg, | |||||
| ) | |||||
| mStr, e = Archive.ExtractManifestFile(pkg) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| manifest, e = ParseManifestFile(mStr) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| if !Variables.IgnoreDepsCheck { | |||||
| depIds, e = CheckPackageDependancies(manifest.Dependancies) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| } | |||||
| e = runInstallCmd(manifest.PreInstallCmds) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| e = Archive.ExtractArchive(pkg, Variables.RootDir) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| e = runInstallCmd(manifest.PostInstallCmds) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| e = Database.InsertPackage(manifest.Name, manifest.Version, depIds) | |||||
| if e != nil { | |||||
| return e | |||||
| } | |||||
| fmt.Printf( | |||||
| "%s successfully installed\n", | |||||
| pkg, | |||||
| ) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @ -0,0 +1,36 @@ | |||||
| package Package | |||||
| import ( | |||||
| yaml "gopkg.in/yaml.v2" | |||||
| ) | |||||
| type Manifest struct { | |||||
| Name string `yaml:"name"` | |||||
| Version string `yaml:"version"` | |||||
| Symlinks map[string]string `yaml:"symlinks,flow"` | |||||
| Dependancies map[string]string `yaml:"dependancies,flow"` | |||||
| PreInstallCmds []string `yaml:"pre_install_cmds"` | |||||
| PostInstallCmds []string `yaml:"post_install_cmds"` | |||||
| } | |||||
| func ParseManifestFile(manifest string) (Manifest, error) { | |||||
| var ( | |||||
| m Manifest = Manifest{} | |||||
| e error | |||||
| ) | |||||
| e = yaml.Unmarshal([]byte(manifest), &m) | |||||
| return m, e | |||||
| } | |||||
| func (m Manifest) CreateManifestString() (string, error) { | |||||
| var ( | |||||
| mByte []byte | |||||
| e error | |||||
| ) | |||||
| mByte, e = yaml.Marshal(&m) | |||||
| if e != nil { | |||||
| return "", e | |||||
| } | |||||
| return string(mByte), e | |||||
| } | |||||
| @ -0,0 +1,55 @@ | |||||
| package Package | |||||
| import ( | |||||
| "PackageManager/Archive" | |||||
| "PackageManager/Color" | |||||
| "PackageManager/Variables" | |||||
| "fmt" | |||||
| "os" | |||||
| "path/filepath" | |||||
| ) | |||||
| func UninstallPackage(pkgs []string) error { | |||||
| var ( | |||||
| pkgFiles []string | |||||
| pkg string | |||||
| f string | |||||
| err error | |||||
| ) | |||||
| for _, pkg = range pkgs { | |||||
| _, err = os.Stat(pkg) | |||||
| if os.IsNotExist(err) { | |||||
| return fmt.Errorf("invalid package %s", pkg) | |||||
| } | |||||
| } | |||||
| for _, pkg = range pkgs { | |||||
| fmt.Printf( | |||||
| "Uninstalling %s...\n", | |||||
| pkg, | |||||
| ) | |||||
| pkgFiles, err = Archive.GetPackageFilePaths(pkg) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| for _, f = range pkgFiles { | |||||
| err = os.Remove(filepath.Join(Variables.RootDir, f)) | |||||
| if os.IsNotExist(err) { | |||||
| continue | |||||
| } | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| fmt.Printf( | |||||
| Color.Green("%s uninstalled\n"), | |||||
| pkg, | |||||
| ) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @ -0,0 +1,31 @@ | |||||
| package ProgressBar | |||||
| import ( | |||||
| "github.com/vbauerster/mpb" | |||||
| "github.com/vbauerster/mpb/decor" | |||||
| ) | |||||
| var ( | |||||
| P = mpb.New() | |||||
| ) | |||||
| func InitBar(name string, total int) *mpb.Bar { | |||||
| var ( | |||||
| bar *mpb.Bar | |||||
| ) | |||||
| bar = P.AddBar(int64(total), | |||||
| mpb.PrependDecorators( | |||||
| decor.Name(name), | |||||
| decor.Percentage(decor.WCSyncSpace), | |||||
| ), | |||||
| mpb.AppendDecorators( | |||||
| // replace ETA decorator with "done" message, OnComplete event | |||||
| decor.OnComplete( | |||||
| decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "done", | |||||
| ), | |||||
| ), | |||||
| ) | |||||
| return bar | |||||
| } | |||||
| @ -1,13 +1,205 @@ | |||||
| package main | package main | ||||
| import ( | import ( | ||||
| "flag" | |||||
| "fmt" | "fmt" | ||||
| "os" | |||||
| "PackageManager/Archive" | |||||
| "PackageManager/Client/Database" | |||||
| "PackageManager/Client/Filesystem" | |||||
| "PackageManager/Client/Package" | |||||
| "PackageManager/Color" | |||||
| "PackageManager/Helper" | |||||
| "PackageManager/Variables" | |||||
| ) | ) | ||||
| func HelpMsg() { | |||||
| var helpMsg string = `Usage of %s: | |||||
| General: | |||||
| -V | -verbose | |||||
| Verbose output | |||||
| Filesystem diff: | |||||
| -Af | -add-files | |||||
| Add files | |||||
| -Cf | -commit | |||||
| Add files | |||||
| -Fd | -fs-diff | |||||
| Filesystem diff | |||||
| -Rf | -reset | |||||
| Reset added files | |||||
| -Cp | -create-pkg | |||||
| Create package from fs diff | |||||
| -Il | -install-local | |||||
| Install package from local tarball | |||||
| -Rl | -remove-local | |||||
| Remove package from local tarball | |||||
| -ignore-deps-check | |||||
| Skip dependancies check | |||||
| ` | |||||
| fmt.Printf(helpMsg, os.Args[0]) | |||||
| } | |||||
| func parseFlagsAndExecute() { | |||||
| var ( | |||||
| getFilesystemDiffFlag bool | |||||
| getFilesystemDiffFlagLong bool | |||||
| addFileDiffFlag bool | |||||
| addFileDiffFlagLong bool | |||||
| commitAddedFilesFlag bool | |||||
| commitAddedFilesFlagLong bool | |||||
| resetAddedFilesFlag bool | |||||
| resetAddedFilesFlagLong bool | |||||
| createPackageFlag bool | |||||
| createPackageFlagLong bool | |||||
| installLocalPackageFlag bool | |||||
| installLocalPackageFlagLong bool | |||||
| uninstallPackageFlag bool | |||||
| uninstallPackageFlagLong bool | |||||
| ignoreDepsCheckFlag bool | |||||
| verboseOutputFlag bool | |||||
| verboseOutputFlagLong bool | |||||
| err error | |||||
| ) | |||||
| flag.Usage = HelpMsg | |||||
| err = Helper.CheckRoot() | |||||
| if err != nil { | |||||
| fmt.Println(Color.Fatal(err)) | |||||
| return | |||||
| } | |||||
| err = Database.InitDB() | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| defer Database.DB.Close() | |||||
| defer Database.FsDB.Close() | |||||
| // TODO: Rework usage function | |||||
| // Initialise flags | |||||
| flag.BoolVar(&verboseOutputFlag, "V", false, "Verbose output") | |||||
| flag.BoolVar(&verboseOutputFlagLong, "verbose", false, "Verbose output") | |||||
| flag.BoolVar(&getFilesystemDiffFlag, "Fd", false, "Filesystem diff") | |||||
| flag.BoolVar(&getFilesystemDiffFlagLong, "fs-diff", false, "Filesystem diff") | |||||
| flag.BoolVar(&addFileDiffFlag, "Af", false, "Add files") | |||||
| flag.BoolVar(&addFileDiffFlagLong, "add-files", false, "Add files") | |||||
| flag.BoolVar(&commitAddedFilesFlag, "Cf", false, "Commit files") | |||||
| flag.BoolVar(&commitAddedFilesFlagLong, "commit", false, "Commit files") | |||||
| flag.BoolVar(&resetAddedFilesFlag, "Rf", false, "Reset added files") | |||||
| flag.BoolVar(&resetAddedFilesFlagLong, "reset", false, "Reset added files") | |||||
| flag.BoolVar(&createPackageFlag, "Cp", false, "Create package from fs diff") | |||||
| flag.BoolVar(&createPackageFlagLong, "create-pkg", false, "Create package from fs diff") | |||||
| flag.BoolVar(&installLocalPackageFlag, "Il", false, "Install package from local tarball") | |||||
| flag.BoolVar(&installLocalPackageFlagLong, "install-local", false, "Install package from local tarball") | |||||
| flag.BoolVar(&uninstallPackageFlag, "Rl", false, "Uninstall local package") | |||||
| flag.BoolVar(&uninstallPackageFlagLong, "remove-local", false, "Uninstall local package") | |||||
| flag.BoolVar(&ignoreDepsCheckFlag, "ignore-deps-check", false, "Ignore dependancies check") | |||||
| flag.Parse() | |||||
| Variables.VerboseOutput = verboseOutputFlag || verboseOutputFlagLong | |||||
| Variables.IgnoreDepsCheck = ignoreDepsCheckFlag | |||||
| if getFilesystemDiffFlag || getFilesystemDiffFlagLong { | |||||
| var rootPath string = Variables.RootDir | |||||
| if len(flag.Args()) > 1 { | |||||
| flag.Usage() | |||||
| fmt.Println(Color.Fatal("Option takes one optional argument")) | |||||
| return | |||||
| } | |||||
| if len(flag.Args()) == 1 { | |||||
| rootPath = flag.Arg(0) | |||||
| } | |||||
| err = Filesystem.ShowFilesystemDiff(rootPath) | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| return | |||||
| } | |||||
| if addFileDiffFlag || addFileDiffFlagLong { | |||||
| if len(flag.Args()) > 1 || len(flag.Args()) < 1 { | |||||
| fmt.Println(Color.Fatal("Must supply one argument")) | |||||
| flag.Usage() | |||||
| return | |||||
| } | |||||
| err = Filesystem.PickFiles(flag.Arg(0)) | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| return | |||||
| } | |||||
| if commitAddedFilesFlag || commitAddedFilesFlagLong { | |||||
| err = Filesystem.CommitFiles() | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| return | |||||
| } | |||||
| if resetAddedFilesFlag || resetAddedFilesFlagLong { | |||||
| err = Filesystem.ResetAllPickedFiles() | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| return | |||||
| } | |||||
| if createPackageFlag || createPackageFlagLong { | |||||
| err = Package.CreatePackage() | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| return | |||||
| } | |||||
| if installLocalPackageFlag || installLocalPackageFlagLong { | |||||
| err = Package.InstallPackage(flag.Args()) | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| return | |||||
| } | |||||
| if uninstallPackageFlag || uninstallPackageFlagLong { | |||||
| err = Package.UninstallPackage(flag.Args()) | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| return | |||||
| } | |||||
| flag.Usage() | |||||
| fmt.Println(Color.Fatal("Nothing to do")) | |||||
| } | |||||
| func main() { | func main() { | ||||
| //e := Archive.TarGzip("/tmp/test", "/tmp/test.tar.gz") | |||||
| e := Archive.UntarGzip("/tmp/test.tar.gz", "/tmp/test") | |||||
| fmt.Println(e) | |||||
| parseFlagsAndExecute() | |||||
| } | } | ||||
| @ -0,0 +1,58 @@ | |||||
| package Color | |||||
| import ( | |||||
| "fmt" | |||||
| "regexp" | |||||
| "runtime" | |||||
| ) | |||||
| var ( | |||||
| Success = Green | |||||
| Info = Teal | |||||
| Warning = Yellow | |||||
| Fatal = Red | |||||
| ) | |||||
| var ( | |||||
| Black = Color("\033[1;30m%s\033[0m") | |||||
| Red = Color("\033[1;31m%s\033[0m") | |||||
| Green = Color("\033[1;32m%s\033[0m") | |||||
| Yellow = Color("\033[1;33m%s\033[0m") | |||||
| Purple = Color("\033[1;34m%s\033[0m") | |||||
| Magenta = Color("\033[1;35m%s\033[0m") | |||||
| Teal = Color("\033[1;36m%s\033[0m") | |||||
| White = Color("\033[1;37m%s\033[0m") | |||||
| ) | |||||
| func init() { | |||||
| if runtime.GOOS != "windows" { | |||||
| return | |||||
| } | |||||
| Black = Color("%s") | |||||
| Red = Color("%s") | |||||
| Green = Color("%s") | |||||
| Yellow = Color("%s") | |||||
| Purple = Color("%s") | |||||
| Magenta = Color("%s") | |||||
| Teal = Color("%s") | |||||
| White = Color("%s") | |||||
| } | |||||
| func Color(colorString string) func(...interface{}) string { | |||||
| var sprint func(args ...interface{}) string = func(args ...interface{}) string { | |||||
| return fmt.Sprintf(colorString, | |||||
| fmt.Sprint(args...)) | |||||
| } | |||||
| return sprint | |||||
| } | |||||
| func Strip(s string) string { | |||||
| var ( | |||||
| reg *regexp.Regexp | |||||
| res string | |||||
| ) | |||||
| reg = regexp.MustCompile(`\033\[.{1,4}m`) | |||||
| res = reg.ReplaceAllString(s, "${1}") | |||||
| return res | |||||
| } | |||||
| @ -0,0 +1,42 @@ | |||||
| package Helper | |||||
| import ( | |||||
| "errors" | |||||
| "os/exec" | |||||
| "strconv" | |||||
| ) | |||||
| func CheckRoot() error { | |||||
| var ( | |||||
| cmd *exec.Cmd | |||||
| output []byte | |||||
| i int | |||||
| err error | |||||
| ) | |||||
| // TODO Make cross platform | |||||
| cmd = exec.Command("id", "-u") | |||||
| output, err = cmd.Output() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| // output has trailing \n | |||||
| // need to remove the \n | |||||
| // otherwise it will cause error for strconv.Atoi | |||||
| // log.Println(output[:len(output)-1]) | |||||
| // 0 = root, 501 = non-root user | |||||
| i, err = strconv.Atoi(string(output[:len(output)-1])) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if i != 0 { | |||||
| return errors.New("please run as root") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @ -0,0 +1,25 @@ | |||||
| package Helper | |||||
| import ( | |||||
| "bufio" | |||||
| "os" | |||||
| "strings" | |||||
| ) | |||||
| var ( | |||||
| reader *bufio.Reader = bufio.NewReader(os.Stdin) | |||||
| ) | |||||
| func Input() string { | |||||
| var ( | |||||
| text string | |||||
| err error | |||||
| ) | |||||
| text, err = reader.ReadString('\n') | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| // convert CRLF to LF | |||||
| return strings.Replace(text, "\n", "", -1) | |||||
| } | |||||
| @ -0,0 +1,14 @@ | |||||
| CLIENT_MAIN=Client/main.go | |||||
| BUILD=go build -o | |||||
| BUILD32=env GOARCH=386 go build | |||||
| LINUX_LDFLAGS=--ldflags "-s -w -extldflags=-static" | |||||
| LINUX_ENV=env GOOS=linux GOARCH=amd64 | |||||
| LINUX_ENV32=env GOOS=linux GOARCH=386 | |||||
| LINUX_OUT=tjpkg | |||||
| build: | |||||
| ${LINUX_ENV} ${BUILD} ${LINUX_OUT} ${CLIENT_MAIN} | |||||
| @ -0,0 +1,23 @@ | |||||
| # TJPKG | |||||
| `tjpkg` is a package manager that facilitates determining what files changed during a manual installation, and creating a tarball package from the diff. These tarball packages can then be installed by others similar to other Linux package managers. | |||||
| ## Why? | |||||
| TODO | |||||
| ## Usage | |||||
| TODO | |||||
| ## TODO | |||||
| - Implement the file server to download packages | |||||
| - Implement package groups in the file server | |||||
| - Implement option to edit package manifest files without manually extracting the package | |||||
| - Fix dependancy checks by checking for packages being installed as well as already installed packages | |||||
| - Finish building LFS toolchain for base group | |||||
| - Implement configuration file | |||||
| - Fix cross compatiblity issues | |||||
| - Fix bug where diff is hanging | |||||
| - Fix errors messages to make more human readable **LOW PRIO** | |||||
| - Finish README | |||||
| @ -0,0 +1,101 @@ | |||||
| //go:build linux || darwin || freebsd || !windows | |||||
| // +build linux darwin freebsd !windows | |||||
| package Variables | |||||
| import ( | |||||
| "os" | |||||
| ) | |||||
| const ( | |||||
| ConfigDir string = "/etc/tjpkg" | |||||
| DatabaseName string = "package_manager.db" | |||||
| FsHashDatabaseName string = "fs_hash.db" | |||||
| ) | |||||
| var ( | |||||
| Editor string = "vi" | |||||
| VerboseOutput bool = false | |||||
| IgnoreDepsCheck bool = false | |||||
| RootDir string = "/" | |||||
| FsHashPicksBucket []byte = []byte("FilesystemPicks") | |||||
| FsHashIndexBucket []byte = []byte("FilesystemIndex") | |||||
| PruneRegexPaths []string = []string{ | |||||
| ConfigDir, | |||||
| "^/\\.git$", | |||||
| "^/dist$", | |||||
| "^/sources$", | |||||
| "^/boot/grub$", | |||||
| "^/proc$", | |||||
| "^/dev$", | |||||
| "^/mnt$", | |||||
| "^/sys$", | |||||
| "^/src$", | |||||
| "^/root$", | |||||
| "^/home$", | |||||
| "^/build$", | |||||
| "^/tools$", | |||||
| "^/opt$", | |||||
| "^/run/user$", | |||||
| "^/run/systemd$", | |||||
| "^/usr/share/zsh$", | |||||
| "^/usr/share/texmf-dist$", | |||||
| "^/usr/share/zoneinfo$", | |||||
| "^/usr/share/zoneinfo-leaps$", | |||||
| "^/tmp$", | |||||
| "^/var/db$", | |||||
| "^/var/cache$", | |||||
| "^/var/log$", | |||||
| "^/var/spool$", | |||||
| "^/var/lib/texmf$", | |||||
| "^/var/lib/postgres$", | |||||
| "^/var/lib/pacman$", | |||||
| "^/var/lib/NetworkManager$", | |||||
| "^/var/lib/systemd$", | |||||
| "^/var/lib/xkb/README.compiled$", | |||||
| "^/var/lib/dpkg$", | |||||
| "^/var/lib/ucf$", | |||||
| "^/var/lib/apt$", | |||||
| "^/etc/ld.so.cache$", | |||||
| "/lost\\+found$", | |||||
| } | |||||
| IgnoreRegexPaths []string = []string{ | |||||
| os.Args[0], | |||||
| "^/usr/share/info/dir$", | |||||
| "^/swapfile$", | |||||
| "^/etc/passwd$", | |||||
| "^/etc/passwd-$", | |||||
| "^/etc/group$", | |||||
| "^/etc/group-$", | |||||
| "^/var/.updated$", | |||||
| "^/var/lib/mlocate/mlocate.db$", | |||||
| "^/var/lib/krb5kdc/kdc.conf$", | |||||
| "^/var/lib/alsa/asound.state$", | |||||
| "^/run/systemd/journal/kernel-seqnum$", | |||||
| } | |||||
| ) | |||||
| func init() { | |||||
| var ( | |||||
| rootDir string | |||||
| editor string | |||||
| ) | |||||
| rootDir = os.Getenv("ROOTDIR") | |||||
| if rootDir != "" { | |||||
| RootDir = rootDir | |||||
| } | |||||
| if RootDir[len(RootDir)-1:] != "/" { | |||||
| RootDir += "/" | |||||
| } | |||||
| editor = os.Getenv("EDITOR") | |||||
| if editor != "" { | |||||
| Editor = editor | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,14 @@ | |||||
| module PackageManager | |||||
| go 1.16 | |||||
| require ( | |||||
| github.com/VividCortex/ewma v1.2.0 // indirect | |||||
| github.com/mattn/go-isatty v0.0.13 // indirect | |||||
| github.com/mattn/go-sqlite3 v1.14.8 | |||||
| github.com/vbauerster/mpb v3.4.0+incompatible | |||||
| go.etcd.io/bbolt v1.3.6 | |||||
| golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect | |||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c | |||||
| gopkg.in/yaml.v2 v2.4.0 | |||||
| ) | |||||
| @ -0,0 +1,28 @@ | |||||
| github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= | |||||
| github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= | |||||
| github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= | |||||
| github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | |||||
| github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= | |||||
| github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= | |||||
| github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw= | |||||
| github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= | |||||
| go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= | |||||
| go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= | |||||
| golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= | |||||
| golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | |||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | |||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= | |||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||||
| golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||||
| golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= | |||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= | |||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | |||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | |||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | |||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | |||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | |||||
| @ -0,0 +1,2 @@ | |||||
| Ncurses needs a post install command | |||||
| ln -sf ../share/terminfo usr/lib/terminfo | |||||