Add git host cli tools

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2025-09-01 12:47:26 +01:00
parent 8a21f9bbc7
commit 9d5bfc9d74
14 changed files with 366 additions and 32 deletions

8
flake.lock generated
View File

@@ -511,11 +511,11 @@
"secrets": {
"flake": false,
"locked": {
"lastModified": 1755532677,
"narHash": "sha256-kufX5RjMXg+Vu++JKCuMGsO2xTx9gN9YGCnRNcmthCM=",
"lastModified": 1756726520,
"narHash": "sha256-3HYpc5YQTrshyksigLMCsyZhvu7ghy38etfCUUtE4Hw=",
"ref": "refs/heads/main",
"rev": "646c6d87251e4562aa7b416c438225718746bb8e",
"revCount": 34,
"rev": "52906e7d78b7b4b74e8c1898924d7dc7fa3b4ab7",
"revCount": 36,
"type": "git",
"url": "ssh://git@karaolidis.com/karaolidis/nix-secrets.git"
},

View File

@@ -1,16 +1,12 @@
{ config, inputs, ... }:
{
sops = {
secrets = {
"git/credentials/github.com/public/username".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/github.com/public/password".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
};
secrets."git/credentials/github.com/tokens/public".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
templates.nix-access-tokens = {
content = ''
access-tokens = github.com=${config.sops.placeholder."git/credentials/github.com/public/password"}
access-tokens = github.com=${config.sops.placeholder."git/credentials/github.com/tokens/public"}
'';
group = "users";
};

View File

@@ -41,5 +41,41 @@ in
);
};
};
home = {
packages = with pkgs; [
(pkgs.writeShellApplication {
name = "gh";
runtimeInputs = with pkgs; [ gh ];
text = builtins.readFile ./gh.sh;
})
(pkgs.writeShellApplication {
name = "glab";
runtimeInputs = with pkgs; [ glab ];
text = builtins.readFile ./glab.sh;
})
(pkgs.writeShellApplication {
name = "tea";
runtimeInputs = with pkgs; [ tea ];
text = builtins.readFile ./tea.sh;
})
];
sessionVariables = {
GITEA_HOST = "git.karaolidis.com";
GITEA_SSH_HOST = "karaolidis.com";
};
};
xdg.configFile = {
"gh/config.yml".source = (pkgs.formats.yaml { }).generate "config.yml" {
version = 1;
git_protocol = "ssh";
};
"glab-cli/config.yml".source = (pkgs.formats.yaml { }).generate "config.yml" {
git_protocol = "ssh";
};
};
};
}

View File

@@ -0,0 +1,8 @@
# shellcheck shell=bash
GH_HOST="${GH_HOST:-github.com}"
GH_TOKEN=$(sed -n "s#https://[^:]*:\([^@]*\)@${GH_HOST}#\1#p" "$HOME/.config/git/credentials")
export GH_TOKEN
exec gh "$@"

View File

@@ -0,0 +1,8 @@
# shellcheck shell=bash
GITLAB_HOST="${GITLAB_HOST:-gitlab.com}"
GITLAB_TOKEN=$(sed -n "s#https://[^:]*:\([^@]*\)@${GITLAB_HOST}#\1#p" "$HOME/.config/git/credentials")
export GITLAB_TOKEN
exec glab "$@"

View File

@@ -0,0 +1,13 @@
# shellcheck shell=bash
GITEA_HOST="${GITEA_HOST:-gitea.com}"
GITEA_SSH_HOST="${GITEA_SSH_HOST:-gitea.com}"
GITEA_TOKEN=$(sed -n "s#https://[^:]*:\([^@]*\)@${GITEA_HOST}#\1#p" "$HOME/.config/git/credentials")
GITEA_INSTANCE_URL="https://${GITEA_HOST}"
GITEA_INSTANCE_SSH_HOST="$GITEA_SSH_HOST"
export GITEA_TOKEN
export GITEA_INSTANCE_URL
export GITEA_INSTANCE_SSH_HOST
exec tea "$@"

View File

