-
Notifications
You must be signed in to change notification settings - Fork 70
/
meta.go
112 lines (95 loc) · 2.49 KB
/
meta.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/sourcegraph/zoekt"
"github.com/sourcegraph/zoekt/build"
)
// mergeMeta updates the .meta files for the shards on disk for o.
//
// This process is best effort. If anything fails we return on the first
// failure. This means you might have an inconsistent state on disk if an
// error is returned. It is recommended to fallback to re-indexing in that
// case.
func mergeMeta(o *build.Options) error {
todo := map[string]string{}
for _, fn := range o.FindAllShards() {
repos, md, err := zoekt.ReadMetadataPath(fn)
if err != nil {
return err
}
var repo *zoekt.Repository
for _, cand := range repos {
if cand.Name == o.RepositoryDescription.Name {
repo = cand
break
}
}
if repo == nil {
return fmt.Errorf("mergeMeta: could not find repo %s in shard %s", o.RepositoryDescription.Name, fn)
}
if updated, err := repo.MergeMutable(&o.RepositoryDescription); err != nil {
return err
} else if !updated {
// This shouldn't happen, but ignore it if it does. We may be working on
// an interrupted shard. This helps us converge to something correct.
continue
}
var merged interface{}
if md.IndexFormatVersion >= 17 {
merged = repos
} else {
// <= v16 expects a single repo, not a list.
merged = repo
}
dst := fn + ".meta"
tmp, err := jsonMarshalTmpFile(merged, dst)
if err != nil {
return err
}
todo[tmp] = dst
// if we fail to rename, this defer will attempt to remove the tmp file.
defer os.Remove(tmp)
}
// best effort once we get here. Rename everything. Return error of last
// failure.
var renameErr error
for tmp, dst := range todo {
if err := os.Rename(tmp, dst); err != nil {
renameErr = err
}
}
return renameErr
}
// jsonMarshalFileTmp marshals v to the temporary file p + ".*.tmp" and
// returns the file name.
//
// Note: .tmp is the same suffix used by Builder. indexserver knows to clean
// them up.
func jsonMarshalTmpFile(v interface{}, p string) (_ string, err error) {
b, err := json.Marshal(v)
if err != nil {
return "", err
}
f, err := os.CreateTemp(filepath.Dir(p), filepath.Base(p)+".*.tmp")
if err != nil {
return "", err
}
defer func() {
f.Close()
if err != nil {
_ = os.Remove(f.Name())
}
}()
if err := f.Chmod(0o666 &^ umask); err != nil {
return "", err
}
if _, err := f.Write(b); err != nil {
return "", err
}
return f.Name(), f.Close()
}
// respect process umask. build does this.
var umask os.FileMode