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
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "github.h"
struct GHContent {
char *data;
size_t len;
};
static size_t writer(void *contents, size_t size, size_t nmemb, void *result) {
const size_t newlen = size * nmemb;
struct GHContent *content = (struct GHContent *) result;
char *ptr = realloc(content->data, content->len + newlen + 1);
if (!ptr) {
perror("realloc failed");
return 0;
}
content->data = ptr;
memcpy(&(content->data[content->len]), contents, newlen);
content->len += newlen;
content->data[content->len] = 0;
return newlen;
}
static char *unescape_lf(char *value) {
char *seq = strstr(value, "\\n");
while (seq != NULL) {
size_t cur_len = strlen(seq);
memmove(seq, seq + 1, strlen(seq) - 1);
*seq = '\n';
if (strlen(seq) && cur_len) {
seq[cur_len - 1] = 0;
}
seq = strstr(value, "\\n");
}
return value;
}
int get_github_release_notes(const char *api_token, const char *repo, const char *tag, const char *target_commitish, char **output) {
const char *field_body = "\"body\":\"";
const char *field_message = "\"message\":\"";
const char *endpoint_header_auth_fmt = "Authorization: Bearer %s";
const char *endpoint_header_api_version = "X-GitHub-Api-Version: " STASIS_GITHUB_API_VERSION;
const char *endpoint_post_fields_fmt = "{\"tag_name\":\"%s\", \"target_commitish\":\"%s\"}";
const char *endpoint_url_fmt = "https://api.github.com/repos/%s/releases/generate-notes";
char endpoint_header_auth[PATH_MAX] = {0};
char endpoint_post_fields[PATH_MAX] = {0};
char endpoint_url[PATH_MAX] = {0};
struct curl_slist *list = NULL;
struct GHContent content;
CURL *curl = curl_easy_init();
if (!curl) {
return -1;
}
// Render the header data
sprintf(endpoint_header_auth, endpoint_header_auth_fmt, api_token);
sprintf(endpoint_post_fields, endpoint_post_fields_fmt, tag, target_commitish);
sprintf(endpoint_url, endpoint_url_fmt, repo);
// Begin curl configuration
curl_easy_setopt(curl, CURLOPT_URL, endpoint_url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, endpoint_post_fields);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &content);
// Append headers to the request
list = curl_slist_append(list, "Accept: application/vnd.github+json");
list = curl_slist_append(list, endpoint_header_auth);
list = curl_slist_append(list, endpoint_header_api_version);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
// Set the user-agent (github requires one)
char user_agent[20] = {0};
sprintf(user_agent, "stasis/%s", VERSION);
curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
// Execute curl request
memset(&content, 0, sizeof(content));
CURLcode res;
res = curl_easy_perform(curl);
// Clean up
curl_slist_free_all(list);
curl_easy_cleanup(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
return -1;
}
// Replace all "\\n" literals with new line characters
char *line = unescape_lf(content.data);
if (line) {
char *data_offset = NULL;
if ((data_offset = strstr(line, field_body))) {
// Skip past the body field
data_offset += strlen(field_body);
// Remove quotation mark (and trailing comma if it exists)
int trim = 2;
char last_char = data_offset[strlen(data_offset) - trim];
if (last_char == ',') {
trim++;
}
data_offset[strlen(data_offset) - trim] = 0;
// Extract release notes
*output = strdup(data_offset);
} else if ((data_offset = strstr(line, field_message))) {
// Skip past the message field
data_offset += strlen(field_message);
*(strchr(data_offset, '"')) = 0;
fprintf(stderr, "GitHub API Error: '%s'\n", data_offset);
fprintf(stderr, "URL: %s\n", endpoint_url);
fprintf(stderr, "POST: %s\n", endpoint_post_fields);
free(content.data);
return -1;
}
} else {
fprintf(stderr, "Unknown error\n");
free(content.data);
return -1;
}
free(content.data);
return 0;
}
|