From 16861d7609c5224795ce2fbeab4380bf65921283 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sat, 18 Jun 2022 14:49:16 -0700 Subject: 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 --- tools/git-bootstrap/README.org | 13 +++ tools/git-bootstrap/go.mod | 3 + tools/git-bootstrap/go.sum | 0 tools/git-bootstrap/main.go | 234 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 250 insertions(+) create mode 100644 tools/git-bootstrap/README.org create mode 100644 tools/git-bootstrap/go.mod create mode 100644 tools/git-bootstrap/go.sum create mode 100644 tools/git-bootstrap/main.go (limited to 'tools/git-bootstrap') 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 = + +To skip the creation of a repository on GitHub: =git-bootstrap -no-github = 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 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 +} -- cgit 1.4.1