Compare commits

...

Author SHA1 Message Date
  Tovi Jaeschke-Rogers 428a95450c Fix bug with ROOTDIR always showing fs diff 3 years ago
  Tovi Jaeschke-Rogers 620f5e03ab Fix ignore paths 3 years ago
  Tovi Jaeschke-Rogers bcf9370526 Fix install/uninstall functionality 3 years ago
  Tovi Jaeschke-Rogers 00f94493e3 Prevent negative waitgroup counter bug 3 years ago
  Tovi Jaeschke-Rogers 48d30fdc45 Fix the thing 3 years ago
  Tovi Jaeschke-Rogers a921764f36 Merge branch 'feature/create-install-pkgs' of git.tovijaeschke.xyz:tovi/PackageManager into feature/create-install-pkgs 3 years ago
  Tovi Jaeschke-Rogers 14dd717c74 Reconfigure waitgroup placing during file system diff 3 years ago
  Tovi Jaeschke-Rogers ffec2239b4 Reconfigure waitgroup placing during file system diff 3 years ago
  Tovi Jaeschke-Rogers 3a50260df8 Add initial CHANGELOG.md and README.md 3 years ago
  Tovi Jaeschke-Rogers 28e148b5b5 Add pre/post install cmds to manifest yml 3 years ago
  Tovi Jaeschke-Rogers 7c3d0dd9f5 Add binary to ignored files and add symlinks to manifest 3 years ago
  Tovi Jaeschke-Rogers d133851f4f Trim RootDir off bucket keys to line everything up 3 years ago
  Tovi Jaeschke-Rogers e028d1733c Add ability to uninstall pkg 3 years ago
  Tovi Jaeschke-Rogers b63f1dd70d Make editing manifest optional for LFS builds 3 years ago
  Tovi Jaeschke-Rogers 7f579e1c66 Add /sources to ignore dirs for LFS 3 years ago
  Tovi Jaeschke-Rogers 12ce07e87e Remove debug println's 3 years ago
  Tovi Jaeschke-Rogers 3d4f5016e0 Move init config dir to Database namespace 3 years ago
  Tovi Jaeschke-Rogers d45f42fe26 Explicitly call InitConfigDir 3 years ago
  Tovi Jaeschke-Rogers 147ae7daf1 Add config dir to variables 3 years ago
  Tovi Jaeschke-Rogers 714eadc3ec Record installed packages and dependancies in sqlite3 db 3 years ago
  Tovi Jaeschke-Rogers 34f5b0c9c0 Add ability to create packages from diff 3 years ago
  Tovi Jaeschke-Rogers 9bd3d23efe Merge pull request 'feature/add-filesystem-db' (#1) from feature/add-filesystem-db into develop 3 years ago
  Tovi Jaeschke-Rogers 35075c741e Make fs scan asynchronous 3 years ago
  Tovi Jaeschke-Rogers a12d1b6d2e WIP 3 years ago
  Tovi Jaeschke-Rogers c6ac839418 Add progress bar and increase ignored files 3 years ago
  Tovi Jaeschke-Rogers 4686a53654 Add bolt db 3 years ago
  Tovi Jaeschke-Rogers d0f53fd27f Add Variables.InstallDirs for accurately scanning system 3 years ago
  Tovi Jaeschke-Rogers 7b6a860b32 Add go.mod and go.sum 3 years ago
  Tovi Jaeschke-Rogers 1872b493b6 Add DestDir variable 3 years ago
  Tovi Jaeschke-Rogers 16d689d525 Fix new files calculation 3 years ago
  Tovi Jaeschke-Rogers 300fadce37 WIP - Add install package 3 years ago
  Tovi Jaeschke-Rogers feaad6a039 Create packages from filesystem diff 3 years ago
  Tovi Jaeschke-Rogers 67488fbd70 Start on package creation 3 years ago
  Tovi Jaeschke-Rogers 8154034d5a Get filesystem diff 3 years ago
30 changed files with 2161 additions and 165 deletions
Unified View
  1. +6
    -0
      .gitignore
  2. +71
    -93
      Archive/Archive.go
  3. +153
    -60
      Archive/Unarchive.go
  4. +6
    -0
      CHANGELOG.md
  5. +122
    -8
      Client/Database/Init.go
  6. +62
    -0
      Client/Database/InstalledPkgs.go
  7. +51
    -0
      Client/Filesystem/CommitFiles.go
  8. +74
    -0
      Client/Filesystem/Config.go
  9. +48
    -0
      Client/Filesystem/CopyFile.go
  10. +214
    -0
      Client/Filesystem/FileObject.go
  11. +239
    -0
      Client/Filesystem/FilesystemDiff.go
  12. +28
    -0
      Client/Filesystem/ManageFileBucket.go
  13. +130
    -0
      Client/Filesystem/PickFiles.go
  14. +32
    -0
      Client/Filesystem/Print.go
  15. +18
    -0
      Client/Filesystem/StipRootDir.go
  16. +158
    -0
      Client/Package/CreatePackage.go
  17. +124
    -0
      Client/Package/InstallPackage.go
  18. +36
    -0
      Client/Package/Manifest.go
  19. +55
    -0
      Client/Package/UninstallPackage.go
  20. +31
    -0
      Client/ProgressBar/Bar.go
  21. +196
    -4
      Client/main.go
  22. +58
    -0
      Color/Color.go
  23. +42
    -0
      Helper/CheckRoot.go
  24. +25
    -0
      Helper/Input.go
  25. +14
    -0
      Makefile
  26. +23
    -0
      README.md
  27. +101
    -0
      Variables/Variables_unix.go
  28. +14
    -0
      go.mod
  29. +28
    -0
      go.sum
  30. +2
    -0
      notes.txt

+ 6
- 0
.gitignore View File

@ -0,0 +1,6 @@
*.db
*.tar
*.tar.gz
tjpkg
test.go
lfs_packages

+ 71
- 93
Archive/Archive.go View File

@ -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
} }

