Moved
This commit is contained in:
parent
5f4ad63266
commit
fb9f151979
4 changed files with 661 additions and 2 deletions
|
|
@ -1 +1 @@
|
|||
crystal build src/mol.cr -o mol.exe --release
|
||||
go build -o mol.exe mol.go
|
||||
2
build.sh
2
build.sh
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/sh
|
||||
crystal build src/mol.cr -o mol --release
|
||||
go build -o mol.exe mol.go
|
||||
BIN
mol.exe
BIN
mol.exe
Binary file not shown.
659
mol.go
Normal file
659
mol.go
Normal file
|
|
@ -0,0 +1,659 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const version = "0.4.0-boredos"
|
||||
|
||||
var (
|
||||
home = os.Getenv("HOME")
|
||||
bin = filepath.Join(home, ".local", "bin")
|
||||
src = ".mol/src"
|
||||
db = ".mol/repo"
|
||||
stage = ".mol/stage"
|
||||
manifestDir = ".mol/manifests"
|
||||
reposFile = ".mol/repos"
|
||||
depCache = ".mol/depcache.json"
|
||||
defaultRepo = envOr("MOL_REPO", "https://git.sr.ht/~chersbobers/mol-pkgrepo/blob/master/PACKAGES?raw=true")
|
||||
goos = runtime.GOOS
|
||||
)
|
||||
|
||||
func init() {
|
||||
if goos == "windows" {
|
||||
bin = filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Local", "mol", "bin")
|
||||
}
|
||||
}
|
||||
|
||||
func envOr(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
const (
|
||||
reset = "\033[0m"
|
||||
bold = "\033[1m"
|
||||
dim = "\033[2m"
|
||||
green = "\033[32m"
|
||||
yellow = "\033[33m"
|
||||
cyan = "\033[36m"
|
||||
red = "\033[31m"
|
||||
)
|
||||
|
||||
func uiInfo(msg string) { fmt.Printf("%s →%s %s\n", cyan, reset, msg) }
|
||||
func uiOk(msg string) { fmt.Printf("%s ✓%s %s%s%s\n", green, reset, bold, msg, reset) }
|
||||
func uiWarn(msg string) { fmt.Printf("%s ⚠%s %s\n", yellow, reset, msg) }
|
||||
func uiErr(msg string) { fmt.Fprintf(os.Stderr, "%s ✗%s %s%s%s\n", red, reset, bold, msg, reset) }
|
||||
func uiDim(msg string) { fmt.Printf("%s %s%s\n", dim, msg, reset) }
|
||||
func uiSection(msg string) { fmt.Printf("\n%s%s▸ %s%s\n", bold, cyan, msg, reset) }
|
||||
|
||||
func uiHeader() {
|
||||
fmt.Printf("%s%smol%s %sv%s · GPLv3%s\n", bold, cyan, reset, dim, version, reset)
|
||||
fmt.Printf("%s%s%s\n", dim, strings.Repeat("─", 42), reset)
|
||||
}
|
||||
|
||||
func uiProgress(label string, total int, fn func(int)) {
|
||||
for i := 0; i < total; i++ {
|
||||
pct := ((i + 1) * 100) / total
|
||||
done := (30 * pct) / 100
|
||||
bar := strings.Repeat("█", done) + strings.Repeat("░", 30-done)
|
||||
fmt.Printf("\r %s%s%s %d%% %s%s%s ", cyan, bar, reset, pct, dim, label, reset)
|
||||
fn(i)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func run(cmd string, args []string, dir string) bool {
|
||||
c := exec.Command(cmd, args...)
|
||||
c.Dir = dir
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
return c.Run() == nil
|
||||
}
|
||||
|
||||
func stageBegin(name string) string {
|
||||
path := filepath.Join(stage, name)
|
||||
os.RemoveAll(path)
|
||||
os.MkdirAll(path, 0755)
|
||||
return path
|
||||
}
|
||||
|
||||
func stageCommit(name, stagedPath string) {
|
||||
manifestPath := filepath.Join(manifestDir, name+".list")
|
||||
os.MkdirAll(manifestDir, 0755)
|
||||
var written []string
|
||||
|
||||
filepath.Walk(stagedPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
rel, _ := filepath.Rel(stagedPath, path)
|
||||
dest := filepath.Join("/", rel)
|
||||
os.MkdirAll(filepath.Dir(dest), 0755)
|
||||
copyFile(path, dest)
|
||||
written = append(written, dest)
|
||||
return nil
|
||||
})
|
||||
|
||||
os.WriteFile(manifestPath, []byte(strings.Join(written, "\n")), 0644)
|
||||
os.RemoveAll(stagedPath)
|
||||
uiOk(fmt.Sprintf("committed %d file(s) for %s", len(written), name))
|
||||
}
|
||||
|
||||
func stageRollback(name string) {
|
||||
manifestPath := filepath.Join(manifestDir, name+".list")
|
||||
if _, err := os.Stat(manifestPath); os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
uiWarn("rolling back " + name + "…")
|
||||
f, _ := os.Open(manifestPath)
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
path := strings.TrimSpace(scanner.Text())
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
os.Remove(path)
|
||||
uiDim("removed " + path)
|
||||
}
|
||||
f.Close()
|
||||
os.Remove(manifestPath)
|
||||
uiOk("rollback complete for " + name)
|
||||
}
|
||||
|
||||
func hasManifest(name string) bool {
|
||||
_, err := os.Stat(filepath.Join(manifestDir, name+".list"))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, in)
|
||||
return err
|
||||
}
|
||||
|
||||
type graph map[string][]string
|
||||
|
||||
func loadGraph() graph {
|
||||
g := make(graph)
|
||||
data, err := os.ReadFile(depCache)
|
||||
if err != nil {
|
||||
return g
|
||||
}
|
||||
json.Unmarshal(data, &g)
|
||||
return g
|
||||
}
|
||||
|
||||
func saveGraph(g graph) {
|
||||
os.MkdirAll(filepath.Dir(depCache), 0755)
|
||||
data, _ := json.Marshal(g)
|
||||
os.WriteFile(depCache, data, 0644)
|
||||
}
|
||||
|
||||
func resolveGraph(root string, g graph) []string {
|
||||
var order []string
|
||||
visited := make(map[string]bool)
|
||||
var visit func(string)
|
||||
visit = func(name string) {
|
||||
if visited[name] {
|
||||
return
|
||||
}
|
||||
visited[name] = true
|
||||
for _, dep := range g[name] {
|
||||
visit(dep)
|
||||
}
|
||||
order = append(order, name)
|
||||
}
|
||||
visit(root)
|
||||
return order
|
||||
}
|
||||
|
||||
func loadRepos() []string {
|
||||
repos := []string{defaultRepo}
|
||||
f, err := os.Open(reposFile)
|
||||
if err != nil {
|
||||
return repos
|
||||
}
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
l := strings.TrimSpace(scanner.Text())
|
||||
if l == "" || strings.HasPrefix(l, "#") {
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, r := range repos {
|
||||
if r == l {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
repos = append(repos, l)
|
||||
}
|
||||
}
|
||||
return repos
|
||||
}
|
||||
|
||||
func addRepo(url string) {
|
||||
os.MkdirAll(filepath.Dir(reposFile), 0755)
|
||||
f, _ := os.OpenFile(reposFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
defer f.Close()
|
||||
fmt.Fprintln(f, url)
|
||||
uiOk("added repo: " + url)
|
||||
}
|
||||
|
||||
func lookup(t string) (string, string) {
|
||||
if strings.Contains(t, "://") {
|
||||
return t, ""
|
||||
}
|
||||
f, err := os.Open(db)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s not found — run: mol sync\n", t)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
l := strings.TrimSpace(scanner.Text())
|
||||
if l == "" || strings.HasPrefix(l, "#") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Fields(l)
|
||||
if len(parts) < 2 || parts[0] != t {
|
||||
continue
|
||||
}
|
||||
binURL := ""
|
||||
for _, p := range parts {
|
||||
if strings.HasPrefix(p, "B=") {
|
||||
binURL = p[2:]
|
||||
}
|
||||
}
|
||||
return parts[1], binURL
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s not found — run: mol sync\n", t)
|
||||
os.Exit(1)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func installed(name string) bool {
|
||||
if hasManifest(name) {
|
||||
return true
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(src, name)); err == nil {
|
||||
return true
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(bin, name)); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func linkBins(path string) {
|
||||
os.MkdirAll(bin, 0755)
|
||||
candidates := []string{
|
||||
filepath.Join(path, "bin"),
|
||||
filepath.Join(path, "target", "release"),
|
||||
path,
|
||||
}
|
||||
for _, d := range candidates {
|
||||
if _, err := os.Stat(d); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
entries, _ := os.ReadDir(d)
|
||||
for _, e := range entries {
|
||||
if e.IsDir() || strings.Contains(e.Name(), ".") {
|
||||
continue
|
||||
}
|
||||
full := filepath.Join(d, e.Name())
|
||||
info, _ := e.Info()
|
||||
if info.Mode()&0111 == 0 {
|
||||
continue
|
||||
}
|
||||
dest := filepath.Join(bin, e.Name())
|
||||
copyFile(full, dest)
|
||||
os.Chmod(dest, 0755)
|
||||
uiInfo("linked → " + dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseMolpkg(path string) ([]string, []string) {
|
||||
var deps, build []string
|
||||
file := filepath.Join(path, "mol.pkg")
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return deps, build
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
inBuild, inOs, inInstall := false, false, false
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
l := strings.TrimSpace(scanner.Text())
|
||||
switch {
|
||||
case strings.Contains(l, "(deps"):
|
||||
for _, m := range extractQuoted(l) {
|
||||
deps = append(deps, m)
|
||||
}
|
||||
case strings.Contains(l, "(build"):
|
||||
inBuild = true
|
||||
case strings.Contains(l, "(install"):
|
||||
inBuild, inInstall = false, true
|
||||
case inBuild && strings.Contains(l, "("+goos):
|
||||
inOs = true
|
||||
case inBuild && inOs && strings.HasPrefix(l, "(run"):
|
||||
parts := extractQuoted(l)
|
||||
if len(parts) > 0 {
|
||||
build = append(build, strings.Join(parts, " "))
|
||||
}
|
||||
case l == ")" && inOs:
|
||||
inOs = false
|
||||
case l == ")" && inBuild:
|
||||
inBuild = false
|
||||
case inInstall && strings.Contains(l, "(bin"):
|
||||
quoted := extractQuoted(l)
|
||||
if len(quoted) == 0 {
|
||||
continue
|
||||
}
|
||||
name := quoted[0]
|
||||
srcPath := filepath.Join(path, name)
|
||||
if goos == "windows" {
|
||||
if _, err := os.Stat(srcPath); os.IsNotExist(err) {
|
||||
srcPath = srcPath + ".exe"
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(srcPath); err == nil {
|
||||
dest := filepath.Join(bin, filepath.Base(srcPath))
|
||||
copyFile(srcPath, dest)
|
||||
os.Chmod(dest, 0755)
|
||||
uiInfo("linked → " + dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
return deps, build
|
||||
}
|
||||
|
||||
func extractQuoted(s string) []string {
|
||||
var results []string
|
||||
for {
|
||||
start := strings.Index(s, `"`)
|
||||
if start == -1 {
|
||||
break
|
||||
}
|
||||
end := strings.Index(s[start+1:], `"`)
|
||||
if end == -1 {
|
||||
break
|
||||
}
|
||||
results = append(results, s[start+1:start+1+end])
|
||||
s = s[start+1+end+1:]
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func install(name string, fromSrc, fast bool) {
|
||||
if installed(name) {
|
||||
return
|
||||
}
|
||||
uiSection("installing " + name)
|
||||
|
||||
url, binURL := lookup(name)
|
||||
|
||||
if (fast || !fromSrc) && binURL != "" {
|
||||
uiInfo("fetching prebuilt binary…")
|
||||
resp, err := http.Get(binURL)
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
fmt.Fprintln(os.Stderr, "download failed")
|
||||
os.Exit(1)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
os.MkdirAll(bin, 0755)
|
||||
dest := filepath.Join(bin, filepath.Base(binURL))
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
os.WriteFile(dest, data, 0755)
|
||||
uiOk(name + " installed (binary)")
|
||||
return
|
||||
}
|
||||
|
||||
n := strings.TrimSuffix(strings.TrimSuffix(filepath.Base(url), ".git"), ".hg")
|
||||
clonePath := filepath.Join(src, n)
|
||||
vcs := "git"
|
||||
if strings.Contains(url, "hg") {
|
||||
vcs = "hg"
|
||||
}
|
||||
uiInfo("cloning " + url + "…")
|
||||
if !run(vcs, []string{"clone", url, clonePath}, "") {
|
||||
fmt.Fprintln(os.Stderr, "clone failed")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
deps, buildCmds := parseMolpkg(clonePath)
|
||||
|
||||
g := loadGraph()
|
||||
g[name] = deps
|
||||
saveGraph(g)
|
||||
|
||||
order := resolveGraph(name, g)
|
||||
for _, dep := range order {
|
||||
if dep != name && !installed(dep) {
|
||||
uiInfo("dependency: " + dep)
|
||||
install(dep, fromSrc, fast)
|
||||
}
|
||||
}
|
||||
|
||||
if len(buildCmds) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "no build steps for %s in %s\n", goos, n)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
staged := stageBegin(name)
|
||||
uiSection("building " + name)
|
||||
failed := false
|
||||
uiProgress("compiling", len(buildCmds), func(i int) {
|
||||
parts := strings.Fields(buildCmds[i])
|
||||
if !run(parts[0], parts[1:], clonePath) {
|
||||
failed = true
|
||||
}
|
||||
})
|
||||
if failed {
|
||||
stageRollback(name)
|
||||
fmt.Fprintln(os.Stderr, "build failed")
|
||||
os.Exit(1)
|
||||
}
|
||||
linkBins(clonePath)
|
||||
stageCommit(name, staged)
|
||||
uiOk(name + " installed from source")
|
||||
}
|
||||
|
||||
func removePkg(name string) {
|
||||
uiSection("removing " + name)
|
||||
if hasManifest(name) {
|
||||
stageRollback(name)
|
||||
}
|
||||
os.RemoveAll(filepath.Join(src, name))
|
||||
os.Remove(filepath.Join(bin, name))
|
||||
os.Remove(filepath.Join(bin, name+".exe"))
|
||||
uiOk(name + " removed")
|
||||
}
|
||||
|
||||
func updatePkg(name string, fast bool) {
|
||||
clonePath := filepath.Join(src, name)
|
||||
if fast {
|
||||
uiInfo("fast update: reinstalling binary for " + name + "…")
|
||||
removePkg(name)
|
||||
install(name, false, true)
|
||||
return
|
||||
}
|
||||
uiSection("updating " + name)
|
||||
if _, err := os.Stat(clonePath); os.IsNotExist(err) {
|
||||
fmt.Fprintln(os.Stderr, name+" not found, install first")
|
||||
os.Exit(1)
|
||||
}
|
||||
vcs := "git"
|
||||
if _, err := os.Stat(filepath.Join(clonePath, ".hg")); err == nil {
|
||||
vcs = "hg"
|
||||
}
|
||||
args := []string{"pull"}
|
||||
if vcs == "hg" {
|
||||
args = []string{"pull", "-u"}
|
||||
}
|
||||
if !run(vcs, args, clonePath) {
|
||||
fmt.Fprintln(os.Stderr, "pull failed")
|
||||
os.Exit(1)
|
||||
}
|
||||
_, buildCmds := parseMolpkg(clonePath)
|
||||
staged := stageBegin(name)
|
||||
for _, cmd := range buildCmds {
|
||||
parts := strings.Fields(cmd)
|
||||
if !run(parts[0], parts[1:], clonePath) {
|
||||
stageRollback(name)
|
||||
fmt.Fprintln(os.Stderr, "build failed: "+cmd)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
linkBins(clonePath)
|
||||
stageCommit(name, staged)
|
||||
uiOk(name + " updated")
|
||||
}
|
||||
|
||||
func syncAll() {
|
||||
uiSection("syncing repos")
|
||||
repos := loadRepos()
|
||||
os.MkdirAll(filepath.Dir(db), 0755)
|
||||
var combined strings.Builder
|
||||
for i, repo := range repos {
|
||||
uiInfo(fmt.Sprintf("(%d/%d) %s", i+1, len(repos), repo))
|
||||
resp, err := http.Get(repo)
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
uiWarn(fmt.Sprintf("skipped: %s", repo))
|
||||
continue
|
||||
}
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
combined.Write(data)
|
||||
combined.WriteString("\n")
|
||||
}
|
||||
os.WriteFile(db, []byte(combined.String()), 0644)
|
||||
uiOk(fmt.Sprintf("synced %d repo(s)", len(repos)))
|
||||
}
|
||||
|
||||
func bootstrap(recipePath string) {
|
||||
uiSection("bootstrapping BoredOS from " + recipePath)
|
||||
if _, err := os.Stat(recipePath); os.IsNotExist(err) {
|
||||
fmt.Fprintln(os.Stderr, "recipe not found: "+recipePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
deps, buildCmds := parseMolpkg(filepath.Dir(recipePath))
|
||||
uiInfo("installing bootstrap deps…")
|
||||
for _, d := range deps {
|
||||
install(d, false, false)
|
||||
}
|
||||
uiInfo("running bootstrap build steps…")
|
||||
for _, cmd := range buildCmds {
|
||||
parts := strings.Fields(cmd)
|
||||
uiDim("$ " + cmd)
|
||||
if !run(parts[0], parts[1:], "") {
|
||||
fmt.Fprintln(os.Stderr, "bootstrap step failed: "+cmd)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
uiOk("bootstrap complete")
|
||||
}
|
||||
|
||||
func contains(args []string, flag string) bool {
|
||||
for _, a := range args {
|
||||
if a == flag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
os.MkdirAll(src, 0755)
|
||||
os.MkdirAll(stage, 0755)
|
||||
os.MkdirAll(manifestDir, 0755)
|
||||
os.MkdirAll(filepath.Dir(db), 0755)
|
||||
|
||||
args := os.Args[1:]
|
||||
cmd := ""
|
||||
if len(args) > 0 {
|
||||
cmd = args[0]
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case "install":
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: mol install <name> [--source] [-f]")
|
||||
os.Exit(1)
|
||||
}
|
||||
install(args[1], contains(args, "--source"), contains(args, "-f"))
|
||||
|
||||
case "remove":
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: mol remove <name>")
|
||||
os.Exit(1)
|
||||
}
|
||||
removePkg(args[1])
|
||||
|
||||
case "update":
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: mol update <name> [-f]")
|
||||
os.Exit(1)
|
||||
}
|
||||
updatePkg(args[1], contains(args, "-f"))
|
||||
|
||||
case "sync":
|
||||
syncAll()
|
||||
|
||||
case "repo":
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: mol repo <add|list>")
|
||||
os.Exit(1)
|
||||
}
|
||||
switch args[1] {
|
||||
case "add":
|
||||
if len(args) < 3 {
|
||||
fmt.Fprintln(os.Stderr, "usage: mol repo add <url>")
|
||||
os.Exit(1)
|
||||
}
|
||||
addRepo(args[2])
|
||||
case "list":
|
||||
for i, r := range loadRepos() {
|
||||
fmt.Printf(" %d. %s\n", i+1, r)
|
||||
}
|
||||
}
|
||||
|
||||
case "bootstrap":
|
||||
if len(args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: mol bootstrap <mol.pkg path>")
|
||||
os.Exit(1)
|
||||
}
|
||||
bootstrap(args[1])
|
||||
|
||||
case "-l":
|
||||
if _, err := os.Stat(db); os.IsNotExist(err) {
|
||||
fmt.Fprintln(os.Stderr, "no repo — run: mol sync")
|
||||
os.Exit(1)
|
||||
}
|
||||
f, _ := os.Open(db)
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
l := strings.TrimSpace(scanner.Text())
|
||||
if l == "" || strings.HasPrefix(l, "#") {
|
||||
continue
|
||||
}
|
||||
fmt.Println(strings.Fields(l)[0])
|
||||
}
|
||||
|
||||
case "--version", "-v":
|
||||
uiHeader()
|
||||
fmt.Printf(" OS : %s\n", goos)
|
||||
fmt.Printf(" arch : %s\n", runtime.GOARCH)
|
||||
|
||||
default:
|
||||
uiHeader()
|
||||
fmt.Printf(`
|
||||
%sUsage:%s mol <command> [options]
|
||||
|
||||
%sPackage commands:%s
|
||||
install <name> [--source] [-f] install a package
|
||||
remove <name> remove a package
|
||||
update <name> [-f] pull, rebuild, re-link
|
||||
sync fetch all configured repos
|
||||
|
||||
%sRepo commands:%s
|
||||
repo add <url> register an external repo
|
||||
repo list show configured repos
|
||||
|
||||
%sBoredOS tools:%s
|
||||
bootstrap <mol.pkg> build BoredOS from a host recipe
|
||||
|
||||
%sInfo:%s
|
||||
-l list available packages
|
||||
--version show version and platform info
|
||||
|
||||
`, bold, reset, cyan, reset, cyan, reset, cyan, reset, cyan, reset)
|
||||
}
|
||||
}
|
||||
Reference in a new issue