@@ -20,6 +20,10 @@
enable = true;
defaultCacheTtl = 31536000;
maxCacheTtl = 31536000;
pinentry = {
package = pkgs.pinentry-all;
program = "pinentry-tty";
};
};
systemd.user = {

View File

@@ -46,35 +46,71 @@ in
key = "ssh/rsa/pass";
};
"git/credentials/personal/git.karaolidis.com/admin/username" = {
"git/credentials/personal/git.karaolidis.com/username" = {
sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
key = "git/credentials/git.karaolidis.com/admin/username";
key = "git/credentials/git.karaolidis.com/username";
};
"git/credentials/personal/git.karaolidis.com/admin/password" = {
"git/credentials/personal/git.karaolidis.com/tokens/admin" = {
sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
key = "git/credentials/git.karaolidis.com/admin/password";
key = "git/credentials/git.karaolidis.com/tokens/admin";
};
"git/credentials/sas/github.com/admin/username" = {
"git/credentials/sas/github.com/username" = {
sopsFile = "${inputs.secrets}/domains/sas/secrets.yaml";
key = "git/credentials/github.com/admin/username";
key = "git/credentials/github.com/username";
};
"git/credentials/sas/github.com/admin/password" = {
"git/credentials/sas/github.com/tokens/admin" = {
sopsFile = "${inputs.secrets}/domains/sas/secrets.yaml";
key = "git/credentials/github.com/admin/password";
key = "git/credentials/github.com/tokens/admin";
};
"git/credentials/personal/github.com/username" = {
sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
key = "git/credentials/github.com/username";
};
"git/credentials/personal/github.com/tokens/admin" = {
sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
key = "git/credentials/github.com/tokens/admin";
};
"git/credentials/personal/gitlab.com/username" = {
sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
key = "git/credentials/gitlab.com/username";
};
"git/credentials/personal/gitlab.com/tokens/admin" = {
sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
key = "git/credentials/gitlab.com/tokens/admin";
};
"git/credentials/personal/gitea.com/username" = {
sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
key = "git/credentials/gitea.com/username";
};
"git/credentials/personal/gitea.com/tokens/admin" = {
sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
key = "git/credentials/gitea.com/tokens/admin";
};
};
templates."git/credentials" = {
content = ''
https://${hmConfig.sops.placeholder."git/credentials/personal/git.karaolidis.com/admin/username"}:${
hmConfig.sops.placeholder."git/credentials/personal/git.karaolidis.com/admin/password"
https://${hmConfig.sops.placeholder."git/credentials/personal/git.karaolidis.com/username"}:${
hmConfig.sops.placeholder."git/credentials/personal/git.karaolidis.com/tokens/admin"
}@git.karaolidis.com
https://${hmConfig.sops.placeholder."git/credentials/sas/github.com/admin/username"}:${
hmConfig.sops.placeholder."git/credentials/sas/github.com/admin/password"
https://${hmConfig.sops.placeholder."git/credentials/sas/github.com/username"}:${
hmConfig.sops.placeholder."git/credentials/sas/github.com/tokens/admin"
}@github.com
https://${hmConfig.sops.placeholder."git/credentials/personal/gitlab.com/username"}:${
hmConfig.sops.placeholder."git/credentials/personal/gitlab.com/tokens/admin"
}@gitlab.com
https://${hmConfig.sops.placeholder."git/credentials/personal/gitea.com/username"}:${
hmConfig.sops.placeholder."git/credentials/personal/gitea.com/tokens/admin"
}@gitea.com
'';
path = "${home}/.config/git/credentials";
};

View File

@@ -19,18 +19,42 @@ in
"ssh/pass".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/git.karaolidis.com/admin/username".sopsFile =
"git/credentials/git.karaolidis.com/username".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/git.karaolidis.com/admin/password".sopsFile =
"git/credentials/git.karaolidis.com/tokens/admin".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/github.com/username".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/github.com/tokens/admin".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/gitlab.com/username".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/gitlab.com/tokens/admin".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/gitea.com/username".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/gitea.com/tokens/admin".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
};
templates."git/credentials" = {
content = ''
https://${hmConfig.sops.placeholder."git/credentials/git.karaolidis.com/admin/username"}:${
hmConfig.sops.placeholder."git/credentials/git.karaolidis.com/admin/password"
https://${hmConfig.sops.placeholder."git/credentials/git.karaolidis.com/username"}:${
hmConfig.sops.placeholder."git/credentials/git.karaolidis.com/tokens/admin"
}@git.karaolidis.com
https://${hmConfig.sops.placeholder."git/credentials/github.com/username"}:${
hmConfig.sops.placeholder."git/credentials/github.com/tokens/admin"
}@github.com
https://${hmConfig.sops.placeholder."git/credentials/gitlab.com/username"}:${
hmConfig.sops.placeholder."git/credentials/gitlab.com/tokens/admin"
}@gitlab.com
https://${hmConfig.sops.placeholder."git/credentials/gitea.com/username"}:${
hmConfig.sops.placeholder."git/credentials/gitea.com/tokens/admin"
}@gitea.com
'';
path = "${home}/.config/git/credentials";
};

