20 Commits

Author SHA1 Message Date
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
1df8064247 1.6.1
Some checks failed
Default (tags) / security (push) Successful in 54s
Default (tags) / test (push) Successful in 2m8s
Default (tags) / release (push) Failing after 1m28s
Default (tags) / metadata (push) Successful in 1m50s
2024-12-14 01:32:22 +01:00
ac1f398422 fix(docs): Updated project metadata and expanded documentation for installation and usage. 2024-12-14 01:32:22 +01:00
3a498c00ee 1.6.0
Some checks failed
Default (tags) / security (push) Successful in 54s
Default (tags) / test (push) Successful in 2m9s
Default (tags) / release (push) Failing after 1m29s
Default (tags) / metadata (push) Successful in 1m54s
2024-12-14 00:54:38 +01:00
bb248ed408 feat(core): Add changelog fetching and parsing functionality 2024-12-14 00:54:38 +01:00
e843197211 1.5.3
Some checks failed
Default (tags) / security (push) Successful in 47s
Default (tags) / test (push) Successful in 2m17s
Default (tags) / release (push) Failing after 1m31s
Default (tags) / metadata (push) Successful in 1m54s
2024-12-14 00:47:24 +01:00
3502a661ea fix(core): Fix filtering logic for returning only tagged commits 2024-12-14 00:47:24 +01:00
d103778a75 1.5.2
Some checks failed
Default (tags) / security (push) Successful in 43s
Default (tags) / test (push) Successful in 2m19s
Default (tags) / release (push) Failing after 1m32s
Default (tags) / metadata (push) Successful in 1m58s
2024-12-14 00:33:58 +01:00
9b1b91eb31 fix(core): Ensure stability of core functionalities. 2024-12-14 00:33:58 +01:00
25b2519324 1.5.1
Some checks failed
Default (tags) / security (push) Successful in 39s
Default (tags) / test (push) Successful in 2m22s
Default (tags) / release (push) Failing after 1m42s
Default (tags) / metadata (push) Successful in 2m6s
2024-12-14 00:32:34 +01:00
166b289eb2 fix(core): Refine logging format in CodeFeed class 2024-12-14 00:32:34 +01:00
6ca6b37b1d 1.5.0
Some checks failed
Default (tags) / security (push) Successful in 53s
Default (tags) / test (push) Successful in 2m13s
Default (tags) / release (push) Failing after 1m50s
Default (tags) / metadata (push) Successful in 2m7s
2024-12-14 00:30:35 +01:00
5d0d125e43 feat(core): Refactor TypeScript interfaces and improve module exports 2024-12-14 00:30:35 +01:00
470f4fe730 1.4.1
Some checks failed
Default (tags) / security (push) Successful in 40s
Default (tags) / test (push) Successful in 2m18s
Default (tags) / release (push) Failing after 1m37s
Default (tags) / metadata (push) Successful in 1m59s
2024-12-13 22:16:48 +01:00
daeb38c91c fix(core): Corrected log formatting for commit information output in CodeFeed 2024-12-13 22:16:47 +01:00
9b46b0d46e 1.4.0
Some checks failed
Default (tags) / security (push) Successful in 54s
Default (tags) / test (push) Successful in 2m18s
Default (tags) / release (push) Failing after 1m40s
Default (tags) / metadata (push) Successful in 2m5s
2024-12-13 22:15:34 +01:00
46bd0a2486 feat(CodeFeed): Enhance commit results with human-readable time 2024-12-13 22:15:33 +01:00
10 changed files with 512 additions and 181 deletions

View File

@ -1,5 +1,63 @@
# Changelog # Changelog
## 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)
Updated project metadata and expanded documentation for installation and usage.
- Updated description and keywords in package.json and npmextra.json.
- Significant expansion of the README.md with detailed installation, usage, and feature instructions.
## 2024-12-14 - 1.6.0 - feat(core)
Add changelog fetching and parsing functionality
- Implemented loadChangelogFromRepo to directly load the changelog from a Gitea repository.
- Introduced parsing functionality to extract specific version details from the loaded changelog.
- Updated CodeFeed class to utilize the changelog for version verification and commit processing.
## 2024-12-14 - 1.5.3 - fix(core)
Fix filtering logic for returning only tagged commits
- Ensure `allCommits` is filtered to only include commits with 'tagged' status before returning.
## 2024-12-14 - 1.5.2 - fix(core)
Ensure stability of core functionalities.
## 2024-12-14 - 1.5.1 - fix(core)
Refine logging format in CodeFeed class
- Modified console log format in fetchAllCommitsFromInstance method for better readability.
## 2024-12-14 - 1.5.0 - feat(core)
Refactor TypeScript interfaces and improve module exports
- Moved TypeScript interfaces to a dedicated file (ts/interfaces/index.ts).
- Updated import/export structure to improve code readability and maintainability.
- Enhanced the package.json to utilize a module exports field for better resolution.
## 2024-12-13 - 1.4.1 - fix(core)
Corrected log formatting for commit information output in CodeFeed
- Fixed formatting issue in commit log output within the CodeFeed class to ensure proper display of timestamps.
## 2024-12-13 - 1.4.0 - feat(CodeFeed)
Enhance commit results with human-readable time
- Integrated smarttime plugin to calculate and format timestamps into human-readable time.
- Updated dependencies in package.json to include smarttime and adjusted versions for existing packages.
- Improved fetchAllCommitsFromInstance method to display formatted time ago information for each commit.
## 2024-12-13 - 1.3.0 - feat(core) ## 2024-12-13 - 1.3.0 - feat(core)
Export CommitResult interface for external use. Export CommitResult interface for external use.

View File

