first
This commit is contained in:
200
scripts/mini-ci.js
Normal file
200
scripts/mini-ci.js
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* 微信小程序自动化构建 & 上传脚本
|
||||
*/
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import ci from 'miniprogram-ci';
|
||||
|
||||
// --- 0. 常量定义与配置 ---
|
||||
const ENV_CONFIG = {
|
||||
allow: ['dev', 'test', 'prod'],
|
||||
default: 'dev',
|
||||
};
|
||||
|
||||
const UNI_BUILD_DIR = 'dist/build/mp-weixin';
|
||||
const QRCODE_OUTPUT_DIR = path.resolve(process.cwd(), UNI_BUILD_DIR);
|
||||
const SETTING = { // 统一的上传/预览配置
|
||||
es6: true,
|
||||
minify: true,
|
||||
// 其他常用配置
|
||||
autoPrefixWXSS: true,
|
||||
disableUseStrict: false,
|
||||
};
|
||||
|
||||
const MODE = process.argv[2];
|
||||
const ENV_ARG = process.argv[3];
|
||||
const PROJECT_ROOT = process.cwd();
|
||||
|
||||
// --- 1. 环境解析 ---
|
||||
const inputEnv = (ENV_ARG || process.env.MINI_ENV || ENV_CONFIG.default).toLowerCase();
|
||||
const ENV = ENV_CONFIG.allow.includes(inputEnv) ? inputEnv : ENV_CONFIG.default;
|
||||
|
||||
console.log(`📌 当前构建环境: ${ENV}`);
|
||||
|
||||
// --- 2. 私钥 & AppID 解析 (优先使用环境变量) ---
|
||||
function getAuthInfo() {
|
||||
const scriptsDir = path.resolve(PROJECT_ROOT, 'scripts');
|
||||
|
||||
// 优先级 1: 环境变量
|
||||
let appid = process.env.MINI_APPID;
|
||||
let privateKey = process.env.MINI_PRIVATE_KEY;
|
||||
|
||||
if (appid && privateKey) {
|
||||
console.log('🔑 使用环境变量 MINI_APPID 和 MINI_PRIVATE_KEY');
|
||||
return { appid, privateKey: privateKey.replace(/\\n/g, '\n').trim() };
|
||||
}
|
||||
|
||||
// 优先级 2: 自动解析私钥文件
|
||||
if (!fs.existsSync(scriptsDir)) {
|
||||
console.error('❌ 未找到 scripts/ 目录,且环境变量未设置');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const keyFile = fs.readdirSync(scriptsDir).find(f => /^private\.[^.]+\.key$/.test(f));
|
||||
if (!keyFile) {
|
||||
console.error(`❌ scripts/ 目录下未找到私钥文件 private.<appid>.key`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const match = keyFile.match(/^private\.(.+)\.key$/);
|
||||
appid = match ? match[1] : null;
|
||||
|
||||
if (!appid) {
|
||||
console.error(`❌ 无法从文件名解析 appid: ${keyFile}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const keyPath = path.resolve(scriptsDir, keyFile);
|
||||
privateKey = fs.readFileSync(keyPath, 'utf8').replace(/\\n/g, '\n').trim();
|
||||
|
||||
console.log(`🔑 使用私钥文件: ${keyFile}`);
|
||||
console.log(`📌 解析得到 appid: ${appid}`);
|
||||
|
||||
return { appid, privateKey };
|
||||
}
|
||||
|
||||
const { appid, privateKey } = getAuthInfo();
|
||||
|
||||
// --- 3. 规范化版本信息 ---
|
||||
function getVersionInfo(env) {
|
||||
try {
|
||||
const pkgVersion = require(path.resolve(PROJECT_ROOT, 'package.json')).version;
|
||||
const commit = execSync('git rev-parse --short HEAD').toString().trim();
|
||||
const date = new Date();
|
||||
const ymd = `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, '0')}${String(date.getDate()).padStart(2, '0')}`;
|
||||
|
||||
// CI 实际上传的 version 必须是 x.y.z 格式,建议使用 package.json 版本
|
||||
const uploadVersion = pkgVersion;
|
||||
|
||||
// 描述信息更详细,包含环境、版本、日期和 Commit
|
||||
const description = `[${env}] ${pkgVersion} (Build ${ymd}-${commit})`;
|
||||
|
||||
return {
|
||||
version: uploadVersion,
|
||||
desc: description,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('⚠️ 获取 Git 版本失败,使用 fallback: 1.0.0', e);
|
||||
return {
|
||||
version: '1.0.0',
|
||||
desc: `[${env}] Fallback 1.0.0`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const versionInfo = getVersionInfo(ENV);
|
||||
|
||||
// --- 4. miniprogram-ci Project 实例 ---
|
||||
if (!fs.existsSync(path.resolve(PROJECT_ROOT, UNI_BUILD_DIR))) {
|
||||
console.error(`❌ 找不到 uni-app 编译产物目录: ${UNI_BUILD_DIR}`);
|
||||
console.error('请先执行 uni-app 编译命令 (例如: npm run build:mp-weixin)');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const project = new ci.Project({
|
||||
appid,
|
||||
type: 'miniProgram',
|
||||
projectPath: PROJECT_ROOT,
|
||||
privateKey,
|
||||
// uni-app 产物目录
|
||||
miniprogramPath: UNI_BUILD_DIR,
|
||||
// 排除不需要上传的文件
|
||||
ignores: [
|
||||
'node_modules/**/*',
|
||||
'src/**/*',
|
||||
'.git/**/*',
|
||||
'dist/dev/**/*',
|
||||
'dist/build/h5/**/*', // 排除其他平台产物
|
||||
],
|
||||
});
|
||||
|
||||
// --- 5. 辅助函数:输出二维码 HTML ---
|
||||
function writeQRCodeHtml(env, buildDir) {
|
||||
const filename = `preview-${env}.png`;
|
||||
const html = `
|
||||
<html>
|
||||
<body>
|
||||
<h2>小程序预览环境:${env}</h2>
|
||||
<img src="${filename}" style="width:240px;height:240px;" />
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
const output = path.resolve(buildDir, `preview-${env}.html`);
|
||||
fs.writeFileSync(output, html, 'utf8');
|
||||
console.log(`📰 已输出二维码 HTML: ${output}`);
|
||||
}
|
||||
|
||||
// --- 6. 主流程 ---
|
||||
async function run() {
|
||||
// 确保二维码输出目录存在
|
||||
fs.mkdirSync(QRCODE_OUTPUT_DIR, { recursive: true });
|
||||
|
||||
// ----------- preview -----------
|
||||
if (MODE === 'preview') {
|
||||
const qrcodePath = path.resolve(QRCODE_OUTPUT_DIR, `preview-${ENV}.png`);
|
||||
console.log(`📡 开始预览,描述: ${versionInfo.desc}`);
|
||||
|
||||
const r = await ci.preview({
|
||||
project,
|
||||
desc: versionInfo.desc,
|
||||
setting: SETTING,
|
||||
qrcodeFormat: 'image',
|
||||
qrcodeOutputDest: qrcodePath,
|
||||
});
|
||||
|
||||
console.log('📦 PREVIEW RESULT:\n', JSON.stringify(r, null, 2));
|
||||
writeQRCodeHtml(ENV, QRCODE_OUTPUT_DIR);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// ----------- upload -----------
|
||||
if (MODE === 'upload') {
|
||||
console.log(`⬆️ 开始上传体验版 version=${versionInfo.version}, env=${ENV}`);
|
||||
|
||||
const r = await ci.upload({
|
||||
project,
|
||||
version: versionInfo.version,
|
||||
desc: versionInfo.desc,
|
||||
setting: SETTING,
|
||||
});
|
||||
|
||||
console.log('📦 UPLOAD RESULT:\n', JSON.stringify(r, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('❌ 未识别命令:只能使用 preview 或 upload');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ---------------------- ERROR ----------------------
|
||||
run().catch((e) => {
|
||||
console.error('❌ CI 执行失败:');
|
||||
// 打印完整错误信息
|
||||
console.error(e.stack || e);
|
||||
process.exit(1);
|
||||
});
|
||||
36
scripts/post-upgrade.js
Normal file
36
scripts/post-upgrade.js
Normal file
@@ -0,0 +1,36 @@
|
||||
// # 执行 `pnpm upgrade` 后会升级 `uniapp` 相关依赖
|
||||
// # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
|
||||
// # 只需要执行下面的命令即可
|
||||
|
||||
import { exec } from 'node:child_process';
|
||||
|
||||
// 定义要执行的命令
|
||||
const dependencies = [
|
||||
'@dcloudio/uni-app-harmony',
|
||||
// TODO: 如果需要某个平台的小程序,请手动删除或注释掉
|
||||
'@dcloudio/uni-mp-alipay',
|
||||
'@dcloudio/uni-mp-baidu',
|
||||
'@dcloudio/uni-mp-jd',
|
||||
'@dcloudio/uni-mp-kuaishou',
|
||||
'@dcloudio/uni-mp-lark',
|
||||
'@dcloudio/uni-mp-qq',
|
||||
'@dcloudio/uni-mp-toutiao',
|
||||
'@dcloudio/uni-mp-xhs',
|
||||
'@dcloudio/uni-quickapp-webview',
|
||||
'@dcloudio/uni-mp-harmony',
|
||||
// vue 已经内置了 @vue/runtime-core,这里移除掉
|
||||
'@vue/runtime-core',
|
||||
];
|
||||
|
||||
// 使用exec执行命令
|
||||
exec(`pnpm remove ${dependencies.join(' ')}`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
// 如果有错误,打印错误信息
|
||||
console.error(`执行出错: ${error}`);
|
||||
return;
|
||||
}
|
||||
// 打印正常输出
|
||||
console.log(`stdout: ${stdout}`);
|
||||
// 如果有错误输出,也打印出来
|
||||
console.error(`stderr: ${stderr}`);
|
||||
});
|
||||
28
scripts/verify-commit.js
Normal file
28
scripts/verify-commit.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 提交信息校验
|
||||
* @link https://github.com/toplenboren/simple-git-hooks
|
||||
* @see 参考:https://github.com/vuejs/vue-next/blob/master/.github/commit-convention.md
|
||||
*/
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import pico from 'picocolors';
|
||||
|
||||
const msgPath = path.resolve('.git/COMMIT_EDITMSG');
|
||||
const msg = readFileSync(msgPath, 'utf-8').trim();
|
||||
|
||||
const commitRE
|
||||
= /^(?:revert: )?(?:feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|mod|release|strengthen)(?:\(.+\))?: .{1,50}/;
|
||||
|
||||
if (!commitRE.test(msg)) {
|
||||
console.log(pico.yellow(`\n提交的信息: ${msg}\n`));
|
||||
console.error(
|
||||
` ${pico.white(pico.bgRed(' 格式错误 '))} ${pico.red(
|
||||
'无效的提交信息格式.',
|
||||
)}\n\n${
|
||||
pico.red(' 正确的提交消息格式. 例如:\n\n')
|
||||
} ${pico.green('feat: add a new feature')}\n`
|
||||
+ ` ${pico.green('fix: fixed an bug')}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user