Compare commits

..

No commits in common. "9dbec05b3f39aef116df815406dda8945e189dfd" and "98e3297d2ed53843bddbd49fca2171a6eee00821" have entirely different histories.

2 changed files with 44 additions and 64 deletions

View File

@ -20,16 +20,15 @@ longDesc: |
Policies that governed servers like this in the past. It will open the elinks browser to a Policies that governed servers like this in the past. It will open the elinks browser to a
page on the wiki. page on the wiki.
examples: | examples: |
$ town aup # open the aup $ aup # open the aup
$ town aup --rainbow # open the aup with rainbow colors $ aup --rainbow # open the aup with rainbow colors
maintainer: vilmibm
``` ```
and using the launcher is like: and using the launcher is like:
$ town aup $ town aup
$ town aup --rainbow $ town aup --rainbow
$ town writo $ town contrib writo
$ town admin ban vilmibm $ town admin ban vilmibm
You can see all the commands with `town help` as well as their descriptions; `town help You can see all the commands with `town help` as well as their descriptions; `town help
@ -41,10 +40,10 @@ account).
Remaining TODOs: Remaining TODOs:
- [ ] fix arg passing
- [ ] make tab completion available for common shells - [ ] make tab completion available for common shells
- [ ] test with a command that makes use of stdin/stdout
- [ ] document / script submitting a tool for inclusion in contrib - [ ] document / script submitting a tool for inclusion in contrib
- [ ] add all existing commands to the buckets
- [ ] make little wrappers for things like `mail` and `chat` - [ ] make little wrappers for things like `mail` and `chat`
- [x] fix arg passing - [ ] add to users' paths
- [x] test with a command that makes use of stdin/stdout
- [x] add all existing commands to the buckets
- [x] add to users' paths

93
main.go
View File

@ -13,16 +13,21 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const binroot = "/town/commands" const binroot = "/town/launcher"
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "town", Use: "town",
Short: "Run commands unique to tilde.town", Short: "Run commands unique to tilde.town",
} }
var contribCmd = &cobra.Command{
Use: "contrib",
Short: "community-maintained town commands",
}
var adminCmd = &cobra.Command{ var adminCmd = &cobra.Command{
Use: "admin", Use: "admin",
Short: "Run administrative commands", Short: "commands used for administering the town",
} }
func isAdmin() (bool, error) { func isAdmin() (bool, error) {
@ -48,13 +53,34 @@ func isAdmin() (bool, error) {
} }
return false, nil return false, nil
} }
func parseCommands(targetCmd *cobra.Command, path string) error { func init() {
rootCmd.AddCommand(contribCmd)
parseCommands(rootCmd, "core")
parseCommands(contribCmd, "contrib")
admin, err := isAdmin()
if err != nil {
panic(fmt.Sprintf("failed to check admin status: %s", err))
}
if admin {
rootCmd.AddCommand(adminCmd)
parseCommands(adminCmd, "admin")
}
// I feel like the example/documentation yaml can be frontmatter for non-binary files. to start
// i'll just do the accompanying yaml file.
}
func parseCommands(targetCmd *cobra.Command, path string) {
binPath := filepath.Join(binroot, path) binPath := filepath.Join(binroot, path)
files, err := ioutil.ReadDir(binPath) files, err := ioutil.ReadDir(binPath)
if err != nil { if err != nil {
return fmt.Errorf("failed to list directory %s: %s", binPath, err) panic(fmt.Sprintf("failed to list directory %s: %s", binPath, err))
} }
for _, file := range files { for _, file := range files {
@ -62,15 +88,12 @@ func parseCommands(targetCmd *cobra.Command, path string) error {
parseCommand(targetCmd, filepath.Join(binPath, file.Name())) parseCommand(targetCmd, filepath.Join(binPath, file.Name()))
} }
} }
return nil
} }
type commandDoc struct { type commandDoc struct {
ShortDesc string `yaml:"shortDesc"` ShortDesc string `yaml:"shortDesc"`
LongDesc string `yaml:"longDesc"` LongDesc string `yaml:"longDesc"`
Examples string Examples string
Maintainer string
} }
func parseCommand(targetCmd *cobra.Command, yamlPath string) { func parseCommand(targetCmd *cobra.Command, yamlPath string) {
@ -80,25 +103,20 @@ func parseCommand(targetCmd *cobra.Command, yamlPath string) {
_, err := os.Stat(executablePath) _, err := os.Stat(executablePath)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "could not find matching executable for %s; skipping...\n", yamlPath) fmt.Fprintf(os.Stderr, "could not find matching executable for %s; skipping...", yamlPath)
return return
} }
yamlBytes, err := ioutil.ReadFile(yamlPath) yamlBytes, err := ioutil.ReadFile(yamlPath)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "could not read %s; skipping...\n", yamlPath) fmt.Fprintf(os.Stderr, "could not read %s; skipping...", yamlPath)
return return
} }
doc := commandDoc{} doc := commandDoc{}
err = yaml.Unmarshal(yamlBytes, &doc) err = yaml.Unmarshal(yamlBytes, &doc)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "could not parse %s; skipping...\n", yamlPath) fmt.Fprintf(os.Stderr, "could not parse %s; skipping...", yamlPath)
return
}
if doc.Maintainer == "" {
fmt.Fprintf(os.Stderr, "%s is missing maintainer field; skipping...\n", yamlPath)
return return
} }
@ -114,8 +132,6 @@ func parseCommand(targetCmd *cobra.Command, yamlPath string) {
parsedCmd.Long = doc.LongDesc parsedCmd.Long = doc.LongDesc
} }
parsedCmd.Long += fmt.Sprintf("\nMaintained by %s; reach out to them via mail or chat with questions", doc.Maintainer)
if doc.Examples != "" { if doc.Examples != "" {
parsedCmd.Example = doc.Examples parsedCmd.Example = doc.Examples
} }
@ -133,41 +149,6 @@ func execWrapper(executablePath string) func(*cobra.Command, []string) error {
} }
} }
func cli() int {
err := parseCommands(rootCmd, "core")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to parse core commands: %s", err)
return 1
}
err = parseCommands(rootCmd, "contrib")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to parse contrib commands: %s", err)
return 1
}
admin, err := isAdmin()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to check admin status: %s", err)
return 2
}
if admin {
rootCmd.AddCommand(adminCmd)
err = parseCommands(adminCmd, "admin")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to parse admin commands: %s", err)
return 1
}
}
// I feel like the example/documentation yaml can be frontmatter for non-binary files. to start
// i'll just do the accompanying yaml file.
rootCmd.Execute()
return 0
}
func main() { func main() {
os.Exit(cli()) rootCmd.Execute()
} }