diff --git a/.gitignore b/.gitignore index 81e57cd4f..4fbc468c0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,10 @@ # compiled output dist/ +/build/ElectronInstall/ +/build/FilesToInstall/ +/build/Output/ +/build/NiuNiuCaptureElectronDemo/ out/ /tmp /out-tsc diff --git a/package.json b/package.json index 005680fc9..da52359a9 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { - "name": "postcat", + "name": "Postcat", "souceLocale": "zh-Hans", "version": "0.3.0", "main": "out/app/electron-main/main.js", "description": "A lightweight, extensible API tool", "homepage": "https://github.com/Postcatlab/postcat.git", "author": "Postcat", - "private": true, + "private": true, "workspaces": [ "src/workbench/*" ], @@ -18,6 +18,7 @@ "build": "npx patch-package && npm-run-all -s electron:build:web clear:electron:tsc electron:tsc && npx esno scripts/build.ts", "build:web": "yarn workspace postcat-web run build:web", "build:static": "npm run clear:electron:tsc&&npm run electron:tsc && npx esno scripts/build.ts", + "build:win:noSign": "npm run clear:electron:tsc&&npm run electron:tsc && npx esno scripts/buildNoSign.ts", "electron:build:web": "yarn workspace postcat-web run build", "electron:static": "npm run electron:tsc && electron .", "release": "npm-run-all -s electron:build:web electron:tsc && npx esno scripts/build.ts --publish=always && node upload.js", @@ -30,7 +31,7 @@ "web:start:direct": "yarn workspace postcat-web run start:direct", "version": "conventional-changelog -p angular -i CHANGELOG.md -s -r -0", "lint:lint-staged": "lint-staged", - "wininstaller": "node scripts/beforeNSISBuild.js && cd build && build-nim.bat", + "wininstaller": "cd build && build-for-electron-postcat.bat", "pack:win": "npm run electron:build && npm run wininstaller", "deployWindows": "node scripts/deployWindows.js", "releaseWindows": "npx esno scripts/releaseWindows.ts" @@ -128,4 +129,4 @@ "prettier --write" ] } -} +} \ No newline at end of file diff --git a/scripts/build.ts b/scripts/build.ts index 058d41558..54ed24eae 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -3,6 +3,7 @@ import { build, BuildResult, Platform } from 'electron-builder'; import type { Configuration } from 'electron-builder'; import minimist from 'minimist'; +import pkgInfo from '../package.json'; import { ELETRON_APP_CONFIG } from '../src/environment'; import { execSync, exec, spawn } from 'node:child_process'; @@ -10,6 +11,7 @@ import { copyFileSync, readFileSync, writeFileSync } from 'node:fs'; import path, { resolve } from 'node:path'; import { exit, platform } from 'node:process'; +const pkgPath = path.join(__dirname, '../package.json'); // 当前 postcat 版本 const version = process.env.npm_package_version; // 保存签名时的参数,供签名后面生成的 自定义安装界面 安装包 @@ -61,6 +63,8 @@ const config: Configuration = { ], generateUpdatesFilesForAllChannels: true, nsis: { + // 指定guid,此guid会存放在注册表中,如果没有指定则系统会自动生成 + guid: 'Postcat', oneClick: false, allowElevation: true, allowToChangeInstallationDirectory: true, @@ -114,22 +118,22 @@ const config: Configuration = { icon: 'src/app/common/images/', target: ['AppImage'] } - // https://www.electron.build/configuration/configuration.html#afterallartifactbuild - // afterAllArtifactBuild: async (buildResult: BuildResult) => { - // console.log('buildResult.artifactPaths', buildResult.artifactPaths); - // if (isWin) { - // await signWindows(); - // // https://github.com/electron-userland/electron-builder/issues/4446 - // const latestPath = path.join(__dirname, '../release/latest.yml'); - // const file = readFileSync(latestPath, 'utf8'); - // // @ts-ignore - // writeFileSync(latestPath, file.replaceAll(`Postcat-Setup-${version}.exe`, `Postcat Setup ${version}.exe`)); - // return buildResult.artifactPaths.map(filePath => { - // return filePath.replace(`Postcat Setup ${version}.exe`, `Postcat-Setup-${version}.exe`); - // }); - // } - // return buildResult.artifactPaths; - // } +}; + +// 这里动态往 package.json 中写入 electron-builder 配置,主要是为了给 build-for-electron.bat 脚本读取配置 +const modifyPkgInfo = () => { + // @ts-ignore + pkgInfo.build = config; + writeFileSync(pkgPath, JSON.stringify(pkgInfo, null, 2)); + // 退出进程/意外退出进程 时主动还原 package.json 信息 + process.on('exit', restorePkgInfo); + process.on('uncaughtException', restorePkgInfo); +}; + +const restorePkgInfo = () => { + Reflect.deleteProperty(pkgInfo, 'build'); + // 还原 package.json 文件 + writeFileSync(pkgPath, JSON.stringify(pkgInfo, null, 2)); }; // 要打包的目标平台 @@ -147,26 +151,29 @@ const signWindows = () => { return resolve(true); } - // 给卸载程序签名 - signOptions[0] = { - ...signOptions[0], - path: 'D:\\git\\postcat\\build\\Uninstall Postcat.exe' - }; - await sign(...signOptions); - - copyFileSync( - path.join(__dirname, '../build', 'Uninstall Postcat.exe'), - path.join(__dirname, '../release/win-unpacked', 'Uninstall Postcat.exe') - ); - + modifyPkgInfo(); + // 生成自定义安装包 const ls = spawn('yarn', ['wininstaller'], { // 仅在当前运行环境为 Windows 时,才使用 shell shell: isWin }); ls.stdout.on('data', async data => { - console.log(decoder.decode(data)); - if (decoder.decode(data).includes('请按任意键继续')) { + const logText = decoder.decode(data); + console.log(logText); + + // build/nsis-build-and-sign.bat + if (logText.includes('是时候给 Uninstall Postcat.exe 签名了')) { + signOptions[0] = { + ...signOptions[0], + path: 'D:\\git\\postcat\\release\\Uninstall Postcat.exe' + }; + await sign(...signOptions); + console.log('卸载程序签名完成!'); + } + + // build/build-by-external.bat + if (logText.includes('pack postcat finished!')) { // 给自定义安装包签名 signOptions[0] = { ...signOptions[0], diff --git a/scripts/buildNoSign.ts b/scripts/buildNoSign.ts new file mode 100644 index 000000000..9fa7aa63b --- /dev/null +++ b/scripts/buildNoSign.ts @@ -0,0 +1,174 @@ +import { sign, doSign } from 'app-builder-lib/out/codeSign/windowsCodeSign'; +import { build, BuildResult, Platform } from 'electron-builder'; +import type { Configuration } from 'electron-builder'; +import minimist from 'minimist'; +import YAML from 'yaml'; + +import pkgInfo from '../package.json'; +import { ELETRON_APP_CONFIG } from '../src/environment'; + +import { execSync, exec, spawn } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { copyFileSync, createReadStream, readFileSync, writeFileSync } from 'node:fs'; +import path, { resolve } from 'node:path'; +import { exit, platform } from 'node:process'; + +const pkgPath = path.join(__dirname, '../package.json'); +// 当前 postcat 版本 +const version = process.env.npm_package_version; +// 保存签名时的参数,供签名后面生成的 自定义安装界面 安装包 +let signOptions: Parameters; + +const isWin = process.platform === 'win32'; +// 参数同 electron-builder cli 命令行参数 +const argv = minimist(process.argv.slice(2)); +// https://nodejs.org/docs/latest/api/util.html#util_class_util_textdecoder +const decoder = new TextDecoder('gbk'); + +// 删除 minimist 解析后默认带的 _ 属性,防止 electron-builder 执行报错 +Reflect.deleteProperty(argv, '_'); + +// mac 系统删除 release 目录 +if (process.platform === 'darwin') { + exec(`rm -r ${path.resolve(__dirname, '../release')}`); +} + +// window 系统删除 release 目录 +if (process.platform === 'win32') { + exec(`rd/s/q ${path.resolve(__dirname, '../release')}`); +} + +const config: Configuration = { + appId: '.postcat.io', + productName: 'Postcat', + asar: true, + directories: { + output: 'release/' + }, + files: [ + 'out/app/**/*.js*', + 'out/platform/**/*.js*', + 'out/environment.js', + 'out/shared/**/*.js*', + 'src/workbench/browser/dist/**/*', + 'out/workbench/browser/src/**/*.js*', + 'out/workbench/node/**/*.js*', + 'out/app/common/**/*', + '!**/*.ts' + ], + publish: [ + 'github', + { + provider: 'generic', + url: ELETRON_APP_CONFIG.BASE_DOWNLOAD_URL + } + ], + generateUpdatesFilesForAllChannels: true, + nsis: { + guid: 'Postcat', + oneClick: false, + allowElevation: true, + allowToChangeInstallationDirectory: true, + // for win - 将协议写入主机的脚本 + include: 'scripts/urlProtoco.nsh' + }, + protocols: [ + // for macOS - 用于在主机注册指定协议 + { + name: 'eoapi', + schemes: ['eoapi'] + } + ], + win: { + icon: 'src/app/common/images/logo.ico', + target: ['nsis', 'portable'] + // extraFiles: [ + // { + // from: './build/Uninstall Postcat.exe', + // to: '.' + // } + // ] + }, + portable: { + splashImage: 'src/app/common/images/postcat.bmp' + }, + mac: { + icon: 'src/app/common/images/512x512.png', + hardenedRuntime: true, + category: 'public.app-category.productivity', + gatekeeperAssess: false, + entitlements: 'scripts/entitlements.mac.plist', + entitlementsInherit: 'scripts/entitlements.mac.plist', + // target: ['dmg', 'zip'] + target: [ + { + target: 'default', + arch: ['x64', 'arm64'] + } + ] + }, + dmg: { + sign: false + }, + afterSign: 'scripts/notarize.js', + linux: { + icon: 'src/app/common/images/', + target: ['AppImage'] + } +}; + +// 这里动态往 package.json 中写入 electron-builder 配置,主要是为了给 build-for-electron.bat 脚本读取配置 +const modifyPkgInfo = () => { + // @ts-ignore + pkgInfo.build = config; + writeFileSync(pkgPath, JSON.stringify(pkgInfo, null, 2)); + // 退出进程/意外退出进程 时主动还原 package.json 信息 + process.on('exit', restorePkgInfo); + process.on('uncaughtException', restorePkgInfo); +}; + +const restorePkgInfo = () => { + Reflect.deleteProperty(pkgInfo, 'build'); + // 还原 package.json 文件 + writeFileSync(pkgPath, JSON.stringify(pkgInfo, null, 2)); +}; + +// 要打包的目标平台 +const targetPlatform: Platform = { + darwin: Platform.MAC, + win32: Platform.WINDOWS, + linux: Platform.LINUX +}[platform]; + +console.log('打包参数', argv); + +Promise.all([ + build({ + config, + targets: targetPlatform.createTarget(), + ...argv + }) +]) + .then(async () => { + modifyPkgInfo(); + + const ls = spawn('yarn', ['wininstaller'], { + // 仅在当前运行环境为 Windows 时,才使用 shell + shell: isWin + }); + + ls.stdout.on('data', async data => { + console.log(decoder.decode(data)); + // build-by-external.bat + if (decoder.decode(data).includes('pack postcat finished!')) { + console.log('\x1b[32m', '打包完成🎉🎉🎉你要的都在 release 目录里🤪🤪🤪'); + exit(); + } + }); + }) + .catch(async error => { + if (error.includes?.('HttpError')) { + } + console.log('\x1b[31m', '打包失败,错误信息:', error); + exit(); + }); diff --git a/src/app/electron-main/updater.ts b/src/app/electron-main/updater.ts index 18d368938..ee0bd6914 100644 --- a/src/app/electron-main/updater.ts +++ b/src/app/electron-main/updater.ts @@ -1,6 +1,9 @@ +// import { dialog } from 'electron'; import log from 'electron-log'; import { autoUpdater } from 'electron-updater'; +import { ELETRON_APP_CONFIG } from '../../environment'; + const appVersion = require('../../../package.json').version; export class EoUpdater { @@ -8,12 +11,31 @@ export class EoUpdater { this.watchLog(); autoUpdater.setFeedURL({ provider: 'generic', - url: 'https://data.postcat.com/download/' + url: ELETRON_APP_CONFIG.BASE_DOWNLOAD_URL }); // 是否自动更新 // autoUpdater.autoDownload = window.pc.getExtensionSettings('common.app.autoUpdate') !== false; if (appVersion.includes('beta')) autoUpdater.channel = 'beta'; console.log('appVersion', appVersion, autoUpdater.channel); + + // autoUpdater.on('update-downloaded', info => { + // log.info('Update downloaded.'); + + // // The update will automatically be installed the next time the + // // app launches. If you want to, you can force the installation + // // now: + // const dialogOpts = { + // type: 'info', + // buttons: ['重启', '稍后'], + // title: '版本升级', + // message: '有新版本可用了', + // detail: `新版本 (${info.version}) 已经下载,重启并更新.` + // }; + + // dialog.showMessageBox(dialogOpts).then(returnValue => { + // if (returnValue.response === 0) autoUpdater.quitAndInstall(false, true); + // }); + // }); } check() { autoUpdater.checkForUpdatesAndNotify(); diff --git a/src/environment.ts b/src/environment.ts index 30ba9a4b1..fd6c32a57 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -6,4 +6,5 @@ export const ELETRON_APP_CONFIG = { // MOCK_URL: 'http://8.219.85.124:5000', NODE_SERVER_PORT: 4201, BASE_DOWNLOAD_URL: 'https://data.postcat.com/download/' + // BASE_DOWNLOAD_URL: 'http://127.0.0.1:8080' } as const; diff --git a/src/workbench/browser/angular.json b/src/workbench/browser/angular.json index f967162f4..040e618f3 100644 --- a/src/workbench/browser/angular.json +++ b/src/workbench/browser/angular.json @@ -1,122 +1 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "cli": { "analytics": false, "defaultCollection": "@angular-eslint/schematics", "cache": { "enabled": true } }, - "version": 1, - "newProjectRoot": "projects", - "projects": { - "postcat": { - "root": "", - "i18n": { "sourceLocale": { "code": "en" }, "locales": { "zh": { "translation": "locale/messages.zh.xlf" } } }, - "sourceRoot": "src", - "projectType": "application", - "schematics": { "@schematics/angular:application": { "strict": true } }, - "architect": { - "build": { - "inlineStyleLanguage": "scss", - "builder": "@angular-builders/custom-webpack:browser", - "options": { - "localize": true, - "aot": true, - "outputPath": "dist", - "index": "src/index.html", - "main": "src/main.ts", - "tsConfig": "src/tsconfig.app.json", - "polyfills": "src/polyfills.ts", - "assets": [ - "src/icon.ico", - "src/assets", - "src/extensions", - { "glob": "**/*", "input": "../../../node_modules/monaco-editor/min/vs", "output": "/assets/vs/" } - ], - "styles": ["src/styles/antd.less", "src/styles.scss"], - "scripts": ["src/assets/libs/protocolcheck.js"], - "customWebpackConfig": { "path": "./angular.webpack.js", "replaceDuplicatePlugins": true }, - "allowedCommonJsDependencies": ["brace", "qs", "rxjs"] - }, - "configurations": { - "dev": { - "optimization": false, - "outputHashing": "none", - "sourceMap": true, - "namedChunks": false, - "localize": false, - "preserveSymlinks": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": false, - "fileReplacements": [{ "replace": "src/environments/environment.ts", "with": "src/environments/environment.dev.ts" }] - }, - "devCn": { - "optimization": false, - "outputHashing": "none", - "sourceMap": true, - "namedChunks": false, - "preserveSymlinks": true, - "localize": ["zh"], - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": false, - "fileReplacements": [{ "replace": "src/environments/environment.ts", "with": "src/environments/environment.dev.ts" }] - }, - "production": { - "preserveSymlinks": false, - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, - "fileReplacements": [{ "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" }] - }, - "docker": { - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, - "fileReplacements": [{ "replace": "src/environments/environment.ts", "with": "src/environments/environment.docker.ts" }] - } - } - }, - "serve": { - "builder": "@angular-builders/custom-webpack:dev-server", - "options": { "browserTarget": "postcat:build", "proxyConfig": "proxy.conf.json" }, - "configurations": { - "dev": { "browserTarget": "postcat:build:dev" }, - "devCn": { "browserTarget": "postcat:build:devCn" }, - "production": { "browserTarget": "postcat:build:production" } - } - }, - "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "postcat:build" } }, - "test": { - "inlineStyleLanguage": "scss", - "builder": "@angular-builders/custom-webpack:karma", - "options": { - "main": "src/test.ts", - "polyfills": "src/polyfills-test.ts", - "tsConfig": "src/tsconfig.spec.json", - "karmaConfig": "src/karma.conf.js", - "scripts": [], - "styles": ["src/styles.scss"], - "assets": ["src/assets"], - "customWebpackConfig": { "path": "./angular.webpack.js", "replaceDuplicatePlugins": true } - } - }, - "lint": { "builder": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] } } - } - }, - "postcat-e2e": { - "root": "e2e", - "projectType": "application", - "architect": { "lint": { "builder": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": ["e2e/**/*.ts"] } } } - } - }, - "defaultProject": "postcat", - "schematics": { - "@schematics/angular:component": { "prefix": "pc", "style": "scss", "inlineStyle": true, "inlineTemplate": true }, - "@schematics/angular:directive": { "prefix": "pc" } - } -} +{"$schema":"./node_modules/@angular/cli/lib/config/schema.json","cli":{"analytics":false,"defaultCollection":"@angular-eslint/schematics","cache":{"enabled":true}},"version":1,"newProjectRoot":"projects","projects":{"postcat":{"root":"","i18n":{"sourceLocale":{"code":"en","baseHref":""},"locales":{"zh":{"translation":"locale/messages.zh.xlf","baseHref":""}}},"sourceRoot":"src","projectType":"application","schematics":{"@schematics/angular:application":{"strict":true}},"architect":{"build":{"inlineStyleLanguage":"scss","builder":"@angular-builders/custom-webpack:browser","options":{"localize":true,"aot":true,"outputPath":"dist","index":"src/index.html","main":"src/main.ts","tsConfig":"src/tsconfig.app.json","polyfills":"src/polyfills.ts","assets":["src/icon.ico","src/assets","src/extensions",{"glob":"**/*","input":"../../../node_modules/monaco-editor/min/vs","output":"/assets/vs/"}],"styles":["src/styles/antd.less","src/styles.scss"],"scripts":["src/assets/libs/protocolcheck.js"],"customWebpackConfig":{"path":"./angular.webpack.js","replaceDuplicatePlugins":true},"allowedCommonJsDependencies":["brace","qs","rxjs"]},"configurations":{"dev":{"optimization":false,"outputHashing":"none","sourceMap":true,"namedChunks":false,"localize":false,"preserveSymlinks":true,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":false,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.dev.ts"}]},"devCn":{"optimization":false,"outputHashing":"none","sourceMap":true,"namedChunks":false,"preserveSymlinks":true,"localize":["zh"],"extractLicenses":true,"vendorChunk":false,"buildOptimizer":false,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.dev.ts"}]},"production":{"preserveSymlinks":false,"optimization":true,"outputHashing":"all","sourceMap":false,"namedChunks":false,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":true,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.prod.ts"}]},"docker":{"optimization":true,"outputHashing":"all","sourceMap":false,"namedChunks":false,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":true,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.docker.ts"}]}}},"serve":{"builder":"@angular-builders/custom-webpack:dev-server","options":{"browserTarget":"postcat:build","proxyConfig":"proxy.conf.json"},"configurations":{"dev":{"browserTarget":"postcat:build:dev"},"devCn":{"browserTarget":"postcat:build:devCn"},"production":{"browserTarget":"postcat:build:production"}}},"extract-i18n":{"builder":"@angular-devkit/build-angular:extract-i18n","options":{"browserTarget":"postcat:build"}},"test":{"inlineStyleLanguage":"scss","builder":"@angular-builders/custom-webpack:karma","options":{"main":"src/test.ts","polyfills":"src/polyfills-test.ts","tsConfig":"src/tsconfig.spec.json","karmaConfig":"src/karma.conf.js","scripts":[],"styles":["src/styles.scss"],"assets":["src/assets"],"customWebpackConfig":{"path":"./angular.webpack.js","replaceDuplicatePlugins":true}}},"lint":{"builder":"@angular-eslint/builder:lint","options":{"lintFilePatterns":["src/**/*.ts","src/**/*.html"]}}}},"postcat-e2e":{"root":"e2e","projectType":"application","architect":{"lint":{"builder":"@angular-eslint/builder:lint","options":{"lintFilePatterns":["e2e/**/*.ts"]}}}}},"defaultProject":"postcat","schematics":{"@schematics/angular:component":{"prefix":"pc","style":"scss","inlineStyle":true,"inlineTemplate":true},"@schematics/angular:directive":{"prefix":"pc"}}} \ No newline at end of file diff --git a/src/workbench/browser/src/app/modules/extension-select/sync-api/sync-api.component.ts b/src/workbench/browser/src/app/modules/extension-select/sync-api/sync-api.component.ts index 0c8534ee6..ad5908332 100644 --- a/src/workbench/browser/src/app/modules/extension-select/sync-api/sync-api.component.ts +++ b/src/workbench/browser/src/app/modules/extension-select/sync-api/sync-api.component.ts @@ -79,7 +79,6 @@ export class SyncApiComponent implements OnInit, OnChanges { updateExtensionModel() { const currentFormater = this.store.getSyncSettingList.find(n => n.pluginId === this.model.__formater); - // console.log('currentFormater', { ...currentFormater }); if (currentFormater && this.currentFormater !== currentFormater) { this.currentFormater = currentFormater; this.model = {