Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: test mode #123

Merged
merged 23 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .examples/cli/scaffold.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ questions:
- "green"
- "blue"
- "yellow"

presets:
default:
Project: "scaffold-test-default"
description: "This is a test description"
colors: ["red", "green"]
85 changes: 1 addition & 84 deletions .examples/cli/{{ .ProjectKebab }}/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,91 +2,8 @@ package main

import (
"fmt"
"os"

"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
)

var (
// Build information. Populated at build-time via -ldflags flag.
version = "dev"
commit = "HEAD"
date = "now"
)

func build() string {
short := commit
if len(commit) > 7 {
short = commit[:7]
}

return fmt.Sprintf("%s (%s) %s", version, short, date)
}

func main() {
ctrl := &controller{}

app := &cli.App{
Name: "{{ .Project }}",
Usage: "{{ .Scaffold.description }}",
Version: build(),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "cwd",
Usage: "current working directory",
Value: ".",
},
&cli.StringFlag{
Name: "log-level",
Usage: "log level (debug, info, warn, error, fatal, panic)",
Value: "panic",
},
},
Before: func(ctx *cli.Context) error {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})

ctrl.cwd = ctx.String("cwd")
ctrl.logLevel = ctx.String("log-level")

switch ctrl.logLevel {
case "debug":
log.Level(zerolog.DebugLevel)
case "info":
log.Level(zerolog.InfoLevel)
case "warn":
log.Level(zerolog.WarnLevel)
case "error":
log.Level(zerolog.ErrorLevel)
case "fatal":
log.Level(zerolog.FatalLevel)
default:
log.Level(zerolog.PanicLevel)
}

return nil
},
Commands: []*cli.Command{
{
Name: "hello",
Usage: "Says hello world",
Action: ctrl.HelloWorld,
},
},
}

if err := app.Run(os.Args); err != nil {
log.Fatal().Err(err).Msg("failed to run scaffold")
}
}

type controller struct {
cwd string
logLevel string
}

func (c *controller) HelloWorld(ctx *cli.Context) error {
fmt.Println("Hello, your favorite colors are {{ .Scaffold.colors | join `, ` }}")
return nil
fmt.Println("colors={{ .Scaffold.colors | join `, ` }} description={{ .Scaffold.description }}")
}
25 changes: 25 additions & 0 deletions .examples/nested/scaffold.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
questions:
- name: "question_1"
prompt:
message: "Question 1"
required: true
- name: "question_2"
prompt:
message: "Question 2"
required: true
- name: "question_3"
prompt:
message: "Question 3"
required: true
- name: "question_4"
prompt:
message: "Question 4"
required: true

presets:
default:
Project: "nested-defaults"
question_1: "Answer 1"
question_2: "Answer 2"
question_3: "Answer 3"
question_4: "Answer 4"
1 change: 1 addition & 0 deletions .examples/nested/{{ .ProjectKebab }}/child/child_1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ .Scaffold.question_2 }}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ .Scaffold.question_3 }}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ .Scaffold.question_4 }}
1 change: 1 addition & 0 deletions .examples/nested/{{ .ProjectKebab }}/root.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ .Scaffold.question_1 }}
12 changes: 5 additions & 7 deletions .examples/role/scaffold.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ rewrites:
computed:
snaked: "{{ snakecase .Scaffold.role_name }}"
static: false
inject:
- name: "add role to site.yaml"
path: site.yaml
at: "# $Scaffold.role_name"
template: |
- name: {{ .Scaffold.role_name }}
role: {{ .Computed.snaked }}

test:
role_name: "test_role"
toggle: true
description: "This is a test role"
5 changes: 4 additions & 1 deletion .github/workflows/partial-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
Go:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
Expand All @@ -26,3 +26,6 @@ jobs:

- name: Test
run: go test ./... -race

- name: Test 'cli' example
run: ./tests/runner.sh
File renamed without changes.
File renamed without changes.
File renamed without changes.
168 changes: 168 additions & 0 deletions app/commands/cmd_new.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package commands

import (
"fmt"
"math/rand"
"os"
"strings"

"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/hay-kot/scaffold/app/core/fsast"
"github.com/hay-kot/scaffold/app/scaffold"
"github.com/hay-kot/scaffold/app/scaffold/pkgs"
"github.com/sahilm/fuzzy"
)

type FlagsNew struct {
NoPrompt bool
Preset string
Snapshot string
}

