This is a total mess.

This commit is contained in:
Elf M. Sternberg 2018-02-25 12:41:46 -08:00
parent f1a31f551e
commit da64736132
17 changed files with 699 additions and 1 deletions

2
.gitignore vendored
View File

@ -11,4 +11,4 @@
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
vendor/

9
AUTHORS.md Normal file
View File

@ -0,0 +1,9 @@
# Credits
## Development Lead
- Elf M. Sternberg [elfsternberg](https://github.com/elfsternberg)
## Contributors
None yet. Why not be the first?

74
CODE-OF-CONDUCT.md Normal file
View File

@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
education, socio-economic status, nationality, personal appearance, race,
religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org

81
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,81 @@
# Contributing
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
You can contribute in many ways:
## Types of Contributions
### Report Bugs
Report bugs at https://github.com/elfsternberg/formulaic/issues.
If you are reporting a bug, please include:
* Your operating system name and version.
* Any details about your local setup that might be helpful in troubleshooting.
* Detailed steps to reproduce the bug.
### Fix Bugs
Look through the GitHub issues for bugs. Anything tagged with "bug"
is open to whoever wants to implement it.
### Implement Features
Look through the GitHub issues for features. Anything tagged with "feature"
is open to whoever wants to implement it.
### Write Documentation
formulaic could always use more documentation, whether as part of the
official formulaic docs, in docstrings, or even on the web in blog posts,
articles, and such.
### Submit Feedback
The best way to send feedback is to file an issue at https://github.com/elfsternberg/formulaic/issues.
If you are proposing a feature:
* Explain in detail how it would work.
* Keep the scope as narrow as possible, to make it easier to implement.
* Remember that this is a volunteer-driven project, and that contributions
are welcome :)
## Get Started!
Ready to contribute? Here's how to set up `formulaic` for local development.
1. Fork the `formulaic` repo on GitHub.
2. Clone your fork locally::
$ git clone git@github.com:your_name_here/formulaic.git
3. Create a branch for local development::
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
4. When you're done making changes, check that your changes pass the tests::
$ make test
6. Commit your changes and push your branch to GitHub::
$ git add .
$ git commit -m "Your detailed description of your changes."
$ git push origin name-of-your-bugfix-or-feature
7. Submit a pull request through the GitHub website.
Pull Request Guidelines
-----------------------
Before you submit a pull request, check that it meets these guidelines:
1. The pull request should include tests.
2. If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in README.md.

113
Gopkg.lock generated Normal file
View File

@ -0,0 +1,113 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/fsnotify/fsnotify"
packages = ["."]
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
branch = "master"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token"
]
revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8"
[[projects]]
name = "github.com/magiconair/properties"
packages = ["."]
revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
version = "v1.7.6"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
[[projects]]
name = "github.com/pelletier/go-toml"
packages = ["."]
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
version = "v1.1.0"
[[projects]]
name = "github.com/spf13/afero"
packages = [
".",
"mem"
]
revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c"
version = "v1.0.2"
[[projects]]
name = "github.com/spf13/cast"
packages = ["."]
revision = "8965335b8c7107321228e3e3702cab9832751bac"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
version = "v1.0.0"
[[projects]]
name = "github.com/spf13/viper"
packages = ["."]
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
version = "v1.0.0"
[[projects]]
name = "github.com/urfave/cli"
packages = ["."]
revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1"
version = "v1.20.0"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "f6cff0780e542efa0c8e864dc8fa522808f6a598"
[[projects]]
name = "golang.org/x/text"
packages = [
"internal/gen",
"internal/triegen",
"internal/ucd",
"transform",
"unicode/cldr",
"unicode/norm"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "7f97868eec74b32b0982dd158a51a446d1da7eb5"
version = "v2.1.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "d8cfff552a8b5944b1ef528b7188d2f8c501391574026859edcab1406ff3ba63"
solver-name = "gps-cdcl"
solver-version = 1

43
Gopkg.toml Normal file
View File

@ -0,0 +1,43 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/spf13/viper"
version = "1.0.0"
[[constraint]]
name = "github.com/gobwas/glob"
[[constraint]]
name = "github.com/urfave/cli"
[[constraint]]
name = "github.com/Masterminds/vcs"
[prune]
go-tests = true
unused-packages = true

36
Makefile Normal file
View File

@ -0,0 +1,36 @@
BIN_NAME=formulaic
VERSION := $(shell grep "const Version " version.go | sed -E 's/.*"(.+)"$$/\1/')
GIT_COMMIT=$(shell git rev-parse HEAD)
GIT_DIRTY=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true)
IMAGE_NAME := "elfsternberg/formulaic"
.PHONY default
default: help
.PHONY build
build: ### Compile the formulaic binary
@echo "building ${BIN_NAME} ${VERSION}"
@echo "GOPATH=${GOPATH}"
go build -ldflags "-X main.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X main.VersionPrerelease=DEV" -o bin/${BIN_NAME}
.PHONY test
test: ### Run the unit tests
go test $(glide nv)
.PHONY get-dep
get-deps: ### Install the dependencies needed to build the project
glide install
.PHONY clean
clean: ### Clean the directory tree
@test ! -e bin/${BIN_NAME} || rm bin/${BIN_NAME}
.PHONY help
help: ### Display this help message
@echo 'Management commands for formulaic:'
@echo
@echo 'Usage:'
@perl -nl scripts/show-help.pl $(MAKEFILE_LIST)

