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,108 +2,25 @@ 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 -} - -package main "github.com/peterbourgon/ff/v3/ffyaml" - AuthorizedKeys string - CloneURL string - Port int - HostKey string -} - - import ( - CloneURL string - Port int -} - -type metaArgs struct { - Title string - "github.com/peterbourgon/ff/v3/ffyaml" -} - -type profileArgs struct { - Username string - Email string - Links []profileLink -} - -type profileLink struct { -import ( "flag" - URL string } -func parseArgs(args []string) (c cliArgs, e error) { - fs := flag.NewFlagSet("ugitd", flag.ContinueOnError) -import ( "github.com/peterbourgon/ff/v3/ffyaml" - - c = cliArgs{ - RepoDir: ".ugit", - SSH: sshArgs{ - AuthorizedKeys: ".ssh/authorized_keys", + "fmt" - "flag" import ( - Port: 8448, - HostKey: ".ssh/ugit_ed25519", - }, - "flag" "github.com/peterbourgon/ff/v3" - CloneURL: "http://localhost:8449", - Port: 8449, - }, - Meta: metaArgs{ - Title: "ugit", - Description: "Minimal git server", - }, - "fmt" import ( - - 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") - "fmt" "github.com/peterbourgon/ff/v3/ffyaml" - 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) - "strings" "github.com/peterbourgon/ff/v3/ffyaml" "strings" -) - } - c.Profile.Links = append(c.Profile.Links, profileLink{ - Name: parts[0], - URL: parts[1], - }) - return nil - }) - - return c, 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,87 +1,15 @@ 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) - "fmt" - 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, -import ( "errors" - 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 } diff --git a/flake.lock b/flake.lock index ac832b83c21fb3f2fae2a7a1c0326d192a066c42..43e6a8fb6322feb0996e91639f05bb8e85f73b7f 100644 --- a/flake.lock +++ b/flake.lock @@ -1,88 +1,125 @@ { "nodes": { - "nixpkgs": { + "flake-utils": { + "inputs": { "locked": { + "lastModified": 1704161960, - "lastModified": 1704161960, + }, + "locked": { + "locked": { "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", + "locked": { "owner": "nixos", + "locked": { "repo": "nixpkgs", + "locked": { "rev": "63143ac2c9186be6d9da6035fa22620018c85932", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { - "owner": "nixos", + "id": "flake-utils", + "type": "indirect" { + "locked": { + }, "nixpkgs": { + "locked": { + "lastModified": 1701020769, + "narHash": "sha256-4YzCo7xMzkG/t/VlTHqOg9hvXCvqdWYDX/jpF0h+Wr8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "b608fc233c0592210250974d1bb3c11dfaf95e58", + "type": "github" + }, + "original": { + "owner": "nixos", "repo": "nixpkgs", "type": "github" } }, -{ + "lastModified": 1704161960, "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "inputs": { + "nixpkgs": [ + "nixpkgs" + ] { +{ + "locked": { + "lastModified": 1700580516, + "lastModified": 1704161960, "repo": "nixpkgs", + "ref": "refs/heads/main", + "rev": "a68a81cbc743e84aaee331ae7e58699398dd732d", + "revCount": 167, + "type": "git", + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", { - "rev": "63143ac2c9186be6d9da6035fa22620018c85932", + }, +{ "nodes": { + "nixpkgs": { + "url": "https://git.jojodev.com/jolheiser/nur" } }, - "nodes": { { + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "inputs": { + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "nodes": { - "nodes": { + "nixpkgs": "nixpkgs", - "nodes": { + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "nixpkgs": { - "nodes": { + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "locked": { { -{ "locked": { - "nodes": { +{ "lastModified": 1704161960, - "nodes": { "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", + "lastModified": 1704161960, - "nodes": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "owner": "nixos", - "nodes": { + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "repo": "nixpkgs", - "nodes": { + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", "rev": "63143ac2c9186be6d9da6035fa22620018c85932", - "nixpkgs": { + "owner": "nixos", - "nixpkgs": { { }, "original": { - "type": "git", + "owner": "nix-systems", - "nixpkgs": { + "repo": "default", { } }, - "nixpkgs": { "nodes": { +{ "inputs": { "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1699401590, - "narHash": "sha256-nx8ExuBRUux9eXSUgkWp1LJMvA3dmA76+2xggZjHTU0=", - "nixpkgs": { + "nodes": { "lastModified": 1704161960, - "nixpkgs": { + "nodes": { "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", - "nixpkgs": { + "nodes": { "owner": "nixos", + "rev": "afca060674b20e0ccecde2d6fe88c887790219a5", + "revCount": 1, "type": "git", "nixpkgs": { - "repo": "nixpkgs", +{ }, "original": { "type": "git", "nixpkgs": { - "repo": "nixpkgs", +{ } } }, diff --git a/flake.nix b/flake.nix index cdcff1f7b1fcd2efa8f0122da5998941ba0aee32..e2c64454e7053bb255b1bd369ffad2a81877c774 100644 --- a/flake.nix +++ b/flake.nix @@ -1,203 +1,65 @@ { - description = "Minimal git server"; - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - 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"; - }; }; { - -{ + description = "Minimal git server"; inputs = { { + description = "Minimal git server"; nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - tailwind-ctp, - tailwind-ctp-lsp, -{ inputs.nixpkgs.follows = "nixpkgs"; -{ }; - pkgs = nixpkgs.legacyPackages.${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"; - description = "Minimal git server"; tailwind-ctp = { - description = "Minimal git server"; url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp"; - description = "Minimal git server"; inputs.nixpkgs.follows = "nixpkgs"; - description = "Minimal git server"; }; - subPackages = ["cmd/ugitd"]; - { - description = "Minimal git server"; - - homepage = "https://git.jolheiser.com/ugit"; - maintainers = with maintainers; [jolheiser]; - mainProgram = "ugitd"; +{ - url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp"; - }; - in { - packages.${system}.default = ugit; +{ inputs = { - inputs = { { - inputs = { description = "Minimal git server"; - gopls - templ - tailwind-ctp - inputs = { tailwind-ctp = { - vscode-langservers-extracted - ]; - }; - nixosModules.default = { +{ nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; { - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; description = "Minimal git server"; - ... - }: let - cfg = config.services.ugit; - configFile = pkgs.writeText "ugit.yaml" cfg.configFile; - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp"; - in { - options = with lib; { +{ tailwind-ctp = { - tailwind-ctp = { { - - package = mkOption { - type = types.package; - description = "ugit package to use"; - default = ugit; - }; - - repoDir = mkOption { - tailwind-ctp = { inputs.nixpkgs.follows = "nixpkgs"; - tailwind-ctp = { +{ }; - url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp"; + description = "Minimal git server"; - }; - - url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp"; { - url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp"; description = "Minimal git server"; - description = "list of keys to use for authorized_keys"; - default = []; - }; - - authorizedKeysFile = mkOption { - tailwind-ctp = { inputs.nixpkgs.follows = "nixpkgs"; - description = "path to authorized_keys file ugit uses for auth"; - default = "/var/lib/ugit/authorized_keys"; - }; + tailwind-ctp = inputs.tailwind-ctp.packages.${system}.default; - url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp"; inputs.nixpkgs.follows = "nixpkgs"; - type = types.str; - description = "path to host key file (will be created if it doesn't exist)"; - inputs.nixpkgs.follows = "nixpkgs"; + inputs = { - }; - - inputs.nixpkgs.follows = "nixpkgs"; + inputs = { { - 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; - }; { - }; - - }; description = "Minimal git server"; }; - }; { - }; - }; - }; - config = lib.mkIf cfg.enable { - users.users."${cfg.user}" = { - home = "/var/lib/ugit"; - createHome = true; - group = "${cfg.group}"; - isSystemUser = true; - tailwind-ctp-lsp = { { -{ - }; { - description = "Minimal git server"; - networking.firewall = lib.mkIf cfg.openFirewall { - tailwind-ctp-lsp = { inputs = { - }; - - tailwind-ctp-lsp = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; { - tailwind-ctp = { - script = let - authorizedKeysPath = - if (builtins.length cfg.authorizedKeys) > 0 - then authorizedKeysFile - else cfg.authorizedKeysFile; - url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp-intellisense"; + description = "Minimal git server"; - in "${cfg.package}/bin/ugitd ${builtins.concatStringsSep " " args}"; - url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp-intellisense"; inputs = { - after = ["network-online.target"]; - serviceConfig = { - User = cfg.user; - url = "git+https://git.jojodev.com/jolheiser/tailwind-ctp-intellisense"; inputs.nixpkgs.follows = "nixpkgs"; - 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,63 +3,57 @@ 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 module go.jolheiser.com/ugit - 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 -module go.jolheiser.com/ugit github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 require ( dario.cat/mergo v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/a-h/templ v0.2.513 -module go.jolheiser.com/ugit + 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/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 - github.com/charmbracelet/wish v1.2.0 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 go 1.20 - github.com/alecthomas/chroma/v2 v2.12.0 -go 1.20 github.com/charmbracelet/log v0.2.5 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/sergi/go-diff v1.1.0 // indirect - github.com/skeema/knownhosts v1.2.1 // indirect + github.com/skeema/knownhosts v1.2.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect -require ( + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect github.com/a-h/templ v0.2.513 + gopkg.in/yaml.v2 v2.4.0 // indirect +) + require ( github.com/alecthomas/chroma/v2 v2.12.0 + +module go.jolheiser.com/ugit require ( - github.com/charmbracelet/log v0.2.5 - golang.org/x/sys v0.15.0 // indirect + golang.org/x/mod v0.12.0 // indirect + github.com/alecthomas/chroma/v2 v2.12.0 require ( - github.com/charmbracelet/wish v1.2.0 + github.com/alecthomas/chroma/v2 v2.12.0 github.com/a-h/templ v0.2.513 - gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 3e36031585f6c948cf921175cd5e582c621c3fea..fe3c6a39859fa096422ff78f57f8cc5450d7f43d 100644 --- a/go.sum +++ b/go.sum @@ -3,20 +3,16 @@ 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= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +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= @@ -33,41 +29,31 @@ 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/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -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/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= 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/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 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.10.0 h1:F0x3xXrAWmhwtzoCokU4IMPcBdncG+HAAqi9FcOOjbQ= -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/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= @@ -79,8 +65,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/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.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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= @@ -101,49 +89,36 @@ 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.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= 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/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -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= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/a-h/templ v0.2.513 h1:ZmwGAOx4NYllnHy+FTpusc4+c5msoMpPIYX0Oy3dNqw= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= +github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM= 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= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +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= @@ -150,15 +126,16 @@ 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= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= -github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM= 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= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 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= @@ -173,18 +150,20 @@ 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= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/a-h/templ v0.2.513 h1:ZmwGAOx4NYllnHy+FTpusc4+c5msoMpPIYX0Oy3dNqw= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= 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= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= 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= @@ -192,16 +171,17 @@ 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= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/a-h/templ v0.2.513 h1:ZmwGAOx4NYllnHy+FTpusc4+c5msoMpPIYX0Oy3dNqw= 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= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/a-h/templ v0.2.513/go.mod h1:9gZxTLtRzM3gQxO8jr09Na0v8/jfliS97S9W5SScanM= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/charmbracelet/keygen v0.5.0 h1:XY0fsoYiCSM9axkrU+2ziE6u6YjJulo/b9Dghnw6MZc= 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= @@ -209,8 +189,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,40 +3,27 @@ import ( "fmt" - "github.com/charmbracelet/log" "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" - "github.com/charmbracelet/wish/logging" -) - -// Settings holds the configuration for the SSH server -type Settings struct { - AuthorizedKeys string - CloneURL string -package ssh "fmt" package ssh - "github.com/charmbracelet/log" - RepoDir string -} - -package ssh "github.com/charmbracelet/wish/logging" -package ssh ) + "fmt" -package ssh - - + "fmt" import ( - + "fmt" "fmt" - + "fmt" "github.com/charmbracelet/log" + "fmt" + "fmt" "github.com/charmbracelet/ssh" + logging.Middleware(), ), ) if err != nil { @@ -44,19 +32,13 @@ return s, nil } -type hooks struct{} +type app struct{} -import ( "fmt" +) -import ( "github.com/charmbracelet/log" - -var ( -import ( +package ssh "github.com/charmbracelet/wish" - NoopLogger logging.Logger = noopLogger{} -) - -type noopLogger struct{} +func (a app) Push(_ string, _ ssh.PublicKey) {} + "github.com/charmbracelet/log" -func (n noopLogger) Printf(format string, v ...interface{}) {} 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 -}