@ -5,10 +5,23 @@
"githost": "code.foss.global", "githost": "code.foss.global",
"gitscope": "foss.global", "gitscope": "foss.global",
"gitrepo": "codefeed", "gitrepo": "codefeed",
"description": "a module for creating feeds for code development", "description": "The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.",
"npmPackagename": "@foss.global/codefeed", "npmPackagename": "@foss.global/codefeed",
"license": "MIT", "license": "MIT",
"projectDomain": "foss.global" "projectDomain": "foss.global",
"keywords": [
"codefeed",
"Gitea",
"commits",
"changelog",
"repository",
"development tools",
"npm",
"module",
"code analysis",
"activity feed",
"version control"
]
} }
}, },
"npmci": { "npmci": {

View File

@ -1,16 +1,18 @@
{ {
"name": "@foss.global/codefeed", "name": "@foss.global/codefeed",
"version": "1.3.0", "version": "1.6.3",
"private": false, "private": false,
"description": "a module for creating feeds for code development", "description": "The @foss.global/codefeed module is designed for generating feeds from Gitea repositories, enhancing development workflows by processing commit data and repository activities.",
"main": "dist_ts/index.js", "exports": {
"typings": "dist_ts/index.d.ts", ".": "./dist_ts/index.js",
"./interfaces": "./dist_ts/interfaces/index.js"
},
"type": "module", "type": "module",
"author": "Task Venture Capital GmbH", "author": "Task Venture Capital GmbH",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"test": "(tstest test/ --web)", "test": "(tstest test/ --web)",
"build": "(tsbuild --web --allowimplicitany)", "build": "(tsbuild tsfolders --web --allowimplicitany)",
"buildDocs": "(tsdoc)" "buildDocs": "(tsdoc)"
}, },
"devDependencies": { "devDependencies": {
@ -19,11 +21,12 @@
"@git.zone/tsrun": "^1.2.46", "@git.zone/tsrun": "^1.2.46",
"@git.zone/tstest": "^1.0.44", "@git.zone/tstest": "^1.0.44",
"@push.rocks/tapbundle": "^5.0.15", "@push.rocks/tapbundle": "^5.0.15",
"@types/node": "^20.8.7" "@types/node": "^22.10.2"
}, },
"dependencies": { "dependencies": {
"@push.rocks/qenv": "^6.1.0", "@push.rocks/qenv": "^6.1.0",
"@push.rocks/smartnpm": "^2.0.4", "@push.rocks/smartnpm": "^2.0.4",
"@push.rocks/smarttime": "^4.1.1",
"@push.rocks/smartxml": "^1.0.8" "@push.rocks/smartxml": "^1.0.8"
}, },
"repository": { "repository": {
@ -45,5 +48,18 @@
"cli.js", "cli.js",
"npmextra.json", "npmextra.json",
"readme.md" "readme.md"
],
"keywords": [
"codefeed",
"Gitea",
"commits",
"changelog",
"repository",
"development tools",
"npm",
"module",
"code analysis",
"activity feed",
"version control"
] ]
} }

133
pnpm-lock.yaml generated
View File

