mirror of
https://github.com/fiatjaf/nak.git
synced 2025-12-08 16:48:51 +00:00
git betterments with remote and branch determination, force-push and fast-forward check.
This commit is contained in:
257
git.go
257
git.go
@@ -212,25 +212,8 @@ var gitInit = &cli.Command{
|
||||
ownerNpub := nip19.EncodeNpub(pk)
|
||||
|
||||
// check existing git remotes
|
||||
cmd = exec.Command("git", "remote", "-v")
|
||||
output, err := cmd.Output()
|
||||
nostrRemote, _, _, err := getGitNostrRemote(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get git remotes: %w", err)
|
||||
}
|
||||
|
||||
remotes := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
var nostrRemote string
|
||||
for _, remote := range remotes {
|
||||
if strings.Contains(remote, "nostr://") {
|
||||
parts := strings.Fields(remote)
|
||||
if len(parts) >= 2 {
|
||||
nostrRemote = parts[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if nostrRemote == "" {
|
||||
remoteURL := fmt.Sprintf("nostr://%s/%s/%s", ownerNpub, config.GraspServers[0], config.Identifier)
|
||||
cmd = exec.Command("git", "remote", "add", "origin", remoteURL)
|
||||
if err := cmd.Run(); err != nil {
|
||||
@@ -400,37 +383,14 @@ func promptForConfig(config *Nip34Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func gitSanityCheck(localConfig Nip34Config, nostrRemote string) (nostr.PubKey, error) {
|
||||
urlParts := strings.TrimPrefix(nostrRemote, "nostr://")
|
||||
parts := strings.Split(urlParts, "/")
|
||||
if len(parts) != 3 {
|
||||
return nostr.ZeroPK, fmt.Errorf("invalid nostr URL format, expected nostr://<npub>/<relay_hostname>/<identifier>, got: %s", nostrRemote)
|
||||
}
|
||||
|
||||
remoteNpub := parts[0]
|
||||
remoteHostname := parts[1]
|
||||
remoteIdentifier := parts[2]
|
||||
|
||||
ownerPk, err := parsePubKey(localConfig.Owner)
|
||||
if err != nil {
|
||||
return nostr.ZeroPK, fmt.Errorf("invalid owner public key: %w", err)
|
||||
}
|
||||
if nip19.EncodeNpub(ownerPk) != remoteNpub {
|
||||
return nostr.ZeroPK, fmt.Errorf("owner in nip34.json does not match git remote npub")
|
||||
}
|
||||
if remoteIdentifier != localConfig.Identifier {
|
||||
return nostr.ZeroPK, fmt.Errorf("git remote identifier '%s' differs from nip34.json identifier '%s'", remoteIdentifier, localConfig.Identifier)
|
||||
}
|
||||
if !slices.Contains(localConfig.GraspServers, remoteHostname) {
|
||||
return nostr.ZeroPK, fmt.Errorf("git remote relay '%s' not in grasp servers %v", remoteHostname, localConfig.GraspServers)
|
||||
}
|
||||
return ownerPk, nil
|
||||
}
|
||||
|
||||
var gitPush = &cli.Command{
|
||||
Name: "push",
|
||||
Usage: "push git changes",
|
||||
Flags: defaultKeyFlags,
|
||||
Flags: append(defaultKeyFlags, &cli.BoolFlag{
|
||||
Name: "force",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "force push to git remotes",
|
||||
}),
|
||||
Action: func(ctx context.Context, c *cli.Command) error {
|
||||
// setup signer
|
||||
kr, _, err := gatherKeyerFromArguments(ctx, c)
|
||||
@@ -455,26 +415,9 @@ var gitPush = &cli.Command{
|
||||
}
|
||||
|
||||
// get git remotes
|
||||
cmd := exec.Command("git", "remote", "-v")
|
||||
output, err := cmd.Output()
|
||||
nostrRemote, localBranch, remoteBranch, err := getGitNostrRemote(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get git remotes: %w", err)
|
||||
}
|
||||
|
||||
remotes := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
var nostrRemote string
|
||||
for _, remote := range remotes {
|
||||
if strings.Contains(remote, "nostr://") {
|
||||
parts := strings.Fields(remote)
|
||||
if len(parts) >= 2 {
|
||||
nostrRemote = parts[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if nostrRemote == "" {
|
||||
return fmt.Errorf("no nostr:// remote found")
|
||||
return err
|
||||
}
|
||||
|
||||
// parse the URL: nostr://<npub>/<relay_hostname>/<identifier>
|
||||
@@ -521,20 +464,14 @@ var gitPush = &cli.Command{
|
||||
log("found state event: %s\n", state.Event.ID)
|
||||
}
|
||||
|
||||
// get current branch and commit
|
||||
res, err := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
|
||||
// get commit for the local branch
|
||||
res, err := exec.Command("git", "rev-parse", localBranch).Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current branch: %w", err)
|
||||
}
|
||||
currentBranch := strings.TrimSpace(string(res))
|
||||
|
||||
res, err = exec.Command("git", "rev-parse", "HEAD").Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current commit: %w", err)
|
||||
return fmt.Errorf("failed to get commit for branch %s: %w", localBranch, err)
|
||||
}
|
||||
currentCommit := strings.TrimSpace(string(res))
|
||||
|
||||
log("current branch: %s, commit: %s\n", currentBranch, currentCommit)
|
||||
log("pushing branch %s to remote branch %s, commit: %s\n", localBranch, remoteBranch, currentCommit)
|
||||
|
||||
// create a new state if we didn't find any
|
||||
if state.Event.ID == nostr.ZeroID {
|
||||
@@ -546,13 +483,22 @@ var gitPush = &cli.Command{
|
||||
}
|
||||
|
||||
// update the branch
|
||||
state.Branches[currentBranch] = currentCommit
|
||||
log("> setting branch %s to commit %s\n", currentBranch, currentCommit)
|
||||
if !c.Bool("force") {
|
||||
if prevCommit, exists := state.Branches[remoteBranch]; exists {
|
||||
// check if prevCommit is an ancestor of currentCommit (fast-forward check)
|
||||
cmd := exec.Command("git", "merge-base", "--is-ancestor", prevCommit, currentCommit)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("non-fast-forward push not allowed, use --force to override")
|
||||
}
|
||||
}
|
||||
}
|
||||
state.Branches[remoteBranch] = currentCommit
|
||||
log("> setting branch %s to commit %s\n", remoteBranch, currentCommit)
|
||||
|
||||
// set the HEAD to the current branch if none is set
|
||||
// set the HEAD to the local branch if none is set
|
||||
if state.HEAD == "" {
|
||||
state.HEAD = currentBranch
|
||||
log("> setting HEAD to branch %s\n", currentBranch)
|
||||
state.HEAD = remoteBranch
|
||||
log("> setting HEAD to branch %s\n", remoteBranch)
|
||||
}
|
||||
|
||||
// create and sign the new state event
|
||||
@@ -573,13 +519,21 @@ var gitPush = &cli.Command{
|
||||
|
||||
// push to git clone URLs
|
||||
for _, cloneURL := range repo.Clone {
|
||||
log("> pushing to: %s\n", cloneURL)
|
||||
cmd := exec.Command("git", "push", cloneURL, fmt.Sprintf("refs/heads/%s:refs/heads/%s", currentBranch, currentBranch))
|
||||
log("> pushing to: %s\n", color.CyanString(cloneURL))
|
||||
args := []string{"push"}
|
||||
if c.Bool("force") {
|
||||
args = append(args, "--force")
|
||||
}
|
||||
args = append(args,
|
||||
cloneURL,
|
||||
fmt.Sprintf("refs/heads/%s:refs/heads/%s", localBranch, remoteBranch),
|
||||
)
|
||||
cmd := exec.Command("git", args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log("(!) failed to push to %s: %v\n%s\n", cloneURL, err, string(output))
|
||||
log("> failed to push to %s: %v\n%s\n", color.RedString(cloneURL), err, string(output))
|
||||
} else {
|
||||
log("> successfully pushed to %s\n", cloneURL)
|
||||
log("> successfully pushed to %s\n", color.GreenString(cloneURL))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,26 +580,9 @@ var gitAnnounce = &cli.Command{
|
||||
}
|
||||
|
||||
// get git remotes
|
||||
cmd = exec.Command("git", "remote", "-v")
|
||||
output, err := cmd.Output()
|
||||
nostrRemote, _, _, err := getGitNostrRemote(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get git remotes: %w", err)
|
||||
}
|
||||
|
||||
remotes := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
var nostrRemote string
|
||||
for _, remote := range remotes {
|
||||
if strings.Contains(remote, "nostr://") {
|
||||
parts := strings.Fields(remote)
|
||||
if len(parts) >= 2 {
|
||||
nostrRemote = parts[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if nostrRemote == "" {
|
||||
return fmt.Errorf("no nostr:// remote found")
|
||||
return err
|
||||
}
|
||||
|
||||
ownerPk, err := gitSanityCheck(localConfig, nostrRemote)
|
||||
@@ -734,3 +671,117 @@ var gitAnnounce = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func getGitNostrRemote(c *cli.Command) (
|
||||
remoteURL string,
|
||||
localBranch string,
|
||||
remoteBranch string,
|
||||
err error,
|
||||
) {
|
||||
// remote
|
||||
var remoteName string
|
||||
var cmd *exec.Cmd
|
||||
args := c.Args()
|
||||
if args.Len() > 0 {
|
||||
remoteName = args.Get(0)
|
||||
} else {
|
||||
// get current branch
|
||||
cmd = exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("failed to get current branch: %w", err)
|
||||
}
|
||||
branch := strings.TrimSpace(string(output))
|
||||
// get remote for branch
|
||||
cmd = exec.Command("git", "config", "--get", fmt.Sprintf("branch.%s.remote", branch))
|
||||
output, err = cmd.Output()
|
||||
if err != nil {
|
||||
remoteName = "origin"
|
||||
} else {
|
||||
remoteName = strings.TrimSpace(string(output))
|
||||
}
|
||||
}
|
||||
// get the URL
|
||||
cmd = exec.Command("git", "remote", "get-url", remoteName)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("remote '%s' does not exist", remoteName)
|
||||
}
|
||||
remoteURL = strings.TrimSpace(string(output))
|
||||
if !strings.Contains(remoteURL, "nostr://") {
|
||||
return "", "", "", fmt.Errorf("remote '%s' is not a nostr remote: %s", remoteName, remoteURL)
|
||||
}
|
||||
|
||||
// branch (local and remote)
|
||||
if args.Len() > 1 {
|
||||
branchSpec := args.Get(1)
|
||||
if strings.Contains(branchSpec, ":") {
|
||||
parts := strings.Split(branchSpec, ":")
|
||||
if len(parts) == 2 {
|
||||
localBranch = parts[0]
|
||||
remoteBranch = parts[1]
|
||||
} else {
|
||||
return "", "", "", fmt.Errorf("invalid branch spec: %s", branchSpec)
|
||||
}
|
||||
} else {
|
||||
localBranch = branchSpec
|
||||
}
|
||||
} else {
|
||||
// get current branch
|
||||
cmd = exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("failed to get current branch: %w", err)
|
||||
}
|
||||
localBranch = strings.TrimSpace(string(output))
|
||||
}
|
||||
|
||||
// get the upstream branch from git config
|
||||
cmd = exec.Command("git", "config", "--get", fmt.Sprintf("branch.%s.merge", localBranch))
|
||||
output, err = cmd.Output()
|
||||
if err == nil {
|
||||
// parse refs/heads/<branch-name> to get just the branch name
|
||||
mergeRef := strings.TrimSpace(string(output))
|
||||
if strings.HasPrefix(mergeRef, "refs/heads/") {
|
||||
remoteBranch = strings.TrimPrefix(mergeRef, "refs/heads/")
|
||||
} else {
|
||||
// fallback if it's not in expected format
|
||||
remoteBranch = localBranch
|
||||
}
|
||||
} else {
|
||||
// no upstream configured, assume same branch name
|
||||
remoteBranch = localBranch
|
||||
}
|
||||
|
||||
return remoteURL, localBranch, remoteBranch, nil
|
||||
}
|
||||
|
||||
func gitSanityCheck(
|
||||
localConfig Nip34Config,
|
||||
nostrRemote string,
|
||||
) (nostr.PubKey, error) {
|
||||
urlParts := strings.TrimPrefix(nostrRemote, "nostr://")
|
||||
parts := strings.Split(urlParts, "/")
|
||||
if len(parts) != 3 {
|
||||
return nostr.ZeroPK, fmt.Errorf("invalid nostr URL format, expected nostr://<npub>/<relay_hostname>/<identifier>, got: %s", nostrRemote)
|
||||
}
|
||||
|
||||
remoteNpub := parts[0]
|
||||
remoteHostname := parts[1]
|
||||
remoteIdentifier := parts[2]
|
||||
|
||||
ownerPk, err := parsePubKey(localConfig.Owner)
|
||||
if err != nil {
|
||||
return nostr.ZeroPK, fmt.Errorf("invalid owner public key: %w", err)
|
||||
}
|
||||
if nip19.EncodeNpub(ownerPk) != remoteNpub {
|
||||
return nostr.ZeroPK, fmt.Errorf("owner in nip34.json does not match git remote npub")
|
||||
}
|
||||
if remoteIdentifier != localConfig.Identifier {
|
||||
return nostr.ZeroPK, fmt.Errorf("git remote identifier '%s' differs from nip34.json identifier '%s'", remoteIdentifier, localConfig.Identifier)
|
||||
}
|
||||
if !slices.Contains(localConfig.GraspServers, remoteHostname) {
|
||||
return nostr.ZeroPK, fmt.Errorf("git remote relay '%s' not in grasp servers %v", remoteHostname, localConfig.GraspServers)
|
||||
}
|
||||
return ownerPk, nil
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@@ -30,6 +30,7 @@ require (
|
||||
github.com/FastFilter/xorfilter v0.2.1 // indirect
|
||||
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/bluekeyes/go-gitdiff v0.7.1 // indirect
|
||||
github.com/btcsuite/btcd v0.24.2 // indirect
|
||||
github.com/btcsuite/btcd/btcutil v1.1.5 // indirect
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -13,6 +13,8 @@ github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7X
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/bluekeyes/go-gitdiff v0.7.1 h1:graP4ElLRshr8ecu0UtqfNTCHrtSyZd3DABQm/DWesQ=
|
||||
github.com/bluekeyes/go-gitdiff v0.7.1/go.mod h1:QpfYYO1E0fTVHVZAZKiRjtSGY9823iCdvGXBcEzHGbM=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
|
||||
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
|
||||
@@ -92,8 +94,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
||||
Reference in New Issue
Block a user