git: more resiliency when updating nip34.json

This commit is contained in:
fiatjaf
2025-11-30 22:21:56 -03:00
parent 210cf66d5f
commit 852fe6bdfb

114
git.go
View File

@@ -181,7 +181,7 @@ aside from those, there is also:
var fetchedRepo *nip34.Repository var fetchedRepo *nip34.Repository
if existingConfig.Identifier == "" { if existingConfig.Identifier == "" {
log(" searching for existing events... ") log(" searching for existing events... ")
repo, _, err := fetchRepositoryAndState(ctx, owner, identifier, nil) repo, _, _, err := fetchRepositoryAndState(ctx, owner, identifier, nil)
if err == nil && repo.Event.ID != nostr.ZeroID { if err == nil && repo.Event.ID != nostr.ZeroID {
fetchedRepo = &repo fetchedRepo = &repo
log("found one from %s.\n", repo.Event.CreatedAt.Time().Format(time.DateOnly)) log("found one from %s.\n", repo.Event.CreatedAt.Time().Format(time.DateOnly))
@@ -367,7 +367,7 @@ aside from those, there is also:
} }
// fetch repository metadata and state // fetch repository metadata and state
repo, state, err := fetchRepositoryAndState(ctx, owner, identifier, relayHints) repo, _, state, err := fetchRepositoryAndState(ctx, owner, identifier, relayHints)
if err != nil { if err != nil {
return err return err
} }
@@ -555,6 +555,7 @@ aside from those, there is also:
} }
pushCmd := exec.Command("git", pushArgs...) pushCmd := exec.Command("git", pushArgs...)
pushCmd.Stderr = os.Stderr pushCmd.Stderr = os.Stderr
pushCmd.Stdout = os.Stdout
if err := pushCmd.Run(); err != nil { if err := pushCmd.Run(); err != nil {
log("! failed to push to %s: %v\n", color.YellowString(remoteName), err) log("! failed to push to %s: %v\n", color.YellowString(remoteName), err)
} else { } else {
@@ -821,9 +822,26 @@ func gitSync(ctx context.Context, signer nostr.Keyer) (nip34.Repository, *nip34.
} }
// fetch repository announcement and state from relays // fetch repository announcement and state from relays
repo, state, err := fetchRepositoryAndState(ctx, owner, localConfig.Identifier, localConfig.GraspServers) repo, upToDateRelays, state, err := fetchRepositoryAndState(ctx, owner, localConfig.Identifier, localConfig.GraspServers)
if err != nil && repo.Event.ID == nostr.ZeroID { notUpToDate := func(graspServer string) bool {
log("couldn't fetch repository metadata (%s), will publish now\n", err) return !slices.Contains(upToDateRelays, nostr.NormalizeURL(graspServer))
}
if upToDateRelays == nil || slices.ContainsFunc(localConfig.GraspServers, notUpToDate) {
var relays []string
if upToDateRelays == nil {
// condition 1
relays = append(sys.FetchOutboxRelays(ctx, owner, 3), localConfig.GraspServers...)
log("couldn't fetch repository metadata (%s), will publish now\n", err)
} else {
// condition 2
relays = make([]string, 0, len(localConfig.GraspServers)-1)
for _, gs := range localConfig.GraspServers {
if notUpToDate(gs) {
relays = append(relays, graspServerHost(gs))
}
}
log("some grasp servers (%v) are not up-to-date, will publish to them\n", relays)
}
// create a local repository object from config and publish it // create a local repository object from config and publish it
localRepo := localConfig.ToRepository() localRepo := localConfig.ToRepository()
@@ -840,7 +858,6 @@ func gitSync(ctx context.Context, signer nostr.Keyer) (nip34.Repository, *nip34.
return repo, state, fmt.Errorf("failed to sign announcement: %w", err) return repo, state, fmt.Errorf("failed to sign announcement: %w", err)
} }
relays := append(sys.FetchOutboxRelays(ctx, owner, 3), localConfig.GraspServers...)
for res := range sys.Pool.PublishMany(ctx, relays, event) { for res := range sys.Pool.PublishMany(ctx, relays, event) {
if res.Error != nil { if res.Error != nil {
log("! error publishing to %s: %v\n", color.YellowString(res.RelayURL), res.Error) log("! error publishing to %s: %v\n", color.YellowString(res.RelayURL), res.Error)
@@ -975,38 +992,60 @@ func gitSetupRemotes(ctx context.Context, dir string, repo nip34.Repository) {
if strings.HasPrefix(remote, "nip34/grasp/") { if strings.HasPrefix(remote, "nip34/grasp/") {
graspURL := rebuildGraspURLFromRemote(remote) graspURL := rebuildGraspURLFromRemote(remote)
getUrlCmd := exec.Command("git", "remote", "get-url", remote)
if dir != "" {
getUrlCmd.Dir = dir
}
if output, err := getUrlCmd.Output(); err != nil {
panic(fmt.Errorf("failed to read remote (%s) url from git: %s", remote, err))
} else {
// check if the remote url is correct so we can update it if not
gitURL := fmt.Sprintf("http%s/%s/%s.git", nostr.NormalizeURL(graspURL)[2:], nip19.EncodeNpub(repo.PubKey), repo.ID)
if strings.TrimSpace(string(output)) != gitURL {
goto delete
}
}
// check if this remote is not present in our grasp list anymore
if !slices.Contains(repo.Relays, nostr.NormalizeURL(graspURL)) { if !slices.Contains(repo.Relays, nostr.NormalizeURL(graspURL)) {
delCmd := exec.Command("git", "remote", "remove", remote) goto delete
if dir != "" { }
delCmd.Dir = dir
} continue
if err := delCmd.Run(); err != nil {
logverbose("failed to remove remote %s: %v\n", remote, err) delete:
} logverbose("deleting remote %s\n", remote)
delCmd := exec.Command("git", "remote", "remove", remote)
if dir != "" {
delCmd.Dir = dir
}
if err := delCmd.Run(); err != nil {
logverbose("failed to remove remote %s: %v\n", remote, err)
} }
} }
} }
// create new remotes for each grasp server // create new remotes for each grasp server
remotes = strings.Split(strings.TrimSpace(string(output)), "\n")
for _, relay := range repo.Relays { for _, relay := range repo.Relays {
remote := gitRemoteName(relay) remote := gitRemoteName(relay)
gitURL := fmt.Sprintf("http%s/%s/%s.git", nostr.NormalizeURL(relay)[2:], nip19.EncodeNpub(repo.PubKey), repo.ID)
if !slices.Contains(remotes, remote) { if slices.Contains(remotes, remote) {
// construct the git URL continue
gitURL := fmt.Sprintf("http%s/%s/%s.git", }
relay[2:], nip19.EncodeNpub(repo.PubKey), repo.ID)
addCmd := exec.Command("git", "remote", "add", remote, gitURL) logverbose("adding new remote for '%s'\n", relay)
if dir != "" { addCmd := exec.Command("git", "remote", "add", remote, gitURL)
addCmd.Dir = dir if dir != "" {
} addCmd.Dir = dir
if out, err := addCmd.Output(); err != nil { }
var stderr string if out, err := addCmd.Output(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok { var stderr string
stderr = string(exiterr.Stderr) if exiterr, ok := err.(*exec.ExitError); ok {
} stderr = string(exiterr.Stderr)
logverbose("failed to add remote %s: %s %s\n", remote, stderr, string(out))
} }
logverbose("failed to add remote %s: %s %s\n", remote, stderr, string(out))
} }
} }
} }
@@ -1069,7 +1108,7 @@ func fetchRepositoryAndState(
pubkey nostr.PubKey, pubkey nostr.PubKey,
identifier string, identifier string,
relayHints []string, relayHints []string,
) (repo nip34.Repository, state *nip34.RepositoryState, err error) { ) (repo nip34.Repository, upToDateRelays []string, state *nip34.RepositoryState, err error) {
// fetch repository announcement (30617) // fetch repository announcement (30617)
relays := appendUnique(relayHints, sys.FetchOutboxRelays(ctx, pubkey, 3)...) relays := appendUnique(relayHints, sys.FetchOutboxRelays(ctx, pubkey, 3)...)
for ie := range sys.Pool.FetchMany(ctx, relays, nostr.Filter{ for ie := range sys.Pool.FetchMany(ctx, relays, nostr.Filter{
@@ -1079,13 +1118,24 @@ func fetchRepositoryAndState(
"d": []string{identifier}, "d": []string{identifier},
}, },
Limit: 2, Limit: 2,
}, nostr.SubscriptionOptions{Label: "nak-git"}) { }, nostr.SubscriptionOptions{
Label: "nak-git",
CheckDuplicate: func(id nostr.ID, relay string) bool {
return false
},
}) {
if ie.Event.CreatedAt > repo.CreatedAt { if ie.Event.CreatedAt > repo.CreatedAt {
repo = nip34.ParseRepository(ie.Event) repo = nip34.ParseRepository(ie.Event)
// reset this list as the previous was for relays with the older version
upToDateRelays = []string{ie.Relay.URL}
} else if ie.Event.CreatedAt == repo.CreatedAt {
// we discard this because it's the same, but this relay is up-to-date
upToDateRelays = append(upToDateRelays, ie.Relay.URL)
} }
} }
if repo.Event.ID == nostr.ZeroID { if repo.Event.ID == nostr.ZeroID {
return repo, state, fmt.Errorf("no repository announcement (kind 30617) found for %s", identifier) return repo, upToDateRelays, state, fmt.Errorf("no repository announcement (kind 30617) found for %s", identifier)
} }
// fetch repository state (30618) // fetch repository state (30618)
@@ -1115,10 +1165,10 @@ func fetchRepositoryAndState(
} }
} }
if stateErr != nil { if stateErr != nil {
return repo, state, stateErr return repo, upToDateRelays, state, stateErr
} }
return repo, state, nil return repo, upToDateRelays, state, nil
} }
type StateErr struct{ string } type StateErr struct{ string }