ソースを参照

Refactor: simplify build infra

SukkaW 2 年 前
コミット
9eda90e85b

+ 8 - 5
Build/build-apple-cdn.ts

@@ -5,13 +5,16 @@ import { parseFelixDnsmasq } from './lib/parse-dnsmasq';
 import { task, traceAsync } from './lib/trace-runner';
 import { SHARED_DESCRIPTION } from './lib/constants';
 import picocolors from 'picocolors';
+import { createMemoizedPromise } from './lib/memo-promise';
+
+export const getAppleCdnDomainsPromise = createMemoizedPromise(() => traceAsync(
+  picocolors.gray('download dnsmasq-china-list apple.china.conf'),
+  () => parseFelixDnsmasq('https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/apple.china.conf'),
+  picocolors.gray
+));
 
 export const buildAppleCdn = task(import.meta.path, async () => {
-  const res = await traceAsync(
-    picocolors.gray('download dnsmasq-china-list apple.china.conf'),
-    () => parseFelixDnsmasq('https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/apple.china.conf'),
-    picocolors.gray
-  );
+  const res = await getAppleCdnDomainsPromise();
 
   const description = [
     ...SHARED_DESCRIPTION,

+ 7 - 2
Build/build-chn-cidr.ts

@@ -6,6 +6,7 @@ import { task, traceAsync, traceSync } from './lib/trace-runner';
 
 import { exclude } from 'fast-cidr-tools';
 import picocolors from 'picocolors';
+import { createMemoizedPromise } from './lib/memo-promise';
 
 // https://github.com/misakaio/chnroutes2/issues/25
 const EXCLUDE_CIDRS = [
@@ -17,17 +18,21 @@ const INCLUDE_CIDRS = [
   '211.99.96.0/19' // wy.com.cn
 ];
 
-export const buildChnCidr = task(import.meta.path, async () => {
+export const getChnCidrPromise = createMemoizedPromise(async () => {
   const cidr = await traceAsync(
     picocolors.gray('download chnroutes2'),
     async () => processLineFromReadline(await fetchRemoteTextAndReadByLine('https://raw.githubusercontent.com/misakaio/chnroutes2/master/chnroutes.txt')),
     picocolors.gray
   );
-  const filteredCidr = traceSync(
+  return traceSync(
     picocolors.gray('processing chnroutes2'),
     () => exclude([...cidr, ...INCLUDE_CIDRS], EXCLUDE_CIDRS, true),
     picocolors.gray
   );
+});
+
+export const buildChnCidr = task(import.meta.path, async () => {
+  const filteredCidr = await getChnCidrPromise();
 
   // Can not use SHARED_DESCRIPTION here as different license
   const description = [

+ 7 - 2
Build/build-domestic-ruleset.ts

@@ -6,8 +6,9 @@ import { processLineFromReadline } from './lib/process-line';
 import { compareAndWriteFile, createRuleset } from './lib/create-file';
 import { task } from './lib/trace-runner';
 import { SHARED_DESCRIPTION } from './lib/constants';
+import { createMemoizedPromise } from './lib/memo-promise';
 
-export const buildDomesticRuleset = task(import.meta.path, async () => {
+export const getDomesticDomainsRulesetPromise = createMemoizedPromise(async () => {
   const results = await processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/domestic.conf')));
 
   results.push(
@@ -17,6 +18,10 @@ export const buildDomesticRuleset = task(import.meta.path, async () => {
     }, []).map((domain) => `DOMAIN-SUFFIX,${domain}`)
   );
 
+  return results;
+});
+
+export const buildDomesticRuleset = task(import.meta.path, async () => {
   const rulesetDescription = [
     ...SHARED_DESCRIPTION,
     '',
@@ -28,7 +33,7 @@ export const buildDomesticRuleset = task(import.meta.path, async () => {
       'Sukka\'s Ruleset - Domestic Domains',
       rulesetDescription,
       new Date(),
-      results,
+      await getDomesticDomainsRulesetPromise(),
       'ruleset',
       path.resolve(import.meta.dir, '../List/non_ip/domestic.conf'),
       path.resolve(import.meta.dir, '../Clash/non_ip/domestic.txt')

+ 184 - 0
Build/build-sspanel-appprofile.ts

@@ -0,0 +1,184 @@
+import fsp from 'fs/promises';
+
+import { getAppleCdnDomainsPromise } from './build-apple-cdn';
+import { getDomesticDomainsRulesetPromise } from './build-domestic-ruleset';
+import { surgeRulesetToClashClassicalTextRuleset } from './lib/clash';
+import { readFileByLine } from './lib/fetch-text-by-line';
+import { processLineFromReadline } from './lib/process-line';
+import { task } from './lib/trace-runner';
+import path from 'path';
+
+import { ALL as AllStreamServices } from '../Source/stream';
+import { getChnCidrPromise } from './build-chn-cidr';
+import { getTelegramCIDRPromise } from './build-telegram-cidr';
+import { compareAndWriteFile } from './lib/create-file';
+
+const POLICY_GROUPS: Array<[name: string, insertProxy: boolean, insertDirect: boolean]> = [
+  ['Default Proxy', true, false],
+  ['Global', true, true],
+  ['Microsoft & Apple', true, true],
+  ['Stream', true, false],
+  ['Domestic', false, true],
+  ['Final Match', true, true]
+];
+
+const removeNoResolved = (line: string) => line.replace(',no-resolve', '');
+
+/**
+ * This only generates a simplified version, for under-used users only.
+ */
+export const buildSSPanelUIMAppProfile = task(import.meta.path, async () => {
+  const [
+    domesticDomains,
+    appleCdnDomains,
+    appleCnDomains,
+    neteaseMusicDomains,
+    microsoftDomains,
+    appleDomains,
+    streamDomains,
+    globalDomains,
+    globalPlusDomains,
+    telegramDomains,
+    domesticCidrs,
+    streamCidrs,
+    { results: rawTelegramCidrs }
+  ] = await Promise.all([
+    // domestic - domains
+    getDomesticDomainsRulesetPromise().then(surgeRulesetToClashClassicalTextRuleset),
+    getAppleCdnDomainsPromise().then(domains => domains.map(domain => `DOMAIN-SUFFIX,${domain}`)),
+    processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/apple_cn.conf'))),
+    processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/neteasemusic.conf'))).then(surgeRulesetToClashClassicalTextRuleset),
+    // microsoft & apple - domains
+    processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/internal_microsoft.conf'))),
+    (processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/apple_services.conf')))).then(surgeRulesetToClashClassicalTextRuleset),
+    // stream - domains
+    surgeRulesetToClashClassicalTextRuleset(AllStreamServices.flatMap((i) => i.rules)),
+    // global - domains
+    processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/global.conf'))).then(surgeRulesetToClashClassicalTextRuleset),
+    processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/global_plus.conf'))).then(surgeRulesetToClashClassicalTextRuleset),
+    processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/telegram.conf'))).then(surgeRulesetToClashClassicalTextRuleset),
+    // domestic - ip cidr
+    getChnCidrPromise().then(cidrs => cidrs.map(cidr => `IP-CIDR,${cidr}`)),
+    AllStreamServices.flatMap((i) => (
+      i.ip
+        ? [
+          ...i.ip.v4.map((ip) => `IP-CIDR,${ip}`),
+          ...i.ip.v6.map((ip) => `IP-CIDR6,${ip}`)
+        ]
+        : []
+    )),
+    // global - ip cidr
+    getTelegramCIDRPromise()
+  ] as const);
+
+  const telegramCidrs = rawTelegramCidrs.map(removeNoResolved);
+
+  const output = generateAppProfile(
+    [
+      ...domesticDomains,
+      ...appleCdnDomains,
+      ...appleCnDomains,
+      ...neteaseMusicDomains
+    ],
+    [
+      ...microsoftDomains,
+      ...appleDomains
+    ],
+    streamDomains,
+    [
+      ...globalDomains,
+      ...globalPlusDomains,
+      ...telegramDomains
+    ],
+    domesticCidrs,
+    streamCidrs,
+    [
+      ...telegramCidrs
+    ]
+  );
+
+  await fsp.mkdir(path.resolve(import.meta.dir, '../List/internal'), { recursive: true });
+
+  await compareAndWriteFile(
+    output,
+    path.resolve(import.meta.dir, '../List/internal/appprofile.php')
+  );
+});
+
+if (import.meta.main) {
+  buildSSPanelUIMAppProfile();
+}
+
+const isTruthy = <T>(i: T | 0 | '' | false | null | undefined): i is T => !!i;
+
+function generateAppProfile(
+  directDomains: string[],
+  microsoftAppleDomains: string[],
+  streamDomains: string[],
+  globalDomains: string[],
+  directCidrs: string[],
+  streamCidrs: string[],
+  globalCidrs: string[]
+) {
+  const result: string[] = [];
+
+  result.push(
+    '<?php',
+    '',
+    'declare(strict_types=1);',
+    '',
+    '$_ENV[\'Clash_Config\'] = [',
+    '    \'port\' => 7890,',
+    '    \'socks-port\' => 7891,',
+    '    \'allow-lan\' => false,',
+    '    \'mode\' => \'Rule\',',
+    '    \'ipv6\' => true,',
+    '    \'log-level\' => \'error\',',
+    '    \'external-controller\' => \'0.0.0.0:9090\',',
+    '];',
+    '',
+    `$_ENV['Clash_Group_Indexes'] = [${JSON.stringify(POLICY_GROUPS.reduce<number[]>((acc, [, insertProxy], index) => {
+      if (insertProxy) {
+        acc.push(index);
+      }
+      return acc;
+    }, [])).slice(1, -1)}];`,
+    '$_ENV[\'Clash_Group_Config\'] = [',
+    '    \'proxy-groups\' => [',
+    ...POLICY_GROUPS.flatMap(([name, insertProxy, insertDirect]) => {
+      return [
+        '        [',
+        `            'name' => '${name}',`,
+        '            \'type\' => \'select\',',
+        '            \'proxies\' => [',
+        insertProxy && name !== 'Default Proxy' && '                \'Default Proxy\',',
+        insertDirect && '                \'DIRECT\',',
+        '            ],',
+        '        ],'
+      ].filter(isTruthy);
+    }),
+    '    ],',
+    '    \'rules\' => [',
+    // domestic - domains
+    ...directDomains.map(line => `        '${line},Domestic',`),
+    // microsoft & apple - domains
+    ...microsoftAppleDomains.map(line => `        '${line},Microsoft & Apple',`),
+    // stream - domains
+    ...streamDomains.map(line => `        '${line},Stream',`),
+    // global - domains
+    ...globalDomains.map(line => `        '${line},Global',`),
+    // domestic - ip cidr
+    ...directCidrs.map(line => `        '${line},Domestic,no-resolve',`),
+    // microsoft & apple - ip cidr (nope)
+    // stream - ip cidr
+    ...streamCidrs.map(line => `        '${line},Stream,no-resolve',`),
+    // global - ip cidr
+    ...globalCidrs.map(line => `        '${line},Global,no-resolve',`),
+    // match
+    '        \'MATCH,Final Match\',',
+    '    ],',
+    '];'
+  );
+
+  return result;
+}

+ 1 - 1
Build/build-stream-service.ts

@@ -7,7 +7,7 @@ import { createRuleset } from './lib/create-file';
 import { ALL, NORTH_AMERICA, EU, HK, TW, JP, KR } from '../Source/stream';
 import { SHARED_DESCRIPTION } from './lib/constants';
 
-const createRulesetForStreamService = (fileId: string, title: string, streamServices: Array<import('../Source/stream').StreamService>) => {
+export const createRulesetForStreamService = (fileId: string, title: string, streamServices: Array<import('../Source/stream').StreamService>) => {
   return [
     // Domains
     ...createRuleset(

+ 8 - 1
Build/build-telegram-cidr.ts

@@ -7,8 +7,9 @@ import { processLine } from './lib/process-line';
 import { createRuleset } from './lib/create-file';
 import { task } from './lib/trace-runner';
 import { SHARED_DESCRIPTION } from './lib/constants';
+import { createMemoizedPromise } from './lib/memo-promise';
 
-export const buildTelegramCIDR = task(import.meta.path, async () => {
+export const getTelegramCIDRPromise = createMemoizedPromise(async () => {
   const resp = await fetchWithRetry('https://core.telegram.org/resources/cidr.txt', defaultRequestInit) as Response;
   const lastModified = resp.headers.get('last-modified');
   const date = lastModified ? new Date(lastModified) : new Date();
@@ -28,6 +29,12 @@ export const buildTelegramCIDR = task(import.meta.path, async () => {
     }
   }
 
+  return { date, results };
+});
+
+export const buildTelegramCIDR = task(import.meta.path, async () => {
+  const { date, results } = await getTelegramCIDRPromise();
+
   if (results.length === 0) {
     throw new Error('Failed to fetch data!');
   }

+ 8 - 1
Build/index.ts

@@ -14,6 +14,8 @@ import { buildStreamService } from './build-stream-service';
 import { buildRedirectModule } from './build-redirect-module';
 import { validate } from './validate-domainset';
 
+import { buildSSPanelUIMAppProfile } from './build-sspanel-appprofile';
+
 import { buildPublic } from './build-public';
 // import type { TaskResult } from './lib/trace-runner';
 
@@ -64,6 +66,10 @@ import { buildPublic } from './build-public';
     const buildRedirectModulePromise = downloadPreviousBuildPromise.then(() => buildRedirectModule());
     const buildStreamServicePromise = downloadPreviousBuildPromise.then(() => buildStreamService());
 
+    const buildSSPanelUIMAppProfilePromise = Promise.all([
+      downloadPreviousBuildPromise
+    ]).then(() => buildSSPanelUIMAppProfile());
+
     const stats = await Promise.all([
       downloadPreviousBuildPromise,
       downloadPublicSuffixListPromise,
@@ -80,7 +86,8 @@ import { buildPublic } from './build-public';
       // buildInternalChnDomainsPromise,
       buildDomesticRulesetPromise,
       buildRedirectModulePromise,
-      buildStreamServicePromise
+      buildStreamServicePromise,
+      buildSSPanelUIMAppProfilePromise
     ]);
 
     await Promise.all([

+ 7 - 0
Build/lib/memo-promise.ts

@@ -0,0 +1,7 @@
+export const createMemoizedPromise = <T>(fn: () => Promise<T>): () => Promise<T> => {
+  let promise: Promise<T> | null = null;
+  return () => {
+    promise ||= fn();
+    return promise;
+  };
+};

+ 76 - 0
Source/non_ip/internal_microsoft.conf

@@ -0,0 +1,76 @@
+# $ custom_build_script
+
+DOMAIN,officecdn-microsoft-com.akamaized.net
+DOMAIN-SUFFIX,aadrm.com
+DOMAIN-SUFFIX,acompli.com
+DOMAIN-SUFFIX,acompli.net
+DOMAIN-SUFFIX,aka.ms
+DOMAIN-SUFFIX,akadns.net
+DOMAIN-SUFFIX,aspnetcdn.com
+DOMAIN-SUFFIX,assets-yammer.com
+DOMAIN-SUFFIX,azure.com
+DOMAIN-SUFFIX,azure.net
+DOMAIN-SUFFIX,azureedge.net
+DOMAIN-SUFFIX,azurerms.com
+DOMAIN-SUFFIX,bing.com
+DOMAIN-SUFFIX,cloudapp.net
+DOMAIN-SUFFIX,cloudappsecurity.com
+DOMAIN-SUFFIX,edgesuite.net
+DOMAIN-SUFFIX,gfx.ms
+DOMAIN-SUFFIX,hotmail.com
+DOMAIN-SUFFIX,live.com
+DOMAIN-SUFFIX,live.net
+DOMAIN-SUFFIX,lync.com
+DOMAIN-SUFFIX,msappproxy.net
+DOMAIN-SUFFIX,msauth.net
+DOMAIN-SUFFIX,msauthimages.net
+DOMAIN-SUFFIX,msecnd.net
+DOMAIN-SUFFIX,msedge.net
+DOMAIN-SUFFIX,msft.net
+DOMAIN-SUFFIX,msftauth.net
+DOMAIN-SUFFIX,msftauthimages.net
+DOMAIN-SUFFIX,msftidentity.com
+DOMAIN-SUFFIX,msidentity.com
+DOMAIN-SUFFIX,msn.com
+DOMAIN-SUFFIX,msocdn.com
+DOMAIN-SUFFIX,msocsp.com
+DOMAIN-SUFFIX,mstea.ms
+DOMAIN-SUFFIX,o365weve.com
+DOMAIN-SUFFIX,oaspapps.com
+DOMAIN-SUFFIX,office.com
+DOMAIN-SUFFIX,office.net
+DOMAIN-SUFFIX,office365.com
+DOMAIN-SUFFIX,officeppe.net
+DOMAIN-SUFFIX,omniroot.com
+DOMAIN-SUFFIX,onedrive.com
+DOMAIN-SUFFIX,onenote.com
+DOMAIN-SUFFIX,onenote.net
+DOMAIN-SUFFIX,onestore.ms
+DOMAIN-SUFFIX,outlook.com
+DOMAIN-SUFFIX,outlookmobile.com
+DOMAIN-SUFFIX,phonefactor.net
+DOMAIN-SUFFIX,public-trust.com
+DOMAIN-SUFFIX,sfbassets.com
+DOMAIN-SUFFIX,sfx.ms
+DOMAIN-SUFFIX,sharepoint.com
+DOMAIN-SUFFIX,sharepointonline.com
+DOMAIN-SUFFIX,skype.com
+DOMAIN-SUFFIX,skypeassets.com
+DOMAIN-SUFFIX,skypeforbusiness.com
+DOMAIN-SUFFIX,staffhub.ms
+DOMAIN-SUFFIX,svc.ms
+DOMAIN-SUFFIX,sway-cdn.com
+DOMAIN-SUFFIX,sway-extensions.com
+DOMAIN-SUFFIX,sway.com
+DOMAIN-SUFFIX,trafficmanager.net
+DOMAIN-SUFFIX,uservoice.com
+DOMAIN-SUFFIX,virtualearth.net
+DOMAIN-SUFFIX,visualstudio.com
+DOMAIN-SUFFIX,windows-ppe.net
+DOMAIN-SUFFIX,windows.com
+DOMAIN-SUFFIX,windows.net
+DOMAIN-SUFFIX,windowsazure.com
+DOMAIN-SUFFIX,windowsupdate.com
+DOMAIN-SUFFIX,wunderlist.com
+DOMAIN-SUFFIX,yammer.com
+DOMAIN-SUFFIX,yammerusercontent.com