11 Commits

Author SHA1 Message Date
5364bfaa8a little improvements and more docs 2023-04-16 00:36:33 -04:00
1d2d293b21 more readme +dirtree pic 2023-04-15 16:06:39 -04:00
aa955c3b4f improved building 2023-04-15 13:33:09 -04:00
e65633a85a Merge branch 'main' of gitea:kevin/scaffold
because it's out of sync
2023-04-15 04:29:54 -04:00
d53ad8993c expanding for multiple platforms 2023-04-15 04:24:05 -04:00
7b7fdc0113 tiny cleanups 2023-04-13 15:40:56 -04:00
b010d63351 Update 'README.md' 2023-04-13 15:24:15 -04:00
c5447a68ae Update 'README.md' 2023-04-13 15:22:25 -04:00
6c03f880da *gitea remote and push 2023-04-13 15:13:38 -04:00
b082810d91 +user init commands in scaffold-<ProjType>.toml 2023-04-10 18:53:24 -04:00
87a045cbea changed .gitignore file handling/creation 2023-04-10 15:54:41 -04:00
7 changed files with 269 additions and 44 deletions

3
.gitignore vendored
View File

@@ -9,5 +9,4 @@ cfg/*.env
cfg/*.toml cfg/*.toml
build/ build/
buildTime.txt build*.txt
buildVersion.txt

View File

@@ -24,6 +24,9 @@ endif
# Variables # Variables
#---------------------------------------------------------------- #----------------------------------------------------------------
progName = scaffold progName = scaffold
arch = amd64
winbinName = $(progName).exe
releaseTag = $(shell git describe --abbrev=0)
#---------------------------------------------------------------- #----------------------------------------------------------------
# Helpers # Helpers
@@ -51,17 +54,25 @@ clean:
#---------------------------------------------------------------- #----------------------------------------------------------------
# build strips debugging info from binary # build strips debugging info from binary
linkerflags = '-s' linkerflags = '-s -w'
prep: clean prep: clean
> date +"%F %a %T %Z" > buildTime.txt > date +"%F %a %T %Z" > buildTime.txt
> git describe --always --tags --dirty --long > buildVersion.txt > git describe --always --tags --dirty --long > buildVersion.txt
> @mkdir -p build
build: prep build: prep linux windows
> @go build -o build/$(progName) . && echo "Build success" || echo "[FAILED] go build"
build-production: prep linux:
> @go build -ldflags=${linkerflags} -o build/$(progName) . && echo "production build success" || echo "[FAILED] production build" > @GOOS=$@ GOARCH=$(arch) go build -o build/$@/$(progName) . && echo $@ "Build success" || echo $@ "[FAILED] go build"
windows:
> @GOOS=$@ GOARCH=$(arch) go build -o build/$@/$(progName)-$(arch)-$(releaseTag).exe . && echo $@ "Build success" || echo $@ "[FAILED] go build"
build-production: prep linx-production
linux-production:
> @GOOS=linx GOARCH=$(arch) go build -ldflags=${linkerflags} -o build/$@/$(progName) . && echo "production build success" || echo "[FAILED] production build"
#------------------------------------------------------------- #-------------------------------------------------------------
# Install - same as build, but places binary on system path # Install - same as build, but places binary on system path
@@ -74,4 +85,4 @@ install-production: prep
> @go install -ldflags=${linkerflags} . && echo "Production build and install success" || echo "[FAILED] Production install" > @go install -ldflags=${linkerflags} . && echo "Production build and install success" || echo "[FAILED] Production install"
## Commands that don't relate to a specific file ## Commands that don't relate to a specific file
.PHONY: help confirm clean prep build build-production .PHONY: help confirm clean prep build build-production linux windows amd64

View File

@@ -2,4 +2,51 @@
## Intro ## Intro
blah blah blah Use scaffold to setup a new project with a directory skeleton of your design, and if you'd like, it can also automatically initialize local, remote and gitea git repositories for the new project.
## What scaffold does
- A new project directory is created
- Your project skeleton/template is copied from your config/projectType/sample directory
- your setup/initialization commmands are run
- a git repository is initialized in the project directory
- and if you enable it, a remote repository is created and setup
## Installation
1 - Grab a version for your system from the releases page.
2 - Put it in a directory that is on your path.
3 - now configure your preferred setups/layouts/skeletons/templates for your projects.
## Configuration
On linux the configuration directory will be at /home/user/.config/devel/scaffold
For the windows versions, you might want to look at your User_Configuration_Directories location, and change it according to your needs. On windows it's value is the %APPDATA% environment variable.
In a PowerShell, use `Get Child-Item Env:` to display all the environment variables.
Then `[Environment]::SetEnvironmentVariable("APPDATA","C:\Users\You\Wherever-you-like\","User")` to set the new value.
## The `scaffold` command
| Command | Description |
| ------- | ----------- |
| scaffold | The program name |
| flags:
-i or -Info | Display the available project types and the build and version information about the program. |
| **Examples:** | |
| scaffold *ProjectType* *NewProjectName* | Stuff |
## Configuration of samples
![Example Directory Structure](assets/images/dirtree-pic.png)
The image should explain alot quickly. The go, go-cli, go-web, hs, js, py and svelte directories define the project types available to scaffold. Arrange the contents of the sample directory, within each project type directory, to your preferred layout for each language/use-case.
## Git
Git is available to be used as the version control system for new projects. Select/Deselect within the `scaffold-<PROJECTTYPE>.toml` file.
*Please Note* that if you also use git to track your configuration files and sample directories, then a `.gitignore` file within your sample directory will interfere with git's ability to track your skeletons/templates properly. So, to allow for this, put what you normally would in a `.gitignore` file instead into a file named `GITIGNORE` in your sample directory. And scaffold will convert it to a `.gitignore` file within your newProject directory.
If you are NOT using git to track your project types and samples/skeletons/templates, then no problem, just place a .gitignore file within the sample directory as you would any other file.

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -11,18 +11,10 @@ scaffold go NewGoProject
scaffold py aPythonProject scaffold py aPythonProject
` `
var gi_template = `%s var cfg_content = `# Scaffold program configuration
.env
*.env
.env.toml
cfg/*.toml # Format: TOML - see https://toml.io for details
cfg/.env
cfg/*.env
cfg/.env*
*.toml
build/ # Too funny - this config file doesn't seem to be needed ;-)
buildTime.txt # let's keep it for future use -- very silly
buildVersion.txt testkey = "test value"`
`

View File

@@ -0,0 +1,106 @@
# New Go Project configuration file
# Format: TOML - see https://toml.io for details
# Go module path is of the form basePath/projectName
# must be addressable if you are going to publish
module_basepath = "somewhere.org"
# where to put new project directories
# within the user's home directory
projects_basedir = "projects/goapps"
# go example dirs
# /cmd, /pkg, /web, /api, /init, /vendor, /internal, /bin, /build,
# /cfg, /configs, /docs, /scripts, /tools, /examples, /tests,
# /deployments, /deploy
# or whatever you like
# Must be string, will be parsed to correct type
# Remember perms are in octal
project_dir_permissions = "0700"
file_permissions = "0660"
###########################################
# Commands to run
###########################################
# after the newProjectDir is created and
# after the sample dir contents are copied over
### before git is initialized (local and remote)
# For the command: go mod init full_module_name -- use the your module_basename
# scaffold will add "/newProjectName" to it automatically
# so ["go", "mod", "init", "modulebase.net"] becomes ==> ["go", "mod", "init", "modulebase.net/NewPojectName"]
commands = [["go", "mod", "init", "somewhere.org"], ["go", "mod", "tidy"]]
##########################################
# Git
setup_git = true
# Do you have a remote location?
# Do you have autologins setup correctly with ssh keys?
# Using your .ssh/config file might be helpful
# setup remote repo?
create_remote_repo = true
# remote repo user@location
remote_user = "auser@someserver.adomain.org"
# remote repo base dir
# relative to remote user home dir
# for auser@someserver.adomain.org:/home/auser/git -- lists git dir as absolute path
# for auser@someserver.adomain.org:git -- lists git dir as relative path
############################
# remote location MUST already exist on remote server
############################
remote_location = "gitdirectory/goprojects"
# default remote short name
remote_label = "origin"
# Gitea -- do you use it somewhere
############################
# Does the Gitea app.ini have
#
# [repository]
# ENABLE_PUSH_CREATE_USER = true
# ENABLE_PUSH_CREATE_ORG = true
#
# it's needed for scaffold to push to gitea
############################
############################
#
# 4 pieces of info are needed
# 1) the server domain name eg gitea.adomain.org
# 2) the ssh port if it's not the default 22
# 3) the server account that clients connect to for ssh
# sometimes it's git or gitea or gitadmin or
# 4) the gitea user account username
# (if your gitea account is mrspecial then it's mrspecial)
#
# items 1, 2 and 3 should be in a Host section in your .ssh/config file
#
# In a section like
# Host gitea
# Hostname gitea.adomain.org
# User serveraccount
# Port 22222
# IdentitiesOnly yes
# PreferredAuthentications publickey
# IdentityFile ~/.ssh/your_id_file_private
#
# Then scaffold will set a git remote for gitea with:
#
# git remote add <gitea_remote_label> <host>:<gitea_user_accountname>/<newprojectname>.git
#
############################
setup_gitea = true
gitea_server_account = "gitea-account"
gitea_ssh_host_label = "gitea"
gitea_user_account = "auser"
remote_label_gitea = "gitea"

106
main.go
View File

@@ -10,6 +10,7 @@ import (
"os/exec" "os/exec"
"os/user" "os/user"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
@@ -30,7 +31,13 @@ type (
Create_Remote_Repo bool Create_Remote_Repo bool
Remote_User string Remote_User string
Remote_Location string Remote_Location string
Remote_Shortname string Remote_Label string
Commands [][]string
Setup_gitea bool
Gitea_server_account string // should be in ~/.ssh/config
Gitea_ssh_host_label string
Gitea_user_account string
Remote_Label_gitea string
} }
ScaffoldSetup struct { ScaffoldSetup struct {
TestKey string TestKey string
@@ -41,7 +48,7 @@ var (
infoFlag = flag.Bool("Info", false, "Displays the program build information and exits.") infoFlag = flag.Bool("Info", false, "Displays the program build information and exits.")
infoFlag2 = flag.Bool("i", false, "Displays the program build information and exits.") infoFlag2 = flag.Bool("i", false, "Displays the program build information and exits.")
sep = "/" sep = "/"
cfg_dir = ".config/devel/scaffold" cfg_dir = "devel/scaffold" // relative to <USER_CONFIG_DIR>, on linux /home/user/.config
cfg_filename = "scaffold-cfg.toml" cfg_filename = "scaffold-cfg.toml"
setup ScaffoldSetup setup ScaffoldSetup
npsetup Setup npsetup Setup
@@ -51,9 +58,23 @@ func main() {
flag.Parse() flag.Parse()
user_cfg_dir, err := os.UserConfigDir()
ifFerr("Unable to determine user config directory", err)
if *infoFlag || *infoFlag2 { if *infoFlag || *infoFlag2 {
fmt.Printf("Scaffold version: %s", buildVersion) fmt.Printf("Scaffold version: %s", buildVersion)
fmt.Printf("Built: %s\n", buildTime) fmt.Printf("Built: %s\n", buildTime)
if runtime.GOOS == "windows" {
fmt.Println("It's a windows system")
}
at, err := availableProjectTypes(user_cfg_dir + sep + cfg_dir)
ifFerr("Unable to get available project types", err)
fmt.Printf("Avaiable types: %v\n", at)
fmt.Println("System says -- user config directory: ", user_cfg_dir)
os.Exit(0) os.Exit(0)
} }
@@ -61,7 +82,7 @@ func main() {
u, err := user.Current() u, err := user.Current()
ifFerr("Unable to get current user details", err) ifFerr("Unable to get current user details", err)
homeDir := u.HomeDir homeDir := u.HomeDir
scaffold_cfg_dir := homeDir + sep + cfg_dir scaffold_cfg_dir := user_cfg_dir + sep + cfg_dir
cfg_file := scaffold_cfg_dir + sep + cfg_filename cfg_file := scaffold_cfg_dir + sep + cfg_filename
if _, err := os.Stat(scaffold_cfg_dir); err != nil { if _, err := os.Stat(scaffold_cfg_dir); err != nil {
@@ -71,13 +92,21 @@ func main() {
if _, err := os.Stat(cfg_file); err != nil { if _, err := os.Stat(cfg_file); err != nil {
fmt.Println("No scaffold program configuration file found - creating a default file") fmt.Println("No scaffold program configuration file found - creating a default file")
// ok now do it cfgfd, err := os.Create(cfg_file)
ifFerr("Unable to create scaffold configuration file", err)
defer cfgfd.Close()
_, err = cfgfd.WriteString(cfg_content)
ifFerr("Unable to write scaffold configuration file content", err)
fmt.Println("Wrote new scaffold configuration file")
_, err = toml.Decode(cfg_content, &setup)
ifFerr("[Failed] to parse config file", err)
} else { } else {
fmt.Println("found config file") fmt.Println("found config file")
}
_, err = toml.DecodeFile(cfg_file, &setup) _, err = toml.DecodeFile(cfg_file, &setup)
ifFerr("[Failed] to parse config file", err) ifFerr("[Failed] to parse config file", err)
}
pts, err := availableProjectTypes(scaffold_cfg_dir) pts, err := availableProjectTypes(scaffold_cfg_dir)
ifFerr("Unable to read scaffold config directory contents", err) ifFerr("Unable to read scaffold config directory contents", err)
@@ -106,6 +135,8 @@ func main() {
_, err = toml.DecodeFile(projectCfgFile, &npsetup) _, err = toml.DecodeFile(projectCfgFile, &npsetup)
ifFerr("Unable to read configuration for requested project type", err) ifFerr("Unable to read configuration for requested project type", err)
//fmt.Printf("Found commands: %+v\n", npsetup.Commands)
//Setup and Ready to begin //Setup and Ready to begin
// Permissions are octal (ugo - user, group, other) // Permissions are octal (ugo - user, group, other)
@@ -158,17 +189,21 @@ func main() {
} }
fmt.Println("Project sample skeleton copied") fmt.Println("Project sample skeleton copied")
// run init commands // run your init commands from the projectType/scaffold-<PROJECTTYPE>.toml file
// make mod file for _, comm := range npsetup.Commands {
modpath := npsetup.Module_basepath + sep + newProjectName args := comm[1:]
_, err = exec.Command("go", "mod", "init", modpath).Output() if comm[0] == "go" && comm[1] == "mod" && comm[2] == "init" {
ifFerr("go mod init failed", err) args[2] = args[2] + sep + newProjectName
//fmt.Printf("executing: %+v -- %+v\n", comm, args)
// do a mod tidy to update the mod file }
_, err = exec.Command("go", "mod", "tidy").Output() _, err = exec.Command(comm[0], args...).Output()
ifFerr("Unable to update the mod file with a tidy", err) if err != nil {
fmt.Println("Updated go.mod") commstring := fmt.Sprintf("Unable to execute command: %+v", comm)
log.Fatalf("%s -- %s\n", commstring, err)
}
fmt.Printf("executing: %+v %+v\n", comm[0], args)
}
// setup git // setup git
if npsetup.Setup_git { if npsetup.Setup_git {
@@ -179,11 +214,30 @@ func main() {
nifd, err := os.Create(".gitignore") nifd, err := os.Create(".gitignore")
ifFerr("Unable to create .gitignore file", err) ifFerr("Unable to create .gitignore file", err)
defer nifd.Close() defer nifd.Close()
gi_content := fmt.Sprintf(gi_template, newProjectName)
var gi_content string
var gi_in []byte = []byte("bin/")
gisrc, err := os.Open("GITIGNORE")
if err == nil { // assume error means there is no source file
gi_in, err = io.ReadAll(gisrc)
if err != nil {
fmt.Println("Unable to read GITIGNORE, using default content")
}
gi_content = fmt.Sprintf("%s\n\n%s", newProjectName, string(gi_in))
_, err = nifd.WriteString(gi_content) _, err = nifd.WriteString(gi_content)
ifFerr("Unable to write .gitignore content", err) ifFerr("Unable to write .gitignore content", err)
fmt.Println("Wrote .gitignore content") fmt.Println("Wrote .gitignore content")
// remove GITIGNORE
err = os.Remove("GITIGNORE")
if err != nil {
fmt.Println("Unable to remove GITIGNORE file")
}
}
defer gisrc.Close()
// stage files // stage files
_, err = exec.Command("git", "add", ".").Output() _, err = exec.Command("git", "add", ".").Output()
ifFerr("Unable to stage files prior to first commit", err) ifFerr("Unable to stage files prior to first commit", err)
@@ -202,7 +256,7 @@ func main() {
// add remote to local repo // add remote to local repo
new_remote_repo := npsetup.Remote_User + ":" + npsetup.Remote_Location + "/" + newProjectName + ".git" new_remote_repo := npsetup.Remote_User + ":" + npsetup.Remote_Location + "/" + newProjectName + ".git"
_, err = exec.Command("git", "remote", "add", npsetup.Remote_Shortname, new_remote_repo).Output() _, err = exec.Command("git", "remote", "add", npsetup.Remote_Label, new_remote_repo).Output()
ifFerr("Unable to set git remote for new repo", err) ifFerr("Unable to set git remote for new repo", err)
fmt.Println("Added remote repo to git as origin") fmt.Println("Added remote repo to git as origin")
@@ -214,6 +268,19 @@ func main() {
fmt.Println("Pushed initial commit to remote") fmt.Println("Pushed initial commit to remote")
} }
if npsetup.Setup_gitea {
// create gitea repo
grcomm := fmt.Sprintf("%s:%s/%s.git", npsetup.Gitea_ssh_host_label, npsetup.Gitea_user_account, newProjectName)
_, err = exec.Command("git", "remote", "add", npsetup.Remote_Label_gitea, grcomm).Output()
ifFerr("Unable to set git remote for new project to gitea", err)
fmt.Println("Added remote repo to git for gitea as ", npsetup.Remote_Label_gitea)
// -u sets upstream tracking
_, err = exec.Command("git", "push", "-u", npsetup.Remote_Label_gitea, "main").Output()
ifFerr("Unable to push new project to gitea", err)
fmt.Println("Pushed new project to gitea")
}
} }
fmt.Printf("New %s Project: %s ready\n", projType, newProjectName) fmt.Printf("New %s Project: %s ready\n", projType, newProjectName)
@@ -241,6 +308,9 @@ func availableProjectTypes(adir string) ([]string, error) {
for _, e := range entries { for _, e := range entries {
if e.IsDir() { if e.IsDir() {
if e.Name() == ".git" {
continue
}
res = append(res, e.Name()) res = append(res, e.Name())
} }
} }