1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | import {
|
18 | GitHubRepoRef,
|
19 | logger,
|
20 | RemoteRepoRef,
|
21 | } from "@atomist/automation-client";
|
22 | import {
|
23 | Attachment,
|
24 | url,
|
25 | } from "@atomist/slack-messages";
|
26 | import { listCommitsBetween } from "../github/ghub";
|
27 | import {
|
28 | avatarUrl,
|
29 | commitUrl,
|
30 | RepoInfo,
|
31 | truncateCommitMessage,
|
32 | userUrl,
|
33 | } from "../lifecycleHelpers";
|
34 |
|
35 |
|
36 |
|
37 | export function linkToDiff(id: RemoteRepoRef, start: string, end: string, endDescription?: string): string {
|
38 | return url(diffUrl(id, start, end), `(Compare with ${endDescription || end.substr(0, 6)})`);
|
39 | }
|
40 |
|
41 | function diffUrl(id: RemoteRepoRef, start: string, end: string): string {
|
42 | return `${id.url}/compare/${start}...${end}`;
|
43 | }
|
44 |
|
45 | export async function renderDiff(token: string, id: GitHubRepoRef, start: string, end: string, color: string): Promise<Attachment[]> {
|
46 | const fromGitHub = await listCommitsBetween(token, id, start, end);
|
47 |
|
48 | const commits: CommitForRendering[] = fromGitHub.commits.map(c => ({
|
49 | message: c.commit.message,
|
50 | sha: c.sha,
|
51 | author: c.author,
|
52 | }));
|
53 |
|
54 | logger.info("Rendering %d commits in diff", commits.length);
|
55 | return render({ owner: id.owner, name: id.repo }, commits, diffUrl(id, start, end), color);
|
56 | }
|
57 |
|
58 |
|
59 | export interface CommitForRendering {
|
60 | sha: string;
|
61 | message: string;
|
62 | author: {
|
63 | login: string,
|
64 | };
|
65 | }
|
66 |
|
67 | function render(repo: RepoInfo, commits: CommitForRendering[], fullDiffLink: string, color: string): Promise<Attachment[]> {
|
68 |
|
69 | const commitsGroupedByAuthor = [];
|
70 |
|
71 | let author;
|
72 | let commitsByAuthor: any = {};
|
73 |
|
74 | for (const commit of commits) {
|
75 | const ca = (commit.author !== undefined && commit.author.login && commit.author.login !== ""
|
76 | ? commit.author.login : "(unknown)");
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | if (author === undefined || author !== ca) {
|
83 | commitsByAuthor = {
|
84 | author: ca,
|
85 | commits: [],
|
86 | };
|
87 | author = ca;
|
88 | commitsGroupedByAuthor.push(commitsByAuthor);
|
89 | }
|
90 | if (ca === author) {
|
91 | commitsByAuthor.commits.push(commit);
|
92 | }
|
93 | }
|
94 |
|
95 | let attachments: Attachment[] = [];
|
96 |
|
97 | commitsGroupedByAuthor
|
98 | .forEach(cgba => {
|
99 | const a = cgba.author;
|
100 |
|
101 | const message = cgba.commits.map(c => renderCommitMessage(repo, c)).join("\n");
|
102 |
|
103 | const fallback = `lots of commits`;
|
104 |
|
105 | const attachment: Attachment = {
|
106 | author_name: `@${a}`,
|
107 | author_link: userUrl(repo, a),
|
108 | author_icon: avatarUrl(repo, a),
|
109 | text: message,
|
110 | mrkdwn_in: ["text"],
|
111 | color,
|
112 | fallback,
|
113 | actions: [],
|
114 | };
|
115 | attachments.push(attachment);
|
116 | });
|
117 |
|
118 |
|
119 | if (attachments.length > 3) {
|
120 | attachments = attachments.slice(0, 3);
|
121 | const fullDiffDescription = `... and more! (${commits.length} total commits)`;
|
122 |
|
123 | const attachment: Attachment = {
|
124 | title_link: fullDiffLink,
|
125 | title: fullDiffDescription,
|
126 | color,
|
127 | fallback: fullDiffDescription,
|
128 | actions: [],
|
129 | };
|
130 | attachments.push(attachment);
|
131 | }
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 | return Promise.resolve(attachments);
|
149 | }
|
150 |
|
151 |
|
152 | export function renderCommitMessage(repo: RepoInfo, commitNode: CommitForRendering): string {
|
153 |
|
154 | const m = truncateCommitMessage(commitNode.message, repo);
|
155 | return "`" + url(commitUrl(repo, commitNode), commitNode.sha.substring(0, 7)) + "` " + m;
|
156 | }
|