6 Commits

Author SHA1 Message Date
a43114ab61 1.6.4
Some checks failed
Default (tags) / security (push) Successful in 55s
Default (tags) / test (push) Successful in 2m16s
Default (tags) / release (push) Failing after 1m33s
Default (tags) / metadata (push) Successful in 1m58s
2024-12-14 22:53:42 +01:00
1e0ccec03e fix(core): Refactor fetch logic to use a unified fetchFunction for API calls 2024-12-14 22:53:42 +01:00
e5e0ceee78 1.6.3
Some checks failed
Default (tags) / security (push) Successful in 52s
Default (tags) / test (push) Successful in 2m11s
Default (tags) / release (push) Failing after 1m31s
Default (tags) / metadata (push) Successful in 1m58s
2024-12-14 02:28:25 +01:00
d9ab609039 fix(codefeed): Refactor and fix formatting issues in the CodeFeed module 2024-12-14 02:28:25 +01:00
aa039e8b5e 1.6.2
Some checks failed
Default (tags) / security (push) Successful in 57s
Default (tags) / test (push) Successful in 2m13s
Default (tags) / release (push) Failing after 1m31s
Default (tags) / metadata (push) Successful in 1m53s
2024-12-14 02:04:10 +01:00
f511ab7a63 fix(core): Fix sorting order of tagged commits by timestamp 2024-12-14 02:04:10 +01:00
4 changed files with 87 additions and 44 deletions

View File

@ -1,5 +1,22 @@
# Changelog # Changelog
## 2024-12-14 - 1.6.4 - fix(core)
Refactor fetch logic to use a unified fetchFunction for API calls
- Consolidated API request logic in the CodeFeed class to use fetchFunction for improved maintainability.
## 2024-12-14 - 1.6.3 - fix(codefeed)
Refactor and fix formatting issues in the CodeFeed module
- Refactored various method format and spacing.
- Fixed error handling formatting for readability.
- Improved consistency in JSON handling for API responses.
## 2024-12-14 - 1.6.2 - fix(core)
Fix sorting order of tagged commits by timestamp
- Fixed the sorting order of commits to be by timestamp in descending order after filtering for tagged commits.
## 2024-12-14 - 1.6.1 - fix(docs) ## 2024-12-14 - 1.6.1 - fix(docs)
Updated project metadata and expanded documentation for installation and usage. Updated project metadata and expanded documentation for installation and usage.

View File