@ -14,6 +14,9 @@ importers:
'@push.rocks/smartnpm': '@push.rocks/smartnpm':
specifier: ^2.0.4 specifier: ^2.0.4
version: 2.0.4 version: 2.0.4
'@push.rocks/smarttime':
specifier: ^4.1.1
version: 4.1.1
'@push.rocks/smartxml': '@push.rocks/smartxml':
specifier: ^1.0.8 specifier: ^1.0.8
version: 1.0.8 version: 1.0.8
@ -34,8 +37,8 @@ importers:
specifier: ^5.0.15 specifier: ^5.0.15
version: 5.5.3(@aws-sdk/client-sso-oidc@3.709.0(@aws-sdk/client-sts@3.709.0))(@aws-sdk/credential-providers@3.709.0(@aws-sdk/client-sso-oidc@3.709.0(@aws-sdk/client-sts@3.709.0)))(socks@2.8.3) version: 5.5.3(@aws-sdk/client-sso-oidc@3.709.0(@aws-sdk/client-sts@3.709.0))(@aws-sdk/credential-providers@3.709.0(@aws-sdk/client-sso-oidc@3.709.0(@aws-sdk/client-sts@3.709.0)))(socks@2.8.3)
'@types/node': '@types/node':
specifier: ^20.8.7 specifier: ^22.10.2
version: 20.17.10 version: 22.10.2
packages: packages:
@ -842,8 +845,8 @@ packages:
'@push.rocks/smartstring@4.0.15': '@push.rocks/smartstring@4.0.15':
resolution: {integrity: sha512-NTNeOjWyg+aHtBTiQEyXamr7oTvYZ3wS1fudHo9ua7CLrykpK+i+RxFyJaLg1zB5x9xQF3NLEQecB14HPFX8Cg==} resolution: {integrity: sha512-NTNeOjWyg+aHtBTiQEyXamr7oTvYZ3wS1fudHo9ua7CLrykpK+i+RxFyJaLg1zB5x9xQF3NLEQecB14HPFX8Cg==}
'@push.rocks/smarttime@4.0.8': '@push.rocks/smarttime@4.1.1':
resolution: {integrity: sha512-He+1ebBowVd8rW+VHZMFmz407xVMQf/JbyKr3s1ozoIlJS1AhZpDvlkzyqLV2tNMP1/cEBeo25ImJN2x1pksBA==} resolution: {integrity: sha512-Ha/3J/G+zfTl4ahpZgF6oUOZnUjpLhrBja0OQ2cloFxF9sKT8I1COaSqIfBGDtoK2Nly4UD4aTJ3JcJNOg/kgA==}
'@push.rocks/smartunique@3.0.9': '@push.rocks/smartunique@3.0.9':
resolution: {integrity: sha512-q6DYQgT7/dqdWi9HusvtWCjdsFzLFXY9LTtaZV6IYNJt6teZOonoygxTdNt9XLn6niBSbLYrHSKvJNTRH/uK+g==} resolution: {integrity: sha512-q6DYQgT7/dqdWi9HusvtWCjdsFzLFXY9LTtaZV6IYNJt6teZOonoygxTdNt9XLn6niBSbLYrHSKvJNTRH/uK+g==}
@ -1391,8 +1394,8 @@ packages:
'@types/node-forge@1.3.11': '@types/node-forge@1.3.11':
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
'@types/node@20.17.10': '@types/node@22.10.2':
resolution: {integrity: sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==} resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==}
'@types/parse5@6.0.3': '@types/parse5@6.0.3':
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==} resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
@ -1862,9 +1865,9 @@ packages:
resolution: {integrity: sha512-9pSLe+tDJnmNak2JeMkz6ZmTCXP5p6vCxSd4kvDqrTJkqAP62j2uAEIZjf8cPDZIakStujqVzh5Y5MIWH3yYAw==} resolution: {integrity: sha512-9pSLe+tDJnmNak2JeMkz6ZmTCXP5p6vCxSd4kvDqrTJkqAP62j2uAEIZjf8cPDZIakStujqVzh5Y5MIWH3yYAw==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
croner@7.0.8: croner@9.0.0:
resolution: {integrity: sha512-4E27J9ZQV9prM9ggU18QGPYPMSblbA9JuGv4Ff3Gk6supX4RszNGQxBgiFBL6wb/L9HuSMpFbQpduMiDRo+z5Q==} resolution: {integrity: sha512-onMB0OkDjkXunhdW9htFjEhqrD54+M94i6ackoUkjHKbRnXdyEyKRelp4nJ1kAz32+s27jP1FsebpJCVl0BsvA==}
engines: {node: '>=6.0'} engines: {node: '>=18.0'}
cross-fetch@3.1.5: cross-fetch@3.1.5:
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
@ -1880,6 +1883,9 @@ packages:
resolution: {integrity: sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ==} resolution: {integrity: sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ==}
engines: {node: '>=14.16'} engines: {node: '>=14.16'}
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
dayjs@1.11.13: dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
@ -3231,6 +3237,10 @@ packages:
resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==} resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==}
engines: {node: '>=12'} engines: {node: '>=12'}
parse-ms@4.0.0:
resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
engines: {node: '>=18'}
parse5@6.0.1: parse5@6.0.1:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
@ -3322,6 +3332,10 @@ packages:
resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==} resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==}
engines: {node: '>=14.16'} engines: {node: '>=14.16'}
pretty-ms@9.2.0:
resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==}
engines: {node: '>=18'}
process-nextick-args@2.0.1: process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
@ -3841,8 +3855,8 @@ packages:
unbzip2-stream@1.4.3: unbzip2-stream@1.4.3:
resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
undici-types@6.19.8: undici-types@6.20.0:
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
unified@11.0.5: unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
@ -4110,7 +4124,7 @@ snapshots:
'@push.rocks/smartrx': 3.0.7 '@push.rocks/smartrx': 3.0.7
'@push.rocks/smartsitemap': 2.0.3 '@push.rocks/smartsitemap': 2.0.3
'@push.rocks/smartstream': 3.2.5 '@push.rocks/smartstream': 3.2.5
'@push.rocks/smarttime': 4.0.8 '@push.rocks/smarttime': 4.1.1
'@push.rocks/taskbuffer': 3.1.7 '@push.rocks/taskbuffer': 3.1.7
'@push.rocks/webrequest': 3.0.37 '@push.rocks/webrequest': 3.0.37
'@push.rocks/webstore': 2.0.20 '@push.rocks/webstore': 2.0.20
@ -5051,7 +5065,7 @@ snapshots:
'@jest/schemas': 29.6.3 '@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4 '@types/istanbul-reports': 3.0.4
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/yargs': 17.0.33 '@types/yargs': 17.0.33
chalk: 4.1.2 chalk: 4.1.2
@ -5189,7 +5203,7 @@ snapshots:
'@push.rocks/smartmatch': 2.0.0 '@push.rocks/smartmatch': 2.0.0
'@push.rocks/smartpromise': 4.0.4 '@push.rocks/smartpromise': 4.0.4
'@push.rocks/smartrx': 3.0.7 '@push.rocks/smartrx': 3.0.7
'@push.rocks/smarttime': 4.0.8 '@push.rocks/smarttime': 4.1.1
'@types/minimatch': 5.1.2 '@types/minimatch': 5.1.2
'@types/symbol-tree': 3.2.5 '@types/symbol-tree': 3.2.5
symbol-tree: 3.2.4 symbol-tree: 3.2.4
@ -5299,7 +5313,7 @@ snapshots:
'@push.rocks/smartpromise': 4.0.4 '@push.rocks/smartpromise': 4.0.4
'@push.rocks/smartrx': 3.0.7 '@push.rocks/smartrx': 3.0.7
'@push.rocks/smartstring': 4.0.15 '@push.rocks/smartstring': 4.0.15
'@push.rocks/smarttime': 4.0.8 '@push.rocks/smarttime': 4.1.1
'@push.rocks/smartunique': 3.0.9 '@push.rocks/smartunique': 3.0.9
'@push.rocks/taskbuffer': 3.1.7 '@push.rocks/taskbuffer': 3.1.7
'@tsclass/tsclass': 4.2.0 '@tsclass/tsclass': 4.2.0
@ -5492,7 +5506,7 @@ snapshots:
'@push.rocks/smartpath': 5.0.18 '@push.rocks/smartpath': 5.0.18
'@push.rocks/smartpromise': 4.0.4 '@push.rocks/smartpromise': 4.0.4
'@push.rocks/smartrequest': 2.0.23 '@push.rocks/smartrequest': 2.0.23
'@push.rocks/smarttime': 4.0.8 '@push.rocks/smarttime': 4.1.1
'@push.rocks/smartversion': 3.0.5 '@push.rocks/smartversion': 3.0.5
package-json: 8.1.1 package-json: 8.1.1
transitivePeerDependencies: transitivePeerDependencies:
@ -5615,7 +5629,7 @@ snapshots:
'@push.rocks/smartlog': 3.0.7 '@push.rocks/smartlog': 3.0.7
'@push.rocks/smartpromise': 4.0.4 '@push.rocks/smartpromise': 4.0.4
'@push.rocks/smartrx': 3.0.7 '@push.rocks/smartrx': 3.0.7
'@push.rocks/smarttime': 4.0.8 '@push.rocks/smarttime': 4.1.1
engine.io: 6.5.4 engine.io: 6.5.4
socket.io: 4.7.5 socket.io: 4.7.5
socket.io-client: 4.7.5 socket.io-client: 4.7.5
@ -5669,15 +5683,16 @@ snapshots:
strip-indent: 4.0.0 strip-indent: 4.0.0
url: 0.11.4 url: 0.11.4
'@push.rocks/smarttime@4.0.8': '@push.rocks/smarttime@4.1.1':
dependencies: dependencies:
'@push.rocks/lik': 6.1.0 '@push.rocks/lik': 6.1.0
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartpromise': 4.0.4 '@push.rocks/smartpromise': 4.0.4
croner: 7.0.8 croner: 9.0.0
date-fns: 4.1.0
dayjs: 1.11.13 dayjs: 1.11.13
is-nan: 1.3.2 is-nan: 1.3.2
pretty-ms: 8.0.0 pretty-ms: 9.2.0
'@push.rocks/smartunique@3.0.9': '@push.rocks/smartunique@3.0.9':
dependencies: dependencies:
@ -5718,7 +5733,7 @@ snapshots:
'@push.rocks/smartrequest': 2.0.23 '@push.rocks/smartrequest': 2.0.23
'@push.rocks/smarts3': 2.2.5 '@push.rocks/smarts3': 2.2.5
'@push.rocks/smartshell': 3.2.0 '@push.rocks/smartshell': 3.2.0
'@push.rocks/smarttime': 4.0.8 '@push.rocks/smarttime': 4.1.1
expect: 29.7.0 expect: 29.7.0
transitivePeerDependencies: transitivePeerDependencies:
- '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sso-oidc'
@ -5741,7 +5756,7 @@ snapshots:
'@push.rocks/smartlog': 3.0.7 '@push.rocks/smartlog': 3.0.7
'@push.rocks/smartpromise': 4.0.4 '@push.rocks/smartpromise': 4.0.4
'@push.rocks/smartrx': 3.0.7 '@push.rocks/smartrx': 3.0.7
'@push.rocks/smarttime': 4.0.8 '@push.rocks/smarttime': 4.1.1
'@push.rocks/smartunique': 3.0.9 '@push.rocks/smartunique': 3.0.9
'@push.rocks/webrequest@3.0.37': '@push.rocks/webrequest@3.0.37':
@ -6326,14 +6341,14 @@ snapshots:
'@types/accepts@1.3.7': '@types/accepts@1.3.7':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/babel__code-frame@7.0.6': {} '@types/babel__code-frame@7.0.6': {}
'@types/body-parser@1.19.5': '@types/body-parser@1.19.5':
dependencies: dependencies:
'@types/connect': 3.4.38 '@types/connect': 3.4.38
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/buffer-json@2.0.3': {} '@types/buffer-json@2.0.3': {}
@ -6349,17 +6364,17 @@ snapshots:
'@types/clean-css@4.2.11': '@types/clean-css@4.2.11':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
source-map: 0.6.1 source-map: 0.6.1
'@types/co-body@6.1.3': '@types/co-body@6.1.3':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/qs': 6.9.17 '@types/qs': 6.9.17
'@types/connect@3.4.38': '@types/connect@3.4.38':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/content-disposition@0.5.8': {} '@types/content-disposition@0.5.8': {}
@ -6372,11 +6387,11 @@ snapshots:
'@types/connect': 3.4.38 '@types/connect': 3.4.38
'@types/express': 5.0.0 '@types/express': 5.0.0
'@types/keygrip': 1.0.6 '@types/keygrip': 1.0.6
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/cors@2.8.17': '@types/cors@2.8.17':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/debounce@1.2.4': {} '@types/debounce@1.2.4': {}
@ -6390,14 +6405,14 @@ snapshots:
'@types/express-serve-static-core@4.19.6': '@types/express-serve-static-core@4.19.6':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/qs': 6.9.17 '@types/qs': 6.9.17
'@types/range-parser': 1.2.7 '@types/range-parser': 1.2.7
'@types/send': 0.17.4 '@types/send': 0.17.4
'@types/express-serve-static-core@5.0.2': '@types/express-serve-static-core@5.0.2':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/qs': 6.9.17 '@types/qs': 6.9.17
'@types/range-parser': 1.2.7 '@types/range-parser': 1.2.7
'@types/send': 0.17.4 '@types/send': 0.17.4
@ -6422,30 +6437,30 @@ snapshots:
'@types/from2@2.3.5': '@types/from2@2.3.5':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/fs-extra@11.0.4': '@types/fs-extra@11.0.4':
dependencies: dependencies:
'@types/jsonfile': 6.1.4 '@types/jsonfile': 6.1.4
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/fs-extra@9.0.13': '@types/fs-extra@9.0.13':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/glob@7.2.0': '@types/glob@7.2.0':
dependencies: dependencies:
'@types/minimatch': 5.1.2 '@types/minimatch': 5.1.2
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/glob@8.1.0': '@types/glob@8.1.0':
dependencies: dependencies:
'@types/minimatch': 5.1.2 '@types/minimatch': 5.1.2
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/gunzip-maybe@1.4.2': '@types/gunzip-maybe@1.4.2':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/hast@3.0.4': '@types/hast@3.0.4':
dependencies: dependencies:
@ -6479,7 +6494,7 @@ snapshots:
'@types/jsonfile@6.1.4': '@types/jsonfile@6.1.4':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/keygrip@1.0.6': {} '@types/keygrip@1.0.6': {}
@ -6496,7 +6511,7 @@ snapshots:
'@types/http-errors': 2.0.4 '@types/http-errors': 2.0.4
'@types/keygrip': 1.0.6 '@types/keygrip': 1.0.6
'@types/koa-compose': 3.2.8 '@types/koa-compose': 3.2.8
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/mdast@4.0.4': '@types/mdast@4.0.4':
dependencies: dependencies:
@ -6514,11 +6529,11 @@ snapshots:
'@types/node-forge@1.3.11': '@types/node-forge@1.3.11':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/node@20.17.10': '@types/node@22.10.2':
dependencies: dependencies:
undici-types: 6.19.8 undici-types: 6.20.0
'@types/parse5@6.0.3': {} '@types/parse5@6.0.3': {}
@ -6534,19 +6549,19 @@ snapshots:
'@types/s3rver@3.7.4': '@types/s3rver@3.7.4':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/semver@7.5.8': {} '@types/semver@7.5.8': {}
'@types/send@0.17.4': '@types/send@0.17.4':
dependencies: dependencies:
'@types/mime': 1.3.5 '@types/mime': 1.3.5
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/serve-static@1.15.7': '@types/serve-static@1.15.7':
dependencies: dependencies:
'@types/http-errors': 2.0.4 '@types/http-errors': 2.0.4
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/send': 0.17.4 '@types/send': 0.17.4
'@types/sinon-chai@3.2.12': '@types/sinon-chai@3.2.12':
@ -6566,11 +6581,11 @@ snapshots:
'@types/tar-stream@2.2.3': '@types/tar-stream@2.2.3':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/through2@2.0.41': '@types/through2@2.0.41':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/triple-beam@1.3.5': {} '@types/triple-beam@1.3.5': {}
@ -6594,7 +6609,7 @@ snapshots:
'@types/whatwg-url@8.2.2': '@types/whatwg-url@8.2.2':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/webidl-conversions': 7.0.3 '@types/webidl-conversions': 7.0.3
'@types/which@2.0.2': {} '@types/which@2.0.2': {}
@ -6603,11 +6618,11 @@ snapshots:
'@types/ws@7.4.7': '@types/ws@7.4.7':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/ws@8.5.13': '@types/ws@8.5.13':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
'@types/yargs-parser@21.0.3': {} '@types/yargs-parser@21.0.3': {}
@ -6617,7 +6632,7 @@ snapshots:
'@types/yauzl@2.10.3': '@types/yauzl@2.10.3':
dependencies: dependencies:
'@types/node': 20.17.10 '@types/node': 22.10.2
optional: true optional: true
'@ungap/structured-clone@1.2.1': {} '@ungap/structured-clone@1.2.1': {}
@ -7034,7 +7049,7 @@ snapshots:
croner@5.7.0: {} croner@5.7.0: {}
croner@7.0.8: {} croner@9.0.0: {}
cross-fetch@3.1.5: cross-fetch@3.1.5:
dependencies: dependencies:
@ -7057,6 +7072,8 @@ snapshots:
dependencies: dependencies:
type-fest: 2.19.0 type-fest: 2.19.0
date-fns@4.1.0: {}
dayjs@1.11.13: {} dayjs@1.11.13: {}
debounce@1.2.1: {} debounce@1.2.1: {}
@ -7200,7 +7217,7 @@ snapshots:
dependencies: dependencies:
'@types/cookie': 0.4.1 '@types/cookie': 0.4.1
'@types/cors': 2.8.17 '@types/cors': 2.8.17
'@types/node': 20.17.10 '@types/node': 22.10.2
accepts: 1.3.8 accepts: 1.3.8
base64id: 2.0.0 base64id: 2.0.0
cookie: 0.4.2 cookie: 0.4.2
@ -7892,7 +7909,7 @@ snapshots:
jest-util@29.7.0: jest-util@29.7.0:
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 20.17.10 '@types/node': 22.10.2
chalk: 4.1.2 chalk: 4.1.2
ci-info: 3.9.0 ci-info: 3.9.0
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -8689,6 +8706,8 @@ snapshots:
parse-ms@3.0.0: {} parse-ms@3.0.0: {}
parse-ms@4.0.0: {}
parse5@6.0.1: {} parse5@6.0.1: {}
parseurl@1.3.3: {} parseurl@1.3.3: {}
@ -8763,6 +8782,10 @@ snapshots:
dependencies: dependencies:
parse-ms: 3.0.0 parse-ms: 3.0.0
pretty-ms@9.2.0:
dependencies:
parse-ms: 4.0.0
process-nextick-args@2.0.1: {} process-nextick-args@2.0.1: {}
progress@2.0.3: {} progress@2.0.3: {}
@ -9386,7 +9409,7 @@ snapshots:
buffer: 5.7.1 buffer: 5.7.1
through: 2.3.8 through: 2.3.8
undici-types@6.19.8: {} undici-types@6.20.0: {}
unified@11.0.5: unified@11.0.5:
dependencies: dependencies:

