Compare commits

...

39 Commits

Author SHA1 Message Date
semantic-release-bot
fff032b1e8 Release 2.10.1
[skip ci]

## [2.10.1](https://github.com/cnpm/npmcore/compare/v2.10.0...v2.10.1) (2023-01-08)

### Bug Fixes

* export _cnpmcore_publish_time on abbreviated manifests ([#374](https://github.com/cnpm/npmcore/issues/374)) ([4bceac5](4bceac5a4c))
2023-01-08 10:41:00 +00:00
fengmk2
4bceac5a4c fix: export _cnpmcore_publish_time on abbreviated manifests (#374) 2023-01-08 18:39:36 +08:00
fengmk2
e09cdad6ec test: run tsc prod on ci (#373) 2023-01-05 22:27:11 +08:00
semantic-release-bot
6384229a53 Release 2.10.0
[skip ci]

## [2.10.0](https://github.com/cnpm/npmcore/compare/v2.9.1...v2.10.0) (2023-01-05)

### Features

* unpublish pkg when upstream block ([#372](https://github.com/cnpm/npmcore/issues/372)) ([7e419c1](7e419c1fb4))
2023-01-05 12:36:22 +00:00
elrrrrrrr
7e419c1fb4 feat: unpublish pkg when upstream block (#372)
> https://registry.npmmirror.com/chalk-next 

如果上游 cnpmcore registry block 包时,下游同步时目前会同步失败,导致包无法自动废弃

* 上游包 unblock 时,下游包应当 unpublish 处理,follow upstream 操作
2023-01-05 20:34:57 +08:00
fengmk2
bda3f1caf4 test: remove tsconfig-paths/register (#369)
https://github.com/eggjs/egg-bin/pull/199
2022-12-19 02:24:59 +08:00
fengmk2
e76885847c test: use redis image (#368) 2022-12-19 00:49:54 +08:00
semantic-release-bot
32d5084fdc Release 2.9.1
[skip ci]

## [2.9.1](https://github.com/cnpm/npmcore/compare/v2.9.0...v2.9.1) (2022-12-17)

### Bug Fixes

* Auto enable npm publish on github action ([3d366dd](3d366dd996))
* fix tsc:prod ([ca78d00](ca78d00f28))
2022-12-17 10:43:53 +00:00
fengmk2
f2055a355f chore: fix branches config 2022-12-17 18:08:57 +08:00
fengmk2
3d366dd996 fix: Auto enable npm publish on github action 2022-12-17 18:04:49 +08:00
fengmk2
6fcc5c6dab Update release.yml 2022-12-16 23:35:58 +08:00
fengmk2
b761a8f4eb Update release.yml 2022-12-16 23:32:38 +08:00
fengmk2
65a8d1d324 Create release.yml 2022-12-16 23:29:46 +08:00
fengmk2
57515de719 🐛 FIX: Use runInAnonymousContextScope instead (#367) 2022-12-16 21:58:55 +08:00
killagu
ca78d00f28 fix: fix tsc:prod 2022-12-15 22:32:11 +08:00
killagu
ea84da989f Release 2.9.0 2022-12-15 22:24:46 +08:00
elrrrrrrr
c562645db7 feat: suspend task before app close (#365)
> 机器计划内重启时,需要等待超时后再重新 retry 恢复 changesStream
同步,新增任务挂起机器,应用退出前,挂起当前机器正在执行的同步任务

* 新增 taskRepository#findTaskByAuthorIpAndType 方法,查找所有当前机器所有 worker 同步的任务
* 新增 module.d.ts 定义,目前仅消费 cnpmcoreCore module 内的 changesStream 方法
* app.ts 内调用 changesStreamService#suspendTaskWhenExit 在应用退出前触发

Co-authored-by: killa <killa123@126.com>
2022-12-15 21:34:24 +08:00
killagu
eb04533714 Release 2.8.1 2022-12-05 14:07:01 +08:00
fengmk2
7bc0fccaca 🤖 TEST: Fix async function mock 2022-12-03 15:03:19 +08:00
fengmk2
84ae9bcfa0 📖 DOC: Update contributors 2022-12-03 15:00:06 +08:00
laibao101
fad30adc56 feat: npm command support npm v6 (#356)
有很多比较久远的包,对 node 版本有限制。最高只能用 node14 npm6。 会导致 npm owner
操作的时候报错。原因是npm6的请求里面 header 没有 npm-command 参数。 增加兼容性。

Co-authored-by: Nice ZHOU 周华 <nice.zhou@nio.com>
2022-12-03 14:59:08 +08:00
elrrrrrrr
f961219dbe fix: Sync save ignore ER_DUP_ENTRY error (#364)
> 2个同步任务并发执行时,可能出现查询时未写入版本,写入时冲突,导致未正常更新 pkg.manifests

* 识别写入异常场景,由于 version 写入已添加 nfs 和 关联数据事务,可以直接放入 updatedVersions 数组进行
manifests 更新
* dist-tag 添加校验逻辑,对于 dist-tag 的更新,必须确保存量 manifests 或本次同步增量的 versions
包含对应版本,否则跳过
2022-12-03 14:49:29 +08:00
fengmk2
c02010f2e5 Release 2.8.0 2022-11-29 00:07:21 +08:00
elrrrrrrr
d55c680ef9 Event cork (#361)
> syncPackage 同步时,由于任务并发,可能会导致同步过程中 versions 表记录已经创建,pkg.manifests 还没有同步
> 针对这种场景做补偿逻辑,防止 tag 打在一个 pkg.manifests 没有的版本里

* 修改 pkg.manifests 补偿逻辑,兼容有 versions 没 pkg.manifests 的情况
* 添加 eventCork 的 advice,在 syncPackage 任务结束后,再统一触发 changes,依赖
[ref](https://github.com/eggjs/tegg/pull/60)

在同步和被同步的场景,确保 changes 发出时,pkg.manifests 已经更新
统一 ctx 内不同 changes 时序可能影响,不影响重新读取 manifests 一致性
2022-11-28 23:59:03 +08:00
fengmk2
c1eb0978ba Release 2.7.1 2022-11-25 21:35:53 +08:00
Ke Wu
c6b8aecfd0 fix: request binary error (#360)
Co-authored-by: 天玎 <tianding.wk@antgroup.com>
2022-11-25 21:34:53 +08:00
fengmk2
32e842e882 Release 2.7.0 2022-11-25 18:26:14 +08:00
Ke Wu
5738d569ea refactor: binary sync task use binaryName by default (#358)
1. 默认使用 config/binaries 的 binaryName 创建同步任务;
2. config/binaries 中的 category 为组合不同 binary 数据的配置。默认跟 binaryName 保持一致;
3. 当 category 跟 binaryName 不一致时,合并 binaryName 和 category 两个二进制数据信息。

以 canvas 和 node-canvas-prebuilt 为例:
1. 创建同步任务时,分别同步各自的数据;
2. 查询二进制数据时,由于 canvas 中的 category 配置为 node-canvas-prebuilt,这时候会合并 canvas
和 node-canvas-prebuilt 两个 binary(binaryName)的数据并返回。

这个重构删除 mergeCategory 字段,使得配置数据更加精简。

Co-authored-by: 天玎 <tianding.wk@antgroup.com>
2022-11-25 18:25:23 +08:00
fengmk2
9dd2d4bbe4 Release 2.6.1 2022-11-23 18:34:56 +08:00
fengmk2
0b35ead2a0 🐛 FIX: typo for canvas 2022-11-23 18:34:22 +08:00
fengmk2
a64ebd80f3 Release 2.6.0 2022-11-23 18:16:50 +08:00
Ke Wu
be8387dfa4 feat: Support canvas sync from different binary (#357)
Co-authored-by: 天玎 <tianding.wk@antgroup.com>
2022-11-23 18:15:51 +08:00
elrrrrrrr
d6c4cf5029 fix: duplicate binary task (#354)
> syncBinary 目前会通过定时任务单机每天创建,导致多实例冲突
> 其他任务类型均通过事件触发不受影响
* 创建 syncBinary 任务时,手动去重
* 添加 bizId 参数 进行兜底
2022-11-12 22:33:42 +08:00
fengmk2
0ada89b2fc Release 2.5.2 2022-11-11 18:32:40 +08:00
elrrrrrrr
7eb209de13 fix: create task when waiting (#352)
> 下游同步 cnpmcore 项目时,cnpmcore 会同时发送 [version, tag] 两个独立的事件
有可能下游在处理 version change 时,tag 尚未创建完成
但由于 task targetName 幂等,导致 tag 的 change 事件未响应,造成 tag 异常

* 创建同步任务时进行判断,如果任务还未开始才放弃创建
2022-11-11 18:31:53 +08:00
killagu
5965dbddbc Release 2.5.1 2022-11-07 10:09:07 +08:00
fengmk2
e40c5021bb 🐛 FIX: Mirror cypress arm64 binary (#351) 2022-11-06 18:46:17 +08:00
killagu
65a3df891d Release 2.5.0 2022-11-04 16:02:50 +08:00
elrrrrrrr
43d77ee91e feat: long description (#349)
目前 db 限制 pkg.description 长度为 10k,cnpmjs.org 为 longtext,可能导致不兼容。

* 长 description 场景,截断字符保存,防止创建 pkg 失败
2022-11-04 14:12:58 +08:00
52 changed files with 1331 additions and 266 deletions

View File

@@ -5,15 +5,9 @@ name: Node.js CI
on:
push:
branches:
- main
- master
branches: [ master ]
pull_request:
branches:
- main
- master
schedule:
- cron: '0 2 * * *'
branches: [ master ]
jobs:
test-mysql57-fs-nfs:
@@ -28,28 +22,28 @@ jobs:
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
redis:
# https://docs.github.com/en/actions/using-containerized-services/about-service-containers#example-mapping-redis-ports
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
strategy:
fail-fast: false
matrix:
node-version: [16, 18, 19]
node-version: [16, 18]
os: [ubuntu-latest]
steps:
- name: Checkout Git Source
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
# https://github.com/marketplace/actions/redis-server-in-github-actions#usage
- name: Start Redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Install Dependencies
run: npm i
@@ -57,15 +51,14 @@ jobs:
run: npm run ci
- name: Code Coverage
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
test-mysql57-oss-nfs:
runs-on: ${{ matrix.os }}
if:
if: |
contains('
refs/heads/main
refs/heads/master
refs/heads/dev
', github.ref)
@@ -80,6 +73,13 @@ jobs:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
redis:
# https://docs.github.com/en/actions/using-containerized-services/about-service-containers#example-mapping-redis-ports
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
strategy:
fail-fast: false
matrix:
@@ -88,19 +88,13 @@ jobs:
steps:
- name: Checkout Git Source
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
# https://github.com/marketplace/actions/redis-server-in-github-actions#usage
- name: Start Redis
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Install Dependencies
run: npm i
@@ -114,6 +108,6 @@ jobs:
CNPMCORE_NFS_OSS_SECRET: ${{ secrets.CNPMCORE_NFS_OSS_SECRET }}
- name: Code Coverage
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}

18
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Release
on:
# 合并后自动发布
push:
branches: [ master, main ]
# 手动发布
workflow_dispatch: {}
jobs:
release:
name: Node.js
uses: artusjs/github-actions/.github/workflows/node-release.yml@v1
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
with:
checkTest: false

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ config/config.prod.ts
config/**/*.js
app/**/*.js
test/**/*.js
app.js
.cnpmcore
.cnpmcore_unittest

23
CHANGELOG.md Normal file
View File

@@ -0,0 +1,23 @@
# Changelog
## [2.10.1](https://github.com/cnpm/npmcore/compare/v2.10.0...v2.10.1) (2023-01-08)
### Bug Fixes
* export _cnpmcore_publish_time on abbreviated manifests ([#374](https://github.com/cnpm/npmcore/issues/374)) ([4bceac5](https://github.com/cnpm/npmcore/commit/4bceac5a4c94f8e8624ae1113ad1c5e69a5a2ae1))
## [2.10.0](https://github.com/cnpm/npmcore/compare/v2.9.1...v2.10.0) (2023-01-05)
### Features
* unpublish pkg when upstream block ([#372](https://github.com/cnpm/npmcore/issues/372)) ([7e419c1](https://github.com/cnpm/npmcore/commit/7e419c1fb4fe297adea86cb5d9eae4c8e77e2aec))
## [2.9.1](https://github.com/cnpm/npmcore/compare/v2.9.0...v2.9.1) (2022-12-17)
### Bug Fixes
* Auto enable npm publish on github action ([3d366dd](https://github.com/cnpm/npmcore/commit/3d366dd996161f8f08ae43bde29b7768f5a5241c))
* fix tsc:prod ([ca78d00](https://github.com/cnpm/npmcore/commit/ca78d00f28930180a9374c01d2a9b3b47d6e9db3))

View File

@@ -1,4 +1,74 @@
2.9.0 / 2022-12-15
==================
**features**
* [[`c562645`](http://github.com/cnpm/cnpmcore/commit/c562645db7c88f9c3c5787fd450b457574d1cce6)] - feat: suspend task before app close (#365) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
2.8.1 / 2022-12-05
==================
**features**
* [[`fad30ad`](http://github.com/cnpm/cnpmcore/commit/fad30adc564c931c0bf63828d83bab84105aaef0)] - feat: npm command support npm v6 (#356) (laibao101 <<369632567@qq.com>>)
**fixes**
* [[`f961219`](http://github.com/cnpm/cnpmcore/commit/f961219dbe4676156e1766db82379ee40087bcd8)] - fix: Sync save ignore ER_DUP_ENTRY error (#364) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
**others**
* [[`7bc0fcc`](http://github.com/cnpm/cnpmcore/commit/7bc0fccaca880efe08228b4109953bd3974d2eb9)] - 🤖 TEST: Fix async function mock (fengmk2 <<fengmk2@gmail.com>>)
* [[`84ae9bc`](http://github.com/cnpm/cnpmcore/commit/84ae9bcfa06124255703b926f83fb5e6a6bf9d6b)] - 📖 DOC: Update contributors (fengmk2 <<fengmk2@gmail.com>>)
2.8.0 / 2022-11-29
==================
**others**
* [[`d55c680`](http://github.com/cnpm/cnpmcore/commit/d55c680ef906ecb27f7967782ad7d25987cef7d4)] - Event cork (#361) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
2.7.1 / 2022-11-25
==================
**fixes**
* [[`c6b8aec`](http://github.com/cnpm/cnpmcore/commit/c6b8aecfd0c2b0d454389e931747c431dac5742b)] - fix: request binary error (#360) (Ke Wu <<gemwuu@163.com>>)
2.7.0 / 2022-11-25
==================
**others**
* [[`5738d56`](http://github.com/cnpm/cnpmcore/commit/5738d569ea691c05c3f3b0b74a454a33fefb8fc7)] - refactor: binary sync task use binaryName by default (#358) (Ke Wu <<gemwuu@163.com>>)
2.6.1 / 2022-11-23
==================
**fixes**
* [[`0b35ead`](http://github.com/cnpm/cnpmcore/commit/0b35ead2a0cd73b89d2d961bafec13d7250fe805)] - 🐛 FIX: typo for canvas (fengmk2 <<fengmk2@gmail.com>>)
2.6.0 / 2022-11-23
==================
**features**
* [[`be8387d`](http://github.com/cnpm/cnpmcore/commit/be8387dfa48b9487156542000a93081fa823694a)] - feat: Support canvas sync from different binary (#357) (Ke Wu <<gemwuu@163.com>>)
**fixes**
* [[`d6c4cf5`](http://github.com/cnpm/cnpmcore/commit/d6c4cf5029ca6450064fc05696a8624b6c36f0b2)] - fix: duplicate binary task (#354) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
2.5.2 / 2022-11-11
==================
**fixes**
* [[`7eb209d`](http://github.com/cnpm/cnpmcore/commit/7eb209de1332417db2070846891d78f5afa0cd10)] - fix: create task when waiting (#352) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
2.5.1 / 2022-11-07
==================
**others**
* [[`e40c502`](http://github.com/cnpm/cnpmcore/commit/e40c5021bb2ba78f8879d19bc477883168560b85)] - 🐛 FIX: Mirror cypress arm64 binary (#351) (fengmk2 <<fengmk2@gmail.com>>)
2.5.0 / 2022-11-04
==================
**features**
* [[`43d77ee`](http://github.com/cnpm/cnpmcore/commit/43d77ee91e52bd74594d9d569b839c1a4b7fbac6)] - feat: long description (#349) (elrrrrrrr <<elrrrrrrr@gmail.com>>)
2.4.1 / 2022-10-28
==================

View File

@@ -24,12 +24,12 @@ See [DEVELOPER.md](DEVELOPER.md)
## Contributors
|[<img src="https://avatars.githubusercontent.com/u/156269?v=4" width="100px;"/><br/><sub><b>fengmk2</b></sub>](https://github.com/fengmk2)<br/>|[<img src="https://avatars.githubusercontent.com/u/6897780?v=4" width="100px;"/><br/><sub><b>killagu</b></sub>](https://github.com/killagu)<br/>|[<img src="https://avatars.githubusercontent.com/u/5574625?v=4" width="100px;"/><br/><sub><b>elrrrrrrr</b></sub>](https://github.com/elrrrrrrr)<br/>|[<img src="https://avatars.githubusercontent.com/u/26033663?v=4" width="100px;"/><br/><sub><b>Zian502</b></sub>](https://github.com/Zian502)<br/>|[<img src="https://avatars.githubusercontent.com/u/13284978?v=4" width="100px;"/><br/><sub><b>Beace</b></sub>](https://github.com/Beace)<br/>|[<img src="https://avatars.githubusercontent.com/u/227713?v=4" width="100px;"/><br/><sub><b>atian25</b></sub>](https://github.com/atian25)<br/>|
|[<img src="https://avatars.githubusercontent.com/u/156269?v=4" width="100px;"/><br/><sub><b>fengmk2</b></sub>](https://github.com/fengmk2)<br/>|[<img src="https://avatars.githubusercontent.com/u/6897780?v=4" width="100px;"/><br/><sub><b>killagu</b></sub>](https://github.com/killagu)<br/>|[<img src="https://avatars.githubusercontent.com/u/5574625?v=4" width="100px;"/><br/><sub><b>elrrrrrrr</b></sub>](https://github.com/elrrrrrrr)<br/>|[<img src="https://avatars.githubusercontent.com/u/26033663?v=4" width="100px;"/><br/><sub><b>Zian502</b></sub>](https://github.com/Zian502)<br/>|[<img src="https://avatars.githubusercontent.com/u/4635838?v=4" width="100px;"/><br/><sub><b>gemwuu</b></sub>](https://github.com/gemwuu)<br/>|[<img src="https://avatars.githubusercontent.com/u/17879221?v=4" width="100px;"/><br/><sub><b>laibao101</b></sub>](https://github.com/laibao101)<br/>|
| :---: | :---: | :---: | :---: | :---: | :---: |
|[<img src="https://avatars.githubusercontent.com/u/17879221?v=4" width="100px;"/><br/><sub><b>laibao101</b></sub>](https://github.com/laibao101)<br/>|[<img src="https://avatars.githubusercontent.com/u/8198408?v=4" width="100px;"/><br/><sub><b>BlackHole1</b></sub>](https://github.com/BlackHole1)<br/>|[<img src="https://avatars.githubusercontent.com/u/1814071?v=4" width="100px;"/><br/><sub><b>xiekw2010</b></sub>](https://github.com/xiekw2010)<br/>|[<img src="https://avatars.githubusercontent.com/u/13471233?v=4" width="100px;"/><br/><sub><b>OpportunityLiu</b></sub>](https://github.com/OpportunityLiu)<br/>|[<img src="https://avatars.githubusercontent.com/u/958063?v=4" width="100px;"/><br/><sub><b>thonatos</b></sub>](https://github.com/thonatos)<br/>|[<img src="https://avatars.githubusercontent.com/u/11039003?v=4" width="100px;"/><br/><sub><b>chenpx976</b></sub>](https://github.com/chenpx976)<br/>|
[<img src="https://avatars.githubusercontent.com/u/29791463?v=4" width="100px;"/><br/><sub><b>fossabot</b></sub>](https://github.com/fossabot)<br/>|[<img src="https://avatars.githubusercontent.com/u/1119126?v=4" width="100px;"/><br/><sub><b>looksgood</b></sub>](https://github.com/looksgood)<br/>|[<img src="https://avatars.githubusercontent.com/u/3478550?v=4" width="100px;"/><br/><sub><b>coolyuantao</b></sub>](https://github.com/coolyuantao)<br/>
|[<img src="https://avatars.githubusercontent.com/u/13284978?v=4" width="100px;"/><br/><sub><b>Beace</b></sub>](https://github.com/Beace)<br/>|[<img src="https://avatars.githubusercontent.com/u/227713?v=4" width="100px;"/><br/><sub><b>atian25</b></sub>](https://github.com/atian25)<br/>|[<img src="https://avatars.githubusercontent.com/u/3478550?v=4" width="100px;"/><br/><sub><b>coolyuantao</b></sub>](https://github.com/coolyuantao)<br/>|[<img src="https://avatars.githubusercontent.com/u/8198408?v=4" width="100px;"/><br/><sub><b>BlackHole1</b></sub>](https://github.com/BlackHole1)<br/>|[<img src="https://avatars.githubusercontent.com/u/1814071?v=4" width="100px;"/><br/><sub><b>xiekw2010</b></sub>](https://github.com/xiekw2010)<br/>|[<img src="https://avatars.githubusercontent.com/u/13471233?v=4" width="100px;"/><br/><sub><b>OpportunityLiu</b></sub>](https://github.com/OpportunityLiu)<br/>|
[<img src="https://avatars.githubusercontent.com/u/958063?v=4" width="100px;"/><br/><sub><b>thonatos</b></sub>](https://github.com/thonatos)<br/>|[<img src="https://avatars.githubusercontent.com/u/11039003?v=4" width="100px;"/><br/><sub><b>chenpx976</b></sub>](https://github.com/chenpx976)<br/>|[<img src="https://avatars.githubusercontent.com/u/29791463?v=4" width="100px;"/><br/><sub><b>fossabot</b></sub>](https://github.com/fossabot)<br/>|[<img src="https://avatars.githubusercontent.com/u/1119126?v=4" width="100px;"/><br/><sub><b>looksgood</b></sub>](https://github.com/looksgood)<br/>
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Sun Aug 28 2022 19:00:22 GMT+0800`.
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Sat Dec 03 2022 14:59:50 GMT+0800`.
<!-- GITCONTRIBUTOR_END -->

11
app.ts
View File

@@ -1,7 +1,6 @@
import path from 'path';
import { readFile } from 'fs/promises';
import { Application } from 'egg';
declare module 'egg' {
interface Application {
binaryHTML: string;
@@ -23,4 +22,14 @@ export default class CnpmcoreAppHook {
const text = await readFile(filepath, 'utf-8');
this.app.binaryHTML = text.replace('{{registry}}', this.app.config.cnpmcore.registry);
}
// 应用退出时执行
// 需要暂停当前执行的 changesStream task
async beforeClose() {
await this.app.runInAnonymousContextScope(async ctx => {
await ctx.beginModuleScope(async () => {
await ctx.module.cnpmcoreCore.changesStreamService.suspendTaskWhenExit();
});
});
}
}

View File

@@ -19,11 +19,13 @@ export abstract class AbstractBinary {
protected httpclient: EggContextHttpClient;
protected logger: EggLogger;
protected binaryConfig: BinaryTaskConfig;
protected binaryName: string;
constructor(httpclient: EggContextHttpClient, logger: EggLogger, binaryConfig: BinaryTaskConfig) {
constructor(httpclient: EggContextHttpClient, logger: EggLogger, binaryConfig: BinaryTaskConfig, binaryName: string) {
this.httpclient = httpclient;
this.logger = logger;
this.binaryConfig = binaryConfig;
this.binaryName = binaryName;
}
abstract fetch(dir: string, params?: any): Promise<FetchResult | undefined>;

View File

@@ -4,13 +4,13 @@ import { BinaryTaskConfig } from '../../../../config/binaries';
export class ApiBinary extends AbstractBinary {
private apiUrl: string;
constructor(httpclient: EggContextHttpClient, logger: EggLogger, binaryConfig: BinaryTaskConfig, apiUrl: string) {
super(httpclient, logger, binaryConfig);
constructor(httpclient: EggContextHttpClient, logger: EggLogger, binaryConfig: BinaryTaskConfig, apiUrl: string, binaryName: string) {
super(httpclient, logger, binaryConfig, binaryName);
this.apiUrl = apiUrl;
}
async fetch(dir: string): Promise<FetchResult | undefined> {
const url = `${this.apiUrl}/${this.binaryConfig.category}${dir}`;
const url = `${this.apiUrl}/${this.binaryName}${dir}`;
const data = await this.requestJSON(url);
if (!Array.isArray(data)) {
this.logger.warn('[ApiBinary.fetch:response-data-not-array] data: %j', data);

View File

@@ -31,10 +31,11 @@ export class CypressBinary extends AbstractBinary {
// "https://cdn.cypress.io/desktop/4.0.0/darwin-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/4.0.0/linux-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/4.0.0/win32-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/darwin-arm64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/darwin-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/linux-x64/cypress.zip"
// "https://cdn.cypress.io/desktop/9.2.0/win32-x64/cypress.zip"
const platforms = [ 'darwin-x64', 'linux-x64', 'win32-x64' ];
const platforms = [ 'darwin-x64', 'darwin-arm64', 'linux-x64', 'win32-x64' ];
for (const platform of platforms) {
this.dirItems[subDir].push({
name: `${platform}/`,

View File

@@ -8,7 +8,7 @@ export class ImageminBinary extends AbstractBinary {
async fetch(dir: string): Promise<FetchResult | undefined> {
if (!this.dirItems) {
this.dirItems = {};
const npmPackageName = this.binaryConfig.options?.npmPackageName ?? this.binaryConfig.category;
const npmPackageName = this.binaryConfig.options?.npmPackageName ?? this.binaryName;
const pkgUrl = `https://registry.npmjs.com/${npmPackageName}`;
const data = await this.requestJSON(pkgUrl);
this.dirItems = {};

View File

@@ -10,7 +10,7 @@ export class NodePreGypBinary extends AbstractBinary {
async fetch(dir: string): Promise<FetchResult | undefined> {
if (!this.dirItems) {
this.dirItems = {};
const pkgUrl = `https://registry.npmjs.com/${this.binaryConfig.category}`;
const pkgUrl = `https://registry.npmjs.com/${this.binaryName}`;
const data = await this.requestJSON(pkgUrl);
this.dirItems = {};
this.dirItems['/'] = [];
@@ -77,7 +77,7 @@ export class NodePreGypBinary extends AbstractBinary {
date,
size: '-',
isDir: false,
url: `${this.binaryConfig.distUrl}/${this.binaryConfig.category}${versionPrefix}/${name}`,
url: `${this.binaryConfig.distUrl}/${this.binaryName}${versionPrefix}/${name}`,
ignoreDownloadStatuses: [ 404 ],
});
}
@@ -99,7 +99,7 @@ export class NodePreGypBinary extends AbstractBinary {
date,
size: '-',
isDir: false,
url: `${this.binaryConfig.distUrl}/${this.binaryConfig.category}${versionPrefix}/${name}`,
url: `${this.binaryConfig.distUrl}/${this.binaryName}${versionPrefix}/${name}`,
ignoreDownloadStatuses: [ 404 ],
});
}
@@ -163,7 +163,7 @@ export class NodePreGypBinary extends AbstractBinary {
const binaryFileName = binaryFile.replace('{platform}', platform)
.replace('{arch}', arch);
remotePath = remotePath.replace('{module_name}', moduleName)
.replace('{name}', this.binaryConfig.category)
.replace('{name}', this.binaryName)
.replace('{version}', version)
.replace('{configuration}', 'Release');
const binaryFilePath = join('/', remotePath, binaryFileName);

View File

@@ -216,6 +216,7 @@ export class Task<T extends TaskBaseData = TaskBaseData> extends Entity {
targetName,
authorId: `pid_${PID}`,
authorIp: HOST_NAME,
bizId: `SyncBinary:${targetName}`,
data: {
// task execute worker
taskWorker: '',

View File

@@ -30,6 +30,7 @@ import { ElectronBinary } from '../../common/adapter/binary/ElectronBinary';
import { NodePreGypBinary } from '../../common/adapter/binary/NodePreGypBinary';
import { ImageminBinary } from '../../common/adapter/binary/ImageminBinary';
import { PlaywrightBinary } from '../../common/adapter/binary/PlaywrightBinary';
import { TaskRepository } from 'app/repository/TaskRepository';
const BinaryClasses = {
[SyncerClass.NodeBinary]: NodeBinary,
@@ -58,6 +59,8 @@ export class BinarySyncerService extends AbstractService {
@Inject()
private readonly taskService: TaskService;
@Inject()
private readonly taskRepository: TaskRepository;
@Inject()
private readonly httpclient: EggContextHttpClient;
@Inject()
private readonly nfsAdapter: NFSAdapter;
@@ -71,15 +74,51 @@ export class BinarySyncerService extends AbstractService {
}
public async listRootBinaries(binaryName: string) {
return await this.binaryRepository.listBinaries(binaryName, '/');
// 通常 binaryName 和 category 是一样的,但是有些特殊的 binaryName 会有多个 category比如 canvas
// 所以查询 canvas 的时候,需要将 binaryName 和 category 的数据都查出来
const {
category,
} = binaries[binaryName];
const reqs = [
this.binaryRepository.listBinaries(binaryName, '/'),
];
if (category && category !== binaryName) {
reqs.push(this.binaryRepository.listBinaries(category, '/'));
}
const [
rootBinary,
categoryBinary,
] = await Promise.all(reqs);
const versions = rootBinary.map(b => b.name);
categoryBinary?.forEach(b => {
const version = b.name;
// 只将没有的版本添加进去
if (!versions.includes(version)) {
rootBinary.push(b);
}
});
return rootBinary;
}
public async downloadBinary(binary: Binary) {
return await this.nfsAdapter.getDownloadUrlOrStream(binary.storePath);
}
// SyncBinary 由定时任务每台单机定时触发,手动去重
// 添加 bizId 在 db 防止重复,记录 id 错误
public async createTask(binaryName: string, lastData?: any) {
return await this.taskService.createTask(Task.createSyncBinary(binaryName, lastData), false);
const existsTask = await this.taskRepository.findTaskByTargetName(binaryName, TaskType.SyncBinary);
if (existsTask) {
return existsTask;
}
try {
return await this.taskService.createTask(Task.createSyncBinary(binaryName, lastData), false);
} catch (e) {
this.logger.error('[BinarySyncerService.createTask] binaryName: %s, error: %s', binaryName, e);
}
}
public async findTask(taskId: string) {
@@ -250,15 +289,13 @@ export class BinarySyncerService extends AbstractService {
private createBinaryInstance(binaryName: string): AbstractBinary | undefined {
const config = this.config.cnpmcore;
const binaryConfig = binaries[binaryName];
if (config.sourceRegistryIsCNpm) {
const binaryConfig = binaries[binaryName];
const syncBinaryFromAPISource = config.syncBinaryFromAPISource || `${config.sourceRegistry}/-/binary`;
return new ApiBinary(this.httpclient, this.logger, binaryConfig, syncBinaryFromAPISource);
}
for (const binaryConfig of Object.values(binaries)) {
if (binaryConfig.category === binaryName) {
return new BinaryClasses[binaryConfig.syncer](this.httpclient, this.logger, binaryConfig);
}
return new ApiBinary(this.httpclient, this.logger, binaryConfig, syncBinaryFromAPISource, binaryName);
}
return new BinaryClasses[binaryConfig.syncer](this.httpclient, this.logger, binaryConfig, binaryName);
}
}

View File

@@ -6,7 +6,7 @@ import {
EggObjectFactory,
Inject,
} from '@eggjs/tegg';
import { TaskType } from '../../common/enum/Task';
import { TaskState, TaskType } from '../../common/enum/Task';
import { AbstractService } from '../../common/AbstractService';
import { TaskRepository } from '../../repository/TaskRepository';
import { HOST_NAME, ChangesStreamTask, Task } from '../entity/Task';
@@ -55,6 +55,26 @@ export class ChangesStreamService extends AbstractService {
return await this.taskService.findExecuteTask(TaskType.ChangesStream) as ChangesStreamTask;
}
public async suspendTaskWhenExit() {
this.logger.info('[ChangesStreamService.suspendTaskWhenExit:start]');
if (this.config.cnpmcore.enableChangesStream) {
// 防止继续获取新的任务
this.config.cnpmcore.enableChangesStream = false;
const authorIp = os.hostname();
// 暂停当前机器所有的 changesStream 任务
const tasks = await this.taskRepository.findTaskByAuthorIpAndType(authorIp, TaskType.ChangesStream);
for (const task of tasks) {
if (task.state === TaskState.Processing) {
this.logger.info('[ChangesStreamService.suspendTaskWhenExit:suspend] taskId: %s', task.taskId);
// 1. 更新任务状态为 waiting
// 2. 重新推入任务队列供其他机器执行
await this.taskService.retryTask(task);
}
}
}
this.logger.info('[ChangesStreamService.suspendTaskWhenExit:finish]');
}
public async executeTask(task: ChangesStreamTask) {
task.authorIp = os.hostname();
task.authorId = `pid_${process.pid}`;

View File

@@ -0,0 +1,16 @@
import { ContextEventBus, Inject } from '@eggjs/tegg';
import { Advice, IAdvice } from '@eggjs/tegg/aop';
@Advice()
export class EventCorkAdvice implements IAdvice {
@Inject()
private eventBus: ContextEventBus;
async beforeCall() {
this.eventBus.cork();
}
async afterFinally() {
this.eventBus.uncork();
}
}

View File

@@ -65,6 +65,7 @@ export interface PublishPackageCmd {
const TOTAL = '@@TOTAL@@';
const SCOPE_TOTAL_PREFIX = '@@SCOPE@@:';
const DESCRIPTION_LIMIT = 1024 * 10;
@ContextProto({
accessLevel: AccessLevel.PUBLIC,
@@ -109,6 +110,11 @@ export class PackageManagerService extends AbstractService {
pkg.registryId = cmd.registryId;
}
}
// 防止 description 长度超过 db 限制
if (pkg.description?.length > DESCRIPTION_LIMIT) {
pkg.description = pkg.description.substring(0, DESCRIPTION_LIMIT);
}
await this.packageRepository.savePackage(pkg);
// create maintainer
await this.packageRepository.savePackageMaintainer(pkg.packageId, publisher.userId);
@@ -123,9 +129,14 @@ export class PackageManagerService extends AbstractService {
delete cmd.packageJson.readme;
}
const publishTime = cmd.publishTime || new Date();
// add _cnpmcore_publish_time field to cmd.packageJson
if (!cmd.packageJson._cnpmcore_publish_time) {
cmd.packageJson._cnpmcore_publish_time = new Date();
cmd.packageJson._cnpmcore_publish_time = publishTime;
}
if (!cmd.packageJson.publish_time) {
cmd.packageJson.publish_time = publishTime.getTime();
}
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
@@ -181,6 +192,9 @@ export class PackageManagerService extends AbstractService {
engines: cmd.packageJson.engines,
_hasShrinkwrap: cmd.packageJson._hasShrinkwrap,
hasInstallScript,
// https://github.com/cnpm/npminstall/blob/13efc7eec21a61e509226e3772bfb75cd5605612/lib/install_package.js#L176
// npminstall require publish time to show the recently update versions
publish_time: cmd.packageJson.publish_time,
});
const abbreviatedDistBytes = Buffer.from(abbreviated);
const abbreviatedDistIntegrity = await calculateIntegrity(abbreviatedDistBytes);
@@ -192,7 +206,7 @@ export class PackageManagerService extends AbstractService {
pkgVersion = PackageVersion.create({
packageId: pkg.packageId,
version: cmd.version,
publishTime: cmd.publishTime || new Date(),
publishTime,
manifestDist: pkg.createManifest(cmd.version, {
size: manifestDistBytes.length,
shasum: manifestDistIntegrity.shasum,
@@ -215,7 +229,14 @@ export class PackageManagerService extends AbstractService {
this.distRepository.saveDist(pkgVersion.manifestDist, manifestDistBytes),
this.distRepository.saveDist(pkgVersion.readmeDist, readmeDistBytes),
]);
await this.packageRepository.createPackageVersion(pkgVersion);
try {
await this.packageRepository.createPackageVersion(pkgVersion);
} catch (e) {
if (e.code === 'ER_DUP_ENTRY') {
throw new ForbiddenError(`Can't modify pre-existing version: ${pkg.fullname}@${cmd.version}`);
}
throw e;
}
if (cmd.skipRefreshPackageManifests !== true) {
await this.refreshPackageChangeVersionsToDists(pkg, [ pkgVersion.version ]);
}

View File

@@ -4,6 +4,7 @@ import {
ContextProto,
Inject,
} from '@eggjs/tegg';
import { Pointcut } from '@eggjs/tegg/aop';
import {
EggContextHttpClient,
} from 'egg';
@@ -18,7 +19,6 @@ import { TaskRepository } from '../../repository/TaskRepository';
import { PackageRepository } from '../../repository/PackageRepository';
import { PackageVersionDownloadRepository } from '../../repository/PackageVersionDownloadRepository';
import { UserRepository } from '../../repository/UserRepository';
import { DistRepository } from '../../repository/DistRepository';
import { Task, SyncPackageTaskOptions, CreateSyncPackageTask } from '../entity/Task';
import { Package } from '../entity/Package';
import { UserService } from './UserService';
@@ -30,6 +30,7 @@ import { RegistryManagerService } from './RegistryManagerService';
import { Registry } from '../entity/Registry';
import { BadRequestError } from 'egg-errors';
import { ScopeManagerService } from './ScopeManagerService';
import { EventCorkAdvice } from './EventCorkerAdvice';
function isoNow() {
return new Date().toISOString();
@@ -63,8 +64,6 @@ export class PackageSyncerService extends AbstractService {
@Inject()
private readonly httpclient: EggContextHttpClient;
@Inject()
private readonly distRepository: DistRepository;
@Inject()
private readonly registryManagerService: RegistryManagerService;
@Inject()
private readonly scopeManagerService: ScopeManagerService;
@@ -245,6 +244,12 @@ export class PackageSyncerService extends AbstractService {
return registry;
}
// 由于 cnpmcore 将 version 和 tag 作为两个独立的 changes 事件分发
// 普通版本发布时,短时间内会有两条相同 task 进行同步
// 尽量保证读取和写入都需保证任务幂等,需要确保 changes 在同步任务完成后再触发
// 通过 DB 唯一索引来保证任务幂等,插入失败不影响 pkg.manifests 更新
// 通过 eventBus.cork/uncork 来暂缓事件触发
@Pointcut(EventCorkAdvice)
public async executeTask(task: Task) {
const fullname = task.targetName;
const [ scope, name ] = getScopeAndName(fullname);
@@ -337,10 +342,13 @@ export class PackageSyncerService extends AbstractService {
const contentLength = headers['content-length'] || '-';
logs.push(`[${isoNow()}] HTTP [${status}] content-length: ${contentLength}, timing: ${JSON.stringify(res.timing)}`);
if (status === 404) {
// 404 unpublished
// 451 blocked
const shouldRemovePkg = status === 404 || status === 451;
if (shouldRemovePkg) {
if (pkg) {
await this.packageManagerService.unpublishPackage(pkg);
logs.push(`[${isoNow()}] 🟢 Package "${fullname}" was unpublished caused by 404 response: ${JSON.stringify(data)}`);
logs.push(`[${isoNow()}] 🟢 Package "${fullname}" was unpublished caused by ${status} response: ${JSON.stringify(data)}`);
logs.push(`[${isoNow()}] 🟢 log: ${logUrl}`);
logs.push(`[${isoNow()}] 🟢🟢🟢🟢🟢 ${url} 🟢🟢🟢🟢🟢`);
await this.taskService.finishTask(task, TaskState.Success, logs.join('\n'));
@@ -470,17 +478,6 @@ export class PackageSyncerService extends AbstractService {
updateVersions.push(version);
logs.push(`[${isoNow()}] 🐛 Remote version ${version} not exists on local abbreviated manifests, need to refresh`);
}
} else {
// try to read from db detect if last sync interrupt before refreshPackageManifestsToDists() be called
existsItem = await this.distRepository.findPackageVersionManifest(pkg.packageId, version);
// only allow existsItem on db to force refresh, to avoid big versions fresh
// see https://r.cnpmjs.org/-/package/@npm-torg/public-scoped-free-org-test-package-2/syncs/61fcc7e8c1646e26a845b674/log
if (existsItem) {
// version not exists on manifests, need to refresh
// bugfix: https://github.com/cnpm/cnpmcore/issues/115
updateVersions.push(version);
logs.push(`[${isoNow()}] 🐛 Remote version ${version} not exists on local manifests, need to refresh`);
}
}
if (existsItem && forceSyncHistory === true) {
@@ -568,17 +565,6 @@ export class PackageSyncerService extends AbstractService {
if (!pkg) {
pkg = await this.packageRepository.findPackage(scope, name);
}
if (pkg) {
// check again, make sure prefix version not exists
const existsPkgVersion = await this.packageRepository.findPackageVersion(pkg.packageId, version);
if (existsPkgVersion) {
await rm(localFile, { force: true });
logs.push(`[${isoNow()}] 🐛 [${syncIndex}] Synced version ${version} already exists, skip publish it`);
await this.taskService.appendTaskLog(task, logs.join('\n'));
logs = [];
continue;
}
}
const publishCmd = {
scope,
@@ -596,12 +582,15 @@ export class PackageSyncerService extends AbstractService {
skipRefreshPackageManifests: true,
};
try {
// 当 version 记录已经存在时,还需要校验一下 pkg.manifests 是否存在
const pkgVersion = await this.packageManagerService.publish(publishCmd, users[0]);
updateVersions.push(pkgVersion.version);
logs.push(`[${isoNow()}] 🟢 [${syncIndex}] Synced version ${version} success, packageVersionId: ${pkgVersion.packageVersionId}, db id: ${pkgVersion.id}`);
} catch (err: any) {
if (err.name === 'ForbiddenError') {
logs.push(`[${isoNow()}] 🐛 [${syncIndex}] Synced version ${version} already exists, skip publish error`);
logs.push(`[${isoNow()}] 🐛 [${syncIndex}] Synced version ${version} already exists, skip publish, try to set in local manifest`);
// 如果 pkg.manifests 不存在,需要补充一下
updateVersions.push(version);
} else {
err.taskId = task.taskId;
this.logger.error(err);
@@ -680,6 +669,12 @@ export class PackageSyncerService extends AbstractService {
let shouldRefreshDistTags = false;
for (const tag in distTags) {
const version = distTags[tag];
// 新 tag 指向的版本既不在存量数据里,也不在本次同步版本列表里
// 例如 latest 对应的 version 写入失败跳过
if (!existsVersionMap[version] && !updateVersions.includes(version)) {
logs.push(`[${isoNow()}] 🚧 invalid tag(${tag}: ${version}), version is not exists, skip`);
continue;
}
const changed = await this.packageManagerService.savePackageTag(pkg, tag, version);
if (changed) {
changedTags.push({ action: 'change', tag, version });

View File

@@ -28,16 +28,21 @@ export class TaskService extends AbstractService {
public async createTask(task: Task, addTaskQueueOnExists: boolean) {
const existsTask = await this.taskRepository.findTaskByTargetName(task.targetName, task.type);
if (existsTask) {
if (addTaskQueueOnExists && existsTask.state === TaskState.Waiting) {
const queueLength = await this.getTaskQueueLength(task.type);
if (queueLength < this.config.cnpmcore.taskQueueHighWaterSize) {
// make sure waiting task in queue
await this.queueAdapter.push<string>(task.type, existsTask.taskId);
this.logger.info('[TaskService.createTask:exists-to-queue] taskType: %s, targetName: %s, taskId: %s, queue size: %s',
task.type, task.targetName, task.taskId, queueLength);
// 如果任务还未被触发,就不继续重复创建
// 如果任务正在执行,可能任务状态已更新,这种情况需要继续创建
if (existsTask.state === TaskState.Waiting) {
// 提高任务的优先级
if (addTaskQueueOnExists) {
const queueLength = await this.getTaskQueueLength(task.type);
if (queueLength < this.config.cnpmcore.taskQueueHighWaterSize) {
// make sure waiting task in queue
await this.queueAdapter.push<string>(task.type, existsTask.taskId);
this.logger.info('[TaskService.createTask:exists-to-queue] taskType: %s, targetName: %s, taskId: %s, queue size: %s',
task.type, task.targetName, task.taskId, queueLength);
}
}
return existsTask;
}
return existsTask;
}
await this.taskRepository.saveTask(task);
await this.queueAdapter.push<string>(task.type, task.taskId);

5
app/core/typing.ts Normal file
View File

@@ -0,0 +1,5 @@
import { ChangesStreamService } from './service/ChangesStreamService';
export interface ContextCnpmcore {
changesStreamService: ChangesStreamService;
}

View File

@@ -33,9 +33,10 @@ export class BinarySyncController extends AbstractController {
method: HTTPMethodEnum.GET,
})
async listBinaries() {
return Object.values(binaries).map(binaryConfig => {
return Object.entries(binaries).map(([ binaryName, binaryConfig ]) => {
return {
name: `${binaryConfig.category}/`,
name: `${binaryName}/`,
category: `${binaryConfig.category}/`,
description: binaryConfig.description,
distUrl: binaryConfig.distUrl,
repoUrl: /^https?:\/\//.test(binaryConfig.repo) ? binaryConfig.repo : `https://github.com/${binaryConfig.repo}`,
@@ -59,7 +60,18 @@ export class BinarySyncController extends AbstractController {
const parsed = path.parse(subpath);
const parent = parsed.dir === '/' ? '/' : `${parsed.dir}/`;
const name = subpath.endsWith('/') ? `${parsed.base}/` : parsed.base;
const binary = await this.binarySyncerService.findBinary(binaryName, parent, name);
// 首先查询 binary === category 的情况
let binary = await this.binarySyncerService.findBinary(binaryName, parent, name);
if (!binary) {
// 查询不到再去查询 mergeCategory 的情况
const category = binaries?.[binaryName]?.category;
if (category) {
// canvas/v2.6.1/canvas-v2.6.1-node-v57-linux-glibc-x64.tar.gz
// -> node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz
binary = await this.binarySyncerService.findBinary(category, parent, name.replace(new RegExp(`^${binaryName}-`), `${category}-`));
}
}
if (!binary) {
throw new NotFoundError(`Binary "${binaryName}${subpath}" not found`);
}

View File

@@ -38,13 +38,13 @@ export class UpdatePackageController extends AbstractController {
method: HTTPMethodEnum.PUT,
})
async update(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPBody() data: Maintainer) {
const npmCommand = ctx.get('npm-command');
if (npmCommand === 'unpublish') {
if (this.isNpmCommandValid(ctx, 'unpublish')) {
// ignore it
return { ok: false };
}
// only support update maintainer
if (npmCommand !== 'owner') {
if (!this.isNpmCommandValid(ctx, 'owner')) {
const npmCommand = this.getNpmCommand(ctx);
throw new BadRequestError(`header: npm-command expected "owner", but got "${npmCommand}"`);
}
ctx.tValidate(MaintainerDataRule, data);
@@ -61,4 +61,21 @@ export class UpdatePackageController extends AbstractController {
await this.packageManagerService.replacePackageMaintainers(pkg, users);
return { ok: true };
}
private getNpmCommand(ctx: EggContext) {
// npm@6: referer: 'xxx [REDACTED]'
// npm@>=7: 'npm-command': 'xxx'
let npmCommand = ctx.get('npm-command');
if (!npmCommand) {
npmCommand = ctx.get('referer').split(' ', 1)[0];
}
return npmCommand;
}
private isNpmCommandValid(ctx: EggContext, expectCommand: string) {
const npmCommand = this.getNpmCommand(ctx);
return npmCommand === expectCommand;
}
}

View File

@@ -21,10 +21,14 @@ export class CreateSyncBinaryTask {
async subscribe() {
if (!this.config.cnpmcore.enableSyncBinary) return;
for (const binary of Object.values(binaries)) {
if (this.config.env === 'unittest' && binary.category !== 'node') continue;
for (const [ binaryName, binary ] of Object.entries(binaries)) {
if (this.config.env === 'unittest' && binaryName !== 'node') continue;
if (binary.disable) continue;
await this.binarySyncerService.createTask(binary.category);
// 默认只同步 binaryName 的二进制,即使有不一致的 category会在同名的 binaryName 任务中同步
// 例如 canvas 只同步 binaryName 为 canvas 的二进制,不同步 category 为 node-canvas-prebuilt 的二进制
// node-canvas-prebuilt 的二进制会在 node-canvas-prebuilt 的任务中同步
await this.binarySyncerService.createTask(binaryName);
}
}
}

View File

@@ -116,4 +116,12 @@ export class TaskRepository extends AbstractRepository {
}).limit(1000);
return models.map(model => ModelConvertor.convertModelToEntity(model, TaskEntity));
}
async findTaskByAuthorIpAndType(authorIp: string, type: TaskType) {
const models = await this.Task.find({
type,
authorIp,
}).limit(1000);
return models.map(model => ModelConvertor.convertModelToEntity(model, TaskEntity));
}
}

View File

@@ -13,7 +13,7 @@ export enum SyncerClass {
}
export type BinaryTaskConfig = {
category: string;
category: string; // 默认 category 为 binaryName但是有些 binary 会有不同的 category比如 canvas包含 canvas 和 node-canvas-prebuilt 两个
description: string;
syncer: SyncerClass;
repo: string;
@@ -637,13 +637,6 @@ const binaries: {
repo: 'eugeneware/ffmpeg-static',
distUrl: 'https://github.com/eugeneware/ffmpeg-static/releases',
},
canvas: {
category: 'canvas',
description: 'Node canvas is a Cairo backed Canvas implementation for NodeJS.',
syncer: SyncerClass.GithubBinary,
repo: 'Automattic/node-canvas',
distUrl: 'https://github.com/Automattic/node-canvas/releases',
},
nodejieba: {
category: 'nodejieba',
description: '"结巴"中文分词的Node.js版本',
@@ -862,6 +855,42 @@ const binaries: {
repo: 'dragonflyoss/image-service',
distUrl: 'https://github.com/dragonflyoss/image-service/releases',
},
canvas: {
// canvas@<=2.6.1 二进制需要从 node-canvas-prebuilt 下载
category: 'node-canvas-prebuilt',
description: 'Node canvas is a Cairo backed Canvas implementation for NodeJS.',
syncer: SyncerClass.GithubBinary,
repo: 'Automattic/node-canvas',
distUrl: 'https://github.com/Automattic/node-canvas/releases',
},
'canvas-prebuilt': {
category: 'canvas-prebuilt',
distUrl: 'https://github.com/node-gfx/node-canvas-prebuilt/releases',
repo: 'chearon/node-canvas-prebuilt',
description: 'Prebuilt versions of node-canvas as a drop-in replacement',
syncer: SyncerClass.GithubBinary,
options: {
nodeArchs: {
linux: [ 'x64' ],
darwin: [ 'x64' ],
win32: [ 'x64' ],
},
},
},
'node-canvas-prebuilt': {
category: 'node-canvas-prebuilt',
distUrl: 'https://github.com/node-gfx/node-canvas-prebuilt/releases',
repo: 'node-gfx/node-canvas-prebuilt',
description: 'Repo used to build binaries for node-canvas on CI',
syncer: SyncerClass.GithubBinary,
options: {
nodeArchs: {
linux: [ 'x64' ],
darwin: [ 'x64' ],
win32: [ 'x64' ],
},
},
},
};
export default binaries;

7
module.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
import { ContextCnpmcore } from "./app/core/typing";
declare module "egg" {
export interface EggContextModule {
cnpmcoreCore: ContextCnpmcore;
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "cnpmcore",
"version": "2.4.1",
"version": "2.10.1",
"description": "npm core",
"files": [
"dist/**/*"
@@ -42,13 +42,13 @@
"lint:fix": "eslint . --ext .ts --fix",
"test": "npm run lint:fix && npm run test-local",
"prepare-database": "rm -rf dist && ts-node test/prepare.ts",
"test-local": "npm run prepare-database && egg-bin test -r tsconfig-paths/register --full-trace",
"t": "npm run lint:fix && npm run prepare-database && egg-bin test --changed -r tsconfig-paths/register --full-trace",
"cov": "npm run prepare-database && egg-bin cov -r tsconfig-paths/register --full-trace",
"ci": "npm run lint && npm run cov",
"test-local": "npm run prepare-database && egg-bin test --full-trace",
"t": "npm run lint:fix && npm run prepare-database && egg-bin test --changed --full-trace",
"cov": "npm run prepare-database && egg-bin cov --full-trace",
"ci": "npm run lint && npm run cov && npm run tsc:prod",
"clean": "tsc -b --clean && rm -rf dist",
"tsc": "npm run clean && ets && tsc -p ./tsconfig.json",
"tsc:prod": "rm -rf dist && ets && tsc -p ./tsconfig.prod.json",
"tsc": "npm run clean && tsc -p ./tsconfig.json",
"tsc:prod": "npm run clean && tsc -p ./tsconfig.prod.json",
"prepublishOnly": "npm run tsc:prod",
"start": "eggctl start --daemon",
"stop": "eggctl stop"
@@ -80,7 +80,7 @@
"base-x": "^3.0.9",
"bson-objectid": "^2.0.1",
"dayjs": "^1.10.7",
"egg": "^3.1.0",
"egg": "^3.9.0",
"egg-cors": "^2.2.3",
"egg-errors": "^2.3.0",
"egg-redis": "^2.4.0",
@@ -102,18 +102,18 @@
"validate-npm-package-name": "^3.0.0"
},
"devDependencies": {
"@types/mocha": "^9.0.0",
"@types/mocha": "^10.0.1",
"@types/mysql": "^2.15.21",
"@types/semver": "^7.3.12",
"coffee": "^5.4.0",
"egg-bin": "^4.16.4",
"egg-bin": "^5.9.0",
"egg-mock": "^5.0.1",
"eslint": "^7.32.0",
"eslint-config-egg": "^9.0.0",
"eslint": "^8.29.0",
"eslint-config-egg": "^12.1.0",
"git-contributor": "^1.0.10",
"npm-cli-login": "^1.0.0",
"ts-node": "^10.4.0",
"typescript": "~4.7.0"
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
},
"author": "killagu",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
-- fix https://github.com/cnpm/cnpmcore/issues/343
UPDATE
`cnpmcore`.`registries`
`registries`
SET
`host` = 'https://registry.npmjs.org'
WHERE

View File

@@ -323,4 +323,17 @@ export class TestUtil {
}
return Buffer.concat(chunks).toString();
}
static pickKeys(obj, keys) {
const d: Record<string, any> = [];
obj.forEach(item => {
const newItem = {};
for (const key of keys) {
newItem[key] = item[key];
}
d.push(newItem);
});
return d;
}
}

View File

@@ -22,7 +22,7 @@ describe('test/common/adapter/binary/ApiBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('cnpmjs.org/mirrors/apis/node.json'),
persist: false,
});
const binary = new ApiBinary(ctx.httpclient, ctx.logger, binaries.node, 'https://cnpmjs.org/mirrors/apis');
const binary = new ApiBinary(ctx.httpclient, ctx.logger, binaries.node, 'https://cnpmjs.org/mirrors/apis', 'node');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -56,7 +56,7 @@ describe('test/common/adapter/binary/ApiBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('r.cnpmjs.org/-/binary/node/v16.13.1.json'),
persist: false,
});
const binary = new ApiBinary(ctx.httpclient, ctx.logger, binaries.node, 'https://r.cnpmjs.org/-/binary');
const binary = new ApiBinary(ctx.httpclient, ctx.logger, binaries.node, 'https://r.cnpmjs.org/-/binary', 'node');
const result = await binary.fetch('/v16.13.1/');
assert(result);
assert(result.items.length > 0);

View File

@@ -22,7 +22,7 @@ describe('test/common/adapter/binary/BucketBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('chromedriver.storage.googleapis.com/index.xml'),
persist: false,
});
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries.chromedriver);
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries.chromedriver, 'chromedriver');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -51,7 +51,7 @@ describe('test/common/adapter/binary/BucketBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('chromedriver.storage.googleapis.com/97.0.4692.71.xml'),
persist: false,
});
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries.chromedriver);
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries.chromedriver, 'chromedriver');
const result = await binary.fetch('/97.0.4692.71/');
assert(result);
assert(result.items.length > 0);
@@ -77,7 +77,7 @@ describe('test/common/adapter/binary/BucketBinary.test.ts', () => {
persist: false,
});
// https://selenium-release.storage.googleapis.com/?delimiter=/&prefix=2.43/
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries.selenium);
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries.selenium, 'selenium');
const result = await binary.fetch('/2.43/');
assert(result);
assert(result.items.length > 0);
@@ -91,7 +91,7 @@ describe('test/common/adapter/binary/BucketBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('node-inspector.s3.amazonaws.com/index.xml'),
persist: false,
});
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries['node-inspector']);
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries['node-inspector'], 'node-inspector');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -105,7 +105,7 @@ describe('test/common/adapter/binary/BucketBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('prisma-builds.s3-eu-west-1.amazonaws.com/index.xml'),
persist: false,
});
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries.prisma);
const binary = new BucketBinary(ctx.httpclient, ctx.logger, binaries.prisma, 'prisma');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);

View File

@@ -22,7 +22,7 @@ describe('test/common/adapter/binary/CypressBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('registry.npmjs.com/cypress.json'),
persist: false,
});
const binary = new CypressBinary(ctx.httpclient, ctx.logger, binaries.cypress);
const binary = new CypressBinary(ctx.httpclient, ctx.logger, binaries.cypress, 'cypress');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -51,13 +51,14 @@ describe('test/common/adapter/binary/CypressBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('registry.npmjs.com/cypress.json'),
persist: false,
});
const binary = new CypressBinary(ctx.httpclient, ctx.logger, binaries.cypress);
const binary = new CypressBinary(ctx.httpclient, ctx.logger, binaries.cypress, 'cypress');
let result = await binary.fetch('/4.0.0/');
assert(result);
assert(result.items.length === 3);
assert(result.items.length === 4);
assert(result.items[0].name === 'darwin-x64/');
assert(result.items[1].name === 'linux-x64/');
assert(result.items[2].name === 'win32-x64/');
assert(result.items[1].name === 'darwin-arm64/');
assert(result.items[2].name === 'linux-x64/');
assert(result.items[3].name === 'win32-x64/');
assert(result.items[0].isDir);
result = await binary.fetch('/4.0.0/darwin-x64/');
@@ -67,6 +68,13 @@ describe('test/common/adapter/binary/CypressBinary.test.ts', () => {
assert(result.items[0].url === 'https://cdn.cypress.io/desktop/4.0.0/darwin-x64/cypress.zip');
assert(!result.items[0].isDir);
result = await binary.fetch('/4.0.0/darwin-arm64/');
assert(result);
assert(result.items.length === 1);
assert(result.items[0].name === 'cypress.zip');
assert(result.items[0].url === 'https://cdn.cypress.io/desktop/4.0.0/darwin-arm64/cypress.zip');
assert(!result.items[0].isDir);
result = await binary.fetch('/4.0.0/linux-x64/');
assert(result);
assert(result.items.length === 1);

View File

@@ -23,7 +23,7 @@ describe('test/common/adapter/binary/ElectronBinary.test.ts', () => {
data: response,
status: 200,
});
const binary = new ElectronBinary(ctx.httpclient, ctx.logger, binaries.electron);
const binary = new ElectronBinary(ctx.httpclient, ctx.logger, binaries.electron, 'electron');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);

View File

@@ -23,7 +23,7 @@ describe('test/common/adapter/binary/GithubBinary.test.ts', () => {
data: response,
status: 200,
});
const binary = new GithubBinary(ctx.httpclient, ctx.logger, binaries.electron);
const binary = new GithubBinary(ctx.httpclient, ctx.logger, binaries.electron, 'electron');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);

View File

@@ -20,7 +20,7 @@ describe('test/common/adapter/binary/ImageminBinary.test.ts', () => {
app.mockHttpclient('https://registry.npmjs.com/jpegtran-bin', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.com/jpegtran-bin.json'),
});
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['jpegtran-bin']);
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['jpegtran-bin'], 'jpegtran-bin');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -88,7 +88,7 @@ describe('test/common/adapter/binary/ImageminBinary.test.ts', () => {
app.mockHttpclient('https://registry.npmjs.com/advpng-bin', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.com/advpng-bin.json'),
});
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['advpng-bin']);
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['advpng-bin'], 'advpng-bin');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -141,7 +141,7 @@ describe('test/common/adapter/binary/ImageminBinary.test.ts', () => {
app.mockHttpclient('https://registry.npmjs.com/mozjpeg', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.com/mozjpeg.json'),
});
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['mozjpeg-bin']);
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['mozjpeg-bin'], 'mozjpeg-bin');
let result = await binary.fetch('/');
assert(result);
// console.log(result.items);
@@ -205,7 +205,7 @@ describe('test/common/adapter/binary/ImageminBinary.test.ts', () => {
app.mockHttpclient('https://registry.npmjs.com/gifsicle', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.com/gifsicle.json'),
});
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['gifsicle-bin']);
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['gifsicle-bin'], 'gifsicle-bin');
const result = await binary.fetch('/');
assert(result);
// console.log(result.items);
@@ -234,7 +234,7 @@ describe('test/common/adapter/binary/ImageminBinary.test.ts', () => {
app.mockHttpclient('https://registry.npmjs.com/optipng-bin', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.com/optipng-bin.json'),
});
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['optipng-bin']);
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['optipng-bin'], 'optipng-bin');
const result = await binary.fetch('/');
assert(result);
// console.log(result.items);
@@ -263,7 +263,7 @@ describe('test/common/adapter/binary/ImageminBinary.test.ts', () => {
app.mockHttpclient('https://registry.npmjs.com/zopflipng-bin', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.com/zopflipng-bin.json'),
});
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['zopflipng-bin']);
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['zopflipng-bin'], 'zopflipng-bin');
const result = await binary.fetch('/');
assert(result);
// console.log(result.items);
@@ -292,7 +292,7 @@ describe('test/common/adapter/binary/ImageminBinary.test.ts', () => {
app.mockHttpclient('https://registry.npmjs.com/jpegoptim-bin', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.com/jpegoptim-bin.json'),
});
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['jpegoptim-bin']);
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['jpegoptim-bin'], 'jpegoptim-bin');
const result = await binary.fetch('/');
assert(result);
// console.log(result.items);
@@ -321,7 +321,7 @@ describe('test/common/adapter/binary/ImageminBinary.test.ts', () => {
app.mockHttpclient('https://registry.npmjs.com/guetzli', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.com/guetzli.json'),
});
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['guetzli-bin']);
const binary = new ImageminBinary(ctx.httpclient, ctx.logger, binaries['guetzli-bin'], 'guetzli-bin');
const result = await binary.fetch('/');
assert(result);
// console.log(result.items);

View File

@@ -21,7 +21,7 @@ describe('test/common/adapter/binary/NodeBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/dist/', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/site/index.html'),
});
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries.node);
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries.node, 'node');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -54,7 +54,7 @@ describe('test/common/adapter/binary/NodeBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/dist/v16.13.1/', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/site/v16.13.1/index.html'),
});
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries.node);
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries.node, 'node');
const result = await binary.fetch('/v16.13.1/');
assert(result);
assert(result.items.length > 0);
@@ -87,7 +87,7 @@ describe('test/common/adapter/binary/NodeBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/download/nightly/v14.0.0-nightly20200119b318926634/', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/download/nightly/v14.0.0-nightly20200119b318926634/index.html'),
});
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries['node-nightly']);
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries['node-nightly'], 'node-nightly');
const result = await binary.fetch('/v14.0.0-nightly20200119b318926634/');
assert(result);
assert(result.items.length > 0);
@@ -129,7 +129,7 @@ describe('test/common/adapter/binary/NodeBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/download/nightly/v14.0.0-nightly20200204ee9e689df2/', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/download/nightly/v14.0.0-nightly20200204ee9e689df2/index.html'),
});
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries['node-nightly']);
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries['node-nightly'], 'node-nightly');
const result = await binary.fetch('/v14.0.0-nightly20200204ee9e689df2/');
assert(result);
assert(result.items.length > 0);
@@ -184,7 +184,7 @@ describe('test/common/adapter/binary/NodeBinary.test.ts', () => {
persist: false,
});
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries.python);
const binary = new NodeBinary(ctx.httpclient, ctx.logger, binaries.python, 'python');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);

View File

@@ -24,7 +24,7 @@ describe('test/common/adapter/binary/NodePreGypBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/dist/index.json', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/site/index.json'),
});
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries.grpc);
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries.grpc, 'grpc');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -67,7 +67,7 @@ describe('test/common/adapter/binary/NodePreGypBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/dist/index.json', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/site/index.json'),
});
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries['grpc-tools']);
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries['grpc-tools'], 'grpc-tools');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -111,7 +111,7 @@ describe('test/common/adapter/binary/NodePreGypBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/dist/index.json', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/site/index.json'),
});
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries.nodegit);
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries.nodegit, 'nodegit');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -155,7 +155,7 @@ describe('test/common/adapter/binary/NodePreGypBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/dist/index.json', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/site/index.json'),
});
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries['skia-canvas']);
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries['skia-canvas'], 'skia-canvas');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -210,7 +210,7 @@ describe('test/common/adapter/binary/NodePreGypBinary.test.ts', () => {
app.mockHttpclient('https://nodejs.org/dist/index.json', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/site/index.json'),
});
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries.wrtc);
const binary = new NodePreGypBinary(ctx.httpclient, ctx.logger, binaries.wrtc, 'wrtc');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);

View File

@@ -22,7 +22,7 @@ describe('test/common/adapter/binary/NwjsBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('dl.nwjs.io/index.html'),
persist: false,
});
const binary = new NwjsBinary(ctx.httpclient, ctx.logger, binaries.nwjs);
const binary = new NwjsBinary(ctx.httpclient, ctx.logger, binaries.nwjs, 'nwjs');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -44,7 +44,7 @@ describe('test/common/adapter/binary/NwjsBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('nwjs2.s3.amazonaws.com/v0.59.0.xml'),
persist: false,
});
const binary = new NwjsBinary(ctx.httpclient, ctx.logger, binaries.nwjs);
const binary = new NwjsBinary(ctx.httpclient, ctx.logger, binaries.nwjs, 'nwjs');
let result = await binary.fetch('/v0.59.0/');
assert(result);
assert(result.items.length > 0);

View File

@@ -29,7 +29,7 @@ describe('test/common/adapter/binary/PlaywrightBinary.test.ts', () => {
})
.reply(200, await TestUtil.readFixturesFile('unpkg.com/playwright-core-browsers.json'))
.persist();
const binary = new PlaywrightBinary(ctx.httpclient, ctx.logger, binaries.playwright);
const binary = new PlaywrightBinary(ctx.httpclient, ctx.logger, binaries.playwright, 'playwright');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -57,7 +57,7 @@ describe('test/common/adapter/binary/PlaywrightBinary.test.ts', () => {
})
.reply(200, await TestUtil.readFixturesFile('unpkg.com/playwright-core-browsers.json'))
.persist();
const binary = new PlaywrightBinary(ctx.httpclient, ctx.logger, binaries.playwright);
const binary = new PlaywrightBinary(ctx.httpclient, ctx.logger, binaries.playwright, 'playwright');
let result = await binary.fetch('/builds/');
assert(result);
// console.log(result.items);

View File

@@ -30,7 +30,7 @@ describe('test/common/adapter/binary/PuppeteerBinary.test.ts', () => {
data: '1055816',
persist: false,
});
const binary = new PuppeteerBinary(ctx.httpclient, ctx.logger, binaries['chromium-browser-snapshots']);
const binary = new PuppeteerBinary(ctx.httpclient, ctx.logger, binaries['chromium-browser-snapshots'], 'chromium-browser-snapshots');
let result = await binary.fetch('/');
assert(result);
assert(result.items.length === 5);

View File

@@ -22,7 +22,7 @@ describe('test/common/adapter/binary/SqlcipherBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('registry.npmjs.com/@journeyapps/sqlcipher.json'),
persist: false,
});
const binary = new SqlcipherBinary(ctx.httpclient, ctx.logger, binaries['@journeyapps/sqlcipher']);
const binary = new SqlcipherBinary(ctx.httpclient, ctx.logger, binaries['@journeyapps/sqlcipher'], '@journeyapps/sqlcipher');
const result = await binary.fetch('/');
assert(result);
assert(result.items.length > 0);
@@ -52,7 +52,7 @@ describe('test/common/adapter/binary/SqlcipherBinary.test.ts', () => {
data: await TestUtil.readFixturesFile('registry.npmjs.com/@journeyapps/sqlcipher.json'),
persist: false,
});
const binary = new SqlcipherBinary(ctx.httpclient, ctx.logger, binaries['@journeyapps/sqlcipher']);
const binary = new SqlcipherBinary(ctx.httpclient, ctx.logger, binaries['@journeyapps/sqlcipher'], '@journeyapps/sqlcipher');
const result = await binary.fetch('/v5.3.1/');
assert(result);
assert(result.items.length > 0);

View File

@@ -5,7 +5,7 @@ import { Registry } from 'app/core/entity/Registry';
import { RegistryManagerService } from 'app/core/service/RegistryManagerService';
import assert = require('assert');
import { Context } from 'egg';
import { app, mock } from 'egg-mock/bootstrap';
import { app } from 'egg-mock/bootstrap';
describe('test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts', () => {
let ctx: Context;
@@ -83,7 +83,7 @@ describe('test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts', ()
],
},
});
const stream = await cnpmjsorgChangesStream.fetchChanges(registry, '1');
const stream = cnpmjsorgChangesStream.fetchChanges(registry, '1');
const changes:ChangesStreamChange[] = [];
for await (const change of stream) {
changes.push(change);
@@ -92,7 +92,7 @@ describe('test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts', ()
});
it('should reject max limit', async () => {
mock(ctx.httpclient, 'request', async (url: string) => {
app.mockHttpclient('https://r2.cnpmjs.org/_changes?since=1&limit=', 'GET', (url = '') => {
const limit = (new URL(url)).searchParams.get('limit');
return {
data: {
@@ -105,7 +105,7 @@ describe('test/common/adapter/changesStream/CnpmjsorgChangesStream.test.ts', ()
},
};
});
const stream = await cnpmjsorgChangesStream.fetchChanges(registry, '1');
const stream = cnpmjsorgChangesStream.fetchChanges(registry, '1');
await assert.rejects(async () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _ of stream) {

View File

@@ -0,0 +1,27 @@
import assert = require('assert');
import { app } from 'egg-mock/bootstrap';
import { Context } from 'egg';
import { BinarySyncerService } from 'app/core/service/BinarySyncerService';
describe('test/core/service/BinarySyncerService/createTask.test.ts', () => {
let ctx: Context;
let binarySyncerService: BinarySyncerService;
beforeEach(async () => {
ctx = await app.mockModuleContext();
binarySyncerService = await ctx.getEggObject(BinarySyncerService);
});
afterEach(async () => {
await app.destroyModuleContext(ctx);
});
describe('createTask()', () => {
it('should ignore duplicate binary task', async () => {
const task = await binarySyncerService.createTask('banana', {});
const newTask = await binarySyncerService.createTask('banana', {});
assert(task?.taskId === newTask?.taskId);
assert(task?.bizId === 'SyncBinary:banana');
});
});
});

View File

@@ -1,4 +1,4 @@
import assert = require('assert/strict');
import assert = require('assert');
import { Readable } from 'node:stream';
import { app, mock } from 'egg-mock/bootstrap';
import { Context } from 'egg';
@@ -10,6 +10,7 @@ import { RegistryType } from 'app/common/enum/Registry';
import { ScopeManagerService } from 'app/core/service/ScopeManagerService';
import { Registry } from 'app/core/entity/Registry';
import { TestUtil } from 'test/TestUtil';
import { RedisQueueAdapter } from 'app/infra/QueueAdapter';
describe('test/core/service/ChangesStreamService.test.ts', () => {
let ctx: Context;
@@ -20,12 +21,14 @@ describe('test/core/service/ChangesStreamService.test.ts', () => {
let task: ChangesStreamTask;
let npmRegistry: Registry;
let cnpmRegistry: Registry;
let queueAdapter: RedisQueueAdapter;
beforeEach(async () => {
ctx = await app.mockModuleContext();
changesStreamService = await ctx.getEggObject(ChangesStreamService);
taskService = await ctx.getEggObject(TaskService);
registryManagerService = await ctx.getEggObject(RegistryManagerService);
scopeManagerService = await ctx.getEggObject(ScopeManagerService);
queueAdapter = await ctx.getEggObject(RedisQueueAdapter);
assert(changesStreamService);
task = Task.createChangesStream('GLOBAL_WORKER', '', '9527');
taskService.createTask(task, false);
@@ -194,4 +197,44 @@ describe('test/core/service/ChangesStreamService.test.ts', () => {
assert(task.data.since === '3');
});
});
describe('suspendTaskWhenExit()', () => {
it('should work', async () => {
app.mockLog();
mock(app.config.cnpmcore, 'enableChangesStream', true);
app.mockHttpclient('https://replicate.npmjs.com/_changes?since=9527', 'GET', () => {
return {
data: {
res: Readable.from(''),
},
};
});
const task = await changesStreamService.findExecuteTask();
assert(task);
await changesStreamService.executeTask(task);
assert(task.state === 'processing');
let len = await queueAdapter.length('changes_stream');
assert(len === 0);
await changesStreamService.suspendTaskWhenExit();
const newTask = await taskService.findTask(task.taskId);
assert(newTask);
assert(newTask.taskId === task.taskId);
assert(newTask.state === 'waiting');
len = await queueAdapter.length('changes_stream');
assert(len === 1);
app.expectLog('[ChangesStreamService.suspendTaskWhenExit:suspend] taskId');
});
it('should ignore when changesStream disable', async () => {
app.mockLog();
mock(app.config.cnpmcore, 'enableChangesStream', true);
await changesStreamService.suspendTaskWhenExit();
app.expectLog('[ChangesStreamService.suspendTaskWhenExit:finish]');
});
});
});

View File

@@ -75,6 +75,28 @@ describe('test/core/service/PackageManagerService/publish.test.ts', () => {
app.expectLog(/\[\d+\.\d+\] \[NFSAdapter:uploadBytes|T\]/);
});
it('should work slice long description', async () => {
app.mockLog();
const { packageId } = await packageManagerService.publish({
dist: {
content: Buffer.alloc(0),
},
tag: '',
scope: '',
name: 'foo',
description: '~'.repeat(1100 * 100),
packageJson: {},
readme: '',
version: '1.0.0',
isPrivate: true,
}, publisher);
const pkgVersion = await packageRepository.findPackageVersion(packageId, '1.0.0');
assert(pkgVersion);
assert.equal(pkgVersion.version, '1.0.0');
const pkg = await packageRepository.findPackage('', 'foo');
assert(pkg?.description === '~'.repeat(1024 * 10));
});
it('should work with dist.localFile', async () => {
const { packageId } = await packageManagerService.publish({
dist: {

View File

@@ -1,18 +1,27 @@
import assert = require('assert');
import { app, mock } from 'egg-mock/bootstrap';
import { Context } from 'egg';
import { setTimeout } from 'timers/promises';
import { PackageSyncerService } from '../../../../app/core/service/PackageSyncerService';
import { TestUtil } from '../../../TestUtil';
import { Task } from 'app/core/entity/Task';
import { TaskState } from '../../../../app/common/enum/Task';
import { TaskRepository } from '../../../../app/repository/TaskRepository';
import { TaskService } from '../../../../app/core/service/TaskService';
describe('test/core/service/PackageSyncerService/createTask.test.ts', () => {
let ctx: Context;
const pkgName = '@cnpmcore/foo';
const username = 'mock_username';
let packageSyncerService: PackageSyncerService;
let taskRepository: TaskRepository;
let taskService: TaskService;
beforeEach(async () => {
ctx = await app.mockModuleContext();
packageSyncerService = await ctx.getEggObject(PackageSyncerService);
taskRepository = await ctx.getEggObject(TaskRepository);
taskService = await ctx.getEggObject(TaskService);
await TestUtil.createPackage({
name: pkgName,
@@ -56,4 +65,25 @@ describe('test/core/service/PackageSyncerService/createTask.test.ts', () => {
});
assert(task);
});
it('should create task when processing', async () => {
mock(packageSyncerService, 'executeTask', async (task: Task) => {
task.state = TaskState.Processing;
await taskRepository.saveTask(task);
await setTimeout(2);
await taskService.finishTask(task, TaskState.Success);
});
const task = await packageSyncerService.createTask(pkgName);
const res = await Promise.all([ packageSyncerService.executeTask(task), (async () => {
await setTimeout(1);
return await packageSyncerService.createTask(pkgName);
})() ]);
assert(res[1].taskId !== task.taskId);
});
it('should not duplicate task when waiting', async () => {
const task = await packageSyncerService.createTask(pkgName);
const newTask = await packageSyncerService.createTask(pkgName);
assert(newTask.taskId === task.taskId);
});
});

View File

@@ -17,6 +17,8 @@ import { Registry } from 'app/core/entity/Registry';
import { RegistryType } from 'app/common/enum/Registry';
import { TaskService } from 'app/core/service/TaskService';
import { ScopeManagerService } from 'app/core/service/ScopeManagerService';
import { UserService } from 'app/core/service/UserService';
import { ChangeRepository } from 'app/repository/ChangeRepository';
describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => {
let ctx: Context;
@@ -27,6 +29,8 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => {
let registryManagerService: RegistryManagerService;
let taskService: TaskService;
let scopeManagerService: ScopeManagerService;
let userService: UserService;
let changeRepository: ChangeRepository;
beforeEach(async () => {
ctx = await app.mockModuleContext();
@@ -37,6 +41,8 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => {
taskService = await ctx.getEggObject(TaskService);
registryManagerService = await ctx.getEggObject(RegistryManagerService);
scopeManagerService = await ctx.getEggObject(ScopeManagerService);
userService = await ctx.getEggObject(UserService);
changeRepository = await ctx.getEggObject(ChangeRepository);
});
afterEach(async () => {
@@ -389,7 +395,6 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => {
persist: false,
});
name = 'cnpmcore-test-sync-dependencies';
// don't add cnpmcore-test-sync-deprecated task if cnpmcore-test-sync-deprecated already exists
const task = await packageSyncerService.createTask(name);
assert(task);
assert.equal(task.targetName, name);
@@ -433,7 +438,7 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => {
app.mockAgent().assertNoPendingInterceptors();
});
it('should ignore publish ForbiddenError on sync task', async () => {
it('should ignore when all version publish ForbiddenError', async () => {
app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-sync-deprecated', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.org/cnpmcore-test-sync-deprecated.json'),
persist: false,
@@ -453,8 +458,7 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => {
const stream = await packageSyncerService.findTaskLog(task);
assert(stream);
const log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('🐛 [1] Synced version 0.0.0 already exists, skip publish error'));
assert(log.includes('🐛 [1] Synced version 0.0.0 already exists, skip publish, try to set in local manifest'));
app.mockAgent().assertNoPendingInterceptors();
});
@@ -483,102 +487,312 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => {
assert(log.includes('❌ All versions sync fail, package not exists'));
});
it('should sync 2 versions package: @cnpmcore/test-sync-package-has-two-versions', async () => {
app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', {
data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}',
persist: false,
repeats: 2,
});
app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'),
persist: false,
});
app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'),
persist: false,
describe('sync version idempotence', async () => {
beforeEach(async () => {
app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', {
data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}',
persist: false,
repeats: 2,
});
app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'),
persist: false,
});
app.mockHttpclient('https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'),
persist: false,
});
});
// https://www.npmjs.com/package/@cnpmcore/test-sync-package-has-two-versions
const name = '@cnpmcore/test-sync-package-has-two-versions';
await packageSyncerService.createTask(name);
let task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
let stream = await packageSyncerService.findTaskLog(task);
assert(stream);
let log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('] 🟢 Synced updated 2 versions, removed 0 versions'));
assert(log.includes('] 🚧 Syncing versions 0 => 2'));
it('should sync 2 versions package: @cnpmcore/test-sync-package-has-two-versions', async () => {
// https://www.npmjs.com/package/@cnpmcore/test-sync-package-has-two-versions
const name = '@cnpmcore/test-sync-package-has-two-versions';
await packageSyncerService.createTask(name);
let task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
let stream = await packageSyncerService.findTaskLog(task);
assert(stream);
let log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('] 🟢 Synced updated 2 versions, removed 0 versions'));
assert(log.includes('] 🚧 Syncing versions 0 => 2'));
// mock listPackageFullManifests return only one version
// 如果 version publish 同步中断了,没有刷新 manifests会导致下一次同步重新 version publish然后报错
// Avoid: Can't modify pre-existing version: 1.0.0
const scopedAndName = getScopeAndName(name);
const manifests = await packageManagerService.listPackageFullManifests(scopedAndName[0], scopedAndName[1]);
delete manifests.data.versions['1.0.0'];
mock.data(PackageManagerService.prototype, 'listPackageFullManifests', manifests);
// mock listPackageFullManifests return only one version
// 如果 version publish 同步中断了,没有刷新 manifests会导致下一次同步重新 version publish然后报错
// Avoid: Can't modify pre-existing version: 1.0.0
const scopedAndName = getScopeAndName(name);
const manifests = await packageManagerService.listPackageFullManifests(scopedAndName[0], scopedAndName[1]);
delete manifests.data.versions['1.0.0'];
mock.data(PackageManagerService.prototype, 'listPackageFullManifests', manifests);
await packageSyncerService.createTask(name);
task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
stream = await packageSyncerService.findTaskLog(task);
assert(stream);
log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('] 🐛 Remote version 1.0.0 not exists on local manifests, need to refresh'));
assert(log.includes('] 🟢 Synced updated 1 versions'));
assert(log.includes('] 🚧 Syncing versions 1 => 2'));
app.mockAgent().assertNoPendingInterceptors();
await mock.restore();
await packageSyncerService.createTask(name);
task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
stream = await packageSyncerService.findTaskLog(task);
assert(stream);
log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('Synced version 1.0.0 already exists, skip publish, try to set in local manifest'));
assert(log.includes('] 🟢 Synced updated 1 versions'));
assert(log.includes('] 🚧 Syncing versions 1 => 2'));
app.mockAgent().assertNoPendingInterceptors();
await mock.restore();
app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', {
data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}',
persist: false,
app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', {
data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}',
persist: false,
});
const abbrs = await packageManagerService.listPackageAbbreviatedManifests(scopedAndName[0], scopedAndName[1]);
delete abbrs.data.versions['1.0.0'];
mock.data(PackageManagerService.prototype, 'listPackageAbbreviatedManifests', abbrs);
await packageSyncerService.createTask(name);
task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
stream = await packageSyncerService.findTaskLog(task);
assert(stream);
log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('] 🐛 Remote version 1.0.0 not exists on local abbreviated manifests, need to refresh'));
assert(log.includes('] 🟢 Synced updated 1 versions'));
assert(log.includes('] 🚧 Syncing versions 2 => 2'));
app.mockAgent().assertNoPendingInterceptors();
await mock.restore();
// mock tag on database but not on manifest dist
// https://github.com/cnpm/cnpmcore/issues/97
app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', {
data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}',
persist: false,
});
const result = await npmRegistry.getFullManifests(name);
result.data['dist-tags'].foo = '2.0.0';
mock.data(NPMRegistry.prototype, 'getFullManifests', result);
mock.data(PackageManagerService.prototype, 'savePackageTag', null);
await packageSyncerService.createTask(name);
task = await packageSyncerService.findExecuteTask();
assert(task);
await packageSyncerService.executeTask(task);
stream = await packageSyncerService.findTaskLog(task);
assert(stream);
log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('] 🚧 Remote tag(foo: 2.0.0) not exists in local dist-tags'));
assert(!log.includes('] 🚧 Refreshing manifests to dists ......'));
app.mockAgent().assertNoPendingInterceptors();
});
const abbrs = await packageManagerService.listPackageAbbreviatedManifests(scopedAndName[0], scopedAndName[1]);
delete abbrs.data.versions['1.0.0'];
mock.data(PackageManagerService.prototype, 'listPackageAbbreviatedManifests', abbrs);
await packageSyncerService.createTask(name);
task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
stream = await packageSyncerService.findTaskLog(task);
assert(stream);
log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('] 🐛 Remote version 1.0.0 not exists on local abbreviated manifests, need to refresh'));
assert(log.includes('] 🟢 Synced updated 1 versions'));
assert(log.includes('] 🚧 Syncing versions 2 => 2'));
app.mockAgent().assertNoPendingInterceptors();
await mock.restore();
it('should updated package manifests when version already published', async () => {
// https://www.npmjs.com/package/@cnpmcore/test-sync-package-has-two-versions
const name = '@cnpmcore/test-sync-package-has-two-versions';
await packageSyncerService.createTask(name);
const task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
const { user } = await userService.create({
name: 'test-user',
password: 'this-is-password',
email: 'hello@example.com',
ip: '127.0.0.1',
});
const publishCmd = {
scope: '@cnpmcore',
name: 'test-sync-package-has-two-versions',
version: '1.0.0',
description: '1.0.0',
readme: '',
registryId: undefined,
packageJson: { name, test: 'test', version: '1.0.0' },
dist: {
content: Buffer.alloc(0),
},
isPrivate: false,
publishTime: new Date(),
skipRefreshPackageManifests: false,
};
const pkgVersion = await packageManagerService.publish(publishCmd, user);
assert(pkgVersion.version === '1.0.0');
const publishCmd2 = {
scope: '@cnpmcore',
name: 'test-sync-package-has-two-versions',
version: '2.0.0',
description: '2.0.0',
readme: '',
registryId: undefined,
packageJson: { name, test: 'test', version: '2.0.0' },
dist: {
content: Buffer.alloc(0),
},
isPrivate: false,
publishTime: new Date(),
skipRefreshPackageManifests: true,
};
const pkgVersion2 = await packageManagerService.publish(publishCmd2, user);
assert(pkgVersion2.version === '2.0.0');
await packageSyncerService.executeTask(task);
const stream = await packageSyncerService.findTaskLog(task);
assert(stream);
const log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('Synced version 2.0.0 already exists, skip publish, try to set in local manifest'));
assert(log.includes('] 🚧 Syncing versions 1 => 2'));
const fullManifests = await packageManagerService.listPackageFullManifests('@cnpmcore', 'test-sync-package-has-two-versions');
assert(fullManifests.data.versions['2.0.0']);
// mock tag on database but not on manifest dist
// https://github.com/cnpm/cnpmcore/issues/97
app.mockHttpclient('https://registry.npmjs.org/%40cnpmcore%2Ftest-sync-package-has-two-versions', 'GET', {
data: '{"_id":"@cnpmcore/test-sync-package-has-two-versions","_rev":"4-541287ae0a14039fea89ac08fa5ec53d","name":"@cnpmcore/test-sync-package-has-two-versions","dist-tags":{"latest":"2.0.0","next":"2.0.0"},"versions":{"1.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"1.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@1.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-WR0T96H8t7ss1FK8GWPPblx+usbjU4bNGRjMHS9t/oVA5DgJDxitydPSFPeIUtXciyekI7R47do9Lc3GgC4P5A==","shasum":"2ddc6ee93b92be6d64139fb1a631d2610f43e946","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-1.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDj5Ui2GU8nVmHFk0hCt/i3gPW9eQdOCZgKzpAlkvERwQIhAPZ0NCefLoEfOpnbdKAUr7Ng9Sy6FMnTsDxDaM2dQHNw"}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_1.0.0_1639442699824_0.6948988437963031"},"_hasShrinkwrap":false},"2.0.0":{"name":"@cnpmcore/test-sync-package-has-two-versions","version":"2.0.0","description":"cnpmcore local test package","main":"index.js","scripts":{"test":"echo \\"hello\\""},"author":"","license":"MIT","gitHead":"60cfb1cf401f87a60a1b0dfd7ee739f98ffd7847","_id":"@cnpmcore/test-sync-package-has-two-versions@2.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-qgHLQzXq+VN7q0JWibeBYrqb3Iajl4lpVuxlQstclRz4ejujfDFswBGSXmCv9FyIIdmSAe5bZo0oHQLsod3pAA==","shasum":"891eb8e08ceadbd86e75b6d66f31f7e5a28a8d68","tarball":"https://registry.npmjs.org/@cnpmcore/test-sync-package-has-two-versions/-/test-sync-package-has-two-versions-2.0.0.tgz","fileCount":2,"unpackedSize":238,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAWVz7mIHF23Gq4a+Swsj2ZSdn87991HcE1+fQm8shNCAiByOIuhaZAbo9hct24qYf7FWqx6Lyluo+Rpnrn91//Ibg=="}]},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/test-sync-package-has-two-versions_2.0.0_1639442732240_0.33204392278137207"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-14T00:44:59.775Z","1.0.0":"2021-12-14T00:44:59.940Z","modified":"2022-05-23T02:33:52.613Z","2.0.0":"2021-12-14T00:45:32.457Z"},"maintainers":[{"email":"killa07071201@gmail.com","name":"killagu"},{"email":"fengmk2@gmail.com","name":"fengmk2"}],"description":"cnpmcore local test package","license":"MIT","readme":"ERROR: No README data found!","readmeFilename":""}',
persist: false,
});
const result = await npmRegistry.getFullManifests(name);
result.data['dist-tags'].foo = '2.0.0';
mock.data(NPMRegistry.prototype, 'getFullManifests', result);
mock.data(PackageManagerService.prototype, 'savePackageTag', null);
await packageSyncerService.createTask(name);
task = await packageSyncerService.findExecuteTask();
assert(task);
await packageSyncerService.executeTask(task);
stream = await packageSyncerService.findTaskLog(task);
assert(stream);
log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('] 🚧 Remote tag(foo: 2.0.0) not exists in local dist-tags'));
assert(!log.includes('] 🚧 Refreshing manifests to dists ......'));
app.mockAgent().assertNoPendingInterceptors();
it('should updated package manifests when version insert duplicated', async () => {
// https://www.npmjs.com/package/@cnpmcore/test-sync-package-has-two-versions
const name = '@cnpmcore/test-sync-package-has-two-versions';
await packageSyncerService.createTask(name);
const task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
const { user } = await userService.create({
name: 'test-user',
password: 'this-is-password',
email: 'hello@example.com',
ip: '127.0.0.1',
});
const publishCmd = {
scope: '@cnpmcore',
name: 'test-sync-package-has-two-versions',
version: '1.0.0',
description: '1.0.0',
readme: '',
registryId: undefined,
packageJson: { name, test: 'test', version: '1.0.0' },
dist: {
content: Buffer.alloc(0),
},
isPrivate: false,
publishTime: new Date(),
skipRefreshPackageManifests: false,
};
const pkgVersion = await packageManagerService.publish(publishCmd, user);
assert(pkgVersion.version === '1.0.0');
const publishCmd2 = {
scope: '@cnpmcore',
name: 'test-sync-package-has-two-versions',
version: '2.0.0',
description: '2.0.0',
readme: '',
registryId: undefined,
packageJson: { name, test: 'test', version: '2.0.0' },
dist: {
content: Buffer.alloc(0),
},
isPrivate: false,
publishTime: new Date(),
skipRefreshPackageManifests: true,
};
const pkgVersion2 = await packageManagerService.publish(publishCmd2, user);
assert(pkgVersion2.version === '2.0.0');
// 模拟查询未发现版本重复,写入时异常
mock(packageRepository, 'findPackageVersion', async () => {
return null;
});
await packageSyncerService.executeTask(task);
const stream = await packageSyncerService.findTaskLog(task);
assert(stream);
const log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes('Synced version 2.0.0 already exists, skip publish, try to set in local manifest'));
assert(log.includes('] 🚧 Syncing versions 1 => 2'));
});
it('should skip version when insert error', async () => {
// https://www.npmjs.com/package/@cnpmcore/test-sync-package-has-two-versions
const name = '@cnpmcore/test-sync-package-has-two-versions';
await packageSyncerService.createTask(name);
const task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
const { user } = await userService.create({
name: 'test-user',
password: 'this-is-password',
email: 'hello@example.com',
ip: '127.0.0.1',
});
const publishCmd = {
scope: '@cnpmcore',
name: 'test-sync-package-has-two-versions',
version: '1.0.0',
description: '1.0.0',
readme: '',
registryId: undefined,
packageJson: { name, test: 'test', version: '1.0.0' },
dist: {
content: Buffer.alloc(0),
},
isPrivate: false,
publishTime: new Date(),
skipRefreshPackageManifests: false,
};
const pkgVersion = await packageManagerService.publish(publishCmd, user);
assert(pkgVersion.version === '1.0.0');
// 模拟查询未发现版本重复,写入时异常
mock(packageRepository, 'findPackageVersion', async () => {
return null;
});
mock(packageRepository, 'createPackageVersion', async () => {
throw new Error('mock error');
});
await packageSyncerService.executeTask(task);
const stream = await packageSyncerService.findTaskLog(task);
assert(stream);
const log = await TestUtil.readStreamToLog(stream);
assert(log.includes(' Synced updated 0 versions, removed 0 versions'));
});
it('event cork should work', async () => {
// https://www.npmjs.com/package/@cnpmcore/test-sync-package-has-two-versions
const name = '@cnpmcore/test-sync-package-has-two-versions';
await packageSyncerService.createTask(name);
const task = await packageSyncerService.findExecuteTask();
assert(task);
assert.equal(task.targetName, name);
await packageSyncerService.executeTask(task);
const stream = await packageSyncerService.findTaskLog(task);
assert(stream);
const finishedTask = await taskService.findTask(task.taskId) as TaskEntity;
const changes = await changeRepository.query(0, 100);
const [ firstChange ] = changes;
const firstChangeDate = new Date(firstChange.createdAt);
const taskFinishedDate = new Date(finishedTask!.updatedAt);
// 任务结束后一起触发
assert(firstChangeDate.getTime() - taskFinishedDate.getTime() > 0);
});
});
it.skip('should sync missing versions in database', async () => {
@@ -1298,6 +1512,45 @@ describe('test/core/service/PackageSyncerService/executeTask.test.ts', () => {
app.mockAgent().assertNoPendingInterceptors();
});
it('should stop sync when upstream blocked', async () => {
// sync first
const name = 'cnpmcore-test-block-from-upstream';
app.mockHttpclient(`https://registry.npmjs.org/${name}`, 'GET', {
data: '{"_id":"cnpmcore-test-block-from-upstream","_rev":"2-bc8b9a2f6532d1bb3f94eaa4e82dbfe0","name":"cnpmcore-test-block-from-upstream","dist-tags":{"latest":"0.0.0"},"versions":{"0.0.0":{"name":"cnpmcore-test-block-from-upstream","readme":"foo readme","version":"0.0.0","description":"","main":"index.js","scripts":{},"author":"","license":"ISC","dependencies":{},"_id":"cnpmcore-test-block-from-upstream@0.0.0","_nodeVersion":"16.13.1","_npmVersion":"8.1.2","dist":{"integrity":"sha512-ptVWDP7Z39wOBk5EBwi2x8/SKZblEsVcdL0jjIsaI2KdLwVpRRRnezJSKpUsXr982nGf0j7nh6RcHSg4Wlu3AA==","shasum":"c73398ff6db39d138a56c04c7a90f35b70d7b78f","tarball":"https://registry.npmjs.org/cnpmcore-test-block-from-upstream/-/cnpmcore-test-block-from-upstream-0.0.0.tgz","fileCount":1,"unpackedSize":250},"_npmUser":{"name":"fengmk2","email":"fengmk2@gmail.com"},"directories":{},"maintainers":[{"name":"fengmk2","email":"fengmk2@gmail.com"}],"_hasShrinkwrap":false,"deprecated":"only test for cnpmcore"}},"time":{"created":"2021-12-11T18:09:24.624Z","0.0.0":"2021-12-11T18:09:24.768Z","modified":"2022-04-12T06:56:55.617Z"},"maintainers":[],"license":"ISC","readme":{"foo":"mock readme is object"},"readmeFilename":""}',
persist: false,
});
app.mockHttpclient('https://registry.npmjs.org/cnpmcore-test-block-from-upstream/-/cnpmcore-test-block-from-upstream-0.0.0.tgz', 'GET', {
data: await TestUtil.readFixturesFile('registry.npmjs.org/foobar/-/foobar-1.0.0.tgz'),
persist: false,
});
await packageSyncerService.createTask(name);
let task = await packageSyncerService.findExecuteTask();
await packageSyncerService.executeTask(task);
const pkg = await packageRepository.findPackage('', name);
const pkgVersion = await packageRepository.findPackageVersion(pkg!.packageId, '0.0.0');
assert(pkg?.name === name);
assert(pkgVersion);
// mock unpublish
app.mockHttpclient(`https://registry.npmjs.org/${name}`, 'GET', {
data: `{"error":"[UNAVAILABLE_FOR_LEGAL_REASONS] ${name} was blocked, reason: foo (operator: cnpmcore_admin/61f154594ce7cf8f5827edf8)"}`,
persist: false,
status: 451,
});
await packageSyncerService.createTask(name);
task = await packageSyncerService.findExecuteTask();
await packageSyncerService.executeTask(task);
const stream = await packageSyncerService.findTaskLog(task);
assert(stream);
const log = await TestUtil.readStreamToLog(stream);
// console.log(log);
assert(log.includes(`🟢 Package "${name}" was unpublished caused by 451 response`));
});
it('should stop sync by block list', async () => {
const name = 'cnpmcore-test-sync-blocklist';
mock(app.config.cnpmcore, 'syncPackageBlockList', [ name, 'foo' ]);

View File

@@ -4,15 +4,22 @@ import { app, mock } from 'egg-mock/bootstrap';
import { BinarySyncerService } from 'app/core/service/BinarySyncerService';
import { NodeBinary } from 'app/common/adapter/binary/NodeBinary';
import { SqlcipherBinary } from 'app/common/adapter/binary/SqlcipherBinary';
import { BinaryRepository } from 'app/repository/BinaryRepository';
import { Binary } from 'app/core/entity/Binary';
import { NFSClientAdapter } from 'app/infra/NFSClientAdapter';
import { TestUtil } from 'test/TestUtil';
describe('test/port/controller/BinarySyncController/showBinary.test.ts', () => {
let ctx: Context;
let binarySyncerService: BinarySyncerService;
let binaryRepository: BinaryRepository;
let nfsClientAdapter: NFSClientAdapter;
beforeEach(async () => {
ctx = await app.mockModuleContext();
binarySyncerService = await ctx.getEggObject(BinarySyncerService);
binaryRepository = await ctx.getEggObject(BinaryRepository);
nfsClientAdapter = await app.getEggObject(NFSClientAdapter);
});
afterEach(async () => {
@@ -47,6 +54,99 @@ describe('test/port/controller/BinarySyncController/showBinary.test.ts', () => {
}
});
it('should show valid root dirs', async () => {
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/',
name: 'v2.6.1/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
const res = await app.httpRequest()
.get('/-/binary/');
assert(res.status === 200);
assert(res.headers['content-type'] === 'application/json; charset=utf-8');
const items = res.body;
assert(items.length > 0);
for (const item of items) {
assert(item.type === 'dir');
assert(item.name);
assert(item.url);
assert(item.repoUrl);
assert(item.distUrl);
assert(item.description);
}
const item = items.filter((item: any) => item.name === 'nwjs/');
assert.deepStrictEqual(item, [{
name: 'nwjs/',
category: 'nwjs/',
description: 'NW.js (previously known as node-webkit) lets you call all Node.js modules directly from DOM and enables a new way of writing applications with all Web technologies.',
distUrl: 'https://dl.nwjs.io/',
repoUrl: 'https://github.com/nwjs/nw.js',
type: 'dir',
url: 'http://localhost:7001/-/binary/nwjs/',
}]);
});
it('should show valid sub dirs', async () => {
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/',
name: 'v2.6.1/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
const res = await app.httpRequest()
.get('/-/binary/node-canvas-prebuilt/');
assert(res.status === 200);
assert(res.headers['content-type'] === 'application/json; charset=utf-8');
const items = TestUtil.pickKeys(res.body, [ 'category', 'name', 'date', 'type', 'url' ]);
assert.deepStrictEqual(items, [{
category: 'node-canvas-prebuilt',
name: 'v2.6.1/',
date: '2021-12-14T13:12:31.587Z',
type: 'dir',
url: 'http://localhost:7001/-/binary/node-canvas-prebuilt/v2.6.1/',
}]);
});
it('should show valid files', async () => {
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/',
name: 'v2.6.1/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/v2.6.1/',
name: 'node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
isDir: false,
size: 10,
date: '2021-12-14T13:12:31.587Z',
}));
const res = await app.httpRequest()
.get('/-/binary/node-canvas-prebuilt/v2.6.1/');
assert(res.status === 200);
assert(res.headers['content-type'] === 'application/json; charset=utf-8');
const items = TestUtil.pickKeys(res.body, [ 'category', 'name', 'date', 'type', 'url' ]);
assert(items.length > 0);
assert.deepStrictEqual(items, [
{
category: 'node-canvas-prebuilt',
name: 'node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
date: '2021-12-14T13:12:31.587Z',
type: 'file',
url: 'http://localhost:7001/-/binary/node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
},
]);
});
it('should show node binaries', async () => {
app.mockHttpclient('https://nodejs.org/dist/index.json', 'GET', {
data: await TestUtil.readFixturesFile('nodejs.org/site/index.json'),
@@ -247,5 +347,210 @@ describe('test/port/controller/BinarySyncController/showBinary.test.ts', () => {
}
app.mockAgent().assertNoPendingInterceptors();
});
it('should merge category binaries when binaryName and category not equal', async () => {
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/',
name: 'v2.6.1/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/',
name: 'v2.7.0/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/v2.6.1/',
name: 'node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
isDir: false,
size: 10,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'canvas',
parent: '/v2.7.0/',
name: 'canvas-v2.7.0-node-v57-linux-glibc-x64.tar.gz',
isDir: false,
size: 10,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'canvas',
parent: '/',
name: 'v2.7.0/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
let res = await app.httpRequest()
.get('/-/binary/canvas');
assert.strictEqual(res.status, 200);
assert(res.body);
let stableData = TestUtil.pickKeys(res.body, [ 'category', 'name', 'date', 'type', 'url' ]);
assert.deepStrictEqual(stableData, [
{
category: 'canvas',
name: 'v2.7.0/',
date: '2021-12-14T13:12:31.587Z',
type: 'dir',
url: 'http://localhost:7001/-/binary/canvas/v2.7.0/',
},
{
category: 'node-canvas-prebuilt',
name: 'v2.6.1/',
date: '2021-12-14T13:12:31.587Z',
type: 'dir',
url: 'http://localhost:7001/-/binary/node-canvas-prebuilt/v2.6.1/',
},
]);
res = await app.httpRequest()
.get('/-/binary/node-canvas-prebuilt');
assert.strictEqual(res.status, 200);
assert(res.body);
stableData = TestUtil.pickKeys(res.body, [ 'category', 'name', 'date', 'type', 'url' ]);
assert.deepStrictEqual(stableData, [
{
category: 'node-canvas-prebuilt',
name: 'v2.6.1/',
date: '2021-12-14T13:12:31.587Z',
type: 'dir',
url: 'http://localhost:7001/-/binary/node-canvas-prebuilt/v2.6.1/',
},
{
category: 'node-canvas-prebuilt',
name: 'v2.7.0/',
date: '2021-12-14T13:12:31.587Z',
type: 'dir',
url: 'http://localhost:7001/-/binary/node-canvas-prebuilt/v2.7.0/',
},
]);
res = await app.httpRequest()
.get('/-/binary/canvas/v2.7.0/');
assert.strictEqual(res.status, 200);
assert(res.body);
stableData = TestUtil.pickKeys(res.body, [ 'category', 'name', 'date', 'type', 'url' ]);
assert.deepStrictEqual(stableData, [
{
name: 'canvas-v2.7.0-node-v57-linux-glibc-x64.tar.gz',
type: 'file',
category: 'canvas',
date: '2021-12-14T13:12:31.587Z',
url: 'http://localhost:7001/-/binary/canvas/v2.7.0/canvas-v2.7.0-node-v57-linux-glibc-x64.tar.gz',
},
]);
res = await app.httpRequest()
.get('/-/binary/canvas/v2.6.1/');
assert.strictEqual(res.status, 200);
assert(res.body);
stableData = TestUtil.pickKeys(res.body, [ 'category', 'name', 'date', 'type', 'url' ]);
assert.deepStrictEqual(stableData, [
{
name: 'node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
type: 'file',
category: 'node-canvas-prebuilt',
date: '2021-12-14T13:12:31.587Z',
url: 'http://localhost:7001/-/binary/node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
},
]);
res = await app.httpRequest()
.get('/-/binary/node-canvas-prebuilt/v2.6.1/');
assert.strictEqual(res.status, 200);
assert(res.body);
stableData = TestUtil.pickKeys(res.body, [ 'category', 'name', 'date', 'type', 'url' ]);
assert.deepStrictEqual(stableData, [
{
name: 'node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
type: 'file',
category: 'node-canvas-prebuilt',
date: '2021-12-14T13:12:31.587Z',
url: 'http://localhost:7001/-/binary/node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
},
]);
res = await app.httpRequest()
.get('/-/binary/canvas/v2.7.1/');
assert.strictEqual(res.status, 404);
res = await app.httpRequest()
.get('/-/binary/node-canvas-prebuilt/v2.7.1/');
assert.strictEqual(res.status, 404);
});
it('should get binary file success', async () => {
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/',
name: 'v2.6.1/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/',
name: 'v2.7.0/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'node-canvas-prebuilt',
parent: '/v2.6.1/',
name: 'node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz',
isDir: false,
size: 10,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'canvas',
parent: '/v2.7.0/',
name: 'canvas-v2.7.0-node-v57-linux-glibc-x64.tar.gz',
isDir: false,
size: 10,
date: '2021-12-14T13:12:31.587Z',
}));
await binaryRepository.saveBinary(Binary.create({
category: 'canvas',
parent: '/',
name: 'v2.7.0/',
isDir: true,
size: 0,
date: '2021-12-14T13:12:31.587Z',
}));
mock(nfsClientAdapter, 'url', async (storeKey: string) => {
return `https://cdn.mock.com${storeKey}`;
});
const res = await app.httpRequest()
.get('/-/binary/canvas/v2.6.1/canvas-v2.6.1-node-v57-linux-glibc-x64.tar.gz');
assert.strictEqual(res.status, 302);
assert.strictEqual(res.headers.location, 'https://cdn.mock.com/binaries/node-canvas-prebuilt/v2.6.1/node-canvas-prebuilt-v2.6.1-node-v57-linux-glibc-x64.tar.gz');
});
});
});

View File

@@ -5,7 +5,6 @@ import { app, mock } from 'egg-mock/bootstrap';
import { TestUtil } from 'test/TestUtil';
import { Task as TaskModel } from 'app/repository/model/Task';
import { PackageSyncerService } from 'app/core/service/PackageSyncerService';
import { TaskState } from 'app/common/enum/Task';
describe('test/port/controller/PackageSyncController/createSyncTask.test.ts', () => {
let publisher: any;
@@ -286,7 +285,7 @@ describe('test/port/controller/PackageSyncController/createSyncTask.test.ts', ()
assert(res.body.id === firstTaskId);
});
it('should dont create exists processing task update less than 1 min', async () => {
it('should dont create exists waiting task', async () => {
let res = await app.httpRequest()
.put('/-/package/koa/syncs')
.expect(201);
@@ -295,13 +294,12 @@ describe('test/port/controller/PackageSyncController/createSyncTask.test.ts', ()
assert(res.body.id);
const firstTaskId = res.body.id;
await TaskModel.update({ taskId: firstTaskId }, { state: TaskState.Processing });
// again dont create
res = await app.httpRequest()
.put('/-/package/koa/syncs')
.expect(201);
assert(res.body.ok === true);
assert(res.body.state === 'processing');
assert(res.body.state === 'waiting');
assert(res.body.id === firstTaskId);
// update bigger than 1 min, same task return
@@ -310,7 +308,7 @@ describe('test/port/controller/PackageSyncController/createSyncTask.test.ts', ()
.put('/-/package/koa/syncs')
.expect(201);
assert(res.body.ok === true);
assert(res.body.state === 'processing');
assert(res.body.state === 'waiting');
assert(res.body.id === firstTaskId);
});
});

View File

@@ -83,6 +83,7 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => {
const versionOne = pkg.versions['1.0.0'];
assert.equal(versionOne.dist.unpackedSize, 6497043);
assert(versionOne._cnpmcore_publish_time);
assert(versionOne.publish_time);
assert.equal(pkg._id, name);
assert(pkg._rev);
assert(versionOne._id);
@@ -214,6 +215,9 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => {
const versionOne = pkg.versions['1.0.0'];
assert(versionOne.dist.unpackedSize === 6497043);
assert(versionOne._cnpmcore_publish_time);
assert.equal(typeof versionOne._cnpmcore_publish_time, 'string');
assert(versionOne.publish_time);
assert.equal(typeof versionOne.publish_time, 'number');
assert(pkg._id === scopedName);
assert(pkg._rev);
assert(versionOne._id);
@@ -258,6 +262,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => {
const versionOne = pkg.versions['2.0.0'];
assert.equal(versionOne.dist.unpackedSize, 6497043);
assert(!versionOne._cnpmcore_publish_time);
assert(versionOne.publish_time);
assert.equal(typeof versionOne.publish_time, 'number');
assert(!pkg._rev);
assert(!pkg._id);
assert(!versionOne._id);
@@ -325,6 +331,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => {
const versionOne = pkg.versions['2.0.0'];
assert.equal(versionOne.dist.unpackedSize, 6497043);
assert(!versionOne._cnpmcore_publish_time);
assert(versionOne.publish_time);
assert.equal(typeof versionOne.publish_time, 'number');
assert(!pkg._rev);
assert(!pkg._id);
assert(!versionOne._id);
@@ -347,6 +355,8 @@ describe('test/port/controller/package/ShowPackageController.test.ts', () => {
const versionOne = pkg.versions['2.0.0'];
assert(versionOne.dist.unpackedSize === 6497043);
assert(!versionOne._cnpmcore_publish_time);
assert(versionOne.publish_time);
assert.equal(typeof versionOne.publish_time, 'number');
assert(!pkg._rev);
assert(!pkg._id);
assert(!versionOne._id);

View File

@@ -134,6 +134,66 @@ describe('test/port/controller/package/UpdatePackageController.test.ts', () => {
})
.expect(400);
assert.equal(res.body.error, '[BAD_REQUEST] header: npm-command expected "owner", but got "adduser"');
// npm@6: referer: 'xxx [REDACTED]'
// npm@>=7: 'npm-command': 'xxx'
// when npm version < 7, npm command can get from referer
res = await app.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'npm/6.3.1')
.set('referer', 'addUser add someone [REDACTED]')
.send({
_id: rev,
_rev: rev,
maintainers: [
{ name: user.name, email: user.email },
],
})
.expect(400);
assert.equal(res.body.error, '[BAD_REQUEST] header: npm-command expected "owner", but got "addUser"');
});
it('should 200 when npm command is npm owner add', async () => {
mock(app.config.cnpmcore, 'enableNpmClientAndVersionCheck', false);
// npm@6: referer: 'xxx [REDACTED]'
// npm@>=7: 'npm-command': 'xxx'
// npm version < 7
const user = await TestUtil.createUser();
let res = await app.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', 'npm/6.3.1')
.set('referer', 'owner add someone [REDACTED]')
.send({
_id: rev,
_rev: rev,
maintainers: [
{ name: user.name, email: user.email },
],
})
.expect(200);
assert.equal(res.statusCode, 200);
assert.deepEqual(res.body, { ok: true });
// npm version >= 7
res = await app.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'npm/7.3.1')
.set('referer', '')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [
{ name: user.name, email: user.email },
],
})
.expect(200);
assert.equal(res.statusCode, 200);
assert.deepEqual(res.body, { ok: true });
});
it('should 403 when npm client invalid', async () => {

View File

@@ -16,6 +16,7 @@
"app/**/*.json",
"config/**/*.ts",
"typings/**/*.ts",
"app.ts"
"app.ts",
"module.d.ts"
]
}