From 6a71a257ac1d3fd24e7718725994dc327729ee4e Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:35:15 +0800 Subject: [PATCH 01/15] ci: add Chrome extension validator --- scripts/validate-chrome-extension.mjs | 105 ++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 scripts/validate-chrome-extension.mjs diff --git a/scripts/validate-chrome-extension.mjs b/scripts/validate-chrome-extension.mjs new file mode 100644 index 0000000..69f9689 --- /dev/null +++ b/scripts/validate-chrome-extension.mjs @@ -0,0 +1,105 @@ +import { existsSync, readFileSync, statSync } from "node:fs"; +import { dirname, join, normalize, relative, resolve, sep } from "node:path"; +import { spawnSync } from "node:child_process"; + +const repoRoot = resolve(process.cwd()); +const extensionRoot = resolve(repoRoot, process.argv[2] ?? "chrome-extension"); +const manifestPath = join(extensionRoot, "manifest.json"); + +function fail(message) { + console.error(`::error::${message}`); + process.exitCode = 1; +} + +function assert(condition, message) { + if (!condition) fail(message); +} + +function safeExtensionPath(value) { + const normalized = normalize(value).replace(/^[/\\]+/, ""); + const absolute = resolve(extensionRoot, normalized); + const relativePath = relative(extensionRoot, absolute); + if (relativePath.startsWith(`..${sep}`) || relativePath === "..") { + fail(`Manifest path escapes chrome-extension/: ${value}`); + return null; + } + return absolute; +} + +function addReference(references, value, source) { + if (typeof value !== "string" || value.trim() === "") return; + if (value.includes("*")) return; + references.push({ value, source }); +} + +assert(existsSync(manifestPath), "chrome-extension/manifest.json is missing"); +if (!existsSync(manifestPath)) process.exit(1); + +let manifest; +try { + manifest = JSON.parse(readFileSync(manifestPath, "utf8")); +} catch (error) { + fail(`manifest.json is not valid JSON: ${error.message}`); + process.exit(1); +} + +assert(manifest.manifest_version === 3, "manifest_version must be 3"); +assert(typeof manifest.name === "string" && manifest.name.trim() !== "", "manifest name is required"); +assert(/^\d+\.\d+\.\d+(?:\.\d+)?$/.test(manifest.version ?? ""), "manifest version must use numeric dot notation"); +assert(Array.isArray(manifest.permissions), "permissions must be an array"); +assert(Array.isArray(manifest.host_permissions), "host_permissions must be an array"); + +const references = []; +addReference(references, manifest.background?.service_worker, "background.service_worker"); +addReference(references, manifest.action?.default_popup, "action.default_popup"); +addReference(references, manifest.options_page, "options_page"); +addReference(references, manifest.options_ui?.page, "options_ui.page"); +addReference(references, manifest.devtools_page, "devtools_page"); + +for (const [size, icon] of Object.entries(manifest.icons ?? {})) { + addReference(references, icon, `icons.${size}`); +} + +for (const [index, script] of (manifest.content_scripts ?? []).entries()) { + for (const file of script.js ?? []) addReference(references, file, `content_scripts[${index}].js`); + for (const file of script.css ?? []) addReference(references, file, `content_scripts[${index}].css`); +} + +for (const [index, resourceGroup] of (manifest.web_accessible_resources ?? []).entries()) { + for (const file of resourceGroup.resources ?? []) { + addReference(references, file, `web_accessible_resources[${index}]`); + } +} + +for (const reference of references) { + const absolute = safeExtensionPath(reference.value); + if (!absolute) continue; + assert(existsSync(absolute), `${reference.source} references missing file: ${reference.value}`); + if (existsSync(absolute)) { + assert(statSync(absolute).isFile(), `${reference.source} must reference a file: ${reference.value}`); + } +} + +const javascriptFiles = references + .map(({ value }) => value) + .filter((value) => value.endsWith(".js")) + .filter((value, index, array) => array.indexOf(value) === index); + +for (const file of javascriptFiles) { + const absolute = safeExtensionPath(file); + if (!absolute || !existsSync(absolute)) continue; + const result = spawnSync(process.execPath, ["--check", absolute], { + cwd: repoRoot, + encoding: "utf8", + }); + if (result.status !== 0) { + fail(`JavaScript syntax check failed for ${file}\n${result.stderr || result.stdout}`); + } +} + +if (process.exitCode) process.exit(process.exitCode); + +console.log(`Chrome extension validation passed.`); +console.log(`Manifest: ${relative(repoRoot, manifestPath)}`); +console.log(`Referenced files checked: ${references.length}`); +console.log(`JavaScript files syntax-checked: ${javascriptFiles.length}`); From 08eed27d2060f43a96d0d060b490267986d90e59 Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:35:37 +0800 Subject: [PATCH 02/15] security: add CodeQL analysis workflow --- .github/workflows/codeql.yml | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..9c14048 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,71 @@ +name: CodeQL + +on: + push: + branches: + - main + pull_request: + branches: + - main + schedule: + - cron: "23 3 * * 1" + workflow_dispatch: + +permissions: + contents: read + security-events: write + packages: read + actions: read + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - language: java-kotlin + build-mode: manual + - language: javascript-typescript + build-mode: none + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + queries: security-extended + + - name: Setup Java + if: matrix.language == 'java-kotlin' + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: "21" + cache: gradle + + - name: Build Java for CodeQL + if: matrix.language == 'java-kotlin' + env: + APP_AUTO_OPEN_BROWSER: "false" + APP_BROWSER_HEADLESS: "true" + APP_CACHE_DIR: /tmp/ai-jobpilot-codeql/cache + APP_DATA_DIR: /tmp/ai-jobpilot-codeql/data + APP_OUTPUT_DIR: /tmp/ai-jobpilot-codeql/output + LOGGING_FILE_NAME: /tmp/ai-jobpilot-codeql/logs/get-jobs.log + SPRING_DATASOURCE_URL: jdbc:sqlite:/tmp/ai-jobpilot-codeql/getjobs-codeql.db + run: | + mkdir -p "$APP_CACHE_DIR" "$APP_DATA_DIR" "$APP_OUTPUT_DIR" "$(dirname "$LOGGING_FILE_NAME")" + chmod +x ./gradlew + ./gradlew clean classes --no-daemon + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{ matrix.language }}" From 3be2726feace2d0134d0c662a4bb7ca185dfc95e Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:35:59 +0800 Subject: [PATCH 03/15] ci: validate Chrome extension and Docker configuration --- .github/workflows/ci.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41461f9..b24ba59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,3 +76,40 @@ jobs: - name: Build frontend working-directory: front run: pnpm build + + chrome-extension: + name: Chrome Extension + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Validate manifest and referenced files + run: node scripts/validate-chrome-extension.mjs + + docker-config: + name: Docker Configuration + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Validate Docker Compose + run: docker compose config --quiet + + - name: Verify required Dockerfile stages + shell: bash + run: | + for stage in backend-dev frontend-dev frontend-build backend-build runtime; do + grep -Eq "^[[:space:]]*FROM[[:space:]].*[[:space:]]AS[[:space:]]${stage}([[:space:]]|$)" Dockerfile || { + echo "Missing Dockerfile stage: ${stage}" + exit 1 + } + done From fb1417ba6ad1c7eaf72070cf31503bc531fe7972 Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:36:41 +0800 Subject: [PATCH 04/15] release: configure generated release notes --- .github/release.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/release.yml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..c01b360 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,35 @@ +changelog: + exclude: + labels: + - skip-changelog + authors: + - dependabot[bot] + + categories: + - title: "🚀 Features" + labels: + - enhancement + - feature + + - title: "🐛 Fixes" + labels: + - bug + - fix + + - title: "🔒 Security" + labels: + - security + + - title: "📚 Documentation" + labels: + - documentation + + - title: "🧰 Maintenance" + labels: + - dependencies + - chore + - ci + + - title: "Other Changes" + labels: + - "*" From a6b7bcd387e8bccf240a969ab287cb2400aa918b Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:37:16 +0800 Subject: [PATCH 05/15] release: add validated artifact workflow --- .github/workflows/release.yml | 137 ++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..107c7d6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,137 @@ +name: Release + +on: + push: + tags: + - "v*.*.*" + workflow_dispatch: + inputs: + version: + description: "Version label used for preview artifacts, for example v1.3.1-rc.1" + required: true + type: string + +permissions: + contents: write + +jobs: + build-and-package: + name: Build and package release artifacts + runs-on: ubuntu-latest + timeout-minutes: 40 + env: + APP_AUTO_OPEN_BROWSER: "false" + APP_BROWSER_HEADLESS: "true" + APP_CACHE_DIR: /tmp/ai-jobpilot-release/cache + APP_DATA_DIR: /tmp/ai-jobpilot-release/data + APP_OUTPUT_DIR: /tmp/ai-jobpilot-release/output + LOGGING_FILE_NAME: /tmp/ai-jobpilot-release/logs/get-jobs.log + SPRING_DATASOURCE_URL: jdbc:sqlite:/tmp/ai-jobpilot-release/getjobs-release.db + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Resolve version + shell: bash + run: | + if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then + VERSION="${{ inputs.version }}" + else + VERSION="$GITHUB_REF_NAME" + fi + + if [[ ! "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid version: $VERSION" + exit 1 + fi + + echo "RELEASE_VERSION=$VERSION" >> "$GITHUB_ENV" + + - name: Setup Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: "21" + cache: gradle + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.20.0 + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: "20" + cache: pnpm + cache-dependency-path: front/pnpm-lock.yaml + + - name: Install frontend dependencies + working-directory: front + run: pnpm install --frozen-lockfile + + - name: Validate frontend + working-directory: front + run: | + pnpm lint + pnpm build + + - name: Validate Chrome extension + run: node scripts/validate-chrome-extension.mjs + + - name: Build backend JAR with frontend assets + shell: bash + run: | + mkdir -p "$APP_CACHE_DIR" "$APP_DATA_DIR" "$APP_OUTPUT_DIR" "$(dirname "$LOGGING_FILE_NAME")" + rm -rf src/main/resources/dist + mkdir -p src/main/resources/dist + cp -a front/out/. src/main/resources/dist/ + chmod +x ./gradlew + ./gradlew clean test bootJar --no-daemon + + - name: Assemble release artifacts + shell: bash + run: | + mkdir -p dist + + JAR_PATH="$(find build/libs -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | head -n 1)" + if [[ -z "$JAR_PATH" ]]; then + echo "No bootable JAR found in build/libs" + exit 1 + fi + + cp "$JAR_PATH" "dist/AI-JobPilot-${RELEASE_VERSION}.jar" + (cd chrome-extension && zip -qr "../dist/AI-JobPilot-${RELEASE_VERSION}-chrome-extension.zip" .) + (cd front/out && zip -qr "../../dist/AI-JobPilot-${RELEASE_VERSION}-frontend-static.zip" .) + git archive --format=zip --output="dist/AI-JobPilot-${RELEASE_VERSION}-source.zip" HEAD + + ( + cd dist + sha256sum AI-JobPilot-* > SHA256SUMS.txt + ) + + - name: Upload preview artifacts + uses: actions/upload-artifact@v4 + with: + name: AI-JobPilot-${{ env.RELEASE_VERSION }} + path: dist/* + if-no-files-found: error + retention-days: 14 + + - name: Publish GitHub Release for tag + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + EXTRA_ARGS=() + if [[ "$RELEASE_VERSION" == *-* ]]; then + EXTRA_ARGS+=(--prerelease) + fi + + gh release create "$GITHUB_REF_NAME" dist/* \ + --verify-tag \ + --title "AI JobPilot ${GITHUB_REF_NAME}" \ + --generate-notes \ + "${EXTRA_ARGS[@]}" From bb5c0fa0b8ee177f486963b5a38c765b161cfbd6 Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:38:04 +0800 Subject: [PATCH 06/15] docs: add release and artifact guide --- docs/releases.md | 138 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/releases.md diff --git a/docs/releases.md b/docs/releases.md new file mode 100644 index 0000000..6a6f51d --- /dev/null +++ b/docs/releases.md @@ -0,0 +1,138 @@ +# 版本发布与下载说明 + +本文说明 AI JobPilot 的版本命名、GitHub Release 产物、校验方式和发布边界。 + +## 当前发布状态 + +AI JobPilot 当前主要面向 Windows 本地开发与个人使用。 + +仓库已建立自动化 Release 构建流程,但现阶段产物属于**技术预览包**,还不是不依赖 Java、Node.js 和 pnpm 的完整桌面安装器。Windows 一键发行包仍通过 GitHub Issue 持续跟踪。 + +## Release 产物 + +正式推送符合 `v*.*.*` 的 Git 标签后,Release 工作流会先执行: + +1. 前端依赖锁定安装 +2. 前端 lint 与静态构建 +3. Chrome 扩展清单、引用文件与 JavaScript 语法检查 +4. 后端测试 +5. 包含前端静态资源的 Spring Boot JAR 构建 +6. 产物 SHA256 校验值生成 + +成功后发布以下文件: + +```text +AI-JobPilot-vX.Y.Z.jar +AI-JobPilot-vX.Y.Z-chrome-extension.zip +AI-JobPilot-vX.Y.Z-frontend-static.zip +AI-JobPilot-vX.Y.Z-source.zip +SHA256SUMS.txt +``` + +### 后端 JAR + +包含后端代码和构建时生成的前端静态资源,适合开发者进行运行验证和二次打包。 + +该文件目前不等同于完整桌面安装器。运行前仍需 Java 21,并需要按照项目文档准备本地配置和数据目录。 + +### Chrome 扩展包 + +解压后可在 Chrome 扩展管理页通过“加载已解压的扩展程序”进行安装。 + +扩展包不包含: + +- 招聘平台账号 +- Cookie 或 Token +- 简历 +- API Key +- Chrome 用户目录 + +### 前端静态包 + +提供构建后的 Next.js 静态文件,主要用于开发验证、后端集成和部署测试。 + +### 源码包 + +由对应 Git 标签直接生成,便于确认 Release 与仓库版本一致。 + +## 校验下载文件 + +下载 Release 文件后,建议使用 `SHA256SUMS.txt` 验证完整性。 + +### Windows PowerShell + +```powershell +Get-FileHash .\AI-JobPilot-vX.Y.Z.jar -Algorithm SHA256 +Get-Content .\SHA256SUMS.txt +``` + +将两处 SHA256 值进行对比。 + +### Linux / macOS + +```bash +sha256sum -c SHA256SUMS.txt +``` + +## 版本命名 + +采用语义化版本: + +```text +v主版本.次版本.修订版本 +``` + +示例: + +```text +v1.3.1 +v1.4.0 +v2.0.0 +``` + +预发布版本可以使用: + +```text +v1.4.0-rc.1 +v1.4.0-beta.1 +``` + +带有 `-rc`、`-beta` 等后缀的标签会作为 GitHub Prerelease 发布。 + +## 发布前检查 + +维护者发布版本前应确认: + +- [ ] `main` 分支 CI 全部通过 +- [ ] CodeQL 没有未处理的高危告警 +- [ ] `CHANGELOG.md` 已更新 +- [ ] `package.json`、Gradle 和 Chrome 扩展版本号一致 +- [ ] 示例截图和文档没有 Cookie、API Key、简历或个人账号信息 +- [ ] `.env`、数据库、日志和 Chrome 用户目录未进入提交 +- [ ] 已知限制已写入 Release Notes +- [ ] Release 产物 SHA256 校验通过 + +## 手动预览构建 + +维护者可以在 GitHub Actions 中手动运行 `Release` 工作流并填写版本号,例如: + +```text +v1.3.1-rc.1 +``` + +手动运行只生成保留 14 天的 Actions Artifact,不会自动创建公开 Release。 + +只有推送正式 Git 标签时,工作流才会创建 GitHub Release。 + +## 安全边界 + +任何 Release 产物都不得包含: + +- `.env` 或密钥配置 +- 本地 SQLite 数据库 +- 简历和用户上传文件 +- 招聘平台 Cookie、Token 或聊天记录 +- 浏览器缓存和 Chrome Profile +- 运行日志 + +发现 Release 中包含敏感信息时,应立即删除对应 Release 和标签、轮换相关密钥,并按照 `SECURITY.md` 处理。 From faae793d0f31a8bb396097c039211d40546bbaf4 Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:38:53 +0800 Subject: [PATCH 07/15] security: document automated checks and release safety --- SECURITY.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/SECURITY.md b/SECURITY.md index 3a7b561..80c553b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -81,6 +81,24 @@ AI JobPilot(投递牛马)默认作为个人电脑上的本地工具使用。 - 不应把包含隐私数据的日志、数据库或浏览器缓存上传为 Artifact。 - 新增第三方 Action 时,应优先锁定可信版本或提交 SHA。 +## 自动化安全检查 + +仓库使用以下自动化检查降低常见风险: + +- **CodeQL**:分别分析 Java / Kotlin 与 JavaScript / TypeScript,包含扩展安全查询。 +- **Dependabot**:每周检查 Gradle、前端依赖和 GitHub Actions,默认不自动提交大版本升级。 +- **Chrome 扩展校验**:检查 Manifest V3、版本号、声明文件、脚本引用和 JavaScript 语法。 +- **CI 临时数据隔离**:测试只使用 GitHub Runner 的临时目录和 SQLite 测试数据库。 +- **Release 校验**:发布前执行测试、构建、扩展校验并生成 SHA256 校验文件。 + +自动化扫描通过并不代表项目不存在安全问题。涉及账号会话、浏览器消息、文件路径、日志输出和外部请求的修改仍需人工审查。 + +## Release 安全 + +Release 产物不得包含 `.env`、数据库、日志、Chrome Profile、真实简历、Cookie、Token 或 API Key。 + +发布后应使用 `SHA256SUMS.txt` 校验文件完整性。完整发布流程与校验方法见 [`docs/releases.md`](docs/releases.md)。 + ## 后续能力的安全要求 AI 代聊 HR、消息提醒、面试信息汇总等能力上线前,必须明确: From e655728d8e0c729f2f52e556a9fac949021331fb Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:39:20 +0800 Subject: [PATCH 08/15] docs: add release and automation navigation --- docs/README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 6d1c863..20638d4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # AI JobPilot 文档中心 -这里汇总 AI JobPilot 的使用、开发、架构、安全和演示资料。当前部分历史文档仍位于仓库根目录或 `doc/`,后续会在不破坏链接的前提下逐步迁移。 +这里汇总 AI JobPilot 的使用、开发、架构、安全、版本发布和演示资料。当前部分历史文档仍位于仓库根目录或 `doc/`,后续会在不破坏链接的前提下逐步迁移。 ## 普通用户 @@ -9,6 +9,7 @@ | [项目主页](../README.md) | 产品定位、能力、截图、快速开始与平台支持 | | [Windows 新手部署](../WINDOWS_SETUP.md) | Windows 安装、启动、验证和排错 | | [任务流程](../TASK_FLOW.md) | 从简历配置、岗位采集到人工确认的完整流程 | +| [版本发布与下载](releases.md) | Release 产物、版本命名、SHA256 校验和发布边界 | | [安全说明](../SECURITY.md) | API Key、Cookie、简历、数据库与浏览器数据边界 | | [路线图](../ROADMAP.md) | 当前阶段、下一步和长期方向 | | [版本记录](../CHANGELOG.md) | 已发布与待发布变更 | @@ -21,6 +22,7 @@ | [开发者本地启动](development/setup.md) | Java、Node、pnpm、后端、前端、扩展和测试 | | [系统架构](../ARCHITECTURE.md) | 模块职责、数据流与 SaaS 演进说明 | | [安全报告](../SECURITY.md#报告安全问题) | 如何在不公开敏感数据的情况下报告问题 | +| [自动化安全检查](../SECURITY.md#自动化安全检查) | CodeQL、Dependabot、扩展校验和 Release 校验范围 | ## Demo 与测试资料 @@ -40,6 +42,15 @@ | [Boss 搜索 API POC](../doc/BOSS_API_POC.md) | Boss 搜索 API 的手动验证和降级路径 | | [历史文档索引](../doc/文档索引.md) | 旧方案、实施记录和补充说明 | +## 自动化工作流 + +| 工作流 | 作用 | +| --- | --- | +| `CI` | 后端测试与构建、前端 lint 与构建、Chrome 扩展校验、Docker 配置校验 | +| `CodeQL` | Java / Kotlin 与 JavaScript / TypeScript 安全分析 | +| `Release` | 版本预览构建、标签发布、产物打包和 SHA256 校验 | +| `Dependabot` | 每周检查后端、前端和 GitHub Actions 依赖更新 | + ## 维护原则 新增文档时请遵循: From f19eb4509d659ff85cd00fe52d00cd010901adb9 Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:40:31 +0800 Subject: [PATCH 09/15] docs: expose security and release status on homepage --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9fc51d..074ec7e 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,15 @@ **一个本地运行、由用户确认后执行的 AI 求职工作台。** 采集岗位、分析匹配度、管理待确认任务,并持续记录投递结果。 -[English](README.en.md) · [快速开始](#快速开始) · [完整流程](TASK_FLOW.md) · [架构说明](ARCHITECTURE.md) · [路线图](ROADMAP.md) · [安全说明](SECURITY.md) +[English](README.en.md) · [快速开始](#快速开始) · [下载与版本](docs/releases.md) · [文档中心](docs/README.md) · [路线图](ROADMAP.md) · [安全说明](SECURITY.md) [![Version](https://img.shields.io/badge/version-1.3.0-4f46e5.svg)](CHANGELOG.md) [![Platform](https://img.shields.io/badge/platform-Windows-0078D4.svg)](WINDOWS_SETUP.md) [![Java](https://img.shields.io/badge/Java-21-E76F00.svg)](build.gradle.kts) [![Node](https://img.shields.io/badge/Node.js-20.19%2B-339933.svg)](front/package.json) [![CI](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/ci.yml/badge.svg)](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/ci.yml) +[![CodeQL](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/codeql.yml/badge.svg)](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/codeql.yml) +[![Release](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/release.yml/badge.svg)](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/release.yml) [![License](https://img.shields.io/badge/license-Non--Commercial-f59e0b.svg)](LICENSE) @@ -97,6 +99,24 @@ Boss 直聘和智联招聘支持 Chrome Bridge,复用你已登录的 Chrome `✅` 表示当前主流程支持,`🟡` 表示已有基础能力但稳定性和覆盖范围仍需继续验证。 +## 下载与版本 + +GitHub Release 工作流会在版本标签发布前自动执行后端测试、前端 lint 与构建、Chrome 扩展校验,并生成 SHA256 校验文件。 + +当前自动化产物包括: + +```text +AI-JobPilot-vX.Y.Z.jar +AI-JobPilot-vX.Y.Z-chrome-extension.zip +AI-JobPilot-vX.Y.Z-frontend-static.zip +AI-JobPilot-vX.Y.Z-source.zip +SHA256SUMS.txt +``` + +这些产物目前属于技术预览包,**还不是不依赖 Java、Node.js 和 pnpm 的完整 Windows 安装器**。详细说明、校验方法和发布边界见 [docs/releases.md](docs/releases.md)。 + +正式版本发布后,可在 [GitHub Releases](https://github.com/damingishere-coder/AI-JobPilot/releases) 下载。 + ## 快速开始 ### Windows 本机启动 @@ -218,13 +238,16 @@ db/getjobs.db - 项目不会绕过平台风控、登录验证、验证码或投递频率限制。 - OpenClaw 通路仍属于实验能力,不是 Windows 主流程的必需项。 - 当前以 Windows 单机使用为主,不是面向公网多人使用的 SaaS 服务。 +- Release 自动化已经建立,但完整 Windows 免开发环境安装器尚未完成。 ## 文档 | 文档 | 用途 | | --- | --- | +| [docs/README.md](docs/README.md) | 使用、开发、安全、Demo 与版本发布的统一文档导航 | | [WINDOWS_SETUP.md](WINDOWS_SETUP.md) | Windows 安装、启动、验证和排错 | | [TASK_FLOW.md](TASK_FLOW.md) | 从简历配置到确认投递的完整流程 | +| [docs/releases.md](docs/releases.md) | Release 产物、版本命名和 SHA256 校验 | | [ARCHITECTURE.md](ARCHITECTURE.md) | 系统架构、模块职责与数据流 | | [SECURITY.md](SECURITY.md) | 本地数据、Cookie、API Key 与安全边界 | | [ROADMAP.md](ROADMAP.md) | 当前阶段、后续方向与优先事项 | @@ -237,7 +260,7 @@ db/getjobs.db 当前版本为 `1.3.0`,重点是巩固 Boss / 智联 Chrome Bridge 采集、AI 分析、人工确认、失败诊断和本地可维护性。 -后续计划包括统一平台适配层、继续组件化其他分析页面,以及在明确合规和人工确认边界后评估 AI 代聊、提醒和面试信息汇总能力。详见 [ROADMAP.md](ROADMAP.md)。 +仓库现已具备后端与前端 CI、Chrome 扩展校验、Docker 配置校验、CodeQL 安全扫描、Dependabot 依赖维护和 Release 产物校验。后续计划包括统一平台适配层、离线 Demo、完整 Windows 发行包,以及在明确合规和人工确认边界后评估 AI 代聊、提醒和面试信息汇总能力。详见 [ROADMAP.md](ROADMAP.md)。 ## 参与贡献 From 1bcff7ea737d2fac0610b4f7017aba93c9816417 Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:41:18 +0800 Subject: [PATCH 10/15] docs: update English security and release status --- README.en.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/README.en.md b/README.en.md index e011b82..0eecf99 100644 --- a/README.en.md +++ b/README.en.md @@ -5,13 +5,15 @@ **A local-first, human-in-the-loop AI workspace for job searching.** Collect job listings, analyze fit, review suggested actions, and track application results from one place. -[简体中文](README.md) · [Quick Start](#quick-start) · [Task Flow](TASK_FLOW.md) · [Architecture](ARCHITECTURE.md) · [Roadmap](ROADMAP.md) · [Security](SECURITY.md) +[简体中文](README.md) · [Quick Start](#quick-start) · [Downloads](docs/releases.md) · [Documentation](docs/README.md) · [Roadmap](ROADMAP.md) · [Security](SECURITY.md) [![Version](https://img.shields.io/badge/version-1.3.0-4f46e5.svg)](CHANGELOG.md) [![Platform](https://img.shields.io/badge/platform-Windows-0078D4.svg)](WINDOWS_SETUP.md) [![Java](https://img.shields.io/badge/Java-21-E76F00.svg)](build.gradle.kts) [![Node](https://img.shields.io/badge/Node.js-20.19%2B-339933.svg)](front/package.json) [![CI](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/ci.yml/badge.svg)](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/ci.yml) +[![CodeQL](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/codeql.yml/badge.svg)](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/codeql.yml) +[![Release](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/release.yml/badge.svg)](https://github.com/damingishere-coder/AI-JobPilot/actions/workflows/release.yml) [![License](https://img.shields.io/badge/license-Non--Commercial-f59e0b.svg)](LICENSE) @@ -76,6 +78,24 @@ AI JobPilot brings these steps into a local workspace: `✅` means the current primary workflow is supported. `🟡` means basic functionality exists but coverage and stability still need validation. +## Downloads and releases + +The GitHub Release workflow runs backend tests, frontend lint and build, Chrome extension validation, and SHA256 generation before publishing a tagged version. + +Current automated artifacts are: + +```text +AI-JobPilot-vX.Y.Z.jar +AI-JobPilot-vX.Y.Z-chrome-extension.zip +AI-JobPilot-vX.Y.Z-frontend-static.zip +AI-JobPilot-vX.Y.Z-source.zip +SHA256SUMS.txt +``` + +These are technical preview artifacts, **not yet a complete Windows installer that removes the Java, Node.js, and pnpm requirements**. See [docs/releases.md](docs/releases.md) for artifact details, checksum instructions, and release boundaries. + +Published versions are available from [GitHub Releases](https://github.com/damingishere-coder/AI-JobPilot/releases). + ## Quick start ### Windows @@ -191,19 +211,28 @@ Read [SECURITY.md](SECURITY.md) before reporting a security issue or sharing dia - The project does not bypass authentication, CAPTCHAs, anti-abuse controls, or application rate limits. - OpenClaw integration remains experimental and is not required for the primary Windows workflow. - The current product is a Windows-focused single-user local application, not a hosted SaaS platform. +- Release automation is available, but a full Windows installer without development prerequisites is not finished yet. ## Documentation | Document | Purpose | | --- | --- | +| [docs/README.md](docs/README.md) | Unified navigation for usage, development, security, demo, and release documents | | [WINDOWS_SETUP.md](WINDOWS_SETUP.md) | Windows installation, startup, verification, and troubleshooting | | [TASK_FLOW.md](TASK_FLOW.md) | End-to-end flow from resume configuration to reviewed application actions | +| [docs/releases.md](docs/releases.md) | Release artifacts, versioning, and SHA256 verification | | [ARCHITECTURE.md](ARCHITECTURE.md) | Architecture, module responsibilities, and data flow | | [SECURITY.md](SECURITY.md) | Local data, cookies, API keys, and security boundaries | | [ROADMAP.md](ROADMAP.md) | Current stage, priorities, and future direction | | [CHANGELOG.md](CHANGELOG.md) | Release history | | [CONTRIBUTING.md](CONTRIBUTING.md) | Bug reports, feature proposals, and code contributions | +## Project status + +The repository now includes backend and frontend CI, Chrome extension validation, Docker configuration validation, CodeQL security analysis, Dependabot maintenance, and validated Release artifacts. + +The next priorities are the unified platform adapter, offline Demo mode, a complete Windows distribution, and carefully scoped communication or interview-assistance capabilities that preserve platform compliance and human confirmation. See [ROADMAP.md](ROADMAP.md). + ## Contributing Bug reports, documentation improvements, reproducible platform issues, and adapter contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request. From c092819375d0df9b8d58cb6a6fb706e869ff38bc Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:41:47 +0800 Subject: [PATCH 11/15] docs: record third-round repository improvements --- CHANGELOG.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02299a2..2834d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,31 @@ ## [Unreleased] +### Added + +- CodeQL Java / Kotlin 与 JavaScript / TypeScript 安全扫描 +- Chrome 扩展 Manifest、引用文件和 JavaScript 语法自动校验 +- Docker Compose 与 Dockerfile 阶段配置检查 +- Git 标签驱动的 Release 构建、打包和 SHA256 校验流程 +- Release Notes 分类配置 +- Release 产物、版本命名和校验文档 +- Dependabot、CODEOWNERS、开发者启动文档和统一文档中心 +- 完全虚构的 Demo 简历、岗位和分析示例数据 + +### Changed + +- CI 从后端与前端双任务扩展为后端、前端、Chrome 扩展和 Docker 配置四类检查 +- Dependabot 默认只自动提交 Minor 与 Patch 更新,避免未经评估的大版本升级 +- 中英文 README 增加 CodeQL、Release、下载说明和文档中心入口 +- 安全文档增加自动化检查和 Release 数据边界 + ### 计划中 +- 将 Demo 示例数据接入独立、可重置且不触发真实投递的离线 Demo 模式 +- 提供不依赖开发环境的完整 Windows 发行包 - 统一智联、猎聘和 51job 的平台适配层 - 继续拆分其他平台分析页面的 hooks 与 components -- 增加脱敏 Demo 数据和首次体验流程 -- 补充真实界面截图、操作 GIF 和发布包 +- 补充真实界面截图和操作 GIF - 继续将旧兼容 DDL 迁移到 Flyway ## [1.3.0] - 2026-08-03 From a3ee2e1d3be22cca88c4005b36a57f62b6d1742c Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:44:10 +0800 Subject: [PATCH 12/15] release: validate packaging workflow in pull requests --- .github/workflows/release.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 107c7d6..1268e38 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,10 @@ on: push: tags: - "v*.*.*" + pull_request: + paths: + - ".github/workflows/release.yml" + - "scripts/validate-chrome-extension.mjs" workflow_dispatch: inputs: version: @@ -37,6 +41,8 @@ jobs: run: | if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then VERSION="${{ inputs.version }}" + elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then + VERSION="v0.0.0-pr${{ github.event.pull_request.number }}" else VERSION="$GITHUB_REF_NAME" fi From f1913c5f8b0ce7769d3697285f3753af65161943 Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:50:45 +0800 Subject: [PATCH 13/15] ci: print actionable backend test diagnostics --- .github/workflows/ci.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b24ba59..57093f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,28 @@ jobs: chmod +x ./gradlew - name: Run backend tests - run: ./gradlew test --no-daemon + run: ./gradlew test --no-daemon --stacktrace + + - name: Print backend failure diagnostics + if: failure() + shell: bash + run: | + echo "::group::JUnit XML reports" + find build/test-results/test -type f -name '*.xml' -print -exec cat {} \; 2>/dev/null || true + echo "::endgroup::" + + echo "::group::Playwright profile-lock test source" + find src/test -type f -name '*PlaywrightManagerProfileLockTest*' -print -exec sed -n '1,240p' {} \; 2>/dev/null || true + echo "::endgroup::" + + echo "::group::Related Playwright manager source" + grep -RIl --include='*.java' 'SingletonLock\|SingletonCookie\|SingletonSocket' src/main src/test 2>/dev/null \ + | sort -u \ + | while read -r file; do + echo "===== $file =====" + sed -n '1,320p' "$file" + done + echo "::endgroup::" - name: Build backend run: ./gradlew build --no-daemon From c0a61ecc03e096ee911c723b202659e3070b49dc Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:54:53 +0800 Subject: [PATCH 14/15] ci: stabilize profile-lock test environment --- .github/workflows/ci.yml | 56 +++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57093f3..077aa23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 env: + HOSTNAME: ai-jobpilot-ci APP_AUTO_OPEN_BROWSER: "false" APP_BROWSER_HEADLESS: "true" APP_CACHE_DIR: /tmp/ai-jobpilot-ci/cache @@ -25,10 +26,10 @@ jobs: SPRING_DATASOURCE_URL: jdbc:sqlite:/tmp/ai-jobpilot-ci/getjobs-ci.db steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: "21" @@ -42,26 +43,33 @@ jobs: - name: Run backend tests run: ./gradlew test --no-daemon --stacktrace - - name: Print backend failure diagnostics + - name: Print failed JUnit reports if: failure() shell: bash run: | - echo "::group::JUnit XML reports" - find build/test-results/test -type f -name '*.xml' -print -exec cat {} \; 2>/dev/null || true - echo "::endgroup::" - - echo "::group::Playwright profile-lock test source" - find src/test -type f -name '*PlaywrightManagerProfileLockTest*' -print -exec sed -n '1,240p' {} \; 2>/dev/null || true - echo "::endgroup::" - - echo "::group::Related Playwright manager source" - grep -RIl --include='*.java' 'SingletonLock\|SingletonCookie\|SingletonSocket' src/main src/test 2>/dev/null \ - | sort -u \ - | while read -r file; do - echo "===== $file =====" - sed -n '1,320p' "$file" - done - echo "::endgroup::" + found_failure=false + for report in build/test-results/test/*.xml; do + if grep -Eq 'failures="[1-9]|errors="[1-9]' "$report"; then + found_failure=true + echo "::group::$report" + cat "$report" + echo "::endgroup::" + fi + done + if [[ "$found_failure" == "false" ]]; then + echo "No failed JUnit XML report was found." + fi + + - name: Upload backend test reports + if: failure() + uses: actions/upload-artifact@v4 + with: + name: backend-test-reports + path: | + build/reports/tests/test + build/test-results/test + if-no-files-found: ignore + retention-days: 7 - name: Build backend run: ./gradlew build --no-daemon @@ -72,7 +80,7 @@ jobs: timeout-minutes: 20 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -80,7 +88,7 @@ jobs: version: 10.20.0 - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: "20" cache: pnpm @@ -104,10 +112,10 @@ jobs: timeout-minutes: 10 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: "20" @@ -120,7 +128,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Validate Docker Compose run: docker compose config --quiet From 570c48d3a47c90eb3c81afc4661c4057043ccdab Mon Sep 17 00:00:00 2001 From: DAMING Date: Mon, 3 Aug 2026 15:55:30 +0800 Subject: [PATCH 15/15] release: stabilize Linux profile-lock tests --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1268e38..e251999 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 40 env: + HOSTNAME: ai-jobpilot-release APP_AUTO_OPEN_BROWSER: "false" APP_BROWSER_HEADLESS: "true" APP_CACHE_DIR: /tmp/ai-jobpilot-release/cache