Compare commits

..
2 Commits
Author SHA1 Message Date
luk 73c5eb6a8e 添加一些信息在 global ignore 文件里,帮助 AI agents 理解 2026-10-07 10:17:31 +08:00
lukandAtomCode b49f8c9f11 seafile_ignore_find2merge 移植 Node 跨平台,global 增加 Flutter 产物忽略规则
- 新增 seafile_ignore_find2merge.js:忠实移植 .sh 语义(深度3树搜索、跳过
  node_modules 等目录、seafile-ignore.txt/.local.txt 触发合并、dry-run 模式),
  修正 .sh 三个问题:默认 global 是 URL 时 cat 失败(改用 fetch)、去掉误抄的
  .git 检测、根目录自身参与合并(find -name '[^.]*' 匹配不到根)
- .sh 同步修正后两项(.git 检测、根目录),保持与 .js 行为一致
- nixhome/seafile-ignore.global.txt 增加 Flutter/Dart 通用机器本地产物:
  .dart_tool/、.flutter-plugins-dependencies、Pods/、local.properties、
  .gradle/、.kotlin/(build/ 因 vue 仓库跟踪 build/ 而仅在项目层 local 忽略)
- 验证:/tmp 夹具树 .sh 与 .js 产物逐字节一致;productX 全树 dry-run 候选 87 条一致

