@ -0,0 +1,6 @@ | |||
*.db | |||
*.tar | |||
*.tar.gz | |||
tjpkg | |||
test.go | |||
lfs_packages |
@ -1,129 +1,107 @@ | |||
package Archive | |||
import ( | |||
"PackageManager/Variables" | |||
"archive/tar" | |||
"compress/gzip" | |||
"io" | |||
"os" | |||
"path/filepath" | |||
"strings" | |||
) | |||
func Gzip(source, target string) error { | |||
func CreateArchive(files []string, target string) error { | |||
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 ( | |||
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 | |||
import ( | |||
"flag" | |||
"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() { | |||
//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 |