diff --git a/.gitignore b/.gitignore index 9b0fc31dcb49d4802a3c1164053076335555c217..f53c2fd9c85934fc3516359d2798b5e86ca53ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ /ugit* -.ssh/ -.ugit/ diff --git a/.helix/languages.toml b/.helix/languages.toml deleted file mode 100644 index adcc7b20b55bb9bddc08aa35dd35060044280875..0000000000000000000000000000000000000000 --- a/.helix/languages.toml +++ /dev/null @@ -1,5 +0,0 @@ -[[language]] -name = "templ" -language-id = "html" -language-servers = ["templ", "vscode-html-language-server", "tailwindcss-ls"] - diff --git a/README.md b/README.md deleted file mode 100644 index b526756dff25a1db2927a6e2ec653de0d97dcff9..0000000000000000000000000000000000000000 --- a/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# ugit - -ugit logo - -Minimal git server - -ugit allows cloning via HTTPS/SSH, but can only be pushed to via SSH. - -There are no plans to directly support issues or PR workflows, although webhooks are planned and auxillary software may be created to facilitate these things. -For now, if you wish to collaborate, please send me patches at [john+ugit@jolheiser.com](mailto:john+ugit@jolheiser.com). - -Currently all HTML is allowed in markdown, ugit is intended to be run by/for a trusted user. - -## Getting your public SSH keys from another forge - -Using GitHub as an example (although Gitea/GitLab should have the same URL scheme) - -Ba/sh -```sh -curl https://github.com/.keys > path/to/authorized_keys -``` - -Nushell -```sh -http get https://github.com/.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 @@ -email icon 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 @@ -link icon 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 @@ - - - ugit icon - - - - - - - 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 } - - - - - - -

Home

- { 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_Var3 := `Home` - _, 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 - } - 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.Raw(string(assets.EmailIcon)).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_Var6 := `john.olheiser@gmail.com` - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(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 - } - 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.Name } - if rhcc.Ref != "" { - { " " } - { "@" + rhcc.Ref } - } -
- } -
{ 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 -}