Skip to content

Commit e6aad73

Browse files
committed
chore(ci): add first centralized reusable workflow
1 parent 822ef01 commit e6aad73

File tree

3 files changed

+393
-0
lines changed

3 files changed

+393
-0
lines changed

bla.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# KMS_KEYS = ["arn:aws:kms:eu-west-1:231436140809:key/1053c7d7-97d7-49ca-9763-5214f28999cc"]
2+
3+
# from aws_lambda_powertools.utilities.data_masking import DataMasking
4+
# from aws_lambda_powertools.utilities.data_masking.provider.kms.aws_encryption_sdk import (
5+
# AWSEncryptionSDKProvider,
6+
# )
7+
8+
9+
# # from aws_lambda_powertools import Logger
10+
11+
# # from aws_lambda_powertools.logging.utils import copy_config_to_registered_loggers
12+
13+
# # logger = Logger()
14+
15+
# # copy_config_to_registered_loggers(source_logger=logger, log_level="DEBUG")
16+
17+
# # def test_encryption_context_kms_api(data: bytes):
18+
# # import boto3
19+
20+
# # kms = boto3.client("kms")
21+
# # cipher = kms.encrypt(KeyId=KMS_KEYS[0], Plaintext=data, EncryptionContext={"h": "e"})
22+
23+
# # # When encryption context isn't an exact match
24+
# # # botocore.errorfactory.InvalidCiphertextException: An error occurred (InvalidCiphertextException) when calling the Decrypt operation:
25+
# # kms.decrypt(CiphertextBlob=cipher["CiphertextBlob"])
26+
27+
28+
# encryption_provider = AWSEncryptionSDKProvider(keys=KMS_KEYS)
29+
# masker = DataMasking(provider=encryption_provider)
30+
31+
32+
# data = {
33+
# "name": "Leandro",
34+
# "operation": "non sensitive",
35+
# "card_number": "1000 4444 333 2222",
36+
# "address": [
37+
# {
38+
# "postcode": 81847,
39+
# "street": "38986 Joanne Stravenue",
40+
# "country": "United States",
41+
# "timezone": "America/La_Paz",
42+
# },
43+
# {"postcode": 94400, "street": "623 Kraig Mall", "country": "United States", "timezone": "America/Mazatlan"},
44+
# ],
45+
# }
46+
47+
48+
# fields = ["address[*].postcode"]
49+
# encrypted = masker.encrypt(data, fields, data_classification="10", data_type="customer_data")
50+
# output = f"""
51+
# ========================Encrypted blob========================
52+
53+
# {masker.encrypt(data)}
54+
55+
# ========================Field encrypted=======================
56+
57+
# {encrypted}
58+
59+
# ========================Decrypted=============================
60+
61+
62+
# {masker.decrypt(encrypted, data_type="customer_data", data_classification="10")}
63+
64+
# ==============================================================
65+
# """
66+
67+
# print(output)
68+
69+
# # encrypted_data = {
70+
# # "zip": [
71+
# # {
72+
# # "postcode": "AgV4cx2NbEYAiNulToXGoaFxGB8avUnn0C5yuECrPbTgrmkAXwABABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREFtV0hyRzROQ0h3Z1hSQmFGVDZCemUwT2dzMGVsK2tTL2hUV1FKZnZrTjJ3OUNQYlk2bTg3QmF0MVJ3NmdNUWNIZz09AAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOmV1LXdlc3QtMToyMzE0MzYxNDA4MDk6a2V5LzEwNTNjN2Q3LTk3ZDctNDljYS05NzYzLTUyMTRmMjg5OTljYwC4AQIBAHgWR9amI/KXfzepTHbCvEUXLMN8D5Fzkp9W3nvbLFphtgF1vgCkRBJno+apgeramUnFAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM1s93BzUOeCTYIFJ/AgEQgDvhPDdfMN6dPAdQN16djAUhoYjkdIbKx+g9VBp2HZ8RF2gBvsla3chPkTR+ElfanPiEqt49rL2c46RvVwIAABAAV8XWwONI8SlcSYaGblM516qB6gh93eEb2DlWbt8uo13BpgdjZhYEIzRU7C+Nmoq6/////wAAAAEAAAAAAAAAAAAAAAEAAAAHfIP2Crb6uIIkj0KnT2y34JUyKL/L3TAAZzBlAjAqPfOXUJBmB3BpUL4VT4xaHVSv74wfrqtHbTVKrzvTKpb7AdwMJTEXLHXcZwQXncwCMQCX82+7r6wZ7aE1d7c/thDLB/w3HcFAvEadnBsBbkGrMpSZrI6TiRuqSE4EIZZLNWc="
73+
# # }
74+
# # ],
75+
# # "name": "Lessa",
76+
# # "operation": "non sensitive",
77+
# # "card_number": "1000 4444 333 2222",
78+
# # }

