Skip to content

Commit 9dd6c48

Browse files
committed
Add support for comment and label manifests.
Introduces `.MANIFEST` files to comment and label directories to keep track of sub-resources when the PullRequest resource is initialized. This allows us to keep track of what resources the user explicitly deleted, and not accidentally overwrite new comments/lables that were introduced during execution. Minor changes included: * Changed fake GitHub server to make copies of PullRequests to avoid pointer collision across tests. * Changed uploadLabels to Add/Delete labels explicitly instead of replacing all. Change manifest path to .MANIFEST to avoid collisions. Fixes #1286
1 parent 5824fd5 commit 9dd6c48

File tree

7 files changed

+399
-52
lines changed

7 files changed

+399
-52
lines changed

cmd/pullrequest-init/disk.go

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"bufio"
2021
"encoding/json"
22+
"fmt"
2123
"io/ioutil"
2224
"net/url"
2325
"os"
@@ -40,6 +42,10 @@ import (
4042

4143
// Filenames for labels and statuses are URL encoded for safety.
4244

45+
const (
46+
manifestPath = ".MANIFEST"
47+
)
48+
4349
// ToDisk converts a PullRequest object to an on-disk representation at the specified path.
4450
func ToDisk(pr *PullRequest, path string) error {
4551
labelsPath := filepath.Join(path, "labels")
@@ -80,28 +86,53 @@ func ToDisk(pr *PullRequest, path string) error {
8086
}
8187

8288
func commentsToDisk(path string, comments []*Comment) error {
89+
// Create a manifest to keep track of the comments that existed when the
90+
// resource was initialized. This is used to verify that a comment that
91+
// doesn't exist on disk was actually deleted by the user and not newly
92+
// created during upload.
93+
f, err := os.Create(filepath.Join(path, manifestPath))
94+
if err != nil {
95+
return err
96+
}
97+
defer f.Close()
98+
manifest := bufio.NewWriter(f)
99+
83100
for _, c := range comments {
84101
commentPath := filepath.Join(path, strconv.FormatInt(c.ID, 10)+".json")
85102
b, err := json.Marshal(c)
86103
if err != nil {
87104
return err
88105
}
89-
if err := ioutil.WriteFile(commentPath, b, 0700); err != nil {
106+
if err := ioutil.WriteFile(commentPath, b, 0600); err != nil {
107+
return err
108+
}
109+
if _, err := manifest.WriteString(fmt.Sprintf("%s\n", commentPath)); err != nil {
90110
return err
91111
}
92112
}
93-
return nil
113+
return manifest.Flush()
94114
}
95115

96116
func labelsToDisk(path string, labels []*Label) error {
117+
f, err := os.Create(filepath.Join(path, manifestPath))
118+
if err != nil {
119+
return err
120+
}
121+
defer f.Close()
122+
manifest := bufio.NewWriter(f)
123+
97124
for _, l := range labels {
98125
name := url.QueryEscape(l.Text)
99126
labelPath := filepath.Join(path, name)
100-
if err := ioutil.WriteFile(labelPath, []byte{}, 0700); err != nil {
127+
if err := ioutil.WriteFile(labelPath, []byte{}, 0600); err != nil {
128+
return err
129+
}
130+
if _, err := manifest.WriteString(fmt.Sprintf("%s\n", labelPath)); err != nil {
101131
return err
102132
}
133+
103134
}
104-
return nil
135+
return manifest.Flush()
105136
}
106137

107138
func statusToDisk(path string, statuses []*Status) error {
@@ -127,57 +158,86 @@ func refToDisk(name, path string, r *GitReference) error {
127158
return ioutil.WriteFile(filepath.Join(path, name+".json"), b, 0700)
128159
}
129160

161+
// Manifest is a list of sub-resources that exist within the PR resource to
162+
// determine whether an item existed when the resource was initialized.
163+
type Manifest map[string]bool
164+
130165
// FromDisk outputs a PullRequest object from an on-disk representation at the specified path.
131-
func FromDisk(path string) (*PullRequest, error) {
166+
func FromDisk(path string) (*PullRequest, map[string]Manifest, error) {
132167
labelsPath := filepath.Join(path, "labels")
133168
commentsPath := filepath.Join(path, "comments")
134169
statusesPath := filepath.Join(path, "status")
135170

136171
pr := PullRequest{}
172+
manifests := make(map[string]Manifest)
137173

138174
var err error
175+
var manifest Manifest
139176

140177
// Start with comments
141-
pr.Comments, err = commentsFromDisk(commentsPath)
178+
pr.Comments, manifest, err = commentsFromDisk(commentsPath)
142179
if err != nil {
143-
return nil, err
180+
return nil, nil, err
144181
}
182+
manifests["comments"] = manifest
145183

146184
// Now Labels
147-
pr.Labels, err = labelsFromDisk(labelsPath)
185+
pr.Labels, manifest, err = labelsFromDisk(labelsPath)
148186
if err != nil {
149-
return nil, err
187+
return nil, nil, err
150188
}
189+
manifests["labels"] = manifest
151190

152191
// Now statuses
153192
pr.Statuses, err = statusesFromDisk(statusesPath)
154193
if err != nil {
155-
return nil, err
194+
return nil, nil, err
156195
}
157196

158197
// Finally refs
159198

160199
pr.Base, err = refFromDisk(path, "base.json")
161200
if err != nil {
162-
return nil, err
201+
return nil, nil, err
163202
}
164203
pr.Head, err = refFromDisk(path, "head.json")
204+
if err != nil {
205+
return nil, nil, err
206+
}
207+
return &pr, manifests, nil
208+
}
209+
210+
func manifestFromDisk(path string) (map[string]bool, error) {
211+
f, err := os.Open(path)
165212
if err != nil {
166213
return nil, err
167214
}
168-
return &pr, nil
215+
defer f.Close()
216+
217+
out := make(map[string]bool)
218+
s := bufio.NewScanner(f)
219+
for s.Scan() {
220+
out[s.Text()] = true
221+
}
222+
if s.Err() != nil {
223+
return nil, err
224+
}
225+
return out, nil
169226
}
170227

171-
func commentsFromDisk(path string) ([]*Comment, error) {
228+
func commentsFromDisk(path string) ([]*Comment, Manifest, error) {
172229
fis, err := ioutil.ReadDir(path)
173230
if err != nil {
174-
return nil, err
231+
return nil, nil, err
175232
}
176233
comments := []*Comment{}
177234
for _, fi := range fis {
235+
if fi.Name() == manifestPath {
236+
continue
237+
}
178238
b, err := ioutil.ReadFile(filepath.Join(path, fi.Name()))
179239
if err != nil {
180-
return nil, err
240+
return nil, nil, err
181241
}
182242
comment := Comment{}
183243
if err := json.Unmarshal(b, &comment); err != nil {
@@ -186,23 +246,38 @@ func commentsFromDisk(path string) ([]*Comment, error) {
186246
}
187247
comments = append(comments, &comment)
188248
}
189-
return comments, nil
249+
250+
manifest, err := manifestFromDisk(filepath.Join(path, manifestPath))
251+
if err != nil {
252+
return nil, nil, err
253+
}
254+
255+
return comments, manifest, nil
190256
}
191257

192-
func labelsFromDisk(path string) ([]*Label, error) {
258+
func labelsFromDisk(path string) ([]*Label, Manifest, error) {
193259
fis, err := ioutil.ReadDir(path)
194260
if err != nil {
195-
return nil, err
261+
return nil, nil, err
196262
}
197263
labels := []*Label{}
198264
for _, fi := range fis {
265+
if fi.Name() == manifestPath {
266+
continue
267+
}
199268
text, err := url.QueryUnescape(fi.Name())
200269
if err != nil {
201-
return nil, err
270+
return nil, nil, err
202271
}
203272
labels = append(labels, &Label{Text: text})
204273
}
205-
return labels, nil
274+
275+
manifest, err := manifestFromDisk(filepath.Join(path, manifestPath))
276+
if err != nil {
277+
return nil, nil, err
278+
}
279+
280+
return labels, manifest, nil
206281
}
207282

208283
func statusesFromDisk(path string) ([]*Status, error) {

cmd/pullrequest-init/disk_test.go

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"bufio"
2021
"encoding/json"
2122
"io/ioutil"
2223
"net/url"
@@ -25,6 +26,7 @@ import (
2526
"reflect"
2627
"sort"
2728
"strconv"
29+
"strings"
2830
"testing"
2931

3032
"github.com/google/go-cmp/cmp"
@@ -113,7 +115,6 @@ func TestToDisk(t *testing.T) {
113115
t.Errorf("Get Status: -want +got: %s", diff)
114116
}
115117
}
116-
117118
// Check the labels
118119
fis, err = ioutil.ReadDir(filepath.Join(d, "labels"))
119120
if err != nil {
@@ -141,10 +142,16 @@ func TestToDisk(t *testing.T) {
141142
}
142143

143144
comments := map[int64]Comment{}
145+
commentManifest := map[string]bool{}
144146
for _, fi := range fis {
147+
if fi.Name() == manifestPath {
148+
continue
149+
}
145150
comment := Comment{}
146-
readAndUnmarshal(t, filepath.Join(d, "comments", fi.Name()), &comment)
151+
path := filepath.Join(d, "comments", fi.Name())
152+
readAndUnmarshal(t, path, &comment)
147153
comments[comment.ID] = comment
154+
commentManifest[path] = true
148155
}
149156
for _, c := range tektonPr.Comments {
150157
actualComment, ok := comments[c.ID]
@@ -155,6 +162,33 @@ func TestToDisk(t *testing.T) {
155162
t.Errorf("Get Comment: -want +got: %s", diff)
156163
}
157164
}
165+
gotManifest := readManifest(t, filepath.Join(d, "comments", manifestPath))
166+
for _, m := range gotManifest {
167+
if _, ok := commentManifest[m]; !ok {
168+
t.Errorf("Comment %s not found in manifest", m)
169+
}
170+
}
171+
if len(commentManifest) != len(gotManifest) {
172+
t.Errorf("Comment manifest does not match expected length. Got %+v", gotManifest)
173+
}
174+
}
175+
176+
func readManifest(t *testing.T, path string) []string {
177+
f, err := os.Open(path)
178+
if err != nil {
179+
t.Fatal(err)
180+
}
181+
defer f.Close()
182+
183+
var out []string
184+
s := bufio.NewScanner(f)
185+
for s.Scan() {
186+
out = append(out, s.Text())
187+
}
188+
if s.Err() != nil {
189+
t.Fatal(err)
190+
}
191+
return out
158192
}
159193

160194
func TestFromDisk(t *testing.T) {
@@ -218,6 +252,9 @@ func TestFromDisk(t *testing.T) {
218252
t.Fatal(err)
219253
}
220254
}
255+
if err := ioutil.WriteFile(filepath.Join(d, "labels", manifestPath), []byte(strings.Join(labels, "\n")), 0600); err != nil {
256+
t.Errorf("Error creating label manifest: %s", err)
257+
}
221258

222259
// Write some comments
223260
comments := []Comment{
@@ -236,16 +273,22 @@ func TestFromDisk(t *testing.T) {
236273
if err := os.MkdirAll(filepath.Join(d, "comments"), 0750); err != nil {
237274
t.Fatal(err)
238275
}
276+
manifest := make([]string, 0, len(comments))
239277
for _, c := range comments {
240-
writeFile(filepath.Join(d, "comments", strconv.FormatInt(c.ID, 10)+".json"), &c)
278+
id := strconv.FormatInt(c.ID, 10)
279+
writeFile(filepath.Join(d, "comments", id+".json"), &c)
280+
manifest = append(manifest, id)
281+
}
282+
if err := ioutil.WriteFile(filepath.Join(d, "comments", manifestPath), []byte(strings.Join(manifest, "\n")), 0700); err != nil {
283+
t.Errorf("Error creating comment manifest: %s", err)
241284
}
242285

243286
// Comments can also be plain text.
244287
if err := ioutil.WriteFile(filepath.Join(d, "comments", "plain"), []byte("plaincomment"), 0700); err != nil {
245288
t.Fatal(err)
246289
}
247290

248-
pr, err := FromDisk(d)
291+
pr, gotManifest, err := FromDisk(d)
249292
if err != nil {
250293
t.Fatal(err)
251294
}
@@ -271,6 +314,13 @@ func TestFromDisk(t *testing.T) {
271314
t.Errorf("Get comments: -want +got: %s", diff)
272315
}
273316
}
317+
commentManifest := Manifest{}
318+
for _, c := range comments {
319+
commentManifest[strconv.FormatInt(c.ID, 10)] = true
320+
}
321+
if diff := cmp.Diff(commentManifest, gotManifest["comments"]); diff != "" {
322+
t.Errorf("Comment manifest: -want + got: %s", diff)
323+
}
274324

275325
// Check the labels
276326
labelsMap := map[string]struct{}{}
@@ -283,6 +333,13 @@ func TestFromDisk(t *testing.T) {
283333
t.Errorf("Get labels: -want +got: %s", diff)
284334
}
285335
}
336+
labelManifest := Manifest{}
337+
for _, l := range labels {
338+
labelManifest[l] = true
339+
}
340+
if diff := cmp.Diff(labelManifest, gotManifest["labels"]); diff != "" {
341+
t.Errorf("Label manifest: -want + got: %s", diff)
342+
}
286343

287344
// Check the statuses
288345
statusMap := map[string]Status{}
@@ -495,7 +552,10 @@ func Test_labelsFromDisk(t *testing.T) {
495552
t.Errorf("Error creating label: %s", err)
496553
}
497554
}
498-
got, err := labelsFromDisk(d)
555+
if err := ioutil.WriteFile(filepath.Join(d, manifestPath), []byte(strings.Join(tt.args.fileNames, "\n")), 0700); err != nil {
556+
t.Errorf("Error creating label manifest: %s", err)
557+
}
558+
got, _, err := labelsFromDisk(d)
499559
if err != nil {
500560
t.Errorf("labelsFromDisk() error = %v", err)
501561
}

0 commit comments

Comments
 (0)