+ 153
- 60
Archive/Unarchive.go View File

@ -2,110 +2,203 @@ package Archive
import ( import (
"archive/tar" "archive/tar"
"bufio"
"bytes"
"compress/gzip" "compress/gzip"
"io" "io"
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
func UnGzip(source, target string) error {
func ExtractArchive(source, target string) error {
var ( var (
reader, writer *os.File
archive *gzip.Reader
e error
inFile *os.File
gzipReader *gzip.Reader
tarReader *tar.Reader
err error
) )
reader, e = os.Open(source)
if e != nil {
return e
inFile, err = os.Open(source)
if err != nil {
return err
} }
defer reader.Close()
defer inFile.Close()
archive, e = gzip.NewReader(reader)
if e != nil {
return e
gzipReader, err = gzip.NewReader(inFile)
if err != nil {
return err
} }
defer archive.Close()
defer gzipReader.Close()
target = filepath.Join(target, archive.Name)
tarReader = tar.NewReader(gzipReader)
writer, e = os.Create(target)
if e != nil {
return e
}
defer writer.Close()
_, e = io.Copy(writer, archive)
return e
return extractFromArchive(tarReader, target)
} }
func Untar(tarball, target string) error {
func extractFromArchive(tarReader *tar.Reader, target string) error {
var ( var (
reader *os.File
tarReader *tar.Reader
header *tar.Header
info fs.FileInfo
file *os.File
path string
e error
header *tar.Header
info fs.FileInfo
file *os.File
path, basePath string
err error
) )
reader, e = os.Open(tarball)
if e != nil {
return e
}
defer reader.Close()
tarReader = tar.NewReader(reader)
for { for {
header, e = tarReader.Next()
if e == io.EOF {
header, err = tarReader.Next()
if err == io.EOF {
break break
} }
if e != nil {
return e
if err != nil {
return err
} }
path = "/" + strings.Join(strings.Split(header.Name, "/")[1:], "/")
path = filepath.Join(target, header.Name)
info = header.FileInfo() info = header.FileInfo()
if filepath.Base(info.Name()) == "manifest.yml" {
continue
}
if info.IsDir() { if info.IsDir() {
e = os.MkdirAll(path, info.Mode())
if e != nil {
return e
err = os.MkdirAll(path, info.Mode())
if err != nil {
return err
} }
continue continue
} }
file, e = os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
if e != nil {
return e
basePath, err = filepath.Abs(filepath.Dir(path))
if err != nil {
return err
} }
defer file.Close()
_, err = os.Stat(basePath)
if os.IsNotExist(err) {
err = os.MkdirAll(basePath, info.Mode())
if err != nil {
return err
}
}
_, e = io.Copy(file, tarReader)
if e != nil {
return e
file, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, info.Mode())
if err != nil {
return err
} }
_, err = io.Copy(file, tarReader)
if err != nil {
return err
}
file.Close()
} }
return nil return nil
}
func ExtractManifestFile(source string) (string, error) {
var (
inFile *os.File
gzipReader *gzip.Reader
tarReader *tar.Reader
err error
)
inFile, err = os.Open(source)
if err != nil {
return "", err
}
defer inFile.Close()
gzipReader, err = gzip.NewReader(inFile)
if err != nil {
return "", err
}
defer gzipReader.Close()
tarReader = tar.NewReader(gzipReader)
return extractManifestFromArchive(tarReader)
}
func extractManifestFromArchive(tarReader *tar.Reader) (string, error) {
var (
header *tar.Header
info fs.FileInfo
manifestWriter *bufio.Writer
manifestBytes bytes.Buffer
err error
)
for {
header, err = tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return manifestBytes.String(), err
}
info = header.FileInfo()
if filepath.Base(info.Name()) != "manifest.yml" {
continue
}
manifestWriter = bufio.NewWriter(&manifestBytes)
_, err = io.Copy(manifestWriter, tarReader)
if err != nil {
return manifestBytes.String(), err
}
}
return manifestBytes.String(), nil
} }
func UntarGzip(source, target string) error {
func GetPackageFilePaths(source string) ([]string, error) {
var ( var (
tarPath string = strings.ReplaceAll(source, ".gz", "")
e error
inFile *os.File
gzipReader *gzip.Reader
tarReader *tar.Reader
info os.FileInfo
header *tar.Header
pkgFiles []string
err error
) )
e = UnGzip(source, tarPath)
if e != nil {
return e
inFile, err = os.Open(source)
if err != nil {
return pkgFiles, err
}
defer inFile.Close()
gzipReader, err = gzip.NewReader(inFile)
if err != nil {
return pkgFiles, err
}
defer gzipReader.Close()
tarReader = tar.NewReader(gzipReader)
for {
header, err = tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return pkgFiles, err
}
info = header.FileInfo()
if filepath.Base(info.Name()) == "manifest.yml" {
continue
}
pkgFiles = append(pkgFiles, header.Name)
} }
return Untar(tarPath, target)
return pkgFiles, nil
} }

