Files
awesome-docker/tests/common.mjs
Julien Bisconti 5b46451014 Modernization (thanks to AI) (#1187)
* -  Removed 3 broken links (labex.io, hashnode.com entries)
-  Fixed rust-lang.org redirect issue
-  Added problematic domains to exclusion list (YouTube playlists, aquasec, cloudsmith)
-  Updated all npm dependencies to latest versions

-  **health_check.mjs** - Comprehensive repository health checker
  - Detects archived repositories
  - Identifies stale projects (2+ years inactive)
  - Flags inactive projects (1-2 years)
  - Generates detailed health reports
  - Run with: `npm run health-check`

-  **test_all.mjs** - Now detects archived repositories
  - Added `isArchived` field to GraphQL query
  - Warns about archived repos that should be marked `💀`
  - Non-blocking warnings (doesn't fail builds)

- Runs every Monday at 9 AM UTC
- Checks all 731+ GitHub repositories for health
- Auto-creates/updates GitHub issue with findings
- Labels: `health-report`, `maintenance`
- Manual trigger available

- Runs every Saturday at 2 AM UTC
- Tests all external links
- Auto-creates issue when links break
- Auto-closes issue when all links fixed
- Labels: `broken-links`, `bug`

- Already checks for duplicates
- Now also checks for archived repos
- Validates link format and availability

-  **MAINTENANCE.md** - Complete guide for maintainers
  - Monthly, quarterly, and annual tasks
  - Emergency procedures
  - Quality standards
  - Metrics to track

-  **AGENTS.md** - Updated with new commands
  - Added health-check command
  - Noted GITHUB_TOKEN requirements
  - Added alphabetical sorting guideline

- **Total Links**: 883 (731 GitHub repos + 152 external)
- **Working Links**: >99% (after fixes)
- **Abandoned Projects**: 15 marked with `💀`
- **Automated Checks**: 3 workflows running

- **Automatic detection** of abandoned/archived projects
- **Weekly monitoring** ensures issues are caught early
- **Proactive alerts** via GitHub issues

- No more manual link checking (automated weekly)
- Archived repos detected automatically
- Contributors get instant PR feedback

- Health metrics tracked over time
- Clear standards documented
- Easy onboarding for new maintainers

- Monday: Health report generated and posted
- Saturday: Link validation runs

- Review health report issue
- Mark any newly archived projects with `💀`

- Run full health check: `npm run health-check`
- Review inactive projects (1-2 years)
- Consider removing very old abandoned projects

- Deep cleanup of `💀` projects
- Update documentation
- Review categories and organization

1. **Auto-PR for Archived Repos**: Bot could auto-create PRs to mark archived repos
2. **Contribution Stats**: Track and display top contributors
3. **Category Health**: Per-category health metrics
4. **Dependency Updates**: Dependabot for npm packages
5. **Star Trending**: Track which projects are gaining popularity

- `tests/health_check.mjs` - Health checker script
- `.github/workflows/health_report.yml` - Weekly health workflow
- `.github/workflows/broken_links.yml` - Link validation workflow
- `.github/MAINTENANCE.md` - Maintainer guide
- `AGENTS.md` - AI agent guidelines

- `README.md` - Removed 3 broken links, fixed 1 redirect
- `tests/test_all.mjs` - Added archive detection
- `tests/exclude_in_test.json` - Added problematic domains
- `package.json` - Added health-check script
- `package-lock.json` - Updated dependencies

Before: Manual maintenance, broken links accumulate, outdated projects linger
After: **Automated health monitoring, proactive issue detection, systematic maintenance**

The list is now **self-maintaining** with minimal human oversight required.

---

*Generated: 2025-10-01*

* update github actions

* remove dead links

* set timeout

* Add badges
2025-10-02 15:03:59 +02:00

109 lines
3.0 KiB
Markdown

import fetch from 'node-fetch';
import { isRedirect } from 'node-fetch';
import {readFileSync} from 'fs';
const LINKS_OPTIONS = {
redirect: 'manual',
headers: {
'Content-Type': 'application/json',
'user-agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
},
timeout: 60000, // 1m
signal: AbortSignal.timeout(60000),
};
const LOG = {
error: (...args) => console.error('❌ ERROR', args),
error_string: (...args) =>
console.error('❌ ERROR', JSON.stringify({ ...args }, null, ' ')),
debug: (...args) => {
if (process.env.DEBUG) console.log('>>> DEBUG: ', { ...args });
},
debug_string: (...args) => {
if (process.env.DEBUG)
console.log('>>> DEBUG: ', JSON.stringify({ ...args }, null, ' '));
},
};
const handleFailure = (error) => {
console.error(`${error.message}: ${error.stack}`, { error });
process.exit(1);
};
process.on('unhandledRejection', handleFailure);
const extract_all_links = (markdown) => {
// if you have a problem and you try to solve it with a regex,
// now you have two problems
// TODO: replace this mess with a mardown parser ?
const re = /(((https:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w\-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/@\-\\\w]*))?)/g;
return markdown.match(re);
};
const find_duplicates = (arr) => {
const hm = {};
const dup = [];
arr.forEach((e) => {
if (hm[e]) dup.push(e);
else hm[e] = true;
});
return dup;
};
const partition = (arr, func) => {
const ap = [[], []];
arr.forEach((e) => (func(e) ? ap[0].push(e) : ap[1].push(e)));
return ap;
};
async function fetch_link(url) {
try {
const { headers, ok, status, statusText } = await fetch(url, LINKS_OPTIONS);
const redirect = isRedirect(status) ? { redirect: { src: url, dst: headers.get("location") } } : {};
return [url, { ok, status: statusText, ...redirect }];
} catch (error) {
return [url, { ok: false, status: error.message }];
}
}
async function batch_fetch({ arr, get, post_filter_func, BATCH_SIZE = 8 }) {
const result = [];
/* eslint-disable no-await-in-loop */
for (let i = 0; i < arr.length; i += BATCH_SIZE) {
const batch = arr.slice(i, i + BATCH_SIZE);
LOG.debug_string({ batch });
let res = await Promise.all(batch.map(get));
console.log(`batch fetched...${i + BATCH_SIZE}`);
res = post_filter_func ? res.filter(post_filter_func) : res;
LOG.debug_string({ res });
result.push(...res);
}
return result;
}
const data = readFileSync('./tests/exclude_in_test.json')
const exclude = JSON.parse(data)
const exclude_length = exclude.length;
const exclude_from_list = (link) => {
let is_excluded = false;
for (let i = 0; i < exclude_length; i += 1) {
if (link.startsWith(exclude[i])) {
is_excluded = true;
break;
}
}
return is_excluded;
};
export default {
LOG,
handleFailure,
extract_all_links,
find_duplicates,
partition,
fetch_link,
batch_fetch,
exclude_from_list,
};