-
Notifications
You must be signed in to change notification settings - Fork 4
/
zanata-github-upload
executable file
·274 lines (207 loc) · 7.19 KB
/
zanata-github-upload
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
#!/usr/bin/env perl
=pod
=head1 NAME
zanata-github-upload - Upload files to GitHub
=head1 SYNOPSIS
zanata-github-upload [Option] E<lt>releaseTagE<gt> E<lt>fileE<gt> ...
=head1 ARGUMENTS
=over 4
=item E<lt>releaseTagE<gt>
Destintation release tag like C<server-4.0.0>
=item <file> ...
Files to be uploaded. Separate multiple files with spaces.
=back
=head1 OPTIONS
=over 4
=item B<-h>
Show complete help.
=item B<-d>
Create a new draft GitHub release.
This program does not find existing draft release,
so running this program multiple times creates multiple draft releases.
=item B<-v>
Verbose mode.
This shows detail debug messages like which module it is processing,
and full HTTP respond structure.
=back
=head1 DESCRIPTION
This program uploads the files to GitHub release.
It will create the release if it does not exists,
then upload files to GitHub release.
Before uploading, the release tag (e.g. platform-4.0.0) should exist,
otherwise it prints error.
You can try this program by creating draft releases.
Specify option C<-d> to create draft release.
Note that this program does not yet find existing draft releases,
so you need to login GitHub Web UI to remove drafts or promote drafts to
official releases.
GitHub credentials are required for creating release and upload files.
This program obtains the credentials by following order:
Use either
1) Environment variables:
ZANATA_GITHUB_USERNAME
ZANATA_GITHUB_TOKEN
2) Configuration file:
${HOME}/.config/zanata-scripts.ini.
The format is explained in section FILES.
If credentials cannot be found in either way, this program prints error messages.
=head1 FILES
=over 4
=item ${HOME}/.config/zanata-scripts.ini
This file configures the zanata-scripts, including the GitHub credentials.
The format looks like:
github_username=<USERNAME>
github_token=<TOKEN>
As this file is plain text, use an access token instead of login password.
To get an access token, see:
https://help.github.com/articles/creating-an-access-token-for-command-line-use/
=back
=cut
#== Common use ==
use strict;
use Cwd 'abs_path';
use Data::Dumper qw(Dumper);
use File::Basename;
use Getopt::Std qw(getopts);
use Pod::Usage qw(pod2usage);
my $scriptDir;
BEGIN {
$scriptDir = dirname( abs_path($0) );
push @INC, $scriptDir;
}
use ZanataScriptsCommon;
##== Program dependency ==
use File::Slurp;
use HTTP::Tiny;
use JSON::XS;
use URI::Escape;
$HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1;
my $githubUsername=undef;
my $githubToken=undef;
##== Function definition ==
my $zanataScriptsIni = $ENV{'HOME'} . '/.config/zanata-scripts.ini';
sub github_rest_response {
my ( $method, $path, $header, $content, $contentCb, $readSizeHint ) = @_;
my $url = "https://" . GITHUB_REST_SERVER . $path;
return rest_response( $method, $url, $header, $content, $contentCb, $readSizeHint );
}
sub github_authenticated_rest_response {
my ( $method, $path, $header, $content, $contentCb, $readSizeHint ) = @_;
my $url = "https://$githubUsername:$githubToken@"
. GITHUB_REST_SERVER
. $path;
return rest_response( $method, $url, $header, $content, $contentCb, $readSizeHint );
}
##== Parse options ==
my %opts = ();
getopts( 'hdv', \%opts );
my $verbose = ( exists $opts{'v'} ) ? 1 : 0;
pod2usage( -verbose => 3, -output => \*STDERR ) if $opts{'h'};
if ( @ARGV < 2 ) {
pod2usage(
-verbose => 1,
-output => \*STDERR,
-exitval => EXIT_FATAL_INVALID_OPTIONS,
-message => "Require <releaseTag> and <file> ... \n"
);
}
my $releaseTag = $ARGV[0];
my ($artifact) = split( /-/, $releaseTag, 2 );
## module is actually GitHub repository
## such as zanata-parent, zanata-server
my $module = `$scriptDir/zanata-functions run get_module $artifact 2>/dev/null`;
chomp($module);
print STDERR "module=$module\n" if $verbose;
shift;
die "Invalid releaseTag $releaseTag. \n"
. " Valid releaseTag looks like platform-4.0.0"
if ($module eq '-');
## Check whether the files are readable.
foreach my $f (@ARGV) {
die "$f is not readable. $!" unless ( -r $f );
}
## Obtaining credential
$githubUsername=$ENV{'ZANATA_GITHUB_USERNAME'};
$githubToken=$ENV{'ZANATA_GITHUB_TOKEN'};
if (( $githubUsername eq '') or ( $githubToken eq '')){
zanata_scripts_ini_load;
if ($githubUsername eq ''){
$githubUsername=zanata_scripts_ini_key_get_value('github_username');
die "Please provide github_username" unless $githubUsername;
}
if ($githubToken eq ''){
$githubToken=zanata_scripts_ini_key_get_value('github_token');
die "Please provide github_token" unless $githubToken;
}
}
##== Check Existing Tags ==
my $response = github_authenticated_rest_response( 'GET',
"/repos/zanata/$module/git/refs/tags/$releaseTag" );
print STDERR Dumper($response) if $verbose;
response_die_if_error( $response, "Tag $releaseTag does not exists." );
##== Create Release ==
print_status( "Create Release", "Start", 's' );
$response = github_authenticated_rest_response( 'GET',
"/repos/zanata/$module/releases/tags/$releaseTag" );
print STDERR Dumper($response) if $verbose;
my $uploadUrl = undef;
if ( $response->code == 200 ) {
##=== Release Already created ===
print_status( '', "Release $releaseTag already created.", '' );
my $contentHRef = decode_json $response->content;
$uploadUrl = $contentHRef->{'upload_url'};
}
elsif ( $response->code == 404 ) {
##=== Creating new release ===
print_status( '', "Creating release $releaseTag", '' );
my $draft = ( exists $opts{'d'} ) ? 'true' : 'false';
$response = github_authenticated_rest_response(
'POST',
"/repos/zanata/$module/releases",
[
Accept => 'application/json',
'Content-Type' => 'application/json'
],
qq({
"tag_name": "$releaseTag",
"draft": $draft,
"prerelease": false
})
);
print STDERR Dumper($response) if $verbose;
my $contentHRef = decode_json $response->content;
$uploadUrl = $contentHRef->{'upload_url'};
response_die_if_error( $response, "Failed to create release." );
}
else {
response_die_if_error( $response, "Failed to query release." );
}
##== Upload ==
print_status( "Upload", "Start", 's' );
$uploadUrl =~ s/^(http.*\/releases\/[0-9]+\/assets).*$/$1/;
my ( $uploadProtocol, $uploadPath ) = split( /:\/\//, $uploadUrl, 2 );
print_status( undef, " protocol=$uploadProtocol path=$uploadPath" )
if $verbose;
## github_upload(<filePath>, [label])
## filePath: Location of the file to be upload
## label: (Optional) GitHub release label
sub github_upload {
my ( $filePath, $label ) = @_;
my $name = basename($filePath);
my $content = read_file( $filePath, binmode => ':raw' );
my $url =
"$uploadProtocol://$githubUsername:$githubToken@"
. $uploadPath
. "?name="
. uri_escape($name);
$url .= "&label=" . uri_escape($label) if ($label);
my @cmd = qw( curl -i -o /tmp/request.txt -X POST);
push @cmd, '-H', 'Content-Type: application/octet-stream';
push @cmd, '--data-binary', "@" . $filePath, $url;
system(@cmd) == 0
or die "system @cmd failed: $?";
}
foreach my $f (@ARGV) {
print_status( undef, " Uploading $f" );
github_upload($f);
}