+ 6
- 0
CHANGELOG.md View File

@ -0,0 +1,6 @@
# TJPKG CHANGELOG
[Unreleased]
## Added
## Changed
## Fixed

+ 122
- 8
Client/Database/Init.go View File

@ -2,23 +2,137 @@ package Database
import ( import (
"database/sql" "database/sql"
"log"
"os"
"path/filepath"
"time"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
bolt "go.etcd.io/bbolt"
"PackageManager/Variables"
) )
var ( var (
DB *sql.DB
DB *sql.DB
FsDB *bolt.DB
) )
func InitConfigDir() error {
var err error
_, err = os.Stat(Variables.ConfigDir)
if os.IsNotExist(err) {
return os.MkdirAll(Variables.ConfigDir, 0644)
}
return err
}
func InitDatabaseFiles() error {
var err error
// Initialise sqlite3 database for package versioning
DB, err = sql.Open(
"sqlite3",
filepath.Join(Variables.ConfigDir, Variables.DatabaseName),
)
if err != nil {
return err
}
// Initialise bolt db for filesystem hashing
FsDB, err = bolt.Open(
filepath.Join(Variables.ConfigDir, Variables.FsHashDatabaseName),
0600,
&bolt.Options{
Timeout: 5 * time.Second,
},
)
return err
}
func init() { func init() {
var e error
DB, e = sql.Open("sqlite3", "./foo.db")
if e != nil {
panic(e)
var err error
err = InitConfigDir()
if err != nil {
panic(err)
}
err = InitDatabaseFiles()
if err != nil {
panic(err)
}
}
func InitBoltDB() error {
var (
tx *bolt.Tx
err error
)
tx, err = FsDB.Begin(true)
if err != nil {
return err
}
defer tx.Rollback()
_, err = tx.CreateBucketIfNotExists(Variables.FsHashIndexBucket)
if err != nil {
return err
}
_, err = tx.CreateBucketIfNotExists(Variables.FsHashPicksBucket)
if err != nil {
return err
} }
return tx.Commit()
} }
func InitDB() {
log.Println("Initialising Database...")
func InitSqlite3DB() error {
var (
stmt *sql.Stmt
err error
)
stmt, err = DB.Prepare(`
CREATE TABLE IF NOT EXISTS installed_packages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(64) NOT NULL,
version VARCHAR(64) NOT NULL,
installed_at INTEGER NOT NULL
)
`)
if err != nil {
return err
}
_, err = stmt.Exec()
if err != nil {
return err
}
stmt, err = DB.Prepare(`
CREATE TABLE IF NOT EXISTS dependancy_linker (
id INTEGER PRIMARY KEY AUTOINCREMENT,
package_id INTEGER,
dependancy_id INTEGER,
FOREIGN KEY(package_id) REFERENCES installed_packages(id),
FOREIGN KEY(dependancy_id) REFERENCES installed_packages(id)
)
`)
if err != nil {
return err
}
_, err = stmt.Exec()
return err
}
func InitDB() error {
var err error = InitBoltDB()
if err != nil {
return err
}
return InitSqlite3DB()
} }

