feature/add-filesystem-db
into develop
3 years ago
@ -0,0 +1,4 @@ | |||||
*.db | |||||
*.tar | |||||
*.tar.gz | |||||
tjpkg |
@ -0,0 +1,50 @@ | |||||
package Filesystem | |||||
import ( | |||||
"PackageManager/Client/Database" | |||||
"PackageManager/Client/ProgressBar" | |||||
"PackageManager/Variables" | |||||
"github.com/vbauerster/mpb" | |||||
bolt "go.etcd.io/bbolt" | |||||
) | |||||
func CommitFiles() error { | |||||
var ( | |||||
fsStatus FilesystemStatus | |||||
indexBucket *bolt.Bucket | |||||
bar *mpb.Bar | |||||
f string | |||||
e error | |||||
) | |||||
fsStatus, e = GetFilesystemDiff(Variables.RootDir) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
e = Database.FsDB.Batch(func(tx *bolt.Tx) error { | |||||
indexBucket = tx.Bucket(Variables.FsHashIndexBucket) | |||||
if len(fsStatus.PickedFiles) > 0 { | |||||
bar = ProgressBar.InitBar("Commiting...", len(fsStatus.PickedFiles)) | |||||
for _, f = range fsStatus.PickedFiles { | |||||
bar.Increment() | |||||
e = AddFileToBucket(indexBucket, f) | |||||
if e != nil { | |||||
return nil | |||||
} | |||||
} | |||||
e = tx.DeleteBucket(Variables.FsHashPicksBucket) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
ProgressBar.CloseBar(bar) | |||||
} | |||||
return nil | |||||
}) | |||||
return e | |||||
} |
@ -0,0 +1,74 @@ | |||||
package Filesystem | |||||
import ( | |||||
"regexp" | |||||
"PackageManager/Variables" | |||||
) | |||||
var ( | |||||
PruneRegex []*regexp.Regexp | |||||
IgnoreRegex []*regexp.Regexp | |||||
) | |||||
func init() { | |||||
var e error | |||||
e = InitPruneRegex() | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
e = InitIgnoreRegex() | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
} | |||||
func InitPruneRegex() error { | |||||
var ( | |||||
r *regexp.Regexp | |||||
s string | |||||
e error | |||||
) | |||||
for _, s = range Variables.PruneRegexPaths { | |||||
r, e = regexp.Compile(s) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
PruneRegex = append(PruneRegex, r) | |||||
} | |||||
return nil | |||||
} | |||||
func InitIgnoreRegex() error { | |||||
var ( | |||||
r *regexp.Regexp | |||||
s string | |||||
e error | |||||
) | |||||
for _, s = range Variables.IgnoreRegexPaths { | |||||
r, e = regexp.Compile(s) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
IgnoreRegex = append(IgnoreRegex, r) | |||||
} | |||||
return nil | |||||
} | |||||
func matchAny(p string, a []*regexp.Regexp) bool { | |||||
var ( | |||||
regex *regexp.Regexp | |||||
match bool | |||||
) | |||||
for _, regex = range a { | |||||
match = regex.MatchString(p) | |||||
if match == true { | |||||
return true | |||||
} | |||||
} | |||||
return false | |||||
} |
@ -0,0 +1,50 @@ | |||||
package Filesystem | |||||
import ( | |||||
"fmt" | |||||
"io/fs" | |||||
"io/ioutil" | |||||
"os" | |||||
"path/filepath" | |||||
) | |||||
func CopyFile(src, dest string) error { | |||||
var ( | |||||
input []byte | |||||
fileInfo fs.FileInfo | |||||
srcBasePath string | |||||
destBasePath string | |||||
e error | |||||
) | |||||
srcBasePath = filepath.Dir(src) | |||||
destBasePath = filepath.Dir(dest) | |||||
fileInfo, e = os.Stat(srcBasePath) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
e = os.MkdirAll(destBasePath, fileInfo.Mode()) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
fileInfo, e = os.Stat(src) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
input, e = ioutil.ReadFile(src) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
e = ioutil.WriteFile(dest, input, fileInfo.Mode()) | |||||
if e != nil { | |||||
fmt.Println(e) | |||||
return e | |||||
} | |||||
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 e error | |||||
if f.IsLink() { | |||||
_, e = os.Lstat(dst) | |||||
if !os.IsNotExist(e) { | |||||
e = os.Remove(dst) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
} | |||||
e = os.Symlink(f.Ref, dst) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
return nil | |||||
} | |||||
f.cp(f.objFile(), dst) | |||||
e = os.Chmod(dst, f.FileMode) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
return nil | |||||
} | |||||
func (f FileObject) Stov(src string) error { | |||||
var e error | |||||
if f.IsLink() { | |||||
return nil | |||||
} | |||||
e = os.MkdirAll(f.objDir(), 0744) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
f.cp(src, f.objFile()) | |||||
return nil | |||||
} | |||||
func (f FileObject) cp(src string, dst string) error { | |||||
var ( | |||||
srcFile, dstFile *os.File | |||||
e error | |||||
) | |||||
fmt.Println("cp ", src, dst) | |||||
srcFile, e = os.Open(src) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
defer srcFile.Close() | |||||
dstFile, e = os.Create(dst) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
defer dstFile.Close() | |||||
_, e = io.Copy(dstFile, srcFile) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
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.Compare(f.Sha1, fn.Sha1) != 0 { | |||||
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 | |||||
e error | |||||
) | |||||
fi, e = os.Lstat(f) | |||||
if e != nil { | |||||
return fo, e | |||||
} | |||||
fo = FileObject{ | |||||
FileMode: fi.Mode(), | |||||
Size: fi.Size(), | |||||
} | |||||
if fo.IsLink() { | |||||
fo.Ref, e = os.Readlink(f) | |||||
if e != nil { | |||||
return fo, e | |||||
} | |||||
return fo, nil | |||||
} | |||||
file, e = os.Open(f) | |||||
if e != nil { | |||||
return fo, e | |||||
} | |||||
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 | |||||
e error | |||||
) | |||||
encoder = gob.NewEncoder(&buf) | |||||
e = encoder.Encode(f) | |||||
return buf.Bytes(), e | |||||
} | |||||
func FromBytes(v []byte) (FileObject, error) { | |||||
var ( | |||||
buf *bytes.Buffer | |||||
decoder *gob.Decoder | |||||
fo FileObject | |||||
e error | |||||
) | |||||
buf = bytes.NewBuffer(v) | |||||
decoder = gob.NewDecoder(buf) | |||||
e = decoder.Decode(&fo) | |||||
if e != nil { | |||||
return fo, e | |||||
} | |||||
return fo, nil | |||||
} |
@ -0,0 +1,217 @@ | |||||
package Filesystem | |||||
import ( | |||||
"context" | |||||
"fmt" | |||||
"os" | |||||
"path/filepath" | |||||
"runtime" | |||||
"github.com/vbauerster/mpb" | |||||
bolt "go.etcd.io/bbolt" | |||||
"golang.org/x/sync/semaphore" | |||||
"PackageManager/Client/Database" | |||||
"PackageManager/Client/ProgressBar" | |||||
"PackageManager/Color" | |||||
"PackageManager/Variables" | |||||
) | |||||
type FilesystemStatus struct { | |||||
NewFiles []string | |||||
PickedFiles []string | |||||
ModifiedFiles []string | |||||
MissingFiles []string | |||||
} | |||||
func ShowFilesystemDiff(root string) error { | |||||
var ( | |||||
fsStatus FilesystemStatus | |||||
//f string | |||||
e error | |||||
) | |||||
fsStatus, e = GetFilesystemDiff(root) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
fmt.Println("New files:") | |||||
PrintFilesOrLength(fsStatus.NewFiles, Color.Green) | |||||
fmt.Println("Added files:") | |||||
PrintFilesOrLength(fsStatus.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 | |||||
e error | |||||
) | |||||
rootStat, e = os.Stat(root) | |||||
if e != nil { | |||||
return fsCount, e | |||||
} | |||||
if rootStat.IsDir() && root[len(root)-1:] != "/" { | |||||
root = root + "/" | |||||
} | |||||
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, e | |||||
} | |||||
func (fsStatus *FilesystemStatus) parseFile(indexBucket, picksBucket *bolt.Bucket, p string, bar *mpb.Bar) { | |||||
var ( | |||||
newFileObject FileObject | |||||
knownFileObject FileObject | |||||
pick, known []byte | |||||
e error | |||||
) | |||||
defer func() { | |||||
bar.Increment() | |||||
}() | |||||
pick = picksBucket.Get([]byte(p)) | |||||
known = indexBucket.Get([]byte(p)) | |||||
if pick != nil { | |||||
fsStatus.PickedFiles = append(fsStatus.PickedFiles, p) | |||||
return | |||||
} | |||||
if known != nil { | |||||
newFileObject, e = CreateFileObject(p) | |||||
if e != nil { | |||||
return | |||||
} | |||||
knownFileObject, e = FromBytes(known) | |||||
if e != nil { | |||||
return | |||||
} | |||||
e = newFileObject.IsDifferent(knownFileObject) | |||||
if e != nil { | |||||
fsStatus.ModifiedFiles = append(fsStatus.ModifiedFiles, p) | |||||
} | |||||
return | |||||
} | |||||
fsStatus.NewFiles = append(fsStatus.NewFiles, p) | |||||
return | |||||
} | |||||
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 | |||||
e error | |||||
) | |||||
poolSize = runtime.NumCPU() | |||||
sem = semaphore.NewWeighted(int64(poolSize)) | |||||
rootStat, e = os.Stat(root) | |||||
if e != nil { | |||||
return fsStatus, e | |||||
} | |||||
if rootStat.IsDir() && root[len(root)-1:] != "/" { | |||||
root = root + "/" | |||||
} | |||||
fsCount, e = GetFilesystemLength(root) | |||||
if e != nil { | |||||
return fsStatus, e | |||||
} | |||||
bar = ProgressBar.InitBar("Scanning...", fsCount) | |||||
e = 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 | |||||
} | |||||
Variables.WG.Add(1) | |||||
sem.Acquire(context.Background(), 1) | |||||
go func() { | |||||
fsStatus.parseFile(indexBucket, picksBucket, p, bar) | |||||
Variables.WG.Done() | |||||
sem.Release(1) | |||||
}() | |||||
return nil | |||||
}) | |||||
indexBucket.ForEach(func(k, v []byte) error { | |||||
_, e = os.Lstat(string(k)) | |||||
if os.IsNotExist(e) { | |||||
fsStatus.MissingFiles = append(fsStatus.MissingFiles, string(k)) | |||||
} | |||||
return nil | |||||
}) | |||||
Variables.WG.Wait() | |||||
ProgressBar.CloseBar(bar) | |||||
return nil | |||||
}) | |||||
return fsStatus, e | |||||
} |
@ -0,0 +1,31 @@ | |||||
package Filesystem | |||||
import ( | |||||
"os" | |||||
bolt "go.etcd.io/bbolt" | |||||
) | |||||
func AddFileToBucket(bucket *bolt.Bucket, filePath string) error { | |||||
var ( | |||||
fileObject FileObject | |||||
fileObjectBytes []byte | |||||
e error | |||||
) | |||||
fileObject, e = CreateFileObject(filePath) | |||||
if os.IsNotExist(e) { | |||||
return nil | |||||
} | |||||
if e != nil { | |||||
return nil | |||||
} | |||||
fileObjectBytes, e = fileObject.ToBytes() | |||||
if e != nil { | |||||
return e | |||||
} | |||||
return bucket.Put([]byte(filePath), fileObjectBytes) | |||||
} | |||||
func RemoveFileFromBucket(bucket *bolt.Bucket, filePath string) error { | |||||
return bucket.Delete([]byte(filePath)) | |||||
} |
@ -0,0 +1,131 @@ | |||||
package Filesystem | |||||
import ( | |||||
"os" | |||||
"github.com/vbauerster/mpb" | |||||
bolt "go.etcd.io/bbolt" | |||||
"PackageManager/Client/Database" | |||||
"PackageManager/Client/ProgressBar" | |||||
"PackageManager/Variables" | |||||
) | |||||
func pickFilesSingle(rootPath string) error { | |||||
var ( | |||||
indexBucket *bolt.Bucket | |||||
picksBucket *bolt.Bucket | |||||
e error | |||||
) | |||||
e = Database.FsDB.Batch(func(tx *bolt.Tx) error { | |||||
indexBucket = tx.Bucket(Variables.FsHashIndexBucket) | |||||
picksBucket = tx.Bucket(Variables.FsHashPicksBucket) | |||||
e = AddFileToBucket(picksBucket, rootPath) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
return RemoveFileFromBucket(indexBucket, rootPath) | |||||
}) | |||||
return e | |||||
} | |||||
func pickFilesRecursive(rootPath string) error { | |||||
var ( | |||||
fsStatus FilesystemStatus | |||||
indexBucket *bolt.Bucket | |||||
picksBucket *bolt.Bucket | |||||
bar *mpb.Bar | |||||
totalLen int | |||||
f string | |||||
e error | |||||
) | |||||
fsStatus, e = GetFilesystemDiff(rootPath) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
totalLen = len(fsStatus.NewFiles) + len(fsStatus.ModifiedFiles) + len(fsStatus.MissingFiles) | |||||
if totalLen == 0 { | |||||
return nil | |||||
} | |||||
bar = ProgressBar.InitBar("Adding...", totalLen) | |||||
e = 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() | |||||
e = AddFileToBucket(picksBucket, f) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
} | |||||
} | |||||
if len(fsStatus.ModifiedFiles) > 0 { | |||||
for _, f = range fsStatus.ModifiedFiles { | |||||
bar.Increment() | |||||
e = AddFileToBucket(picksBucket, f) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
} | |||||
} | |||||
if len(fsStatus.MissingFiles) > 0 { | |||||
for _, f = range fsStatus.MissingFiles { | |||||
bar.Increment() | |||||
e = RemoveFileFromBucket(indexBucket, f) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
e = RemoveFileFromBucket(picksBucket, f) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
} | |||||
} | |||||
Variables.WG.Wait() | |||||
ProgressBar.CloseBar(bar) | |||||
return nil | |||||
}) | |||||
return e | |||||
} | |||||
func PickFiles(rootPath string) error { | |||||
var ( | |||||
rootStat os.FileInfo | |||||
e error | |||||
) | |||||
rootStat, e = os.Stat(rootPath) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
if !rootStat.IsDir() { | |||||
return pickFilesSingle(rootPath) | |||||
} | |||||
return pickFilesRecursive(rootPath) | |||||
} | |||||
func ResetAllPickedFiles() error { | |||||
var ( | |||||
e error | |||||
) | |||||
e = Database.FsDB.Batch(func(tx *bolt.Tx) error { | |||||
return tx.DeleteBucket(Variables.FsHashPicksBucket) | |||||
}) | |||||
return e | |||||
} |
@ -0,0 +1,25 @@ | |||||
package Filesystem | |||||
import ( | |||||
"PackageManager/Variables" | |||||
"fmt" | |||||
) | |||||
func PrintFiles(files []string, color func(...interface{}) string) { | |||||
var f string | |||||
for _, f = range files { | |||||
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) | |||||
return | |||||
} | |||||
PrintFilesLength(files) | |||||
} |
@ -0,0 +1,134 @@ | |||||
package Package | |||||
func CreatePackage() error { | |||||
return nil | |||||
/* | |||||
var ( | |||||
dirtyFiles map[int]string | |||||
newFiles map[int]string | |||||
pkgFiles map[int]string = make(map[int]string) | |||||
choices string | |||||
choicesSplit []string | |||||
filePath string | |||||
pkgName string | |||||
pkgVersion string | |||||
pkgNameVersion string | |||||
tmpDir string | |||||
index int | |||||
ok bool | |||||
e error | |||||
) | |||||
fmt.Println("Initialising package creation...") | |||||
dirtyFiles, newFiles, e = Filesystem.GetFilesystemDiff() | |||||
if e != nil { | |||||
return e | |||||
} | |||||
fmt.Println("\nModified files...") | |||||
for i, file := range dirtyFiles { | |||||
fmt.Printf( | |||||
"\t%d - %s\n", | |||||
i, | |||||
Color.Red(file), | |||||
) | |||||
} | |||||
fmt.Println("\nNew files...") | |||||
for i, file := range newFiles { | |||||
fmt.Printf( | |||||
"\t%d - %s\n", | |||||
i, | |||||
Color.Red(file), | |||||
) | |||||
} | |||||
fmt.Println("Please select the files you would like to use to create the package. Leave empty for all.") | |||||
choices = Helper.Input() | |||||
if choices == "" { | |||||
for i, file := range dirtyFiles { | |||||
pkgFiles[i] = file | |||||
} | |||||
for i, file := range newFiles { | |||||
pkgFiles[i] = file | |||||
} | |||||
} else { | |||||
choicesSplit = strings.Split(choices, ",") | |||||
for _, i := range choicesSplit { | |||||
index, e = strconv.Atoi(i) | |||||
if e != nil { | |||||
// TODO: Handle this error | |||||
panic(e) | |||||
} | |||||
filePath, ok = dirtyFiles[index] | |||||
if !ok { | |||||
filePath, ok = newFiles[index] | |||||
if !ok { | |||||
return errors.New("Invalid package selection") | |||||
} | |||||
} | |||||
pkgFiles[index] = filePath | |||||
} | |||||
} | |||||
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") | |||||
for i, file := range pkgFiles { | |||||
fmt.Printf( | |||||
"\t%d - %s\n", | |||||
i, | |||||
Color.Green(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) | |||||
tmpDir, e = ioutil.TempDir("/tmp", pkgNameVersion) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
defer os.RemoveAll(tmpDir) | |||||
for _, file := range pkgFiles { | |||||
Filesystem.CopyFile(file, filepath.Join(tmpDir, file)) | |||||
} | |||||
// TODO: Add dependancy management here | |||||
e = Archive.TarGzip(tmpDir, pkgNameVersion+".tar.gz") | |||||
if e != nil { | |||||
return e | |||||
} | |||||
fmt.Printf( | |||||
Color.Green("\nSuccessfully created package %s\n"), | |||||
pkgNameVersion, | |||||
) | |||||
return nil | |||||
*/ | |||||
} |
@ -0,0 +1,38 @@ | |||||
package Package | |||||
import ( | |||||
"errors" | |||||
"fmt" | |||||
"os" | |||||
"PackageManager/Archive" | |||||
"PackageManager/Variables" | |||||
) | |||||
func InstallPackage(pkgs []string) error { | |||||
var ( | |||||
pkg string | |||||
e error | |||||
) | |||||
for _, pkg = range pkgs { | |||||
_, e = os.Stat(pkg) | |||||
if os.IsNotExist(e) { | |||||
return errors.New(fmt.Sprintf("Invalid package %s", pkg)) | |||||
} | |||||
} | |||||
for _, pkg = range pkgs { | |||||
fmt.Printf( | |||||
"Installing %s...\n", | |||||
pkg, | |||||
) | |||||
e = Archive.UntarGzip(pkg, Variables.RootDir) | |||||
fmt.Printf( | |||||
"%s successfully installed\n", | |||||
pkg, | |||||
) | |||||
} | |||||
return nil | |||||
} |
@ -0,0 +1,35 @@ | |||||
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 | |||||
} | |||||
func CloseBar(bar *mpb.Bar) { | |||||
bar.Abort(false) | |||||
} |
@ -1,13 +1,164 @@ | |||||
package main | package main | ||||
import ( | import ( | ||||
"flag" | |||||
"fmt" | "fmt" | ||||
"os" | |||||
"PackageManager/Archive" | |||||
"PackageManager/Client/Database" | |||||
"PackageManager/Client/Filesystem" | |||||
"PackageManager/Color" | |||||
"PackageManager/Helper" | |||||
"PackageManager/Variables" | |||||
) | ) | ||||
func HelpMsg() { | |||||
var helpMsg string | |||||
helpMsg = `Usage of %s: | |||||
-Af | -add-files | |||||
Add files | |||||
-Cf | -commit | |||||
Add files | |||||
-Fd | -fs-diff | |||||
Filesystem diff | |||||
-Rf | -reset | |||||
Reset added files | |||||
-V | -verbose | |||||
Verbose output | |||||
` | |||||
helpMsg = fmt.Sprintf(helpMsg, os.Args[0]) | |||||
fmt.Println(helpMsg) | |||||
} | |||||
func main() { | func main() { | ||||
var ( | |||||
getFilesystemDiffFlag bool | |||||
getFilesystemDiffFlagLong bool | |||||
addFileDiffFlag bool | |||||
addFileDiffFlagLong bool | |||||
commitAddedFilesFlag bool | |||||
commitAddedFilesFlagLong bool | |||||
resetAddedFilesFlag bool | |||||
resetAddedFilesFlagLong bool | |||||
verboseOutputFlag bool | |||||
verboseOutputFlagLong bool | |||||
e error | |||||
) | |||||
flag.Usage = HelpMsg | |||||
e = Helper.CheckRoot() | |||||
if e != nil { | |||||
fmt.Println(Color.Fatal(e)) | |||||
return | |||||
} | |||||
e = Database.InitDB() | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
// 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.Parse() | |||||
Variables.VerboseOutput = verboseOutputFlag || verboseOutputFlagLong | |||||
if getFilesystemDiffFlag || getFilesystemDiffFlagLong { | |||||
var rootPath string = Variables.RootDir | |||||
if len(flag.Args()) > 1 { | |||||
// TODO: Fix this msg | |||||
fmt.Println(Color.Fatal("Option takes one optional argument")) | |||||
flag.Usage() | |||||
return | |||||
} | |||||
if len(flag.Args()) == 1 { | |||||
rootPath = flag.Arg(0) | |||||
} | |||||
e = Filesystem.ShowFilesystemDiff(rootPath) | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
return | |||||
} | |||||
if addFileDiffFlag || addFileDiffFlagLong { | |||||
if len(flag.Args()) > 1 && len(flag.Args()) < 1 { | |||||
fmt.Println(Color.Fatal("Must supply one argument")) | |||||
flag.Usage() | |||||
return | |||||
} | |||||
e = Filesystem.PickFiles(flag.Arg(0)) | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
return | |||||
} | |||||
if commitAddedFilesFlag || commitAddedFilesFlagLong { | |||||
e = Filesystem.CommitFiles() | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
return | |||||
} | |||||
if resetAddedFilesFlag || resetAddedFilesFlagLong { | |||||
e = Filesystem.ResetAllPickedFiles() | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
return | |||||
} | |||||
/* | |||||
if createPackageFlag || createPackageFlagLong { | |||||
e = Package.CreatePackage() | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
return | |||||
} | |||||
if installLocalPackageFlag || installLocalPackageFlagLong { | |||||
e = Package.InstallPackage(flag.Args()) | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
return | |||||
} | |||||
*/ | |||||
flag.Usage() | |||||
fmt.Println(Color.Fatal("Nothing to do")) | |||||
//e := Archive.TarGzip("/tmp/test", "/tmp/test.tar.gz") | //e := Archive.TarGzip("/tmp/test", "/tmp/test.tar.gz") | ||||
e := Archive.UntarGzip("/tmp/test.tar.gz", "/tmp/test") | |||||
fmt.Println(e) | |||||
//e := Archive.UntarGzip("/tmp/test.tar.gz", "/tmp/test") | |||||
//fmt.Println(e) | |||||
} | } |
@ -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 { | |||||
sprint := 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 | |||||
e error | |||||
) | |||||
// TODO Make cross platform | |||||
cmd = exec.Command("id", "-u") | |||||
output, e = cmd.Output() | |||||
if e != nil { | |||||
return e | |||||
} | |||||
// 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, e = strconv.Atoi(string(output[:len(output)-1])) | |||||
if e != nil { | |||||
return e | |||||
} | |||||
if i != 0 { | |||||
return errors.New("Please run as root") | |||||
} | |||||
return nil | |||||
} |
@ -0,0 +1,24 @@ | |||||
package Helper | |||||
import ( | |||||
"bufio" | |||||
"os" | |||||
"strings" | |||||
) | |||||
var ( | |||||
reader *bufio.Reader = bufio.NewReader(os.Stdin) | |||||
) | |||||
func Input() string { | |||||
var ( | |||||
text string | |||||
e error | |||||
) | |||||
text, e = reader.ReadString('\n') | |||||
if e != nil { | |||||
panic(e) | |||||
} | |||||
// 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,76 @@ | |||||
package Variables | |||||
import ( | |||||
"os" | |||||
"sync" | |||||
) | |||||
const ( | |||||
DatabaseName string = "package_manager.db" | |||||
FsHashDatabaseName string = "fs_hash.db" | |||||
) | |||||
var ( | |||||
WG sync.WaitGroup | |||||
VerboseOutput bool = false | |||||
RootDir string = "/" | |||||
FsHashPicksBucket []byte = []byte("FilesystemPicks") | |||||
FsHashIndexBucket []byte = []byte("FilesystemIndex") | |||||
PruneRegexPaths []string = []string{ | |||||
"^/\\.git$", | |||||
"^/dist$", | |||||
"^/boot/grub$", | |||||
"^/proc$", | |||||
"^/dev$", | |||||
"^/mnt$", | |||||
"^/sys$", | |||||
"^/src$", | |||||
"^/root$", | |||||
"^/home$", | |||||
"^/build$", | |||||
"^/tools$", | |||||
"^/opt$", | |||||
"^/run/user$", | |||||
"^/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$", | |||||
"/lost\\+found$", | |||||
} | |||||
IgnoreRegexPaths []string = []string{ | |||||
"^/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 | |||||
) | |||||
rootDir = os.Getenv("ROOTDIR") | |||||
if rootDir != "" { | |||||
RootDir = rootDir | |||||
} | |||||
} |
@ -0,0 +1,5 @@ | |||||
module PackageManager | |||||
go 1.16 | |||||
require github.com/mattn/go-sqlite3 v1.14.7 |
@ -0,0 +1,2 @@ | |||||
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA= | |||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= |