grim/hgkeeper

add a switch to disable the cache with it forced off right now as apparently nothing ever expires right now
//go:generate esc -o resources.go -pkg setup -include resources\/.+ -prefix resources/ .
package setup
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"text/template"
"golang.org/x/crypto/ssh"
"bitbucket.org/rw_grim/hgkeeper/globals"
"bitbucket.org/rw_grim/hgkeeper/hg"
)
type Command struct {
AdminUsername string `kong:"flag,name='admin-username',env='HGK_ADMIN_USERNAME',help='the name of the initial admin user',required='true'"`
AdminSSHPubkey string `kong:"flag,name='admin-pubkey',env='HGK_ADMIN_PUBKEY',help='the path to the ssh pubkey to use for the admin',required='true',type='existingfile'"`
}
var (
hgUsername = "hgkeeper"
commitMessage = "initial revision"
)
func runCmd(cmd *hg.Command) error {
output, err := cmd.Cmd().CombinedOutput()
if len(output) > 0 {
fmt.Printf("%s\n", output)
}
return err
}
func (c *Command) createAdminRepo(reposPath, adminRepo string) error {
path := filepath.Join(reposPath, adminRepo)
// create the admin repo
if err := runCmd(hg.Init(path)); err != nil {
return err
}
filenames := []string{}
// walk through the embeded resources and dump them into the directory
for name, data := range _escData {
// skip directories as we only add the ones were we have files
if data.isDir {
continue
}
// we're copying a regular file now, so figure out the paths so we can
// create them if necessary. We have to special case dothg because
// using .hg causes issues
var absname string
if strings.HasPrefix(name, "/dothg/") {
absname = filepath.Join(path, ".hg", name[7:])
} else {
absname = filepath.Join(path, name)
}
dirname := filepath.Dir(absname)
// if we don't have the directory create it
if _, err := os.Stat(dirname); err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(dirname, 0755); err != nil {
return err
}
} else {
return err
}
}
// If this is a template file, execute it
if filepath.Ext(name) == ".template" {
absname = absname[:len(absname)-len(".template")]
tplate, err := template.New(name).Parse(FSMustString(false, name))
if err != nil {
return err
}
file, err := os.Create(absname)
if err != nil {
return err
}
defer file.Close()
tplateData := struct {
AdminRepo string
AdminUsername string
}{
AdminRepo: "/" + adminRepo,
AdminUsername: c.AdminUsername,
}
if err := tplate.Execute(file, tplateData); err != nil {
fmt.Printf("error: %v\n", err)
return err
}
} else {
// If it's not a template just write it out
if err := ioutil.WriteFile(absname, FSMustByte(false, name), 0644); err != nil {
return err
}
}
rel, err := filepath.Rel(path, absname)
if err != nil {
return err
}
if !strings.HasPrefix(rel, fmt.Sprintf(".hg%c", filepath.Separator)) {
filenames = append(filenames, rel)
}
}
// create our keys directory
keysDir := filepath.Join(path, "keys")
if err := os.MkdirAll(keysDir, 0755); err != nil {
return err
}
// now copy the admin key into the keys directory
pubkey, err := ioutil.ReadFile(c.AdminSSHPubkey)
if err != nil {
return err
}
adminPubkey := filepath.Join(keysDir, c.AdminUsername)
relAdminPubkey, err := filepath.Rel(path, adminPubkey)
if err != nil {
return err
}
filenames = append(filenames, relAdminPubkey)
if err := ioutil.WriteFile(adminPubkey, pubkey, 0644); err != nil {
return err
}
// add our files
if err := runCmd(hg.Add(path, filenames...)); err != nil {
return err
}
// commit our changes
if err := runCmd(hg.Commit(path, hgUsername, commitMessage)); err != nil {
return err
}
return nil
}
func isPubkey(filename string) error {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
// ssh.ParsePublicKey wants the wire format which includes a size
// parameter. However, since every entry of an authorized_key file is
// just a straight public key, we can use ParseAuthorizedKey to check if
// the file is a public key.
if _, _, _, _, err := ssh.ParseAuthorizedKey(bytes); err != nil {
return err
}
return nil
}
func (c *Command) Run(g *globals.Globals) error {
// make sure the adminPubkey is an SSH pubkey as it is too easy to
// accidentally give the path to the private key rather than the public
// key, and we do no want the private key in the repository.
if err := isPubkey(c.AdminSSHPubkey); err != nil {
return fmt.Errorf("%s is not a public key file", c.AdminSSHPubkey)
}
// create the admin repo
if err := c.createAdminRepo(g.ReposPath, g.AdminRepo); err != nil {
// do clean up
return err
}
return nil
}