View File

@@ -19,18 +19,42 @@ in
"ssh/pass".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/git.karaolidis.com/admin/username".sopsFile =
"git/credentials/git.karaolidis.com/username".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/git.karaolidis.com/admin/password".sopsFile =
"git/credentials/git.karaolidis.com/tokens/admin".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/github.com/username".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/github.com/tokens/admin".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/gitlab.com/username".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/gitlab.com/tokens/admin".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/gitea.com/username".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"git/credentials/gitea.com/tokens/admin".sopsFile =
"${inputs.secrets}/domains/personal/secrets.yaml";
};
templates."git/credentials" = {
content = ''
https://${hmConfig.sops.placeholder."git/credentials/git.karaolidis.com/admin/username"}:${
hmConfig.sops.placeholder."git/credentials/git.karaolidis.com/admin/password"
https://${hmConfig.sops.placeholder."git/credentials/git.karaolidis.com/username"}:${
hmConfig.sops.placeholder."git/credentials/git.karaolidis.com/tokens/admin"
}@git.karaolidis.com
https://${hmConfig.sops.placeholder."git/credentials/github.com/username"}:${
hmConfig.sops.placeholder."git/credentials/github.com/tokens/admin"
}@github.com
https://${hmConfig.sops.placeholder."git/credentials/gitlab.com/username"}:${
hmConfig.sops.placeholder."git/credentials/gitlab.com/tokens/admin"
}@gitlab.com
https://${hmConfig.sops.placeholder."git/credentials/gitea.com/username"}:${
hmConfig.sops.placeholder."git/credentials/gitea.com/tokens/admin"
}@gitea.com
'';
path = "${home}/.config/git/credentials";
};

View File

@@ -5,6 +5,7 @@ final: prev:
hyprland = import ./hyprland final prev;
mpv = import ./mpv final prev;
spicetify-cli = import ./spicetify-cli final prev;
tea = import ./tea final prev;
telepresence = import ./telepresence final prev;
}
// (import ../packages { pkgs = final; })

10
overlays/tea/default.nix Normal file
View File

@@ -0,0 +1,10 @@
final: prev:
prev.tea.overrideAttrs (oldAttrs: {
patches = oldAttrs.patches or [ ] ++ [
(builtins.fetchurl {
url = "https://gitea.com/gitea/tea/pulls/639.patch";
sha256 = "sha256:0c5gpi6aajd3h0wp7lrvj5qk9wsqhgbap7ijvl0x117v0g8mgzvs";
})
./instance-ssh-host-env.patch
];
})

View File

