Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3a79a9a
`vi.hakovn`
tachibana-shin Dec 25, 2025
371ffba
Merge branch 'vi.hakovn'
tachibana-shin Dec 29, 2025
64b1a4d
Add ci check domain (#17)
tachibana-shin Dec 31, 2025
add36d0
Merge branch 'main' of https://github.com/Aidoku-Community/sources
tachibana-shin Dec 31, 2025
0416dab
Merge branch 'main' of https://github.com/tachibana-shin/aidoku-sourc…
tachibana-shin Dec 31, 2025
6316969
close #16
tachibana-shin Dec 31, 2025
f6d70f0
close #15
tachibana-shin Dec 31, 2025
7ae07d3
close #13
tachibana-shin Dec 31, 2025
3e1a5ad
close #12
tachibana-shin Dec 31, 2025
90ebb95
close #11
tachibana-shin Dec 31, 2025
d1b392f
close #9
tachibana-shin Dec 31, 2025
88cb0f5
close #10
tachibana-shin Dec 31, 2025
68dc03a
Close #18
tachibana-shin Jan 1, 2026
9df68de
Merge branch 'Aidoku-Community:main' into main
tachibana-shin Jan 1, 2026
2cacf9c
Update source.json
tachibana-shin Jan 2, 2026
a2686ad
Update lib.rs
tachibana-shin Jan 2, 2026
2114a13
Merge branch 'Aidoku-Community:main' into main
tachibana-shin Jan 3, 2026
2bd2ac9
check-domains
tachibana-shin Dec 31, 2025
2c7f77b
deno setup
tachibana-shin Dec 31, 2025
05562b3
pin deno version
tachibana-shin Dec 31, 2025
c371579
try use fs read file
tachibana-shin Dec 31, 2025
ea0ae54
js err
tachibana-shin Dec 31, 2025
51ef529
fix json parse and add tag
tachibana-shin Dec 31, 2025
d3a2dac
update title
tachibana-shin Dec 31, 2025
1cf0bb5
output json not found?
tachibana-shin Dec 31, 2025
a1790e8
Apply suggestions from code review
tachibana-shin Dec 31, 2025
1a5685b
ci(domain): auto-close stale issues
tachibana-shin Jan 3, 2026
71aa9ac
ci(check-domain): add auto-assign to new issues
tachibana-shin Jan 3, 2026
2590e2c
ci(check-domain): add source folder links to issues
tachibana-shin Jan 3, 2026
75c9307
Merge branch 'main' into test-scripts-check-domain
tachibana-shin Jan 3, 2026
62beb91
Merge pull request #63 from tachibana-shin/vi.hakovn
tachibana-shin Jan 11, 2026
aeaeed8
Merge branch 'main' into test-scripts-check-domain
tachibana-shin Jan 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions .github/workflows/check-domain.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
name: Check Domains

on:
schedule:
- cron: "0 * * * *" # every hour
workflow_dispatch:

permissions:
issues: write
contents: read

jobs:
check:
runs-on: ubuntu-latest

steps:
- name: Checkout repo
uses: actions/checkout@v4

- uses: denoland/setup-deno@v2
with:
deno-version: v2.6.3

- name: Run domain checker
id: run
env:
EXCLUDE: ${{ vars.SCAN_SOURCES_EXCLUDE || '' }}
run: |
cd check-domains
deno task start

- name: Create issues per domain
uses: actions/github-script@v7
env:
AUTO_ASSIGN: ${{ vars.ISSUE_AUTO_ASSIGN || '' }}
with:
script: |
const fs = require("fs");

const data = JSON.parse(fs.readFileSync("check-domain-output.json", "utf8"));

const deiced = data?.deiced || {};
const changed = data?.changed || {};

const LABEL_TYPE = "domain"
const AUTO_ASSIGN = process.env.AUTO_ASSIGN || null;

const owner = context.repo.owner;
const repo = context.repo.repo;

const branch = context.ref.replace("refs/heads/", "");

const activeIssues = new Set();

const issues = await github.rest.issues.listForRepo({
owner,
repo,
state: "open"
});

// Helper to build direct link to the source folder
const sourceFolderLink = (name) =>
`https://github.com/${owner}/${repo}/tree/${branch}/sources/${name}`;

// Helper to create issue with labels
const createIssue = async (title, body, extraLabels = []) => {
const exists = issues.data.find(i => i.title === title);
if (exists) {
activeIssues.add(exists.number);
console.log("Issue already exists:", title);
return;
}

const labels = ["ci", LABEL_TYPE, ...extraLabels];

const newIssue = await github.rest.issues.create({
owner,
repo,
title,
body,
labels
});

activeIssues.add(newIssue.data.number);

console.log("Issue created:", title);

if (AUTO_ASSIGN) {
console.log("Auto-assign to:", AUTO_ASSIGN);

await github.rest.issues.addAssignees({
owner,
repo,
issue_number: newIssue.data.number,
assignees: [AUTO_ASSIGN]
});
}
};

// ---- Handle deiced ----
for (const source of Object.keys(deiced)) {
const urls = deiced[source];
const folderUrl = sourceFolderLink(source);

const title = `source(${source}): domain deiced`;

const body = [
`## ❄️ Domain Deiced`,
`**Source:** ${source}`,
``,
`### Source Folder`,
`${folderUrl}`,
``,
`### URLs Checked`,
urls.map(u => "- " + u).join("\n"),
``,
`### Timestamp`,
new Date().toISOString()
].join("\n");

await createIssue(title, body, ["deiced"]);
}

// ---- Handle changed ----
for (const source of Object.keys(changed)) {
const urls = changed[source];
const original = urls[0];
const redirected = urls[1];
const folderUrl = sourceFolderLink(source);

const title = `source(${source}): changed → \`${redirected}\``;

const body = [
`## 🔀 Domain Redirect Detected`,
`**Source:** ${source}`,
``,
`### Source Folder`,
`${folderUrl}`,
``,
`### Original`,
`- ${original}`,
``,
`### Redirected To`,
`- ${redirected}`,
``,
`### Timestamp`,
new Date().toISOString()
].join("\n");

await createIssue(title, body, ["changed"]);
}

// ---- Auto-close stale fixed issues ----
console.log("Checking for stale issues to close...");

for (const issue of issues.data) {
if (!issue.labels.some(l => l.name === LABEL_TYPE)) continue;

// If this issue wasn't registered as active, it is now fixed
if (!activeIssues.has(issue.number)) {
console.log("Closing stale issue:", issue.title);

// Comment before close
await github.rest.issues.createComment({
owner,
repo,
issue_number: issue.number,
body: "Automatically closed because the domain is now resolved. 🟢"
});

// Close it
await github.rest.issues.update({
owner,
repo,
issue_number: issue.number,
state: "closed"
});
}
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ templates/*/target
.zed
.ropeproject
/target

/check-domain-output.json
11 changes: 11 additions & 0 deletions check-domains/deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"tasks": {
"dev": "deno run --allow-read --allow-env --allow-write=../check-domain-output.json --allow-net --watch main.ts",
"start": "deno run --allow-read --allow-env --allow-write=../check-domain-output.json --allow-net main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1",
"p-limit": "npm:p-limit@^7.2.0",
"ts-retry": "npm:ts-retry@^6.0.0"
}
}
41 changes: 41 additions & 0 deletions check-domains/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions check-domains/domain-alive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// ドメインが生きているかどうかをDNSで確認する
// google dns?

import type { LookupAddress } from "node:dns";
import { lookup } from "node:dns/promises";

export interface AliveOk {
alive: true;
records: LookupAddress[];
}
export interface AliveFailed {
alive: false;
domains: string[];
err: unknown;
}
export async function checkDomainAlive(
url: string
): Promise<AliveOk | AliveFailed> {
try {
const result = await lookup(new URL(url).hostname, { all: true });

// console.log("DNS Records:", result);
console.log("Domain %s is alive ✓", url);

return { alive: true, records: result };
} catch (err) {
console.log("DNS lookup failed ✗", err);
return { alive: false, domains: [url], err };
}
}
53 changes: 53 additions & 0 deletions check-domains/domain-redirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// HTTPステータスとリダイレクトを確認する関数

import type { AliveFailed } from "./domain-alive.ts";

export interface DomainOk {
alive: true;
from: string;
location: string;
}
export async function checkDomainHttp(
domain: string
): Promise<DomainOk | AliveFailed | void> {
const url = domain.startsWith("http") ? domain : `https://${domain}`;

try {
const res = await fetch(url, {
redirect: "manual", // do NOT auto redirect
method: "GET",
});

// Redirect?
const location = res.headers.get("location");

if (location) {
const objUrl = new URL(url);
const objLocation = new URL(location, url);

if (
!checkDomainEqual(objUrl, objLocation, "protocol") ||
!checkDomainEqual(objUrl, objLocation, "hostname") ||
!checkDomainEqual(objUrl, objLocation, "port") ||
// !checkDomainEqual(objUrl, objLocation, "pathname") ||
!checkDomainEqual(objUrl, objLocation, "username") ||
!checkDomainEqual(objUrl, objLocation, "password")
) {
console.log("%s redirected to: %s", url, location);
return {
alive: true,
from: url,
location,
};
}
}
// deno-lint-ignore no-explicit-any
} catch (err) {
console.log("HTTP check failed ✗", err instanceof Error ? err.message : String(err));
return { alive: false, err, domains: [url] };
}
}

function checkDomainEqual(u1: URL, u2: URL, name: keyof URL): boolean {
return u1[name] === u2[name];
}
Loading
Loading