Co-Authored-By: AtomCode (qwen3.8-27b) <[email protected]>
2026-10-07 09:58:09 +08:00
5 changed files with 311 additions and 4 deletions
+3
View File
@@ -6,6 +6,7 @@
# https://gitignore.io # https://gitignore.io
### .gitignore_global ### ### .gitignore_global ###
### global ignore settings in opx/sysconfig/nixhome/.gitignore_global
# Self defined pattern to ignore # Self defined pattern to ignore
?*.gitignore ?*.gitignore
@@ -83,4 +84,6 @@ _desktop.ini
.env.*.local .env.*.local
### .gitignore.local.txt ### ### .gitignore.local.txt ###
### Followings are project-specific ignore settings merged by opx/sysconfig/git_ignore_find2merge.js
+25 -1
View File
@@ -7,6 +7,7 @@
# seafile-ignore.txt 只能忽略还没有被同步的文件。对于已经被同步的文件,如果后来把它添加到 seafile-ignore.txt 中,系统只会忽略后续更改,已经上传的版本不会受影响。 # seafile-ignore.txt 只能忽略还没有被同步的文件。对于已经被同步的文件,如果后来把它添加到 seafile-ignore.txt 中,系统只会忽略后续更改,已经上传的版本不会受影响。
### seafile-ignore.global.txt ### ### seafile-ignore.global.txt ###
### global ignore settings in opx/sysconfig/nixhome/seafile-ignore.global.txt
# 自定义的后缀名,凡有 sfignore 后缀的都不进行同步 # 自定义的后缀名,凡有 sfignore 后缀的都不进行同步
*.sfignore *.sfignore
@@ -83,5 +84,28 @@ OneDrive/Icon
.playwright-mcp/ .playwright-mcp/
*/.playwright-mcp/ */.playwright-mcp/
### seafile-ignore.local.txt ### # Flutter/Dart 机器本地产物(各端由 `flutter pub get` / `pod install` / 构建在本机重生成,不跨机同步)。
# 双机开发时 .dart_tool/package_config.json 内是绝对路径(file:///Users/<用户>/.pub-cache/...),同步过去会让另一台机器编译报 No such file or directory。
# 注:pub 缓存在 ~/.pub-cache,位于资料库根之外,seafile 同步不到,无需在此列出。
# 注:build/ 未全局忽略,以防波及用了该常见词的目录;按需放各项目 seafile-ignore.local.txt。
# ---- Dart / Flutter:包解析 + 构建产物(核心,换机必跑 `flutter pub get` 重生成)----
.dart_tool/
*/.dart_tool/
.flutter-plugins-dependencies
*/.flutter-plugins-dependencies
# ---- iOS / macOS:CocoaPods 检出目录(Podfile.lock 仍提交共享,只忽略 Pods/ 实体)----
Pods/
*/Pods/
# ---- Android:本机 SDK 路径 + 构建缓存(换机必跑 `flutter build` 重生成 local.properties)----
local.properties
*/local.properties
.gradle/
*/.gradle/
.kotlin/
*/.kotlin/
### seafile-ignore.local.txt ###
### Followings are project-specific ignore settings merged by opx/sysconfig/seafile_ignore_find2merge.js
+111
View File
@@ -0,0 +1,111 @@
# https://help.seafile.com/syncing_client/excluding_files/
# 注释。通配符:* 匹配0到若干个字符,包括代表目录的/。? 匹配1个字符,包括/。
# seafile-ignore.txt 只能控制在客户端需要忽略哪些文件。你依然可以在 seahub 的 web 界面创建这些被客户端忽略的文件。
# 在这种情况下,
# 这些文件会被同步到客户端,但是用户在客户端对这些文件的后续修改会被忽略,不会被同步回服务器。
# 文件在服务器端的后续更改会被同步到客户端,如果客户端也同时修改了这些文件,系统会生成冲突文件。
# seafile-ignore.txt 只能忽略还没有被同步的文件。对于已经被同步的文件,如果后来把它添加到 seafile-ignore.txt 中,系统只会忽略后续更改,已经上传的版本不会受影响。
### seafile-ignore.global.txt ###
### global ignore settings in opx/sysconfig/nixhome/seafile-ignore.global.txt
# 自定义的后缀名,凡有 sfignore 后缀的都不进行同步
*.sfignore
*.sfignore/
*.sfignore.*
*.sfignore.*/
*.sfomit
*.sfomit.*
*.sfomit/
*.sfomit.*/
*.nosf
*.nosf.*
*.nosf/
*.nosf.*/
## everything 'git pull or fetch' will update `.git/FETCH_HEAD`, even if the content doesn't change. To avoid too many useless updates of this file in Seafile history:
FETCH_HEAD
*/FETCH_HEAD
.Trash/
.Trashes/
.DS_Store
*/.DS_Store
*.aae # AAE 文件主要在苹果的照片应用程序中使用,保存对原始照片所做的编辑,比如,裁剪、旋转或调整亮度等操作的信息。
.thumbnails
*/.thumbnails
Thumbs.db
*/Thumbs.db
thumbs.db
*/thumbs.db
_desktop.ini
*/_desktop.ini
._*
*/._*
.$*
*/.$*
~$*
*/~$*
node_modules/
*/node_modules/
pages4loader.json5
*/pages4loader.json5
.deploy_git/
*/.deploy_git/
# next.js 项目
.next/
*/.next/
# HBuilder 目录
unpackage/
*/unpackage/
Icon
OneDrive/Icon
# wrangler project
.dev.vars*
*/.dev.vars*
.wrangler/
*/.wrangler/
.playwright-mcp/
*/.playwright-mcp/
# Flutter/Dart 机器本地产物(各端由 `flutter pub get` / `pod install` / 构建在本机重生成,不跨机同步)。
# 双机开发时 .dart_tool/package_config.json 内是绝对路径(file:///Users/<用户>/.pub-cache/...),同步过去会让另一台机器编译报 No such file or directory。
# 注:pub 缓存在 ~/.pub-cache,位于资料库根之外,seafile 同步不到,无需在此列出。
# 注:build/ 未全局忽略——tic-www-vue / cox-user-vue / cox-team-vue 等把 build/ 纳入 git 跟踪,全局忽略会波及;按需放各项目 seafile-ignore.local.txt。
# ---- Dart / Flutter:包解析 + 构建产物(核心,换机必跑 `flutter pub get` 重生成)----
.dart_tool/
*/.dart_tool/
.flutter-plugins-dependencies
*/.flutter-plugins-dependencies
# ---- iOS / macOS:CocoaPods 检出目录(Podfile.lock 仍提交共享,只忽略 Pods/ 实体)----
Pods/
*/Pods/
# ---- Android:本机 SDK 路径 + 构建缓存(换机必跑 `flutter build` 重生成 local.properties)----
local.properties
*/local.properties
.gradle/
*/.gradle/
.kotlin/
*/.kotlin/
### seafile-ignore.local.txt ###
### Followings are project-specific ignore settings merged by opx/sysconfig/seafile_ignore_find2merge.js
+168
View File
@@ -0,0 +1,168 @@
#!/usr/bin/env node
// Cross-platform (node) port of seafile_ignore_find2merge.sh / .bat
//
// Search [ROOTPATH], merge [GLOBALPATH/seafile-ignore.global.txt] and [ROOTPATH/<dir>/seafile-ignore.local.txt]
// into [ROOTPATH/<dir>/seafile-ignore.txt], for every <dir> that has a seafile-ignore.txt OR a seafile-ignore.local.txt.
// (Unlike git_ignore_find2merge, a .git dir is NOT a trigger — seafile ignore rules are independent of git.)
//
// usage:
// node seafile_ignore_find2merge.js [rootPath] [globalPath] [y|other]
// rootPath start of tree search (default: current dir)
// globalPath path to seafile-ignore.global.txt, or its parent dir, or an http(s) URL
// (default: https://git.tic.cc/opx/sysconfig/raw/branch/main/nixhome/seafile-ignore.global.txt)
// yesNo 'y' to write, anything else = dry-run (default: dry-run)
//
// node seafile_ignore_find2merge.js # dry-run from cwd
// node seafile_ignore_find2merge.js ~/Shared y
// node seafile_ignore_find2merge.js /srv/projects /srv/proj/seafile-ignore.global.txt y
//
// Semantics ported from the .sh (two .sh gaps fixed):
// - the root dir itself is processed too (the .sh's find -name '[^.]*' never matched the root dir)
// - a dir is updated iff it has seafile-ignore.txt OR seafile-ignore.local.txt (the .sh's `.git` check was dropped)
// - tree search is max depth 3, skipping hidden dirs and the known build/dep dirs
// - the .sh does `cat $GLOBALPATH` which fails when the default is a URL; here http(s) is fetched with fetch(),
// a local file / parent-dir keeps the original behavior.
const fs = require('fs')
const os = require('os')
const path = require('path')
const readline = require('readline')
const DEFAULT_GLOBAL_URL = 'https://git.tic.cc/opx/sysconfig/raw/branch/main/nixhome/seafile-ignore.global.txt'
// same skip list as: grep -E -v 'node_modules|uni_modules|\.deploy_git|\.git|.svn|\.vscode|\.wrangler|unpackage|_webroot|_logstore|_datasotre|_archive|_filestore|_ssl'
const SKIP_DIR_PATTERN = /node_modules|uni_modules|\.deploy_git|\.git|\.svn|\.vscode|\.wrangler|unpackage|_webroot|_logstore|_datasotre|_archive|_filestore|_ssl/
const MAX_DEPTH = 3 // relative to ROOTPATH, matching find -mindepth 0 -maxdepth 3
function resolveHome (p) {
if (p === '~') return os.homedir()
if (p.startsWith('~/')) return path.join(os.homedir(), p.slice(2))
return p
}
async function ask (question) {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
const answer = await new Promise(resolve => rl.question(question, resolve))
rl.close()
return answer.trim()
}
// find . -mindepth 0 -maxdepth 3 -type d -name '[^.]*' (relative paths, root itself excluded)
function collectCandidateDirs (rootPath) {
const candidates = []
function walk (dir, depth) {
if (depth > MAX_DEPTH) return
let entries
try {
entries = fs.readdirSync(dir, { withFileTypes: true })
} catch {
return
}
for (const entry of entries) {
if (!entry.isDirectory()) continue
if (entry.name.startsWith('.')) continue // -name '[^.]*' excludes hidden dirs
const absDir = path.join(dir, entry.name)
const relDir = path.relative(rootPath, absDir)
if (relDir.split(path.sep).length > MAX_DEPTH) continue
candidates.push(relDir)
walk(absDir, depth + 1)
}
}
walk(rootPath, 1)
return candidates
}
async function loadGlobalContent (globalPathInput) {
const globalPath = resolveHome(globalPathInput)
if (/^https?:\/\//i.test(globalPath)) {
console.log('√√√ GLOBALPATH (url) = [[' + globalPath + ']]')
const response = await fetch(globalPath)
if (!response.ok) {
console.log('××× Fetch failed: HTTP ' + response.status + ' for [[' + globalPath + ']]. Exit now...')
process.exit(1)
}
return response.text()
}
let globalFile = globalPath
if (fs.existsSync(globalPath) && fs.statSync(globalPath).isDirectory()) {
globalFile = path.join(globalPath, 'seafile-ignore.global.txt')
}
if (!fs.existsSync(globalFile)) {
console.log('××× Not found [[' + globalFile + ']]. Exit now...')
process.exit(1)
}
console.log('√√√ GLOBALPATH = [[' + globalFile + ']]')
return fs.readFileSync(globalFile, 'utf8')
}
async function main () {
console.log('')
console.log('Search in [ROOTPATH], Merge [GLOBALPATH/seafile-ignore.global.txt] and [ROOTPATH/*/seafile-ignore.local.txt] files to [seafile-ignore.txt]')
console.log('')
// --- ROOTPATH ---
let rootPath = process.argv[2]
if (!rootPath) {
console.log('::*** Enter [root path] or [leave blank] for default [[' + process.cwd() + ']] to start tree search for seafile-ignore.txt files')
rootPath = await ask('***:: ')
}
if (rootPath) rootPath = path.resolve(resolveHome(rootPath))
else rootPath = process.cwd()
if (!fs.existsSync(rootPath) || !fs.statSync(rootPath).isDirectory()) {
console.log('××× [[' + rootPath + ']] not exist! Exit now. ***')
process.exit(1)
}
console.log('√√√ ROOTPATH = [[' + rootPath + ']]')
console.log('')
// --- GLOBALPATH ---
let globalPathInput = process.argv[3]
if (!globalPathInput) {
console.log('::*** Enter [path to seafile-ignore.global.txt] or [leave blank] for default [[' + DEFAULT_GLOBAL_URL + ']]')
globalPathInput = await ask('***:: ')
}
const globalContent = await loadGlobalContent(globalPathInput)
console.log('')
// --- y/N ---
let yesNo = process.argv[4]
if (!yesNo) {
console.log('::*** Enter [y] to start updating, or [anything else] for dry-run')
yesNo = await ask('***:: ')
}
const willWrite = yesNo === 'y'
console.log('*** Starting from [[' + rootPath + ']] ***')
console.log('')
// --- search + merge ---
// The root dir itself is also a candidate: the old .sh's find -name '[^.]*' never matched the root,
// so ROOTPATH's own seafile-ignore.txt was never updated — fixed here (and in the .sh).
const candidateDirs = collectCandidateDirs(rootPath).sort()
if (
fs.existsSync(path.join(rootPath, 'seafile-ignore.txt')) ||
fs.existsSync(path.join(rootPath, 'seafile-ignore.local.txt'))
) candidateDirs.unshift('.')
let updatedCount = 0
for (const relDir of candidateDirs) {
const absDir = relDir === '.' ? rootPath : path.join(rootPath, relDir)
if (relDir !== '.' && SKIP_DIR_PATTERN.test(relDir)) continue
const ignoreFile = path.join(absDir, 'seafile-ignore.txt')
const localFile = path.join(absDir, 'seafile-ignore.local.txt')
if (!fs.existsSync(ignoreFile) && !fs.existsSync(localFile)) continue
console.log('---- updating [[' + relDir + '/seafile-ignore.txt]] ----')
if (willWrite) {
let merged = globalContent
if (fs.existsSync(localFile)) merged += fs.readFileSync(localFile, 'utf8')
fs.writeFileSync(ignoreFile, merged)
updatedCount++
}
console.log('')
}
if (willWrite) console.log('*** done: ' + updatedCount + ' file(s) updated ***')
}
main().catch(err => {
console.error(err)
process.exit(1)
})
+4 -3
View File
@@ -55,9 +55,10 @@ cd $ROOTPATH
echo "*** Starting from [[`pwd`]] ***" echo "*** Starting from [[`pwd`]] ***"
echo "" echo ""
find . -mindepth 0 -maxdepth 3 -type d -name '[^.]*' | grep -E -v 'node_modules|uni_modules|\.deploy_git|\.git|.svn|\.vscode|\.wrangler|unpackage|_webroot|_logstore|_datasotre|_archive|_filestore|_ssl' | while read repo # 根目录自身也是候选(find -name '[^.]*' 匹配不到根目录 .,手动补上)
do { echo "."; find . -mindepth 0 -maxdepth 3 -type d -name '[^.]*'; } | grep -E -v 'node_modules|uni_modules|\.deploy_git|\.git|.svn|\.vscode|\.wrangler|unpackage|_webroot|_logstore|_datasotre|_archive|_filestore|_ssl' | while read repo
if [ -f "$repo/seafile-ignore.txt" ] || [ -d "$repo/.git" ] do
if [ -f "$repo/seafile-ignore.txt" ] || [ -f "$repo/seafile-ignore.local.txt" ]
then then
echo "---- updating [[$repo/seafile-ignore.txt]] ----" echo "---- updating [[$repo/seafile-ignore.txt]] ----"
if [ "$YESNO" = 'y' ] if [ "$YESNO" = 'y' ]