129
readme.md
View File

@ -1,7 +1,130 @@
```markdown
# @foss.global/codefeed # @foss.global/codefeed
a module for creating feeds for code development A module for creating feeds for code development.
## How to create the docs ## Install
To create docs run gitzone aidoc. To install the `@foss.global/codefeed` package, you can run the following npm command in your project directory:
```bash
npm install @foss.global/codefeed
```
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.
## Usage
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.
### Setting Up CodeFeed
To get started, import the `CodeFeed` class from the module:
```typescript
import { CodeFeed } from '@foss.global/codefeed';
```
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:
```typescript
const codeFeed = new CodeFeed('https://your-gitea-instance-url.com', 'your-api-token');
```
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.
### Fetching Commits
One of the core functionalities of CodeFeed is fetching commits from a Gitea instance. By calling `fetchAllCommitsFromInstance`, you can retrieve commits across multiple repositories:
```typescript
(async () => {
try {
const commits = await codeFeed.fetchAllCommitsFromInstance();
console.log(commits);
} catch (error) {
console.error('An error occurred while fetching commits:', error);
}
})();
```
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.
Each commit object in the resulting array conforms to the `ICommitResult` interface, containing details such as:
- `baseUrl`
- `org`
- `repo`
- `timestamp`
- `hash`
- `commitMessage`
- `tagged` (boolean)
- `publishedOnNpm` (boolean)
- `prettyAgoTime` (human-readable relative time)
- `changelog` (text from the `changelog.md` associated with a commit)
### Understanding the Data Fetch Process
#### Fetching Organizations
The `fetchAllOrganizations` method collects all organizations within the Gitea instance:
```typescript
const organizations = await codeFeed.fetchAllOrganizations();
console.log('Organizations:', organizations);
```
This method interacts with the Gitea API to pull organization names, aiding further requests that require organization context.
#### Fetching Repositories
Repositories under these organizations can be retrieved using `fetchAllRepositories`:
```typescript
const repositories = await codeFeed.fetchAllRepositories();
console.log('Repositories:', repositories);
```
Here, filtering by organization can help narrow down the scope further when dealing with large instances.
#### Fetching Tags and Commits
To handle repository-specific details, use:
- `fetchTags(owner: string, repo: string)`: Appropriately handles paginated tag data within a repository.
- `fetchRecentCommitsForRepo(owner: string, repo: string)`: Gathers commit data specific to the past 24 hours for a given repository.
```typescript
const tags = await codeFeed.fetchTags('orgName', 'repoName');
const recentCommits = await codeFeed.fetchRecentCommitsForRepo('orgName', 'repoName');
console.log('Tags:', tags);
console.log('Recent Commits:', recentCommits);
```
### Changelog Integration
Loading changelog content from a repository is integrated into the flow with `loadChangelogFromRepo`. This can be accessed when processing specific commits:
```typescript
await codeFeed.loadChangelogFromRepo('org', 'repo');
const changelog = codeFeed.getChangelogForVersion('1.0.0');
console.log('Changelog for version 1.0.0:', changelog);
```
### Reacting to Repository Activity
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:
```typescript
const hasActivity = await codeFeed.hasNewActivity({ orgName: 'orgName', repoName: 'repoName' });
console.log('New activity detected:', hasActivity);
```
### Conclusion
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.
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!
```
undefined

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@foss.global/codefeed', name: '@foss.global/codefeed',
version: '1.3.0', version: '1.6.3',
description: 'a module for creating feeds for code development' 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