25
NOTES.md Normal file
View File

@ -0,0 +1,25 @@
As this is my third Go project and remarkably like my git-linter project, I've
decided to try and keep the same sort of documentation path I chose for that
project.
# Strategies
formulaic uses a variety of strategies that it passes through to understand
what to do.
1. Parse the command line.
2. Read in the configuration file, in the following order (so later
keys override earlier keys):
a. From formulaic.[toml|json|yaml]
b. From $HOME/.formulaic/config.[toml|json|yaml]
c. As specified as a filename on the command line
d. As specified as key-value pairs on the command line.
3. Find or pull down the requested template.
4. Unless otherwise specified, make queries as specified in the formulaic
configuration.
5. Generate the project

18
README.md Normal file
View File

@ -0,0 +1,18 @@
# formulaic
A Cookiecutter Clone in Go
## Getting started
This project requires Go to be installed. On OS X with Homebrew you can just run `brew install go`.
Running it then should be as simple as:
```console
$ make
$ ./bin/formulaic
```
### Testing
``make test``

16
TODO.md Normal file
View File

@ -0,0 +1,16 @@
* Re-arrange to use the command feature.
* Add the --update and --nokeep options to fetch
* Global configuration file
* Command: 'list' - List the existing local templates
** Option: '-r' - List remote templates. (Make this, DO NOT ENABLE)
* Command: 'fetch' - Get a template, do not run it.
* Feature: DESCRIPTION.md in the template root for the list option
* URL Hash as a replay target, to avoid trying to encode the URL as path
* See if the Go template language has date handlers, figure out how to
parse that.
* Does Viper return TOML, JSON, and YAML files in order? Consider
Progressive Parsing of configuration (earlier keys with hard values can
be substituted for later keys with templated values.)
* Cascading rules for applying keys and values to templates
* Add TGZ, tar.bz, tar.xz, zip to list of capabilities.
* Figure out your list of commands, including the default.

48
commands/commands.go Normal file
View File

