--- a/access/access.go Wed Sep 18 11:13:34 2019 -0500
+++ b/access/access.go Wed Dec 18 00:03:13 2019 -0600
@@ -1,6 +1,8 @@
@@ -15,17 +17,37 @@
-func Setup(reposPath, adminRepo string) error {
+func Setup(repositoriesPath, adminRepo string) error { + reposPath = repositoriesPath adminRepoName = adminRepo
adminRepoPath = filepath.Join(reposPath, adminRepo)
+ configPath, err := ioutil.TempFile("", "hgkeeper-hgweb-*.config") + hgwebConfigPath = configPath.Name() + if err := os.Remove(hgwebConfigPath); err != nil { + "failed to remove temporary hgweb config from %q", func AdminRepo() string {
@@ -34,6 +56,10 @@
+func HgwebConfigPath() string { // Refresh will try to reload the casbin model and policies followed by SSH
// keys. If there is an error it's possible that the casbin model and polcies
// could have been updated but the ssh keys were not.
@@ -49,6 +75,10 @@
+ if err := refreshHgWeb(reposPath); err != nil { --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgweb/files/hgweb.cgi Wed Dec 18 00:03:13 2019 -0600
@@ -0,0 +1,9 @@
+config = '{{.HGWEB_CONFIG}}' +from mercurial import demandimport; demandimport.enable() +from mercurial.hgweb import hgweb, wsgicgi +application = hgweb(config) +wsgicgi.launch(application) --- a/serve/command.go Wed Sep 18 11:13:34 2019 -0500
+++ b/serve/command.go Wed Dec 18 00:03:13 2019 -0600
@@ -1,30 +1,68 @@
+ log "github.com/sirupsen/logrus" "bitbucket.org/rw_grim/hgkeeper/access"
"bitbucket.org/rw_grim/hgkeeper/globals"
+ "bitbucket.org/rw_grim/hgkeeper/hgweb" "bitbucket.org/rw_grim/hgkeeper/ssh"
SSHAddr string `kong:"flag,name='ssh-listen-addr',env='HGK_SSH_LISTEN_ADDR',short='l',help='what address to listen on',default=':22222'"`
SSHHostKeysPath string `kong:"flag,name='ssh-host-keys-path',env='HGK_SSH_HOST_KEYS_PATH',short='H',help='the path where host keys are kept',default='host-keys'"`
+ HTTPAddr string `kong:"flag,name='http-listen-addr',env='HGK_HTTP_LISTEN_ADDR',help='what address the http server listens on',default=':8080'"` func (c *Command) Run(g *globals.Globals) error {
if err := access.Setup(g.ReposPath, g.AdminRepo); err != nil {
+ defer access.Teardown() - s, err := ssh.NewServer(c.SSHHostKeysPath, g.ReposPath, access.AdminRepoPath())
+ signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) + errChan := make(chan error, 10) + ssh, err := ssh.NewServer(c.SSHHostKeysPath, g.ReposPath, access.AdminRepoPath()) + hgweb, err := hgweb.NewServer(c.HTTPAddr) - err = s.Listen(c.SSHAddr)
+ if err := ssh.Listen(c.SSHAddr); err != nil { + if err := hgweb.Listen(); err != nil { + case s := <-signalChan: + log.Infof("Captured %v signal. Exiting...", s)
--- a/setup/resources/model.conf Wed Sep 18 11:13:34 2019 -0500
+++ b/setup/resources/model.conf Wed Dec 18 00:03:13 2019 -0600
@@ -2,7 +2,7 @@
# This model is based on the priorty example from the casbin documentation. It
# will evaluate polcies in a top to bottom approach accepting the first one that
-# matches. This means that you have to be care when defining your policies.
+# matches. This means that you have to be careful when defining your policies. # Say you would like to disable public access by default but then grant it to
# specific repositories later. This would need to be defined in the following
--- a/ssh/server.go Wed Sep 18 11:13:34 2019 -0500
+++ b/ssh/server.go Wed Dec 18 00:03:13 2019 -0600
@@ -109,6 +109,8 @@
func (s *Server) Listen(addr string) error {
+ log.Infof("ssh listening on %s", s.server.Addr) if err := s.server.ListenAndServe(); err != nil {
if err != ssh.ErrServerClosed {
@@ -117,3 +119,13 @@
+func (s *Server) Close() error { + if err := s.server.Close(); err != nil {