Compare commits

...

65 Commits

Author SHA1 Message Date
4df53b0b1b Merge pull request 'fix(deps): update module github.com/spf13/cobra to v1.8.1' (#6) from renovate/github.com-spf13-cobra-1.x into master
Reviewed-on: #6
2024-07-07 16:12:52 +00:00
Renovate Bot
80ad56859a fix(deps): update module github.com/spf13/cobra to v1.8.1 2024-07-06 14:22:35 -07:00
dda31be0bd Merge pull request 'fix(deps): update module github.com/fsnotify/fsnotify to v1.7.0' (#5) from renovate/github.com-fsnotify-fsnotify-1.x into master
Reviewed-on: #5
2024-07-06 21:06:16 +00:00
c241c301eb Merge pull request 'fix(deps): update module golang.org/x/net to v0.27.0' (#7) from renovate/golang.org-x-net-0.x into master
Reviewed-on: #7
2024-07-06 21:06:10 +00:00
9c5d8c6880 Merge pull request 'fix(deps): update module gopkg.in/yaml.v2 to v3' (#8) from renovate/gopkg.in-yaml.v2-3.x into master
Reviewed-on: #8
2024-07-06 21:06:07 +00:00
Renovate Bot
cdad1d23a9 fix(deps): update module gopkg.in/yaml.v2 to v3 2024-07-06 13:50:00 -07:00
Renovate Bot
032b1c4963 fix(deps): update module golang.org/x/net to v0.27.0 2024-07-06 13:49:57 -07:00
Renovate Bot
8f507fc092 fix(deps): update module github.com/fsnotify/fsnotify to v1.7.0 2024-07-06 13:49:49 -07:00
3f7815e571 Update renovate.json 2024-07-06 20:35:58 +00:00
7eb6f55084 Merge pull request 'chore(deps): update module github.com/sirupsen/logrus to v1.9.3' (#2) from renovate/github.com-sirupsen-logrus-1.x into master
Reviewed-on: #2
2024-07-06 20:25:57 +00:00
04bc1855f0 Merge pull request 'chore(deps): update module github.com/fatih/color to v1.17.0' (#3) from renovate/github.com-fatih-color-1.x into master
Reviewed-on: #3
2024-07-06 20:25:49 +00:00
Renovate Bot
275eca953b chore(deps): update module github.com/fatih/color to v1.17.0 2024-07-06 13:18:50 -07:00
Renovate Bot
47da49d37a chore(deps): update module github.com/sirupsen/logrus to v1.9.3 2024-07-06 13:18:47 -07:00
edb8acb6dc Merge pull request 'chore: Configure Renovate' (#1) from renovate/configure into master
Reviewed-on: #1
2024-07-06 20:11:15 +00:00
Renovate Bot
99a1463cfc Add renovate.json 2024-07-06 13:09:31 -07:00
8cf145955c
docs: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-11 21:35:03 -08:00
62c1714958
updates
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-10 09:59:19 -08:00
49effe24b4
update urls 2023-03-09 23:48:25 -08:00
0f19f085ff
docs: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-08 15:16:52 -08:00
7fa2e67a89
docs: readme
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-08 15:16:39 -08:00
0a0c88f25d
feat: per script environmental variables
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-08 15:14:45 -08:00
4992e63edb
docs: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-08 13:47:07 -08:00
32d1074fd9
improved: aliases
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-08 13:46:44 -08:00
8f9ff1dada
docs: readme
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-08 13:22:04 -08:00
ec35355705
docs: readme
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-08 13:07:06 -08:00
40203011d5 fix: fix makefile
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-06 17:49:03 -08:00
2d893b57c2 doc: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-06 11:02:52 -08:00
4e854075df improved: make haunt run generate default config if none found
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-06 11:02:34 -08:00
0e454d971d ci: add site unpacking / packing to makefile, ignore assets directory
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-06 10:39:51 -08:00
764265ba04 docs: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-06 10:18:00 -08:00
8dd9007194 remove
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-06 10:17:39 -08:00
ee43417bb5 server redo 2023-03-06 10:17:28 -08:00
8805406e51 version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 23:58:07 -08:00
55e3fe4fca fix: remove debug
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 23:57:48 -08:00
d7c1169e39 docs: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 23:54:50 -08:00
b06ee1d6d7 fix: fix build/install
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 23:54:34 -08:00
f0c93b4fde docs: readme
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 23:52:48 -08:00
0279e65cee version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 23:48:13 -08:00
91a2115a18 version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 23:47:44 -08:00
a5adf4c85e docs: update docs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 23:47:02 -08:00
6e515d040a clean
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 23:39:01 -08:00
0c09e8f29b version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 23:28:31 -08:00
71990c4f7c clean
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 23:26:09 -08:00
712ef52265 improved: change default to install + run and change start -> run
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 23:15:54 -08:00
d6c1b9704c fix: remove binary
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 22:18:46 -08:00
25d270b0a0 ci: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 16:10:36 -08:00
5a743978d7 doc: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 16:01:10 -08:00
65b222db8d fix: make build not default
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 16:00:54 -08:00
5a2ae44e73 doc: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 14:41:21 -08:00
1bd099d9c6 fix git ignore
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 14:41:02 -08:00
b71c2ec517 fix: haunt start works
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 14:40:00 -08:00
42922c97b2 fix: defaults should not be /
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 14:28:09 -08:00
d7b13f16ca feat: start now accepts multiple arguments
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 13:15:04 -08:00
fa14ce5f2a improved: added tab completion for start command
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 13:00:44 -08:00
e131342e1f fix: error on one project should not terminate haunt
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-05 12:10:39 -08:00
30f1f56d7d doc: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 02:13:00 -08:00
1767d94341 improve: better prints and error handling
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-05 02:10:06 -08:00
8b0a7cb20c doc: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-04 23:51:24 -08:00
9c281f840b ci: fix github build
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-04 23:44:28 -08:00
dbf37249f4 ignore
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-04 23:42:44 -08:00
6f79719919 ci: build changes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-04 23:42:01 -08:00
c0e975e139 fix: error could cause infinite hang
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-04 23:28:07 -08:00
6c8b9d191c docs: version bump
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-04 22:40:29 -08:00
4b48056fdc docs: updated readme
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2023-03-04 22:29:39 -08:00
e8237d5458 improved: made watcher paths relative to the config file location
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-04 22:28:42 -08:00
27 changed files with 638 additions and 368 deletions

View File

@ -17,6 +17,10 @@ builds:
ignore:
- goos: windows
goarch: "386"
# Custom ldflags templates.
# Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser`.
ldflags:
- -s -w -X github.com/abs3ntdev/haunt/cmd.Version={{.Version}}
archives:
- format: tar.gz

5
.gitignore vendored
View File

@ -25,13 +25,14 @@ _testmain.go
*.exe
*.test
*.prof
/assets/
.glide
.idea
.DS_Store
.realize
/haunt/
/haunt
realize/assets/*
docker
vendor
bin

View File

@ -1,6 +1,6 @@
gitea_urls:
api: https://gitea.asdf.cafe/api/v1
download: https://gitea.asdf.cafe
api: https://git.asdf.cafe/api/v1
download: https://git.asdf.cafe
skip_tls_verify: false
before:
@ -17,6 +17,10 @@ builds:
ignore:
- goos: windows
goarch: "386"
# Custom ldflags templates.
# Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser`.
ldflags:
- -s -w -X github.com/abs3ntdev/haunt/cmd.Version={{.Version}}
archives:
- format: tar.gz

View File

@ -4,10 +4,9 @@ pipeline:
commands:
- go mod tidy
- go build -o bin/haunt
- mkdir completions
- ./bin/haunt completion zsh > completions/haunt_zsh
- ./bin/haunt completion bash > completions/haunt_bash
- ./bin/haunt completion fish > completions/haunt_fish
when:
event: push
publish:
image: goreleaser/goreleaser
commands:

View File

@ -5,6 +5,12 @@ ${pkgname}: $(shell find . -name '*.go')
mkdir -p bin
go build -o bin/${pkgname} .
decode:
go run ./hack/unpack/main.go
pack-site:
go-bindata -pkg haunt -o src/haunt/bindata.go -fs assets/...
completions:
mkdir -p completions
./bin/${pkgname} completion zsh > completions/_${pkgname}
@ -18,17 +24,18 @@ tidy:
go mod tidy
clean:
rm -f bin
rm -rf bin
rm -rf completions
rm -rf assets
uninstall:
rm -f /usr/bin/${pkgname}
rm -f /usr/local/bin/${pkgname}
rm -f /usr/share/zsh/site-functions/_${pkgname}
rm -f /usr/share/bash-completion/completions/${pkgname}
rm -f /usr/share/fish/vendor_completions.d/${pkgname}.fish
install:
cp bin/${pkgname} /usr/bin
cp bin/${pkgname} /usr/local/bin
bin/${pkgname} completion zsh > /usr/share/zsh/site-functions/_${pkgname}
bin/${pkgname} completion bash > /usr/share/bash-completion/completions/${pkgname}
bin/${pkgname} completion fish > /usr/share/fish/vendor_completions.d/${pkgname}.fish

104
README.md
View File

@ -1,4 +1,5 @@
## Quickstart
### Install
```
go install github.com/abs3ntdev/haunt@latest
```
@ -11,58 +12,59 @@ cd haunt
make build && sudo make install
```
#### aur
```
yay -S haunt-go-git
```
### Completions
completions will be automatically installed if you used the Makefile, if you did not you can generate completions with `haunt completion [bash/fish/powershell/zsh]`
for example: `haunt completion zsh > _haunt`
you can also source the output of the completion command directly in your .zshrc with:\
`source <(haunt completion zsh) && compdef _haunt haunt`
## Commands List
### Run Command
From **project/projects** root execute:
```
haunt init
```
then
```
haunt start
```
haunt init will add your root directory if it contains a main.go file and will add any directory inside of cmd as projects. If you wish to add additional projects run haunt add [name] or edit the config file manually. By default projects are set to run go install and go run [project]
***start*** command supports the following custom parameters:
--name="name" -> Run by name on existing configuration
--path="haunt/server" -> Custom Path (if not specified takes the working directory name)
--generate -> Enable go generate
--fmt -> Enable go fmt
--test -> Enable go test
--vet -> Enable go vet
--install -> Enable go install
--build -> Enable go build
--run -> Enable go run
--server -> Enable the web server
--open -> Open web ui in default browser
--no-config -> Ignore an existing config / skip the creation of a new one
Some examples:
haunt start
haunt start --path="mypath"
haunt start --name="haunt" --build
haunt start --path="haunt" --run --no-config
haunt start --install --test --fmt --no-config
haunt start --path="/Users/username/go/src/github.com/oxequa/haunt-examples/coin/"
### Init Command
This command will generate a .haunt.yaml with sane default for your current project/projects.\
If there is a main.go in the root directory it will be added along with any directories inside the relative path `cmd`
haunt init
### Add Command
Add a project to an existing config file or create a new one.
haunt add [name]
### Run Command
```
haunt run
```
the run command allows for specifying projects by name, all provided will be ran according to the config file:
Some examples:
haunt run
haunt run server api
### Add Command
Add a project, the same defaults init uses will be used for new projects unless flags are provided.
haunt add [name] [--flags]
Possible flags are:
-b, --build Enable go build
-f, --fmt Enable go fmt
-g, --generate Enable go generate
-h, --help help for add
-i, --install Enable go install (default true)
-p, --path string Project base path (default "./")
-r, --run Enable go run (default true)
-t, --test Enable go test
-v, --vet Enable go vet
### Remove Command
Remove a project by its name
@ -88,7 +90,7 @@ Remove a project by its name
schema:
- name: coin
path: cmd/coin // project path
env: // env variables available at startup
env: // env variables for run
test: test
myvar: value
commands: // go commands supported
@ -116,8 +118,9 @@ Remove a project by its name
args: // arguments to pass at the project
- --myarg
watcher:
paths: // watched paths
- /
paths: // watched paths are relative to directory you run haunt in
- src
- cmd/coin
ignore_paths: // ignored paths
- vendor
extensions: // watched extensions
@ -128,14 +131,21 @@ Remove a project by its name
command: echo before global
global: true
output: true
env: // env variables per script
test: test
myvar: value
- type: before
command: echo before change
output: true
env: // env variables per script
test: othertest
myvar: othervalue
- type: after
command: echo after change
output: true
env: // env variables per script
key: 1
- type: after
command: echo after global
global: true
output: true
errorOutputPattern: mypattern //custom error pattern

View File

@ -14,6 +14,7 @@ var addConfig config.Flags
var addCmd = &cobra.Command{
Use: "add",
Aliases: []string{"a", "create", "new"},
Short: "Adds a project by name",
Long: "Adds a project by name, if path is provided it will use 'cmd/name', all flags provided will be saved in the config file. By default go install and go run will be ran",
Args: cobra.MatchAll(cobra.ExactArgs(1)),
@ -27,8 +28,8 @@ var addCmd = &cobra.Command{
}
func getPotentialProjets(in string) []string {
r := haunt.NewHaunt()
err := r.Settings.Read(&r)
h := haunt.NewHaunt()
err := h.Settings.Read(&h)
if err != nil {
return []string{}
}
@ -39,7 +40,7 @@ func getPotentialProjets(in string) []string {
}
for _, dir := range cmdDir {
exists := false
for _, proj := range r.Projects {
for _, proj := range h.Projects {
if dir.Name() == proj.Name {
exists = true
continue
@ -60,7 +61,7 @@ func getPotentialProjets(in string) []string {
func init() {
rootCmd.AddCommand(addCmd)
addCmd.Flags().StringVarP(&addConfig.Path, "path", "p", "", "Project base path")
addCmd.Flags().StringVarP(&addConfig.Path, "path", "p", "./", "Project base path")
addCmd.Flags().BoolVarP(&addConfig.Format, "fmt", "f", false, "Enable go fmt")
addCmd.Flags().BoolVarP(&addConfig.Vet, "vet", "v", false, "Enable go vet")
addCmd.Flags().BoolVarP(&addConfig.Test, "test", "t", false, "Enable go test")
@ -73,27 +74,24 @@ func init() {
// Add a project to an existing config or create a new one
func add(cmd *cobra.Command, args []string) (err error) {
addConfig.Name = args[0]
r := haunt.NewHaunt()
h := haunt.NewHaunt()
// read a config if exist
err = r.Settings.Read(&r)
err = h.Settings.Read(&h)
if err != nil {
return err
}
if addConfig.Path == "" {
addConfig.Path = "cmd/" + addConfig.Name
}
projects := len(r.Schema.Projects)
projects := len(h.Projects)
// create and add a new project
r.Schema.Add(r.Schema.New(addConfig))
if len(r.Schema.Projects) > projects {
h.Add(h.New(addConfig))
if len(h.Projects) > projects {
// update config
err = r.Settings.Write(r)
err = h.Settings.Write(h)
if err != nil {
return err
}
log.Println(r.Prefix(haunt.Green.Bold("project successfully added")))
log.Println(h.Prefix(haunt.Green.Bold("project successfully added")))
} else {
log.Println(r.Prefix(haunt.Green.Bold("project can't be added")))
log.Println(h.Prefix(haunt.Green.Bold("project can't be added")))
}
return nil
}

View File

@ -10,6 +10,7 @@ import (
// cleanCmd represents the clean command
var cleanCmd = &cobra.Command{
Use: "clean",
Aliases: []string{"c"},
Short: "Deletes the haunt config file",
RunE: clean,
}
@ -20,10 +21,10 @@ func init() {
// Clean remove haunt file
func clean(cmd *cobra.Command, args []string) (err error) {
r := haunt.NewHaunt()
if err := r.Settings.Remove(haunt.RFile); err != nil {
h := haunt.NewHaunt()
if err := h.Settings.Remove(haunt.HFile); err != nil {
return err
}
log.Println(r.Prefix(haunt.Green.Bold("folder successfully removed")))
log.Println(h.Prefix(haunt.Green.Bold("config file removed successfully removed")))
return nil
}

View File

@ -13,6 +13,7 @@ import (
// initCmd represents the init command
var initCmd = &cobra.Command{
Use: "init",
Aliases: []string{"i"},
Short: "Generates a haunt config file using sane defaults",
Long: "Generates a haunt config file using sane defaults, haunt will look for a main.go file and any directories inside the relative path 'cmd' and add them all as projects",
RunE: defaultConfig,
@ -23,10 +24,10 @@ func init() {
}
func defaultConfig(cmd *cobra.Command, args []string) error {
r := haunt.NewHaunt()
h := haunt.NewHaunt()
write := true
if _, err := os.Stat(haunt.RFile); err == nil {
fmt.Print(r.Prefix("Config file exists. Overwire? " + haunt.Magenta.Bold("[y/n] ") + haunt.Green.Bold("(n) ")))
if _, err := os.Stat(haunt.HFile); err == nil {
fmt.Print(h.Prefix("Config file exists. Overwire? " + haunt.Magenta.Bold("[y/n] ") + haunt.Green.Bold("(n) ")))
var overwrite string
fmt.Scanf("%s", &overwrite)
write = false
@ -36,15 +37,15 @@ func defaultConfig(cmd *cobra.Command, args []string) error {
}
}
if write {
r.SetDefaults()
err := r.Settings.Write(r)
h.SetDefaults()
err := h.Settings.Write(h)
if err != nil {
return err
}
log.Println(r.Prefix(
log.Println(h.Prefix(
"Config file has successfully been saved at .haunt.yaml",
))
log.Println(r.Prefix(
log.Println(h.Prefix(
"Run haunt add --help to see how to add more projects",
))
return nil

View File

@ -10,6 +10,7 @@ import (
var removeCmd = &cobra.Command{
Use: "remove [names]",
Aliases: []string{"delete", "r"},
Short: "Removes all projects by name from config file",
Args: cobra.MatchAll(cobra.MinimumNArgs(1), cobra.OnlyValidArgs),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
@ -23,14 +24,14 @@ func init() {
}
func getProjectNames(input string) []string {
r := haunt.NewHaunt()
h := haunt.NewHaunt()
// read a config if exist
err := r.Settings.Read(&r)
err := h.Settings.Read(&h)
if err != nil {
return []string{}
}
names := []string{}
for _, project := range r.Schema.Projects {
for _, project := range h.Projects {
if strings.HasPrefix(project.Name, input) {
names = append(names, project.Name)
}
@ -40,22 +41,22 @@ func getProjectNames(input string) []string {
// Remove a project from an existing config
func remove(cmd *cobra.Command, args []string) (err error) {
r := haunt.NewHaunt()
h := haunt.NewHaunt()
// read a config if exist
err = r.Settings.Read(&r)
err = h.Settings.Read(&h)
if err != nil {
return err
}
for _, arg := range args {
err := r.Schema.Remove(arg)
err = h.Remove(arg)
if err != nil {
log.Println(r.Prefix(haunt.Red.Bold(arg + " project not found")))
log.Println(h.Prefix(haunt.Red.Bold(arg + " project not found")))
continue
}
log.Println(r.Prefix(haunt.Green.Bold(arg + " successfully removed")))
log.Println(h.Prefix(haunt.Green.Bold(arg + " successfully removed")))
}
// update config
err = r.Settings.Write(r)
err = h.Settings.Write(h)
if err != nil {
return err
}

View File

@ -1,9 +1,7 @@
package cmd
import (
"fmt"
"os"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@ -21,8 +19,8 @@ var rootCmd = &cobra.Command{
func Execute() {
err := rootCmd.Execute()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
logrus.Error(err)
return
}
}

95
cmd/run.go Normal file
View File

@ -0,0 +1,95 @@
package cmd
import (
"fmt"
"log"
"os"
"strings"
"github.com/abs3ntdev/haunt/src/haunt"
"github.com/spf13/cobra"
)
var startCmd = &cobra.Command{
Use: "start",
Aliases: []string{"s", "run"},
Short: "run haunt, optionally provide the name of projects to only run those otherwise will run all configured projects",
Args: cobra.MatchAll(cobra.OnlyValidArgs),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return getProjectNamesToRun(toComplete), cobra.ShellCompDirectiveNoFileComp
},
RunE: start,
}
func init() {
rootCmd.AddCommand(startCmd)
}
func getProjectNamesToRun(input string) []string {
h := haunt.NewHaunt()
// read a config if exist
err := h.Settings.Read(&h)
if err != nil {
return []string{}
}
names := []string{}
for _, project := range h.Projects {
if strings.HasPrefix(project.Name, input) {
names = append(names, project.Name)
}
}
return names
}
// haunt workflow
func start(cmd *cobra.Command, args []string) (err error) {
h := haunt.NewHaunt()
// read a config if exist
err = h.Settings.Read(&h)
if err != nil {
if os.IsNotExist(err) {
log.Println(h.Prefix("No config file found, initializing one for you"))
err = defaultConfig(cmd, args)
if err != nil {
log.Println(h.Prefix("Failed to generate default config: " + err.Error()))
}
err = h.Settings.Read(&h)
if err != nil {
return fmt.Errorf(h.Prefix("Failed to read config file: " + err.Error()))
}
} else {
return err
}
}
if len(args) >= 1 {
// filter by name flag if exist
h.Projects = h.Filter(args)
if len(h.Projects) == 0 {
log.Println(h.Prefix("No valid project found, exiting. Check your config file or run haunt add"))
return
}
}
// increase file limit
if h.Settings.FileLimit != 0 {
if err = h.Settings.Flimit(); err != nil {
return err
}
}
// web server
if h.Server.Status {
h.Server.Parent = h
err = h.Server.Start()
if err != nil {
return err
}
err = h.Server.OpenURL()
if err != nil {
return err
}
}
// start workflow
return h.Start()
}

View File

@ -1,93 +0,0 @@
package cmd
import (
"github.com/abs3ntdev/haunt/src/config"
"github.com/abs3ntdev/haunt/src/haunt"
"github.com/spf13/cobra"
)
var startConfig config.Flags
var startCmd = &cobra.Command{
Use: "start",
Short: "Start haunt on a given path, generates a config file if one does not already exist",
RunE: start,
}
func init() {
rootCmd.AddCommand(startCmd)
startCmd.Flags().StringVarP(&startConfig.Path, "path", "p", "", "Project base path")
startCmd.Flags().StringVarP(&startConfig.Name, "name", "n", "", "Run a project by its name")
startCmd.Flags().BoolVarP(&startConfig.Format, "fmt", "f", false, "Enable go fmt")
startCmd.Flags().BoolVarP(&startConfig.Vet, "vet", "v", false, "Enable go vet")
startCmd.Flags().BoolVarP(&startConfig.Test, "test", "t", false, "Enable go test")
startCmd.Flags().BoolVarP(&startConfig.Generate, "generate", "g", false, "Enable go generate")
startCmd.Flags().BoolVarP(&startConfig.Server, "server", "s", false, "Start server")
startCmd.Flags().BoolVarP(&startConfig.Open, "open", "o", false, "Open into the default browser")
startCmd.Flags().BoolVarP(&startConfig.Install, "install", "i", false, "Enable go install")
startCmd.Flags().BoolVarP(&startConfig.Build, "build", "b", false, "Enable go build")
startCmd.Flags().BoolVarP(&startConfig.Run, "run", "r", false, "Enable go run")
startCmd.Flags().BoolVarP(&startConfig.Legacy, "legacy", "l", false, "Legacy watch by polling instead fsnotify")
startCmd.Flags().BoolVarP(&startConfig.NoConfig, "no-config", "c", false, "Ignore existing config and doesn't create a new one")
}
// Start haunt workflow
func start(cmd *cobra.Command, args []string) (err error) {
r := haunt.NewHaunt()
// set legacy watcher
if startConfig.Legacy {
r.Settings.Legacy.Set(startConfig.Legacy, 1)
}
// set server
if startConfig.Server {
r.Server.Set(startConfig.Server, startConfig.Open, haunt.Port, haunt.Host)
}
// check no-config and read
if !startConfig.NoConfig {
// read a config if exist
err := r.Settings.Read(&r)
if err != nil {
return err
}
if startConfig.Name != "" {
// filter by name flag if exist
r.Schema.Projects = r.Schema.Filter("Name", startConfig.Name)
}
// increase file limit
if r.Settings.FileLimit != 0 {
if err = r.Settings.Flimit(); err != nil {
return err
}
}
}
// check project list length
if len(r.Schema.Projects) == 0 {
// create a new project based on given params
project := r.Schema.New(startConfig)
// Add to projects list
r.Schema.Add(project)
// save config
if !startConfig.NoConfig {
err = r.Settings.Write(r)
if err != nil {
return err
}
}
}
// Start web server
if r.Server.Status {
r.Server.Parent = r
err = r.Server.Start()
if err != nil {
return err
}
err = r.Server.OpenURL()
if err != nil {
return err
}
}
// start workflow
return r.Start()
}

View File

@ -7,16 +7,12 @@ import (
"github.com/spf13/cobra"
)
var Version = "v0.2.11"
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Short: "Prints current verison",
Run: version,
}
@ -36,6 +32,6 @@ func init() {
// Version print current version
func version(cmd *cobra.Command, args []string) {
r := haunt.NewHaunt()
log.Println(r.Prefix(haunt.Green.Bold(haunt.RVersion)))
h := haunt.NewHaunt()
log.Println(h.Prefix(haunt.Green.Bold(Version)))
}

19
go.mod
View File

@ -3,13 +3,14 @@ module github.com/abs3ntdev/haunt
go 1.19
require (
github.com/fatih/color v1.14.1
github.com/fsnotify/fsnotify v1.6.0
github.com/fatih/color v1.17.0
github.com/fsnotify/fsnotify v1.7.0
github.com/labstack/echo v3.3.10+incompatible
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
golang.org/x/net v0.7.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
golang.org/x/net v0.27.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
)
require (
@ -17,12 +18,12 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
)

22
go.sum
View File

@ -1,4 +1,5 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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=
@ -6,8 +7,12 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@ -22,13 +27,19 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -46,8 +57,12 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -56,8 +71,15 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
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=

22
hack/unpack/main.go Normal file
View File

@ -0,0 +1,22 @@
package main
import (
"fmt"
"github.com/abs3ntdev/haunt/src/haunt"
)
func main() {
names := haunt.AssetNames()
fmt.Println("Assets in bindata:")
fmt.Println(names)
wd := "assets"
for i, v := range names {
fmt.Printf("Restoring asset [%v] [%s]\n", i, v)
err := haunt.RestoreAsset(wd, v)
if err != nil {
fmt.Println("Failed to restore", v)
}
}
}

View File

@ -3,5 +3,14 @@ package main
import "github.com/abs3ntdev/haunt/cmd"
func main() {
// names := haunt.AssetNames()
// fmt.Println("Assets in bindata:")
// fmt.Println(names)
//
// wd := "www"
// for i, v := range names {
// fmt.Printf("Restoring asset [%v] [%s]\n", i, v)
// haunt.RestoreAsset(wd, v)
// }
cmd.Execute()
}

3
renovate.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": ["config:recommended"]
}

File diff suppressed because one or more lines are too long

View File

@ -16,16 +16,14 @@ import (
)
var (
// RPrefix tool name
RPrefix = "haunt"
// RVersion current version
RVersion = "v0.1.0"
// RExt file extension
RExt = ".yaml"
// RFile config file name
RFile = "." + RPrefix + RExt
// RExtWin windows extension
RExtWin = ".exe"
// HPrefix tool name
HPrefix = "haunt"
// HExt file extension
HExt = ".yaml"
// HFile config file name
HFile = "." + HPrefix + HExt
// HExtWin windows extension
HExtWin = ".exe"
)
type (
@ -78,17 +76,17 @@ func init() {
}
}
func (r *Haunt) SetDefaults() {
r.Server = Server{Parent: r, Status: true, Open: false, Port: Port}
r.Settings.FileLimit = 0
r.Settings.Legacy.Interval = 100 * time.Millisecond
r.Settings.Legacy.Force = false
r.Settings.Files.Errors = Resource{Name: FileErr, Status: false}
r.Settings.Files.Errors = Resource{Name: FileOut, Status: false}
r.Settings.Files.Errors = Resource{Name: FileLog, Status: false}
func (h *Haunt) SetDefaults() {
h.Server = Server{Parent: h, Status: true, Open: false, Port: Port}
h.Settings.FileLimit = 0
h.Settings.Legacy.Interval = 100 * time.Millisecond
h.Settings.Legacy.Force = false
h.Settings.Errors = Resource{Name: FileErr, Status: false}
h.Settings.Errors = Resource{Name: FileOut, Status: false}
h.Settings.Errors = Resource{Name: FileLog, Status: false}
if _, err := os.Stat("main.go"); err == nil {
log.Println(r.Prefix(Green.Bold("Adding: " + filepath.Base(Wdir()))))
r.Schema.Projects = append(r.Schema.Projects, Project{
log.Println(h.Prefix(Green.Bold("Adding: " + filepath.Base(Wdir()))))
h.Projects = append(h.Projects, Project{
Name: filepath.Base(Wdir()),
Path: Wdir(),
Tools: Tools{
@ -101,21 +99,21 @@ func (r *Haunt) SetDefaults() {
},
Watcher: Watch{
Exts: []string{"go"},
Paths: []string{"/"},
Paths: []string{"./"},
},
})
} else {
log.Println(r.Prefix(Magenta.Bold("Skipping: " + filepath.Base(Wdir()) + " no main.go file in root")))
log.Println(h.Prefix(Magenta.Bold("Skipping: " + filepath.Base(Wdir()) + " no main.go file in root")))
}
subDirs, err := os.ReadDir("cmd")
if err != nil {
log.Println(r.Prefix("cmd directory not found, skipping"))
log.Println(h.Prefix("cmd directory not found, skipping"))
return
}
for _, dir := range subDirs {
if dir.IsDir() {
log.Println(r.Prefix(Green.Bold("Adding: " + dir.Name())))
r.Schema.Projects = append(r.Schema.Projects, Project{
log.Println(h.Prefix(Green.Bold("Adding: " + dir.Name())))
h.Projects = append(h.Projects, Project{
Name: dir.Name(),
Path: "cmd/" + dir.Name(),
Tools: Tools{
@ -128,35 +126,35 @@ func (r *Haunt) SetDefaults() {
},
Watcher: Watch{
Exts: []string{"go"},
Paths: []string{"/"},
Paths: []string{"cmd/" + dir.Name()},
},
})
} else {
log.Println(r.Prefix(Magenta.Bold("Skipping: " + dir.Name() + " not a directory")))
log.Println(h.Prefix(Magenta.Bold("Skipping: " + dir.Name() + " not a directory")))
}
}
}
// Stop haunt workflow
func (r *Haunt) Stop() error {
for k := range r.Schema.Projects {
if r.Schema.Projects[k].exit != nil {
close(r.Schema.Projects[k].exit)
func (h *Haunt) Stop() error {
for k := range h.Projects {
if h.Schema.Projects[k].exit != nil {
close(h.Schema.Projects[k].exit)
}
}
return nil
}
// Start haunt workflow
func (r *Haunt) Start() error {
if len(r.Schema.Projects) > 0 {
func (h *Haunt) Start() error {
if len(h.Projects) > 0 {
var wg sync.WaitGroup
wg.Add(len(r.Schema.Projects))
for k := range r.Schema.Projects {
r.Schema.Projects[k].exit = make(chan os.Signal, 1)
signal.Notify(r.Schema.Projects[k].exit, os.Interrupt)
r.Schema.Projects[k].parent = r
go r.Schema.Projects[k].Watch(&wg)
wg.Add(len(h.Projects))
for k := range h.Projects {
h.Schema.Projects[k].exit = make(chan os.Signal, 1)
signal.Notify(h.Schema.Projects[k].exit, os.Interrupt)
h.Schema.Projects[k].parent = h
go h.Schema.Projects[k].Watch(&wg)
}
wg.Wait()
} else {
@ -166,9 +164,9 @@ func (r *Haunt) Start() error {
}
// Prefix a given string with tool name
func (r *Haunt) Prefix(input string) string {
func (h *Haunt) Prefix(input string) string {
if len(input) > 0 {
return fmt.Sprint(Yellow.Bold("["), strings.ToUpper(RPrefix), Yellow.Bold("]"), ": ", input)
return fmt.Sprint(Yellow.Bold("["), strings.ToUpper(HPrefix), Yellow.Bold("]"), ": ", input)
}
return input
}

View File

@ -7,6 +7,7 @@ package haunt
import (
"errors"
"fmt"
"log"
"os"
"sync"
"time"
@ -115,7 +116,10 @@ func (w *filePoller) Close() error {
w.closed = true
for name := range w.watches {
w.remove(name)
err := w.remove(name)
if err != nil {
log.Println(log.Prefix(), err.Error())
}
delete(w.watches, name)
}
w.mu.Unlock()
@ -196,7 +200,12 @@ func (w *filePoller) Walk(path string, init bool) string {
if check == nil && init {
_, err := os.Stat(path)
if err == nil {
go w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: path}, w.watches[path])
go func() {
err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: path}, w.watches[path])
if err != nil {
log.Println(log.Prefix(), err.Error())
}
}()
}
}
return path
@ -241,13 +250,16 @@ func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{}
// If it doesn't exist at this point, it must have been removed
// no need to send the error here since this is a valid operation
if os.IsNotExist(err) {
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Remove, Name: f.Name()}, chClose); err != nil {
if err = w.sendEvent(fsnotify.Event{Op: fsnotify.Remove, Name: f.Name()}, chClose); err != nil {
return
}
lastFi = nil
}
// at this point, send the error
w.sendErr(err, chClose)
err = w.sendErr(err, chClose)
if err != nil {
log.Println(log.Prefix(), err.Error())
}
return
case lastFi == nil:
if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: f.Name()}, chClose); err != nil {

View File

@ -43,6 +43,7 @@ type Ignore struct {
type Command struct {
Cmd string `yaml:"command" json:"command"`
Type string `yaml:"type" json:"type"`
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
Path string `yaml:"path,omitempty" json:"path,omitempty"`
Global bool `yaml:"global,omitempty" json:"global,omitempty"`
Output bool `yaml:"output,omitempty" json:"output,omitempty"`
@ -115,19 +116,21 @@ func (p *Project) Before() {
}
// setup go tools
p.Tools.Setup()
p.Tools.Setup(p)
// global commands before
p.cmd(p.stop, "before", true)
// indexing files and dirs
for _, dir := range p.Watcher.Paths {
base, _ := filepath.Abs(p.Path)
base = filepath.Join(base, dir)
base, _ := filepath.Abs(dir)
if _, err := os.Stat(base); err == nil {
if err := filepath.Walk(base, p.walk); err != nil {
p.Err(err)
}
}
}
// start message
msg = fmt.Sprintln(p.pname(p.Name, 1), ":", Blue.Bold("Watching"), Magenta.Bold(p.files), "file/s", Magenta.Bold(p.folders), "folder/s")
out = BufferOut{Time: time.Now(), Text: "Watching " + strconv.FormatInt(p.files, 10) + " files/s " + strconv.FormatInt(p.folders, 10) + " folder/s"}
@ -140,6 +143,7 @@ func (p *Project) Err(err error) {
p.parent.Err(Context{Project: p})
return
}
if err != nil {
msg = fmt.Sprintln(p.pname(p.Name, 2), ":", Red.Regular(err.Error()))
out = BufferOut{Time: time.Now(), Text: err.Error()}
@ -170,6 +174,7 @@ func (p *Project) Reload(path string, stop <-chan bool) {
p.parent.Reload(Context{Project: p, Watcher: p.watcher, Path: path, Stop: stop})
return
}
var done bool
var install, build Response
go func() {
@ -179,14 +184,17 @@ func (p *Project) Reload(path string, stop <-chan bool) {
return
}
}()
if done {
return
}
// before command
p.cmd(stop, "before", false)
if done {
return
}
// Go supported tools
if len(path) > 0 {
fi, err := os.Stat(path)
@ -198,6 +206,7 @@ func (p *Project) Reload(path string, stop <-chan bool) {
}
p.tools(stop, path, fi)
}
// Prevent fake events on polling startup
p.init = true
// prevent errors using haunt without config with only run flag
@ -259,6 +268,13 @@ func (p *Project) Reload(path string, stop <-chan bool) {
p.stamp("error", out, msg, "")
}
}()
} else {
if install.Err != nil {
log.Println(p.parent.Prefix("Install failed for: " + p.Name))
}
if build.Err != nil {
log.Println(p.parent.Prefix("Build failed for: " + p.Name))
}
}
if done {
return
@ -271,19 +287,24 @@ func (p *Project) Watch(wg *sync.WaitGroup) {
var err error
// change channel
p.stop = make(chan bool)
// init a new watcher
p.watcher, err = NewFileWatcher(p.parent.Settings.Legacy)
if err != nil {
log.Fatal(err)
}
defer func() {
close(p.stop)
p.watcher.Close()
}()
// before start checks
p.Before()
// start watcher
go p.Reload("", p.stop)
L:
for {
select {
@ -296,7 +317,10 @@ L:
switch event.Op {
case fsnotify.Chmod:
case fsnotify.Remove:
p.watcher.Remove(event.Name)
err := p.watcher.Remove(event.Name)
if err != nil {
log.Println(p.parent.Prefix("Failed to remove watcher: " + err.Error()))
}
if p.Validate(event.Name, false) && ext(event.Name) != "" {
// stop and restart
close(p.stop)
@ -311,7 +335,10 @@ L:
continue
}
if fi.IsDir() {
filepath.Walk(event.Name, p.walk)
err := filepath.Walk(event.Name, p.walk)
if err != nil {
log.Println(p.parent.Prefix("Failed to walk directory: " + err.Error()))
}
} else {
// stop and restart
close(p.stop)
@ -516,7 +543,7 @@ func (p *Project) stamp(t string, o BufferOut, msg string, stream string) {
case "out":
p.Buffer.StdOut = append(p.Buffer.StdOut, o)
if p.parent.Settings.Files.Outputs.Status {
f := p.parent.Settings.Create(p.Path, p.parent.Settings.Files.Outputs.Name)
f := p.parent.Settings.Create(p.Path, p.parent.Settings.Outputs.Name)
if _, err := f.WriteString(strings.Join(content, " ")); err != nil {
p.parent.Settings.Fatal(err, "")
}
@ -524,7 +551,7 @@ func (p *Project) stamp(t string, o BufferOut, msg string, stream string) {
case "log":
p.Buffer.StdLog = append(p.Buffer.StdLog, o)
if p.parent.Settings.Files.Logs.Status {
f := p.parent.Settings.Create(p.Path, p.parent.Settings.Files.Logs.Name)
f := p.parent.Settings.Create(p.Path, p.parent.Settings.Logs.Name)
if _, err := f.WriteString(strings.Join(content, " ")); err != nil {
p.parent.Settings.Fatal(err, "")
}
@ -532,7 +559,7 @@ func (p *Project) stamp(t string, o BufferOut, msg string, stream string) {
case "error":
p.Buffer.StdErr = append(p.Buffer.StdErr, o)
if p.parent.Settings.Files.Errors.Status {
f := p.parent.Settings.Create(p.Path, p.parent.Settings.Files.Errors.Name)
f := p.parent.Settings.Create(p.Path, p.parent.Settings.Errors.Name)
if _, err := f.WriteString(strings.Join(content, " ")); err != nil {
p.parent.Settings.Fatal(err, "")
}
@ -551,7 +578,14 @@ func (p *Project) stamp(t string, o BufferOut, msg string, stream string) {
func (p Project) buildEnvs() (envs []string) {
for k, v := range p.Env {
envs = append(envs, fmt.Sprintf("%s=%s", strings.Replace(k, "=", "", -1), v))
envs = append(envs, fmt.Sprintf("%s=%s", strings.ReplaceAll(k, "=", ""), v))
}
return
}
func (c Command) buildEnvs() (envs []string) {
for k, v := range c.Env {
envs = append(envs, fmt.Sprintf("%s=%s", strings.ReplaceAll(k, "=", ""), v))
}
return
}
@ -565,8 +599,14 @@ func (p *Project) run(path string, stream chan Response, stop <-chan bool) (err
// https://github.com/golang/go/issues/5615
// https://github.com/golang/go/issues/6720
if build != nil {
build.Process.Signal(os.Interrupt)
build.Process.Wait()
err = build.Process.Signal(os.Interrupt)
if err != nil {
log.Println(p.parent.Prefix("Failed to send interrupt: " + err.Error()))
}
_, err = build.Process.Wait()
if err != nil {
log.Println(p.parent.Prefix("Error: " + err.Error()))
}
}
}()
@ -603,15 +643,15 @@ func (p *Project) run(path string, stream chan Response, stop <-chan bool) (err
if p.Tools.Run.Method != "" {
path = p.Tools.Run.Method
}
if _, err := os.Stat(path); err == nil {
if _, err = os.Stat(path); err == nil {
build = exec.Command(path, args...)
} else if _, err := os.Stat(path + RExtWin); err == nil {
build = exec.Command(path+RExtWin, args...)
} else if _, err = os.Stat(path + HExtWin); err == nil {
build = exec.Command(path+HExtWin, args...)
} else {
if _, err = os.Stat(path); err == nil {
build = exec.Command(path, args...)
} else if _, err = os.Stat(path + RExtWin); err == nil {
build = exec.Command(path+RExtWin, args...)
} else if _, err = os.Stat(path + HExtWin); err == nil {
build = exec.Command(path+HExtWin, args...)
} else {
return errors.New("project not found")
}
@ -683,6 +723,10 @@ func (c *Command) exec(base string, stop <-chan bool) (response Response) {
done := make(chan error)
args := strings.Split(strings.Replace(strings.Replace(c.Cmd, "'", "", -1), "\"", "", -1), " ")
ex := exec.Command(args[0], args[1:]...)
appendEnvs := c.buildEnvs()
if len(appendEnvs) > 0 {
ex.Env = append(ex.Env, appendEnvs...)
}
ex.Dir = base
// make cmd path
if c.Path != "" {
@ -695,13 +739,19 @@ func (c *Command) exec(base string, stop <-chan bool) (response Response) {
ex.Stdout = &stdout
ex.Stderr = &stderr
// Start command
ex.Start()
err := ex.Start()
if err != nil {
log.Println(log.Prefix(), err.Error())
}
go func() { done <- ex.Wait() }()
// Wait a result
select {
case <-stop:
// Stop running command
ex.Process.Kill()
err := ex.Process.Kill()
if err != nil {
log.Println(log.Prefix(), err.Error())
}
case err := <-done:
// Command completed
response.Name = c.Cmd

View File

@ -2,6 +2,8 @@ package haunt
import (
"errors"
"fmt"
"path/filepath"
"reflect"
"github.com/abs3ntdev/haunt/src/config"
@ -35,6 +37,13 @@ func (s *Schema) Remove(name string) error {
// New create a project using cli fields
func (s *Schema) New(flags config.Flags) Project {
if flags.Name == "" {
flags.Name = filepath.Base(Wdir())
}
if flags.Path == "" {
flags.Path = Wdir()
}
fmt.Println(flags.Name)
project := Project{
Name: flags.Name,
Path: flags.Path,
@ -62,7 +71,7 @@ func (s *Schema) New(flags config.Flags) Project {
},
},
Watcher: Watch{
Paths: []string{"/"},
Paths: []string{"./"},
Ignore: []string{".git", ".haunt", "vendor"},
Exts: []string{"go"},
},
@ -70,18 +79,15 @@ func (s *Schema) New(flags config.Flags) Project {
return project
}
// Filter project list by field
func (s *Schema) Filter(field string, value interface{}) []Project {
// Filter project list by names
func (s *Schema) Filter(names []string) []Project {
result := []Project{}
for _, item := range s.Projects {
v := reflect.ValueOf(item)
for i := 0; i < v.NumField(); i++ {
if v.Type().Field(i).Name == field {
if reflect.DeepEqual(v.Field(i).Interface(), value) {
for _, name := range names {
if item.Name == name {
result = append(result, item)
}
}
}
}
return result
}

View File

@ -57,7 +57,9 @@ func (s *Server) projects(c echo.Context) (err error) {
} else {
err := json.Unmarshal([]byte(text), &s.Parent)
if err == nil {
s.Parent.Settings.Write(s.Parent)
if err := s.Parent.Settings.Write(s.Parent); err != nil {
log.Println(s.Parent.Prefix("Failed to write: " + err.Error()))
}
break
}
}
@ -88,7 +90,10 @@ func (s *Server) render(c echo.Context, path string, mime int) error {
rs.Header().Set(echo.HeaderContentType, "image/png")
}
rs.WriteHeader(http.StatusOK)
rs.Write(data)
_, err = rs.Write(data)
if err != nil {
return err
}
return nil
}
@ -151,8 +156,11 @@ func (s *Server) Start() (err error) {
e.HideBanner = true
e.Debug = false
go func() {
err := e.Start(string(s.Host) + ":" + strconv.Itoa(s.Port))
if err != nil {
log.Println(s.Parent.Prefix("Failed to start on " + s.Host + ":" + strconv.Itoa(s.Port)))
}
log.Println(s.Parent.Prefix("Started on " + string(s.Host) + ":" + strconv.Itoa(s.Port)))
e.Start(string(s.Host) + ":" + strconv.Itoa(s.Port))
}()
}
return nil

View File

@ -1,7 +1,6 @@
package haunt
import (
"io/ioutil"
"log"
"os"
"path/filepath"
@ -14,9 +13,9 @@ import (
const (
Permission = 0o775
File = ".haunt.yaml"
FileOut = ".r.outputs.log"
FileErr = ".r.errors.log"
FileLog = ".r.logs.log"
FileOut = ".h.outputs.log"
FileErr = ".h.errors.log"
FileLog = ".h.logs.log"
)
// Settings defines a group of general settings and options
@ -72,10 +71,10 @@ func (s *Settings) Remove(d string) error {
// Read config file
func (s *Settings) Read(out interface{}) error {
// backward compatibility
if _, err := os.Stat(RFile); err != nil {
if _, err := os.Stat(HFile); err != nil {
return err
}
content, err := s.Stream(RFile)
content, err := s.Stream(HFile)
if err == nil {
err = yaml.Unmarshal(content, out)
return err
@ -89,7 +88,7 @@ func (s *Settings) Write(out interface{}) error {
if err != nil {
return err
}
s.Fatal(ioutil.WriteFile(RFile, y, Permission))
s.Fatal(os.WriteFile(HFile, y, Permission))
return nil
}
@ -99,7 +98,7 @@ func (s Settings) Stream(file string) ([]byte, error) {
if err != nil {
return nil, err
}
content, err := ioutil.ReadFile(file)
content, err := os.ReadFile(file)
s.Fatal(err)
return content, err
}

View File

@ -4,8 +4,8 @@ import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
@ -39,7 +39,7 @@ type Tools struct {
}
// Setup go tools
func (t *Tools) Setup() {
func (t *Tools) Setup(p *Project) {
gocmd := "go"
// go clean
@ -48,6 +48,8 @@ func (t *Tools) Setup() {
t.Clean.isTool = true
t.Clean.cmd = replace([]string{gocmd, "clean"}, t.Clean.Method)
t.Clean.Args = split([]string{}, t.Clean.Args)
msg = fmt.Sprintln(p.pname(p.Name, 1), ":", Magenta.Bold(t.Clean.name), "running. Method:", Blue.Bold(strings.Join(t.Clean.cmd, " "), " ", strings.Join(t.Clean.Args, " ")))
log.Print(msg)
}
// go generate
if t.Generate.Status {
@ -56,6 +58,8 @@ func (t *Tools) Setup() {
t.Generate.name = "Generate"
t.Generate.cmd = replace([]string{gocmd, "generate"}, t.Generate.Method)
t.Generate.Args = split([]string{}, t.Generate.Args)
msg = fmt.Sprintln(p.pname(p.Name, 1), ":", Magenta.Bold(t.Generate.name), "running. Method:", Blue.Bold(strings.Join(t.Generate.cmd, " "), " ", strings.Join(t.Generate.Args, " ")))
log.Print(msg)
}
// go fmt
if t.Fmt.Status {
@ -66,6 +70,8 @@ func (t *Tools) Setup() {
t.Fmt.isTool = true
t.Fmt.cmd = replace([]string{"gofmt"}, t.Fmt.Method)
t.Fmt.Args = split([]string{}, t.Fmt.Args)
msg = fmt.Sprintln(p.pname(p.Name, 1), ":", Magenta.Bold(t.Fmt.name), "running. Method:", Blue.Bold(strings.Join(t.Fmt.cmd, " "), " ", strings.Join(t.Fmt.Args, " ")))
log.Print(msg)
}
// go vet
if t.Vet.Status {
@ -74,6 +80,8 @@ func (t *Tools) Setup() {
t.Vet.isTool = true
t.Vet.cmd = replace([]string{gocmd, "vet"}, t.Vet.Method)
t.Vet.Args = split([]string{}, t.Vet.Args)
msg = fmt.Sprintln(p.pname(p.Name, 1), ":", Magenta.Bold(t.Vet.name), "running. Method:", Blue.Bold(strings.Join(t.Vet.cmd, " "), " ", strings.Join(t.Vet.Args, " ")))
log.Print(msg)
}
// go test
if t.Test.Status {
@ -82,11 +90,13 @@ func (t *Tools) Setup() {
t.Test.name = "Test"
t.Test.cmd = replace([]string{gocmd, "test"}, t.Test.Method)
t.Test.Args = split([]string{}, t.Test.Args)
p.parent.Prefix(t.Test.name + ": Running")
msg = fmt.Sprintln(p.pname(p.Name, 1), ":", Magenta.Bold(t.Test.name), "running. Method:", Blue.Bold(strings.Join(t.Test.cmd, " "), " ", strings.Join(t.Test.Args, " ")))
log.Print(msg)
}
// go install
t.Install.name = "Install"
t.Install.cmd = replace([]string{gocmd, "install"}, t.Install.Method)
fmt.Println(t.Install.cmd)
t.Install.Args = split([]string{}, t.Install.Args)
// go build
if t.Build.Status {
@ -104,7 +114,7 @@ func (t *Tool) Exec(path string, stop <-chan bool) (response Response) {
}
// check if there is at least one go file
matched := false
files, _ := ioutil.ReadDir(path)
files, _ := os.ReadDir(path)
for _, f := range files {
matched, _ = filepath.Match("*.go", f.Name())
if matched {
@ -137,6 +147,7 @@ func (t *Tool) Exec(path string, stop <-chan bool) (response Response) {
}
cmd.Stdout = &out
cmd.Stderr = &stderr
// Start command
err := cmd.Start()
if err != nil {
@ -149,7 +160,9 @@ func (t *Tool) Exec(path string, stop <-chan bool) (response Response) {
select {
case <-stop:
// Stop running command
cmd.Process.Kill()
if err := cmd.Process.Kill(); err != nil {
response.Err = err
}
case err := <-done:
// Command completed
response.Name = t.name
@ -180,14 +193,29 @@ func (t *Tool) Compile(path string, stop <-chan bool) (response Response) {
cmd.Stdout = &out
cmd.Stderr = &stderr
// Start command
cmd.Start()
go func() { done <- cmd.Wait() }()
err := cmd.Start()
if err != nil {
log.Println(log.Prefix(), err.Error())
if cmd.Process != nil {
err := cmd.Process.Kill()
if err != nil {
log.Println(log.Prefix(), err.Error())
}
}
}
go func() {
done <- cmd.Wait()
}()
// Wait a result
response.Name = t.name
select {
case <-stop:
// Stop running command
cmd.Process.Kill()
err := cmd.Process.Kill()
if err != nil {
fmt.Println(err.Error())
return
}
case err := <-done:
// Command completed
if err != nil {