feat: 生成最后更新时间字段,支持增量更新
This commit is contained in:
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@@ -10,6 +10,14 @@ on:
|
||||
- 'repo/**'
|
||||
- 'build/build.js'
|
||||
- '.github/workflows/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
force_update:
|
||||
description: '强制执行全量更新'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Skip this job if the repository is a fork
|
||||
@@ -33,7 +41,15 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: node ./build/build.js
|
||||
- name: Build repo.json
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.force_update }}" = "true" ]; then
|
||||
echo "执行强制全量更新"
|
||||
node ./build/build.js --force
|
||||
else
|
||||
echo "执行增量更新"
|
||||
node ./build/build.js
|
||||
fi
|
||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: update repo.json
|
||||
@@ -63,4 +79,4 @@ jobs:
|
||||
r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
||||
r2-bucket: ${{ secrets.R2_BUCKET }}
|
||||
source-dir: my_files/
|
||||
destination-dir: github_mirror/
|
||||
destination-dir: github_mirror/
|
||||
163
build/build.js
163
build/build.js
@@ -3,9 +3,50 @@ const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// 处理命令行参数
|
||||
const args = process.argv.slice(2);
|
||||
const forceFullUpdate = args.includes('--force') || args.includes('-f');
|
||||
|
||||
// 在文件开头添加全局变量
|
||||
const pathingDirsWithoutIcon = new Set();
|
||||
|
||||
// 检查是否存在现有的repo.json文件
|
||||
const repoJsonPath = path.resolve(__dirname, '..', 'repo.json');
|
||||
let existingRepoJson = null;
|
||||
let modifiedFiles = [];
|
||||
|
||||
// 尝试加载现有的repo.json文件
|
||||
try {
|
||||
if (fs.existsSync(repoJsonPath) && !forceFullUpdate) {
|
||||
existingRepoJson = JSON.parse(fs.readFileSync(repoJsonPath, 'utf8'));
|
||||
console.log('找到现有的repo.json文件,将执行增量更新');
|
||||
|
||||
// 获取Git中修改的文件
|
||||
try {
|
||||
// 获取当前分支名称
|
||||
const currentBranch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
|
||||
console.log(`当前分支: ${currentBranch}`);
|
||||
|
||||
// 获取此次变更的文件列表
|
||||
const changedFiles = execSync(`git diff --name-only HEAD~1 HEAD`).toString().trim().split('\n');
|
||||
modifiedFiles = changedFiles.filter(file => file.startsWith('repo/'));
|
||||
console.log(`检测到 ${modifiedFiles.length} 个修改的文件:`);
|
||||
modifiedFiles.forEach(file => console.log(` - ${file}`));
|
||||
} catch (e) {
|
||||
console.warn('无法获取Git修改文件列表,将执行全量更新', e);
|
||||
modifiedFiles = [];
|
||||
}
|
||||
} else {
|
||||
if (forceFullUpdate) {
|
||||
console.log('检测到--force参数,将执行全量更新');
|
||||
} else {
|
||||
console.log('未找到现有的repo.json文件,将执行全量更新');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('读取现有repo.json文件出错,将执行全量更新', e);
|
||||
}
|
||||
|
||||
function calculateSHA1(filePath) {
|
||||
const fileBuffer = fs.readFileSync(filePath);
|
||||
const hashSum = crypto.createHash('sha1');
|
||||
@@ -33,6 +74,24 @@ function formatTime(timestamp) {
|
||||
return timestamp.replace(/[-: ]/g, '').split('+')[0];
|
||||
}
|
||||
|
||||
// 格式化最后更新时间为标准的北京时间格式:YYYY-MM-DD HH:MM:SS
|
||||
function formatLastUpdated(timestamp) {
|
||||
if (!timestamp) return null;
|
||||
|
||||
try {
|
||||
// 解析Git时间戳格式 (如: "2023-01-01 12:00:00 +0800")
|
||||
const dateMatch = timestamp.match(/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/);
|
||||
if (dateMatch) {
|
||||
const [_, year, month, day, hour, minute, second] = dateMatch;
|
||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||
}
|
||||
return timestamp;
|
||||
} catch (e) {
|
||||
console.warn(`格式化时间戳出错 ${timestamp}:`, e);
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
function convertNewlines(text) {
|
||||
return text.replace(/\\n/g, '\n');
|
||||
}
|
||||
@@ -48,16 +107,21 @@ function extractInfoFromCombatFile(filePath) {
|
||||
.map(char => char.trim())
|
||||
.filter(char => char.length > 0 && !char.match(/^[,.]$/)); // 过滤掉单个逗号或句号
|
||||
|
||||
// 获取最后更新时间
|
||||
const gitTimestamp = getGitTimestamp(filePath);
|
||||
const lastUpdated = formatLastUpdated(gitTimestamp);
|
||||
|
||||
// 优先使用文件中的版本号,其次使用提交时间,最后使用 SHA
|
||||
const version = versionMatch ? versionMatch[1].trim() :
|
||||
(getGitTimestamp(filePath) ? formatTime(getGitTimestamp(filePath)) :
|
||||
(gitTimestamp ? formatTime(gitTimestamp) :
|
||||
calculateSHA1(filePath).substring(0, 7));
|
||||
|
||||
return {
|
||||
author: authorMatch ? authorMatch[1].trim() : '',
|
||||
description: descriptionMatch ? convertNewlines(descriptionMatch[1].trim()) : '',
|
||||
tags: tags,
|
||||
version: version
|
||||
version: version,
|
||||
lastUpdated: lastUpdated
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,19 +133,25 @@ function extractInfoFromJSFolder(folderPath) {
|
||||
manifestContent = manifestContent.replace(/,(\s*[}\]])/g, '$1');
|
||||
const manifest = JSON.parse(manifestContent);
|
||||
const combinedDescription = `${manifest.name || ''}~|~${manifest.description || ''}`;
|
||||
|
||||
// 获取最后更新时间
|
||||
const gitTimestamp = getGitTimestamp(manifestPath);
|
||||
const lastUpdated = formatLastUpdated(gitTimestamp);
|
||||
|
||||
return {
|
||||
version: manifest.version || '',
|
||||
description: convertNewlines(combinedDescription),
|
||||
author: manifest.authors && manifest.authors.length > 0 ? manifest.authors[0].name : '',
|
||||
tags: manifest.tags || []
|
||||
tags: manifest.tags || [],
|
||||
lastUpdated: lastUpdated
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`解析 ${manifestPath} 时出错:`, error);
|
||||
console.error('文件内容:', fs.readFileSync(manifestPath, 'utf8'));
|
||||
return { version: '', description: '', author: '', tags: [] };
|
||||
return { version: '', description: '', author: '', tags: [], lastUpdated: null };
|
||||
}
|
||||
}
|
||||
return { version: '', description: '', author: '', tags: [] };
|
||||
return { version: '', description: '', author: '', tags: [], lastUpdated: null };
|
||||
}
|
||||
|
||||
function extractInfoFromPathingFile(filePath, parentFolders) {
|
||||
@@ -100,9 +170,13 @@ function extractInfoFromPathingFile(filePath, parentFolders) {
|
||||
|
||||
const contentObj = JSON.parse(content);
|
||||
|
||||
// 获取最后更新时间
|
||||
const gitTimestamp = getGitTimestamp(filePath);
|
||||
const lastUpdated = formatLastUpdated(gitTimestamp);
|
||||
|
||||
// 优先使用文件中的版本号,其次使用提交时间,最后使用 SHA
|
||||
const version = contentObj.info?.version ||
|
||||
(getGitTimestamp(filePath) ? formatTime(getGitTimestamp(filePath)) :
|
||||
(gitTimestamp ? formatTime(gitTimestamp) :
|
||||
calculateSHA1(filePath).substring(0, 7));
|
||||
|
||||
// 从父文件夹获取默认标签
|
||||
@@ -132,7 +206,8 @@ function extractInfoFromPathingFile(filePath, parentFolders) {
|
||||
author: contentObj.info.author || '',
|
||||
description: convertNewlines(contentObj.info.description || ''),
|
||||
version: version,
|
||||
tags: tags
|
||||
tags: tags,
|
||||
lastUpdated: lastUpdated
|
||||
};
|
||||
}
|
||||
|
||||
@@ -143,6 +218,10 @@ function extractInfoFromTCGFile(filePath, parentFolder) {
|
||||
const versionMatch = content.match(/\/\/\s*版本:(.*)/);
|
||||
const characterMatches = content.match(/角色\d+\s?=([^|\r\n{]+)/g);
|
||||
|
||||
// 获取最后更新时间
|
||||
const gitTimestamp = getGitTimestamp(filePath);
|
||||
const lastUpdated = formatLastUpdated(gitTimestamp);
|
||||
|
||||
let tags = characterMatches
|
||||
? characterMatches.map(match => match.split('=')[1].trim())
|
||||
.filter(tag => tag && !tag.startsWith('角色'))
|
||||
@@ -157,18 +236,80 @@ function extractInfoFromTCGFile(filePath, parentFolder) {
|
||||
|
||||
// 优先使用文件中的版本号,其次使用提交时间,最后使用 SHA
|
||||
const version = versionMatch ? versionMatch[1].trim() :
|
||||
(getGitTimestamp(filePath) ? formatTime(getGitTimestamp(filePath)) :
|
||||
(gitTimestamp ? formatTime(gitTimestamp) :
|
||||
calculateSHA1(filePath).substring(0, 7));
|
||||
|
||||
return {
|
||||
author: authorMatch ? authorMatch[1].trim() : '',
|
||||
description: descriptionMatch ? convertNewlines(descriptionMatch[1].trim()) : '',
|
||||
tags: [...new Set(tags)], // 去重
|
||||
version: version
|
||||
version: version,
|
||||
lastUpdated: lastUpdated
|
||||
};
|
||||
}
|
||||
|
||||
// 检查文件是否需要处理(增量更新模式下)
|
||||
function shouldProcessFile(filePath) {
|
||||
// 如果没有现有的repo.json或没有修改文件列表,则处理所有文件
|
||||
if (!existingRepoJson || modifiedFiles.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 将filePath转换为相对于仓库根目录的路径
|
||||
const relativeFilePath = path.relative(path.resolve(__dirname, '..'), filePath).replace(/\\/g, '/');
|
||||
|
||||
// 检查此文件或其所在目录是否在修改列表中
|
||||
return modifiedFiles.some(modifiedFile => {
|
||||
return relativeFilePath === modifiedFile ||
|
||||
relativeFilePath.startsWith(path.dirname(modifiedFile) + '/') ||
|
||||
modifiedFile.startsWith(relativeFilePath + '/');
|
||||
});
|
||||
}
|
||||
|
||||
// 在目录树中查找节点的辅助函数
|
||||
function findNodeInTree(tree, nodePath, currentPath = '') {
|
||||
if (!tree) return null;
|
||||
|
||||
if (tree.type === 'directory') {
|
||||
const newPath = currentPath ? `${currentPath}/${tree.name}` : tree.name;
|
||||
|
||||
if (newPath === nodePath) {
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (tree.children) {
|
||||
for (const child of tree.children) {
|
||||
const result = findNodeInTree(child, nodePath, newPath);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function generateDirectoryTree(dir, currentDepth = 0, parentFolders = []) {
|
||||
// 检查是否在增量更新模式下需要处理此目录
|
||||
const shouldProcess = shouldProcessFile(dir);
|
||||
|
||||
// 如果在增量更新模式下不需要处理此目录,尝试从现有repo.json找到对应节点
|
||||
if (!shouldProcess && existingRepoJson && existingRepoJson.indexes) {
|
||||
const category = parentFolders[0];
|
||||
const relativePath = parentFolders.join('/');
|
||||
|
||||
// 在现有repo.json中查找此目录节点
|
||||
const categoryTree = existingRepoJson.indexes.find(index => index.name === category);
|
||||
if (categoryTree) {
|
||||
const existingNode = findNodeInTree(categoryTree, relativePath);
|
||||
if (existingNode) {
|
||||
console.log(`使用现有数据: ${relativePath}`);
|
||||
return existingNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const stats = fs.statSync(dir);
|
||||
const info = {
|
||||
name: path.basename(dir),
|
||||
@@ -206,7 +347,8 @@ function generateDirectoryTree(dir, currentDepth = 0, parentFolders = []) {
|
||||
.map(child => {
|
||||
const childPath = path.join(dir, child);
|
||||
return generateDirectoryTree(childPath, currentDepth + 1, [...parentFolders, info.name]);
|
||||
});
|
||||
})
|
||||
.filter(child => child !== null); // 过滤掉null
|
||||
}
|
||||
} else {
|
||||
// 如果是 desktop.ini 或 icon.ico 文件,直接返回 null
|
||||
@@ -298,6 +440,5 @@ const repoJson = {
|
||||
"indexes": result
|
||||
};
|
||||
|
||||
const repoJsonPath = path.resolve(__dirname, '..', 'repo.json');
|
||||
fs.writeFileSync(repoJsonPath, JSON.stringify(repoJson, null, 2));
|
||||
console.log('repo.json 文件已创建并保存在 repo 同级目录中。');
|
||||
|
||||
Reference in New Issue
Block a user