序文の代わりに
良い一日!私の名前はセルゲイです。Medpoint24-Labのチームリーダーです。私はnodejsで1年半余り開発してきました-その前に私はC#を持っていました、そしてその前でさえ、すべてが異なっていてそれほど深刻ではありません。ええと、つまり、私は車ほどの経験がなく、問題を解決するときに真剣に頭を悩ませなければならないこともあります。これを解決したら、常に調査結果をチームメートと共有したいと思うでしょう。
そして数日前、彼らは私にブログを始めるようにアドバイスしました...そして私は思った、多分それからただHabrに書く?
おそらく、インテリジェントであるがあまり経験のない開発者がきしむ音を立てて這う実際的な状況の例は、同様にインテリジェントで経験の浅い人にとっても興味深いでしょう))そしておそらく他の誰かが役に立つでしょう。
私は理論に没頭することなく、しかしそれにリンクしてあなたに話そうとします。
それはどうなるのでしょうか?
パイロットは、lernaを使用してモノリポジトリのCI / CDを整理しようとしたときに発生した興味深い問題に焦点を当てます。この投稿はすぐにお知らせします。
モノリポジトリについてではありません。モノレパの長所と短所は、概念として、ハブレを含む多くの投稿で長い間説明されてきました(ちなみに、これはかなりホリバーです)
. Nx, rush, yarn workspaces. , lerna .
. npm, yarn pnpm c npm . npm ()...
nestjs. !
, .
?
:
, npm-, , .
packages
+-- @contract
| +-- src
| +-- package.json
| ...
|
+-- application
| +-- src
| +-- package.json
| ...
|
+-- package.json
+-- lerna.json
...
?
, , "" .
, axios.post(....) (any), .
import { Client } from '@contract/some-service';
const client = new Client(options);
const filters: StronglyTypedObject = ...
const data = await client.getSomeData(filters)
/*
* .
* getSomeData() ,
* , axios.
*/
, , , . .
, :
const query = new SomeQuery({ ... });
const data = await client.call(query);
/*
* , -
* , . rabbitMQ.
*/
http-, , RabbitMQ, redis. .
, , ? , . - , lerna bootstrap.
lerna bootstrap --hoist
--hoist
- . , , , node_modules . + , .
lerna bootstrap
. , application/package.json
"dependencies": {
"@contract/core": "^1.0.0"
}
, npm-, node_modules packages. , , .
CI/CD. . , 1000 - .
, issues github, Stackoverflow . . .. , , "" (, ).
, :
PR , , .
, , unit-.
( - ).
@contract npm registry ( , ).
, , . (, , - docker, . , )
, , . node_modules - , .
!
CI/CD .
:
lerna : lerna version lerna publish ( ). :
lerna publish --conventional-commits --yes
# : publish version.
# , .
conventional commits.
4 .lerna publish
, - (, , ), lerna version
npm publish
. , npm publish --registry
, , . lerna publish
, lerna.json (. 7):
{
"version": "1.2.2",
"npmClient": "npm",
"command": {
"publish": {
"message": "chore(release): publish",
"registry": ....
}
},
"packages": [
"packages/@contract",
"packages/application"
]
}
.npmrc ( npm) .
, CI- ( CI/CD):
# Pull checkout
lerna bootsrap --hoist
lerna run build # npm run build .
lerna publish --conventional-commits --yes
cp packages/application/build /tmp/place/for/artifact
...
node_modules.
№1. node_modules /tmp/place/for/artifact. :
( jest, typescript ). 2 , 22, node_modules .
, , , . . lerna . - - , .
№2. . package.json packages/application. , ! package.json , npm i
- ! :
, , CI npm install npm ci
. npm install , package.json, package-lock.json shrinkwrap.json ( ). lock- .
:
lock- . dependencies "~" "^" - , . . ( ) .
lock- package.json. , package.json ( ), package-lock.json , npm ci :
, , - npm install.
, : lerna bootstrap --hoist
package-lock.json . , .
, package.json packages/application lock- - . , ! application lock- . :
№3. "". , , lock- . :
lerna bootstrap
lock- . ! npm ci
, . ?
package-lock.json .. @contract/core! , , ...
№4. , npm install . :
lerna exec -- npm i
, lock- ! npm ci
! !
...
, @contract- . ! npm i
npm registry. - . , , , (, build publish). , .. , . , , .
, publish
. - , , - , , .
№4. , , ...
:
lerna exec -- npm i # lock- .
lerna link # .
lerna run build
lerna publish --conventional-commits ...
cp packages/application/build /path/to/artifact
# production
# - sourceMaps .
cp packages/application/package*.json /path/to/artifact
(cd /path/to/artifact && npm ci --production)
! - .. jest - 3- 4- ...
... , . . , , , lerna bootstrap --hoist
.
- . , . - (, , ...) - , . . , . , .
, lerna bootstrap --hoist
lerna exec -- npm i && lerna link
- ? - lerna bootstrap
, --hoist
. hoist... . - .
, , :
packages
+-- @contract
| +-- node_modules
| +-- class-transformer
| +-- src
| +-- package.json
| ...
|
+-- application
| +-- node_modules
| +-- class-transformer
| +-- @contract ->
| +-- src
| +-- package.json
| ...
|
+-- package.json
+-- lerna.json
...
. application contract class-transformer. -, , , , , node_modules .
class-transformer - , .
,
class-transformer - . nestjs (ValidationPipe). :
import { Type } from 'class-transformer';
import { IsInt, IsPositive } from 'class-validator';
export class Query {
@IsInt()
@IsPositive()
@Type(() => Number)
id: number;
}
GET (?id=100500) , nest , . IsInt() ( , IsPositive() 100%).
: . @Type() - . , return Number(id)
@Transform() .
class-validator class-transformer.
- . ( - 3 )
:
. @Type(), class-transformer : " ". , nest plainToClass , Query. .
" " . , plainToClass , @Type() !
. , . ,
import
, .
- - , - .
Query , , , @contract class-transformer.
, class-validator . , ( global?). .
. , - , ( node_modules, , node_modules... ) --hoist. registry, ( ...) - , .
, - ...
?
( ), :
( ), :
lerna bootstrap --hoist # npm i ! lock-file!
lerna run build
jest
# ...
CI ,
lerna publish
, :
# Makefile
# .
BUILD:=build.$(shell jq .version packages/application/package.json | sed 's/"//g')
artifact:
# build/prod sourceMap' ,
(cd packages/application && npm run build:prod -- --outDir ../../deploy/$(BUILD))
cp -r packages/application/package*.json deploy/$(BUILD)
# - package-lock.json
(cd deploy/$(BUILD) && npm ci --production)
# - , package*.json
# tar.gz .
rm deploy/$(BUILD)/package*.jsosdf
make, . , Dockerfile, .
lock-, ?
lerna exec -- npm i
lerna clean --yes
# . , .
# lock-
lerna bootstrap -- hoist
, , . application ( @contract) , lock-:
# Makefile
add:
# ( ) package.json
lerna add --scope=$(scope) $(package) --no-bootstrap
# package-lock.json
lerna exec --scope=$(scope) -- npm i
# node_modules units/application !
lerna clean --yes
# package-lock.json
lerna bootstrap --hoist
# ( scope package.json):
$ make add scope=app_name package left-pad
? lerna add package-lock.json, . . -. ...
:
- .
CI/CD - .
しかし、最も重要なことは、トンネルの終わりには常に光があります!そして、あなたがそのような問題を解決している間、あなたはしばしば新しい知識の良い層を上げることに成功します。
これが最後の繰り返しではないと確信しています。私はすべてがより簡単に、よりきれいにできるという気持ちを残しません-私はコメントの意見やアイデアに喜んでいます。
私はまだnpmshrinkwrapコマンドで遊ぶ必要があります、例えば...
最後まで読んでくださった方々に感謝します...他に誰かいますか?
この「実践からの物語」という形式がおもしろい場合は、「そう」と「そうでない」と書いてください。物語があるので...私はそれらを持っています。
ご清聴ありがとうございました!