-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
test: add e2e tests #5739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: add e2e tests #5739
Changes from 15 commits
7898718
c531e0f
977374a
042d9c1
458861e
b563d97
f32e4b0
2b96ab0
ac059c6
ab8ae78
71c18b2
0a7bc37
1f5ccd8
7295bed
161b853
1654355
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| name: 'Clone Repositories' | ||
| description: 'Clone self and upstream repositories' | ||
|
|
||
| inputs: | ||
| ecosystem-ci-project: | ||
| description: 'The ecosystem ci project to clone' | ||
| required: false | ||
| default: '' | ||
|
|
||
| runs: | ||
| using: 'composite' | ||
| steps: | ||
| - name: Output ecosystem ci project hash | ||
| shell: bash | ||
| id: ecosystem-ci-project-hash | ||
| if: ${{ inputs.ecosystem-ci-project != '' }} | ||
| run: | | ||
| node -e "const fs = require('fs'); const data = JSON.parse(fs.readFileSync('./ecosystem-ci/repo.json', 'utf8')); const project = data['${{ inputs.ecosystem-ci-project }}']; console.log('ECOSYSTEM_CI_PROJECT_HASH=' + project.hash);" >> $GITHUB_OUTPUT | ||
| node -e "const fs = require('fs'); const data = JSON.parse(fs.readFileSync('./ecosystem-ci/repo.json', 'utf8')); const project = data['${{ inputs.ecosystem-ci-project }}']; console.log('ECOSYSTEM_CI_PROJECT_REPOSITORY=' + project.repository.replace('https://github.com/', '').replace('.git', ''));" >> $GITHUB_OUTPUT | ||
|
|
||
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | ||
| if: ${{ inputs.ecosystem-ci-project != '' }} | ||
| with: | ||
| repository: ${{ steps.ecosystem-ci-project-hash.outputs.ECOSYSTEM_CI_PROJECT_REPOSITORY }} | ||
| path: ecosystem-ci/${{ inputs.ecosystem-ci-project }} | ||
| ref: ${{ steps.ecosystem-ci-project-hash.outputs.ECOSYSTEM_CI_PROJECT_HASH }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| name: E2E Test | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - next | ||
| paths-ignore: | ||
| - '**/*.md' | ||
| pull_request: | ||
| branches: | ||
| - next | ||
| paths-ignore: | ||
| - '**/*.md' | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: bash | ||
|
|
||
| jobs: | ||
| e2e-test: | ||
| name: ${{ matrix.project.name }} E2E test | ||
| permissions: | ||
| contents: read | ||
| packages: read | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| project: | ||
| - name: cnpmcore | ||
| node-version: 24 | ||
| command: | | ||
| npm install | ||
| npm run lint -- --quiet | ||
| npm run typecheck | ||
| npm run build | ||
| npm run prepublishOnly | ||
| - name: examples | ||
| node-version: 24 | ||
| command: | | ||
| # examples/helloworld https://github.com/eggjs/examples/blob/master/helloworld/package.json | ||
| cd helloworld | ||
| npm install | ||
| npm run lint | ||
| npm run test | ||
| npm run prepublishOnly | ||
| cd .. | ||
|
|
||
| # examples/hello-tegg https://github.com/eggjs/examples/blob/master/hello-tegg/package.json | ||
| cd hello-tegg | ||
| npm install | ||
| npm run lint | ||
| npm run test | ||
| npm run prepublishOnly | ||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| steps: | ||
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 | ||
| - uses: ./.github/actions/clone | ||
| with: | ||
| ecosystem-ci-project: ${{ matrix.project.name }} | ||
|
|
||
| - name: Install pnpm | ||
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4 | ||
|
|
||
| - name: Set up Node.js | ||
| uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 | ||
| with: | ||
| node-version: ${{ matrix.project.node-version }} | ||
| cache: 'pnpm' | ||
|
|
||
| - name: Install dependencies | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Build all packages | ||
| run: pnpm build | ||
|
|
||
| - name: Pack packages into tgz | ||
| run: | | ||
| pnpm -r pack | ||
|
|
||
| - name: Override dependencies from tgz in ${{ matrix.project.name }} | ||
| working-directory: ecosystem-ci/${{ matrix.project.name }} | ||
| run: | | ||
| node ../patch-project.ts ${{ matrix.project.name }} | ||
|
|
||
| - name: Run e2e test commands in ${{ matrix.project.name }} | ||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| working-directory: ecosystem-ci/${{ matrix.project.name }} | ||
| run: ${{ matrix.project.command }} | ||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| cnpmcore | ||
| examples |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,87 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { execSync } from 'node:child_process'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { existsSync } from 'node:fs'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { join } from 'node:path'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import repos from './repo.json' with { type: 'json' }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cwd = import.meta.dirname; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function exec(cmd: string, execCwd: string = cwd): string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return execSync(cmd, { cwd: execCwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function getRemoteUrl(dir: string): string | null { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return exec('git remote get-url origin', dir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function normalizeGitUrl(url: string): string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Convert git@github.com:owner/repo.git to github.com/owner/repo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Convert https://github.com/owner/repo.git to github.com/owner/repo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return url | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .replace(/^git@([^:]+):/, '$1/') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .replace(/^https?:\/\//, '') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .replace(/\.git$/, ''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function isSameRepo(url1: string, url2: string): boolean { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return normalizeGitUrl(url1) === normalizeGitUrl(url2); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function getCurrentHash(dir: string): string | null { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return exec('git rev-parse HEAD', dir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function cloneRepo(repoUrl: string, branch: string, targetDir: string): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.info(`Cloning ${repoUrl} (branch: ${branch})...`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exec(`git clone --branch ${branch} ${repoUrl} ${targetDir}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+42
to
+44
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function checkoutHash(dir: string, hash: string): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.info(`Checking out ${hash}...`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exec(`git fetch origin`, dir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exec(`git checkout ${hash}`, dir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+42
to
+50
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function cloneRepo(repoUrl: string, branch: string, targetDir: string): void { | |
| console.info(`Cloning ${repoUrl} (branch: ${branch})...`); | |
| exec(`git clone --branch ${branch} ${repoUrl} ${targetDir}`); | |
| } | |
| function checkoutHash(dir: string, hash: string): void { | |
| console.info(`Checking out ${hash}...`); | |
| exec(`git fetch origin`, dir); | |
| exec(`git checkout ${hash}`, dir); | |
| function validateGitUrl(name: string, url: string): string { | |
| // Allow typical Git URL characters, disallow whitespace and shell metacharacters. | |
| const gitUrlPattern = /^[A-Za-z0-9@._:/%+-]+$/; | |
| if (!gitUrlPattern.test(url)) { | |
| throw new Error(`Invalid ${name}: contains unsafe characters`); | |
| } | |
| return url; | |
| } | |
| function validateGitRef(name: string, ref: string): string { | |
| // Allow typical Git ref characters (no spaces or shell metacharacters). | |
| const gitRefPattern = /^[A-Za-z0-9._\/\-]+$/; | |
| if (!gitRefPattern.test(ref)) { | |
| throw new Error(`Invalid ${name}: contains unsafe characters`); | |
| } | |
| return ref; | |
| } | |
| function validateGitHash(name: string, hash: string): string { | |
| // Git hashes are hex strings, usually 40 chars, but allow shorter prefixes. | |
| const gitHashPattern = /^[0-9a-fA-F]{7,40}$/; | |
| if (!gitHashPattern.test(hash)) { | |
| throw new Error(`Invalid ${name}: must be a hex commit hash`); | |
| } | |
| return hash; | |
| } | |
| function cloneRepo(repoUrl: string, branch: string, targetDir: string): void { | |
| const safeRepoUrl = validateGitUrl('repository URL', repoUrl); | |
| const safeBranch = validateGitRef('branch', branch); | |
| console.info(`Cloning ${safeRepoUrl} (branch: ${safeBranch})...`); | |
| exec(`git clone --branch ${safeBranch} ${safeRepoUrl} ${targetDir}`); | |
| } | |
| function checkoutHash(dir: string, hash: string): void { | |
| const safeHash = validateGitHash('hash', hash); | |
| console.info(`Checking out ${safeHash}...`); | |
| exec(`git fetch origin`, dir); | |
| exec(`git checkout ${safeHash}`, dir); |
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Dec 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for git command failures. When cloneRepo or checkoutHash fail (lines 81-82), the script continues execution and prints "Done!" even though the operation may have failed. This could lead to false positives in CI/CD. Consider wrapping the main loop in a try-catch block or tracking failures to exit with an appropriate error code.
| for (const [repoName, repo] of Object.entries(repos)) { | |
| const targetDir = join(cwd, repoName); | |
| if (existsSync(targetDir)) { | |
| console.info(`\nDirectory ${repoName} exists, validating...`); | |
| const remoteUrl = getRemoteUrl(targetDir); | |
| if (!remoteUrl) { | |
| console.error(` ✗ ${repoName} is not a git repository`); | |
| continue; | |
| } | |
| if (!isSameRepo(remoteUrl, repo.repository)) { | |
| console.error(` ✗ Remote mismatch: expected ${repo.repository}, got ${remoteUrl}`); | |
| continue; | |
| } | |
| console.info(` ✓ Remote matches`); | |
| const currentHash = getCurrentHash(targetDir); | |
| if (currentHash === repo.hash) { | |
| console.info(` ✓ Already at correct commit ${repo.hash.slice(0, 7)}`); | |
| } else { | |
| console.info(` → Current: ${currentHash?.slice(0, 7)}, expected: ${repo.hash.slice(0, 7)}`); | |
| checkoutHash(targetDir, repo.hash); | |
| console.info(` ✓ Checked out ${repo.hash.slice(0, 7)}`); | |
| } | |
| } else { | |
| cloneRepo(repo.repository, repo.branch, targetDir); | |
| checkoutHash(targetDir, repo.hash); | |
| console.info(`✓ Cloned and checked out ${repo.hash.slice(0, 7)}`); | |
| } | |
| } | |
| console.info('\nDone!'); | |
| let hasError = false; | |
| for (const [repoName, repo] of Object.entries(repos)) { | |
| const targetDir = join(cwd, repoName); | |
| try { | |
| if (existsSync(targetDir)) { | |
| console.info(`\nDirectory ${repoName} exists, validating...`); | |
| const remoteUrl = getRemoteUrl(targetDir); | |
| if (!remoteUrl) { | |
| console.error(` ✗ ${repoName} is not a git repository`); | |
| continue; | |
| } | |
| if (!isSameRepo(remoteUrl, repo.repository)) { | |
| console.error(` ✗ Remote mismatch: expected ${repo.repository}, got ${remoteUrl}`); | |
| continue; | |
| } | |
| console.info(` ✓ Remote matches`); | |
| const currentHash = getCurrentHash(targetDir); | |
| if (currentHash === repo.hash) { | |
| console.info(` ✓ Already at correct commit ${repo.hash.slice(0, 7)}`); | |
| } else { | |
| console.info(` → Current: ${currentHash?.slice(0, 7)}, expected: ${repo.hash.slice(0, 7)}`); | |
| checkoutHash(targetDir, repo.hash); | |
| console.info(` ✓ Checked out ${repo.hash.slice(0, 7)}`); | |
| } | |
| } else { | |
| cloneRepo(repo.repository, repo.branch, targetDir); | |
| checkoutHash(targetDir, repo.hash); | |
| console.info(`✓ Cloned and checked out ${repo.hash.slice(0, 7)}`); | |
| } | |
| } catch (error) { | |
| hasError = true; | |
| console.error(`\n✗ Failed to process ${repoName}:`, error); | |
| } | |
| } | |
| if (hasError) { | |
| console.error('\nDone with errors.'); | |
| process.exitCode = 1; | |
| } else { | |
| console.info('\nDone!'); | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,125 @@ | ||||||||||||
| import fs from 'node:fs'; | ||||||||||||
| import { glob } from 'node:fs/promises'; | ||||||||||||
| import { dirname, join, relative } from 'node:path'; | ||||||||||||
|
|
||||||||||||
| import yaml from 'js-yaml'; | ||||||||||||
|
|
||||||||||||
| import repos from './repo.json' with { type: 'json' }; | ||||||||||||
|
|
||||||||||||
| const projectDir = import.meta.dirname; | ||||||||||||
| const rootDir = join(projectDir, '..'); | ||||||||||||
| const tgzPath = rootDir; | ||||||||||||
|
|
||||||||||||
| const projects = Object.keys(repos); | ||||||||||||
|
|
||||||||||||
| const project = process.argv[2]; | ||||||||||||
|
|
||||||||||||
| if (!projects.includes(project)) { | ||||||||||||
| console.error(`Project ${project} is not defined in repo.json`); | ||||||||||||
fengmk2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||
| process.exit(1); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Read pnpm-workspace.yaml to get workspace patterns | ||||||||||||
| const workspaceConfig = yaml.load(fs.readFileSync(join(rootDir, 'pnpm-workspace.yaml'), 'utf8')) as { | ||||||||||||
| packages: string[]; | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| // Use glob to find all package directories dynamically | ||||||||||||
| async function discoverPackages(): Promise<[string, string][]> { | ||||||||||||
| const packages: [string, string][] = []; | ||||||||||||
|
|
||||||||||||
| for (const pattern of workspaceConfig.packages) { | ||||||||||||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| // Convert pnpm patterns (e.g., 'packages/*') to glob patterns for package.json | ||||||||||||
| const globPattern = `${pattern}/package.json`; | ||||||||||||
|
|
||||||||||||
| for await (const entry of glob(globPattern, { cwd: rootDir })) { | ||||||||||||
| const pkgJsonPath = join(rootDir, entry); | ||||||||||||
| try { | ||||||||||||
| const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); | ||||||||||||
|
|
||||||||||||
| // Skip private packages and packages without names | ||||||||||||
| if (pkgJson.private || !pkgJson.name) continue; | ||||||||||||
|
|
||||||||||||
| const relativePath = dirname(relative(rootDir, pkgJsonPath)); | ||||||||||||
| packages.push([pkgJson.name, relativePath]); | ||||||||||||
| } catch { | ||||||||||||
| // Skip if package.json is invalid or cannot be read | ||||||||||||
| console.warn(`Warning: Could not read ${pkgJsonPath}`); | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+45
to
+48
|
||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| return packages; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| async function buildOverrides(): Promise<Record<string, string>> { | ||||||||||||
| const packages = await discoverPackages(); | ||||||||||||
| const overrides: Record<string, string> = {}; | ||||||||||||
|
|
||||||||||||
| for (const [name, path] of packages) { | ||||||||||||
| const version = JSON.parse(fs.readFileSync(join(tgzPath, path, 'package.json'), 'utf8')).version; | ||||||||||||
| const filename = `${name.replace('@', '').replace('/', '-')}-${version}.tgz`; | ||||||||||||
| overrides[name] = `file:${tgzPath}/${filename}`; | ||||||||||||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| return overrides; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| async function patchPackageJSON(filePath: string, overrides: Record<string, string>): Promise<void> { | ||||||||||||
| const packageJson = JSON.parse(fs.readFileSync(filePath, 'utf8')); | ||||||||||||
| // Add overrides with tgz files | ||||||||||||
| packageJson.overrides = { | ||||||||||||
| ...packageJson.overrides, | ||||||||||||
| ...overrides, | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| for (const name in packageJson.dependencies) { | ||||||||||||
| const override = overrides[name]; | ||||||||||||
| if (override) { | ||||||||||||
| packageJson.dependencies[name] = override; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| for (const name in packageJson.devDependencies) { | ||||||||||||
| const override = overrides[name]; | ||||||||||||
| if (override) { | ||||||||||||
| packageJson.devDependencies[name] = override; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
|
|
||||||||||||
| const packageJsonString = JSON.stringify(packageJson, null, 2) + '\n'; | ||||||||||||
| console.log(packageJsonString); | ||||||||||||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| console.log(packageJsonString); | |
| console.error(packageJsonString); |
Copilot
AI
Dec 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message when an unsupported project is encountered in the switch statement is inconsistent with the earlier validation. At this point, the project has already been validated to exist in repo.json (line 17-20), so this error message is misleading. Consider changing to something like console.error(\Project ${project} exists in repo.json but has no patch handler defined`)` to accurately reflect the issue.
| console.error(`Project ${project} is not supported`); | |
| console.error(`Project ${project} exists in repo.json but has no patch handler defined`); |
Copilot
AI
Dec 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for the main function. If any of the async operations fail, the process will exit with a success code (0) by default. Add a catch block to ensure the process exits with a non-zero code on failure.
| main(); | |
| main().catch((err) => { | |
| console.error(err); | |
| process.exitCode = 1; | |
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| { | ||
| "cnpmcore": { | ||
| "repository": "https://github.com/cnpm/cnpmcore.git", | ||
| "branch": "master", | ||
| "hash": "8a1b79f3e0ec1aa1a9dcd37d059846bc4f4219f8" | ||
| }, | ||
| "examples": { | ||
| "repository": "https://github.com/eggjs/examples.git", | ||
| "branch": "master", | ||
| "hash": "9191a92e5ec0712a2e4864e999b7db1874fe8ca3" | ||
| } | ||
| } | ||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,9 @@ | |||||||
| "type": "git", | ||||||||
| "url": "git+https://github.com/eggjs/egg.git" | ||||||||
| }, | ||||||||
| "files": [ | ||||||||
| "README.md" | ||||||||
| ], | ||||||||
fengmk2 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+11
to
+13
|
||||||||
| "files": [ | |
| "README.md" | |
| ], |
Uh oh!
There was an error while loading. Please reload this page.