electron
This commit is contained in:
@@ -2,51 +2,33 @@ import sharp from 'sharp';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import png2icons from 'png2icons';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = path.resolve(__dirname, '..');
|
||||
const sourceIcon = path.join(rootDir, 'appicon-square.png');
|
||||
|
||||
// Tauri desktop icons
|
||||
const tauriIcons = [
|
||||
// Electron desktop icons
|
||||
const electronIcons = [
|
||||
{ name: '16x16.png', size: 16 },
|
||||
{ name: '24x24.png', size: 24 },
|
||||
{ name: '32x32.png', size: 32 },
|
||||
{ name: '48x48.png', size: 48 },
|
||||
{ name: '64x64.png', size: 64 },
|
||||
{ name: '128x128.png', size: 128 },
|
||||
{ name: '128x128@2x.png', size: 256 },
|
||||
{ name: '256x256.png', size: 256 },
|
||||
{ name: '512x512.png', size: 512 },
|
||||
{ name: '1024x1024.png', size: 1024 },
|
||||
{ name: 'icon.png', size: 512 },
|
||||
{ name: 'Square30x30Logo.png', size: 30 },
|
||||
{ name: 'Square44x44Logo.png', size: 44 },
|
||||
{ name: 'Square71x71Logo.png', size: 71 },
|
||||
{ name: 'Square89x89Logo.png', size: 89 },
|
||||
{ name: 'Square107x107Logo.png', size: 107 },
|
||||
{ name: 'Square142x142Logo.png', size: 142 },
|
||||
{ name: 'Square150x150Logo.png', size: 150 },
|
||||
{ name: 'Square284x284Logo.png', size: 284 },
|
||||
{ name: 'Square310x310Logo.png', size: 310 },
|
||||
{ name: 'StoreLogo.png', size: 50 },
|
||||
];
|
||||
|
||||
// Android mipmap sizes
|
||||
const androidMipmaps = [
|
||||
{ folder: 'mipmap-mdpi', size: 48 },
|
||||
{ folder: 'mipmap-hdpi', size: 72 },
|
||||
{ folder: 'mipmap-xhdpi', size: 96 },
|
||||
{ folder: 'mipmap-xxhdpi', size: 144 },
|
||||
{ folder: 'mipmap-xxxhdpi', size: 192 },
|
||||
];
|
||||
|
||||
// Android adaptive icon foreground sizes (with padding for safe zone)
|
||||
const androidForegroundSizes = [
|
||||
{ folder: 'mipmap-mdpi', size: 108 },
|
||||
{ folder: 'mipmap-hdpi', size: 162 },
|
||||
{ folder: 'mipmap-xhdpi', size: 216 },
|
||||
{ folder: 'mipmap-xxhdpi', size: 324 },
|
||||
{ folder: 'mipmap-xxxhdpi', size: 432 },
|
||||
];
|
||||
|
||||
async function generateTauriIcons() {
|
||||
const iconsDir = path.join(rootDir, 'src-tauri', 'icons');
|
||||
async function generateElectronIcons() {
|
||||
const iconsDir = path.join(rootDir, 'icons');
|
||||
|
||||
for (const icon of tauriIcons) {
|
||||
// Ensure icons directory exists
|
||||
await fs.mkdir(iconsDir, { recursive: true });
|
||||
|
||||
for (const icon of electronIcons) {
|
||||
const outputPath = path.join(iconsDir, icon.name);
|
||||
await sharp(sourceIcon)
|
||||
.resize(icon.size, icon.size)
|
||||
@@ -56,97 +38,26 @@ async function generateTauriIcons() {
|
||||
}
|
||||
|
||||
// Generate ICO file (Windows)
|
||||
const icoPath = path.join(iconsDir, 'icon.ico');
|
||||
const sizes = [16, 24, 32, 48, 64, 128, 256];
|
||||
const icoBuffers = await Promise.all(
|
||||
sizes.map(size =>
|
||||
sharp(sourceIcon)
|
||||
.resize(size, size)
|
||||
.png()
|
||||
.toBuffer()
|
||||
)
|
||||
);
|
||||
// For ICO, we'll just use the 256x256 as the main icon
|
||||
await sharp(sourceIcon)
|
||||
.resize(256, 256)
|
||||
.png()
|
||||
.toFile(icoPath.replace('.ico', '.ico.png'));
|
||||
// Copy as ico (sharp doesn't support ico directly, need to use png2ico or similar)
|
||||
console.log('Note: ICO file needs manual conversion or use png2ico tool');
|
||||
|
||||
// Generate ICNS file (macOS) - sharp doesn't support icns directly
|
||||
console.log('Note: ICNS file needs manual conversion or use iconutil on macOS');
|
||||
}
|
||||
const sourceBuffer = await fs.readFile(sourceIcon);
|
||||
const icoBuffer = png2icons.createICO(sourceBuffer, png2icons.BEZIER, 0, true, true);
|
||||
await fs.writeFile(path.join(iconsDir, 'icon.ico'), icoBuffer);
|
||||
console.log('Generated: icon.ico');
|
||||
|
||||
async function generateAndroidIcons() {
|
||||
const androidResDir = path.join(rootDir, 'src-tauri', 'gen', 'android', 'app', 'src', 'main', 'res');
|
||||
|
||||
for (const mipmap of androidMipmaps) {
|
||||
const outputDir = path.join(androidResDir, mipmap.folder);
|
||||
|
||||
// Standard launcher icon
|
||||
await sharp(sourceIcon)
|
||||
.resize(mipmap.size, mipmap.size)
|
||||
.png()
|
||||
.toFile(path.join(outputDir, 'ic_launcher.png'));
|
||||
console.log(`Generated: ${mipmap.folder}/ic_launcher.png`);
|
||||
|
||||
// Round launcher icon
|
||||
const roundSize = mipmap.size;
|
||||
const roundIcon = await sharp(sourceIcon)
|
||||
.resize(roundSize, roundSize)
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
// Create circular mask
|
||||
const circleMask = Buffer.from(
|
||||
`<svg><circle cx="${roundSize/2}" cy="${roundSize/2}" r="${roundSize/2}" fill="white"/></svg>`
|
||||
);
|
||||
|
||||
await sharp(roundIcon)
|
||||
.composite([{
|
||||
input: circleMask,
|
||||
blend: 'dest-in'
|
||||
}])
|
||||
.png()
|
||||
.toFile(path.join(outputDir, 'ic_launcher_round.png'));
|
||||
console.log(`Generated: ${mipmap.folder}/ic_launcher_round.png`);
|
||||
}
|
||||
|
||||
// Generate adaptive icon foregrounds
|
||||
for (const fg of androidForegroundSizes) {
|
||||
const outputDir = path.join(androidResDir, fg.folder);
|
||||
const iconSize = Math.floor(fg.size * 0.66); // Icon should be ~66% of the foreground
|
||||
const padding = Math.floor((fg.size - iconSize) / 2);
|
||||
|
||||
await sharp(sourceIcon)
|
||||
.resize(iconSize, iconSize)
|
||||
.extend({
|
||||
top: padding,
|
||||
bottom: fg.size - iconSize - padding,
|
||||
left: padding,
|
||||
right: fg.size - iconSize - padding,
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
})
|
||||
.png()
|
||||
.toFile(path.join(outputDir, 'ic_launcher_foreground.png'));
|
||||
console.log(`Generated: ${fg.folder}/ic_launcher_foreground.png`);
|
||||
}
|
||||
// Generate ICNS file (macOS)
|
||||
const icnsBuffer = png2icons.createICNS(sourceBuffer, png2icons.BEZIER, 0);
|
||||
await fs.writeFile(path.join(iconsDir, 'icon.icns'), icnsBuffer);
|
||||
console.log('Generated: icon.icns');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Generating icons from:', sourceIcon);
|
||||
console.log('');
|
||||
|
||||
console.log('=== Tauri Desktop Icons ===');
|
||||
await generateTauriIcons();
|
||||
console.log('=== Electron Desktop Icons ===');
|
||||
await generateElectronIcons();
|
||||
console.log('');
|
||||
|
||||
console.log('=== Android Icons ===');
|
||||
await generateAndroidIcons();
|
||||
console.log('');
|
||||
|
||||
console.log('Done! Note: ICO and ICNS files may need manual conversion.');
|
||||
console.log('Done!');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import fetch from "node-fetch";
|
||||
import { getOctokit, context } from "@actions/github";
|
||||
|
||||
async function getAssetSign(url) {
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
});
|
||||
|
||||
return response.text();
|
||||
}
|
||||
|
||||
async function createTauriRelease() {
|
||||
if (process.env.GITHUB_TOKEN === undefined) {
|
||||
throw new Error("GITHUB_TOKEN is not found!");
|
||||
}
|
||||
|
||||
const github = getOctokit(process.env.GITHUB_TOKEN);
|
||||
const { repos } = github.rest;
|
||||
const repoMetaData = {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
};
|
||||
|
||||
const tagsResult = await repos.listTags({ ...repoMetaData, per_page: 10, page: 1 });
|
||||
const latestTag = tagsResult.data.find((tag) => tag.name.startsWith("v"));
|
||||
console.log(latestTag);
|
||||
|
||||
const latestRelease = await repos.getReleaseByTag({ ...repoMetaData, tag: latestTag.name });
|
||||
const latestAssets = latestRelease.data.assets;
|
||||
|
||||
const windowsX86_64 = {};
|
||||
const linuxX86_64 = {};
|
||||
const darwinX86_64 = {};
|
||||
const darwinAarch64 = {};
|
||||
|
||||
const promises = latestAssets.map(async (asset) => {
|
||||
const { name, browser_download_url } = asset;
|
||||
|
||||
if (/\.msi\.zip$/.test(name)) {
|
||||
windowsX86_64.url = browser_download_url;
|
||||
}
|
||||
if (/\.msi\.zip\.sig$/.test(name)) {
|
||||
windowsX86_64.signature = await getAssetSign(browser_download_url);
|
||||
}
|
||||
|
||||
if (/\.AppImage\.tar\.gz$/.test(name)) {
|
||||
linuxX86_64.url = browser_download_url;
|
||||
}
|
||||
if (/\.AppImage\.tar\.gz\.sig$/.test(name)) {
|
||||
linuxX86_64.signature = await getAssetSign(browser_download_url);
|
||||
}
|
||||
|
||||
if (/universal\.app\.tar\.gz$/.test(name)) {
|
||||
darwinX86_64.url = browser_download_url;
|
||||
}
|
||||
if (/universal\.app\.tar\.gz\.sig$/.test(name)) {
|
||||
darwinX86_64.signature = await getAssetSign(browser_download_url);
|
||||
}
|
||||
|
||||
if (/universal\.app\.tar\.gz$/.test(name)) {
|
||||
darwinAarch64.url = browser_download_url;
|
||||
}
|
||||
if (/universal\.app\.tar\.gz\.sig$/.test(name)) {
|
||||
darwinAarch64.signature = await getAssetSign(browser_download_url);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
|
||||
const releaseData = {
|
||||
name: latestTag.name,
|
||||
notes: `https://github.com/${repoMetaData.owner}/${repoMetaData.repo}/releases/tag/${latestTag.name}`,
|
||||
pub_date: new Date().toISOString(),
|
||||
platforms: {},
|
||||
};
|
||||
|
||||
if (windowsX86_64.url) releaseData.platforms["windows-x86_64"] = windowsX86_64;
|
||||
else console.error('Failed to get release for windowsX86_64');
|
||||
|
||||
if (linuxX86_64.url) releaseData.platforms["linux-x86_64"] = linuxX86_64;
|
||||
else console.error('Failed to get release for linuxX86_64');
|
||||
|
||||
if (darwinX86_64.url) releaseData.platforms["darwin-x86_64"] = darwinX86_64;
|
||||
else console.error('Failed to get release for darwinX86_64');
|
||||
|
||||
if (darwinAarch64.url) releaseData.platforms["darwin-aarch64"] = darwinAarch64;
|
||||
else console.error('Failed to get release for darwinAarch64');
|
||||
|
||||
const releaseResult = await repos.getReleaseByTag({ ...repoMetaData, tag: 'tauri' });
|
||||
const tauriRelease = releaseResult.data;
|
||||
|
||||
const prevReleaseAsset = tauriRelease.assets.find((asset) => asset.name === 'release.json');
|
||||
if (prevReleaseAsset) {
|
||||
await repos.deleteReleaseAsset({ ...repoMetaData, asset_id: prevReleaseAsset.id });
|
||||
}
|
||||
|
||||
console.log(releaseData);
|
||||
await repos.uploadReleaseAsset({
|
||||
...repoMetaData,
|
||||
release_id: tauriRelease.id,
|
||||
name: 'release.json',
|
||||
data: JSON.stringify(releaseData, null, 2),
|
||||
});
|
||||
}
|
||||
createTauriRelease();
|
||||
Reference in New Issue
Block a user