mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
Add Bonfida import fix script and improve release workflow
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env node
|
||||
// Fix broken ESM imports in @bonfida/spl-name-service
|
||||
// The package ships with hardcoded ../node_modules/ paths that break in electron asar
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = path.join(__dirname, '..');
|
||||
const bonfidaEsmDir = path.join(rootDir, 'node_modules/@bonfida/spl-name-service/dist/esm');
|
||||
const bonfidaCjsDir = path.join(rootDir, 'node_modules/@bonfida/spl-name-service/dist/cjs');
|
||||
|
||||
// Map of known packages to their correct import paths
|
||||
const packageMappings = {
|
||||
'@noble/hashes': (subpath) => {
|
||||
// ../node_modules/@noble/hashes/esm/sha256.js -> @noble/hashes/sha256
|
||||
if (subpath.includes('/sha256')) return '@noble/hashes/sha256';
|
||||
if (subpath.includes('/sha512')) return '@noble/hashes/sha512';
|
||||
return '@noble/hashes';
|
||||
},
|
||||
'@noble/curves': (subpath) => {
|
||||
// ../node_modules/@noble/curves/esm/ed25519.js -> @noble/curves/ed25519
|
||||
if (subpath.includes('/ed25519')) return '@noble/curves/ed25519';
|
||||
if (subpath.includes('/secp256k1')) return '@noble/curves/secp256k1';
|
||||
return '@noble/curves';
|
||||
},
|
||||
'@solana/spl-token': () => '@solana/spl-token',
|
||||
'@solana/web3.js': () => '@solana/web3.js',
|
||||
'@bonfida/sns-records': () => '@bonfida/sns-records',
|
||||
'@scure/base': () => '@scure/base',
|
||||
buffer: () => 'buffer',
|
||||
borsh: () => 'borsh',
|
||||
graphemesplit: () => 'graphemesplit',
|
||||
bs58: () => 'bs58',
|
||||
'ipaddr.js': () => 'ipaddr.js',
|
||||
punycode: () => 'punycode',
|
||||
};
|
||||
|
||||
// Generic function to fix node_modules imports
|
||||
function fixImportsInContent(content) {
|
||||
// Match patterns like from"../node_modules/PACKAGE/..." or from"./node_modules/PACKAGE/..."
|
||||
const importPattern = /from\s*["'](\.\.?\/node_modules\/((?:@[^\/]+\/[^\/]+|[^\/]+))([^"']*))["']/g;
|
||||
const sideEffectPattern = /import\s*["'](\.\.?\/node_modules\/((?:@[^\/]+\/[^\/]+|[^\/]+))([^"']*))["']/g;
|
||||
const requirePattern = /require\s*\(\s*["'](\.\.?\/node_modules\/((?:@[^\/]+\/[^\/]+|[^\/]+))([^"']*))["']\s*\)/g;
|
||||
|
||||
let modified = false;
|
||||
|
||||
const replaceImport = (match, fullPath, packageName, subpath) => {
|
||||
modified = true;
|
||||
const mapper = packageMappings[packageName];
|
||||
const correctImport = mapper ? mapper(subpath) : packageName;
|
||||
return `from"${correctImport}"`;
|
||||
};
|
||||
|
||||
const replaceSideEffect = (match, fullPath, packageName, subpath) => {
|
||||
modified = true;
|
||||
const mapper = packageMappings[packageName];
|
||||
const correctImport = mapper ? mapper(subpath) : packageName;
|
||||
return `import"${correctImport}"`;
|
||||
};
|
||||
|
||||
const replaceRequire = (match, fullPath, packageName, subpath) => {
|
||||
modified = true;
|
||||
const mapper = packageMappings[packageName];
|
||||
const correctImport = mapper ? mapper(subpath) : packageName;
|
||||
return `require("${correctImport}")`;
|
||||
};
|
||||
|
||||
let result = content;
|
||||
result = result.replace(importPattern, replaceImport);
|
||||
result = result.replace(sideEffectPattern, replaceSideEffect);
|
||||
result = result.replace(requirePattern, replaceRequire);
|
||||
|
||||
return { content: result, modified };
|
||||
}
|
||||
|
||||
function fixFile(filePath) {
|
||||
if (!fs.existsSync(filePath)) return false;
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const result = fixImportsInContent(content);
|
||||
|
||||
if (result.modified) {
|
||||
fs.writeFileSync(filePath, result.content);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function fixDirectory(dir) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
console.log(`Directory not found: ${dir}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let fixedCount = 0;
|
||||
|
||||
function walk(currentDir) {
|
||||
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(currentDir, entry.name);
|
||||
|
||||
if (entry.isDirectory() && entry.name !== 'node_modules') {
|
||||
walk(fullPath);
|
||||
} else if (entry.isFile() && /\.(m?js|cjs)$/.test(entry.name)) {
|
||||
if (fixFile(fullPath)) {
|
||||
fixedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(dir);
|
||||
return fixedCount;
|
||||
}
|
||||
|
||||
console.log('Fixing @bonfida/spl-name-service broken imports...');
|
||||
|
||||
const esmFixed = fixDirectory(bonfidaEsmDir);
|
||||
const cjsFixed = fixDirectory(bonfidaCjsDir);
|
||||
|
||||
console.log(`Fixed ${esmFixed} ESM files and ${cjsFixed} CJS files`);
|
||||
|
||||
// Also remove the nested node_modules that are no longer needed
|
||||
const nestedEsm = path.join(bonfidaEsmDir, 'node_modules');
|
||||
const nestedCjs = path.join(bonfidaCjsDir, 'node_modules');
|
||||
|
||||
if (fs.existsSync(nestedEsm)) {
|
||||
fs.rmSync(nestedEsm, { recursive: true });
|
||||
console.log('Removed nested ESM node_modules');
|
||||
}
|
||||
|
||||
if (fs.existsSync(nestedCjs)) {
|
||||
fs.rmSync(nestedCjs, { recursive: true });
|
||||
console.log('Removed nested CJS node_modules');
|
||||
}
|
||||
|
||||
console.log('Done!');
|
||||
+106
-22
@@ -1,34 +1,118 @@
|
||||
import {execSync} from 'child_process'
|
||||
import path from 'path'
|
||||
import {fileURLToPath} from 'url'
|
||||
import {readFileSync} from 'fs'
|
||||
import { execSync } from 'child_process';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { readFileSync, readdirSync } from 'fs';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const dirname = path.join(path.dirname(fileURLToPath(import.meta.url)))
|
||||
const conventionalChangelog = path.join(dirname, '..', 'node_modules', '.bin', 'conventional-changelog')
|
||||
const packageJsonPath = path.join(dirname, '..', 'package.json')
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
|
||||
const version = packageJson.version
|
||||
const dirname = path.join(path.dirname(fileURLToPath(import.meta.url)));
|
||||
const conventionalChangelog = path.join(dirname, '..', 'node_modules', '.bin', 'conventional-changelog');
|
||||
const version = JSON.parse(readFileSync(path.join(dirname, '..', 'package.json'), 'utf8')).version;
|
||||
|
||||
// sometimes release-count 1 is empty
|
||||
let releaseChangelog =
|
||||
execSync(`${conventionalChangelog} --preset angular --release-count 1`).toString() ||
|
||||
execSync(`${conventionalChangelog} --preset angular --release-count 2`).toString()
|
||||
// changelog (use last non-empty)
|
||||
let releaseChangelog =
|
||||
execSync(`${conventionalChangelog} --preset angular --release-count 1`).toString() ||
|
||||
execSync(`${conventionalChangelog} --preset angular --release-count 2`).toString();
|
||||
releaseChangelog = releaseChangelog.trim().replace(/\n\n+/g, '\n\n');
|
||||
|
||||
// format
|
||||
releaseChangelog = releaseChangelog.trim().replace(/\n\n+/g, '\n\n')
|
||||
// discover artifacts
|
||||
const distDir = path.join(dirname, '..', 'dist');
|
||||
let files = [];
|
||||
try {
|
||||
files = readdirSync(distDir);
|
||||
} catch {}
|
||||
|
||||
// In CI finalization, dist is empty. Prefer GitHub release assets when available.
|
||||
const getReleaseAssetNames = async () => {
|
||||
try {
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
const repo = process.env.GITHUB_REPOSITORY; // owner/repo
|
||||
const tag = process.env.GITHUB_REF_NAME || `v${version}`;
|
||||
if (!token || !repo || !tag) return [];
|
||||
const res = await fetch(`https://api.github.com/repos/${repo}/releases/tags/${tag}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'User-Agent': '5chan-release-notes',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
Accept: 'application/vnd.github+json',
|
||||
},
|
||||
});
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json();
|
||||
return (data.assets || []).map((a) => a.name).filter(Boolean);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const remoteFiles = await getReleaseAssetNames();
|
||||
if (remoteFiles.length) {
|
||||
files = remoteFiles;
|
||||
}
|
||||
|
||||
const linkTo = (file) => `https://github.com/plebbit/5chan/releases/download/v${version}/${file}`;
|
||||
const has = (s, sub) => s.toLowerCase().includes(sub);
|
||||
const isArm = (s) => has(s, 'arm64') || has(s, 'aarch64');
|
||||
const isX64 = (s) => has(s, 'x64') || has(s, 'x86_64') || !isArm(s);
|
||||
|
||||
// buckets
|
||||
const androidApk = files.find((f) => f.endsWith('.apk'));
|
||||
const htmlZip = files.find((f) => f.includes('5chan-html') && f.endsWith('.zip'));
|
||||
|
||||
const linux = files.filter((f) => f.endsWith('.AppImage'));
|
||||
const mac = files.filter((f) => f.endsWith('.dmg'));
|
||||
const win = files.filter((f) => f.toLowerCase().endsWith('.exe'));
|
||||
|
||||
const linuxArm = linux.find(isArm);
|
||||
const linuxX64 = linux.find(isX64);
|
||||
const macArm = mac.find(isArm);
|
||||
const macX64 = mac.find(isX64);
|
||||
|
||||
const winSetupX64 = win.find((f) => has(f, 'setup') && isX64(f));
|
||||
const winPortableX64 = win.find((f) => has(f, 'portable') && isX64(f));
|
||||
|
||||
// small section builder without push()
|
||||
const section = (title, lines) => {
|
||||
const body = lines.filter(Boolean).join('\n');
|
||||
return body ? `### ${title}\n${body}` : '';
|
||||
};
|
||||
|
||||
const macSection = section('macOS', [
|
||||
macArm && `- Apple Silicon (arm64): [Download DMG](${linkTo(macArm)})`,
|
||||
macX64 && `- Intel (x64): [Download DMG](${linkTo(macX64)})`,
|
||||
mac.length > 0 &&
|
||||
`- If macOS shows "5chan.app is damaged and can't be opened. You should move it to the Trash.", run this in Terminal to fix it: \`xattr -dr com.apple.quarantine "/Applications/5chan.app"\` then open the app again.`,
|
||||
]);
|
||||
|
||||
const winSection = section('Windows', [
|
||||
winSetupX64 && `- Installer (x64): [Download EXE](${linkTo(winSetupX64)})`,
|
||||
winPortableX64 && `- Portable (x64): [Download EXE](${linkTo(winPortableX64)})`,
|
||||
win.length > 0 &&
|
||||
`- If Windows shows "Windows protected your PC" (SmartScreen), click "More info" then "Run anyway". To permanently allow, right-click the .exe → Properties → check "Unblock", or run in PowerShell: \`Unblock-File -Path .\\path\\to\\file.exe\`.`,
|
||||
]);
|
||||
|
||||
const linuxSection = section('Linux', [
|
||||
linuxArm && `- AppImage (arm64): [Download](${linkTo(linuxArm)})`,
|
||||
linuxX64 && `- AppImage (x64): [Download](${linkTo(linuxX64)})`,
|
||||
]);
|
||||
|
||||
const androidSection = section('Android', [androidApk && `- APK: [Download](${linkTo(androidApk)})`]);
|
||||
|
||||
const htmlSection = section('Static HTML build', [htmlZip && `- 5chan-html (zip): [Download](${linkTo(htmlZip)})`]);
|
||||
|
||||
const downloads = [macSection, winSection, linuxSection, androidSection, htmlSection].filter(Boolean).join('\n\n');
|
||||
|
||||
const releaseBody = `This version rebrands the app to 5chan, introducing a set of new features and several performance improvements.
|
||||
|
||||
- Web app: https://5chan.app
|
||||
- Decentralized web app: https://5chan.eth (only works on [Brave Browser](https://brave.com/) or via [IPFS Companion](https://docs.ipfs.tech/install/ipfs-companion/#prerequisites))
|
||||
- Decentralized web app via IPFS/IPNS gateways (works on any browser): [5chan.eth.limo](https://5chan.eth.limo), [5chan.eth.link](https://5chan.eth.link), [dweb.link/ipfs.io](https://dweb.link/ipns/5chan.eth)
|
||||
- Decentralized web app via IPFS node you run: https://5chan.eth (works on [Brave Browser](https://brave.com/) or use [IPFS Companion](https://docs.ipfs.tech/install/ipfs-companion/#prerequisites))
|
||||
|
||||
## Downloads
|
||||
|
||||
- Android app: [Download APK](https://github.com/plebbit/5chan/releases/download/v${version}/5chan-${version}.apk)
|
||||
- Linux app: [Download AppImage](https://github.com/plebbit/5chan/releases/download/v${version}/5chan-${version}.AppImage)
|
||||
- macOS app: [Download DMG](https://github.com/plebbit/5chan/releases/download/v${version}/5chan-${version}.dmg)
|
||||
- Windows app: [Download EXE](https://github.com/plebbit/5chan/releases/download/v${version}/5chan.Setup.${version}.exe)
|
||||
${downloads}
|
||||
|
||||
${releaseChangelog}`
|
||||
## Changes
|
||||
|
||||
console.log(releaseBody)
|
||||
${releaseChangelog}`;
|
||||
|
||||
console.log(releaseBody);
|
||||
|
||||
Reference in New Issue
Block a user