Skip to content

Commit c40f0f7

Browse files
committed
chore(workflow): 优化更新检查逻辑,使用TypeScript脚本对比内容哈希
- 用diff-hash.ts脚本替代复杂的shell命令,避免参数过长问题 - 脚本复用了现有代码库中的版本信息读取和内容哈希计算方法 - 实现了远程与本地USB.IDS数据哈希的比较,提升更新检测准确性 - 修改GitHub Actions自动更新流程,集成diff-hash脚本判断是否跳过更新 - 新增diff-hash命令至package.json脚本列表,方便调用与管理 - 补充脚本使用说明至scripts目录README,详细介绍用法和机制
1 parent 81c3c01 commit c40f0f7

File tree

4 files changed

+181
-37
lines changed

4 files changed

+181
-37
lines changed

.github/workflows/auto-update.yml

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,43 +45,12 @@ jobs:
4545
- name: Check if update is needed
4646
id: check-update
4747
run: |
48-
# 获取npm最新版本的contentHash作为基准
49-
mkdir -p /tmp/npm-check
50-
cd /tmp/npm-check
51-
npm install usb.ids --silent 2>/dev/null || true
52-
53-
# 获取 npm 最新版本的 contentHash
54-
if [ -f "node_modules/usb.ids/usb.ids.version.json" ]; then
55-
NPM_HASH=$(node -p "require('./node_modules/usb.ids/usb.ids.version.json').contentHash" 2>/dev/null || echo "")
48+
# 使用TypeScript脚本进行哈希差异检查,避免shell参数过长问题
49+
if pnpm run diff-hash; then
50+
echo "No update needed, content hash is the same"
51+
echo "skip=true" >> $GITHUB_OUTPUT
5652
else
57-
NPM_HASH=""
58-
fi
59-
echo "NPM hash: $NPM_HASH"
60-
61-
# 清理临时目录
62-
cd /
63-
rm -rf /tmp/npm-check
64-
65-
# 回到工作目录,获取远程数据但不保存,只计算hash
66-
cd $GITHUB_WORKSPACE
67-
68-
# 下载远程数据并计算contentHash(不保存文件)
69-
REMOTE_CONTENT=$(curl -s "http://www.linux-usb.org/usb.ids" || curl -s "https://raw.githubusercontent.com/systemd/systemd/main/hwdb.d/usb.ids" || echo "")
70-
71-
if [ -n "$REMOTE_CONTENT" ]; then
72-
# 使用Node.js计算SHA256 hash
73-
REMOTE_HASH=$(node -e "const crypto = require('crypto'); console.log(crypto.createHash('sha256').update(process.argv[1]).digest('hex'));" "$REMOTE_CONTENT")
74-
echo "Remote hash: $REMOTE_HASH"
75-
76-
if [ "$REMOTE_HASH" = "$NPM_HASH" ] && [ -n "$NPM_HASH" ]; then
77-
echo "No update needed, contentHash is the same"
78-
echo "skip=true" >> $GITHUB_OUTPUT
79-
else
80-
echo "Update needed, contentHash is different or no previous version"
81-
echo "skip=false" >> $GITHUB_OUTPUT
82-
fi
83-
else
84-
echo "Failed to fetch remote data, forcing update"
53+
echo "Update needed, content hash is different or error occurred"
8554
echo "skip=false" >> $GITHUB_OUTPUT
8655
fi
8756

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
"prepare": "simple-git-hooks",
5959
"bumpp": "bumpp",
6060
"lint-staged": "lint-staged",
61-
"update-readme-version": "tsx scripts/update-readme-version.ts"
61+
"update-readme-version": "tsx scripts/update-readme-version.ts",
62+
"diff-hash": "tsx scripts/diff-hash.ts"
6263
},
6364
"devDependencies": {
6465
"@antfu/eslint-config": "^4.2.1",

scripts/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,66 @@ This directory contains utility scripts for the USB.IDS project. Each script ser
44

55
## Available Scripts
66

7+
### `diff-hash.ts`
8+
9+
Compares content hashes between remote USB.IDS data and local processed data to determine if an update is needed. Designed for use in GitHub Actions workflow.
10+
11+
#### Purpose
12+
Determines whether the automated update workflow should proceed by comparing the content hash of remote USB.IDS data with the hash from the local version file. This approach avoids circular dependency logic and provides more reliable change detection.
13+
14+
#### Usage
15+
16+
```bash
17+
# Run using npm script (recommended)
18+
pnpm run diff-hash
19+
20+
# Or run directly with tsx
21+
tsx scripts/diff-hash.ts
22+
```
23+
24+
#### What it does
25+
26+
1. Reads local version information from `usb.ids.version.json` file
27+
2. Downloads remote USB.IDS data from official sources using `downloadFromUrls()` from `src/fetcher.ts`
28+
3. Calculates content hash using `generateContentHash()` from `src/parser.ts`
29+
4. Compares the local and remote content hashes
30+
5. Exits with appropriate exit codes for shell script integration
31+
32+
#### Comparison Strategy
33+
34+
- **Local baseline**: Uses the local `usb.ids.version.json` file as the comparison baseline
35+
- **Avoids circular logic**: No longer depends on npm package for version comparison
36+
- **First-run handling**: Automatically triggers update if no local version file exists
37+
- **Content-driven**: Only content changes trigger updates, not timestamps
38+
39+
#### Exit Codes
40+
41+
- **0**: No update needed (content hashes match)
42+
- **1**: Update needed (content hashes differ, no local version, or error occurred)
43+
44+
#### Key Features
45+
46+
- **Shell Integration**: Designed for use in GitHub Actions workflows
47+
- **Error Handling**: Gracefully handles network errors and missing data
48+
- **Code Reuse**: Leverages existing functions from `src/core.ts`, `src/fetcher.ts`, `src/parser.ts`, and `src/utils.ts`
49+
- **Functional Programming**: Uses pure functions and functional programming style
50+
- **Detailed Logging**: Provides clear status messages for debugging
51+
- **Content-based Detection**: Uses SHA256 hash comparison for accurate change detection
52+
- **Consistent Logic**: Reuses `loadVersionInfo()` from core.ts for file operations
53+
54+
#### GitHub Actions Integration
55+
56+
```yaml
57+
- name: Check if update is needed
58+
id: check-update
59+
run: |
60+
if pnpm run diff-hash; then
61+
echo "skip=true" >> $GITHUB_OUTPUT
62+
else
63+
echo "skip=false" >> $GITHUB_OUTPUT
64+
fi
65+
```
66+
767
### `update-readme-version.ts`
868

969
Updates the version information in the project's README.md file based on data from `usb.ids.version.json`.

scripts/diff-hash.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env tsx
2+
3+
/**
4+
* USB.IDS数据哈希差异检查脚本
5+
* 该脚本比较远程数据与本地已处理数据的contentHash来判断是否需要更新
6+
* 避免了使用npm包作为基准的循环逻辑问题
7+
* 复用core.ts中的现有函数以保持代码一致性
8+
*/
9+
10+
import type { VersionInfo } from '../src/types'
11+
import * as path from 'node:path'
12+
import { USB_IDS_SOURCE, USB_IDS_VERSION_JSON_FILE } from '../src/config'
13+
import { loadVersionInfo } from '../src/core'
14+
import { downloadFromUrls } from '../src/fetcher'
15+
import { generateContentHash } from '../src/parser'
16+
import { logger } from '../src/utils'
17+
18+
/**
19+
* 获取本地版本信息
20+
* 复用core.ts中的loadVersionInfo函数
21+
*/
22+
function getLocalVersionInfo(): VersionInfo | null {
23+
try {
24+
const versionFilePath = path.resolve(process.cwd(), USB_IDS_VERSION_JSON_FILE)
25+
const versionInfo = loadVersionInfo(versionFilePath)
26+
27+
if (!versionInfo) {
28+
logger.warn('Local version file does not exist')
29+
return null
30+
}
31+
32+
logger.info(`Local version: ${versionInfo.version}`)
33+
logger.info(`Local hash: ${versionInfo.contentHash}`)
34+
35+
return versionInfo
36+
}
37+
catch (error) {
38+
logger.warn(`Error reading local version info: ${(error as Error).message}`)
39+
return null
40+
}
41+
}
42+
43+
/**
44+
* 获取远程数据的contentHash
45+
* 复用fetcher.ts中的downloadFromUrls和parser.ts中的generateContentHash
46+
*/
47+
async function getRemoteContentHash(): Promise<string | null> {
48+
try {
49+
logger.info('Downloading remote USB.IDS data...')
50+
const content = await downloadFromUrls(USB_IDS_SOURCE)
51+
52+
if (!content) {
53+
logger.warn('Failed to download remote USB.IDS data')
54+
return null
55+
}
56+
57+
const hash = generateContentHash(content)
58+
logger.info(`Remote content hash: ${hash}`)
59+
return hash
60+
}
61+
catch (error) {
62+
logger.warn(`Error downloading remote data: ${(error as Error).message}`)
63+
return null
64+
}
65+
}
66+
67+
/**
68+
* 主函数:比较本地与远程数据的哈希差异
69+
* 使用函数式编程风格,保持逻辑清晰简洁
70+
*/
71+
async function diffHash(): Promise<void> {
72+
try {
73+
logger.start('Comparing local and remote content hashes...')
74+
75+
// 获取本地版本信息(复用core.ts逻辑)
76+
const localInfo = getLocalVersionInfo()
77+
78+
// 获取远程数据hash(复用fetcher和parser逻辑)
79+
const remoteHash = await getRemoteContentHash()
80+
if (!remoteHash) {
81+
logger.info('Failed to get remote hash, forcing update')
82+
process.exit(1) // 退出码1表示需要更新
83+
}
84+
85+
// 如果没有本地版本信息,说明是首次运行,需要更新
86+
if (!localInfo) {
87+
logger.info('No local version info found, update needed for initial setup')
88+
process.exit(1) // 退出码1表示需要更新
89+
}
90+
91+
// 比较hash值
92+
if (remoteHash === localInfo.contentHash) {
93+
logger.success('No difference found, content hash is the same')
94+
process.exit(0) // 退出码0表示不需要更新
95+
}
96+
else {
97+
logger.info('Hash difference detected')
98+
logger.info(`Remote: ${remoteHash}`)
99+
logger.info(`Local: ${localInfo.contentHash}`)
100+
process.exit(1) // 退出码1表示需要更新
101+
}
102+
}
103+
catch (error) {
104+
logger.error(`Hash comparison failed: ${(error as Error).message}`)
105+
process.exit(1) // 出错时也强制更新
106+
}
107+
}
108+
109+
// 当直接运行此脚本时执行检查
110+
if (import.meta.url === `file://${process.argv[1]}`) {
111+
diffHash()
112+
}
113+
114+
export { diffHash }

0 commit comments

Comments
 (0)