@@ -0,0 +1,174 @@
diff --git a/modules/config/login.go b/modules/config/login.go
index 3b77fb9..94de9cd 100644
--- a/modules/config/login.go
+++ b/modules/config/login.go
@@ -13,6 +13,7 @@ import (
"net/http/cookiejar"
"net/url"
"os"
+ "strconv"
"strings"
"time"
@@ -200,6 +201,63 @@ func UpdateLogin(login *Login) error {
return saveConfig()
}
+// CreateLoginFromEnvVars returns a login based on environment variables, or nil if no login can be created
+func CreateLoginFromEnvVars() (*Login, error) {
+ var token string
+
+ giteaToken := os.Getenv("GITEA_TOKEN")
+ githubToken := os.Getenv("GH_TOKEN")
+ giteaInstanceURL := os.Getenv("GITEA_INSTANCE_URL")
+ instanceInsecure := os.Getenv("GITEA_INSTANCE_INSECURE")
+ giteaInstanceSSHHost := os.Getenv("GITEA_INSTANCE_SSH_HOST")
+ insecure := false
+ if len(instanceInsecure) > 0 {
+ insecure, _ = strconv.ParseBool(instanceInsecure)
+ }
+
+ // if no tokens are set, or no instance url for gitea fail fast
+ if len(giteaInstanceURL) == 0 || (len(giteaToken) == 0 && len(githubToken) == 0) {
+ return nil, nil
+ }
+
+ token = giteaToken
+ if len(giteaToken) == 0 {
+ token = githubToken
+ }
+
+ login := &Login{
+ Name: "GITEA_LOGIN_VIA_ENV",
+ URL: giteaInstanceURL,
+ Token: token,
+ SSHHost: giteaInstanceSSHHost,
+ Insecure: insecure,
+ SSHKey: "",
+ SSHCertPrincipal: "",
+ SSHKeyFingerprint: "",
+ SSHAgent: false,
+ VersionCheck: true,
+ Created: time.Now().Unix(),
+ }
+
+ client := login.Client()
+ u, _, err := client.GetMyUserInfo()
+ if err != nil {
+ return nil, fmt.Errorf("failed to validate token: %s", err)
+ }
+
+ login.User = u.UserName
+
+ if login.SSHHost == "" {
+ parsedURL, err := url.Parse(giteaInstanceURL)
+ if err != nil {
+ return nil, err
+ }
+ login.SSHHost = parsedURL.Host
+ }
+
+ return login, nil
+}
+
// Client returns a client to operate Gitea API. You may provide additional modifiers
// for the client like gitea.SetBasicAuth() for customization
func (l *Login) Client(options ...gitea.ClientOption) *gitea.Client {
diff --git a/modules/context/context.go b/modules/context/context.go
index aec5592..636eeec 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -9,9 +9,7 @@ import (
"log"
"os"
"path"
- "strconv"
"strings"
- "time"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/git"
@@ -108,16 +106,6 @@ func InitCommand(cmd *cli.Command) *TeaContext {
c.RepoSlug = repoFlag
}
- // override config user with env variable
- envLogin := GetLoginByEnvVar()
- if envLogin != nil {
- _, err := utils.ValidateAuthenticationMethod(envLogin.URL, envLogin.Token, "", "", false, "", "")
- if err != nil {
- log.Fatal(err.Error())
- }
- c.Login = envLogin
- }
-
// override login from flag, or use default login if repo based detection failed
if len(loginFlag) != 0 {
c.Login = config.GetLoginByName(loginFlag)
@@ -196,10 +184,25 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
return repo, nil, "", fmt.Errorf("Remote '%s' not found in this Git repository", remoteValue)
}
+ envLogin, err := config.CreateLoginFromEnvVars()
+ if err != nil {
+ log.Fatal(err.Error())
+ }
+
logins, err := config.GetLogins()
if err != nil {
return repo, nil, "", err
}
+
+ if envLogin != nil {
+ _, err := utils.ValidateAuthenticationMethod(envLogin.URL, envLogin.Token, "", "", false, "", "")
+ if err != nil {
+ log.Fatal(err.Error())
+ }
+
+ logins = append([]config.Login{*envLogin}, logins...)
+ }
+
for _, l := range logins {
sshHost := l.GetSSHHost()
for _, u := range remoteConfig.URLs {
@@ -223,40 +226,3 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
return repo, nil, "", errNotAGiteaRepo
}
-
-// GetLoginByEnvVar returns a login based on environment variables, or nil if no login can be created
-func GetLoginByEnvVar() *config.Login {
- var token string
-
- giteaToken := os.Getenv("GITEA_TOKEN")
- githubToken := os.Getenv("GH_TOKEN")
- giteaInstanceURL := os.Getenv("GITEA_INSTANCE_URL")
- instanceInsecure := os.Getenv("GITEA_INSTANCE_INSECURE")
- insecure := false
- if len(instanceInsecure) > 0 {
- insecure, _ = strconv.ParseBool(instanceInsecure)
- }
-
- // if no tokens are set, or no instance url for gitea fail fast
- if len(giteaInstanceURL) == 0 || (len(giteaToken) == 0 && len(githubToken) == 0) {
- return nil
- }
-
- token = giteaToken
- if len(giteaToken) == 0 {
- token = githubToken
- }
-
- return &config.Login{
- Name: "GITEA_LOGIN_VIA_ENV",
- URL: giteaInstanceURL,
- Token: token,
- Insecure: insecure,
- SSHKey: "",
- SSHCertPrincipal: "",
- SSHKeyFingerprint: "",
- SSHAgent: false,
- Created: time.Now().Unix(),
- VersionCheck: false,
- }
-}