Skip to content

Commit

Permalink
Add support for comment and label manifests.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wlynch committed Oct 14, 2019
1 parent 5824fd5 commit dddd6f1
Show file tree
Hide file tree
Showing 7 changed files with 416 additions and 58 deletions.
123 changes: 97 additions & 26 deletions cmd/pullrequest-init/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
package main

import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"os"
Expand All @@ -40,6 +42,10 @@ import (

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

const (
manifestPath = ".MANIFEST"
)

// ToDisk converts a PullRequest object to an on-disk representation at the specified path.
func ToDisk(pr *PullRequest, path string) error {
labelsPath := filepath.Join(path, "labels")
Expand Down Expand Up @@ -80,28 +86,54 @@ func ToDisk(pr *PullRequest, path string) error {
}

func commentsToDisk(path string, comments []*Comment) error {
// Create a manifest to keep track of the comments that existed when the
// resource was initialized. This is used to verify that a comment that
// doesn't exist on disk was actually deleted by the user and not newly
// created during upload.
f, err := os.Create(filepath.Join(path, manifestPath))
if err != nil {
return err
}
defer f.Close()
manifest := bufio.NewWriter(f)

for _, c := range comments {
commentPath := filepath.Join(path, strconv.FormatInt(c.ID, 10)+".json")
id := strconv.FormatInt(c.ID, 10)
commentPath := filepath.Join(path, id+".json")
b, err := json.Marshal(c)
if err != nil {
return err
}
if err := ioutil.WriteFile(commentPath, b, 0700); err != nil {
if err := ioutil.WriteFile(commentPath, b, 0600); err != nil {
return err
}
if _, err := manifest.WriteString(fmt.Sprintf("%s\n", id)); err != nil {
return err
}
}
return nil
return manifest.Flush()
}

func labelsToDisk(path string, labels []*Label) error {
f, err := os.Create(filepath.Join(path, manifestPath))
if err != nil {
return err
}
defer f.Close()
manifest := bufio.NewWriter(f)

for _, l := range labels {
name := url.QueryEscape(l.Text)
labelPath := filepath.Join(path, name)
if err := ioutil.WriteFile(labelPath, []byte{}, 0700); err != nil {
if err := ioutil.WriteFile(labelPath, []byte{}, 0600); err != nil {
return err
}
if _, err := manifest.WriteString(fmt.Sprintf("%s\n", l.Text)); err != nil {
return err
}

}
return nil
return manifest.Flush()
}

func statusToDisk(path string, statuses []*Status) error {
Expand All @@ -127,57 +159,81 @@ func refToDisk(name, path string, r *GitReference) error {
return ioutil.WriteFile(filepath.Join(path, name+".json"), b, 0700)
}

// Manifest is a list of sub-resources that exist within the PR resource to
// determine whether an item existed when the resource was initialized.
type Manifest map[string]bool

// FromDisk outputs a PullRequest object from an on-disk representation at the specified path.
func FromDisk(path string) (*PullRequest, error) {
func FromDisk(path string) (*PullRequest, map[string]Manifest, error) {
labelsPath := filepath.Join(path, "labels")
commentsPath := filepath.Join(path, "comments")
statusesPath := filepath.Join(path, "status")

pr := PullRequest{}
manifests := make(map[string]Manifest)

var err error
var manifest Manifest

// Start with comments
pr.Comments, err = commentsFromDisk(commentsPath)
pr.Comments, manifest, err = commentsFromDisk(commentsPath)
if err != nil {
return nil, err
return nil, nil, err
}
manifests["comments"] = manifest

// Now Labels
pr.Labels, err = labelsFromDisk(labelsPath)
pr.Labels, manifest, err = labelsFromDisk(labelsPath)
if err != nil {
return nil, err
return nil, nil, err
}
manifests["labels"] = manifest

// Now statuses
pr.Statuses, err = statusesFromDisk(statusesPath)
if err != nil {
return nil, err
return nil, nil, err
}

// Finally refs

pr.Base, err = refFromDisk(path, "base.json")
if err != nil {
return nil, err
return nil, nil, err
}
pr.Head, err = refFromDisk(path, "head.json")
if err != nil {
return nil, nil, err
}
return &pr, manifests, nil
}

func manifestFromDisk(path string) (map[string]bool, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()

out := make(map[string]bool)
s := bufio.NewScanner(f)
for s.Scan() {
out[s.Text()] = true
}
if s.Err() != nil {
return nil, err
}
return &pr, nil
return out, nil
}

func commentsFromDisk(path string) ([]*Comment, error) {
func commentsFromDisk(path string) ([]*Comment, Manifest, error) {
fis, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
return nil, nil, err
}
comments := []*Comment{}
for _, fi := range fis {
if fi.Name() == manifestPath {
continue
}
b, err := ioutil.ReadFile(filepath.Join(path, fi.Name()))
if err != nil {
return nil, err
return nil, nil, err
}
comment := Comment{}
if err := json.Unmarshal(b, &comment); err != nil {
Expand All @@ -186,23 +242,38 @@ func commentsFromDisk(path string) ([]*Comment, error) {
}
comments = append(comments, &comment)
}
return comments, nil

manifest, err := manifestFromDisk(filepath.Join(path, manifestPath))
if err != nil {
return nil, nil, err
}

return comments, manifest, nil
}

func labelsFromDisk(path string) ([]*Label, error) {
func labelsFromDisk(path string) ([]*Label, Manifest, error) {
fis, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
return nil, nil, err
}
labels := []*Label{}
for _, fi := range fis {
if fi.Name() == manifestPath {
continue
}
text, err := url.QueryUnescape(fi.Name())
if err != nil {
return nil, err
return nil, nil, err
}
labels = append(labels, &Label{Text: text})
}
return labels, nil

manifest, err := manifestFromDisk(filepath.Join(path, manifestPath))
if err != nil {
return nil, nil, err
}

return labels, manifest, nil
}

func statusesFromDisk(path string) ([]*Status, error) {
Expand Down
Loading

0 comments on commit dddd6f1

Please sign in to comment.