func (ctrl *Controller) New(args []string, flags FlagsNew) error {
if len(args) == 0 {
return fmt.Errorf("missing scaffold name")
}

path, err := ctrl.resolve(args[0], flags.NoPrompt)
if err != nil {
return err
}

if path == "" {
return fmt.Errorf("missing scaffold path")
}

rest := args[1:]
argvars, err := parseArgVars(rest)
if err != nil {
return err
}

var varfunc func(*scaffold.Project) (map[string]any, error)
switch {
case flags.NoPrompt:
varfunc = func(p *scaffold.Project) (map[string]any, error) {
caseVars, ok := p.Conf.Presets[flags.Preset]
if !ok {
return nil, fmt.Errorf("case %s not found", flags.Preset)
}

project, ok := caseVars["Project"].(string)
if !ok || project == "" {
// Generate 4 random digits
name := fmt.Sprintf("scaffold-test-%04d", rand.Intn(10000))
caseVars["Project"] = name
project = name
}
p.Name = project

// Test cases do not use rc.Defaults
vars := scaffold.MergeMaps(caseVars, argvars)
return vars, nil
}

default:
varfunc = func(p *scaffold.Project) (map[string]any, error) {
vars := scaffold.MergeMaps(argvars, ctrl.rc.Defaults)
vars, err = p.AskQuestions(vars, ctrl.engine)
if err != nil {
return nil, err
}

return vars, nil
}
}

outfs := ctrl.Flags.OutputFS()

err = ctrl.runscaffold(runconf{
scaffolddir: path,
showMessages: !flags.NoPrompt,
varfunc: varfunc,
outputfs: outfs,
})
if err != nil {
return err
}

if flags.Snapshot != "" {
ast, err := fsast.New(outfs)
if err != nil {
return err
}

if flags.Snapshot == "stdout" {
fmt.Println(ast.String())
} else {
file, err := os.Create(flags.Snapshot)
if err != nil {
return err
}

_ = file.Close()

_, err = file.WriteString(ast.String())
if err != nil {
return err
}
}
}

return nil
}

func (ctrl *Controller) fuzzyFallBack(str string) ([]string, []string, error) {
systemScaffolds, err := pkgs.ListSystem(os.DirFS(ctrl.Flags.Cache))
if err != nil {
return nil, nil, err
}

localScaffolds, err := pkgs.ListLocal(os.DirFS(ctrl.Flags.OutputDir))
if err != nil {
return nil, nil, err
}

systemMatches := fuzzy.Find(str, systemScaffolds)
systemMatchesOutput := make([]string, len(systemMatches))
for i, match := range systemMatches {
systemMatchesOutput[i] = match.Str
}

localMatches := fuzzy.Find(str, localScaffolds)
localMatchesOutput := make([]string, len(localMatches))
for i, match := range localMatches {
localMatchesOutput[i] = match.Str
}

return systemMatchesOutput, localMatchesOutput, nil
}

func basicAuthAuthorizer(pkgurl, username, password string) pkgs.AuthProviderFunc {
return func(url string) (transport.AuthMethod, bool) {
if url != pkgurl {
return nil, false
}

return &http.BasicAuth{
Username: username,
Password: password,
}, true
}
}

func parseArgVars(args []string) (map[string]any, error) {
vars := make(map[string]any, len(args))

for _, v := range args {
if !strings.Contains(v, "=") {
return nil, fmt.Errorf("variable %s is not in the form of key=value", v)
}

kv := strings.Split(v, "=")
vars[kv[0]] = kv[1]
}

return vars, nil
}
15 changes: 12 additions & 3 deletions app/commands/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package commands

import (
"github.com/hay-kot/scaffold/app/core/engine"
"github.com/hay-kot/scaffold/app/core/rwfs"
"github.com/hay-kot/scaffold/app/scaffold"
)

Expand All @@ -13,16 +14,24 @@ type Flags struct {
Cache string
OutputDir string
ScaffoldDirs []string
Cwd string
}

// OutputFS returns a WriteFS based on the OutputDir flag
func (f Flags) OutputFS() rwfs.WriteFS {
if f.OutputDir == ":memory:" {
return rwfs.NewMemoryWFS()
}

return rwfs.NewOsWFS(f.OutputDir)
}

type Controller struct {
// Global Flags
// Flags contains the CLI flags
// that are from the root command
Flags Flags

engine *engine.Engine
rc *scaffold.ScaffoldRC
vars map[string]string
prepared bool
}

Expand Down
Loading