@ -1,6 +1,6 @@
{ {
"name": "@foss.global/codefeed", "name": "@foss.global/codefeed",
"version": "1.6.1", "version": "1.6.4",
"private": false, "private": false,
"description": "The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.", "description": "The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.",
"exports": { "exports": {

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@foss.global/codefeed', name: '@foss.global/codefeed',
version: '1.6.1', version: '1.6.4',
description: 'The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.' description: 'The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.'
} }

View File

@ -11,7 +11,8 @@ export class CodeFeed {
constructor(baseUrl: string, token?: string, lastRunTimestamp?: string) { constructor(baseUrl: string, token?: string, lastRunTimestamp?: string) {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
this.token = token; this.token = token;
this.lastRunTimestamp = lastRunTimestamp || new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); this.lastRunTimestamp =
lastRunTimestamp || new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
console.log('CodeFeed initialized with last run timestamp:', this.lastRunTimestamp); console.log('CodeFeed initialized with last run timestamp:', this.lastRunTimestamp);
} }
@ -19,16 +20,18 @@ export class CodeFeed {
* Load the changelog directly from the Gitea repository. * Load the changelog directly from the Gitea repository.
*/ */
private async loadChangelogFromRepo(owner: string, repo: string): Promise<void> { private async loadChangelogFromRepo(owner: string, repo: string): Promise<void> {
const url = `${this.baseUrl}/api/v1/repos/${owner}/${repo}/contents/changelog.md`; const url = `/api/v1/repos/${owner}/${repo}/contents/changelog.md`;
const headers: Record<string, string> = {}; const headers: Record<string, string> = {};
if (this.token) { if (this.token) {
headers['Authorization'] = `token ${this.token}`; headers['Authorization'] = `token ${this.token}`;
} }
const response = await fetch(url, { headers }); const response = await this.fetchFunction(url, { headers });
if (!response.ok) { if (!response.ok) {
console.error(`Could not fetch CHANGELOG.md from ${owner}/${repo}: ${response.status} ${response.statusText}`); console.error(
`Could not fetch CHANGELOG.md from ${owner}/${repo}: ${response.status} ${response.statusText}`
);
this.changelogContent = ''; this.changelogContent = '';
return; return;
} }
@ -77,8 +80,8 @@ export class CodeFeed {
} }
private async fetchAllOrganizations(): Promise<string[]> { private async fetchAllOrganizations(): Promise<string[]> {
const url = `${this.baseUrl}/api/v1/orgs`; const url = `/api/v1/orgs`;
const response = await fetch(url, { const response = await this.fetchFunction(url, {
headers: this.token ? { Authorization: `token ${this.token}` } : {}, headers: this.token ? { Authorization: `token ${this.token}` } : {},
}); });
@ -91,21 +94,23 @@ export class CodeFeed {
} }
private async fetchOrgRssFeed(optionsArg: { private async fetchOrgRssFeed(optionsArg: {
orgName: string, orgName: string;
repoName?: string, repoName?: string;
}): Promise<any[]> { }): Promise<any[]> {
let rssUrl: string; let rssUrl: string;
if (optionsArg.orgName && !optionsArg.repoName) { if (optionsArg.orgName && !optionsArg.repoName) {
rssUrl = `${this.baseUrl}/${optionsArg.orgName}.atom`; rssUrl = `/${optionsArg.orgName}.atom`;
} else if (optionsArg.orgName && optionsArg.repoName) { } else if (optionsArg.orgName && optionsArg.repoName) {
rssUrl = `${this.baseUrl}/${optionsArg.orgName}/${optionsArg.repoName}.atom`; rssUrl = `/${optionsArg.orgName}/${optionsArg.repoName}.atom`;
} else { } else {
throw new Error('Invalid arguments provided to fetchOrgRssFeed.'); throw new Error('Invalid arguments provided to fetchOrgRssFeed.');
} }
const response = await fetch(rssUrl); const response = await this.fetchFunction(rssUrl, {});
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to fetch RSS feed for organization ${optionsArg.orgName}/${optionsArg.repoName}: ${response.statusText}`); throw new Error(
`Failed to fetch RSS feed for organization ${optionsArg.orgName}/${optionsArg.repoName}: ${response.statusText}`
);
} }
const rssText = await response.text(); const rssText = await response.text();
@ -114,8 +119,8 @@ export class CodeFeed {
} }
private async hasNewActivity(optionsArg: { private async hasNewActivity(optionsArg: {
orgName: string, orgName: string;
repoName?: string, repoName?: string;
}): Promise<boolean> { }): Promise<boolean> {
const entries = await this.fetchOrgRssFeed(optionsArg); const entries = await this.fetchOrgRssFeed(optionsArg);
@ -130,12 +135,10 @@ export class CodeFeed {
const allRepos: plugins.interfaces.IRepository[] = []; const allRepos: plugins.interfaces.IRepository[] = [];
while (true) { while (true) {
const url = new URL(`${this.baseUrl}/api/v1/repos/search`); const url = `/api/v1/repos/search?limit=50&page=${page.toString()}`;
url.searchParams.set('limit', '50');
url.searchParams.set('page', page.toString());
const resp = await fetch(url.href, { const resp = await this.fetchFunction(url, {
headers: this.token ? { 'Authorization': `token ${this.token}` } : {}, headers: this.token ? { Authorization: `token ${this.token}` } : {},
}); });
if (!resp.ok) { if (!resp.ok) {
@ -159,16 +162,16 @@ export class CodeFeed {
const tags: plugins.interfaces.ITag[] = []; const tags: plugins.interfaces.ITag[] = [];
while (true) { while (true) {
const url = new URL(`${this.baseUrl}/api/v1/repos/${owner}/${repo}/tags`); const url = `/api/v1/repos/${owner}/${repo}/tags?limit=50&page=${page.toString()}`;
url.searchParams.set('limit', '50');
url.searchParams.set('page', page.toString());
const resp = await fetch(url.href, { const resp = await this.fetchFunction(url, {
headers: this.token ? { 'Authorization': `token ${this.token}` } : {}, headers: this.token ? { Authorization: `token ${this.token}` } : {},
}); });
if (!resp.ok) { if (!resp.ok) {
console.error(`Failed to fetch tags for ${owner}/${repo}: ${resp.status} ${resp.statusText} at ${url.href}`); console.error(
`Failed to fetch tags for ${owner}/${repo}: ${resp.status} ${resp.statusText} at ${url}`
);
throw new Error(`Failed to fetch tags for ${owner}/${repo}: ${resp.statusText}`); throw new Error(`Failed to fetch tags for ${owner}/${repo}: ${resp.statusText}`);
} }
@ -191,22 +194,25 @@ export class CodeFeed {
return taggedCommitShas; return taggedCommitShas;
} }
private async fetchRecentCommitsForRepo(owner: string, repo: string): Promise<plugins.interfaces.ICommit[]> { private async fetchRecentCommitsForRepo(
owner: string,
repo: string
): Promise<plugins.interfaces.ICommit[]> {
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
let page = 1; let page = 1;
const recentCommits: plugins.interfaces.ICommit[] = []; const recentCommits: plugins.interfaces.ICommit[] = [];
while (true) { while (true) {
const url = new URL(`${this.baseUrl}/api/v1/repos/${owner}/${repo}/commits`); const url = `/api/v1/repos/${owner}/${repo}/commits?limit=50&page=${page.toString()}`;
url.searchParams.set('limit', '50');
url.searchParams.set('page', page.toString());
const resp = await fetch(url.href, { const resp = await this.fetchFunction(url, {
headers: this.token ? { 'Authorization': `token ${this.token}` } : {}, headers: this.token ? { Authorization: `token ${this.token}` } : {},
}); });
if (!resp.ok) { if (!resp.ok) {
console.error(`Failed to fetch commits for ${owner}/${repo}: ${resp.status} ${resp.statusText} at ${url.href}`); console.error(
`Failed to fetch commits for ${owner}/${repo}: ${resp.status} ${resp.statusText} at ${url}`
);
throw new Error(`Failed to fetch commits for ${owner}/${repo}: ${resp.statusText}`); throw new Error(`Failed to fetch commits for ${owner}/${repo}: ${resp.statusText}`);
} }
@ -265,7 +271,10 @@ export class CodeFeed {
continue; continue;
} }
} catch (error: any) { } catch (error: any) {
console.error(`Error fetching activity for repository ${orgName}/${r.name}:`, error.message); console.error(
`Error fetching activity for repository ${orgName}/${r.name}:`,
error.message
);
continue; continue;
} }
@ -286,12 +295,14 @@ export class CodeFeed {
org, org,
repo, repo,
timestamp: c.commit.author.date, timestamp: c.commit.author.date,
prettyAgoTime: plugins.smarttime.getMilliSecondsAsHumanReadableAgoTime(new Date(c.commit.author.date).getTime()), prettyAgoTime: plugins.smarttime.getMilliSecondsAsHumanReadableAgoTime(
new Date(c.commit.author.date).getTime()
),
hash: c.sha, hash: c.sha,
commitMessage: c.commit.message, commitMessage: c.commit.message,
tagged: taggedCommitShas.has(c.sha), tagged: taggedCommitShas.has(c.sha),
publishedOnNpm: false, publishedOnNpm: false,
changelog: undefined changelog: undefined,
}; };
return commit; return commit;
}); });
@ -306,15 +317,23 @@ export class CodeFeed {
}); });
if (correspondingVersion) { if (correspondingVersion) {
commitResult.publishedOnNpm = true; commitResult.publishedOnNpm = true;
const changelogEntry = this.getChangelogForVersion(versionCandidate);
if (changelogEntry) {
commitResult.changelog = changelogEntry;
}
} }
} }
} catch (error: any) { } catch (error: any) {
console.error(`Failed to fetch package info for ${org}/${repo}:`, error.message); console.error(`Failed to fetch package info for ${org}/${repo}:`, error.message);
} }
try {
for (const commitResult of commitResults.filter((c) => c.tagged)) {
const versionCandidate = commitResult.commitMessage.replace('\n', '').trim();
const changelogEntry = this.getChangelogForVersion(versionCandidate);
if (changelogEntry) {
commitResult.changelog = changelogEntry;
}
}
} catch (error: any) {
console.error(`Failed to fetch changelog info for ${org}/${repo}:`, error.message);
}
} }
allCommits.push(...commitResults); allCommits.push(...commitResults);
@ -326,7 +345,9 @@ export class CodeFeed {
console.log(`Processed ${allCommits.length} commits in total.`); console.log(`Processed ${allCommits.length} commits in total.`);
allCommits = allCommits.filter(commitArg => commitArg.tagged); allCommits = allCommits
.filter((commitArg) => commitArg.tagged)
.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
console.log(`Filtered to ${allCommits.length} commits with tagged statuses.`); console.log(`Filtered to ${allCommits.length} commits with tagged statuses.`);
@ -337,9 +358,14 @@ export class CodeFeed {
${c.commitMessage} ${c.commitMessage}
Published on npm: ${c.publishedOnNpm} Published on npm: ${c.publishedOnNpm}
${c.changelog ? `Changelog:\n${c.changelog}\n` : ''} ${c.changelog ? `Changelog:\n${c.changelog}\n` : ''}
`); `);
} }
return allCommits; return allCommits;
} }
}
public async fetchFunction(urlArg: string, optionsArg: RequestInit): Promise<Response> {
const response = await fetch(`${this.baseUrl}${urlArg}`, optionsArg);
return response;
}
}