Skip to content

Commit 0f5d885

Browse files
author
jessicazu
committed
Initial commit
0 parents  commit 0f5d885

File tree

2,192 files changed

+344179
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2,192 files changed

+344179
-0
lines changed

README.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# trello-github-actions
2+
You can link GitHub Issues and Pull Request with Trello card.
3+
4+
# Input
5+
## trello-action
6+
You can choose from the following.
7+
- **create_card_when_issue_opened**
8+
9+
This creates Trello card when issue opened and corresponds to:
10+
11+
Issue Title ⇆ Trello Title
12+
13+
Issue Description ⇆ Trello Description
14+
15+
Issue Assignees ⇆ Trello Members
16+
17+
Issue Labels ⇆ Trello Labels
18+
19+
*To link Issue Assginees and Issue Members, the Github user name and Trello user name must be the same.
20+
21+
*To link Issue Labels and Trello Labels, each name must be the same.
22+
23+
- **move_card_when_pull_request_opened**
24+
25+
This moves Trello card when issue opened. It uses the issue number {#number} included in the description of the pull request and searches for cards from DEPARTURE_LIST and moves to DESTINATION_LIST. If you set up a reviewer, it will be added to the card members.
26+
27+
- **move_card_when_pull_request_closed**
28+
29+
This moves Trello card when issue closed.
30+
31+
# Env
32+
`TRELLO_API_KEY`: Your Trello API key
33+
34+
`TRELLO_API_TOKEN`: Your Trello API token
35+
36+
`TRELLO_BOARD_ID`: Your Trello board ID
37+
38+
`TRELLO_LIST_ID`: Your Trello list ID (only: create_card_when_issue_open)
39+
40+
`TRELLO_DEPARTURE_LIST_ID`: Your Trello list ID to move from (only: create_card_when_issue_open, move_card_when_pull_request_closed)
41+
42+
`TRELLO_DESTINATION_LIST_ID`: Your Trello list ID to move to (only: create_card_when_issue_open, move_card_when_pull_request_closed)

action.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: 'Link GitHub Issue and Pull Request with Trello Card'
2+
3+
description: 'You can link GitHub issues and pull request with Trello card.'
4+
5+
inputs:
6+
trello-action:
7+
description: 'Action to do'
8+
required: true
9+
10+
runs:
11+
using: 'node12'
12+
main: 'index.js'

index.js

+260
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
const core = require('@actions/core');
2+
const github = require('@actions/github');
3+
const request = require('request-promise-native');
4+
5+
try {
6+
const apiKey = process.env['TRELLO_API_KEY'];
7+
const apiToken = process.env['TRELLO_API_TOKEN'];
8+
const boardId = process.env['TRELLO_BOARD_ID'];
9+
const action = core.getInput('trello-action');
10+
11+
switch (action) {
12+
case 'create_card_when_issue_opened':
13+
createCardWhenIssueOpen(apiKey, apiToken, boardId);
14+
break;
15+
case 'move_card_when_pull_request_opened':
16+
moveCardWhenPullRequestOpen(apiKey, apiToken, boardId);
17+
break;
18+
case 'move_card_when_pull_request_closed':
19+
moveCardWhenPullRequestClose(apiKey, apiToken, boardId);
20+
break;
21+
22+
}
23+
} catch (error) {
24+
core.setFailed(error.message);
25+
}
26+
27+
function createCardWhenIssueOpen(apiKey, apiToken, boardId) {
28+
const listId = process.env['TRELLO_LIST_ID'];
29+
const issue = github.context.payload.issue
30+
const number = issue.number;
31+
const title = issue.title;
32+
const description = issue.body;
33+
const url = issue.html_url;
34+
const assignees = issue.assignees.map(assignee => assignee.login);
35+
const issueLabelNames = issue.labels.map(label => label.name);
36+
37+
getLabelsOfBoard(apiKey, apiToken, boardId).then(function(response) {
38+
const trelloLabels = response;
39+
const trelloLabelIds = [];
40+
issueLabelNames.forEach(function(issueLabelName) {
41+
trelloLabels.forEach(function(trelloLabel) {
42+
if (trelloLabel.name == issueLabelName) {
43+
trelloLabelIds.push(trelloLabel.id);
44+
}
45+
});
46+
});
47+
48+
getMembersOfBoard(apiKey, apiToken, boardId).then(function(response) {
49+
const members = response;
50+
const memberIds = [];
51+
assignees.forEach(function(assignee) {
52+
members.forEach(function(member) {
53+
if (member.username == assignee) {
54+
memberIds.push(member.id)
55+
}
56+
});
57+
});
58+
const cardParams = {
59+
number: number, title: title, description: description, url: url, memberIds: memberIds.join(), labelIds: trelloLabelIds.join()
60+
}
61+
62+
createCard(apiKey, apiToken, listId, cardParams).then(function(response) {
63+
console.dir(response);
64+
});
65+
});
66+
});
67+
}
68+
69+
function moveCardWhenPullRequestOpen(apiKey, apiToken, boardId) {
70+
const departureListId = process.env['TRELLO_DEPARTURE_LIST_ID'];
71+
const destinationListId = process.env['TRELLO_DESTINATION_LIST_ID'];
72+
const pullRequest = github.context.payload.pull_request
73+
const issue_number = pullRequest.body.match(/#[0-9]+/)[0].slice(1);
74+
const url = pullRequest.html_url;
75+
const reviewers = pullRequest.requested_reviewers.map(reviewer => reviewer.login);
76+
77+
getMembersOfBoard(apiKey, apiToken, boardId).then(function(response) {
78+
const members = response;
79+
const additionalMemberIds = [];
80+
reviewers.forEach(function(reviewer) {
81+
members.forEach(function(member) {
82+
if (member.username == reviewer) {
83+
additionalMemberIds.push(member.id);
84+
}
85+
});
86+
});
87+
88+
getCardsOfList(apiKey, apiToken, departureListId).then(function(response) {
89+
const cards = response;
90+
let cardId;
91+
let existingMemberIds = [];
92+
cards.some(function(card) {
93+
const card_issue_number = card.name.match(/#[0-9]+/)[0].slice(1);
94+
if (card_issue_number == issue_number) {
95+
cardId = card.id;
96+
existingMemberIds = card.idMembers;
97+
return true;
98+
}
99+
});
100+
const cardParams = {
101+
destinationListId: destinationListId, memberIds: existingMemberIds.concat(additionalMemberIds).join()
102+
}
103+
104+
if (cardId) {
105+
putCard(apiKey, apiToken, cardId, cardParams).then(function(response) {
106+
addUrlSourceToCard(apiKey, apiToken, cardId, url);
107+
});
108+
} else {
109+
core.setFailed('Card not found.');
110+
}
111+
});
112+
});
113+
}
114+
115+
function moveCardWhenPullRequestClose(apiKey, apiToken, boardId) {
116+
const departureListId = process.env['TRELLO_DEPARTURE_LIST_ID'];
117+
const destinationListId = process.env['TRELLO_DESTINATION_LIST_ID'];
118+
const pullRequest = github.context.payload.pull_request
119+
const issue_number = pullRequest.body.match(/#[0-9]+/)[0].slice(1);
120+
const url = pullRequest.html_url;
121+
const reviewers = pullRequest.requested_reviewers.map(reviewer => reviewer.login);
122+
123+
getMembersOfBoard(apiKey, apiToken, boardId).then(function(response) {
124+
const members = response;
125+
const additionalMemberIds = [];
126+
reviewers.forEach(function(reviewer) {
127+
members.forEach(function(member) {
128+
if (member.username == reviewer) {
129+
additionalMemberIds.push(member.id);
130+
}
131+
});
132+
});
133+
134+
getCardsOfList(apiKey, apiToken, departureListId).then(function(response) {
135+
const cards = response;
136+
let cardId;
137+
let existingMemberIds = [];
138+
cards.some(function(card) {
139+
const card_issue_number = card.name.match(/#[0-9]+/)[0].slice(1);
140+
if (card_issue_number == issue_number) {
141+
cardId = card.id;
142+
existingMemberIds = card.idMembers;
143+
return true;
144+
}
145+
});
146+
const cardParams = {
147+
destinationListId: destinationListId, memberIds: existingMemberIds.concat(additionalMemberIds).join()
148+
}
149+
150+
if (cardId) {
151+
putCard(apiKey, apiToken, cardId, cardParams);
152+
} else {
153+
core.setFailed('Card not found.');
154+
}
155+
});
156+
});
157+
}
158+
159+
function getLabelsOfBoard(apiKey, apiToken, boardId) {
160+
return new Promise(function(resolve, reject) {
161+
request(`https://api.trello.com/1/boards/${boardId}/labels?key=${apiKey}&token=${apiToken}`)
162+
.then(function(body) {
163+
resolve(JSON.parse(body));
164+
})
165+
.catch(function(error) {
166+
reject(error);
167+
})
168+
});
169+
}
170+
171+
function getMembersOfBoard(apiKey, apiToken, boardId) {
172+
return new Promise(function(resolve, reject) {
173+
request(`https://api.trello.com/1/boards/${boardId}/members?key=${apiKey}&token=${apiToken}`)
174+
.then(function(body) {
175+
resolve(JSON.parse(body));
176+
})
177+
.catch(function(error) {
178+
reject(error);
179+
})
180+
});
181+
}
182+
183+
function getCardsOfList(apiKey, apiToken, listId) {
184+
return new Promise(function(resolve, reject) {
185+
request(`https://api.trello.com/1/lists/${listId}/cards?key=${apiKey}&token=${apiToken}`)
186+
.then(function(body) {
187+
resolve(JSON.parse(body));
188+
})
189+
.catch(function(error) {
190+
reject(error);
191+
})
192+
});
193+
}
194+
195+
function createCard(apiKey, apiToken, listId, params) {
196+
const options = {
197+
method: 'POST',
198+
url: 'https://api.trello.com/1/cards',
199+
form: {
200+
'idList': listId,
201+
'keepFromSource': 'all',
202+
'key': apiKey,
203+
'token': apiToken,
204+
'name': `[#${params.number}] ${params.title}`,
205+
'desc': params.description,
206+
'urlSource': params.url,
207+
'idMembers': params.memberIds,
208+
'idLabels': params.labelIds
209+
},
210+
json: true
211+
}
212+
return new Promise(function(resolve, reject) {
213+
request(options)
214+
.then(function(body) {
215+
resolve(body);
216+
})
217+
.catch(function(error) {
218+
reject(error);
219+
})
220+
});
221+
}
222+
223+
function putCard(apiKey, apiToken, cardId, params) {
224+
const options = {
225+
method: 'PUT',
226+
url: `https://api.trello.com/1/cards/${cardId}?key=${apiKey}&token=${apiToken}`,
227+
form: {
228+
'idList': params.destinationListId,
229+
'idMembers': params.memberIds,
230+
}
231+
}
232+
return new Promise(function(resolve, reject) {
233+
request(options)
234+
.then(function(body) {
235+
resolve(JSON.parse(body));
236+
})
237+
.catch(function(error) {
238+
reject(error);
239+
})
240+
});
241+
}
242+
243+
function addUrlSourceToCard(apiKey, apiToken, cardId, url) {
244+
const options = {
245+
method: 'POST',
246+
url: `https://api.trello.com/1/cards/${cardId}/attachments?key=${apiKey}&token=${apiToken}`,
247+
form: {
248+
url: url
249+
}
250+
}
251+
return new Promise(function(resolve, reject) {
252+
request(options)
253+
.then(function(body) {
254+
resolve(JSON.parse(body));
255+
})
256+
.catch(function(error) {
257+
reject(error);
258+
})
259+
});
260+
}

node_modules/.bin/semver

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/sshpk-conv

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/sshpk-sign

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/sshpk-verify

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/uuid

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/which

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)