Alih-alih kata pengantar
Selamat siang! Nama saya Sergey, dan saya adalah pemimpin tim di Medpoint24-Lab. Saya telah mengembangkan di nodejs selama lebih dari satu setengah tahun - sebelum itu saya memiliki C #, dan bahkan sebelum itu, semuanya berbeda dan tidak terlalu serius. Artinya, saya tidak memiliki banyak pengalaman sebagai mobil, dan terkadang saya harus benar-benar mematahkan kepala saya saat memecahkan masalah yang muncul. Setelah menyelesaikan ini, Anda selalu ingin membagikan temuan Anda dengan rekan satu tim Anda.
Dan beberapa hari yang lalu, mereka menyarankan saya untuk memulai sebuah blog ... dan saya berpikir, mungkin kemudian menulis di Habr?
Mungkin contoh situasi praktis di mana pengembang yang cerdas, tetapi tidak terlalu berpengalaman merangkak keluar dengan berderit, akan menarik bagi mereka yang sama-sama cerdas dan tidak berpengalaman)) Dan mungkin orang lain akan berguna.
Saya akan mencoba memberi tahu Anda tanpa tenggelam dalam teori, tetapi dengan tautan ke sana.
Tentang apa itu?
Percontohan akan fokus pada masalah menarik yang kami temui saat mencoba mengatur CI / CD untuk repositori mono dengan lerna . Saya akan segera memberi tahu Anda bahwa posting ini:
bukan tentang monorepositories . Pro dan kontra tentang monorepa, sebagai sebuah konsep, telah lama dideskripsikan di banyak postingan, termasuk di Habré (ngomong-ngomong yang ini cukup holivar)
. 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 - .
Tapi yang terpenting, selalu ada cahaya di ujung terowongan! Dan saat Anda memecahkan masalah seperti itu, Anda sering kali berhasil meningkatkan lapisan pengetahuan baru yang baik.
Saya yakin ini bukan iterasi terakhir. Saya merasa bahwa semuanya dapat dibuat lebih sederhana, lebih bersih - saya akan dengan senang hati memiliki pendapat dan ide di komentar.
Saya masih perlu bermain-main dengan perintah npm shrinkwrap, misalnya ...
Terima kasih banyak kepada mereka yang telah membaca sampai akhir ... Apakah ada orang lain di sini?
Jika format "cerita dari latihan" ini menarik, harap tulis apa yang "jadi", yang "tidak begitu". Karena ceritanya ... Aku memilikinya.
Terima kasih atas perhatiannya!