@ -0,0 +1,48 @@
package commands
func listLocalTemplates(spath *string) ([]string, error) {
templatePath := filepath.Join(spath, "templates")
templateStat, err := os.Stat(templatePath)
if os.IsNotExist(err) {
os.Mkdir(templatePath, 0755)
return []
}
if err != nil {
return nil, cliNewExitError(err, 1)
}
func List(spath *string) error {
path, err := filepath.Abs(spath)
if err != nil {
return nil, cli.NewExitError(fmt.Sprintf("%s: not a valid path", path), 1)
}
pathstats, err := os.Stat(path)
if os.IsNotExist(err) {
os.Mkdir(path)
return path
}
if error != nil {
return nil, cli.NewExitError(fmt.Sprintf("%s: not a valid path", path), 1)
}
if !pathstats.IsDir() {
return nil, cli.NewExitError(fmt.Sprintf("%s: not a directory", path), 1)
}
path, nil
}

8
commands/template.go Normal file
View File

@ -0,0 +1,8 @@
package commands
import (
"github.com/Masterminds/vcs"
)
func getTemplate(url *string) (*string, error) {

53
config/config.go Normal file
View File

@ -0,0 +1,53 @@
package config
import (
"time"
"github.com/spf13/viper"
)
// Provider defines a set of read-only methods for accessing the application
// configuration params as defined in one of the config files.
type Provider interface {
ConfigFileUsed() string
Get(key string) interface{}
GetBool(key string) bool
GetDuration(key string) time.Duration
GetFloat64(key string) float64
GetInt(key string) int
GetInt64(key string) int64
GetSizeInBytes(key string) uint
GetString(key string) string
GetStringMap(key string) map[string]interface{}
GetStringMapString(key string) map[string]string
GetStringMapStringSlice(key string) map[string][]string
GetStringSlice(key string) []string
GetTime(key string) time.Time
InConfig(key string) bool
IsSet(key string) bool
}
var defaultConfig *viper.Viper
func Config() Provider {
return defaultConfig
}
func LoadConfigProvider(appName string) Provider {
return readViperConfig(appName)
}
func init() {
defaultConfig = readViperConfig("FORMULAIC")
}
func readViperConfig(appName string) *viper.Viper {
v := viper.New()
v.SetEnvPrefix(appName)
v.AutomaticEnv()
// global defaults
return v
}

BIN
formulaic Executable file

Binary file not shown.

159
main.go Normal file
View File

@ -0,0 +1,159 @@
package main
import (
"fmt"
"github.com/urfave/cli"
"formulaic/commands"
"log"
"os"
"os/user"
"path/filepath"
"time"
)
func formRoot() (*string, error) {
usr, err := user.Current()
if err != nil {
return nil, cli.NewExitError("Could not determine current user.", 1)
}
rootpath, err := filepath.Abs(usr.HomeDir + "/.formulaic/")
if err != nil {
return nil, cli.NewExitError(err, 1)
}
rootstat, err := os.Stat(rootpath)
if os.IsNotExist(err) {
os.Mkdir(rootpath, 0755)
return &rootpath, nil
}
if !rootstat.IsDir() {
return nil, cli.NewExitError(fmt.Sprintf("%s: not a directory", rootpath), 1)
}
return &rootpath, nil
}
func main() {
app := cli.NewApp()
root, err := formRoot()
if err != nil {
log.Fatal(err)
}
branchFlag := cli.StringFlag{
Name: "checkout, b",
Usage: "branch, tag or commit to checkout",
}
updateFlag := cli.BoolFlag{
Name: "update, u",
Usage: "update the template local template if it is already present",
}
app.Name = "formulaic"
app.Version = "0.2"
app.Compiled = time.Now()
app.Copyright = "Copyright © 2018 Elf M. Sternberg"
app.Usage = "generate a new project from a project template"
app.Authors = []cli.Author{
{
Name: "Elf M. Sternberg",
Email: "elf.sternberg@gmail.com",
},
}
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "verbose, V",
Usage: "print debugging information",
},
}
app.Commands = []cli.Command{
{
Name: "build",
Aliases: []string{"b"},
Usage: "build a new project from a formulaic template",
UsageText: "formulaic build [options] [template name or url] [key=value ...]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "specify alternative formulaic config file",
Value: "formulaic.[toml|json|yaml]",
},
cli.StringFlag{
Name: "path, o",
Usage: "specify path to directory into which project will be written",
Value: ".",
},
cli.BoolFlag{
Name: "force, f",
Usage: "overwrite if output directory exists",
},
updateFlag,
branchFlag,
cli.BoolFlag{
Name: "noprompt, x",
Usage: "do not prompt for input, use the formulaic config file only",
},
cli.BoolFlag{
Name: "replay, r",
Usage: "use replay file, if any",
},
},
Action: func(c *cli.Context) error {
fmt.Printf("Build.")
return nil
},
},
{
Name: "list",
Aliases: []string{"l"},
Usage: "list all formulaic templates in your cache, with optional filter",
UsageText: "formulaic list [options] [search expression]",
Flags: []cli.Flag{
/* This is commented out because I know that
maintaining a template repository will never be a
priority for me.
*/
/*
cli.BoolFlag{
Name: "remote, r",
Usage: "show remote templates from template repository",
},
*/
cli.BoolFlag{
Name: "long, l",
Usage: "show the long description, if available",
},
cli.BoolFlag{
Name: "prompts, p",
Usage: "show the template prompts, if available",
},
},
Action: func(c *cli.Context) error {
return commands.List(root)
},
},
{
Name: "fetch",
Aliases: []string{"f"},
Usage: "fetch and store, but do not run, a template",
UsageText: "formulaic fetch [options] [template url]",
Flags: []cli.Flag{
updateFlag,
},
Action: func(c *cli.Context) error {
fmt.Printf("Fetch.")
return nil
},
},
}
app.Run(os.Args)
}

3
scripts/show-help.pl Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env perl -nl
# Extracts help entries and shows them.
m{^([\w\-]+\:).*?###(.*)$} && printf("make %-18s %s\n", $1, $2);

12
version/version.go Normal file
View File

@ -0,0 +1,12 @@
package main
// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string
// The main version number that is being run at the moment.
const Version = "0.1.0"
// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development)
var VersionPrerelease = ""