[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
193
scripts/sync-docker-icons.mjs
Normal file
193
scripts/sync-docker-icons.mjs
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Manual one-off sync — NOT wired into build or pack.
|
||||
* node scripts/sync-docker-icons.mjs
|
||||
*
|
||||
* Generates colored SVGs (Simple Icons, CC0) plus a few official brand assets
|
||||
* into public/docker-icons/, then writes domain/systemManager/dockerIconBundled.ts.
|
||||
*/
|
||||
import { mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as simpleIcons from 'simple-icons';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = join(__dirname, '..');
|
||||
const SRC_FILE = join(ROOT, 'domain/systemManager/dockerImageIcons.ts');
|
||||
const OUT_DIR = join(ROOT, 'public/docker-icons');
|
||||
const MANIFEST_FILE = join(ROOT, 'domain/systemManager/dockerIconBundled.ts');
|
||||
|
||||
/**
|
||||
* Official assets not covered by Simple Icons — see public/docker-icons/NOTICE.md
|
||||
* Prefer SVG when the upstream project publishes one; PNG only when no SVG exists.
|
||||
*/
|
||||
const OFFICIAL_ICONS = [
|
||||
{
|
||||
iconId: 'nacos',
|
||||
file: 'nacos.svg',
|
||||
url: 'https://raw.githubusercontent.com/nacos-group/nacos-logo/master/Nacos%20logo%20%E7%99%BD%E8%93%9D%E8%B5%84%E6%BA%90%205.svg',
|
||||
},
|
||||
{
|
||||
iconId: 'polaris',
|
||||
file: 'polaris.svg',
|
||||
url: 'https://raw.githubusercontent.com/polarismesh/polaris/main/logo.svg',
|
||||
},
|
||||
{
|
||||
iconId: 'memcached',
|
||||
file: 'memcached.png',
|
||||
url: 'https://memcached.org/images/memcached_link_125.png',
|
||||
},
|
||||
];
|
||||
|
||||
const slugById = parseStringRecord(SRC_FILE, 'SIMPLE_ICONS_SLUG');
|
||||
const styleById = parseTileStyles(SRC_FILE);
|
||||
|
||||
const iconsBySlug = new Map();
|
||||
for (const icon of Object.values(simpleIcons)) {
|
||||
if (icon && typeof icon === 'object' && 'slug' in icon && 'path' in icon) {
|
||||
iconsBySlug.set(icon.slug, icon);
|
||||
}
|
||||
}
|
||||
|
||||
mkdirSync(OUT_DIR, { recursive: true });
|
||||
|
||||
const bundled = [];
|
||||
const iconFiles = {};
|
||||
const skipped = [];
|
||||
|
||||
for (const [iconId, slug] of Object.entries(slugById)) {
|
||||
const brand = iconsBySlug.get(slug);
|
||||
if (!brand) {
|
||||
skipped.push({ iconId, slug, reason: 'slug not in simple-icons' });
|
||||
continue;
|
||||
}
|
||||
|
||||
const iconColor = styleById[iconId]?.iconColor ?? 'ffffff';
|
||||
const fill = iconColor.startsWith('#') ? iconColor : `#${iconColor}`;
|
||||
const file = `${iconId}.svg`;
|
||||
|
||||
const svg = [
|
||||
`<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">`,
|
||||
` <title>${escapeXml(brand.title)}</title>`,
|
||||
` <path d="${brand.path}" fill="${fill}"/>`,
|
||||
`</svg>`,
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
writeFileSync(join(OUT_DIR, file), svg, 'utf8');
|
||||
bundled.push(iconId);
|
||||
iconFiles[iconId] = file;
|
||||
}
|
||||
|
||||
for (const entry of OFFICIAL_ICONS) {
|
||||
const response = await fetch(entry.url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to download ${entry.iconId} from ${entry.url}: ${response.status}`);
|
||||
}
|
||||
const buffer = Buffer.from(await response.arrayBuffer());
|
||||
writeFileSync(join(OUT_DIR, entry.file), buffer);
|
||||
bundled.push(entry.iconId);
|
||||
iconFiles[entry.iconId] = entry.file;
|
||||
console.log(`Downloaded official ${entry.iconId} -> ${entry.file}`);
|
||||
}
|
||||
|
||||
const bundledSet = new Set(bundled);
|
||||
for (const file of readdirSync(OUT_DIR)) {
|
||||
if (file === 'NOTICE.md') continue;
|
||||
const iconId = file.replace(/\.(svg|png|webp)$/i, '');
|
||||
const owned =
|
||||
bundledSet.has(iconId) && iconFiles[iconId] === file;
|
||||
if (!owned) {
|
||||
unlinkSync(join(OUT_DIR, file));
|
||||
console.log(`Removed stale ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
bundled.sort();
|
||||
const fileEntries = bundled
|
||||
.filter((id) => iconFiles[id] !== `${id}.svg`)
|
||||
.map((id) => ` '${id}': '${iconFiles[id]}',`);
|
||||
|
||||
writeFileSync(
|
||||
MANIFEST_FILE,
|
||||
[
|
||||
'// Auto-generated by scripts/sync-docker-icons.mjs — do not edit.',
|
||||
'export const BUNDLED_DOCKER_ICON_IDS = new Set<string>([',
|
||||
...bundled.map((id) => ` '${id}',`),
|
||||
']);',
|
||||
'',
|
||||
'/** Non-default filenames (e.g. official PNG assets). Default is `${id}.svg`. */',
|
||||
'export const DOCKER_ICON_FILES: Record<string, string> = {',
|
||||
...fileEntries,
|
||||
'};',
|
||||
'',
|
||||
].join('\n'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
console.log(`Wrote ${bundled.length} icons to public/docker-icons/`);
|
||||
console.log(`Updated ${MANIFEST_FILE}`);
|
||||
|
||||
if (skipped.length > 0) {
|
||||
console.log(`Skipped ${skipped.length} (no official Simple Icons slug):`);
|
||||
for (const item of skipped) {
|
||||
console.log(` ${item.iconId} -> ${item.slug}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseStringRecord(filePath, constName) {
|
||||
const src = readFileSync(filePath, 'utf8');
|
||||
const start = src.indexOf(`const ${constName}`);
|
||||
if (start < 0) throw new Error(`Could not find ${constName} in ${filePath}`);
|
||||
|
||||
const braceStart = src.indexOf('{', start);
|
||||
const braceEnd = findMatchingBrace(src, braceStart);
|
||||
const block = src.slice(braceStart + 1, braceEnd);
|
||||
|
||||
const out = {};
|
||||
for (const line of block.split('\n')) {
|
||||
const match = line.match(/^\s*([a-zA-Z0-9_]+):\s*'([^']*)',?\s*$/);
|
||||
if (match) out[match[1]] = match[2];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function parseTileStyles(filePath) {
|
||||
const src = readFileSync(filePath, 'utf8');
|
||||
const start = src.indexOf('const ICON_TILE_STYLE');
|
||||
if (start < 0) throw new Error(`Could not find ICON_TILE_STYLE in ${filePath}`);
|
||||
|
||||
const braceStart = src.indexOf('{', start);
|
||||
const braceEnd = findMatchingBrace(src, braceStart);
|
||||
const block = src.slice(braceStart + 1, braceEnd);
|
||||
|
||||
const out = {};
|
||||
const re =
|
||||
/^\s*([a-zA-Z0-9_]+):\s*\{\s*background:\s*'([^']*)',\s*iconColor:\s*'([^']*)'\s*\},?\s*$/;
|
||||
for (const line of block.split('\n')) {
|
||||
const match = line.match(re);
|
||||
if (match) {
|
||||
out[match[1]] = { background: match[2], iconColor: match[3] };
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function findMatchingBrace(src, openIndex) {
|
||||
let depth = 0;
|
||||
for (let i = openIndex; i < src.length; i += 1) {
|
||||
if (src[i] === '{') depth += 1;
|
||||
else if (src[i] === '}') {
|
||||
depth -= 1;
|
||||
if (depth === 0) return i;
|
||||
}
|
||||
}
|
||||
throw new Error('Unbalanced braces while parsing dockerImageIcons.ts');
|
||||
}
|
||||
|
||||
function escapeXml(value) {
|
||||
return value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
Reference in New Issue
Block a user