Skip to content
Open
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
28 changes: 14 additions & 14 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/kozmod/progen)

Simple projects generator.

___
### Installation

```console
Expand All @@ -17,21 +17,21 @@ go install github.com/kozmod/progen@latest
```console
go build -o progen .
```

___
### About

`progen` use `yml` config file to generate directories, files and execute commands

#### Allowed config file's keys
`progen` use `yml` config file to generate directories, files and execute commands ([actions](#Actions))
___
### Args

| Name | Type | Description |
|:-----|:------:|:------------------------------------------------------------------:|
| f | string | path to config file |
| v | bool | verbose output |
| dr | bool | `dry run` mode <br/>(to verbose output should be combine with`-v`) |
| help | bool | show flags |

#### Action config file's tags
___
### Actions

| Key | Type | Optional | Description |
|:------------------|:-----------------:|:--------:|:---------------------------------------------------------------------------------:|
Expand All @@ -58,8 +58,8 @@ go build -o progen .
✳️ required one of for parent block

**❗Note:** all action execute on declaration order

#### Example
___
### Example

```yaml
## preprocessing of "raw" config use `text/template` of golang's stdlib
Expand Down Expand Up @@ -136,22 +136,22 @@ files:
cmd:
- curl -H PRIVATE-TOKEN:{{.vars.TOKEN}} {{.vars.REPO_1}}/.editorconfig/raw?ref=master -o .editorconfig
```
___
### Generate

#### Generate project structure from configuration file

use configuration file with default name (`progen.yaml`)
`progen` use `progen.yaml` as default configuration file

```console
progen -v
```

or define custom config location using `-f`
`-f` flag set custom configuration file

```console
progen -v -f conf.yml
```

generated project structure
generated files and directories

```console
.
Expand Down
53 changes: 52 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package config
import (
"fmt"
"net/url"
"os"
"reflect"
"strconv"
"strings"

"gopkg.in/yaml.v3"
Expand All @@ -19,9 +21,9 @@ const (

type Config struct {
HTTP *HTTPClient `yaml:"http"`
Dirs []string `yaml:"dirs,flow"`
Files []File `yaml:"files,flow"`
Cmd []string `yaml:"cmd,flow"`
Dirs []Dir `yaml:"dirs,flow"`
}

type HTTPClient struct {
Expand All @@ -30,12 +32,43 @@ type HTTPClient struct {
Debug bool `yaml:"debug"`
}

type Dir struct {
Path string `yaml:"path"`
Perm *Perm `yaml:"perm"`
}

func (dir *Dir) UnmarshalYAML(unmarshal func(any) error) error {
var raw string
err := unmarshal(&raw)
if err == nil {
*dir = Dir{
Perm: nil,
Path: raw,
}
return nil
}

if _, ok := err.(*yaml.TypeError); !ok {
return fmt.Errorf("unmarshal dir: string: %w", err)
}

type alias Dir
var val alias
err = unmarshal(&val)
if err != nil {
return fmt.Errorf("unmarshal dir: struct: %w", err)
}
*dir = Dir(val)
return nil
}

type File struct {
Path string `yaml:"path"`
Data *string `yaml:"data"`
Get *Get `yaml:"get"`
Local *string `yaml:"local"`
ExecTmplSkip bool `yaml:"tmpl_skip"`
Perm *Perm `yaml:"perm"`
}

type Get struct {
Expand All @@ -60,6 +93,24 @@ func (addr *AddrURL) UnmarshalYAML(unmarshal func(any) error) error {
return nil
}

type Perm struct {
os.FileMode `yaml:",inline"`
}

func (perm *Perm) UnmarshalYAML(unmarshal func(any) error) error {
var raw string
if err := unmarshal(&raw); err != nil {
return fmt.Errorf("unmarshal perm: %w", err)
}
fm, err := strconv.ParseUint(raw, 0, 32)
if err != nil {
return fmt.Errorf("unmarshal perm unit: %w", err)
}

*perm = Perm{FileMode: os.FileMode(fm)}
return nil
}

func UnmarshalYamlConfig(in []byte) (Config, error) {
var conf Config
err := yaml.Unmarshal(in, &conf)
Expand Down
22 changes: 16 additions & 6 deletions internal/entity/entity.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package entity

import "os"

const (
Space = " "
Empty = ""
Expand All @@ -17,23 +19,31 @@ type Logger interface {
Debugf(format string, any ...any)
}

type Dir struct {
Path string
Perm os.FileMode
}

type File struct {
Path string
Name string
Perm os.FileMode
}

type DataFile struct {
Path string
Name string
File
Data []byte
ExecTmpl bool
}

type LocalFile struct {
Path string
Name string
File
LocalPath string
ExecTmpl bool
}

type RemoteFile struct {
Path string
Name string
File
URL string
Headers map[string]string
ExecTmpl bool
Expand Down
23 changes: 15 additions & 8 deletions internal/factory/file_proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package factory

import (
"fmt"
"os"
"path/filepath"

"github.com/go-resty/resty/v2"
Expand All @@ -26,25 +27,31 @@ func NewFileProc(
var client *resty.Client
for _, f := range conf.Files {
var (
name = filepath.Base(f.Path)
path = filepath.Dir(f.Path)
file = entity.File{
Name: filepath.Base(f.Path),
Path: filepath.Dir(f.Path),
Perm: os.ModePerm,
}
template = !f.ExecTmplSkip
)

if f.Perm != nil {
file.Perm = f.Perm.FileMode
}

var producer entity.FileProducer
switch {
case f.Data != nil:
file := entity.DataFile{
Name: name,
Path: path,
File: file,
Data: []byte(*f.Data),
ExecTmpl: template,
}
producer = proc.NewStoredProducer(file)

case f.Get != nil:
file := entity.RemoteFile{
Name: name,
Path: path,
File: file,
URL: f.Get.URL,
Headers: f.Get.Headers,
ExecTmpl: template,
Expand All @@ -55,10 +62,10 @@ func NewFileProc(
}

producer = proc.NewRemoteProducer(file, client)

case f.Local != nil:
file := entity.LocalFile{
Name: name,
Path: path,
File: file,
LocalPath: *f.Local,
ExecTmpl: template,
}
Expand Down
29 changes: 22 additions & 7 deletions internal/factory/mkdir_proc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package factory

import (
"os"

"github.com/kozmod/progen/internal/config"
"github.com/kozmod/progen/internal/entity"
"github.com/kozmod/progen/internal/proc"
Expand All @@ -11,25 +13,38 @@ func NewMkdirProc(conf config.Config, logger entity.Logger, dryRun bool) (proc.P
return nil, nil
}

dirSet := uniqueVal(conf.Dirs)
dirSet := uniqueVal[config.Dir, string](conf.Dirs, func(dir config.Dir) string {
return dir.Path
})

dirs := make([]entity.Dir, 0, len(dirSet))
for _, dir := range dirSet {
perm := os.ModePerm
if dir.Perm != nil {
perm = dir.Perm.FileMode
}

dirs = append(dirs, entity.Dir{Path: dir.Path, Perm: perm})
}

if dryRun {
return proc.NewDryRunMkdirAllProc(dirSet, logger), nil
return proc.NewDryRunMkdirAllProc(dirs, logger), nil
}

return proc.NewMkdirAllProc(dirSet, logger), nil
return proc.NewMkdirAllProc(dirs, logger), nil
}

func uniqueVal[T comparable](in []T) []T {
set := make(map[T]struct{}, len(in))
func uniqueVal[T any, R comparable](in []T, keyFn func(T) R) []T {
set := make(map[R]struct{}, len(in))
out := make([]T, 0, len(in))
for _, val := range in {
_, ok := set[val]
key := keyFn(val)
_, ok := set[key]
if ok {
continue
}
out = append(out, val)
set[val] = struct{}{}
set[key] = struct{}{}
}
return out
}
Loading