lerna + CI =?または3本の松に絡まないようにする方法

序文の代わりに

良い一日!私の名前はセルゲイです。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 . . .. , , "" (, ).





, :





  1. PR , , .





  2. , , unit-.





  3. ( - ).





  4. @contract npm registry ( , ).





  5. , , . (, , - docker, . , )





  6. , , . node_modules - , .





!

CI/CD .





:





lerna : lerna version lerna publish ( ). :





lerna publish --conventional-commits --yes
#  :  publish     version.
#    ,      .
      
      



conventional commits.

lerna publish



, , CI- . Conventional Commits. commit-, lerna , semver (, ). , ( )! .









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コマンドで遊ぶ必要があります、例えば...









最後まで読んでくださった方々に感謝します...他に誰かいますか?





この「実践からの物語」という形式がおもしろい場合は、「そう」と「そうでない」と書いてください。物語があるので...私はそれらを持っています。









ご清聴ありがとうございました!












All Articles