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
page on the wiki.
examples: |
$ town aup # open the aup
$ town aup --rainbow # open the aup with rainbow colors
maintainer: vilmibm
$ aup # open the aup
$ aup --rainbow # open the aup with rainbow colors
```
and using the launcher is like:
$ town aup
$ town aup --rainbow
$ town writo
$ town contrib writo
$ town admin ban vilmibm
You can see all the commands with `town help` as well as their descriptions; `town help
@ -41,10 +40,10 @@ account).
Remaining TODOs:
- [ ] fix arg passing
- [ ] 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
- [ ] add all existing commands to the buckets
- [ ] make little wrappers for things like `mail` and `chat`
- [x] fix arg passing
- [x] test with a command that makes use of stdin/stdout
- [x] add all existing commands to the buckets
- [x] add to users' paths
- [ ] add to users' paths

87
main.go
View File

@ -13,16 +13,21 @@ import (
"gopkg.in/yaml.v3"
)
const binroot = "/town/commands"
const binroot = "/town/launcher"
var rootCmd = &cobra.Command{
Use: "town",
Short: "Run commands unique to tilde.town",
}
var contribCmd = &cobra.Command{
Use: "contrib",
Short: "community-maintained town commands",
}
var adminCmd = &cobra.Command{
Use: "admin",
Short: "Run administrative commands",
Short: "commands used for administering the town",
}
func isAdmin() (bool, error) {
@ -48,13 +53,34 @@ func isAdmin() (bool, error) {
}
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)
files, err := ioutil.ReadDir(binPath)
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 {
@ -62,15 +88,12 @@ func parseCommands(targetCmd *cobra.Command, path string) error {
parseCommand(targetCmd, filepath.Join(binPath, file.Name()))
}
}
return nil
}
type commandDoc struct {
ShortDesc string `yaml:"shortDesc"`
LongDesc string `yaml:"longDesc"`
Examples string
Maintainer string
}
func parseCommand(targetCmd *cobra.Command, yamlPath string) {
@ -80,25 +103,20 @@ func parseCommand(targetCmd *cobra.Command, yamlPath string) {
_, err := os.Stat(executablePath)
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
}
yamlBytes, err := ioutil.ReadFile(yamlPath)
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
}
doc := commandDoc{}
err = yaml.Unmarshal(yamlBytes, &doc)
if err != nil {
fmt.Fprintf(os.Stderr, "could not parse %s; skipping...\n", yamlPath)
return
}
if doc.Maintainer == "" {
fmt.Fprintf(os.Stderr, "%s is missing maintainer field; skipping...\n", yamlPath)
fmt.Fprintf(os.Stderr, "could not parse %s; skipping...", yamlPath)
return
}
@ -114,8 +132,6 @@ func parseCommand(targetCmd *cobra.Command, yamlPath string) {
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 != "" {
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() {
os.Exit(cli())
rootCmd.Execute()
}