+ 62
- 0
Client/Database/InstalledPkgs.go View File

@ -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
}

+ 51
- 0
Client/Filesystem/CommitFiles.go View File

@ -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
}

+ 74
- 0
Client/Filesystem/Config.go View File

@ -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
}

+ 48
- 0
Client/Filesystem/CopyFile.go View File

@ -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
}

+ 214
- 0
Client/Filesystem/FileObject.go View File

@ -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
}

+ 239
- 0
Client/Filesystem/FilesystemDiff.go View File

@ -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
}

+ 28
- 0
Client/Filesystem/ManageFileBucket.go View File

@ -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))
}

+ 130
- 0
Client/Filesystem/PickFiles.go View File

@ -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
}

+ 32
- 0
Client/Filesystem/Print.go View File

@ -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)
}

+ 18
- 0
Client/Filesystem/StipRootDir.go View File

@ -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,
),
)
}

+ 158
- 0
Client/Package/CreatePackage.go View File

@ -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
}

+ 124
- 0
Client/Package/InstallPackage.go View File

@ -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
}

+ 36
- 0
Client/Package/Manifest.go View File

@ -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
}

+ 55
- 0
Client/Package/UninstallPackage.go View File

@ -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
}

+ 31
- 0
Client/ProgressBar/Bar.go View File

@ -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
}

+ 196
- 4
Client/main.go View File

@ -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()
} }

+ 58
- 0
Color/Color.go View File

@ -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
}

+ 42
- 0
Helper/CheckRoot.go View File

@ -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
}

+ 25
- 0
Helper/Input.go View File

@ -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)
}

+ 14
- 0
Makefile View File

@ -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}

+ 23
- 0
README.md View File

@ -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

+ 101
- 0
Variables/Variables_unix.go View File

@ -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
}
}

+ 14
- 0
go.mod View File

@ -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
)

+ 28
- 0
go.sum View File

@ -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=

+ 2
- 0
notes.txt View File

@ -0,0 +1,2 @@
Ncurses needs a post install command
ln -sf ../share/terminfo usr/lib/terminfo

Loading…
Cancel
Save