about summary refs log tree commit diff
path: root/tools/git-bootstrap
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2022-06-18 14:49:16 -0700
committerFranck Cuny <franck@fcuny.net>2022-06-18 14:50:55 -0700
commit16861d7609c5224795ce2fbeab4380bf65921283 (patch)
tree5584b03e9731c914b670d9a347b5201f0fb023bd /tools/git-bootstrap
parentfeat(tools/ssh-key-to-forge): import a SSH key to a forge (diff)
downloadworld-16861d7609c5224795ce2fbeab4380bf65921283.tar.gz
feat(tools/git-bootstrap): initialize a git repository with my defaults
This tool can be used to create a new git repository. It will create a
README, add the license file, and a few other things I expect.

Change-Id: I14123c8f5b7e2d23373a505a146d2c9f6c08615e
Reviewed-on: https://cl.fcuny.net/c/world/+/450
Tested-by: CI
Reviewed-by: Franck Cuny <franck@fcuny.net>
Diffstat (limited to '')
-rw-r--r--tools/git-bootstrap/README.org13
-rw-r--r--tools/git-bootstrap/go.mod3
-rw-r--r--tools/git-bootstrap/go.sum0
-rw-r--r--tools/git-bootstrap/main.go234
4 files changed, 250 insertions, 0 deletions
diff --git a/tools/git-bootstrap/README.org b/tools/git-bootstrap/README.org
new file mode 100644
index 0000000..74ced1b
--- /dev/null
+++ b/tools/git-bootstrap/README.org
@@ -0,0 +1,13 @@
+#+TITLE: git-bootstrap
+
+Bootstrap a new git repository with a license, readme, and create a remote repository on GitHub.
+
+* GitHub
+When running the command, a new remote repository is created on GitHub when a token is present in the git configuration, under github.bootstrap.
+
+To create a token:
+1. go to https://github.com/settings/tokens
+2. create a token with the scope for repo
+3. store the token with =git config --global github.bootstrap  <token>=
+
+To skip the creation of a repository on GitHub: =git-bootstrap -no-github <repo name>=
diff --git a/tools/git-bootstrap/go.mod b/tools/git-bootstrap/go.mod
new file mode 100644
index 0000000..11cfa6a
--- /dev/null
+++ b/tools/git-bootstrap/go.mod
@@ -0,0 +1,3 @@
+module golang.fcuny.net/git-bootstrap
+
+go 1.17
diff --git a/tools/git-bootstrap/go.sum b/tools/git-bootstrap/go.sum
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/git-bootstrap/go.sum
diff --git a/tools/git-bootstrap/main.go b/tools/git-bootstrap/main.go
new file mode 100644
index 0000000..23041ac
--- /dev/null
+++ b/tools/git-bootstrap/main.go
@@ -0,0 +1,234 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path"
+	"strings"
+	"time"
+)
+
+const (
+	defaultLicense = `Copyright (c) %d %s
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+`
+)
+
+const (
+	forgeBaseURL = "https://git.fcuny.net"
+)
+
+var (
+	publicRepo = flag.Bool("public", false, "create a public repository at git.fcuny.net")
+	userEmail  = flag.String("email", "franck@fcuny.net", "the author email that will be used for user.email")
+	userName   = flag.String("username", "Franck Cuny", "the author name that will be used for user.name")
+)
+
+// GitRepo is the repository
+type GitRepo struct {
+	Name string
+	Path string
+}
+
+func init() {
+	flag.Parse()
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, "Usage: %s\n", os.Args[0])
+		flag.PrintDefaults()
+	}
+}
+
+func main() {
+	if flag.Arg(0) != "" {
+		path := flag.Arg(0)
+		if _, err := os.Stat(path); err != nil {
+			if os.IsNotExist(err) {
+				if err = os.Mkdir(path, 0766); err != nil {
+					log.Fatalf("Failed to create directory %s: %v", path, err)
+				}
+			}
+			if err = os.Chdir(path); err != nil {
+				log.Fatalf("Failed to change working directory to %s: %v", path, err)
+			}
+		}
+	}
+	repo, err := newRepository()
+
+	if err != nil {
+		log.Fatalf("Failed to get working directory for the repository: %v", err)
+	}
+
+	repo.Create()
+	repo.SetUser()
+	repo.InitialCommit()
+	repo.AddLicense()
+	repo.AddReadme()
+	repo.Commit()
+}
+
+func newRepository() (GitRepo, error) {
+	cwd, err := os.Getwd()
+	if err != nil {
+		return GitRepo{}, err
+	}
+
+	baseName := path.Base(cwd)
+	repo := GitRepo{
+		Name: baseName,
+		Path: cwd,
+	}
+	return repo, nil
+}
+
+// Run runs a git command
+func (r *GitRepo) Run(args []string) ([]string, error) {
+	out, err := exec.Command("git", args...).Output()
+	if err != nil {
+		return []string{}, err
+	}
+	return strings.Split(string(out), "\n"), nil
+}
+
+// Create creates a git repository
+func (r *GitRepo) Create() {
+	if _, err := os.Stat(".git"); err != nil {
+		if os.IsNotExist(err) {
+			_, err := r.Run([]string{"init"})
+			if err != nil {
+				log.Fatalf("Error while creating the repository: %v", err)
+			}
+		} else {
+			log.Fatalf("Unexpected error: %v", err)
+		}
+	}
+}
+
+// SetUser sets the correct username and email in .git/config
+func (r *GitRepo) SetUser() {
+	config, err := r.Run([]string{"config", "--local", "user.name"})
+	if err != nil || config[0] != *userName {
+		if _, err := r.Run([]string{"config", "--local", "user.name", *userName}); err != nil {
+			log.Printf("failed to set the username to %s: %v", *userName, err)
+		}
+	}
+
+	config, err = r.Run([]string{"config", "--local", "user.email"})
+	if err != nil || config[0] != *userEmail {
+		if _, err := r.Run([]string{"config", "--local", "user.email", *userEmail}); err != nil {
+			log.Printf("failed to set the email to %s: %v", *userEmail, err)
+		}
+	}
+}
+
+// InitialCommit creates the initial commit in the new repository
+func (r *GitRepo) InitialCommit() {
+	if _, err := os.Stat(".git/refs/heads/main"); err != nil {
+		if os.IsNotExist(err) {
+			_, err := r.Run([]string{"commit", "--allow-empty", "-m", "Initial commit\n\nThis commit is empty on purpose."})
+			if err != nil {
+				log.Fatalf("Error while creating the initial commit: %v", err)
+			}
+		}
+	}
+}
+
+// AddLicense adds a license file to the repository
+func (r *GitRepo) AddLicense() {
+	licenseFiles := []string{"LICENSE", "license", "LICENSE.txt", "license.txt"}
+	licenseExists := skipIfExist(licenseFiles)
+
+	if licenseExists {
+		return
+	}
+
+	head := []byte(fmt.Sprintf(defaultLicense, time.Now().Year(), *userName))
+	f, err := os.Create("LICENSE.txt")
+	if err != nil {
+		log.Fatalf("Error while creating the license file: %v", err)
+	}
+	defer f.Close()
+
+	_, err = f.Write(head)
+	if err != nil {
+		log.Fatalf("Error while writing the license file: %v", err)
+	}
+}
+
+// AddReadme adds a README to the repository
+func (r *GitRepo) AddReadme() {
+	readmeFiles := []string{"README", "README.txt", "README.md", "README.org"}
+	readmeExists := skipIfExist(readmeFiles)
+
+	if readmeExists {
+		return
+	}
+
+	head := []byte(fmt.Sprintf("#+TITLE: %s", r.Name))
+	f, err := os.Create("README.org")
+	if err != nil {
+		log.Fatalf("Error while creating the README file: %v", err)
+	}
+	defer f.Close()
+	_, err = f.Write(head)
+	if err != nil {
+		log.Fatalf("Error while writing the README file: %v", err)
+	}
+}
+
+//Commit commit the files to the repository
+func (r *GitRepo) Commit() {
+	filesAdded := []string{}
+	for _, file := range []string{"README.org", "LICENSE.txt"} {
+		skipFile := false
+		if _, err := os.Stat(file); err != nil {
+			if os.IsNotExist(err) {
+				skipFile = true
+			} else {
+				log.Fatalf("Error while committing %s: %v", file, err)
+			}
+		}
+		if skipFile {
+			continue
+		}
+		_, err := r.Run([]string{"add", file})
+		if err != nil {
+			log.Fatalf("Error while adding %s: %v", file, err)
+		}
+		filesAdded = append(filesAdded, file)
+	}
+	commitMessage := fmt.Sprintf("Add %s", strings.Join(filesAdded, ", "))
+	if _, err := r.Run([]string{"commit", "-m", commitMessage}); err != nil {
+		log.Fatalf("Error while committing the base files: %v", err)
+	}
+}
+
+func skipIfExist(fileNames []string) bool {
+	for _, fileName := range fileNames {
+		_, err := os.Stat(fileName)
+		if err == nil {
+			return true
+		}
+	}
+	return false
+}