-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
fup |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package base | ||
|
||
import ( | ||
"gopkg.in/yaml.v3" | ||
"io" | ||
"os" | ||
) | ||
|
||
type Settings struct { | ||
ArchiveDir string `yaml:"archive_dir"` | ||
HostFacts map[string]map[string]any `yaml:"host_facts"` | ||
} | ||
|
||
type Unless struct { | ||
Cmd string `yaml:"cmd"` | ||
Post string `yaml:"post"` | ||
Ls string `yaml:"ls"` | ||
} | ||
|
||
type Archive struct { | ||
Url string `yaml:"url"` | ||
Unless Unless `yaml:"unless"` | ||
Version string `yaml:"version"` | ||
Symlink []string `yaml:"symlink"` | ||
Binary string `yaml:"binary"` | ||
} | ||
|
||
func (a Archive) ExpandArchive(property string) string { | ||
if property == "version" { | ||
return a.Version | ||
} | ||
|
||
return "" | ||
} | ||
|
||
type Config struct { | ||
Settings *Settings `yaml:"settings"` | ||
Archives []Archive `yaml:"archives"` | ||
} | ||
|
||
func ReadConfig(filename string) (Config, error) { | ||
config := Config{} | ||
|
||
f, err := os.Open(filename) | ||
if err != nil { | ||
return config, err | ||
} | ||
|
||
data, err := io.ReadAll(f) | ||
err = yaml.Unmarshal(data, &config) | ||
if err != nil { | ||
return config, err | ||
} | ||
return config, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
settings: | ||
archive_dir: ~/fup | ||
host_facts: | ||
lock_period: | ||
'*': 600 | ||
natrium: 3600 | ||
archives: | ||
- url: https://github.com/cli/cli/releases/download/v${version}/gh_${version}_linux_amd64.tar.gz | ||
version: 2.18.1 | ||
unless: | ||
cmd: gh version | ||
post: 'head 0 | split 2' | ||
symlink: | ||
- gh_${version}_linux_amd64/bin/gh | ||
- url: https://github.com/tectonic-typesetting/tectonic/releases/download/tectonic%40${version}/tectonic-${version}-x86_64-unknown-linux-gnu.tar.gz | ||
version: 0.11.0 | ||
unless: | ||
cmd: tectonic --version | ||
post: split 1 | ||
binary: tectonic |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module github.com/femnad/fup | ||
|
||
go 1.18 | ||
|
||
require ( | ||
github.com/alexflint/go-arg v1.4.3 | ||
gopkg.in/yaml.v3 v3.0.1 | ||
) | ||
|
||
require github.com/alexflint/go-scalar v1.1.0 // indirect |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo= | ||
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA= | ||
github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM= | ||
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/alexflint/go-arg" | ||
"github.com/femnad/fup/base" | ||
"github.com/femnad/fup/provision" | ||
"log" | ||
) | ||
|
||
var args struct { | ||
File string `arg:"required,-f,--file"` | ||
} | ||
|
||
func main() { | ||
arg.MustParse(&args) | ||
config, err := base.ReadConfig(args.File) | ||
if err != nil { | ||
log.Fatalf("%v\n", err) | ||
} | ||
|
||
p := provision.Provisioner{Config: config} | ||
p.Apply() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package provision | ||
|
||
import ( | ||
"archive/tar" | ||
"compress/gzip" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"log" | ||
"net/http" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/femnad/fup/base" | ||
) | ||
|
||
const ( | ||
bufferSize = 8192 | ||
dirMode = 0755 | ||
) | ||
|
||
var ( | ||
okStatuses = []int{http.StatusOK} | ||
) | ||
|
||
// Yoinked from https://gosamples.dev/generics-slice-contains/. | ||
func contains[T comparable](elems []T, needle T) bool { | ||
for _, elem := range elems { | ||
if needle == elem { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func expandUser(path string) string { | ||
return strings.Replace(path, "~", os.Getenv("HOME"), 1) | ||
} | ||
|
||
func processDownload(archive base.Archive, archiveDir string, processor func(closer io.ReadCloser, target string) error) error { | ||
url := archive.Url | ||
if url == "" { | ||
return fmt.Errorf("no URL given for archive %v", archive) | ||
} | ||
|
||
url = os.Expand(url, archive.ExpandArchive) | ||
log.Printf("Downloading %s", url) | ||
|
||
cl := http.Client{} | ||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resp, err := cl.Do(req) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
statusCode := resp.StatusCode | ||
if !contains(okStatuses, statusCode) { | ||
log.Printf("Skipping archive download, got response %d from URL %s", statusCode, url) | ||
return nil | ||
} | ||
|
||
dirName := expandUser(archiveDir) | ||
err = os.MkdirAll(dirName, dirMode) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if archive.Binary != "" { | ||
dirName = filepath.Join(dirName, archive.Binary) | ||
} | ||
|
||
return processor(resp.Body, dirName) | ||
} | ||
|
||
func mkdirAll(dir string, mode os.FileMode) error { | ||
err := os.MkdirAll(dir, mode) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Shamelessly lifted from https://golangdocs.com/tar-gzip-in-golang | ||
func untar(tarfile io.ReadCloser, target string) error { | ||
defer func() { | ||
err := tarfile.Close() | ||
if err != nil { | ||
log.Fatalf("Error closing tarfile: %v", err) | ||
} | ||
}() | ||
|
||
gzipReader, err := gzip.NewReader(tarfile) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
tarReader := tar.NewReader(gzipReader) | ||
for { | ||
header, err := tarReader.Next() | ||
if errors.Is(err, io.EOF) { | ||
break | ||
} else if err != nil { | ||
panic(err) | ||
} | ||
|
||
outputPath := filepath.Join(target, header.Name) | ||
info := header.FileInfo() | ||
if info.IsDir() { | ||
if err = mkdirAll(outputPath, info.Mode()); err != nil { | ||
return err | ||
} | ||
continue | ||
} | ||
|
||
dir, _ := path.Split(outputPath) | ||
if err = os.MkdirAll(dir, dirMode); err != nil { | ||
return err | ||
} | ||
file, err := os.OpenFile(outputPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = io.Copy(file, tarReader) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = file.Close() | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func Extract(archive base.Archive, archiveDir string) error { | ||
return processDownload(archive, archiveDir, untar) | ||
} | ||
|
||
func download(closer io.ReadCloser, target string) error { | ||
f, err := os.Create(target) | ||
if err != nil { | ||
return fmt.Errorf("error creating target %s: %w", target, err) | ||
} | ||
|
||
for { | ||
buf := make([]byte, bufferSize) | ||
|
||
readBytes, readErr := closer.Read(buf) | ||
if !errors.Is(readErr, io.EOF) && err != nil { | ||
return readErr | ||
} | ||
|
||
_, writeErr := f.Write(buf[:readBytes]) | ||
if writeErr != nil { | ||
return err | ||
} | ||
if errors.Is(readErr, io.EOF) { | ||
break | ||
} | ||
} | ||
|
||
err = closer.Close() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = f.Close() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
|
||
} | ||
|
||
func Download(archive base.Archive, archiveDir string) error { | ||
return processDownload(archive, archiveDir, download) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package provision | ||
|
||
import ( | ||
"github.com/femnad/fup/base" | ||
"log" | ||
) | ||
|
||
type Provisioner struct { | ||
Config base.Config | ||
} | ||
|
||
func (p Provisioner) Apply() { | ||
p.downloadArchives() | ||
} | ||
|
||
func (p Provisioner) downloadArchives() { | ||
for _, archive := range p.Config.Archives { | ||
err := Extract(archive, p.Config.Settings.ArchiveDir) | ||
if err != nil { | ||
log.Fatalf("Error downloading archive %v: %v", archive.Url, err) | ||
} | ||
} | ||
} |