feat: 生成最后更新时间字段,支持增量更新

This commit is contained in:
秋云
2025-05-06 23:10:03 +08:00
parent a0994f17e5
commit 0fee719a1b
2 changed files with 170 additions and 13 deletions

View File

@@ -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/

View File

@@ -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 同级目录中。');