Au lieu d'une préface
Bonne journée! Je m'appelle Sergey et je suis chef d'équipe chez Medpoint24-Lab. Je développe dans nodejs depuis un peu plus d'un an et demi - avant ça j'avais C #, et même avant ça, tout est différent et pas très sérieux. Eh bien, c'est-à-dire que je n'ai pas autant d'expérience en tant que voiture, et parfois je dois sérieusement me casser la tête pour résoudre des problèmes qui surviennent. Une fois ce problème résolu, vous souhaitez toujours partager vos découvertes avec vos coéquipiers.
Et il y a quelques jours, ils m'ont conseillé de créer un blog ... et j'ai pensé, peut-être alors juste écrire sur Habr?
Peut-être que des exemples de situations pratiques à partir desquelles un développeur intelligent, mais pas très expérimenté rampe avec un grincement, intéresseront peut-être ceux qui sont tout aussi intelligents et inexpérimentés)) Et peut-être que quelqu'un d'autre sera utile.
Je vais essayer de vous dire sans immersion dans la théorie, mais avec des liens vers elle.
De quoi s'agit-il?
Le pilote se concentrera sur un problème intéressant que nous avons rencontré en essayant d'organiser un CI / CD pour un dépôt mono avec lerna . Je vais vous dire tout de suite que ce post:
pas sur les monorépôts . Les avantages et les inconvénients du monorepa, en tant que concept, ont longtemps été décrits dans de nombreux articles, y compris sur Habré (celui-ci est assez holivar, d'ailleurs)
. 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 - .
Mais surtout, il y a toujours de la lumière au bout du tunnel! Et pendant que vous résolvez de tels problèmes, vous parvenez souvent à acquérir une bonne couche de nouvelles connaissances.
Je suis sûr que ce n'est pas la dernière itération. Je ne laisse pas le sentiment que tout peut être fait plus facilement, plus propre - je serai ravi des opinions et des idées dans les commentaires.
Je dois encore jouer avec la commande npm shrinkwrap, par exemple ...
Un grand merci à ceux qui ont lu jusqu'au bout ... Y a-t-il quelqu'un d'autre ici?
Si ce format «histoire de la pratique» est intéressant, veuillez écrire ce qui est «tellement», ce qui ne l'est pas. Parce que les histoires ... je les ai.
Merci pour l'attention!