playground/.prettierrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"tabWidth": 2,
3+
"useTabs": false
4+
}

playground/app.mjs

+311
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
const DEFAULT_EMPTY_RESPONSE = [{}];
2+
const MONTH = new Date().toLocaleString("default", { month: "long" });
3+
const BLOCKED_LABELS = [
4+
"do-not-merge",
5+
"need-issue",
6+
"need-rfc",
7+
"need-customer-feedback",
8+
];
9+
10+
/**
11+
* Calculate the difference in days between the current date and a given datetime.
12+
*
13+
* @param {string} datetime - The datetime string to calculate the difference from.
14+
* @returns {number} - The difference in days between the current date and the given datetime.
15+
*/
16+
const diffInDays = (datetime) => {
17+
const diff_in_ms = new Date() - new Date(datetime);
18+
19+
// ms(1000)->seconds(60)->minutes(60)->hours(24)->days
20+
return Math.floor(diff_in_ms / (1000 * 60 * 60 * 24));
21+
};
22+
23+
/**
24+
* Formats a datetime string into a localized human date string e.g., April 5, 2024.
25+
*
26+
* @param {string} datetime - The datetime string to format.
27+
* @returns {string} The formatted date string.
28+
*
29+
* @example
30+
* const datetime = "2022-01-01T12:00:00Z";
31+
* console.log(formatDate(datetime)); // April 5, 2024
32+
*/
33+
const formatDate = (datetime) => {
34+
const date = new Date(datetime);
35+
return new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(date);
36+
};
37+
38+
/**
39+
* Generates a markdown table from an array of key:value object.
40+
*
41+
* This function takes an array of objects as input and generates a markdown table with the keys as column headings and the values as rows.
42+
*
43+
* @param {Array<Object>} data - The data to generate the table from.
44+
* @returns {Object} An object containing the formatted table components.
45+
* - heading: The formatted column headings of the table.
46+
* - dashes: The formatted dashed line separating the headings from the rows.
47+
* - rows: The formatted rows of the table.
48+
*
49+
* @example
50+
* const data = [
51+
* { name: 'John', age: 30, city: 'New York' },
52+
* { name: 'Jane', age: 25, city: 'London' },
53+
* { name: 'Bob', age: 35, city: 'Paris' }
54+
* ];
55+
*
56+
* const table = buildMarkdownTable(data);
57+
* console.log(table.heading); // '| name | age | city |'
58+
* console.log(table.dashes); // '| ---- | --- | ---- |'
59+
* console.log(table.rows); // '| John | 30 | New York |'
60+
*/
61+
const buildMarkdownTable = (data) => {
62+
const keys = Object.keys(data[0]);
63+
64+
if (keys.length === 0) {
65+
return "Not available";
66+
}
67+
68+
const formatted_headings = `${keys.join(" | ")}`;
69+
const keyLength = keys[0]?.length || 0;
70+
const dashes = keys.map(() => `${"-".repeat(keyLength)} |`).join(" ");
71+
72+
const values = data.map((issues) => Object.values(issues));
73+
const rows = values.map((row) => `${row.join(" | ")} |`).join("\n");
74+
75+
return `${formatted_headings}
76+
${dashes}
77+
${rows}`;
78+
};
79+
80+
/**
81+
* Retrieves a list of PRs from a repository sorted by `reactions-+1` keyword.
82+
*
83+
* @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments
84+
* @typedef {Object} Response
85+
* @property {string} title - The title of the issue, with a link to the issue.
86+
* @property {string} created_at - The creation date of the issue, formatted as April 5, 2024.
87+
* @property {number} reaction_count - The total number of reactions on the issue.
88+
* @property {string} labels - The labels of the issue, enclosed in backticks.
89+
* @returns {Promise<Array<Response>>} A promise resolving with an array of issue objects.
90+
*
91+
*/
92+
async function getTopFeatureRequests({ github, context, core }) {
93+
core.info("Fetching feature requests sorted by +1 reactions");
94+
95+
const { data: issues } = await github.rest.issues.listForRepo({
96+
owner: context.repo.owner,
97+
repo: context.repo.repo,
98+
labels: "feature-request",
99+
sort: "reactions-+1",
100+
direction: "desc",
101+
per_page: 3,
102+
});
103+
104+
core.info("Successfully fetched issues");
105+
core.debug(issues);
106+
107+
return issues.map((issue) => ({
108+
title: `[${issue.title}](${issue.html_url})`,
109+
created_at: formatDate(issue.created_at),
110+
reaction_count: issue.reactions.total_count,
111+
labels: `${issue.labels.map((label) => `\`${label.name}\``).join(",")}`, // enclose each label with `<label>` for rendering
112+
}));
113+
}
114+
115+
/**
116+
* Retrieves a list of issues from a repository sorted by `comments` keyword.
117+
*
118+
* @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments
119+
* @typedef {Object} Response
120+
* @property {string} title - The title of the issue, with a link to the issue.
121+
* @property {string} created_at - The creation date of the issue, formatted as April 5, 2024.
122+
* @property {number} comment_count - The total number of comments in the issue.
123+
* @property {string} labels - The labels of the issue, enclosed in backticks.
124+
* @returns {Promise<Array<Response>>} A promise resolving with an array of issue objects.
125+
*
126+
*/
127+
async function getTopMostCommented({ github, context, core }) {
128+
core.info("Fetching issues sorted by comments");
129+
130+
const { data: issues } = await github.rest.issues.listForRepo({
131+
owner: context.repo.owner,
132+
repo: context.repo.repo,
133+
sort: "comments",
134+
direction: "desc",
135+
per_page: 3,
136+
});
137+
138+
core.info("Successfully fetched issues");
139+
core.debug(issues);
140+
141+
return issues.map((issue) => ({
142+
title: `[${issue.title}](${issue.html_url})`,
143+
created_at: formatDate(issue.created_at),
144+
comment_count: issue.comments,
145+
labels: `${issue.labels.map((label) => `\`${label.name}\``).join(",")}`, // enclose each label with `<label>` for rendering
146+
}));
147+
}
148+
149+
/**
150+
* Retrieves a list of oldest issues from a repository sorted by `created` keyword, excluding blocked labels.
151+
*
152+
* @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments
153+
*
154+
* @typedef {Object} Response
155+
* @property {string} title - The title of the issue, with a link to the issue.
156+
* @property {string} created_at - The creation date of the issue, formatted as April 5, 2024.
157+
* @property {number} last_update - Number of days since the last update.
158+
* @property {string} labels - The labels of the issue, enclosed in backticks.
159+
* @returns {Promise<Array<Response>>} A promise resolving with an array of issue objects.
160+
*/
161+
async function getTopOldestIssues({ github, context, core }) {
162+
core.info("Fetching issues sorted by creation date");
163+
const { data: issues } = await githubClient.rest.issues.listForRepo({
164+
owner: context.repo.owner,
165+
repo: context.repo.repo,
166+
sort: "created",
167+
direction: "asc",
168+
per_page: 30,
169+
});
170+
171+
core.info("Successfully fetched issues");
172+
core.debug(issues);
173+
174+
core.info(
175+
`Filtering out issues that contained blocking labels: ${BLOCKED_LABELS}`
176+
);
177+
const top3 = issues
178+
.filter((issue) =>
179+
issue.labels.every((label) => !BLOCKED_LABELS.includes(label.name))
180+
)
181+
.slice(0, 3);
182+
183+
core.debug(top3);
184+
185+
return top3.map((issue) => {
186+
return {
187+
title: `[${issue.title}](${issue.html_url})`,
188+
created_at: formatDate(issue.created_at),
189+
last_update: `${diffInDays(issue.updated_at)} days`,
190+
labels: `${issue.labels.map((label) => `\`${label.name}\``).join(",")}`, // enclose each label with `<label>` for rendering
191+
};
192+
});
193+
}
194+
195+
/**
196+
* Retrieves a list of long running pull requests from a repository, excluding blocked labels.
197+
*
198+
* @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments
199+
*
200+
* @typedef {Object} Response
201+
* @property {string} title - The title of the PR, with a link to the PR.
202+
* @property {string} created_at - The creation date of the PR, formatted as April 5, 2024.
203+
* @property {number} last_update - Number of days since the last update.
204+
* @property {string} labels - The labels of the PR, enclosed in backticks.
205+
* @returns {Promise<Array<Response>>} A promise resolving with an array of PR objects.
206+
*/
207+
async function getLongRunningPRs({ github, context, core }) {
208+
core.info("Fetching PRs sorted by long-running");
209+
const { data: prs } = await github.rest.pulls.list({
210+
owner: context.repo.owner,
211+
repo: context.repo.repo,
212+
sort: "long-running",
213+
direction: "desc",
214+
per_page: 30,
215+
});
216+
217+
core.debug(issues);
218+
219+
core.info(
220+
`Filtering out issues that contained blocking labels: ${BLOCKED_LABELS}`
221+
);
222+
const top3 = prs
223+
.filter((pr) =>
224+
pr.labels.every((label) => !BLOCKED_LABELS.includes(label.name))
225+
)
226+
.slice(0, 3);
227+
228+
core.debug(top3);
229+
230+
return top3.map((issue) => {
231+
return {
232+
title: `[${issue.title}](${issue.html_url})`,
233+
created_at: formatDate(issue.created_at),
234+
last_update: `${diffInDays(issue.updated_at)} days`,
235+
labels: `${issue.labels.map((label) => `\`${label.name}\``).join(",")}`, // enclose each label with `<label>` for rendering
236+
};
237+
});
238+
}
239+
240+
/**
241+
* Creates a monthly roadmap issue report with top PFRs, most active issues, and stale requests.
242+
*
243+
* Example issue: https://github.com/heitorlessa/action-script-playground/issues/24
244+
*
245+
* @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments
246+
* @returns {Promise<void>} A promise resolving when the issue is created.
247+
*
248+
*/
249+
async function createMonthlyRoadmapReport({ github, context, core }) {
250+
core.info("Fetching GitHub data concurrently");
251+
252+
const [
253+
{ value: featureRequests = DEFAULT_EMPTY_RESPONSE },
254+
{ value: longRunningPRs = DEFAULT_EMPTY_RESPONSE },
255+
{ value: oldestIssues = DEFAULT_EMPTY_RESPONSE },
256+
{ value: mostActiveIssues = DEFAULT_EMPTY_RESPONSE },
257+
] = await Promise.allSettled([
258+
getTopFeatureRequests({ github, context, core }),
259+
getLongRunningPRs({ github, context, core }),
260+
getTopOldestIssues({ github, context, core }),
261+
getTopMostCommented({ github, context, core }),
262+
]);
263+
264+
const tables = {
265+
featureRequests: buildMarkdownTable(featureRequests),
266+
mostActiveIssues: buildMarkdownTable(mostActiveIssues),
267+
longRunningPRs: buildMarkdownTable(longRunningPRs),
268+
oldestIssues: buildMarkdownTable(oldestIssues),
269+
};
270+
271+
const body = `
272+
273+
Quick report of top 3 issues/PRs to assist in roadmap updates. Issues or PRs with the following labels are excluded:
274+
275+
* \`do-not-merge\`
276+
* \`need-rfc\`
277+
* \`need-issue\`
278+
* \`need-customer-feedback\`
279+
280+
> **NOTE**: It does not guarantee they will be in the roadmap. Some might already be and there might be a blocker.
281+
282+
## Top 3 Feature Requests
283+
284+
${tables.featureRequests}
285+
286+
## Top 3 Most Commented Issues
287+
288+
${tables.mostActiveIssues}
289+
290+
## Top 3 Long Running Pull Requests
291+
292+
${tables.longRunningPRs}
293+
294+
## Top 3 Oldest Issues
295+
296+
${tables.oldestIssues}
297+
`;
298+
299+
return await githubClient.issues.create({
300+
owner: context.repo.owner,
301+
repo: context.repo.repo,
302+
title: `Roadmap update reminder - ${MONTH}`,
303+
body,
304+
});
305+
}
306+
307+
// @ts-check
308+
/** @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
309+
module.exports = async ({ github, context, core }) => {
310+
return await createMonthlyRoadmapReport({ github, context, core });
311+
};

0 commit comments

Comments
 (0)