.keys | save --force path/to/authorized_keys
-```
-
-## License
-
-[MIT](LICENSE)
-
-Lots of inspiration and some starting code used from [wish](https://github.com/charmbracelet/wish) [(MIT)](https://github.com/charmbracelet/wish/blob/3e6f92a166118390484ce4a0904114b375b9e485/LICENSE) and [legit](https://github.com/icyphox/legit) [(MIT)](https://github.com/icyphox/legit/blob/bdfc973207a67a3b217c130520d53373d088763c/license).
diff --git a/assets/assets.go b/assets/assets.go
deleted file mode 100644
index a69ac02d5491fc7bc3f9c47a281c6784ca222345..0000000000000000000000000000000000000000
--- a/assets/assets.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package assets
-
-import "embed"
-
-var (
- //go:embed *.svg
- Icons embed.FS
- LinkIcon = must("link.svg")
- EmailIcon = must("email.svg")
- LogoIcon = must("ugit.svg")
-)
-
-func must(path string) []byte {
- content, err := Icons.ReadFile(path)
- if err != nil {
- panic(err)
- }
- return content
-}
diff --git a/assets/email.svg b/assets/email.svg
deleted file mode 100644
index 9703d2b72940ba8ffda26bc11273d7b39e070cac..0000000000000000000000000000000000000000
--- a/assets/email.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/assets/link.svg b/assets/link.svg
deleted file mode 100644
index f0a10d1a4723096e08d3ae4918221f12dd88d063..0000000000000000000000000000000000000000
--- a/assets/link.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/assets/ugit.svg b/assets/ugit.svg
deleted file mode 100644
index 18146d538504407a79f49a044e105ec919deb2d6..0000000000000000000000000000000000000000
--- a/assets/ugit.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
diff --git a/cmd/ugitd/args.go b/cmd/ugitd/args.go
index a1a0e46ffecc4167880a9d9a77c26d8338d56d4f..6b4d999c3abced3697291f6d9de4406bf6bab7f5 100644
--- a/cmd/ugitd/args.go
+++ b/cmd/ugitd/args.go
@@ -2,97 +2,19 @@ package main
import (
"flag"
- "fmt"
- "strings"
"github.com/peterbourgon/ff/v3"
"github.com/peterbourgon/ff/v3/ffyaml"
)
-type cliArgs struct {
- Debug bool
- RepoDir string
- SSH sshArgs
- HTTP httpArgs
- Meta metaArgs
- Profile profileArgs
-}
-
-type sshArgs struct {
- AuthorizedKeys string
- CloneURL string
- Port int
- HostKey string
+type args struct {
+ db string
}
-type httpArgs struct {
- CloneURL string
- Port int
-}
-
-type metaArgs struct {
- Title string
- Description string
-}
-
-type profileArgs struct {
- Username string
- Email string
- Links []profileLink
-}
-
-type profileLink struct {
- Name string
- URL string
-}
-
-func parseArgs(args []string) (c cliArgs, e error) {
+func parseArgs(args []string) (a args, e error) {
fs := flag.NewFlagSet("ugitd", flag.ContinueOnError)
fs.String("config", "ugit.yaml", "Path to config file")
-
- c = cliArgs{
- RepoDir: ".ugit",
- SSH: sshArgs{
- AuthorizedKeys: ".ssh/authorized_keys",
- CloneURL: "ssh://localhost:8448",
- Port: 8448,
- HostKey: ".ssh/ugit_ed25519",
- },
- HTTP: httpArgs{
- CloneURL: "http://localhost:8449",
- Port: 8449,
- },
- Meta: metaArgs{
- Title: "ugit",
- Description: "Minimal git server",
- },
- }
-
- fs.BoolVar(&c.Debug, "debug", c.Debug, "Debug logging")
- fs.StringVar(&c.RepoDir, "repo-dir", c.RepoDir, "Path to directory containing repositories")
- fs.StringVar(&c.SSH.AuthorizedKeys, "ssh.authorized-keys", c.SSH.AuthorizedKeys, "Path to authorized_keys")
- fs.StringVar(&c.SSH.CloneURL, "ssh.clone-url", c.SSH.CloneURL, "SSH clone URL base")
- fs.IntVar(&c.SSH.Port, "ssh.port", c.SSH.Port, "SSH port")
- fs.StringVar(&c.SSH.HostKey, "ssh.host-key", c.SSH.HostKey, "SSH host key (created if it doesn't exist)")
- fs.StringVar(&c.HTTP.CloneURL, "http.clone-url", c.HTTP.CloneURL, "HTTP clone URL base")
- fs.IntVar(&c.HTTP.Port, "http.port", c.HTTP.Port, "HTTP port")
- fs.StringVar(&c.Meta.Title, "meta.title", c.Meta.Title, "App title")
- fs.StringVar(&c.Meta.Description, "meta.description", c.Meta.Description, "App description")
- fs.StringVar(&c.Profile.Username, "profile.username", c.Profile.Username, "Username for index page")
- fs.StringVar(&c.Profile.Email, "profile.email", c.Profile.Email, "Email for index page")
- fs.Func("profile.links", "Link(s) for index page", func(s string) error {
- parts := strings.SplitN(s, ",", 2)
- if len(parts) != 2 {
- return fmt.Errorf("invalid profile link %q", s)
- }
- c.Profile.Links = append(c.Profile.Links, profileLink{
- Name: parts[0],
- URL: parts[1],
- })
- return nil
- })
-
- return c, ff.Parse(fs, args,
+ return a, ff.Parse(fs, args,
ff.WithEnvVarPrefix("UGIT"),
ff.WithConfigFileFlag("config"),
ff.WithAllowMissingConfigFile(true),
diff --git a/cmd/ugitd/main.go b/cmd/ugitd/main.go
index 3286e60ad9d56cec90db13ed2193ed617f68ea86..fc9a7bf721de9da0c45b41d5e058a5691a749d13 100644
--- a/cmd/ugitd/main.go
+++ b/cmd/ugitd/main.go
@@ -1,85 +1,14 @@
package main
import (
- "errors"
- "flag"
"fmt"
"os"
- "os/signal"
-
- "go.jolheiser.com/ugit/internal/http"
- "go.jolheiser.com/ugit/internal/ssh"
-
- "github.com/charmbracelet/log"
- "github.com/go-chi/chi/v5/middleware"
- "github.com/go-git/go-git/v5/utils/trace"
)
func main() {
args, err := parseArgs(os.Args[1:])
if err != nil {
- if errors.Is(err, flag.ErrHelp) {
- return
- }
panic(err)
}
-
- if args.Debug {
- trace.SetTarget(trace.Packet)
- log.SetLevel(log.DebugLevel)
- } else {
- middleware.DefaultLogger = http.NoopLogger
- ssh.DefaultLogger = ssh.NoopLogger
- }
-
- if err := os.MkdirAll(args.RepoDir, os.ModePerm); err != nil {
- panic(err)
- }
-
- sshSettings := ssh.Settings{
- AuthorizedKeys: args.SSH.AuthorizedKeys,
- CloneURL: args.SSH.CloneURL,
- Port: args.SSH.Port,
- HostKey: args.SSH.HostKey,
- RepoDir: args.RepoDir,
- }
- sshSrv, err := ssh.New(sshSettings)
- if err != nil {
- panic(err)
- }
- go func() {
- fmt.Printf("SSH listening on ssh://localhost:%d\n", sshSettings.Port)
- if err := sshSrv.ListenAndServe(); err != nil {
- panic(err)
- }
- }()
-
- httpSettings := http.Settings{
- Title: args.Meta.Title,
- Description: args.Meta.Description,
- CloneURL: args.HTTP.CloneURL,
- Port: args.HTTP.Port,
- RepoDir: args.RepoDir,
- Profile: http.Profile{
- Username: args.Profile.Username,
- Email: args.Profile.Email,
- },
- }
- for _, link := range args.Profile.Links {
- httpSettings.Profile.Links = append(httpSettings.Profile.Links, http.Link{
- Name: link.Name,
- URL: link.URL,
- })
- }
- httpSrv := http.New(httpSettings)
- go func() {
- fmt.Printf("HTTP listening on http://localhost:%d\n", httpSettings.Port)
- if err := httpSrv.ListenAndServe(); err != nil {
- panic(err)
- }
- }()
-
- ch := make(chan os.Signal, 1)
- signal.Notify(ch, os.Kill, os.Interrupt)
- <-ch
+ fmt.Println(args)
}
diff --git a/flake.lock b/flake.lock
index ac832b83c21fb3f2fae2a7a1c0326d192a066c42..43e6a8fb6322feb0996e91639f05bb8e85f73b7f 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,66 +1,98 @@
{
"nodes": {
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1694529238,
+ "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
+ "type": "github"
+ },
+ "original": {
+ "id": "flake-utils",
+ "type": "indirect"
+ }
+ },
"nixpkgs": {
"locked": {
- "lastModified": 1704161960,
- "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=",
+ "lastModified": 1701020769,
+ "narHash": "sha256-4YzCo7xMzkG/t/VlTHqOg9hvXCvqdWYDX/jpF0h+Wr8=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "63143ac2c9186be6d9da6035fa22620018c85932",
+ "rev": "b608fc233c0592210250974d1bb3c11dfaf95e58",
"type": "github"
},
"original": {
"owner": "nixos",
- "ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
- "root": {
- "inputs": {
- "nixpkgs": "nixpkgs",
- "tailwind-ctp": "tailwind-ctp",
- "tailwind-ctp-lsp": "tailwind-ctp-lsp"
- }
- },
- "tailwind-ctp": {
+ "nur": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
- "lastModified": 1695841587,
- "narHash": "sha256-fgiZd5AV+hi8Ne0bJ8SyAx5nppseW4aXJQEIDSr0VNA=",
+ "lastModified": 1700580516,
+ "narHash": "sha256-h72i6afGKreU+DjpZ6+qersarYYp4YjX+DBQ+MQkOG4=",
"ref": "refs/heads/main",
- "rev": "afca060674b20e0ccecde2d6fe88c887790219a5",
- "revCount": 1,
+ "rev": "a68a81cbc743e84aaee331ae7e58699398dd732d",
+ "revCount": 167,
"type": "git",
- "url": "https://git.jojodev.com/jolheiser/tailwind-ctp"
+ "url": "https://git.jojodev.com/jolheiser/nur"
},
"original": {
"type": "git",
- "url": "https://git.jojodev.com/jolheiser/tailwind-ctp"
+ "url": "https://git.jojodev.com/jolheiser/nur"
}
},
- "tailwind-ctp-lsp": {
+ "root": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs",
+ "nur": "nur",
+ "tailwind-ctp": "tailwind-ctp"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ },
+ "tailwind-ctp": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
- "lastModified": 1699401590,
- "narHash": "sha256-nx8ExuBRUux9eXSUgkWp1LJMvA3dmA76+2xggZjHTU0=",
- "ref": "refs/heads/master",
- "rev": "b321333ad08bf21db242f246b10ad4a50b8fc8a0",
- "revCount": 848,
+ "lastModified": 1695841587,
+ "narHash": "sha256-fgiZd5AV+hi8Ne0bJ8SyAx5nppseW4aXJQEIDSr0VNA=",
+ "ref": "refs/heads/main",
+ "rev": "afca060674b20e0ccecde2d6fe88c887790219a5",
+ "revCount": 1,
"type": "git",
- "url": "https://git.jojodev.com/jolheiser/tailwind-ctp-intellisense"
+ "url": "https://git.jojodev.com/jolheiser/tailwind-ctp"
},
"original": {
"type": "git",
- "url": "https://git.jojodev.com/jolheiser/tailwind-ctp-intellisense"
+ "url": "https://git.jojodev.com/jolheiser/tailwind-ctp"
}
}
},
diff --git a/flake.nix b/flake.nix
index cdcff1f7b1fcd2efa8f0122da5998941ba0aee32..e2c64454e7053bb255b1bd369ffad2a81877c774 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,163 +1,36 @@
{
- description = "Minimal git server";
-
inputs = {
- nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
+ nixpkgs.url = "github:nixos/nixpkgs";
+ nur = {
+ url = "git+https://git.jojodev.com/jolheiser/nur";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
tailwind-ctp = {
url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp";
inputs.nixpkgs.follows = "nixpkgs";
};
- tailwind-ctp-lsp = {
- url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp-intellisense";
- inputs.nixpkgs.follows = "nixpkgs";
- };
};
outputs = {
self,
+ flake-utils,
nixpkgs,
+ nur,
tailwind-ctp,
- tailwind-ctp-lsp,
} @ inputs: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
+ nur = inputs.nur.packages.${system};
tailwind-ctp = inputs.tailwind-ctp.packages.${system}.default;
- tailwind-ctp-lsp = inputs.tailwind-ctp-lsp.packages.${system}.default;
- ugit = pkgs.buildGoModule rec {
- pname = "ugitd";
- version = "0.0.1";
- src = pkgs.nix-gitignore.gitignoreSource [] (builtins.path {
- name = pname;
- path = ./.;
- });
- subPackages = ["cmd/ugitd"];
- vendorHash = "sha256-E4cwC6c0d+HvHldqGYiWdPEdS2fch6imvAXzxb2MMdY=";
- meta = with pkgs.lib; {
- description = "Minimal git server";
- homepage = "https://git.jolheiser.com/ugit";
- maintainers = with maintainers; [jolheiser];
- mainProgram = "ugitd";
- };
- };
in {
- packages.${system}.default = ugit;
devShells.${system}.default = pkgs.mkShell {
nativeBuildInputs = with pkgs; [
- go
- gopls
- templ
+ # go
+ # gopls
+ nur.templ
tailwind-ctp
- tailwind-ctp-lsp
- vscode-langservers-extracted
+ sqlc
];
- };
- nixosModules.default = {
- pkgs,
- lib,
- config,
- ...
- }: let
- cfg = config.services.ugit;
- configFile = pkgs.writeText "ugit.yaml" cfg.configFile;
- authorizedKeysFile = pkgs.writeText "ugit_keys" (builtins.concatStringsSep "\n" cfg.authorizedKeys);
- in {
- options = with lib; {
- services.ugit = {
- enable = mkEnableOption "Enable ugit";
-
- package = mkOption {
- type = types.package;
- description = "ugit package to use";
- default = ugit;
- };
-
- repoDir = mkOption {
- type = types.str;
- description = "where ugit stores repositories";
- default = "/var/lib/ugit/repos";
- };
-
- authorizedKeys = mkOption {
- type = types.listOf types.str;
- description = "list of keys to use for authorized_keys";
- default = [];
- };
-
- authorizedKeysFile = mkOption {
- type = types.str;
- description = "path to authorized_keys file ugit uses for auth";
- default = "/var/lib/ugit/authorized_keys";
- };
-
- hostKeyFile = mkOption {
- type = types.str;
- description = "path to host key file (will be created if it doesn't exist)";
- default = "/var/lib/ugit/ugit_ed25519";
- };
-
- configFile = mkOption {
- type = types.str;
- default = "";
- description = "config.yaml contents";
- };
-
- user = mkOption {
- type = types.str;
- default = "ugit";
- description = "User account under which ugit runs";
- };
-
- group = mkOption {
- type = types.str;
- default = "ugit";
- description = "Group account under which ugit runs";
- };
-
- debug = mkOption {
- type = types.bool;
- default = false;
- };
-
- openFirewall = mkOption {
- type = types.bool;
- default = false;
- };
- };
- };
- config = lib.mkIf cfg.enable {
- users.users."${cfg.user}" = {
- home = "/var/lib/ugit";
- createHome = true;
- group = "${cfg.group}";
- isSystemUser = true;
- isNormalUser = false;
- description = "user for ugit service";
- };
- users.groups."${cfg.group}" = {};
- networking.firewall = lib.mkIf cfg.openFirewall {
- allowedTCPPorts = [8448 8449];
- };
-
- systemd.services.ugit = {
- enable = true;
- script = let
- authorizedKeysPath =
- if (builtins.length cfg.authorizedKeys) > 0
- then authorizedKeysFile
- else cfg.authorizedKeysFile;
- args = ["--config=${configFile}" "--repo-dir=${cfg.repoDir}" "--ssh.authorized-keys=${authorizedKeysPath}" "--ssh.host-key=${cfg.hostKeyFile}"] ++ lib.optionals cfg.debug ["--debug"];
- in "${cfg.package}/bin/ugitd ${builtins.concatStringsSep " " args}";
- wantedBy = ["multi-user.target"];
- after = ["network-online.target"];
- serviceConfig = {
- User = cfg.user;
- Group = cfg.group;
- Restart = "always";
- RestartSec = "15";
- WorkingDirectory = "/var/lib/ugit";
- };
- };
- };
};
};
}
diff --git a/go.mod b/go.mod
index 3af97fb05ebeb5960238f3b62b6af33eee6ebb08..6b64d7e9c3a5dbe02a58bbf93c0f69c9e3d31c01 100644
--- a/go.mod
+++ b/go.mod
@@ -3,53 +3,49 @@
go 1.20
require (
- github.com/a-h/templ v0.2.513
- github.com/alecthomas/chroma/v2 v2.12.0
- github.com/charmbracelet/log v0.2.5
github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103
github.com/charmbracelet/wish v1.2.0
- github.com/dustin/go-humanize v1.0.1
- github.com/go-chi/chi/v5 v5.0.11
- github.com/go-git/go-billy/v5 v5.5.0
- github.com/go-git/go-git/v5 v5.11.0
- github.com/peterbourgon/ff/v3 v3.4.0
- github.com/yuin/goldmark v1.6.0
- github.com/yuin/goldmark-emoji v1.0.2
- github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
)
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
- github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
+ github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
+ github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/keygen v0.5.0 // indirect
github.com/charmbracelet/lipgloss v0.9.1 // indirect
- github.com/cloudflare/circl v1.3.6 // indirect
+ github.com/charmbracelet/log v0.2.5 // indirect
+ github.com/cloudflare/circl v1.3.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
- github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
+ github.com/go-git/go-billy/v5 v5.5.0 // indirect
+ github.com/go-git/go-git/v5 v5.10.0 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
- github.com/sergi/go-diff v1.3.1 // indirect
- github.com/skeema/knownhosts v1.2.1 // indirect
+ github.com/sergi/go-diff v1.1.0 // indirect
+ github.com/skeema/knownhosts v1.2.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
- golang.org/x/crypto v0.17.0 // indirect
- golang.org/x/mod v0.14.0 // indirect
- golang.org/x/net v0.19.0 // indirect
- golang.org/x/sys v0.15.0 // indirect
- golang.org/x/tools v0.16.1 // indirect
+ golang.org/x/crypto v0.14.0 // indirect
+ golang.org/x/net v0.17.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
+
+require (
+ github.com/mattn/go-isatty v0.0.18 // indirect
+ github.com/peterbourgon/ff/v3 v3.4.0
+ golang.org/x/mod v0.12.0 // indirect
+ golang.org/x/sys v0.13.0 // indirect
+ golang.org/x/tools v0.13.0 // indirect
+)
diff --git a/go.sum b/go.sum
index 3e36031585f6c948cf921175cd5e582c621c3fea..fe3c6a39859fa096422ff78f57f8cc5450d7f43d 100644
--- a/go.sum
+++ b/go.sum
@@ -3,16 +3,10 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
-github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
-github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
-github.com/a-h/templ v0.2.513 h1:ZmwGAOx4NYllnHy+FTpusc4+c5msoMpPIYX0Oy3dNqw=
-github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM=
-github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
-github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
-github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
-github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
-github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
+github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
+github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
+github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
+github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -29,39 +23,29 @@ github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 h1:wpHMERIN0pQZE635jWwT1dISgfjbpUcEma+fbPKSMCU=
github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103/go.mod h1:0Vm2/8yBljiLDnGJHU8ehswfawrEybGk33j5ssqKQVM=
github.com/charmbracelet/wish v1.2.0 h1:h5Wj9pr97IQz/l4gM5Xep2lXcY/YM+6O2RC2o3x0JIQ=
github.com/charmbracelet/wish v1.2.0/go.mod h1:JX3fC+178xadJYAhPu6qWtVDpJTwpnFvpdjz9RKJlUE=
+github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
-github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
-github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
-github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
-github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
-github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
-github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
-github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
-github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
-github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
+github.com/go-git/go-git/v5 v5.10.0 h1:F0x3xXrAWmhwtzoCokU4IMPcBdncG+HAAqi9FcOOjbQ=
+github.com/go-git/go-git/v5 v5.10.0/go.mod h1:1FOZ/pQnqw24ghP2n7cunVl0ON55BsjPYvhWHvZGhoo=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
@@ -73,8 +57,10 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
+github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
+github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
+github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -95,39 +81,30 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
-github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
-github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
+github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
+github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
-github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
+github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
+github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
-github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
-github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
-github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
-github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
-github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
-golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
+golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -135,12 +112,12 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
-golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -155,15 +132,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
+golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -171,13 +148,13 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
-golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
+golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
+golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -185,8 +162,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/git/git.go b/internal/git/git.go
deleted file mode 100644
index 141ced54eafa7de63a9c257624aefb7651ed57af..0000000000000000000000000000000000000000
--- a/internal/git/git.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package git
-
-import (
- "errors"
- "io/fs"
- "os"
- "path/filepath"
- "sort"
-
- "github.com/dustin/go-humanize"
- "github.com/go-git/go-git/v5"
- "github.com/go-git/go-git/v5/plumbing"
- "github.com/go-git/go-git/v5/plumbing/object"
-)
-
-// EnsureRepo ensures that the repo exists in the given directory
-func EnsureRepo(dir string, repo string) error {
- exists, err := PathExists(dir)
- if err != nil {
- return err
- }
- if !exists {
- err = os.MkdirAll(dir, os.ModeDir|os.FileMode(0o700))
- if err != nil {
- return err
- }
- }
- rp := filepath.Join(dir, repo)
- exists, err = PathExists(rp)
- if err != nil {
- return err
- }
- if !exists {
- _, err := git.PlainInit(rp, true)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// PathExists checks if a path exists and returns true if it does
-func PathExists(path string) (bool, error) {
- _, err := os.Stat(path)
- if err == nil {
- return true, nil
- }
- if errors.Is(err, fs.ErrNotExist) {
- return false, nil
- }
- return true, err
-}
-
-func (r Repo) Tree(ref string) (*object.Tree, error) {
- g, err := r.Git()
- if err != nil {
- return nil, err
- }
-
- hash, err := g.ResolveRevision(plumbing.Revision(ref))
- if err != nil {
- return nil, err
- }
-
- c, err := g.CommitObject(*hash)
- if err != nil {
- return nil, err
- }
-
- return c.Tree()
-}
-
-type FileInfo struct {
- Path string
- IsDir bool
- Mode string
- Size string
-}
-
-func (f FileInfo) Name() string {
- return filepath.Base(f.Path)
-}
-
-func (r Repo) Dir(ref, path string) ([]FileInfo, error) {
- t, err := r.Tree(ref)
- if err != nil {
- return nil, err
- }
- if path != "" {
- t, err = t.Tree(path)
- if err != nil {
- return nil, err
- }
- }
-
- fis := make([]FileInfo, 0)
- for _, entry := range t.Entries {
- fm, err := entry.Mode.ToOSFileMode()
- if err != nil {
- return nil, err
- }
- size, err := t.Size(entry.Name)
- if err != nil {
- return nil, err
- }
- fis = append(fis, FileInfo{
- Path: filepath.Join(path, entry.Name),
- IsDir: fm.IsDir(),
- Mode: fm.String(),
- Size: humanize.Bytes(uint64(size)),
- })
- }
- sort.Slice(fis, func(i, j int) bool {
- fi1 := fis[i]
- fi2 := fis[j]
- return (fi1.IsDir && !fi2.IsDir) || fi1.Name() < fi2.Name()
- })
-
- return fis, nil
-}
-
-func (r Repo) FileContent(ref, file string) (string, error) {
- t, err := r.Tree(ref)
- if err != nil {
- return "", err
- }
-
- f, err := t.File(file)
- if err != nil {
- return "", err
- }
-
- content, err := f.Contents()
- if err != nil {
- return "", err
- }
-
- return content, nil
-}
diff --git a/internal/git/meta.go b/internal/git/meta.go
deleted file mode 100644
index 04f7f1b21ded58c765f75bb6db3ab0d23898de18..0000000000000000000000000000000000000000
--- a/internal/git/meta.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package git
-
-import (
- "encoding/json"
- "errors"
- "io/fs"
- "os"
- "path/filepath"
-)
-
-type RepoMeta struct {
- Description string `json:"description"`
- Private bool `json:"private"`
-}
-
-func (m *RepoMeta) Update(meta RepoMeta) error {
- data, err := json.Marshal(meta)
- if err != nil {
- return err
- }
- return json.Unmarshal(data, m)
-}
-
-func (r Repo) metaPath() string {
- return filepath.Join(r.path, "ugit.json")
-}
-
-func (r Repo) SaveMeta() error {
- // Compatibility with gitweb, because why not
- // Ignoring the error because it's not technically detrimental to ugit
- desc, err := os.Create(filepath.Join(r.path, "description"))
- if err == nil {
- defer desc.Close()
- desc.WriteString(r.Meta.Description)
- }
-
- fi, err := os.Create(r.metaPath())
- if err != nil {
- return err
- }
- defer fi.Close()
- return json.NewEncoder(fi).Encode(r.Meta)
-}
-
-func ensureJSONFile(path string) error {
- _, err := os.Stat(path)
- if err == nil {
- return nil
- }
- if !errors.Is(err, fs.ErrNotExist) {
- return err
- }
- fi, err := os.Create(path)
- if err != nil {
- return err
- }
- defer fi.Close()
- if _, err := fi.WriteString(`{"private":true}`); err != nil {
- return err
- }
- return nil
-}
diff --git a/internal/git/protocol.go b/internal/git/protocol.go
deleted file mode 100644
index 60b927dd25e02d5590034310892558834e761c78..0000000000000000000000000000000000000000
--- a/internal/git/protocol.go
+++ /dev/null
@@ -1,223 +0,0 @@
-package git
-
-import (
- "bufio"
- "context"
- "fmt"
- "io"
- "strconv"
- "strings"
-
- "github.com/go-git/go-billy/v5/osfs"
- "github.com/go-git/go-git/v5"
- "github.com/go-git/go-git/v5/plumbing/format/pktline"
- "github.com/go-git/go-git/v5/plumbing/protocol/packp"
- "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
- "github.com/go-git/go-git/v5/plumbing/serverinfo"
- "github.com/go-git/go-git/v5/plumbing/transport"
- "github.com/go-git/go-git/v5/plumbing/transport/server"
- "github.com/go-git/go-git/v5/storage/filesystem"
- "github.com/go-git/go-git/v5/utils/ioutil"
-)
-
-type ReadWriteContexter interface {
- io.ReadWriteCloser
- Context() context.Context
-}
-
-type Protocol struct {
- endpoint *transport.Endpoint
- server transport.Transport
-}
-
-func NewProtocol(repoPath string) (Protocol, error) {
- endpoint, err := transport.NewEndpoint("/")
- if err != nil {
- return Protocol{}, err
- }
- fs := osfs.New(repoPath)
- loader := server.NewFilesystemLoader(fs)
- gitServer := server.NewServer(loader)
- return Protocol{
- endpoint: endpoint,
- server: gitServer,
- }, nil
-}
-
-func (p Protocol) HTTPInfoRefs(rwc ReadWriteContexter) error {
- session, err := p.server.NewUploadPackSession(p.endpoint, nil)
- if err != nil {
- return err
- }
- defer ioutil.CheckClose(rwc, &err)
- return p.infoRefs(rwc, session, "# service=git-upload-pack")
-}
-
-func (p Protocol) infoRefs(rwc ReadWriteContexter, session transport.UploadPackSession, prefix string) error {
- ar, err := session.AdvertisedReferencesContext(rwc.Context())
- if err != nil {
- return err
- }
-
- if prefix != "" {
- ar.Prefix = [][]byte{
- []byte(prefix),
- pktline.Flush,
- }
- }
-
- if err := ar.Encode(rwc); err != nil {
- return err
- }
-
- return nil
-}
-
-func (p Protocol) HTTPUploadPack(rwc ReadWriteContexter) error {
- return p.uploadPack(rwc, false)
-}
-
-func (p Protocol) SSHUploadPack(rwc ReadWriteContexter) error {
- return p.uploadPack(rwc, true)
-}
-
-func (p Protocol) uploadPack(rwc ReadWriteContexter, ssh bool) error {
- session, err := p.server.NewUploadPackSession(p.endpoint, nil)
- if err != nil {
- return err
- }
- defer ioutil.CheckClose(rwc, &err)
-
- if ssh {
- if err := p.infoRefs(rwc, session, ""); err != nil {
- return err
- }
- }
-
- req := packp.NewUploadPackRequest()
- if err := req.Decode(rwc); err != nil {
- return err
- }
-
- var resp *packp.UploadPackResponse
- resp, err = session.UploadPack(rwc.Context(), req)
- if err != nil {
- return err
- }
-
- if err := resp.Encode(rwc); err != nil {
- return fmt.Errorf("could not encode upload pack: %w", err)
- }
-
- return nil
-}
-
-func (p Protocol) SSHReceivePack(rwc ReadWriteContexter, repo *Repo) error {
- buf := bufio.NewReader(rwc)
-
- session, err := p.server.NewReceivePackSession(p.endpoint, nil)
- if err != nil {
- return err
- }
-
- ar, err := session.AdvertisedReferencesContext(rwc.Context())
- if err != nil {
- return fmt.Errorf("internal error in advertised references: %w", err)
- }
- _ = ar.Capabilities.Set(capability.PushOptions)
- _ = ar.Capabilities.Set("no-thin")
-
- if err := ar.Encode(rwc); err != nil {
- return fmt.Errorf("error in advertised references encoding: %w", err)
- }
-
- req := packp.NewReferenceUpdateRequest()
- _ = req.Capabilities.Set(capability.ReportStatus)
- if err := req.Decode(buf); err != nil {
- // FIXME this is a hack, but go-git doesn't accept a 0000 if there are no refs to update
- if !strings.EqualFold(err.Error(), "capabilities delimiter not found") {
- return fmt.Errorf("error decoding: %w", err)
- }
- }
-
- // FIXME also a hack, if the next bytes are PACK then we have a packfile, otherwise assume it's push options
- peek, err := buf.Peek(4)
- if err != nil {
- return err
- }
- if string(peek) != "PACK" {
- s := pktline.NewScanner(buf)
- for s.Scan() {
- val := string(s.Bytes())
- if val == "" {
- break
- }
- if s.Err() != nil {
- return s.Err()
- }
- parts := strings.SplitN(val, "=", 2)
- req.Options = append(req.Options, &packp.Option{
- Key: parts[0],
- Value: parts[1],
- })
- }
- }
-
- if err := handlePushOptions(repo, req.Options); err != nil {
- return fmt.Errorf("could not handle push options: %w", err)
- }
-
- // FIXME if there are only delete commands, there is no packfile and ReceivePack will block forever
- noPack := true
- for _, c := range req.Commands {
- if c.Action() != packp.Delete {
- noPack = false
- break
- }
- }
- if noPack {
- req.Packfile = nil
- }
-
- rs, err := session.ReceivePack(rwc.Context(), req)
- if err != nil {
- return fmt.Errorf("error in receive pack: %w", err)
- }
-
- if err := rs.Encode(rwc); err != nil {
- return fmt.Errorf("could not encode receive pack: %w", err)
- }
-
- return nil
-}
-
-func handlePushOptions(repo *Repo, opts []*packp.Option) error {
- var changed bool
- for _, opt := range opts {
- switch strings.ToLower(opt.Key) {
- case "desc", "description":
- changed = repo.Meta.Description != opt.Value
- repo.Meta.Description = opt.Value
- case "private":
- private, err := strconv.ParseBool(opt.Value)
- if err != nil {
- continue
- }
- changed = repo.Meta.Private != private
- repo.Meta.Private = private
- }
- }
- if changed {
- return repo.SaveMeta()
- }
- return nil
-}
-
-func UpdateServerInfo(repo string) error {
- r, err := git.PlainOpen(repo)
- if err != nil {
- return err
- }
- fs := r.Storer.(*filesystem.Storage).Filesystem()
- return serverinfo.UpdateServerInfo(r.Storer, fs)
-}
diff --git a/internal/git/repo.go b/internal/git/repo.go
deleted file mode 100644
index 6b7c997b668328f2b5ae3c70f949cbaeae2883b8..0000000000000000000000000000000000000000
--- a/internal/git/repo.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package git
-
-import (
- "encoding/json"
- "errors"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/go-git/go-git/v5"
- "github.com/go-git/go-git/v5/plumbing"
- "github.com/go-git/go-git/v5/plumbing/object"
-)
-
-type Repo struct {
- path string
- Meta RepoMeta
-}
-
-func (r Repo) Name() string {
- return strings.TrimSuffix(filepath.Base(r.path), ".git")
-}
-
-func NewRepo(dir, name string) (*Repo, error) {
- if !strings.HasSuffix(name, ".git") {
- name += ".git"
- }
- r := &Repo{
- path: filepath.Join(dir, name),
- }
-
- _, err := os.Stat(r.path)
- if err != nil {
- return nil, err
- }
-
- if err := ensureJSONFile(r.metaPath()); err != nil {
- return nil, err
- }
- fi, err := os.Open(r.metaPath())
- if err != nil {
- return nil, err
- }
- defer fi.Close()
-
- if err := json.NewDecoder(fi).Decode(&r.Meta); err != nil {
- return nil, err
- }
-
- return r, nil
-}
-
-// DefaultBranch returns the branch referenced by HEAD, setting it if needed
-func (r Repo) DefaultBranch() (string, error) {
- repo, err := r.Git()
- if err != nil {
- return "", err
- }
-
- ref, err := repo.Head()
- if err != nil {
- if !errors.Is(err, plumbing.ErrReferenceNotFound) {
- return "", err
- }
- brs, err := repo.Branches()
- if err != nil {
- return "", err
- }
- defer brs.Close()
- fb, err := brs.Next()
- if err != nil {
- return "", err
- }
- // Rename the default branch to the first branch available
- ref = fb
- sym := plumbing.NewSymbolicReference(plumbing.HEAD, fb.Name())
- if err := repo.Storer.SetReference(sym); err != nil {
- return "", err
- }
- }
-
- return strings.TrimPrefix(ref.Name().String(), "refs/heads/"), nil
-}
-
-// Git allows access to the git repository
-func (r Repo) Git() (*git.Repository, error) {
- return git.PlainOpen(r.path)
-}
-
-// LastCommit returns the last commit of the repo
-func (r Repo) LastCommit() (*object.Commit, error) {
- repo, err := r.Git()
- if err != nil {
- return nil, err
- }
-
- head, err := repo.Head()
- if err != nil {
- return nil, err
- }
-
- return repo.CommitObject(head.Hash())
-}
diff --git a/internal/html/base.templ b/internal/html/base.templ
deleted file mode 100644
index 5d2e3b99083bb603a8b0e534aed2dbf95b5a2444..0000000000000000000000000000000000000000
--- a/internal/html/base.templ
+++ /dev/null
@@ -1,24 +0,0 @@
-package html
-
-type BaseContext struct {
- Title string
- Description string
-}
-
-templ base(bc BaseContext) {
-
-
-
- { bc.Title }
-
-
-
-
-
-
-
- { children... }
-
-
-}
-
diff --git a/internal/html/base_templ.go b/internal/html/base_templ.go
deleted file mode 100644
index d5f04c2c21f2edcb34afc5fc6fd467d4f58d8d9f..0000000000000000000000000000000000000000
--- a/internal/html/base_templ.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.2.501
-package html
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import "context"
-import "io"
-import "bytes"
-
-type BaseContext struct {
- Title string
- Description string
-}
-
-func base(bc BaseContext) templ.Component {
- return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var2 string
- templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(bc.Title)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `base.templ`, Line: 11, Col: 20}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
- }
- return templ_7745c5c3_Err
- })
-}
diff --git a/internal/html/chroma.go b/internal/html/chroma.go
deleted file mode 100644
index 1ea0601d93977cad0f9414c5d3794465b620f191..0000000000000000000000000000000000000000
--- a/internal/html/chroma.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package html
-
-import (
- "io"
-
- "github.com/alecthomas/chroma/v2"
- "github.com/alecthomas/chroma/v2/formatters/html"
- "github.com/alecthomas/chroma/v2/lexers"
- "github.com/alecthomas/chroma/v2/styles"
-)
-
-var (
- Formatter = html.New(
- html.WithLineNumbers(true),
- html.WithLinkableLineNumbers(true, "L"),
- html.WithClasses(true),
- html.LineNumbersInTable(true),
- )
- Code = code{}
-)
-
-type code struct{}
-
-func (c code) Convert(source []byte, fileName string, writer io.Writer) error {
- lexer := lexers.Match(fileName)
- if lexer == nil {
- lexer = lexers.Fallback
- }
- lexer = chroma.Coalesce(lexer)
-
- style := styles.Get("catppuccin-mocha")
- if style == nil {
- style = styles.Fallback
- }
-
- iter, err := lexer.Tokenise(nil, string(source))
- if err != nil {
- return err
- }
-
- return Formatter.Format(writer, style, iter)
-}
diff --git a/internal/html/generate.css b/internal/html/generate.css
deleted file mode 100644
index 34d0aefbe66512d4dc681fa85c49fd965d10370f..0000000000000000000000000000000000000000
--- a/internal/html/generate.css
+++ /dev/null
@@ -1,44 +0,0 @@
-.markdown * {
- all: revert;
- color: rgb(var(--ctp-text));
-}
-
-.markdown a {
- color: rgb(var(--ctp-blue));
- text-decoration-line: underline;
- text-decoration-style: dashed;
-}
-
-.markdown a:hover {
- text-decoration-style: solid;
-}
-
-.chroma {
- font-size: small;
-}
-
-.chroma * {
- background-color: rgb(var(--ctp-base)) !important;
-}
-
-.chroma table {
- border-spacing: 5px 0 !important;
-}
-
-.chroma .lnt {
- color: rgb(var(--ctp-subtext1)) !important;
-}
-
-.chroma .lnt:target,
-.chroma .lnt:focus {
- color: rgb(var(--ctp-subtext0)) !important;
-}
-
-.chroma .line {
- white-space: break-spaces;
-}
-
-.chroma .line.active,
-.chroma .line.active * {
- background: rgb(var(--ctp-surface0)) !important;
-}
\ No newline at end of file
diff --git a/internal/html/generate.go b/internal/html/generate.go
deleted file mode 100644
index b4c1a441ecbcc15257f9b2f3bc05fac5e7afd1d4..0000000000000000000000000000000000000000
--- a/internal/html/generate.go
+++ /dev/null
@@ -1,96 +0,0 @@
-//go:build generate
-
-package main
-
-import (
- "bytes"
- _ "embed"
- "fmt"
- "go/format"
- "os"
- "os/exec"
-
- "go.jolheiser.com/ugit/internal/html"
-
- "github.com/alecthomas/chroma/v2/styles"
-)
-
-var (
- tailwindCSS = `
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-`
- //go:embed generate.css
- otherCSS string
-)
-
-//go:generate templ generate
-//go:generate go run generate.go
-func main() {
- if err := tailwind(); err != nil {
- panic(err)
- }
-}
-
-func tailwind() error {
- fmt.Println("generating tailwind...")
-
- tmp, err := os.CreateTemp(os.TempDir(), "ugit-tailwind*")
- if err != nil {
- return err
- }
- defer os.Remove(tmp.Name())
- if _, err := tmp.WriteString(tailwindCSS + otherCSS); err != nil {
- return err
- }
-
- fmt.Println("generating chroma styles...")
-
- latte := styles.Get("catppuccin-latte")
- if err := html.Formatter.WriteCSS(tmp, latte); err != nil {
- return err
- }
-
- tmp.WriteString("@media (prefers-color-scheme: dark) {")
- mocha := styles.Get("catppuccin-mocha")
- if err := html.Formatter.WriteCSS(tmp, mocha); err != nil {
- return err
- }
- tmp.WriteString("}")
-
- fmt.Println("finished generating chroma styles")
-
- tmp.Close()
-
- styles, err := os.Create("tailwind.go")
- if err != nil {
- return err
- }
- defer styles.Close()
-
- var buf bytes.Buffer
- cmd := exec.Command("tailwind-ctp", "-i", tmp.Name(), "--minify")
- cmd.Stdout = &buf
- if err := cmd.Run(); err != nil {
- return err
- }
-
- code := fmt.Sprintf(`// Code generated by generate.go - DO NOT EDIT.
-package html
-
- import "net/http"
-
- func TailwindHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "text/css")
- w.Write([]byte(%q))
- }`, buf.String())
- formatted, err := format.Source([]byte(code))
- if err != nil {
- return err
- }
- styles.Write(formatted)
-
- fmt.Println("finished generating tailwind")
- return nil
-}
diff --git a/internal/html/index.templ b/internal/html/index.templ
deleted file mode 100644
index 96d204d1b66b31b1def91fae5b3233b30740de91..0000000000000000000000000000000000000000
--- a/internal/html/index.templ
+++ /dev/null
@@ -1,72 +0,0 @@
-package html
-
-import "go.jolheiser.com/ugit/internal/git"
-import "github.com/dustin/go-humanize"
-import "go.jolheiser.com/ugit/assets"
-
-type IndexContext struct {
- BaseContext
- Profile IndexProfile
- CloneURL string
- Repos []*git.Repo
-}
-
-type IndexProfile struct {
- Username string
- Email string
- Links []IndexLink
-}
-
-type IndexLink struct {
- Name string
- URL string
-}
-
-func lastCommit(repo *git.Repo, human bool) string {
- c, err := repo.LastCommit()
- if err != nil {
- return ""
- }
- if human {
- return humanize.Time(c.Author.When)
- }
- return c.Author.When.Format("01/02/2006 03:04:05 PM")
-}
-
-templ Index(ic IndexContext) {
- @base(ic.BaseContext) {
-
- { ic.Title }
- { ic.Description }
-
-
-
- if ic.Profile.Username != "" {
-
{ `@` + ic.Profile.Username }
- }
- if ic.Profile.Email != "" {
-
-
@templ.Raw(string(assets.EmailIcon))
-
john.olheiser@gmail.com
-
- }
-
-
- for _, link := range ic.Profile.Links {
-
-
@templ.Raw(string(assets.LinkIcon))
-
{ link.Name }
-
- }
-
-
- for _, repo := range ic.Repos {
-
-
{ repo.Meta.Description }
-
{ lastCommit(repo, true) }
- }
-
-
- }
-}
-
diff --git a/internal/html/index_templ.go b/internal/html/index_templ.go
deleted file mode 100644
index 15f200494b1434b8c9a070a0de0badae6b10252b..0000000000000000000000000000000000000000
--- a/internal/html/index_templ.go
+++ /dev/null
@@ -1,261 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.2.501
-package html
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import "context"
-import "io"
-import "bytes"
-
-import "go.jolheiser.com/ugit/internal/git"
-import "github.com/dustin/go-humanize"
-import "go.jolheiser.com/ugit/assets"
-
-type IndexContext struct {
- BaseContext
- Profile IndexProfile
- CloneURL string
- Repos []*git.Repo
-}
-
-type IndexProfile struct {
- Username string
- Email string
- Links []IndexLink
-}
-
-type IndexLink struct {
- Name string
- URL string
-}
-
-func lastCommit(repo *git.Repo, human bool) string {
- c, err := repo.LastCommit()
- if err != nil {
- return ""
- }
- if human {
- return humanize.Time(c.Author.When)
- }
- return c.Author.When.Format("01/02/2006 03:04:05 PM")
-}
-
-func Index(ic IndexContext) templ.Component {
- return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(ic.Title)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 38, Col: 53}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(ic.Description)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 39, Col: 53}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if ic.Profile.Username != "" {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var5 string
- templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(`@` + ic.Profile.Username)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 44, Col: 56}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- if ic.Profile.Email != "" {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, link := range ic.Profile.Links {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- for _, repo := range ic.Repos {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var11 string
- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(repo.Meta.Description)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 64, Col: 66}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var12 string
- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(lastCommit(repo, true))
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 65, Col: 100}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
- }
- return templ_7745c5c3_Err
- })
- templ_7745c5c3_Err = base(ic.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
- }
- return templ_7745c5c3_Err
- })
-}
diff --git a/internal/html/markdown.go b/internal/html/markdown.go
deleted file mode 100644
index e109162de66398d42dc38e2d8442c37f28b3d7e4..0000000000000000000000000000000000000000
--- a/internal/html/markdown.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package html
-
-import (
- "bytes"
- "path/filepath"
-
- "go.jolheiser.com/ugit/internal/git"
-
- chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
- "github.com/yuin/goldmark"
- emoji "github.com/yuin/goldmark-emoji"
- highlighting "github.com/yuin/goldmark-highlighting/v2"
- "github.com/yuin/goldmark/extension"
- "github.com/yuin/goldmark/parser"
- goldmarkhtml "github.com/yuin/goldmark/renderer/html"
-)
-
-var Markdown = goldmark.New(
- goldmark.WithRendererOptions(
- goldmarkhtml.WithUnsafe(),
- ),
- goldmark.WithParserOptions(
- parser.WithAutoHeadingID(),
- ),
- goldmark.WithExtensions(
- extension.GFM,
- emoji.Emoji,
- highlighting.NewHighlighting(
- highlighting.WithStyle("catppuccin-mocha"),
- highlighting.WithFormatOptions(
- chromahtml.WithClasses(true),
- chromahtml.WithLineNumbers(true),
- chromahtml.WithLinkableLineNumbers(true, "md-"),
- chromahtml.LineNumbersInTable(true),
- ),
- ),
- ),
-)
-
-func Readme(repo *git.Repo, ref, path string) (string, error) {
- var readme string
- var err error
- for _, md := range []string{"README.md", "readme.md"} {
- readme, err = repo.FileContent(ref, filepath.Join(path, md))
- if err == nil {
- break
- }
- }
-
- if readme != "" {
- var buf bytes.Buffer
- if err := Markdown.Convert([]byte(readme), &buf); err != nil {
- return "", err
- }
- return buf.String(), nil
- }
-
- for _, md := range []string{"README.txt", "README", "readme.txt", "readme"} {
- readme, err = repo.FileContent(ref, filepath.Join(path, md))
- if err == nil {
- return readme, nil
- }
- }
-
- return "", nil
-}
diff --git a/internal/html/readme.templ b/internal/html/readme.templ
deleted file mode 100644
index cdcc3814d800f94f227178690cdc564f737d5afb..0000000000000000000000000000000000000000
--- a/internal/html/readme.templ
+++ /dev/null
@@ -1,10 +0,0 @@
-package html
-
-type ReadmeComponentContext struct {
- Markdown string
-}
-
-templ readmeComponent(rcc ReadmeComponentContext) {
- @templ.Raw(rcc.Markdown)
-}
-
diff --git a/internal/html/readme_templ.go b/internal/html/readme_templ.go
deleted file mode 100644
index 812b724ecb6b3a666d0914fab8d601257eaf8d13..0000000000000000000000000000000000000000
--- a/internal/html/readme_templ.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.2.501
-package html
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import "context"
-import "io"
-import "bytes"
-
-type ReadmeComponentContext struct {
- Markdown string
-}
-
-func readmeComponent(rcc ReadmeComponentContext) templ.Component {
- return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(rcc.Markdown).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
- }
- return templ_7745c5c3_Err
- })
-}
diff --git a/internal/html/repo.templ b/internal/html/repo.templ
deleted file mode 100644
index 6bca6011774bef42cf2d01c3e344d3cecafce2bf..0000000000000000000000000000000000000000
--- a/internal/html/repo.templ
+++ /dev/null
@@ -1,23 +0,0 @@
-package html
-
-import "fmt"
-
-type RepoHeaderComponentContext struct {
- Name string
- Ref string
- Description string
-}
-
-templ repoHeaderComponent(rhcc RepoHeaderComponentContext) {
- if rhcc.Name != "" {
-
- }
- { rhcc.Description }
-}
-
diff --git a/internal/html/repo_file.templ b/internal/html/repo_file.templ
deleted file mode 100644
index c589066f84df572afb2fb9702cabcdd33d96c786..0000000000000000000000000000000000000000
--- a/internal/html/repo_file.templ
+++ /dev/null
@@ -1,68 +0,0 @@
-package html
-
-type RepoFileContext struct {
- BaseContext
- RepoHeaderComponentContext
- Code string
- Path string
-}
-
-templ RepoFile(rfc RepoFileContext) {
- @base(rfc.BaseContext) {
- @repoHeaderComponent(rfc.RepoHeaderComponentContext)
- Raw{ " - " }{ rfc.Path }@templ.Raw(rfc.Code)
- }
-
-}
-
diff --git a/internal/html/repo_file_templ.go b/internal/html/repo_file_templ.go
deleted file mode 100644
index 2616d1f1b3f3ac15d107d5f29106db4d11908410..0000000000000000000000000000000000000000
--- a/internal/html/repo_file_templ.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.2.501
-package html
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import "context"
-import "io"
-import "bytes"
-
-type RepoFileContext struct {
- BaseContext
- RepoHeaderComponentContext
- Code string
- Path string
-}
-
-func RepoFile(rfc RepoFileContext) templ.Component {
- return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- templ_7745c5c3_Err = repoHeaderComponent(rfc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Var3 := `Raw`
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(" - ")
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 12, Col: 153}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var5 string
- templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(rfc.Path)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_file.templ`, Line: 12, Col: 165}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = templ.Raw(rfc.Code).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
- }
- return templ_7745c5c3_Err
- })
- templ_7745c5c3_Err = base(rfc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
- }
- return templ_7745c5c3_Err
- })
-}
diff --git a/internal/html/repo_templ.go b/internal/html/repo_templ.go
deleted file mode 100644
index 3ee5f408417a1d1badba62e07261a748cfd44a4e..0000000000000000000000000000000000000000
--- a/internal/html/repo_templ.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.2.501
-package html
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import "context"
-import "io"
-import "bytes"
-
-import "fmt"
-
-type RepoHeaderComponentContext struct {
- Name string
- Ref string
- Description string
-}
-
-func repoHeaderComponent(rhcc RepoHeaderComponentContext) templ.Component {
- return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- if rhcc.Name != "" {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(rhcc.Name)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 13, Col: 153}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if rhcc.Ref != "" {
- var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(" ")
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 15, Col: 9}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var6 string
- templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs("@" + rhcc.Ref)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 16, Col: 195}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var7 string
- templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(rhcc.Description)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo.templ`, Line: 20, Col: 50}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
- }
- return templ_7745c5c3_Err
- })
-}
diff --git a/internal/html/repo_tree.templ b/internal/html/repo_tree.templ
deleted file mode 100644
index 204a14d58af81b3c54ce19aa2b5b73c5406eba22..0000000000000000000000000000000000000000
--- a/internal/html/repo_tree.templ
+++ /dev/null
@@ -1,51 +0,0 @@
-package html
-
-import (
- "fmt"
- "go.jolheiser.com/ugit/internal/git"
-)
-
-type RepoTreeContext struct {
- BaseContext
- RepoHeaderComponentContext
- RepoTreeComponentContext
- ReadmeComponentContext
- Description string
-}
-
-templ RepoTree(rtc RepoTreeContext) {
- @base(rtc.BaseContext) {
- @repoHeaderComponent(rtc.RepoHeaderComponentContext)
- @repoTreeComponent(rtc.RepoTreeComponentContext)
- @readmeComponent(rtc.ReadmeComponentContext)
- }
-}
-
-type RepoTreeComponentContext struct {
- Repo string
- Ref string
- Tree []git.FileInfo
- Back string
-}
-
-func slashDir(name string, isDir bool) string {
- if isDir {
- return name + "/"
- }
- return name
-}
-
-templ repoTreeComponent(rtcc RepoTreeComponentContext) {
-
- if rtcc.Back != "" {
-
-
- }
- for _, fi := range rtcc.Tree {
-
{ fi.Mode }
-
{ fi.Size }
-
- }
-
-}
-
diff --git a/internal/html/repo_tree_templ.go b/internal/html/repo_tree_templ.go
deleted file mode 100644
index ecbf44cac165bfdf02b0a4fa9380990c08194d98..0000000000000000000000000000000000000000
--- a/internal/html/repo_tree_templ.go
+++ /dev/null
@@ -1,199 +0,0 @@
-// Code generated by templ - DO NOT EDIT.
-
-// templ: version: v0.2.501
-package html
-
-//lint:file-ignore SA4006 This context is only used if a nested component is present.
-
-import "github.com/a-h/templ"
-import "context"
-import "io"
-import "bytes"
-
-import (
- "fmt"
- "go.jolheiser.com/ugit/internal/git"
-)
-
-type RepoTreeContext struct {
- BaseContext
- RepoHeaderComponentContext
- RepoTreeComponentContext
- ReadmeComponentContext
- Description string
-}
-
-func RepoTree(rtc RepoTreeContext) templ.Component {
- return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var1 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var1 == nil {
- templ_7745c5c3_Var1 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- templ_7745c5c3_Err = repoHeaderComponent(rtc.RepoHeaderComponentContext).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = repoTreeComponent(rtc.RepoTreeComponentContext).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- templ_7745c5c3_Err = readmeComponent(rtc.ReadmeComponentContext).Render(ctx, templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
- }
- return templ_7745c5c3_Err
- })
- templ_7745c5c3_Err = base(rtc.BaseContext).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
- }
- return templ_7745c5c3_Err
- })
-}
-
-type RepoTreeComponentContext struct {
- Repo string
- Ref string
- Tree []git.FileInfo
- Back string
-}
-
-func slashDir(name string, isDir bool) string {
- if isDir {
- return name + "/"
- }
- return name
-}
-
-func repoTreeComponent(rtcc RepoTreeComponentContext) templ.Component {
- return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
- if !templ_7745c5c3_IsBuffer {
- templ_7745c5c3_Buffer = templ.GetBuffer()
- defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
- }
- ctx = templ.InitializeContext(ctx)
- templ_7745c5c3_Var3 := templ.GetChildren(ctx)
- if templ_7745c5c3_Var3 == nil {
- templ_7745c5c3_Var3 = templ.NopComponent
- }
- ctx = templ.ClearChildren(ctx)
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if rtcc.Back != "" {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- for _, fi := range rtcc.Tree {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var6 string
- templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fi.Mode)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 44, Col: 36}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- var templ_7745c5c3_Var7 string
- templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fi.Size)
- if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `repo_tree.templ`, Line: 45, Col: 36}
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- }
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
")
- if templ_7745c5c3_Err != nil {
- return templ_7745c5c3_Err
- }
- if !templ_7745c5c3_IsBuffer {
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
- }
- return templ_7745c5c3_Err
- })
-}
diff --git a/internal/html/tailwind.config.js b/internal/html/tailwind.config.js
deleted file mode 100644
index e0a1834b2cff8c49b8712115aaf8a7ec060831c8..0000000000000000000000000000000000000000
--- a/internal/html/tailwind.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/** @type {import('tailwindcss').Config} */
-module.exports = {
- content: ["./**/*.templ"],
- plugins: [require("@catppuccin/tailwindcss")],
-}
-
-
diff --git a/internal/html/tailwind.go b/internal/html/tailwind.go
deleted file mode 100644
index 8bd8a40894b2e434ee2f64c38be9f8cc5e8336ba..0000000000000000000000000000000000000000
--- a/internal/html/tailwind.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// Code generated by generate.go - DO NOT EDIT.
-package html
-
-import "net/http"
-
-func TailwindHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "text/css")
- w.Write([]byte("/*! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:\"\"}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}.latte{--ctp-rosewater:220,138,120;--ctp-flamingo:221,120,120;--ctp-pink:234,118,203;--ctp-mauve:136,57,239;--ctp-red:210,15,57;--ctp-maroon:230,69,83;--ctp-peach:254,100,11;--ctp-yellow:223,142,29;--ctp-green:64,160,43;--ctp-teal:23,146,153;--ctp-sky:4,165,229;--ctp-sapphire:32,159,181;--ctp-blue:30,102,245;--ctp-lavender:114,135,253;--ctp-text:76,79,105;--ctp-subtext1:92,95,119;--ctp-subtext0:108,111,133;--ctp-overlay2:124,127,147;--ctp-overlay1:140,143,161;--ctp-overlay0:156,160,176;--ctp-surface2:172,176,190;--ctp-surface1:188,192,204;--ctp-surface0:204,208,218;--ctp-base:239,241,245;--ctp-mantle:230,233,239;--ctp-crust:220,224,232}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-5{grid-column:span 5/span 5}.col-span-6{grid-column:span 6/span 6}.mx-auto{margin-left:auto;margin-right:auto}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mr-1{margin-right:.25rem}.mt-2{margin-top:.5rem}.mt-5{margin-top:1.25rem}.inline-block{display:inline-block}.grid{display:grid}.h-5{height:1.25rem}.w-5{width:1.25rem}.max-w-7xl{max-width:80rem}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.gap-1{gap:.25rem}.rounded{border-radius:.25rem}.bg-base\\/50{background-color:rgba(var(--ctp-base),.5)}.stroke-mauve{stroke:rgb(var(--ctp-mauve))}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.align-middle{vertical-align:middle}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-blue{--tw-text-opacity:1;color:rgba(var(--ctp-blue),var(--tw-text-opacity))}.text-mauve{--tw-text-opacity:1;color:rgba(var(--ctp-mauve),var(--tw-text-opacity))}.text-subtext0{--tw-text-opacity:1;color:rgba(var(--ctp-subtext0),var(--tw-text-opacity))}.text-subtext1{--tw-text-opacity:1;color:rgba(var(--ctp-subtext1),var(--tw-text-opacity))}.text-text{--tw-text-opacity:1;color:rgba(var(--ctp-text),var(--tw-text-opacity))}.text-text\\/70{color:rgba(var(--ctp-text),.7)}.text-text\\/80{color:rgba(var(--ctp-text),.8)}.underline{text-decoration-line:underline}.decoration-blue\\/50{text-decoration-color:rgba(var(--ctp-blue),.5)}.decoration-mauve\\/50{text-decoration-color:rgba(var(--ctp-mauve),.5)}.decoration-text\\/50{text-decoration-color:rgba(var(--ctp-text),.5)}.decoration-dashed{text-decoration-style:dashed}.markdown *{all:revert;color:rgb(var(--ctp-text))}.markdown a{color:rgb(var(--ctp-blue));text-decoration-line:underline;text-decoration-style:dashed}.markdown a:hover{text-decoration-style:solid}.chroma{font-size:small}.chroma *{background-color:rgb(var(--ctp-base))!important}.chroma table{border-spacing:5px 0!important}.chroma .lnt{color:rgb(var(--ctp-subtext1))!important}.chroma .lnt:focus,.chroma .lnt:target{color:rgb(var(--ctp-subtext0))!important}.chroma .line{white-space:break-spaces}.chroma .line.active,.chroma .line.active *{background:rgb(var(--ctp-surface0))!important}.bg,.chroma{color:#4c4f69;background-color:#eff1f5}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#bcc0cc;background-color:#eff1f5}.chroma .err{color:#d20f39}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{color:#bcc0cc}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#8c8fa1}.chroma .line{display:flex}.chroma .k{color:#8839ef}.chroma .kc{color:#fe640b}.chroma .kd{color:#d20f39}.chroma .kn{color:#179299}.chroma .kp,.chroma .kr{color:#8839ef}.chroma .kt{color:#d20f39}.chroma .na{color:#1e66f5}.chroma .bp,.chroma .nb{color:#04a5e5}.chroma .nc,.chroma .no{color:#df8e1d}.chroma .nd{color:#1e66f5;font-weight:700}.chroma .ni{color:#179299}.chroma .ne{color:#fe640b}.chroma .fm,.chroma .nf{color:#1e66f5}.chroma .nl{color:#04a5e5}.chroma .nn,.chroma .py{color:#fe640b}.chroma .nt{color:#8839ef}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#dc8a78}.chroma .s{color:#40a02b}.chroma .sa{color:#d20f39}.chroma .sb,.chroma .sc{color:#40a02b}.chroma .dl{color:#1e66f5}.chroma .sd{color:#9ca0b0}.chroma .s2{color:#40a02b}.chroma .se{color:#1e66f5}.chroma .sh{color:#9ca0b0}.chroma .si,.chroma .sx{color:#40a02b}.chroma .sr{color:#179299}.chroma .s1,.chroma .ss{color:#40a02b}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fe640b}.chroma .o,.chroma .ow{color:#04a5e5;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#9ca0b0;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#d20f39;background-color:#ccd0da}.chroma .ge{font-style:italic}.chroma .gr{color:#d20f39}.chroma .gh{color:#fe640b;font-weight:700}.chroma .gi{color:#40a02b;background-color:#ccd0da}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fe640b}.chroma .gt{color:#d20f39}.chroma .gl{text-decoration:underline}@media (prefers-color-scheme:dark){.bg,.chroma{color:#cdd6f4;background-color:#1e1e2e}.chroma .lntd:last-child{width:100%}.chroma .ln:target,.chroma .lnt:target{color:#45475a;background-color:#1e1e2e}.chroma .err{color:#f38ba8}.chroma .lnlinks{outline:none;text-decoration:none;color:inherit}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0}.chroma .hl{color:#45475a}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;-moz-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#7f849c}.chroma .line{display:flex}.chroma .k{color:#cba6f7}.chroma .kc{color:#fab387}.chroma .kd{color:#f38ba8}.chroma .kn{color:#94e2d5}.chroma .kp,.chroma .kr{color:#cba6f7}.chroma .kt{color:#f38ba8}.chroma .na{color:#89b4fa}.chroma .bp,.chroma .nb{color:#89dceb}.chroma .nc,.chroma .no{color:#f9e2af}.chroma .nd{color:#89b4fa;font-weight:700}.chroma .ni{color:#94e2d5}.chroma .ne{color:#fab387}.chroma .fm,.chroma .nf{color:#89b4fa}.chroma .nl{color:#89dceb}.chroma .nn,.chroma .py{color:#fab387}.chroma .nt{color:#cba6f7}.chroma .nv,.chroma .vc,.chroma .vg,.chroma .vi,.chroma .vm{color:#f5e0dc}.chroma .s{color:#a6e3a1}.chroma .sa{color:#f38ba8}.chroma .sb,.chroma .sc{color:#a6e3a1}.chroma .dl{color:#89b4fa}.chroma .sd{color:#6c7086}.chroma .s2{color:#a6e3a1}.chroma .se{color:#89b4fa}.chroma .sh{color:#6c7086}.chroma .si,.chroma .sx{color:#a6e3a1}.chroma .sr{color:#94e2d5}.chroma .s1,.chroma .ss{color:#a6e3a1}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#fab387}.chroma .o,.chroma .ow{color:#89dceb;font-weight:700}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .cp,.chroma .cpf,.chroma .cs{color:#6c7086;font-style:italic}.chroma .cpf{font-weight:700}.chroma .gd{color:#f38ba8;background-color:#313244}.chroma .ge{font-style:italic}.chroma .gr{color:#f38ba8}.chroma .gh{color:#fab387;font-weight:700}.chroma .gi{color:#a6e3a1;background-color:#313244}.chroma .gs,.chroma .gu{font-weight:700}.chroma .gu{color:#fab387}.chroma .gt{color:#f38ba8}.chroma .gl{text-decoration:underline}.dark\\:mocha{--ctp-rosewater:245,224,220;--ctp-flamingo:242,205,205;--ctp-pink:245,194,231;--ctp-mauve:203,166,247;--ctp-red:243,139,168;--ctp-maroon:235,160,172;--ctp-peach:250,179,135;--ctp-yellow:249,226,175;--ctp-green:166,227,161;--ctp-teal:148,226,213;--ctp-sky:137,220,235;--ctp-sapphire:116,199,236;--ctp-blue:137,180,250;--ctp-lavender:180,190,254;--ctp-text:205,214,244;--ctp-subtext1:186,194,222;--ctp-subtext0:166,173,200;--ctp-overlay2:147,153,178;--ctp-overlay1:127,132,156;--ctp-overlay0:108,112,134;--ctp-surface2:88,91,112;--ctp-surface1:69,71,90;--ctp-surface0:49,50,68;--ctp-base:30,30,46;--ctp-mantle:24,24,37;--ctp-crust:17,17,27}}.hover\\:decoration-solid:hover{text-decoration-style:solid}@media (prefers-color-scheme:dark){.dark\\:bg-base\\/95{background-color:rgba(var(--ctp-base),.95)}.dark\\:text-lavender{--tw-text-opacity:1;color:rgba(var(--ctp-lavender),var(--tw-text-opacity))}.dark\\:decoration-lavender\\/50{text-decoration-color:rgba(var(--ctp-lavender),.5)}}@media (min-width:640px){.sm\\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}}"))
-}
diff --git a/internal/http/git.go b/internal/http/git.go
deleted file mode 100644
index 56d7e28eae08bad2cdc63716cf285ee84afc2ea3..0000000000000000000000000000000000000000
--- a/internal/http/git.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package http
-
-import (
- "errors"
- "net/http"
- "path/filepath"
-
- "go.jolheiser.com/ugit/internal/git"
- "go.jolheiser.com/ugit/internal/http/httperr"
-
- "github.com/go-chi/chi/v5"
-)
-
-func (rh repoHandler) infoRefs(w http.ResponseWriter, r *http.Request) error {
- if r.URL.Query().Get("service") != "git-upload-pack" {
- return httperr.Status(errors.New("pushing isn't supported via HTTP(S), use SSH"), http.StatusBadRequest)
- }
-
- w.Header().Set("Content-Type", "application/x-git-upload-pack-advertisement")
- rp := filepath.Join(rh.s.RepoDir, chi.URLParam(r, "repo")+".git")
- repo, err := git.NewProtocol(rp)
- if err != nil {
- return httperr.Error(err)
- }
- if err := repo.HTTPInfoRefs(Session{
- w: w,
- r: r,
- }); err != nil {
- return httperr.Error(err)
- }
-
- return nil
-}
-
-func (rh repoHandler) uploadPack(w http.ResponseWriter, r *http.Request) error {
- w.Header().Set("content-type", "application/x-git-upload-pack-result")
- rp := filepath.Join(rh.s.RepoDir, chi.URLParam(r, "repo")+".git")
- repo, err := git.NewProtocol(rp)
- if err != nil {
- return httperr.Error(err)
- }
- if err := repo.HTTPUploadPack(Session{
- w: w,
- r: r,
- }); err != nil {
- return httperr.Error(err)
- }
-
- return nil
-}
diff --git a/internal/http/http.go b/internal/http/http.go
deleted file mode 100644
index d4d406766ce829b478bb98ae92190b7351dfa51d..0000000000000000000000000000000000000000
--- a/internal/http/http.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package http
-
-import (
- "fmt"
- "net/http"
- "net/url"
-
- "go.jolheiser.com/ugit/assets"
- "go.jolheiser.com/ugit/internal/git"
- "go.jolheiser.com/ugit/internal/html"
- "go.jolheiser.com/ugit/internal/http/httperr"
-
- "github.com/go-chi/chi/v5"
- "github.com/go-chi/chi/v5/middleware"
-)
-
-// Server is the container struct for the HTTP server
-type Server struct {
- port int
- mux *chi.Mux
-}
-
-// ListenAndServe simply wraps http.ListenAndServe to contain the functionality here
-func (s Server) ListenAndServe() error {
- return http.ListenAndServe(fmt.Sprintf("localhost:%d", s.port), s.mux)
-}
-
-// Settings is the configuration for the HTTP server
-type Settings struct {
- Title string
- Description string
- CloneURL string
- Port int
- RepoDir string
- Profile Profile
-}
-
-// Profile is the index profile
-type Profile struct {
- Username string
- Email string
- Links []Link
-}
-
-// Link is a profile link
-type Link struct {
- Name string
- URL string
-}
-
-func (s Settings) goGet(repo string) string {
- u, _ := url.Parse(s.CloneURL)
- return fmt.Sprintf(`%[1]s`, repo, u.Hostname(), s.CloneURL)
-}
-
-// New returns a new HTTP server
-func New(settings Settings) Server {
- mux := chi.NewMux()
-
- mux.Use(middleware.Logger)
- mux.Use(middleware.Recoverer)
-
- rh := repoHandler{s: settings}
- mux.Route("/{repo}.git", func(r chi.Router) {
- r.Get("/info/refs", httperr.Handler(rh.infoRefs))
- r.Post("/git-upload-pack", httperr.Handler(rh.uploadPack))
- })
-
- mux.Route("/", func(r chi.Router) {
- r.Get("/", httperr.Handler(rh.index))
- r.Route("/{repo}", func(r chi.Router) {
- r.Get("/", func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Query().Has("go-get") {
- repo := chi.URLParam(r, "repo")
- w.Write([]byte(settings.goGet(repo)))
- return
- }
- rh.repoTree("", "").ServeHTTP(w, r)
- })
- r.Get("/tree/{ref}/*", func(w http.ResponseWriter, r *http.Request) {
- rh.repoTree(chi.URLParam(r, "ref"), chi.URLParam(r, "*")).ServeHTTP(w, r)
- })
- })
- })
-
- mux.Route("/_", func(r chi.Router) {
- r.Get("/favicon.svg", func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "image/svg+xml")
- w.Write(assets.LogoIcon)
- })
- r.Get("/tailwind.css", html.TailwindHandler)
- })
-
- return Server{mux: mux, port: settings.Port}
-}
-
-type repoHandler struct {
- s Settings
-}
-
-func (rh repoHandler) baseContext() html.BaseContext {
- return html.BaseContext{
- Title: rh.s.Title,
- Description: rh.s.Description,
- }
-}
-
-func (rh repoHandler) repoHeaderContext(repo *git.Repo, r *http.Request) html.RepoHeaderComponentContext {
- return html.RepoHeaderComponentContext{
- Description: repo.Meta.Description,
- Name: chi.URLParam(r, "repo"),
- Ref: chi.URLParam(r, "ref"),
- }
-}
-
-// NoopLogger is a no-op logging middleware
-func NoopLogger(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- next.ServeHTTP(w, r)
- })
-}
diff --git a/internal/http/httperr/httperr.go b/internal/http/httperr/httperr.go
deleted file mode 100644
index b6bcd1fd0a1b45c3797645548bba0eb24db5b36d..0000000000000000000000000000000000000000
--- a/internal/http/httperr/httperr.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package httperr
-
-import (
- "errors"
- "net/http"
-
- "github.com/charmbracelet/log"
-)
-
-type httpError struct {
- err error
- status int
-}
-
-func (h httpError) Error() string {
- return h.err.Error()
-}
-
-func (h httpError) Unwrap() error {
- return h.err
-}
-
-func Error(err error) httpError {
- return Status(err, http.StatusInternalServerError)
-}
-
-func Status(err error, status int) httpError {
- return httpError{err: err, status: status}
-}
-
-func Handler(fn func(w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- if err := fn(w, r); err != nil {
- status := http.StatusInternalServerError
-
- var httpErr httpError
- if errors.As(err, &httpErr) {
- status = httpErr.status
- }
-
- log.Error(err)
- http.Error(w, http.StatusText(status), status)
- }
- }
-}
diff --git a/internal/http/index.go b/internal/http/index.go
deleted file mode 100644
index dfe761c69d3d410b25b048b4d0dbcd2cf3051ee7..0000000000000000000000000000000000000000
--- a/internal/http/index.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package http
-
-import (
- "net/http"
- "os"
- "sort"
- "time"
-
- "go.jolheiser.com/ugit/internal/git"
- "go.jolheiser.com/ugit/internal/html"
- "go.jolheiser.com/ugit/internal/http/httperr"
-)
-
-func (rh repoHandler) index(w http.ResponseWriter, r *http.Request) error {
- repoPaths, err := os.ReadDir(rh.s.RepoDir)
- if err != nil {
- return httperr.Error(err)
- }
-
- repos := make([]*git.Repo, 0, len(repoPaths))
- for _, repoName := range repoPaths {
- repo, err := git.NewRepo(rh.s.RepoDir, repoName.Name())
- if err != nil {
- return httperr.Error(err)
- }
- if !repo.Meta.Private {
- repos = append(repos, repo)
- }
- }
- sort.Slice(repos, func(i, j int) bool {
- var when1, when2 time.Time
- if c, err := repos[i].LastCommit(); err == nil {
- when1 = c.Author.When
- }
- if c, err := repos[j].LastCommit(); err == nil {
- when2 = c.Author.When
- }
- return when1.After(when2)
- })
-
- links := make([]html.IndexLink, 0, len(rh.s.Profile.Links))
- for _, link := range rh.s.Profile.Links {
- links = append(links, html.IndexLink{
- Name: link.Name,
- URL: link.URL,
- })
- }
-
- if err := html.Index(html.IndexContext{
- BaseContext: rh.baseContext(),
- Profile: html.IndexProfile{
- Username: rh.s.Profile.Username,
- Email: rh.s.Profile.Email,
- Links: links,
- },
- CloneURL: rh.s.CloneURL,
- Repos: repos,
- }).Render(r.Context(), w); err != nil {
- return httperr.Error(err)
- }
-
- return nil
-}
diff --git a/internal/http/repo.go b/internal/http/repo.go
deleted file mode 100644
index bdf82a252fe33762c304586820ed55bdb5996f7b..0000000000000000000000000000000000000000
--- a/internal/http/repo.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package http
-
-import (
- "bytes"
- "errors"
- "io/fs"
- "mime"
- "net/http"
- "path/filepath"
-
- "go.jolheiser.com/ugit/internal/git"
- "go.jolheiser.com/ugit/internal/html"
- "go.jolheiser.com/ugit/internal/http/httperr"
-
- "github.com/go-chi/chi/v5"
- "github.com/go-git/go-git/v5/plumbing/object"
-)
-
-func (rh repoHandler) repoTree(ref, path string) http.HandlerFunc {
- return httperr.Handler(func(w http.ResponseWriter, r *http.Request) error {
- repoName := chi.URLParam(r, "repo")
- repo, err := git.NewRepo(rh.s.RepoDir, repoName)
- if err != nil {
- httpErr := http.StatusInternalServerError
- if errors.Is(err, fs.ErrNotExist) {
- httpErr = http.StatusNotFound
- }
- return httperr.Status(err, httpErr)
- }
- if repo.Meta.Private {
- return httperr.Status(errors.New("could not get git repo"), http.StatusNotFound)
- }
-
- if ref == "" {
- ref, err = repo.DefaultBranch()
- if err != nil {
- return httperr.Error(err)
- }
- }
-
- tree, err := repo.Dir(ref, path)
- if err != nil {
- if errors.Is(err, object.ErrDirectoryNotFound) {
- return rh.repoFile(w, r, repo, ref, path)
- }
- return httperr.Error(err)
- }
-
- readmeContent, err := html.Readme(repo, ref, path)
- if err != nil {
- return httperr.Error(err)
- }
-
- var back string
- if path != "" {
- back = filepath.Dir(path)
- }
- if err := html.RepoTree(html.RepoTreeContext{
- Description: repo.Meta.Description,
- BaseContext: rh.baseContext(),
- RepoHeaderComponentContext: rh.repoHeaderContext(repo, r),
- RepoTreeComponentContext: html.RepoTreeComponentContext{
- Repo: repoName,
- Ref: ref,
- Tree: tree,
- Back: back,
- },
- ReadmeComponentContext: html.ReadmeComponentContext{
- Markdown: readmeContent,
- },
- }).Render(r.Context(), w); err != nil {
- return httperr.Error(err)
- }
-
- return nil
- })
-}
-
-func (rh repoHandler) repoFile(w http.ResponseWriter, r *http.Request, repo *git.Repo, ref, path string) error {
- content, err := repo.FileContent(ref, path)
- if err != nil {
- return httperr.Error(err)
- }
-
- if r.URL.Query().Has("raw") {
- if r.URL.Query().Has("pretty") {
- ext := filepath.Ext(path)
- w.Header().Set("Content-Type", mime.TypeByExtension(ext))
- }
- w.Write([]byte(content))
- return nil
- }
-
- var buf bytes.Buffer
- if err := html.Code.Convert([]byte(content), filepath.Base(path), &buf); err != nil {
- return httperr.Error(err)
- }
-
- if err := html.RepoFile(html.RepoFileContext{
- BaseContext: rh.baseContext(),
- RepoHeaderComponentContext: rh.repoHeaderContext(repo, r),
- Code: buf.String(),
- Path: path,
- }).Render(r.Context(), w); err != nil {
- return httperr.Error(err)
- }
-
- return nil
-}
diff --git a/internal/http/session.go b/internal/http/session.go
deleted file mode 100644
index b427f08abe58ddf096e570e618541f397cf09728..0000000000000000000000000000000000000000
--- a/internal/http/session.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package http
-
-import (
- "context"
- "net/http"
-)
-
-// Session fulfills git.ReadWriteContexter for an HTTP request
-type Session struct {
- w http.ResponseWriter
- r *http.Request
-}
-
-// Read implements io.Reader
-func (s Session) Read(p []byte) (n int, err error) {
- return s.r.Body.Read(p)
-}
-
-// Write implements io.Writer
-func (s Session) Write(p []byte) (n int, err error) {
- return s.w.Write(p)
-}
-
-// Close implements io.Closer
-func (s Session) Close() error {
- return s.r.Body.Close()
-}
-
-// Context implements git.ReadWriteContexter
-func (s Session) Context() context.Context {
- return s.r.Context()
-}
diff --git a/internal/ssh/ssh.go b/internal/ssh/ssh.go
index 31c49eb29bb3d509f412a4542eedc8a0d2aefb00..4a494b653835ddc7d00ee5ad234eee5d02a02794 100644
--- a/internal/ssh/ssh.go
+++ b/internal/ssh/ssh.go
@@ -3,30 +3,20 @@
import (
"fmt"
- "github.com/charmbracelet/log"
"github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
+ "github.com/charmbracelet/wish/git"
"github.com/charmbracelet/wish/logging"
)
-// Settings holds the configuration for the SSH server
-type Settings struct {
- AuthorizedKeys string
- CloneURL string
- Port int
- HostKey string
- RepoDir string
-}
-
-// New creates a new SSH server.
-func New(settings Settings) (*ssh.Server, error) {
+func New() (*ssh.Server, error) {
s, err := wish.NewServer(
- wish.WithAuthorizedKeys(settings.AuthorizedKeys),
- wish.WithAddress(fmt.Sprintf(":%d", settings.Port)),
- wish.WithHostKeyPath(settings.HostKey),
+ wish.WithAuthorizedKeys(".ssh/authorized_keys"),
+ wish.WithAddress("localhost:8448"),
+ wish.WithHostKeyPath(".ssh/ugit_ed25519"),
wish.WithMiddleware(
- Middleware(settings.RepoDir, settings.CloneURL, settings.Port, hooks{}),
- logging.MiddlewareWithLogger(DefaultLogger),
+ git.Middleware(".ugit", app{}),
+ logging.Middleware(),
),
)
if err != nil {
@@ -36,16 +26,10 @@
return s, nil
}
-type hooks struct{}
+type app struct{}
-func (a hooks) Push(_ string, _ ssh.PublicKey) {}
-func (a hooks) Fetch(_ string, _ ssh.PublicKey) {}
-
-var (
- DefaultLogger logging.Logger = log.StandardLog()
- NoopLogger logging.Logger = noopLogger{}
-)
-
-type noopLogger struct{}
-
-func (n noopLogger) Printf(format string, v ...interface{}) {}
+func (a app) AuthRepo(repo string, pk ssh.PublicKey) git.AccessLevel {
+ return git.ReadWriteAccess
+}
+func (a app) Push(_ string, _ ssh.PublicKey) {}
+func (a app) Fetch(_ string, _ ssh.PublicKey) {}
diff --git a/internal/ssh/wish.go b/internal/ssh/wish.go
deleted file mode 100644
index 89fd6ef63875ca728bd02a35aa741abc2cf73486..0000000000000000000000000000000000000000
--- a/internal/ssh/wish.go
+++ /dev/null
@@ -1,164 +0,0 @@
-package ssh
-
-import (
- "context"
- "errors"
- "fmt"
- "io/fs"
- "os"
- "path/filepath"
- "strings"
-
- "go.jolheiser.com/ugit/internal/git"
-
- "github.com/charmbracelet/log"
- "github.com/charmbracelet/ssh"
- "github.com/charmbracelet/wish"
-)
-
-// ErrSystemMalfunction represents a general system error returned to clients.
-var ErrSystemMalfunction = errors.New("something went wrong")
-
-// ErrInvalidRepo represents an attempt to access a non-existent repo.
-var ErrInvalidRepo = errors.New("invalid repo")
-
-// Hooks is an interface that allows for custom authorization
-// implementations and post push/fetch notifications. Prior to git access,
-// AuthRepo will be called with the ssh.Session public key and the repo name.
-// Implementers return the appropriate AccessLevel.
-type Hooks interface {
- Push(string, ssh.PublicKey)
- Fetch(string, ssh.PublicKey)
-}
-
-// Session wraps sn ssh.Session to implement git.ReadWriteContexter
-type Session struct {
- s ssh.Session
-}
-
-// Read implements io.Reader
-func (s Session) Read(p []byte) (n int, err error) {
- return s.s.Read(p)
-}
-
-// Write implements io.Writer
-func (s Session) Write(p []byte) (n int, err error) {
- return s.s.Write(p)
-}
-
-// Close implements io.Closer
-func (s Session) Close() error {
- return nil
-}
-
-// Context returns an interface context.Context
-func (s Session) Context() context.Context {
- return s.s.Context()
-}
-
-// Middleware adds Git server functionality to the ssh.Server. Repos are stored
-// in the specified repo directory. The provided Hooks implementation will be
-// checked for access on a per repo basis for a ssh.Session public key.
-// Hooks.Push and Hooks.Fetch will be called on successful completion of
-// their commands.
-func Middleware(repoDir string, cloneURL string, port int, gh Hooks) wish.Middleware {
- return func(sh ssh.Handler) ssh.Handler {
- return func(s ssh.Session) {
- sess := Session{s: s}
- cmd := s.Command()
-
- // Git operations
- if len(cmd) == 2 {
- gc := cmd[0]
- // repo should be in the form of "repo.git" or "user/repo.git"
- repo := strings.TrimSuffix(strings.TrimPrefix(cmd[1], "/"), "/")
- repo = filepath.Clean(repo)
- if n := strings.Count(repo, "/"); n > 1 {
- Fatal(s, ErrInvalidRepo)
- return
- }
- pk := s.PublicKey()
- switch gc {
- case "git-receive-pack":
- if err := gitPack(sess, gc, repoDir, repo); err != nil {
- Fatal(s, ErrSystemMalfunction)
- }
- gh.Push(repo, pk)
- return
- case "git-upload-archive", "git-upload-pack":
- if err := gitPack(sess, gc, repoDir, repo); err != nil {
- if errors.Is(err, ErrInvalidRepo) {
- Fatal(s, ErrInvalidRepo)
- }
- log.Error("unknown git error", "error", err)
- Fatal(s, ErrSystemMalfunction)
- }
- gh.Fetch(repo, pk)
- return
- }
- }
-
- // Repo list
- if len(cmd) == 0 {
- des, err := os.ReadDir(repoDir)
- if err != nil && err != fs.ErrNotExist {
- log.Error("invalid repository", "error", err)
- }
- for _, de := range des {
- fmt.Fprintln(s, de.Name())
- fmt.Fprintf(s, "\tgit clone %s/%s\n", cloneURL, de.Name())
- }
- }
- sh(s)
- }
- }
-}
-
-func gitPack(s Session, gitCmd string, repoDir string, repoName string) error {
- rp := filepath.Join(repoDir, repoName)
- protocol, err := git.NewProtocol(rp)
- if err != nil {
- return err
- }
- switch gitCmd {
- case "git-upload-pack":
- exists, err := git.PathExists(rp)
- if !exists {
- return ErrInvalidRepo
- }
- if err != nil {
- return err
- }
- return protocol.SSHUploadPack(s)
- case "git-receive-pack":
- err := git.EnsureRepo(repoDir, repoName)
- if err != nil {
- return err
- }
- repo, err := git.NewRepo(repoDir, repoName)
- if err != nil {
- return err
- }
- err = protocol.SSHReceivePack(s, repo)
- if err != nil {
- return err
- }
- _, err = repo.DefaultBranch()
- if err != nil {
- return err
- }
- // Needed for git dumb http server
- return git.UpdateServerInfo(rp)
- default:
- return fmt.Errorf("unknown git command: %s", gitCmd)
- }
-}
-
-// Fatal prints to the session's STDOUT as a git response and exit 1.
-func Fatal(s ssh.Session, v ...interface{}) {
- msg := fmt.Sprint(v...)
- // hex length includes 4 byte length prefix and ending newline
- pktLine := fmt.Sprintf("%04x%s\n", len(msg)+5, msg)
- _, _ = wish.WriteString(s, pktLine)
- s.Exit(1) // nolint: errcheck
-}