Em vez de um prefácio
Dia bom! Meu nome é Sergey, e sou líder da equipe da Medpoint24-Lab. Estou desenvolvendo no nodejs há pouco mais de um ano e meio - antes disso eu tinha C # e, mesmo antes disso, tudo é diferente e não muito sério. Bem, isto é, não tenho tanta experiência como um carro, e às vezes tenho que quebrar seriamente minha cabeça para resolver os problemas que surgem. Tendo resolvido isso, você sempre deseja compartilhar suas descobertas com seus colegas de equipe.
E alguns dias atrás, eles me aconselharam a começar um blog ... e eu pensei, talvez então apenas escrever no Habr?
Talvez exemplos de situações práticas nas quais um desenvolvedor inteligente, mas não muito experiente, saia rastejando, sejam do interesse de pessoas igualmente inteligentes e inexperientes)) E talvez outra pessoa seja útil.
Vou tentar te contar sem imersão na teoria, mas com links para ela.
Será sobre o quê?
O piloto se concentrará em um problema interessante que encontramos ao tentar organizar um CI / CD para um repositório mono com lerna . Direi imediatamente que este post:
não sobre monorepositórios . Os prós e contras da monorepa, como conceito, há muito tempo são descritos em muitos posts, inclusive no Habré (este é bem holivar, aliás)
. 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 - .
Mas o mais importante, sempre há luz no fim do túnel! E enquanto você está resolvendo esses problemas, muitas vezes você consegue levantar uma boa camada de novos conhecimentos.
Tenho certeza de que esta não é a última iteração. Não deixo a sensação de que tudo pode ser feito de forma mais fácil, mais limpa - Terei todo o gosto em opiniões e ideias nos comentários.
Ainda preciso brincar com o comando npm shrinkwrap, por exemplo ...
Muito obrigado a todos aqueles que leram até o fim ... Há mais alguém aqui?
Se este formato "história da prática" for interessante, escreva o que é "isso" e o que "não é". Porque as histórias ... eu as tenho.
Obrigado pela atenção!