@ -1,10 +1,19 @@
// module
import * as interfaces from './interfaces/index.js';
export {
interfaces,
}
// @push.rocks // @push.rocks
import * as qenv from '@push.rocks/qenv'; import * as qenv from '@push.rocks/qenv';
import * as smartnpm from '@push.rocks/smartnpm'; import * as smartnpm from '@push.rocks/smartnpm';
import * as smartxml from '@push.rocks/smartxml'; import * as smartxml from '@push.rocks/smartxml';
import * as smarttime from '@push.rocks/smarttime';
export { export {
qenv, qenv,
smartnpm, smartnpm,
smartxml, smartxml,
smarttime,
} }

View File

@ -1,66 +1,84 @@
import * as plugins from './codefeed.plugins.js'; import * as plugins from './codefeed.plugins.js';
interface RepositoryOwner {
login: string;
}
interface Repository {
owner: RepositoryOwner;
name: string;
}
interface CommitAuthor {
date: string;
}
interface CommitDetail {
message: string;
author: CommitAuthor;
}
interface Commit {
sha: string;
commit: CommitDetail;
}
interface Tag {
commit?: {
sha?: string;
};
}
interface RepoSearchResponse {
data: Repository[];
}
export interface CommitResult {
baseUrl: string;
org: string;
repo: string;
timestamp: string;
hash: string;
commitMessage: string;
tagged: boolean;
publishedOnNpm: boolean;
}
export class CodeFeed { export class CodeFeed {
private baseUrl: string; private baseUrl: string;
private token?: string; private token?: string;
private npmRegistry = new plugins.smartnpm.NpmRegistry(); private npmRegistry = new plugins.smartnpm.NpmRegistry();
private smartxmlInstance = new plugins.smartxml.SmartXml(); private smartxmlInstance = new plugins.smartxml.SmartXml();
private lastRunTimestamp: string; private lastRunTimestamp: string;
private changelogContent: string;
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);
} }
/** /**
* Fetch all organizations from the Gitea instance. * Load the changelog directly from the Gitea repository.
*/ */
private async loadChangelogFromRepo(owner: string, repo: string): Promise<void> {
const url = `${this.baseUrl}/api/v1/repos/${owner}/${repo}/contents/changelog.md`;
const headers: Record<string, string> = {};
if (this.token) {
headers['Authorization'] = `token ${this.token}`;
}
const response = await fetch(url, { headers });
if (!response.ok) {
console.error(
`Could not fetch CHANGELOG.md from ${owner}/${repo}: ${response.status} ${response.statusText}`
);
this.changelogContent = '';
return;
}
const data = await response.json();
if (!data.content) {
console.warn(`No content field found in response for ${owner}/${repo}/changelog.md`);
this.changelogContent = '';
return;
}
const decodedContent = Buffer.from(data.content, 'base64').toString('utf8');
this.changelogContent = decodedContent;
}
/**
* Parse the changelog to find the entry for a given version.
* The changelog format is assumed as:
*
* # Changelog
*
* ## <date> - <version> - <description>
* <changes...>
*/
private getChangelogForVersion(version: string): string | undefined {
if (!this.changelogContent) {
return undefined;
}
const lines = this.changelogContent.split('\n');
const versionHeaderIndex = lines.findIndex((line) => line.includes(`- ${version} -`));
if (versionHeaderIndex === -1) {
return undefined;
}
const changelogLines: string[] = [];
for (let i = versionHeaderIndex + 1; i < lines.length; i++) {
const line = lines[i];
// The next version header starts with `## `
if (line.startsWith('## ')) {
break;
}
changelogLines.push(line);
}
return changelogLines.join('\n').trim();
}
private async fetchAllOrganizations(): Promise<string[]> { private async fetchAllOrganizations(): Promise<string[]> {
const url = `${this.baseUrl}/api/v1/orgs`; const url = `${this.baseUrl}/api/v1/orgs`;
const response = await fetch(url, { const response = await fetch(url, {
@ -75,56 +93,46 @@ export class CodeFeed {
return data.map((org) => org.username); return data.map((org) => org.username);
} }
/**
* Fetch organization-level activity RSS feed.
*/
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 = `${this.baseUrl}/${optionsArg.orgName}.atom`;
} else if (optionsArg.orgName && optionsArg.repoName) { } else if (optionsArg.orgName && optionsArg.repoName) {
rssUrl = `${this.baseUrl}/${optionsArg.orgName}/${optionsArg.repoName}.atom`; rssUrl = `${this.baseUrl}/${optionsArg.orgName}/${optionsArg.repoName}.atom`;
} else {
throw new Error('Invalid arguments provided to fetchOrgRssFeed.');
} }
const response = await fetch(rssUrl); const response = await fetch(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();
// Parse the Atom feed using fast-xml-parser
const rssData = this.smartxmlInstance.parseXmlToObject(rssText); const rssData = this.smartxmlInstance.parseXmlToObject(rssText);
// Return the <entry> elements from the feed
return rssData.feed.entry || []; return rssData.feed.entry || [];
} }
/**
* Check if the organization's RSS feed has any new activities since the last run.
*/
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);
// Filter entries to find new activities since the last run
return entries.some((entry: any) => { return entries.some((entry: any) => {
const updated = new Date(entry.updated); const updated = new Date(entry.updated);
return updated > new Date(this.lastRunTimestamp); return updated > new Date(this.lastRunTimestamp);
}); });
} }
/** private async fetchAllRepositories(): Promise<plugins.interfaces.IRepository[]> {
* Fetch all repositories accessible to the token/user.
*/
private async fetchAllRepositories(): Promise<Repository[]> {
let page = 1; let page = 1;
const allRepos: Repository[] = []; const allRepos: plugins.interfaces.IRepository[] = [];
while (true) { while (true) {
const url = new URL(`${this.baseUrl}/api/v1/repos/search`); const url = new URL(`${this.baseUrl}/api/v1/repos/search`);
@ -132,14 +140,14 @@ export class CodeFeed {
url.searchParams.set('page', page.toString()); url.searchParams.set('page', page.toString());
const resp = await fetch(url.href, { const resp = await fetch(url.href, {
headers: this.token ? { 'Authorization': `token ${this.token}` } : {}, headers: this.token ? { Authorization: `token ${this.token}` } : {},
}); });
if (!resp.ok) { if (!resp.ok) {
throw new Error(`Failed to fetch repositories: ${resp.statusText}`); throw new Error(`Failed to fetch repositories: ${resp.statusText}`);
} }
const data: RepoSearchResponse = await resp.json(); const data: plugins.interfaces.IRepoSearchResponse = await resp.json();
allRepos.push(...data.data); allRepos.push(...data.data);
if (data.data.length < 50) { if (data.data.length < 50) {
@ -151,12 +159,9 @@ export class CodeFeed {
return allRepos; return allRepos;
} }
/**
* Fetch all tags for a given repository.
*/
private async fetchTags(owner: string, repo: string): Promise<Set<string>> { private async fetchTags(owner: string, repo: string): Promise<Set<string>> {
let page = 1; let page = 1;
const tags: Tag[] = []; const tags: plugins.interfaces.ITag[] = [];
while (true) { while (true) {
const url = new URL(`${this.baseUrl}/api/v1/repos/${owner}/${repo}/tags`); const url = new URL(`${this.baseUrl}/api/v1/repos/${owner}/${repo}/tags`);
@ -164,15 +169,17 @@ export class CodeFeed {
url.searchParams.set('page', page.toString()); url.searchParams.set('page', page.toString());
const resp = await fetch(url.href, { const resp = await fetch(url.href, {
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.href}`
);
throw new Error(`Failed to fetch tags for ${owner}/${repo}: ${resp.statusText}`); throw new Error(`Failed to fetch tags for ${owner}/${repo}: ${resp.statusText}`);
} }
const data: Tag[] = await resp.json(); const data: plugins.interfaces.ITag[] = await resp.json();
tags.push(...data); tags.push(...data);
if (data.length < 50) { if (data.length < 50) {
@ -191,13 +198,13 @@ export class CodeFeed {
return taggedCommitShas; return taggedCommitShas;
} }
/** private async fetchRecentCommitsForRepo(
* Fetch commits from the last 24 hours for a repository. owner: string,
*/ repo: string
private async fetchRecentCommitsForRepo(owner: string, repo: string): Promise<Commit[]> { ): 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: Commit[] = []; const recentCommits: plugins.interfaces.ICommit[] = [];
while (true) { while (true) {
const url = new URL(`${this.baseUrl}/api/v1/repos/${owner}/${repo}/commits`); const url = new URL(`${this.baseUrl}/api/v1/repos/${owner}/${repo}/commits`);
@ -205,15 +212,17 @@ export class CodeFeed {
url.searchParams.set('page', page.toString()); url.searchParams.set('page', page.toString());
const resp = await fetch(url.href, { const resp = await fetch(url.href, {
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.href}`
);
throw new Error(`Failed to fetch commits for ${owner}/${repo}: ${resp.statusText}`); throw new Error(`Failed to fetch commits for ${owner}/${repo}: ${resp.statusText}`);
} }
const data: Commit[] = await resp.json(); const data: plugins.interfaces.ICommit[] = await resp.json();
if (data.length === 0) { if (data.length === 0) {
break; break;
} }
@ -233,13 +242,10 @@ export class CodeFeed {
return recentCommits; return recentCommits;
} }
/** public async fetchAllCommitsFromInstance(): Promise<plugins.interfaces.ICommitResult[]> {
* Fetch all commits by querying all organizations.
*/
public async fetchAllCommitsFromInstance(): Promise<CommitResult[]> {
const orgs = await this.fetchAllOrganizations(); const orgs = await this.fetchAllOrganizations();
console.log(`Found ${orgs.length} organizations`); console.log(`Found ${orgs.length} organizations`);
let allCommits: CommitResult[] = []; let allCommits: plugins.interfaces.ICommitResult[] = [];
for (const orgName of orgs) { for (const orgName of orgs) {
console.log(`Checking activity for organization: ${orgName}`); console.log(`Checking activity for organization: ${orgName}`);
@ -252,7 +258,7 @@ export class CodeFeed {
console.log(`No new activity for organization: ${orgName}`); console.log(`No new activity for organization: ${orgName}`);
continue; continue;
} }
} catch (error) { } catch (error: any) {
console.error(`Error fetching activity for organization ${orgName}:`, error.message); console.error(`Error fetching activity for organization ${orgName}:`, error.message);
continue; continue;
} }
@ -270,10 +276,14 @@ export class CodeFeed {
console.log(`No new activity for repository: ${orgName}/${r.name}`); console.log(`No new activity for repository: ${orgName}/${r.name}`);
continue; continue;
} }
} catch (error) { } 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;
} }
const org = r.owner.login; const org = r.owner.login;
const repo = r.name; const repo = r.name;
console.log(`Processing repository ${org}/${repo}`); console.log(`Processing repository ${org}/${repo}`);
@ -282,48 +292,81 @@ export class CodeFeed {
const taggedCommitShas = await this.fetchTags(org, repo); const taggedCommitShas = await this.fetchTags(org, repo);
const commits = await this.fetchRecentCommitsForRepo(org, repo); const commits = await this.fetchRecentCommitsForRepo(org, repo);
const commitResults = commits.map((c) => ({ // Load the changelog from this repo.
await this.loadChangelogFromRepo(org, repo);
const commitResults = commits.map((c) => {
const commit: plugins.interfaces.ICommitResult = {
baseUrl: this.baseUrl, baseUrl: this.baseUrl,
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()
),
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,
};
return commit;
});
if (commitResults.length > 0) { if (commitResults.length > 0) {
try { try {
const packageInfo = await this.npmRegistry.getPackageInfo(`@${org}/${repo}`); const packageInfo = await this.npmRegistry.getPackageInfo(`@${org}/${repo}`);
for (const commit of commitResults.filter((c) => c.tagged)) { for (const commitResult of commitResults.filter((c) => c.tagged)) {
const versionCandidate = commitResult.commitMessage.replace('\n', '').trim();
const correspondingVersion = packageInfo.allVersions.find((versionArg) => { const correspondingVersion = packageInfo.allVersions.find((versionArg) => {
return versionArg.version === commit.commitMessage.replace('\n', ''); return versionArg.version === versionCandidate;
}); });
if (correspondingVersion) { if (correspondingVersion) {
commit.publishedOnNpm = true; commitResult.publishedOnNpm = true;
} }
} }
} catch (error) { } 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);
} catch (error) { } catch (error: any) {
console.error(`Skipping repository ${org}/${repo} due to error:`, error.message); console.error(`Skipping repository ${org}/${repo} due to error:`, error.message);
} }
} }
} }
console.log(`Processed ${allCommits.length} commits in total.`); console.log(`Processed ${allCommits.length} commits in total.`);
allCommits = allCommits
.filter((commitArg) => commitArg.tagged)
.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
console.log(`Filtered to ${allCommits.length} commits with tagged statuses.`);
for (const c of allCommits) { for (const c of allCommits) {
console.log(`______________________________________________________ console.log(` ==========================================================================
Commit ${c.hash} by ${c.org}/${c.repo} at ${c.timestamp} ${c.prettyAgoTime} ago:
${c.org}/${c.repo}
${c.commitMessage} ${c.commitMessage}
Published on npm: ${c.publishedOnNpm} Published on npm: ${c.publishedOnNpm}
${c.changelog ? `Changelog:\n${c.changelog}\n` : ''}
`); `);
} }
return allCommits; return allCommits;
} }
} }

45
ts/interfaces/index.ts Normal file
View File

@ -0,0 +1,45 @@
export interface IRepositoryOwner {
login: string;
}
export interface IRepository {
owner: IRepositoryOwner;
name: string;
}
export interface ICommitAuthor {
date: string;
}
export interface ICommitDetail {
message: string;
author: ICommitAuthor;
}
export interface ICommit {
sha: string;
commit: ICommitDetail;
}
export interface ITag {
commit?: {
sha?: string;
};
}
export interface IRepoSearchResponse {
data: IRepository[];
}
export interface ICommitResult {
baseUrl: string;
org: string;
repo: string;
timestamp: string;
hash: string;
commitMessage: string;
tagged: boolean;
publishedOnNpm: boolean;
prettyAgoTime: string;
changelog: string | undefined;
}

View File

@ -8,7 +8,8 @@
"esModuleInterop": true, "esModuleInterop": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"baseUrl": ".", "baseUrl": ".",
"paths": {} "paths": {
}
}, },
"exclude": [ "exclude": [
"dist_*/**/*.d.ts" "dist_*/**/*.d.ts"