File: upload.go

package info (click to toggle)
addchain 0.4.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,396 kB
  • sloc: sh: 428; makefile: 10
file content (162 lines) | stat: -rw-r--r-- 3,685 bytes parent folder | download
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/google/subcommands"

	"github.com/mmcloughlin/addchain/internal/cli"
	"github.com/mmcloughlin/addchain/internal/zenodo"
)

// upload subcommand.
type upload struct {
	cli.Command

	httpclient HTTPClient
	zenodo     Zenodo
	varsfile   VarsFile
	metadata   string
	publish    bool
}

func (*upload) Name() string     { return "upload" }
func (*upload) Synopsis() string { return "upload version" }
func (*upload) Usage() string {
	return `Usage: upload <version>

Bump version and update related files.

`
}

func (cmd *upload) SetFlags(f *flag.FlagSet) {
	cmd.httpclient.SetFlags(f)
	cmd.zenodo.SetFlags(f)
	cmd.varsfile.SetFlags(f)
	f.StringVar(&cmd.metadata, "metadata", RepoPath(".zenodo.json"), "path to zenodo metadata")
	f.BoolVar(&cmd.publish, "publish", false, "publish the zenodo record")
}

func (cmd *upload) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	// Fetch version.
	version, err := cmd.varsfile.Get("releaseversion")
	if err != nil {
		return cmd.Error(err)
	}

	// HTTP Client.
	httpclient, err := cmd.httpclient.Client()
	if err != nil {
		return cmd.Error(err)
	}

	// Download archive from github.
	archive := bytes.NewBuffer(nil)
	url := "https://api.github.com/repos/mmcloughlin/addchain/zipball/v" + version
	if err := Download(ctx, archive, httpclient, url); err != nil {
		return cmd.Error(err)
	}

	cmd.Log.Printf("downloaded zip archive bytes %d", archive.Len())

	// Zenodo client.
	client, err := cmd.zenodo.Client(httpclient)
	if err != nil {
		return cmd.Error(err)
	}

	// Load zenodo metadata.
	metadata, err := LoadZenodoMetadata(cmd.metadata)
	if err != nil {
		return cmd.Error(err)
	}

	cmd.Log.Printf("loaded zenodo metadata from %q", cmd.metadata)

	// Set metadata.
	id, err := cmd.varsfile.Get("zenodoid")
	if err != nil {
		return cmd.Error(err)
	}

	d, err := client.DepositionUpdate(ctx, id, metadata)
	if err != nil {
		return cmd.Error(err)
	}

	cmd.Log.Printf("updated zenodo metadata")

	// Clear Zenodo files.
	if err := client.DepositionFilesDeleteAll(ctx, id); err != nil {
		return cmd.Error(err)
	}

	cmd.Log.Printf("cleared all files from zenodo deposit")

	// Upload new Zenodo file.
	archivefilename := fmt.Sprintf("addchain_%s.zip", version)
	file, err := client.DepositionFilesCreate(ctx, id, archivefilename, "application/zip", archive)
	if err != nil {
		return cmd.Error(err)
	}

	cmd.Log.Printf("uploaded file %q", file.Filename)

	// Optionally publish zenodo deposit.
	if cmd.publish {
		if _, err := client.DepositionPublish(ctx, id); err != nil {
			return cmd.Error(err)
		}
		cmd.Log.Printf("published zenodo record")
	} else {
		cmd.Log.Printf("publish skipped: review and publish at %q", d.Links["html"])
	}

	return subcommands.ExitSuccess
}

// Download a file over HTTP.
func Download(ctx context.Context, w io.Writer, c *http.Client, url string) (err error) {
	// Issue GET request.
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return err
	}

	res, err := c.Do(req)
	if err != nil {
		return err
	}
	defer func() {
		if errc := res.Body.Close(); errc != nil && err == nil {
			err = errc
		}
	}()

	// Copy to writer.
	_, err = io.Copy(w, res.Body)
	return
}

// LoadZenodoMetadata reads and parses a Zenodo metadata file.
func LoadZenodoMetadata(filename string) (*zenodo.DepositionMetadata, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	m := new(zenodo.DepositionMetadata)
	if err := json.Unmarshal(b, m); err != nil {
		return nil, err
	}

	return m, nil
}