@foss.global/codefeed 1.5.3 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/index.d.ts +13 -19
- package/dist_ts/index.js +69 -27
- package/dist_ts/interfaces/index.d.ts +13 -12
- package/npmextra.json +15 -2
- package/package.json +15 -2
- package/readme.md +126 -3
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/index.ts +98 -47
- package/ts/interfaces/index.ts +13 -12
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@foss.global/codefeed',
|
|
6
|
-
version: '1.
|
|
7
|
-
description: '
|
|
6
|
+
version: '1.6.1',
|
|
7
|
+
description: 'The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx1QkFBdUI7SUFDN0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLGlMQUFpTDtDQUMvTCxDQUFBIn0=
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -5,33 +5,27 @@ export declare class CodeFeed {
|
|
|
5
5
|
private npmRegistry;
|
|
6
6
|
private smartxmlInstance;
|
|
7
7
|
private lastRunTimestamp;
|
|
8
|
+
private changelogContent;
|
|
8
9
|
constructor(baseUrl: string, token?: string, lastRunTimestamp?: string);
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Load the changelog directly from the Gitea repository.
|
|
11
12
|
*/
|
|
12
|
-
private
|
|
13
|
+
private loadChangelogFromRepo;
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
+
* Parse the changelog to find the entry for a given version.
|
|
16
|
+
* The changelog format is assumed as:
|
|
17
|
+
*
|
|
18
|
+
* # Changelog
|
|
19
|
+
*
|
|
20
|
+
* ## <date> - <version> - <description>
|
|
21
|
+
* <changes...>
|
|
15
22
|
*/
|
|
23
|
+
private getChangelogForVersion;
|
|
24
|
+
private fetchAllOrganizations;
|
|
16
25
|
private fetchOrgRssFeed;
|
|
17
|
-
/**
|
|
18
|
-
* Check if the organization's RSS feed has any new activities since the last run.
|
|
19
|
-
*/
|
|
20
26
|
private hasNewActivity;
|
|
21
|
-
/**
|
|
22
|
-
* Fetch all repositories accessible to the token/user.
|
|
23
|
-
*/
|
|
24
27
|
private fetchAllRepositories;
|
|
25
|
-
/**
|
|
26
|
-
* Fetch all tags for a given repository.
|
|
27
|
-
*/
|
|
28
28
|
private fetchTags;
|
|
29
|
-
/**
|
|
30
|
-
* Fetch commits from the last 24 hours for a repository.
|
|
31
|
-
*/
|
|
32
29
|
private fetchRecentCommitsForRepo;
|
|
33
|
-
|
|
34
|
-
* Fetch all commits by querying all organizations.
|
|
35
|
-
*/
|
|
36
|
-
fetchAllCommitsFromInstance(): Promise<plugins.interfaces.CommitResult[]>;
|
|
30
|
+
fetchAllCommitsFromInstance(): Promise<plugins.interfaces.ICommitResult[]>;
|
|
37
31
|
}
|
package/dist_ts/index.js
CHANGED
|
@@ -9,8 +9,58 @@ export class CodeFeed {
|
|
|
9
9
|
console.log('CodeFeed initialized with last run timestamp:', this.lastRunTimestamp);
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Load the changelog directly from the Gitea repository.
|
|
13
13
|
*/
|
|
14
|
+
async loadChangelogFromRepo(owner, repo) {
|
|
15
|
+
const url = `${this.baseUrl}/api/v1/repos/${owner}/${repo}/contents/changelog.md`;
|
|
16
|
+
const headers = {};
|
|
17
|
+
if (this.token) {
|
|
18
|
+
headers['Authorization'] = `token ${this.token}`;
|
|
19
|
+
}
|
|
20
|
+
const response = await fetch(url, { headers });
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
console.error(`Could not fetch CHANGELOG.md from ${owner}/${repo}: ${response.status} ${response.statusText}`);
|
|
23
|
+
this.changelogContent = '';
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const data = await response.json();
|
|
27
|
+
if (!data.content) {
|
|
28
|
+
console.warn(`No content field found in response for ${owner}/${repo}/changelog.md`);
|
|
29
|
+
this.changelogContent = '';
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const decodedContent = Buffer.from(data.content, 'base64').toString('utf8');
|
|
33
|
+
this.changelogContent = decodedContent;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Parse the changelog to find the entry for a given version.
|
|
37
|
+
* The changelog format is assumed as:
|
|
38
|
+
*
|
|
39
|
+
* # Changelog
|
|
40
|
+
*
|
|
41
|
+
* ## <date> - <version> - <description>
|
|
42
|
+
* <changes...>
|
|
43
|
+
*/
|
|
44
|
+
getChangelogForVersion(version) {
|
|
45
|
+
if (!this.changelogContent) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const lines = this.changelogContent.split('\n');
|
|
49
|
+
const versionHeaderIndex = lines.findIndex((line) => line.includes(`- ${version} -`));
|
|
50
|
+
if (versionHeaderIndex === -1) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const changelogLines = [];
|
|
54
|
+
for (let i = versionHeaderIndex + 1; i < lines.length; i++) {
|
|
55
|
+
const line = lines[i];
|
|
56
|
+
// The next version header starts with `## `
|
|
57
|
+
if (line.startsWith('## ')) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
changelogLines.push(line);
|
|
61
|
+
}
|
|
62
|
+
return changelogLines.join('\n').trim();
|
|
63
|
+
}
|
|
14
64
|
async fetchAllOrganizations() {
|
|
15
65
|
const url = `${this.baseUrl}/api/v1/orgs`;
|
|
16
66
|
const response = await fetch(url, {
|
|
@@ -22,9 +72,6 @@ export class CodeFeed {
|
|
|
22
72
|
const data = await response.json();
|
|
23
73
|
return data.map((org) => org.username);
|
|
24
74
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Fetch organization-level activity RSS feed.
|
|
27
|
-
*/
|
|
28
75
|
async fetchOrgRssFeed(optionsArg) {
|
|
29
76
|
let rssUrl;
|
|
30
77
|
if (optionsArg.orgName && !optionsArg.repoName) {
|
|
@@ -33,30 +80,24 @@ export class CodeFeed {
|
|
|
33
80
|
else if (optionsArg.orgName && optionsArg.repoName) {
|
|
34
81
|
rssUrl = `${this.baseUrl}/${optionsArg.orgName}/${optionsArg.repoName}.atom`;
|
|
35
82
|
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error('Invalid arguments provided to fetchOrgRssFeed.');
|
|
85
|
+
}
|
|
36
86
|
const response = await fetch(rssUrl);
|
|
37
87
|
if (!response.ok) {
|
|
38
88
|
throw new Error(`Failed to fetch RSS feed for organization ${optionsArg.orgName}/${optionsArg.repoName}: ${response.statusText}`);
|
|
39
89
|
}
|
|
40
90
|
const rssText = await response.text();
|
|
41
|
-
// Parse the Atom feed using fast-xml-parser
|
|
42
91
|
const rssData = this.smartxmlInstance.parseXmlToObject(rssText);
|
|
43
|
-
// Return the <entry> elements from the feed
|
|
44
92
|
return rssData.feed.entry || [];
|
|
45
93
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Check if the organization's RSS feed has any new activities since the last run.
|
|
48
|
-
*/
|
|
49
94
|
async hasNewActivity(optionsArg) {
|
|
50
95
|
const entries = await this.fetchOrgRssFeed(optionsArg);
|
|
51
|
-
// Filter entries to find new activities since the last run
|
|
52
96
|
return entries.some((entry) => {
|
|
53
97
|
const updated = new Date(entry.updated);
|
|
54
98
|
return updated > new Date(this.lastRunTimestamp);
|
|
55
99
|
});
|
|
56
100
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Fetch all repositories accessible to the token/user.
|
|
59
|
-
*/
|
|
60
101
|
async fetchAllRepositories() {
|
|
61
102
|
let page = 1;
|
|
62
103
|
const allRepos = [];
|
|
@@ -79,9 +120,6 @@ export class CodeFeed {
|
|
|
79
120
|
}
|
|
80
121
|
return allRepos;
|
|
81
122
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Fetch all tags for a given repository.
|
|
84
|
-
*/
|
|
85
123
|
async fetchTags(owner, repo) {
|
|
86
124
|
let page = 1;
|
|
87
125
|
const tags = [];
|
|
@@ -111,9 +149,6 @@ export class CodeFeed {
|
|
|
111
149
|
}
|
|
112
150
|
return taggedCommitShas;
|
|
113
151
|
}
|
|
114
|
-
/**
|
|
115
|
-
* Fetch commits from the last 24 hours for a repository.
|
|
116
|
-
*/
|
|
117
152
|
async fetchRecentCommitsForRepo(owner, repo) {
|
|
118
153
|
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
119
154
|
let page = 1;
|
|
@@ -146,9 +181,6 @@ export class CodeFeed {
|
|
|
146
181
|
}
|
|
147
182
|
return recentCommits;
|
|
148
183
|
}
|
|
149
|
-
/**
|
|
150
|
-
* Fetch all commits by querying all organizations.
|
|
151
|
-
*/
|
|
152
184
|
async fetchAllCommitsFromInstance() {
|
|
153
185
|
const orgs = await this.fetchAllOrganizations();
|
|
154
186
|
console.log(`Found ${orgs.length} organizations`);
|
|
@@ -191,6 +223,8 @@ export class CodeFeed {
|
|
|
191
223
|
try {
|
|
192
224
|
const taggedCommitShas = await this.fetchTags(org, repo);
|
|
193
225
|
const commits = await this.fetchRecentCommitsForRepo(org, repo);
|
|
226
|
+
// Load the changelog from this repo.
|
|
227
|
+
await this.loadChangelogFromRepo(org, repo);
|
|
194
228
|
const commitResults = commits.map((c) => {
|
|
195
229
|
const commit = {
|
|
196
230
|
baseUrl: this.baseUrl,
|
|
@@ -202,18 +236,24 @@ export class CodeFeed {
|
|
|
202
236
|
commitMessage: c.commit.message,
|
|
203
237
|
tagged: taggedCommitShas.has(c.sha),
|
|
204
238
|
publishedOnNpm: false,
|
|
239
|
+
changelog: undefined
|
|
205
240
|
};
|
|
206
241
|
return commit;
|
|
207
242
|
});
|
|
208
243
|
if (commitResults.length > 0) {
|
|
209
244
|
try {
|
|
210
245
|
const packageInfo = await this.npmRegistry.getPackageInfo(`@${org}/${repo}`);
|
|
211
|
-
for (const
|
|
246
|
+
for (const commitResult of commitResults.filter((c) => c.tagged)) {
|
|
247
|
+
const versionCandidate = commitResult.commitMessage.replace('\n', '').trim();
|
|
212
248
|
const correspondingVersion = packageInfo.allVersions.find((versionArg) => {
|
|
213
|
-
return versionArg.version ===
|
|
249
|
+
return versionArg.version === versionCandidate;
|
|
214
250
|
});
|
|
215
251
|
if (correspondingVersion) {
|
|
216
|
-
|
|
252
|
+
commitResult.publishedOnNpm = true;
|
|
253
|
+
const changelogEntry = this.getChangelogForVersion(versionCandidate);
|
|
254
|
+
if (changelogEntry) {
|
|
255
|
+
commitResult.changelog = changelogEntry;
|
|
256
|
+
}
|
|
217
257
|
}
|
|
218
258
|
}
|
|
219
259
|
}
|
|
@@ -229,16 +269,18 @@ export class CodeFeed {
|
|
|
229
269
|
}
|
|
230
270
|
}
|
|
231
271
|
console.log(`Processed ${allCommits.length} commits in total.`);
|
|
272
|
+
allCommits = allCommits.filter(commitArg => commitArg.tagged);
|
|
273
|
+
console.log(`Filtered to ${allCommits.length} commits with tagged statuses.`);
|
|
232
274
|
for (const c of allCommits) {
|
|
233
275
|
console.log(` ==========================================================================
|
|
234
276
|
${c.prettyAgoTime} ago:
|
|
235
277
|
${c.org}/${c.repo}
|
|
236
278
|
${c.commitMessage}
|
|
237
279
|
Published on npm: ${c.publishedOnNpm}
|
|
280
|
+
${c.changelog ? `Changelog:\n${c.changelog}\n` : ''}
|
|
238
281
|
`);
|
|
239
282
|
}
|
|
240
|
-
allCommits = allCommits.filter(commitArg => commitArg.tagged);
|
|
241
283
|
return allCommits;
|
|
242
284
|
}
|
|
243
285
|
}
|
|
244
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBR2pELE1BQU0sT0FBTyxRQUFRO0lBT25CLFlBQVksT0FBZSxFQUFFLEtBQWMsRUFBRSxnQkFBeUI7UUFKOUQsZ0JBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDakQscUJBQWdCLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBSXpELElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ25CLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDckcsT0FBTyxDQUFDLEdBQUcsQ0FBQywrQ0FBK0MsRUFBRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUN0RixDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMscUJBQXFCO1FBQ2pDLE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLE9BQU8sY0FBYyxDQUFDO1FBQzFDLE1BQU0sUUFBUSxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRTtZQUNoQyxPQUFPLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxhQUFhLEVBQUUsU0FBUyxJQUFJLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRTtTQUNwRSxDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ2pC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
|
|
286
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBRWpELE1BQU0sT0FBTyxRQUFRO0lBUW5CLFlBQVksT0FBZSxFQUFFLEtBQWMsRUFBRSxnQkFBeUI7UUFMOUQsZ0JBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDakQscUJBQWdCLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBS3pELElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ25CLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDckcsT0FBTyxDQUFDLEdBQUcsQ0FBQywrQ0FBK0MsRUFBRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUN0RixDQUFDO0lBRUQ7O09BRUc7SUFDSyxLQUFLLENBQUMscUJBQXFCLENBQUMsS0FBYSxFQUFFLElBQVk7UUFDN0QsTUFBTSxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxpQkFBaUIsS0FBSyxJQUFJLElBQUksd0JBQXdCLENBQUM7UUFDbEYsTUFBTSxPQUFPLEdBQTJCLEVBQUUsQ0FBQztRQUMzQyxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNmLE9BQU8sQ0FBQyxlQUFlLENBQUMsR0FBRyxTQUFTLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNuRCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMs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
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface IRepositoryOwner {
|
|
2
2
|
login: string;
|
|
3
3
|
}
|
|
4
|
-
export interface
|
|
5
|
-
owner:
|
|
4
|
+
export interface IRepository {
|
|
5
|
+
owner: IRepositoryOwner;
|
|
6
6
|
name: string;
|
|
7
7
|
}
|
|
8
|
-
export interface
|
|
8
|
+
export interface ICommitAuthor {
|
|
9
9
|
date: string;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface ICommitDetail {
|
|
12
12
|
message: string;
|
|
13
|
-
author:
|
|
13
|
+
author: ICommitAuthor;
|
|
14
14
|
}
|
|
15
|
-
export interface
|
|
15
|
+
export interface ICommit {
|
|
16
16
|
sha: string;
|
|
17
|
-
commit:
|
|
17
|
+
commit: ICommitDetail;
|
|
18
18
|
}
|
|
19
|
-
export interface
|
|
19
|
+
export interface ITag {
|
|
20
20
|
commit?: {
|
|
21
21
|
sha?: string;
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
export interface
|
|
25
|
-
data:
|
|
24
|
+
export interface IRepoSearchResponse {
|
|
25
|
+
data: IRepository[];
|
|
26
26
|
}
|
|
27
|
-
export interface
|
|
27
|
+
export interface ICommitResult {
|
|
28
28
|
baseUrl: string;
|
|
29
29
|
org: string;
|
|
30
30
|
repo: string;
|
|
@@ -34,4 +34,5 @@ export interface CommitResult {
|
|
|
34
34
|
tagged: boolean;
|
|
35
35
|
publishedOnNpm: boolean;
|
|
36
36
|
prettyAgoTime: string;
|
|
37
|
+
changelog: string | undefined;
|
|
37
38
|
}
|
package/npmextra.json
CHANGED
|
@@ -5,10 +5,23 @@
|
|
|
5
5
|
"githost": "code.foss.global",
|
|
6
6
|
"gitscope": "foss.global",
|
|
7
7
|
"gitrepo": "codefeed",
|
|
8
|
-
"description": "
|
|
8
|
+
"description": "The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.",
|
|
9
9
|
"npmPackagename": "@foss.global/codefeed",
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"projectDomain": "foss.global"
|
|
11
|
+
"projectDomain": "foss.global",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"codefeed",
|
|
14
|
+
"Gitea",
|
|
15
|
+
"commits",
|
|
16
|
+
"changelog",
|
|
17
|
+
"repository",
|
|
18
|
+
"development tools",
|
|
19
|
+
"npm",
|
|
20
|
+
"module",
|
|
21
|
+
"code analysis",
|
|
22
|
+
"activity feed",
|
|
23
|
+
"version control"
|
|
24
|
+
]
|
|
12
25
|
}
|
|
13
26
|
},
|
|
14
27
|
"npmci": {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foss.global/codefeed",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./dist_ts/index.js",
|
|
8
8
|
"./interfaces": "./dist_ts/interfaces/index.js"
|
|
@@ -44,6 +44,19 @@
|
|
|
44
44
|
"npmextra.json",
|
|
45
45
|
"readme.md"
|
|
46
46
|
],
|
|
47
|
+
"keywords": [
|
|
48
|
+
"codefeed",
|
|
49
|
+
"Gitea",
|
|
50
|
+
"commits",
|
|
51
|
+
"changelog",
|
|
52
|
+
"repository",
|
|
53
|
+
"development tools",
|
|
54
|
+
"npm",
|
|
55
|
+
"module",
|
|
56
|
+
"code analysis",
|
|
57
|
+
"activity feed",
|
|
58
|
+
"version control"
|
|
59
|
+
],
|
|
47
60
|
"scripts": {
|
|
48
61
|
"test": "(tstest test/ --web)",
|
|
49
62
|
"build": "(tsbuild tsfolders --web --allowimplicitany)",
|
package/readme.md
CHANGED
|
@@ -1,7 +1,130 @@
|
|
|
1
|
+
```markdown
|
|
1
2
|
# @foss.global/codefeed
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
A module for creating feeds for code development.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## Install
|
|
6
7
|
|
|
7
|
-
To
|
|
8
|
+
To install the `@foss.global/codefeed` package, you can run the following npm command in your project directory:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @foss.global/codefeed
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Ensure that you have a compatible version of Node.js installed and that your project is set up to support ECMAScript modules. The `@foss.global/codefeed` module uses ESM syntax.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
The `@foss.global/codefeed` package is designed to help developers generate feeds for code developments, specifically targeting Gitea repositories. It fetches and processes commit data, changelogs, and repository activities for further analysis or visualization. Here, we'll delve into how you can utilize the different features of the `CodeFeed` class.
|
|
19
|
+
|
|
20
|
+
### Setting Up CodeFeed
|
|
21
|
+
|
|
22
|
+
To get started, import the `CodeFeed` class from the module:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { CodeFeed } from '@foss.global/codefeed';
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then, create an instance of `CodeFeed`. You'll need the base URL of your Gitea instance and optionally an API token if your repositories require authentication:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
const codeFeed = new CodeFeed('https://your-gitea-instance-url.com', 'your-api-token');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The constructor can also accept a `lastRunTimestamp` which indicates the last time a sync was performed. If not provided, it defaults to 24 hours prior to the current time.
|
|
35
|
+
|
|
36
|
+
### Fetching Commits
|
|
37
|
+
|
|
38
|
+
One of the core functionalities of CodeFeed is fetching commits from a Gitea instance. By calling `fetchAllCommitsFromInstance`, you can retrieve commits across multiple repositories:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
(async () => {
|
|
42
|
+
try {
|
|
43
|
+
const commits = await codeFeed.fetchAllCommitsFromInstance();
|
|
44
|
+
console.log(commits);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('An error occurred while fetching commits:', error);
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This method scans all organizations and repositories, filters commits tagged within the last 24 hours, and enriches them with metadata like changelogs or npm publication status.
|
|
52
|
+
|
|
53
|
+
Each commit object in the resulting array conforms to the `ICommitResult` interface, containing details such as:
|
|
54
|
+
- `baseUrl`
|
|
55
|
+
- `org`
|
|
56
|
+
- `repo`
|
|
57
|
+
- `timestamp`
|
|
58
|
+
- `hash`
|
|
59
|
+
- `commitMessage`
|
|
60
|
+
- `tagged` (boolean)
|
|
61
|
+
- `publishedOnNpm` (boolean)
|
|
62
|
+
- `prettyAgoTime` (human-readable relative time)
|
|
63
|
+
- `changelog` (text from the `changelog.md` associated with a commit)
|
|
64
|
+
|
|
65
|
+
### Understanding the Data Fetch Process
|
|
66
|
+
|
|
67
|
+
#### Fetching Organizations
|
|
68
|
+
|
|
69
|
+
The `fetchAllOrganizations` method collects all organizations within the Gitea instance:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
const organizations = await codeFeed.fetchAllOrganizations();
|
|
73
|
+
console.log('Organizations:', organizations);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This method interacts with the Gitea API to pull organization names, aiding further requests that require organization context.
|
|
77
|
+
|
|
78
|
+
#### Fetching Repositories
|
|
79
|
+
|
|
80
|
+
Repositories under these organizations can be retrieved using `fetchAllRepositories`:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const repositories = await codeFeed.fetchAllRepositories();
|
|
84
|
+
console.log('Repositories:', repositories);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Here, filtering by organization can help narrow down the scope further when dealing with large instances.
|
|
88
|
+
|
|
89
|
+
#### Fetching Tags and Commits
|
|
90
|
+
|
|
91
|
+
To handle repository-specific details, use:
|
|
92
|
+
|
|
93
|
+
- `fetchTags(owner: string, repo: string)`: Appropriately handles paginated tag data within a repository.
|
|
94
|
+
|
|
95
|
+
- `fetchRecentCommitsForRepo(owner: string, repo: string)`: Gathers commit data specific to the past 24 hours for a given repository.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const tags = await codeFeed.fetchTags('orgName', 'repoName');
|
|
99
|
+
const recentCommits = await codeFeed.fetchRecentCommitsForRepo('orgName', 'repoName');
|
|
100
|
+
|
|
101
|
+
console.log('Tags:', tags);
|
|
102
|
+
console.log('Recent Commits:', recentCommits);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Changelog Integration
|
|
106
|
+
|
|
107
|
+
Loading changelog content from a repository is integrated into the flow with `loadChangelogFromRepo`. This can be accessed when processing specific commits:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
await codeFeed.loadChangelogFromRepo('org', 'repo');
|
|
111
|
+
const changelog = codeFeed.getChangelogForVersion('1.0.0');
|
|
112
|
+
console.log('Changelog for version 1.0.0:', changelog);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Reacting to Repository Activity
|
|
116
|
+
|
|
117
|
+
The method `hasNewActivity` checks for recent changes within an organization or a repository. This is particularly useful for setting up alerting systems or continuous integration triggers:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const hasActivity = await codeFeed.hasNewActivity({ orgName: 'orgName', repoName: 'repoName' });
|
|
121
|
+
console.log('New activity detected:', hasActivity);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Conclusion
|
|
125
|
+
|
|
126
|
+
The `@foss.global/codefeed` module provides robust capabilities for extracting and managing feed data related to code developments in Gitea environments. Through systematic setup and leveraging API-driven methods, it becomes a valuable tool for developers aiming to keep track of software progress and changes efficiently. The integration hooks like changelog and npm verification further enrich its utility, offering consolidated insights into each commit's journey from codebase to published package.
|
|
127
|
+
|
|
128
|
+
Explore integrating these capabilities into your development workflows to enhance tracking, deployment pipelines, or analytics systems within your projects. Remember to always handle API tokens securely and adhere to best practices when managing access to repository resources. Stay updated on any changes or enhancements to this module for further feature exposures or bug fixes. Happy coding!
|
|
129
|
+
```
|
|
130
|
+
undefined
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@foss.global/codefeed',
|
|
6
|
-
version: '1.
|
|
7
|
-
description: '
|
|
6
|
+
version: '1.6.1',
|
|
7
|
+
description: 'The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.'
|
|
8
8
|
}
|
package/ts/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as plugins from './codefeed.plugins.js';
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
export class CodeFeed {
|
|
5
4
|
private baseUrl: string;
|
|
6
5
|
private token?: string;
|
|
7
6
|
private npmRegistry = new plugins.smartnpm.NpmRegistry();
|
|
8
7
|
private smartxmlInstance = new plugins.smartxml.SmartXml();
|
|
9
8
|
private lastRunTimestamp: string;
|
|
9
|
+
private changelogContent: string;
|
|
10
10
|
|
|
11
11
|
constructor(baseUrl: string, token?: string, lastRunTimestamp?: string) {
|
|
12
12
|
this.baseUrl = baseUrl;
|
|
@@ -16,8 +16,66 @@ export class CodeFeed {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Load the changelog directly from the Gitea repository.
|
|
20
20
|
*/
|
|
21
|
+
private async loadChangelogFromRepo(owner: string, repo: string): Promise<void> {
|
|
22
|
+
const url = `${this.baseUrl}/api/v1/repos/${owner}/${repo}/contents/changelog.md`;
|
|
23
|
+
const headers: Record<string, string> = {};
|
|
24
|
+
if (this.token) {
|
|
25
|
+
headers['Authorization'] = `token ${this.token}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const response = await fetch(url, { headers });
|
|
29
|
+
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
console.error(`Could not fetch CHANGELOG.md from ${owner}/${repo}: ${response.status} ${response.statusText}`);
|
|
32
|
+
this.changelogContent = '';
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const data = await response.json();
|
|
37
|
+
if (!data.content) {
|
|
38
|
+
console.warn(`No content field found in response for ${owner}/${repo}/changelog.md`);
|
|
39
|
+
this.changelogContent = '';
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const decodedContent = Buffer.from(data.content, 'base64').toString('utf8');
|
|
44
|
+
this.changelogContent = decodedContent;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Parse the changelog to find the entry for a given version.
|
|
49
|
+
* The changelog format is assumed as:
|
|
50
|
+
*
|
|
51
|
+
* # Changelog
|
|
52
|
+
*
|
|
53
|
+
* ## <date> - <version> - <description>
|
|
54
|
+
* <changes...>
|
|
55
|
+
*/
|
|
56
|
+
private getChangelogForVersion(version: string): string | undefined {
|
|
57
|
+
if (!this.changelogContent) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const lines = this.changelogContent.split('\n');
|
|
61
|
+
const versionHeaderIndex = lines.findIndex((line) => line.includes(`- ${version} -`));
|
|
62
|
+
if (versionHeaderIndex === -1) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const changelogLines: string[] = [];
|
|
67
|
+
for (let i = versionHeaderIndex + 1; i < lines.length; i++) {
|
|
68
|
+
const line = lines[i];
|
|
69
|
+
// The next version header starts with `## `
|
|
70
|
+
if (line.startsWith('## ')) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
changelogLines.push(line);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return changelogLines.join('\n').trim();
|
|
77
|
+
}
|
|
78
|
+
|
|
21
79
|
private async fetchAllOrganizations(): Promise<string[]> {
|
|
22
80
|
const url = `${this.baseUrl}/api/v1/orgs`;
|
|
23
81
|
const response = await fetch(url, {
|
|
@@ -32,18 +90,17 @@ export class CodeFeed {
|
|
|
32
90
|
return data.map((org) => org.username);
|
|
33
91
|
}
|
|
34
92
|
|
|
35
|
-
/**
|
|
36
|
-
* Fetch organization-level activity RSS feed.
|
|
37
|
-
*/
|
|
38
93
|
private async fetchOrgRssFeed(optionsArg: {
|
|
39
94
|
orgName: string,
|
|
40
95
|
repoName?: string,
|
|
41
96
|
}): Promise<any[]> {
|
|
42
|
-
let rssUrl: string
|
|
97
|
+
let rssUrl: string;
|
|
43
98
|
if (optionsArg.orgName && !optionsArg.repoName) {
|
|
44
99
|
rssUrl = `${this.baseUrl}/${optionsArg.orgName}.atom`;
|
|
45
100
|
} else if (optionsArg.orgName && optionsArg.repoName) {
|
|
46
101
|
rssUrl = `${this.baseUrl}/${optionsArg.orgName}/${optionsArg.repoName}.atom`;
|
|
102
|
+
} else {
|
|
103
|
+
throw new Error('Invalid arguments provided to fetchOrgRssFeed.');
|
|
47
104
|
}
|
|
48
105
|
|
|
49
106
|
const response = await fetch(rssUrl);
|
|
@@ -52,36 +109,25 @@ export class CodeFeed {
|
|
|
52
109
|
}
|
|
53
110
|
|
|
54
111
|
const rssText = await response.text();
|
|
55
|
-
|
|
56
|
-
// Parse the Atom feed using fast-xml-parser
|
|
57
112
|
const rssData = this.smartxmlInstance.parseXmlToObject(rssText);
|
|
58
|
-
|
|
59
|
-
// Return the <entry> elements from the feed
|
|
60
113
|
return rssData.feed.entry || [];
|
|
61
114
|
}
|
|
62
115
|
|
|
63
|
-
/**
|
|
64
|
-
* Check if the organization's RSS feed has any new activities since the last run.
|
|
65
|
-
*/
|
|
66
116
|
private async hasNewActivity(optionsArg: {
|
|
67
117
|
orgName: string,
|
|
68
118
|
repoName?: string,
|
|
69
119
|
}): Promise<boolean> {
|
|
70
120
|
const entries = await this.fetchOrgRssFeed(optionsArg);
|
|
71
121
|
|
|
72
|
-
// Filter entries to find new activities since the last run
|
|
73
122
|
return entries.some((entry: any) => {
|
|
74
123
|
const updated = new Date(entry.updated);
|
|
75
124
|
return updated > new Date(this.lastRunTimestamp);
|
|
76
125
|
});
|
|
77
126
|
}
|
|
78
127
|
|
|
79
|
-
|
|
80
|
-
* Fetch all repositories accessible to the token/user.
|
|
81
|
-
*/
|
|
82
|
-
private async fetchAllRepositories(): Promise<plugins.interfaces.Repository[]> {
|
|
128
|
+
private async fetchAllRepositories(): Promise<plugins.interfaces.IRepository[]> {
|
|
83
129
|
let page = 1;
|
|
84
|
-
const allRepos: plugins.interfaces.
|
|
130
|
+
const allRepos: plugins.interfaces.IRepository[] = [];
|
|
85
131
|
|
|
86
132
|
while (true) {
|
|
87
133
|
const url = new URL(`${this.baseUrl}/api/v1/repos/search`);
|
|
@@ -96,7 +142,7 @@ export class CodeFeed {
|
|
|
96
142
|
throw new Error(`Failed to fetch repositories: ${resp.statusText}`);
|
|
97
143
|
}
|
|
98
144
|
|
|
99
|
-
const data: plugins.interfaces.
|
|
145
|
+
const data: plugins.interfaces.IRepoSearchResponse = await resp.json();
|
|
100
146
|
allRepos.push(...data.data);
|
|
101
147
|
|
|
102
148
|
if (data.data.length < 50) {
|
|
@@ -108,12 +154,9 @@ export class CodeFeed {
|
|
|
108
154
|
return allRepos;
|
|
109
155
|
}
|
|
110
156
|
|
|
111
|
-
/**
|
|
112
|
-
* Fetch all tags for a given repository.
|
|
113
|
-
*/
|
|
114
157
|
private async fetchTags(owner: string, repo: string): Promise<Set<string>> {
|
|
115
158
|
let page = 1;
|
|
116
|
-
const tags: plugins.interfaces.
|
|
159
|
+
const tags: plugins.interfaces.ITag[] = [];
|
|
117
160
|
|
|
118
161
|
while (true) {
|
|
119
162
|
const url = new URL(`${this.baseUrl}/api/v1/repos/${owner}/${repo}/tags`);
|
|
@@ -129,7 +172,7 @@ export class CodeFeed {
|
|
|
129
172
|
throw new Error(`Failed to fetch tags for ${owner}/${repo}: ${resp.statusText}`);
|
|
130
173
|
}
|
|
131
174
|
|
|
132
|
-
const data: plugins.interfaces.
|
|
175
|
+
const data: plugins.interfaces.ITag[] = await resp.json();
|
|
133
176
|
tags.push(...data);
|
|
134
177
|
|
|
135
178
|
if (data.length < 50) {
|
|
@@ -148,13 +191,10 @@ export class CodeFeed {
|
|
|
148
191
|
return taggedCommitShas;
|
|
149
192
|
}
|
|
150
193
|
|
|
151
|
-
|
|
152
|
-
* Fetch commits from the last 24 hours for a repository.
|
|
153
|
-
*/
|
|
154
|
-
private async fetchRecentCommitsForRepo(owner: string, repo: string): Promise<plugins.interfaces.Commit[]> {
|
|
194
|
+
private async fetchRecentCommitsForRepo(owner: string, repo: string): Promise<plugins.interfaces.ICommit[]> {
|
|
155
195
|
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
156
196
|
let page = 1;
|
|
157
|
-
const recentCommits: plugins.interfaces.
|
|
197
|
+
const recentCommits: plugins.interfaces.ICommit[] = [];
|
|
158
198
|
|
|
159
199
|
while (true) {
|
|
160
200
|
const url = new URL(`${this.baseUrl}/api/v1/repos/${owner}/${repo}/commits`);
|
|
@@ -170,7 +210,7 @@ export class CodeFeed {
|
|
|
170
210
|
throw new Error(`Failed to fetch commits for ${owner}/${repo}: ${resp.statusText}`);
|
|
171
211
|
}
|
|
172
212
|
|
|
173
|
-
const data: plugins.interfaces.
|
|
213
|
+
const data: plugins.interfaces.ICommit[] = await resp.json();
|
|
174
214
|
if (data.length === 0) {
|
|
175
215
|
break;
|
|
176
216
|
}
|
|
@@ -190,13 +230,10 @@ export class CodeFeed {
|
|
|
190
230
|
return recentCommits;
|
|
191
231
|
}
|
|
192
232
|
|
|
193
|
-
|
|
194
|
-
* Fetch all commits by querying all organizations.
|
|
195
|
-
*/
|
|
196
|
-
public async fetchAllCommitsFromInstance(): Promise<plugins.interfaces.CommitResult[]> {
|
|
233
|
+
public async fetchAllCommitsFromInstance(): Promise<plugins.interfaces.ICommitResult[]> {
|
|
197
234
|
const orgs = await this.fetchAllOrganizations();
|
|
198
235
|
console.log(`Found ${orgs.length} organizations`);
|
|
199
|
-
let allCommits: plugins.interfaces.
|
|
236
|
+
let allCommits: plugins.interfaces.ICommitResult[] = [];
|
|
200
237
|
|
|
201
238
|
for (const orgName of orgs) {
|
|
202
239
|
console.log(`Checking activity for organization: ${orgName}`);
|
|
@@ -209,7 +246,7 @@ export class CodeFeed {
|
|
|
209
246
|
console.log(`No new activity for organization: ${orgName}`);
|
|
210
247
|
continue;
|
|
211
248
|
}
|
|
212
|
-
} catch (error) {
|
|
249
|
+
} catch (error: any) {
|
|
213
250
|
console.error(`Error fetching activity for organization ${orgName}:`, error.message);
|
|
214
251
|
continue;
|
|
215
252
|
}
|
|
@@ -227,10 +264,11 @@ export class CodeFeed {
|
|
|
227
264
|
console.log(`No new activity for repository: ${orgName}/${r.name}`);
|
|
228
265
|
continue;
|
|
229
266
|
}
|
|
230
|
-
} catch (error) {
|
|
267
|
+
} catch (error: any) {
|
|
231
268
|
console.error(`Error fetching activity for repository ${orgName}/${r.name}:`, error.message);
|
|
232
269
|
continue;
|
|
233
270
|
}
|
|
271
|
+
|
|
234
272
|
const org = r.owner.login;
|
|
235
273
|
const repo = r.name;
|
|
236
274
|
console.log(`Processing repository ${org}/${repo}`);
|
|
@@ -239,8 +277,11 @@ export class CodeFeed {
|
|
|
239
277
|
const taggedCommitShas = await this.fetchTags(org, repo);
|
|
240
278
|
const commits = await this.fetchRecentCommitsForRepo(org, repo);
|
|
241
279
|
|
|
280
|
+
// Load the changelog from this repo.
|
|
281
|
+
await this.loadChangelogFromRepo(org, repo);
|
|
282
|
+
|
|
242
283
|
const commitResults = commits.map((c) => {
|
|
243
|
-
const commit: plugins.interfaces.
|
|
284
|
+
const commit: plugins.interfaces.ICommitResult = {
|
|
244
285
|
baseUrl: this.baseUrl,
|
|
245
286
|
org,
|
|
246
287
|
repo,
|
|
@@ -250,45 +291,55 @@ export class CodeFeed {
|
|
|
250
291
|
commitMessage: c.commit.message,
|
|
251
292
|
tagged: taggedCommitShas.has(c.sha),
|
|
252
293
|
publishedOnNpm: false,
|
|
253
|
-
|
|
294
|
+
changelog: undefined
|
|
295
|
+
};
|
|
254
296
|
return commit;
|
|
255
297
|
});
|
|
256
298
|
|
|
257
299
|
if (commitResults.length > 0) {
|
|
258
300
|
try {
|
|
259
301
|
const packageInfo = await this.npmRegistry.getPackageInfo(`@${org}/${repo}`);
|
|
260
|
-
for (const
|
|
302
|
+
for (const commitResult of commitResults.filter((c) => c.tagged)) {
|
|
303
|
+
const versionCandidate = commitResult.commitMessage.replace('\n', '').trim();
|
|
261
304
|
const correspondingVersion = packageInfo.allVersions.find((versionArg) => {
|
|
262
|
-
return versionArg.version ===
|
|
305
|
+
return versionArg.version === versionCandidate;
|
|
263
306
|
});
|
|
264
307
|
if (correspondingVersion) {
|
|
265
|
-
|
|
308
|
+
commitResult.publishedOnNpm = true;
|
|
309
|
+
const changelogEntry = this.getChangelogForVersion(versionCandidate);
|
|
310
|
+
if (changelogEntry) {
|
|
311
|
+
commitResult.changelog = changelogEntry;
|
|
312
|
+
}
|
|
266
313
|
}
|
|
267
314
|
}
|
|
268
|
-
} catch (error) {
|
|
315
|
+
} catch (error: any) {
|
|
269
316
|
console.error(`Failed to fetch package info for ${org}/${repo}:`, error.message);
|
|
270
317
|
}
|
|
271
318
|
}
|
|
272
319
|
|
|
273
320
|
allCommits.push(...commitResults);
|
|
274
|
-
} catch (error) {
|
|
321
|
+
} catch (error: any) {
|
|
275
322
|
console.error(`Skipping repository ${org}/${repo} due to error:`, error.message);
|
|
276
323
|
}
|
|
277
324
|
}
|
|
278
325
|
}
|
|
279
326
|
|
|
280
327
|
console.log(`Processed ${allCommits.length} commits in total.`);
|
|
328
|
+
|
|
329
|
+
allCommits = allCommits.filter(commitArg => commitArg.tagged);
|
|
330
|
+
|
|
331
|
+
console.log(`Filtered to ${allCommits.length} commits with tagged statuses.`);
|
|
332
|
+
|
|
281
333
|
for (const c of allCommits) {
|
|
282
334
|
console.log(` ==========================================================================
|
|
283
335
|
${c.prettyAgoTime} ago:
|
|
284
336
|
${c.org}/${c.repo}
|
|
285
337
|
${c.commitMessage}
|
|
286
338
|
Published on npm: ${c.publishedOnNpm}
|
|
339
|
+
${c.changelog ? `Changelog:\n${c.changelog}\n` : ''}
|
|
287
340
|
`);
|
|
288
341
|
}
|
|
289
342
|
|
|
290
|
-
allCommits = allCommits.filter(commitArg => commitArg.tagged);
|
|
291
|
-
|
|
292
343
|
return allCommits;
|
|
293
344
|
}
|
|
294
345
|
}
|
package/ts/interfaces/index.ts
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface IRepositoryOwner {
|
|
2
2
|
login: string;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
export interface
|
|
6
|
-
owner:
|
|
5
|
+
export interface IRepository {
|
|
6
|
+
owner: IRepositoryOwner;
|
|
7
7
|
name: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export interface
|
|
10
|
+
export interface ICommitAuthor {
|
|
11
11
|
date: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export interface
|
|
14
|
+
export interface ICommitDetail {
|
|
15
15
|
message: string;
|
|
16
|
-
author:
|
|
16
|
+
author: ICommitAuthor;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export interface
|
|
19
|
+
export interface ICommit {
|
|
20
20
|
sha: string;
|
|
21
|
-
commit:
|
|
21
|
+
commit: ICommitDetail;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export interface
|
|
24
|
+
export interface ITag {
|
|
25
25
|
commit?: {
|
|
26
26
|
sha?: string;
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export interface
|
|
31
|
-
data:
|
|
30
|
+
export interface IRepoSearchResponse {
|
|
31
|
+
data: IRepository[];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export interface
|
|
34
|
+
export interface ICommitResult {
|
|
35
35
|
baseUrl: string;
|
|
36
36
|
org: string;
|
|
37
37
|
repo: string;
|
|
@@ -41,4 +41,5 @@ export interface CommitResult {
|
|
|
41
41
|
tagged: boolean;
|
|
42
42
|
publishedOnNpm: boolean;
|
|
43
43
|
prettyAgoTime: string;
|
|
44
|
+
changelog: string | undefined;
|
|
44
45
|
}
|