├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc.js ├── .git-blame-ignore-revs ├── .github ├── CODEOWNERS └── dependabot.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── .release-please-manifest.json ├── commitlint.config.js ├── contributing.md ├── core ├── cli │ ├── .editorconfig │ ├── CHANGELOG.md │ ├── bin │ │ ├── run │ │ └── run.cmd │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── config.ts │ │ ├── config │ │ │ ├── hash.ts │ │ │ └── validate-plugins.ts │ │ ├── fetch.ts │ │ ├── help.ts │ │ ├── index.ts │ │ ├── init.ts │ │ ├── install.ts │ │ ├── messages.ts │ │ ├── plugin.ts │ │ ├── plugin │ │ │ ├── entry-point.ts │ │ │ ├── is-descendent.ts │ │ │ ├── merge-commands.ts │ │ │ ├── merge-hooks.ts │ │ │ ├── merge-inits.ts │ │ │ ├── merge-plugin-options.ts │ │ │ ├── merge-task-options.ts │ │ │ ├── merge-tasks.ts │ │ │ ├── options.ts │ │ │ ├── reduce-installations.ts │ │ │ ├── require-resolve.ts │ │ │ └── resolve-root.ts │ │ ├── rc-file.ts │ │ └── tasks.ts │ ├── test │ │ ├── __snapshots__ │ │ │ └── config.test.ts.snap │ │ ├── config.test.ts │ │ ├── files │ │ │ ├── conflict-resolution │ │ │ │ └── .toolkitrc.yml │ │ │ ├── conflicted │ │ │ │ └── .toolkitrc.yml │ │ │ ├── cousins │ │ │ │ └── .toolkitrc.yml │ │ │ ├── duplicate │ │ │ │ └── .toolkitrc.yml │ │ │ ├── multiple-hook-options │ │ │ │ └── .toolkitrc.yml │ │ │ ├── successful │ │ │ │ └── .toolkitrc.yml │ │ │ └── with-index │ │ │ │ ├── .toolkitrc.yml │ │ │ │ └── index.js │ │ ├── helpers.ts │ │ ├── index.test.ts │ │ ├── options.test.ts │ │ └── resolve-root.test.ts │ ├── tsconfig.json │ └── tsconfig.test.json ├── create │ ├── CHANGELOG.md │ ├── bin │ │ └── create-tool-kit │ ├── files │ │ └── oidc-cloudformation.yml │ ├── package.json │ ├── src │ │ ├── augmentations.d.ts │ │ ├── bizOps.ts │ │ ├── declarations.d.ts │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── makefile.ts │ │ └── prompts │ │ │ ├── confirmation.ts │ │ │ ├── conflicts.ts │ │ │ ├── main.ts │ │ │ ├── oidc.ts │ │ │ ├── options.ts │ │ │ ├── scheduledPipeline.ts │ │ │ └── systemCode.ts │ └── tsconfig.json └── sandbox │ ├── .gitignore │ ├── jest.config.js │ └── readme.md ├── docs ├── concepts.md ├── developing-tool-kit.md ├── extending-tool-kit.md ├── migration-guides │ └── v4.md ├── resolving-plugin-conflicts.md └── tool-kit-principles.md ├── etc ├── concepts.sketch ├── installing-hook.svg ├── logo.sketch ├── logo.svg ├── plugin.svg └── running-command.svg ├── jest.config.base.js ├── jest.config.js ├── lib ├── base │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── base.ts │ │ ├── hook.ts │ │ ├── index.ts │ │ ├── init.ts │ │ ├── symbols.ts │ │ ├── task.ts │ │ └── type-utils.ts │ └── tsconfig.json ├── config │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── conflict │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── doppler │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── schema.ts │ ├── test │ │ └── index.test.ts │ └── tsconfig.json ├── error │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── logger │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── format.ts │ │ ├── helpers.ts │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── styles.ts │ │ └── transports.ts │ └── tsconfig.json ├── plugin │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── root-schema.ts │ └── tsconfig.json ├── state │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── validated │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ └── index.ts │ ├── test │ │ └── index.test.ts │ └── tsconfig.json └── wait-for-ok │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ └── index.ts │ └── tsconfig.json ├── orb ├── .circleci │ └── README.md ├── .gitignore ├── .yamllint ├── CHANGELOG.md ├── README.md ├── src │ ├── @orb.yml │ ├── README.md │ ├── commands │ │ ├── README.md │ │ ├── attach-workspace.yml │ │ ├── persist-workspace.yml │ │ └── serverless-assume-role.yml │ ├── executors │ │ └── default.yml │ ├── jobs │ │ ├── README.md │ │ ├── build.yml │ │ ├── deploy-production.yml │ │ ├── deploy-review.yml │ │ ├── deploy-staging.yml │ │ ├── e2e-test-review.yml │ │ ├── e2e-test-staging.yml │ │ ├── heroku-promote.yml │ │ ├── heroku-provision.yml │ │ ├── heroku-staging.yml │ │ ├── publish-tag.yml │ │ ├── publish.yml │ │ ├── setup.yml │ │ └── test.yml │ └── tests │ │ └── README.md └── version.txt ├── package-lock.json ├── package.json ├── plugins ├── aws │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── assume-role.ts │ └── tsconfig.json ├── babel │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── babel.ts │ ├── test │ │ ├── files │ │ │ ├── babel.config.json │ │ │ └── index.js │ │ └── tasks │ │ │ └── babel.test.ts │ └── tsconfig.json ├── backend-heroku-app │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── index.js │ ├── package.json │ └── readme.md ├── backend-serverless-app │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── index.js │ ├── package.json │ └── readme.md ├── circleci-deploy │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── index.js │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── test │ │ ├── __snapshots__ │ │ │ └── circleci-deploy.test.ts.snap │ │ └── circleci-deploy.test.ts │ └── tsconfig.json ├── circleci-npm │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── index.js │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── test │ │ ├── __snapshots__ │ │ │ └── circleci-npm.test.ts.snap │ │ └── circleci-npm.test.ts │ └── tsconfig.json ├── circleci │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── circleci-config.ts │ │ ├── init-env-vars.ts │ │ └── schemas │ │ │ ├── hook.ts │ │ │ └── plugin.ts │ ├── test │ │ ├── __snapshots__ │ │ │ └── circleci-config.test.ts.snap │ │ ├── circleci-config.test.ts │ │ └── files │ │ │ ├── .toolkitrc.yml │ │ │ ├── managed │ │ │ ├── without-job │ │ │ │ └── .circleci │ │ │ │ │ └── config.yml │ │ │ └── without-workflow-job │ │ │ │ └── .circleci │ │ │ │ └── config.yml │ │ │ └── unmanaged │ │ │ ├── with-workflow-job │ │ │ └── .circleci │ │ │ │ └── config.yml │ │ │ ├── without-tool-kit │ │ │ └── .circleci │ │ │ │ └── config.yml │ │ │ └── without-workflow-job │ │ │ └── .circleci │ │ │ └── config.yml │ └── tsconfig.json ├── cloudsmith │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ └── schema.ts │ └── tsconfig.json ├── commitlint │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── commitlint.ts │ └── tsconfig.json ├── component │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── index.js │ ├── package.json │ ├── readme.md │ └── tsconfig.json ├── containerised-app-with-assets │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ └── readme.md ├── containerised-app │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ └── schema.js ├── cypress │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── index.ts │ │ └── tasks │ │ │ └── cypress.ts │ └── tsconfig.json ├── docker │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── image-info.ts │ │ ├── schema.ts │ │ └── tasks │ │ │ ├── auth-cloudsmith.ts │ │ │ ├── build.ts │ │ │ └── push.ts │ └── tsconfig.json ├── eslint │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── eslint.ts │ ├── test │ │ └── tasks │ │ │ └── eslint.test.ts │ └── tsconfig.json ├── frontend-app │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── index.js │ ├── package.json │ └── readme.md ├── hako │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── hako.ts │ │ └── tasks │ │ │ ├── delete.ts │ │ │ └── deploy.ts │ └── tsconfig.json ├── heroku │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── buildHerokuReviewApp.ts │ │ ├── checkIfStagingUpdated.ts │ │ ├── createBuild.ts │ │ ├── declarations.d.ts │ │ ├── getHerokuReviewApp.ts │ │ ├── getHerokuStagingApp.ts │ │ ├── getPipelineCouplings.ts │ │ ├── githubApi.ts │ │ ├── gtg.ts │ │ ├── herokuClient.ts │ │ ├── promoteStagingToProduction.ts │ │ ├── repeatedCheckForBuildSuccess.ts │ │ ├── repeatedCheckForSuccessStatus.ts │ │ ├── scaleDyno.ts │ │ ├── schema.ts │ │ ├── setStagingSlug.ts │ │ └── tasks │ │ │ ├── production.ts │ │ │ ├── review.ts │ │ │ ├── staging.ts │ │ │ └── teardown.ts │ ├── test │ │ ├── createBuild.test.ts │ │ ├── getHerokuReviewApp.test.ts │ │ ├── getHerokuStagingApp.test.ts │ │ ├── getPipelineCouplings.test.ts │ │ ├── gtg.test.ts │ │ ├── promoteStagingToProduction.test.ts │ │ ├── repeatedCheckForBuildSuccess.test.ts │ │ ├── repeatedCheckForSuccessStatus.test.ts │ │ ├── scaleDyno.test.ts │ │ ├── setStagingSlug.test.ts │ │ └── tasks │ │ │ ├── production.test.ts │ │ │ ├── review.test.ts │ │ │ ├── staging.test.ts │ │ │ └── teardown.test.ts │ └── tsconfig.json ├── husky-npm │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── jest │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── jest.ts │ ├── tests │ │ └── jest-plugin.test.ts │ └── tsconfig.json ├── lint-staged-npm │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── index.js │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ └── schema.ts │ └── tsconfig.json ├── lint-staged │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── declarations.d.ts │ │ └── tasks │ │ │ └── lint-staged.ts │ └── tsconfig.json ├── mocha │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── mocha.ts │ ├── test │ │ ├── files │ │ │ ├── fail │ │ │ │ └── test.js │ │ │ └── pass │ │ │ │ └── test.js │ │ └── tasks │ │ │ └── mocha.test.ts │ └── tsconfig.json ├── monorepo │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── load-workspace-configs.ts │ │ └── tasks │ │ │ └── workspace-command.ts │ ├── test │ │ ├── __snapshots__ │ │ │ └── load-workspace-configs.test.ts.snap │ │ ├── files │ │ │ ├── invalid │ │ │ │ ├── a │ │ │ │ │ ├── .toolkitrc.yml │ │ │ │ │ └── package.json │ │ │ │ ├── b │ │ │ │ │ ├── .toolkitrc.yml │ │ │ │ │ └── package.json │ │ │ │ ├── c │ │ │ │ │ ├── .toolkitrc.yml │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ └── successful │ │ │ │ ├── a │ │ │ │ ├── .toolkitrc.yml │ │ │ │ └── package.json │ │ │ │ ├── b │ │ │ │ ├── .toolkitrc.yml │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ └── load-workspace-configs.test.ts │ └── tsconfig.json ├── n-test │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── README.md │ ├── __mocks__ │ │ └── puppeteer.ts │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── tasks │ │ │ └── n-test.ts │ │ └── types │ │ │ └── n-test.d.ts │ ├── test │ │ ├── files │ │ │ └── smoke.js │ │ └── tasks │ │ │ └── n-test.test.ts │ ├── tsconfig.json │ └── tsconfig.test.json ├── next-router │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── schema.ts │ │ ├── tasks │ │ │ └── next-router.ts │ │ └── types │ │ │ └── ft-next-router.d.ts │ └── tsconfig.json ├── node-test │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── node-test.ts │ └── tsconfig.json ├── node │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── node.ts │ └── tsconfig.json ├── nodemon │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── nodemon.ts │ └── tsconfig.json ├── npm │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── tasks │ │ │ ├── prune.ts │ │ │ └── publish.ts │ │ └── types │ │ │ └── declarations.d.ts │ ├── test │ │ └── npm-publish.test.ts │ └── tsconfig.json ├── package-json-hook │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── package-json-helper.ts │ │ └── schema.ts │ ├── test │ │ ├── files │ │ │ ├── existing-hook │ │ │ │ └── package.json │ │ │ ├── multiple-hooks │ │ │ │ └── package.json │ │ │ ├── with-hook │ │ │ │ └── package.json │ │ │ └── without-hook │ │ │ │ └── package.json │ │ └── index.test.ts │ └── tsconfig.json ├── prettier │ ├── .gitignore │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── prettier.ts │ ├── test │ │ ├── .prettierrc-test.json │ │ ├── files │ │ │ └── fixtures │ │ │ │ ├── formatted-config-file.ts │ │ │ │ ├── formatted-default.ts │ │ │ │ └── unformatted.ts │ │ └── tasks │ │ │ └── prettier.test.ts │ └── tsconfig.json ├── serverless │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── schema.ts │ │ └── tasks │ │ │ ├── deploy.ts │ │ │ ├── provision.ts │ │ │ ├── run.ts │ │ │ └── teardown.ts │ └── tsconfig.json ├── typescript │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ │ └── tasks │ │ │ └── typescript.ts │ ├── test │ │ └── tasks │ │ │ └── typescript.test.ts │ └── tsconfig.json ├── upload-assets-to-s3 │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ └── tasks │ │ │ └── upload-assets-to-s3.ts │ ├── test │ │ ├── files │ │ │ ├── nested │ │ │ │ └── test.js.gz │ │ │ ├── test.css │ │ │ ├── test.js │ │ │ ├── test.json │ │ │ └── test.ts │ │ └── tasks │ │ │ └── upload-assets-to-s3.test.ts │ └── tsconfig.json └── webpack │ ├── .gitignore │ ├── .toolkitrc.yml │ ├── CHANGELOG.md │ ├── jest.config.js │ ├── package.json │ ├── readme.md │ ├── src │ └── tasks │ │ └── webpack.ts │ ├── test │ └── tasks │ │ └── webpack.test.ts │ └── tsconfig.json ├── pull_request_template.md ├── readme.md ├── release-please-config.json ├── scripts ├── circleci-publish.sh ├── clean-up-packages.sh ├── create-plugin.js ├── generate-and-commit-docs.sh └── generate-docs.js ├── tsconfig.json ├── tsconfig.settings.json └── types └── financial-times__package-json ├── index.d.ts └── package.json /.eslintignore: -------------------------------------------------------------------------------- 1 | */**/lib/ 2 | **/test/files 3 | core/sandbox 4 | scripts 5 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | d3920defa32a6c86139a20c2574a5225a0cf6b24 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/about-codeowners/ for more information about this file. 2 | 3 | * @financial-times/platforms 4 | 5 | 6 | # Allow Dependency Auto-Merger to approve dependency bump PRs 7 | **/package*.json @ft-dependency-auto-merger @financial-times/platforms -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'daily' 7 | open-pull-requests-limit: 20 8 | commit-message: 9 | prefix: 'fix:' 10 | prefix-development: 'chore:' 11 | groups: 12 | aws-sdk: 13 | patterns: 14 | - '@aws-sdk/*' 15 | update-types: 16 | - 'minor' 17 | - 'patch' 18 | development-dependencies: 19 | dependency-type: 'development' 20 | exclude-patterns: 21 | - '@types/*' 22 | update-types: 23 | - 'minor' 24 | - 'patch' 25 | reliability-kit: 26 | patterns: 27 | - '@dotcom-reliability-kit/*' 28 | update-types: 29 | - 'minor' 30 | - 'patch' 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | /.nyc_output 4 | /dist 5 | */**/lib 6 | /tmp 7 | /yarn.lock 8 | node_modules 9 | *.tsbuildinfo 10 | **/.toolkitstate 11 | coverage 12 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | README.md 2 | **/test/files 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 110, 3 | "semi": false, 4 | "singleQuote": true, 5 | "bracketSpacing": true, 6 | "arrowParens": "always", 7 | "jsxBracketSameLine": true, 8 | "trailingComma": "none" 9 | } 10 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'type-enum': [ 5 | 2, 6 | 'always', 7 | ['backstage', 'build', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test'] 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/cli/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /core/cli/bin/run.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | node "%~dp0\run" %* 4 | -------------------------------------------------------------------------------- /core/cli/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | ...base.config, 6 | transform: { 7 | '^.+\\.tsx?$': [ 8 | 'ts-jest', 9 | { 10 | ...base.tsJestConfig, 11 | tsconfig: path.resolve(__dirname, './tsconfig.test.json') 12 | } 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/cli/readme.md: -------------------------------------------------------------------------------- 1 | # dotcom-tool-kit 2 | 3 | _(This README is for the core `dotcom-tool-kit` package that must be installed to use Tool Kit. You can find the documentation for the Tool Kit project itself at https://github.com/Financial-Times/dotcom-tool-kit/blob/main/readme.md.)_ 4 | 5 | The primary Tool Kit binary that will be invoked to handle all your hooks and tasks. 6 | 7 | ## Options 8 | 9 | There are some global options available that will affect all plugins. All are optional but you can override them with the `dotcom-tool-kit` key. 10 | 11 | | Key | Description | Default value | 12 | |-|-|-| 13 | | `allowNativeFetch` | use Node's native fetch if supported | `false` | 14 | -------------------------------------------------------------------------------- /core/cli/src/config/validate-plugins.ts: -------------------------------------------------------------------------------- 1 | import type { RawConfig, ValidPluginsConfig } from '@dotcom-tool-kit/config' 2 | import { Validated, reduceValidated } from '@dotcom-tool-kit/validated' 3 | 4 | export function validatePlugins(config: RawConfig): Validated { 5 | const validatedPlugins = reduceValidated( 6 | Object.entries(config.plugins).map(([id, plugin]) => plugin.map((p) => [id, p] as const)) 7 | ) 8 | return validatedPlugins.map((plugins) => ({ ...config, plugins: Object.fromEntries(plugins) })) 9 | } 10 | -------------------------------------------------------------------------------- /core/cli/src/fetch.ts: -------------------------------------------------------------------------------- 1 | import type { RootOptions } from '@dotcom-tool-kit/plugin/src/root-schema' 2 | 3 | // function that plugins can check if they need to implement their own logic to 4 | // disable Node 18's native fetch 5 | export const shouldDisableNativeFetch = (options: RootOptions): boolean => { 6 | // disable Node 18's native fetch if the Node runtime supports it (older 7 | // runtimes don't support the flag, implying they also don't use native 8 | // fetch) and the user hasn't opted out of the behaviour 9 | return !options.allowNativeFetch && process.allowedNodeEnvironmentFlags.has('--no-experimental-fetch') 10 | } 11 | -------------------------------------------------------------------------------- /core/cli/src/index.ts: -------------------------------------------------------------------------------- 1 | import { loadConfig } from './config' 2 | import type { Logger } from 'winston' 3 | import util from 'util' 4 | import { formatPluginTree } from './messages' 5 | 6 | export { runTasks } from './tasks' 7 | export { shouldDisableNativeFetch } from './fetch' 8 | 9 | export async function listPlugins(logger: Logger): Promise { 10 | const config = await loadConfig(logger, { validate: false, root: process.cwd() }) 11 | 12 | const rootPlugin = config.plugins['app root'] 13 | if (rootPlugin?.valid) { 14 | logger.info(formatPluginTree(rootPlugin.value).join('\n')) 15 | } 16 | } 17 | 18 | export async function printConfig(logger: Logger): Promise { 19 | const config = await loadConfig(logger, { validate: false, root: process.cwd() }) 20 | 21 | logger.info(util.inspect(config, { depth: null, colors: true })) 22 | } 23 | -------------------------------------------------------------------------------- /core/cli/src/init.ts: -------------------------------------------------------------------------------- 1 | import type { ValidConfig } from '@dotcom-tool-kit/config' 2 | import { Init, InitClass } from '@dotcom-tool-kit/base' 3 | import { Validated, reduceValidated } from '@dotcom-tool-kit/validated' 4 | import type { Logger } from 'winston' 5 | import { importEntryPoint } from './plugin/entry-point' 6 | 7 | const loadInitEntrypoints = async (logger: Logger, config: ValidConfig): Promise> => { 8 | const initModuleResults = reduceValidated( 9 | await Promise.all(config.inits.map((entryPoint) => importEntryPoint(Init as InitClass, entryPoint))) 10 | ) 11 | 12 | return initModuleResults.map((initModules) => 13 | initModules.map((initModule) => new initModule.baseClass(logger)) 14 | ) 15 | } 16 | 17 | export async function runInit(logger: Logger, config: ValidConfig): Promise { 18 | const initResults = await loadInitEntrypoints(logger, config) 19 | const inits = initResults.unwrap('plugin initialisation classes were invalid!') 20 | 21 | await Promise.all(inits.map(async (init) => init.init({ 22 | cwd: config.root 23 | }))) 24 | } 25 | -------------------------------------------------------------------------------- /core/cli/src/plugin/is-descendent.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from '@dotcom-tool-kit/plugin' 2 | 3 | export function isDescendent(possibleAncestor: Plugin, possibleDescendent: Plugin): boolean { 4 | if (!possibleDescendent.parent) { 5 | return false 6 | } else if (possibleDescendent.parent === possibleAncestor) { 7 | return true 8 | } else { 9 | return isDescendent(possibleAncestor, possibleDescendent.parent) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/cli/src/plugin/merge-hooks.ts: -------------------------------------------------------------------------------- 1 | import type { EntryPoint, Plugin } from '@dotcom-tool-kit/plugin' 2 | import type { ValidPluginsConfig } from '@dotcom-tool-kit/config' 3 | import { isConflict } from '@dotcom-tool-kit/conflict' 4 | 5 | export const mergeHooks = (config: ValidPluginsConfig, plugin: Plugin) => { 6 | if (plugin.rcFile) { 7 | // add hooks to the registry, handling any conflicts 8 | // TODO refactor with command conflict handler 9 | for (const [hookName, hookSpec] of Object.entries(plugin.rcFile.installs || {})) { 10 | const existingHookId = config.hooks[hookName] 11 | const entryPoint: EntryPoint = { 12 | plugin, 13 | modulePath: hookSpec.entryPoint 14 | } 15 | 16 | if (existingHookId) { 17 | const conflicting = isConflict(existingHookId) ? existingHookId.conflicting : [existingHookId] 18 | 19 | config.hooks[hookName] = { 20 | plugin, 21 | conflicting: conflicting.concat(entryPoint) 22 | } 23 | } else { 24 | config.hooks[hookName] = entryPoint 25 | hookSpec.managesFiles?.forEach((file) => config.hookManagedFiles.add(file)) 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/cli/src/plugin/merge-inits.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from '@dotcom-tool-kit/plugin' 2 | import type { ValidPluginsConfig } from '@dotcom-tool-kit/config' 3 | 4 | export const mergeInits = (config: ValidPluginsConfig, plugin: Plugin) => { 5 | if (plugin.rcFile) { 6 | // no conflict resolution needed; we'll just run them all ig 7 | config.inits.push( 8 | ...plugin.rcFile.init.map((init) => ({ 9 | plugin, 10 | modulePath: init 11 | })) 12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/cli/src/plugin/merge-tasks.ts: -------------------------------------------------------------------------------- 1 | import type { EntryPoint, Plugin } from '@dotcom-tool-kit/plugin' 2 | import type { ValidPluginsConfig } from '@dotcom-tool-kit/config' 3 | import { isConflict } from '@dotcom-tool-kit/conflict' 4 | 5 | // add plugin tasks to our task registry, handling any conflicts 6 | export const mergeTasks = (config: ValidPluginsConfig, plugin: Plugin) => { 7 | if (plugin.rcFile) { 8 | for (const [taskName, modulePath] of Object.entries(plugin.rcFile.tasks || {})) { 9 | const existingTaskId = config.tasks[taskName] 10 | const entryPoint: EntryPoint = { 11 | plugin, 12 | modulePath 13 | } 14 | 15 | if (existingTaskId) { 16 | const conflicting = isConflict(existingTaskId) ? existingTaskId.conflicting : [existingTaskId] 17 | 18 | config.tasks[taskName] = { 19 | plugin, 20 | conflicting: conflicting.concat(entryPoint) 21 | } 22 | } else { 23 | config.tasks[taskName] = entryPoint 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/cli/src/plugin/require-resolve.ts: -------------------------------------------------------------------------------- 1 | export const resolve = require.resolve 2 | -------------------------------------------------------------------------------- /core/cli/src/plugin/resolve-root.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { resolve } from "./require-resolve" 3 | import { ToolKitError } from "@dotcom-tool-kit/error" 4 | import { styles } from "@dotcom-tool-kit/logger" 5 | 6 | export function resolveRoot(id: string, root: string): string { 7 | const isRelative = id.startsWith('./') 8 | // resolve the .toolkitrc.yml of a plugin as many plugins don't have valid 9 | // entrypoints now that we're intending their tasks/hooks to be loaded via 10 | // entrypoints defined in config 11 | const pluginPath = path.join(id, '.toolkitrc.yml') 12 | 13 | try { 14 | const resolvedPath = resolve(isRelative ? './' + pluginPath : pluginPath, { paths: [root] }) 15 | 16 | return path.dirname(resolvedPath) 17 | } catch(error) { 18 | if(error instanceof Error && 'code' in error && error.code === 'MODULE_NOT_FOUND') { 19 | const error = new ToolKitError(`Couldn't resolve plugin ${styles.plugin(id)} from directory ${styles.filepath(root)}`) 20 | error.details = `If this is a built-in Tool Kit plugin, check it's installed as a dependency. If it's a custom plugin, check it has a ${styles.filepath('.toolkitrc.yml')}, and that the path is the correct relative path to the plugin from the ${styles.filepath('.toolkitrc.yml')} it's being included from.` 21 | throw error 22 | } 23 | 24 | throw error 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/cli/test/files/conflict-resolution/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/webpack' 3 | - '@dotcom-tool-kit/babel' 4 | - '@dotcom-tool-kit/circleci' # for build:ci hook 5 | - '@dotcom-tool-kit/heroku' # for build:remote hook and build:local via npm plugin 6 | 7 | options: 8 | plugins: 9 | '@dotcom-tool-kit/heroku': 10 | pipeline: tool-kit-test 11 | '@dotcom-tool-kit/doppler': 12 | project: tool-kit-test 13 | tasks: 14 | HerokuProduction: 15 | scaling: 16 | tool-kit-test: 17 | web: 18 | size: standard-1x 19 | quantity: 1 20 | 21 | commands: 22 | build:local: 23 | - Webpack 24 | - Babel 25 | build:ci: 26 | - Webpack 27 | - Babel 28 | build:remote: 29 | - Webpack 30 | - Babel 31 | 32 | version: 2 33 | -------------------------------------------------------------------------------- /core/cli/test/files/conflicted/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/webpack' 3 | - '@dotcom-tool-kit/babel' 4 | - '@dotcom-tool-kit/npm' 5 | - '@dotcom-tool-kit/circleci-deploy' 6 | 7 | options: 8 | 9 | commands: 10 | 11 | version: 2 12 | -------------------------------------------------------------------------------- /core/cli/test/files/cousins/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/frontend-app' 3 | - '@dotcom-tool-kit/babel' 4 | - '@dotcom-tool-kit/npm' 5 | - '@dotcom-tool-kit/circleci-deploy' 6 | 7 | options: 8 | 9 | commands: 10 | 11 | version: 2 12 | -------------------------------------------------------------------------------- /core/cli/test/files/duplicate/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/backend-heroku-app' 3 | - '@dotcom-tool-kit/heroku' 4 | 5 | options: 6 | plugins: 7 | '@dotcom-tool-kit/heroku': 8 | pipeline: tool-kit-test 9 | 10 | '@dotcom-tool-kit/doppler': 11 | project: tool-kit-test 12 | tasks: 13 | HerokuProduction: 14 | scaling: 15 | tool-kit-test: 16 | web: 17 | size: standard-1x 18 | quantity: 1 19 | 20 | commands: 21 | 22 | version: 2 23 | -------------------------------------------------------------------------------- /core/cli/test/files/multiple-hook-options/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/circleci' 3 | - '@dotcom-tool-kit/package-json-hook' 4 | options: 5 | hooks: 6 | - PackageJson: 7 | scripts: 8 | somethingcustom: 'test:local' 9 | - CircleCi: 10 | jobs: 11 | - name: test 12 | command: test:local 13 | -------------------------------------------------------------------------------- /core/cli/test/files/successful/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/eslint' 3 | - '@dotcom-tool-kit/mocha' 4 | - '@dotcom-tool-kit/n-test' 5 | 6 | options: 7 | tasks: 8 | Eslint: 9 | files: 10 | - webpack.config.js 11 | Mocha: 12 | testDir: './mocha_tests' 13 | NTest: 14 | host: 'https://example.com' 15 | 16 | commands: 17 | 'test:local': 18 | - Mocha 19 | - Eslint 20 | 'test:ci': 21 | - Mocha 22 | - Eslint 23 | 24 | version: 2 25 | -------------------------------------------------------------------------------- /core/cli/test/files/with-index/.toolkitrc.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/core/cli/test/files/with-index/.toolkitrc.yml -------------------------------------------------------------------------------- /core/cli/test/files/with-index/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/core/cli/test/files/with-index/index.js -------------------------------------------------------------------------------- /core/cli/test/helpers.ts: -------------------------------------------------------------------------------- 1 | import { format } from 'pretty-format' 2 | 3 | export const removeRoots = (config) => { 4 | // HACK:IM:20250207 the configs that are loaded include absolute paths in 5 | // them which are unsuitable for snapshots (as they will differ from 6 | // machine-to-machine). we can't mock away the logic that sets the config 7 | // file paths as it's needed when loading the options schemas. we also can't 8 | // reliably edit the config object before passing it to Jest as it's very 9 | // deep and contains many circular references (this also precludes using 10 | // JSON.stringify.) instead let's call the same pretty-format function Jest 11 | // calls. i'm using similar options to what Jest uses so that the diff this 12 | // creates is understandable. 13 | const stringified = format(config, { 14 | escapeRegex: true, 15 | indent: 2, 16 | printFunctionName: false, 17 | escapeString: false, 18 | printBasicPrototype: false 19 | }) 20 | // HACK:IM:20250207 replaceAll does exist in Node 18 but we don't need to 21 | // edit the tsconfig.json just for this file as test files aren't 22 | // type-checked anyway 23 | return stringified.replaceAll(`${process.cwd()}/`, '') 24 | } 25 | -------------------------------------------------------------------------------- /core/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../../lib/wait-for-ok" 6 | }, 7 | { 8 | "path": "../../lib/error" 9 | }, 10 | { 11 | "path": "../../lib/logger" 12 | }, 13 | { 14 | "path": "../../lib/plugin" 15 | }, 16 | { 17 | "path": "../../lib/config" 18 | }, 19 | { 20 | "path": "../../lib/validated" 21 | }, 22 | { 23 | "path": "../../lib/conflict" 24 | }, 25 | { 26 | "path": "../../lib/state" 27 | }, 28 | { 29 | "path": "../../lib/base" 30 | } 31 | ], 32 | "compilerOptions": { 33 | "outDir": "lib", 34 | "rootDir": "src" 35 | }, 36 | "include": [ 37 | "src/**/*" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /core/cli/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "resolveJsonModule": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /core/create/bin/create-tool-kit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../lib/index.js') 3 | -------------------------------------------------------------------------------- /core/create/src/augmentations.d.ts: -------------------------------------------------------------------------------- 1 | // workaround for simple-git using DOM types in their definitions 2 | 3 | declare global { 4 | /* eslint-disable @typescript-eslint/no-empty-interface */ 5 | export interface AbortSignal {} 6 | } 7 | 8 | export {} 9 | -------------------------------------------------------------------------------- /core/create/src/declarations.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'komatsu' { 2 | type Status = 'pending' | 'done' | 'info' | 'fail' 3 | 4 | type LogOptions = { 5 | status?: S 6 | message: string 7 | error?: Error 8 | } 9 | 10 | type LogPromiseLabels = 11 | | string 12 | | { 13 | pending: string | (() => string) 14 | done: string | ((result: T) => string) 15 | fail: string | ((error: Error) => string) 16 | } 17 | 18 | type Spinner = { 19 | message: string 20 | status: Status 21 | frame: number 22 | error?: Error 23 | tick: NodeJS.Timer 24 | } 25 | 26 | export default class Spinners { 27 | spinners: Map 28 | log(id: string, options: LogOptions): void 29 | logPromise(promise: Promise, labels?: LogPromiseLabels): Promise 30 | renderSymbol(spinner: Spinner): string 31 | stop(): void 32 | } 33 | } 34 | 35 | declare module '@quarterto/parse-makefile-rules' { 36 | export default function parse(str: string): Record 37 | } 38 | -------------------------------------------------------------------------------- /core/create/src/prompts/confirmation.ts: -------------------------------------------------------------------------------- 1 | import { styles } from '@dotcom-tool-kit/logger' 2 | import prompt from 'prompts' 3 | 4 | export interface ConfirmationParams { 5 | deleteConfig: boolean 6 | addEslintConfig: boolean 7 | fixGitignore: boolean 8 | packagesToInstall: string[] 9 | packagesToRemove: string[] 10 | configFile: string 11 | } 12 | 13 | export default ({ 14 | deleteConfig, 15 | addEslintConfig, 16 | fixGitignore, 17 | packagesToInstall, 18 | packagesToRemove, 19 | configFile 20 | }: ConfirmationParams): Promise> => { 21 | return prompt({ 22 | name: 'confirm', 23 | type: 'confirm', 24 | initial: true, 25 | message: () => { 26 | return `so, we're gonna: 27 | 28 | install the following packages: 29 | ${packagesToInstall.map((p) => `- ${styles.plugin(p)}`).join('\n')}\ 30 | ${addEslintConfig ? `\n\nadd a default eslint config file at ${styles.filepath('./.eslintrc.js')}` : ''}\ 31 | ${ 32 | packagesToRemove.length > 0 33 | ? '\n\nuninstall the following packages:\n' + 34 | packagesToRemove.map((p) => `- ${styles.plugin(p)}`).join('\n') 35 | : '' 36 | } 37 | 38 | create a ${styles.filepath('.toolkitrc.yml')} containing: 39 | ${configFile.trimEnd()}\ 40 | ${deleteConfig ? `\n\nregenerate ${styles.filepath('.circleci/config.yml')}` : ''}\ 41 | ${fixGitignore ? `\n\nupdate ${styles.filepath('.gitignore')}` : ''} 42 | 43 | sound good?` 44 | } 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /core/create/src/prompts/systemCode.ts: -------------------------------------------------------------------------------- 1 | import { styles } from '@dotcom-tool-kit/logger' 2 | import prompt from 'prompts' 3 | import type { PackageJson } from 'type-fest' 4 | import { BizOpsSystem, getBizOpsSystem } from '../bizOps' 5 | 6 | export interface ConfirmationParams { 7 | packageJson: PackageJson 8 | } 9 | 10 | export default async ({ packageJson }: ConfirmationParams): Promise => { 11 | const guessedSystemCode = packageJson.name 12 | let bizOpsSystem 13 | if (guessedSystemCode) { 14 | bizOpsSystem = await getBizOpsSystem(guessedSystemCode) 15 | } 16 | if (!bizOpsSystem) { 17 | // try to guess the system code then assume an error means the guess was 18 | // wrong 19 | const { systemCode } = await prompt({ 20 | name: 'systemCode', 21 | type: 'text', 22 | message: `what's your system code? leave this field blank if this is not a system ${styles.dim( 23 | `(${ 24 | guessedSystemCode 25 | ? `failed to get system for ${guessedSystemCode}` 26 | : "couldn't find system code in package.json" 27 | })` 28 | )}` 29 | }) 30 | if (systemCode) { 31 | bizOpsSystem = await getBizOpsSystem(systemCode) 32 | } 33 | } 34 | return bizOpsSystem 35 | } 36 | -------------------------------------------------------------------------------- /core/create/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/error" 10 | }, 11 | { 12 | "path": "../../lib/logger" 13 | }, 14 | { 15 | "path": "../../lib/config" 16 | }, 17 | { 18 | "path": "../../lib/plugin" 19 | }, 20 | { 21 | "path": "../../lib/schemas" 22 | }, 23 | { 24 | "path": "../cli" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /core/sandbox/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !readme.md 3 | !.gitignore 4 | !jest.config.js 5 | -------------------------------------------------------------------------------- /core/sandbox/jest.config.js: -------------------------------------------------------------------------------- 1 | // Jest seems to have a bug where a directory in its list of projects without a 2 | // Jest config will cause all tests to be run twice. Let's leave a config here 3 | // to prevent that. 4 | module.exports = {} 5 | -------------------------------------------------------------------------------- /core/sandbox/readme.md: -------------------------------------------------------------------------------- 1 | # ⛱ sandbox 2 | 3 | This folder can be used to test Tool Kit in the context of an app. Everything in this folder is gitignored apart from this readme, so you can make any changes you need to without having to keep it up to date or worry about merges. 4 | -------------------------------------------------------------------------------- /docs/tool-kit-principles.md: -------------------------------------------------------------------------------- 1 | # Principles for Tool Kit 2 | 3 | - A repo using Tool Kit should be familiar to a JavaScript developer 4 | - Tool Kit provides sensible, foundational configuration for common tasks for Customer Products repos 5 | - There should be less friction using Tool Kit than implementing tooling ad hoc in a team yourself 6 | - Tool Kit defines the development lifecycle for Customer Products repos 7 | - Tool Kit manages configuration files in your repo for running development lifecycle tasks 8 | - Tool Kit is modular: the core doesn't make assumptions about what tooling you're using, and plugins are independent of each other 9 | - Tool Kit enables integration with tooling to be reusable across Customer Products teams 10 | - Tool Kit is extensible by teams outside of Platforms without necessarily involving us 11 | - When a team has extended Tool Kit, that can be reused by other teams 12 | - Tool Kit's internals should be familiar to a JavaScript developer 13 | - Keeping Tool Kit up-to-date in your repo should not be a chore 14 | 15 | 16 | Platforms reserves the right to ignore these principles if it's too hard to follow them :( 17 | -------------------------------------------------------------------------------- /etc/concepts.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/etc/concepts.sketch -------------------------------------------------------------------------------- /etc/logo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/etc/logo.sketch -------------------------------------------------------------------------------- /jest.config.base.js: -------------------------------------------------------------------------------- 1 | const tsJestConfig = { 2 | tsconfig: 'tsconfig.settings.json', 3 | isolatedModules: true 4 | } 5 | module.exports.tsJestConfig = tsJestConfig 6 | 7 | /** @type {import('jest').Config} */ 8 | module.exports.config = { 9 | testEnvironment: 'node', 10 | testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/*.+(spec|test).[jt]s?(x)'], 11 | testPathIgnorePatterns: ['/node_modules/', '/.+/lib/', '/test/files'], 12 | clearMocks: true, 13 | transform: { 14 | '^.+\\.tsx?$': ['ts-jest', tsJestConfig] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('./jest.config.base') 2 | 3 | /** @type {import('jest').Config} */ 4 | module.exports = { 5 | ...base.config, 6 | projects: ['/core/*', '/plugins/*', '/lib/*'] 7 | } 8 | -------------------------------------------------------------------------------- /lib/base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/base", 3 | "version": "1.2.3", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/conflict": "^1.0.1", 14 | "@dotcom-tool-kit/logger": "^4.2.2", 15 | "@dotcom-tool-kit/validated": "^1.0.3", 16 | "semver": "^7.7.2", 17 | "winston": "^3.17.0" 18 | }, 19 | "devDependencies": { 20 | "@dotcom-tool-kit/config": "^1.1.1", 21 | "@dotcom-tool-kit/plugin": "^1.1.0", 22 | "type-fest": "^4.41.0", 23 | "winston": "^3.17.0", 24 | "zod": "^3.24.4" 25 | }, 26 | "peerDependencies": { 27 | "zod": "^3.24.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/base/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base' 2 | export * from './hook' 3 | export * from './init' 4 | export * from './task' 5 | -------------------------------------------------------------------------------- /lib/base/src/init.ts: -------------------------------------------------------------------------------- 1 | import type { Logger } from 'winston' 2 | import { initSymbol, typeSymbol } from './symbols' 3 | import { Base } from './base' 4 | 5 | export type InitContext = { 6 | cwd: string 7 | } 8 | 9 | export abstract class Init extends Base { 10 | logger: Logger 11 | 12 | constructor(logger: Logger) { 13 | super() 14 | this.logger = logger.child({ hook: this.constructor.name }) 15 | } 16 | 17 | static get [typeSymbol](): symbol { 18 | return initSymbol 19 | } 20 | 21 | get [typeSymbol](): symbol { 22 | return initSymbol 23 | } 24 | 25 | abstract init(context: InitContext): Promise 26 | } 27 | 28 | export type InitConstructor = { 29 | new (logger: Logger): Init 30 | } 31 | 32 | export type InitClass = InitConstructor & typeof Init 33 | -------------------------------------------------------------------------------- /lib/base/src/symbols.ts: -------------------------------------------------------------------------------- 1 | // uses Symbol.for, not Symbol, so that they're compatible across different 2 | // @dotcom-tool-kit/base instances 3 | 4 | // TODO these symbols say '@dotcom-tool-kit/types' because they used to live 5 | // in that package. changing that would be a backwards compatibility nightmare 6 | 7 | // used as the name for the property we use to identify classes 8 | export const typeSymbol = Symbol.for('@dotcom-tool-kit/types') 9 | 10 | // used to identify the Base, Task and Hook classes 11 | export const baseSymbol = Symbol.for('@dotcom-tool-kit/types/base') 12 | export const taskSymbol = Symbol.for('@dotcom-tool-kit/types/task') 13 | export const hookSymbol = Symbol.for('@dotcom-tool-kit/types/hook') 14 | export const initSymbol = Symbol.for('@dotcom-tool-kit/types/init') 15 | -------------------------------------------------------------------------------- /lib/base/src/type-utils.ts: -------------------------------------------------------------------------------- 1 | export type Default = T extends undefined ? D : T 2 | -------------------------------------------------------------------------------- /lib/base/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../conflict" 6 | }, 7 | { 8 | "path": "../logger" 9 | }, 10 | { 11 | "path": "../plugin" 12 | }, 13 | { 14 | "path": "../validated" 15 | }, 16 | { 17 | "path": "../config" 18 | } 19 | ], 20 | "compilerOptions": { 21 | "outDir": "lib", 22 | "rootDir": "src" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/config", 3 | "version": "1.1.1", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/conflict": "^1.0.1", 14 | "@dotcom-tool-kit/plugin": "^1.1.0", 15 | "@dotcom-tool-kit/validated": "^1.0.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../plugin" 6 | } 7 | ], 8 | "compilerOptions": { 9 | "outDir": "lib", 10 | "rootDir": "src" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/conflict/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### Dependencies 4 | 5 | * The following workspace dependencies were updated 6 | * dependencies 7 | * @dotcom-tool-kit/plugin bumped from ^1.0.0 to ^1.1.0 8 | 9 | ## 1.0.0 (2024-09-10) 10 | 11 | 12 | ### Features 13 | 14 | * move conflict into its own package ([8ab46a0](https://github.com/Financial-Times/dotcom-tool-kit/commit/8ab46a06370d32fd19300fd6a58a775e04a96717)) 15 | -------------------------------------------------------------------------------- /lib/conflict/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/conflict", 3 | "version": "1.0.1", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/plugin": "^1.1.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/conflict/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from '@dotcom-tool-kit/plugin' 2 | 3 | export interface Conflict { 4 | plugin: Plugin 5 | conflicting: T[] 6 | } 7 | 8 | export function isConflict(thing: unknown): thing is Conflict { 9 | return Boolean((thing as Conflict).conflicting) 10 | } 11 | 12 | export function findConflictingEntries( 13 | items: Record> 14 | ): [string, Conflict][] { 15 | return Object.entries(items).filter((entry): entry is [string, Conflict] => isConflict(entry[1])) 16 | } 17 | 18 | export function findConflicts(items: (U | Conflict)[]): Conflict[] { 19 | const conflicts: Conflict[] = [] 20 | 21 | for (const item of items) { 22 | if (isConflict(item)) { 23 | conflicts.push(item) 24 | } 25 | } 26 | 27 | return conflicts 28 | } 29 | 30 | export function withoutConflicts(items: (U | Conflict)[]): U[] { 31 | const nonConflicts: U[] = [] 32 | 33 | for (const item of items) { 34 | if (!isConflict(item)) { 35 | nonConflicts.push(item) 36 | } 37 | } 38 | 39 | return nonConflicts 40 | } 41 | -------------------------------------------------------------------------------- /lib/conflict/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../plugin" 6 | } 7 | ], 8 | "compilerOptions": { 9 | "outDir": "lib", 10 | "rootDir": "src" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/doppler/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | optionsSchema: './lib/schema' 4 | -------------------------------------------------------------------------------- /lib/doppler/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config, 5 | moduleDirectories: ['node_modules'] 6 | } 7 | -------------------------------------------------------------------------------- /lib/doppler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/doppler", 3 | "version": "2.2.3", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "dependencies": { 10 | "@dotcom-tool-kit/error": "^4.1.1", 11 | "@dotcom-tool-kit/logger": "^4.2.2", 12 | "tslib": "^2.8.1", 13 | "zod": "^3.24.4" 14 | }, 15 | "keywords": [], 16 | "author": "FT.com Platforms Team ", 17 | "license": "ISC", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 21 | "directory": "lib/circleci" 22 | }, 23 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 24 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/lib/doppler", 25 | "files": [ 26 | "/lib", 27 | ".toolkitrc.yml" 28 | ], 29 | "volta": { 30 | "extends": "../../package.json" 31 | }, 32 | "devDependencies": { 33 | "spawk": "^1.8.2", 34 | "winston": "^3.17.0" 35 | }, 36 | "engines": { 37 | "node": "18.x || 20.x || 22.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/doppler/src/schema.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod' 2 | 3 | // In theory, these fields should be required as Doppler won't work without them, 4 | // but not every app that pulls in the Doppler plugin actually needs to use 5 | // Doppler, e.g., an app that uses the `nodemon` plugin with the `useDoppler` 6 | // option set to false. 7 | export default z 8 | .object({ 9 | project: z.string() 10 | }) 11 | .partial() 12 | -------------------------------------------------------------------------------- /lib/doppler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../error" 6 | }, 7 | { 8 | "path": "../logger" 9 | } 10 | ], 11 | "compilerOptions": { 12 | "outDir": "lib", 13 | "rootDir": "src" 14 | }, 15 | "include": ["src/**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /lib/error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/error", 3 | "version": "4.1.1", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "lib/error" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/lib/error", 19 | "files": [ 20 | "/lib" 21 | ], 22 | "volta": { 23 | "extends": "../../package.json" 24 | }, 25 | "dependencies": { 26 | "tslib": "^2.8.1" 27 | }, 28 | "engines": { 29 | "node": "18.x || 20.x || 22.x" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/error/src/index.ts: -------------------------------------------------------------------------------- 1 | export class ToolKitError extends Error { 2 | name = 'ToolKitError' 3 | details?: string 4 | exitCode?: number 5 | } 6 | 7 | export interface ConflictingTask { 8 | task: string 9 | plugin: string 10 | } 11 | 12 | export interface CommandTaskConflict { 13 | command: string 14 | conflictingTasks: ConflictingTask[] 15 | } 16 | 17 | export class ToolKitConflictError extends ToolKitError { 18 | conflicts: CommandTaskConflict[] 19 | 20 | constructor(message: string, conflicts: CommandTaskConflict[]) { 21 | super(message) 22 | this.conflicts = conflicts 23 | } 24 | } 25 | 26 | export function hasToolKitConflicts(error: unknown): error is ToolKitConflictError { 27 | return error instanceof ToolKitConflictError && error.conflicts.length > 0 28 | } 29 | -------------------------------------------------------------------------------- /lib/error/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/logger", 3 | "version": "4.2.2", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "lib/logger" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/lib/logger", 19 | "files": [ 20 | "/lib" 21 | ], 22 | "volta": { 23 | "extends": "../../package.json" 24 | }, 25 | "dependencies": { 26 | "@apaleslimghost/boxen": "^5.1.3", 27 | "@dotcom-tool-kit/error": "^4.1.1", 28 | "ansi-regex": "^5.0.1", 29 | "chalk": "^4.1.0", 30 | "triple-beam": "^1.4.1", 31 | "tslib": "^2.8.1", 32 | "winston": "^3.17.0", 33 | "winston-transport": "^4.9.0" 34 | }, 35 | "devDependencies": { 36 | "@types/triple-beam": "^1.3.5" 37 | }, 38 | "engines": { 39 | "node": "18.x || 20.x || 22.x" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/logger/src/index.ts: -------------------------------------------------------------------------------- 1 | export { createFormatter } from './format' 2 | export { createWritableLogger, hookConsole, hookFork, hookStream, waitOnExit } from './helpers' 3 | export { rootLogger } from './logger' 4 | export { styles } from './styles' 5 | -------------------------------------------------------------------------------- /lib/logger/src/logger.ts: -------------------------------------------------------------------------------- 1 | import winston from 'winston' 2 | import { createFormatter } from './format' 3 | import { consoleTransport } from './transports' 4 | 5 | export const rootLogger: winston.Logger = winston.createLogger({ 6 | level: process.env.LOG_LEVEL ?? (process.env.CIRCLECI ? 'verbose' : 'info'), 7 | transports: [consoleTransport] 8 | }) 9 | rootLogger.format = createFormatter(rootLogger) 10 | -------------------------------------------------------------------------------- /lib/logger/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [{ "path": "../error" }] 8 | } 9 | -------------------------------------------------------------------------------- /lib/plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/plugin", 3 | "version": "1.1.0", 4 | "description": "", 5 | "main": "lib", 6 | "devDependencies": {}, 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC" 13 | } 14 | -------------------------------------------------------------------------------- /lib/plugin/src/index.ts: -------------------------------------------------------------------------------- 1 | type TaskSpecWithOptions = Record> 2 | type TaskSpec = string | TaskSpecWithOptions 3 | type InstallsSpec = { 4 | entryPoint: string 5 | managesFiles?: string[] 6 | } 7 | 8 | export const CURRENT_RC_FILE_VERSION = 2 9 | 10 | export type RCFile = { 11 | version?: typeof CURRENT_RC_FILE_VERSION 12 | plugins: string[] 13 | installs: { [id: string]: InstallsSpec } 14 | tasks: { [id: string]: string } 15 | commands: { [id: string]: TaskSpec | TaskSpec[] } 16 | hooks?: { [id: string]: TaskSpec | TaskSpec[] } 17 | options: { 18 | plugins: { [id: string]: Record } 19 | tasks: { [id: string]: Record } 20 | hooks: { [id: string]: Record }[] 21 | } 22 | optionsSchema?: string 23 | init: string[] 24 | } 25 | 26 | export interface Plugin { 27 | id: string 28 | root: string 29 | rcFile?: RCFile 30 | parent?: Plugin 31 | children?: Plugin[] 32 | } 33 | 34 | export interface CommandTask { 35 | id: string 36 | plugin: Plugin 37 | tasks: OptionsForTask[] 38 | } 39 | 40 | export interface OptionsForPlugin { 41 | options: Record 42 | plugin: Plugin 43 | forPlugin: Plugin 44 | } 45 | 46 | export interface OptionsForTask { 47 | options: Record 48 | plugin: Plugin 49 | task: string 50 | } 51 | 52 | export interface EntryPoint { 53 | plugin: Plugin 54 | modulePath: string 55 | } 56 | -------------------------------------------------------------------------------- /lib/plugin/src/root-schema.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod' 2 | 3 | export const RootSchema = z.object({ 4 | allowNativeFetch: z.boolean().default(false) 5 | }) 6 | export type RootOptions = z.infer 7 | -------------------------------------------------------------------------------- /lib/plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/state", 3 | "version": "4.3.2", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "lib/state" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/lib/state", 19 | "files": [ 20 | "/lib", 21 | ".toolkitrc.yml" 22 | ], 23 | "volta": { 24 | "extends": "../../package.json" 25 | }, 26 | "dependencies": { 27 | "tslib": "^2.8.1" 28 | }, 29 | "engines": { 30 | "node": "18.x || 20.x || 22.x" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/state/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/validated/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### Dependencies 4 | 5 | * The following workspace dependencies were updated 6 | * dependencies 7 | * @dotcom-tool-kit/error bumped from ^4.0.0 to ^4.0.1 8 | 9 | ### Dependencies 10 | 11 | * The following workspace dependencies were updated 12 | * dependencies 13 | * @dotcom-tool-kit/error bumped from ^4.0.1 to ^4.1.0 14 | 15 | ## [1.0.3](https://github.com/Financial-Times/dotcom-tool-kit/compare/validated-v1.0.2...validated-v1.0.3) (2025-05-06) 16 | 17 | 18 | ### Dependencies 19 | 20 | * The following workspace dependencies were updated 21 | * dependencies 22 | * @dotcom-tool-kit/error bumped from ^4.1.0 to ^4.1.1 23 | 24 | ## 1.0.0 (2024-09-10) 25 | 26 | 27 | ### Features 28 | 29 | * move validated into its own package ([a8ed591](https://github.com/Financial-Times/dotcom-tool-kit/commit/a8ed59131bc603ed01fd8672646b3c5d75c77bde)) 30 | 31 | 32 | ### Dependencies 33 | 34 | * The following workspace dependencies were updated 35 | * dependencies 36 | * @dotcom-tool-kit/error bumped from ^3.1.0 to ^4.0.0 37 | -------------------------------------------------------------------------------- /lib/validated/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /lib/validated/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/validated", 3 | "version": "1.0.3", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dotcom-tool-kit/error": "^4.1.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/validated/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "outDir": "lib", 6 | "rootDir": "src" 7 | }, 8 | "references": [ 9 | { 10 | "path": "../error" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /lib/wait-for-ok/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/wait-for-ok", 3 | "version": "4.1.2", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "tslib": "^2.8.1" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 18 | "directory": "lib/wait-for-ok" 19 | }, 20 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 21 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/lib/wait-for-ok", 22 | "devDependencies": { 23 | "winston": "^3.17.0" 24 | }, 25 | "files": [ 26 | "/lib", 27 | ".toolkitrc.yml" 28 | ], 29 | "volta": { 30 | "extends": "../../package.json" 31 | }, 32 | "engines": { 33 | "node": "18.x || 20.x || 22.x" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/wait-for-ok/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src", 6 | "types": ["node"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /orb/.gitignore: -------------------------------------------------------------------------------- 1 | # orb.yml is "packed" from source, and not published directly from the repository. 2 | orb.yml -------------------------------------------------------------------------------- /orb/.yamllint: -------------------------------------------------------------------------------- 1 | extends: relaxed 2 | 3 | rules: 4 | line-length: 5 | max: 200 6 | allow-non-breakable-inline-mappings: true 7 | 8 | -------------------------------------------------------------------------------- /orb/src/@orb.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | description: > 4 | Helper jobs for dotcom-tool-kit to add to your CircleCI config depending on 5 | the hooks used 6 | 7 | # This information will be displayed in the orb registry and is not mandatory. 8 | display: 9 | home_url: 'https://www.ft.com' 10 | source_url: 'https://www.github.com/financial-times/dotcom-tool-kit' 11 | 12 | # If your orb requires other orbs, you can import them like this. Otherwise remove the "orbs" stanza. 13 | orbs: 14 | node: circleci/node@5.0.2 15 | change-api: financial-times/change-api@1.0.9 16 | cloudsmith-oidc: ft-circleci-orbs/cloudsmith-oidc@1.0 17 | aws-cli: circleci/aws-cli@3.1.4 18 | serverless-framework: circleci/serverless-framework@2.0.2 19 | -------------------------------------------------------------------------------- /orb/src/README.md: -------------------------------------------------------------------------------- 1 | # Orb Source 2 | 3 | Orbs are shipped as individual `orb.yml` files, however, to make development easier, it is possible to author an orb in _unpacked_ form, which can be _packed_ with the CircleCI CLI and published. 4 | 5 | The default `.circleci/config.yml` file contains the configuration code needed to automatically pack, test, and deploy and changes made to the contents of the orb source in this directory. 6 | 7 | ## @orb.yml 8 | 9 | This is the entry point for our orb "tree", which becomes our `orb.yml` file later. 10 | 11 | Within the `@orb.yml` we generally specify 4 configuration keys 12 | 13 | **Keys** 14 | 15 | 1. **version** 16 | Specify version 2.1 for orb-compatible configuration `version: 2.1` 17 | 2. **description** 18 | Give your orb a description. Shown within the CLI and orb registry 19 | 3. **display** 20 | Specify the `home_url` referencing documentation or product URL, and `source_url` linking to the orb's source repository. 21 | 4. **orbs** 22 | (optional) Some orbs may depend on other orbs. Import them here. 23 | 24 | ## See: 25 | - [Orb Author Intro](https://circleci.com/docs/2.0/orb-author-intro/#section=configuration) 26 | - [Reusable Configuration](https://circleci.com/docs/2.0/reusing-config) -------------------------------------------------------------------------------- /orb/src/commands/README.md: -------------------------------------------------------------------------------- 1 | # Commands 2 | 3 | Easily add and author [Reusable Commands](https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands) to the `src/commands` directory. 4 | 5 | Each _YAML_ file within this directory will be treated as an orb command, with a name which matches its filename. 6 | 7 | Here's an example of a command: 8 | 9 | ```yaml 10 | description: > 11 | Replace this text with a description for this command. 12 | # What will this command do? 13 | # Descriptions should be short, simple, and clear. 14 | parameters: 15 | greeting: 16 | type: string 17 | default: "Hello" 18 | description: "Select a proper greeting" 19 | steps: 20 | - run: 21 | name: Hello World 22 | command: echo << parameters.greeting >> world 23 | ``` 24 | 25 | ## See: 26 | - [Orb Author Intro](https://circleci.com/docs/2.0/orb-author-intro/#section=configuration) 27 | - [How to author commands](https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands) 28 | -------------------------------------------------------------------------------- /orb/src/commands/attach-workspace.yml: -------------------------------------------------------------------------------- 1 | description: > 2 | Attaches workspace to the current working directory. Useful when you want to 3 | reuse artifacts from a previous job. 4 | 5 | steps: 6 | - attach_workspace: 7 | at: . 8 | -------------------------------------------------------------------------------- /orb/src/commands/persist-workspace.yml: -------------------------------------------------------------------------------- 1 | description: > 2 | Stores current working directory as a workspace. Useful when you want to store 3 | artifacts for subsequent jobs to use. 4 | 5 | parameters: 6 | path: 7 | type: string 8 | default: .toolkitstate 9 | description: Path to persist to workspace 10 | 11 | steps: 12 | - persist_to_workspace: 13 | root: . 14 | paths: 15 | - << parameters.path >> 16 | -------------------------------------------------------------------------------- /orb/src/commands/serverless-assume-role.yml: -------------------------------------------------------------------------------- 1 | description: > 2 | Assume AWS role configured to use a CircleCI OIDC token to allow you to 3 | deploy the project via Serverless 4 | 5 | parameters: 6 | aws-account-id: 7 | type: string 8 | system-code: 9 | type: string 10 | 11 | steps: 12 | - aws-cli/setup: 13 | role-arn: arn:aws:iam::<< parameters.aws-account-id >>:role/CircleCI-role-<< parameters.system-code >> 14 | role-session-name: CircleCI-role-<< parameters.system-code >> 15 | profile-name: CircleCI-role-<< parameters.system-code >> 16 | - serverless-framework/setup 17 | -------------------------------------------------------------------------------- /orb/src/executors/default.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | tag: 3 | type: string 4 | default: '16.18-browsers' 5 | 6 | docker: 7 | - image: cimg/node:<< parameters.tag >> 8 | -------------------------------------------------------------------------------- /orb/src/jobs/README.md: -------------------------------------------------------------------------------- 1 | # Jobs 2 | 3 | Easily author and add [Parameterized Jobs](https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs) to the `src/jobs` directory. 4 | 5 | Each _YAML_ file within this directory will be treated as an orb job, with a name which matches its filename. 6 | 7 | Jobs may invoke orb commands and other steps to fully automate tasks with minimal user configuration. 8 | 9 | Here's an example of a job: 10 | 11 | ```yaml 12 | # What will this job do? 13 | # Descriptions should be short, simple, and clear. 14 | Sample description 15 | executor: default 16 | parameters: 17 | greeting: 18 | type: string 19 | default: "Hello" 20 | description: "Select a proper greeting" 21 | steps: 22 | - greet: 23 | greeting: << parameters.greeting >> 24 | ``` 25 | 26 | ## See: 27 | - [Orb Author Intro](https://circleci.com/docs/2.0/orb-author-intro/#section=configuration) 28 | - [How To Author Commands](https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs) 29 | - [Node Orb "test" Job](https://github.com/CircleCI-Public/node-orb/blob/master/src/jobs/test.yml) 30 | -------------------------------------------------------------------------------- /orb/src/jobs/build.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | 6 | executor: << parameters.executor >> 7 | 8 | steps: 9 | - attach-workspace 10 | - run: 11 | name: Run the project build-production task 12 | command: npx dotcom-tool-kit build:ci 13 | - persist-workspace: 14 | path: . 15 | -------------------------------------------------------------------------------- /orb/src/jobs/deploy-review.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | aws-account-id: 6 | default: '' 7 | type: string 8 | system-code: 9 | default: '' 10 | type: string 11 | cloudsmith-org: 12 | default: 'financial-times' 13 | type: string 14 | cloudsmith-service-account: 15 | default: '' 16 | type: string 17 | 18 | executor: << parameters.executor >> 19 | 20 | environment: 21 | CLOUDSMITH_ORGANISATION: << parameters.cloudsmith-org >> 22 | CLOUDSMITH_SERVICE_ACCOUNT: << parameters.cloudsmith-service-account >> 23 | 24 | steps: 25 | - attach-workspace 26 | - setup_remote_docker: 27 | docker_layer_caching: true 28 | - when: 29 | condition: 30 | and: 31 | - << parameters.cloudsmith-org >> 32 | - << parameters.cloudsmith-service-account >> 33 | steps: 34 | - cloudsmith-oidc/authenticate_with_oidc 35 | - when: 36 | condition: 37 | and: 38 | - << parameters.aws-account-id >> 39 | - << parameters.system-code >> 40 | steps: 41 | - serverless-assume-role: 42 | aws-account-id: << parameters.aws-account-id >> 43 | system-code: << parameters.system-code >> 44 | - run: 45 | name: Create and test review app 46 | command: npx dotcom-tool-kit deploy:review 47 | - persist-workspace 48 | -------------------------------------------------------------------------------- /orb/src/jobs/deploy-staging.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | cloudsmith-org: 6 | default: 'financial-times' 7 | type: string 8 | cloudsmith-service-account: 9 | default: '' 10 | type: string 11 | 12 | executor: << parameters.executor >> 13 | 14 | environment: 15 | CLOUDSMITH_ORGANISATION: << parameters.cloudsmith-org >> 16 | CLOUDSMITH_SERVICE_ACCOUNT: << parameters.cloudsmith-service-account >> 17 | 18 | steps: 19 | - attach-workspace 20 | - setup_remote_docker: 21 | docker_layer_caching: true 22 | - when: 23 | condition: 24 | and: 25 | - << parameters.cloudsmith-org >> 26 | - << parameters.cloudsmith-service-account >> 27 | steps: 28 | - cloudsmith-oidc/authenticate_with_oidc 29 | - run: 30 | name: Deploy to staging 31 | command: npx dotcom-tool-kit deploy:staging 32 | - persist-workspace 33 | -------------------------------------------------------------------------------- /orb/src/jobs/e2e-test-review.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | aws-account-id: 6 | default: '' 7 | type: string 8 | system-code: 9 | default: '' 10 | type: string 11 | 12 | executor: << parameters.executor >> 13 | 14 | steps: 15 | - attach-workspace 16 | - when: 17 | condition: 18 | and: 19 | - << parameters.aws-account-id >> 20 | - << parameters.system-code >> 21 | steps: 22 | - serverless-assume-role: 23 | aws-account-id: << parameters.aws-account-id >> 24 | system-code: << parameters.system-code >> 25 | - run: 26 | name: Smoke tests 27 | command: npx dotcom-tool-kit test:review 28 | - run: 29 | when: always 30 | name: Teardown review app 31 | command: npx dotcom-tool-kit teardown:review 32 | - store_artifacts: 33 | path: /root/project/e2e-test-results 34 | -------------------------------------------------------------------------------- /orb/src/jobs/e2e-test-staging.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | 6 | executor: << parameters.executor >> 7 | 8 | steps: 9 | - attach-workspace 10 | - run: 11 | name: Run smoke tests on staging 12 | command: npx dotcom-tool-kit test:staging 13 | - run: 14 | when: always 15 | name: Scale down staging 16 | command: npx dotcom-tool-kit teardown:staging 17 | - store_artifacts: 18 | path: /root/project/e2e-test-results 19 | -------------------------------------------------------------------------------- /orb/src/jobs/heroku-promote.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | system-code: 6 | default: $CIRCLE_PROJECT_REPONAME 7 | description: >- 8 | The system-code of the system being changed. Defaults to the repository 9 | name. 10 | type: string 11 | 12 | executor: << parameters.executor >> 13 | 14 | steps: 15 | - attach-workspace 16 | - run: 17 | name: Deploy to production 18 | command: npx dotcom-tool-kit deploy:production 19 | - change-api/change-log: 20 | environment: production 21 | system-code: << parameters.system-code >> 22 | -------------------------------------------------------------------------------- /orb/src/jobs/heroku-provision.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | aws-account-id: 6 | default: '' 7 | type: string 8 | system-code: 9 | default: '' 10 | type: string 11 | 12 | executor: << parameters.executor >> 13 | 14 | steps: 15 | - attach-workspace 16 | - when: 17 | condition: 18 | and: 19 | - << parameters.aws-account-id >> 20 | - << parameters.system-code >> 21 | steps: 22 | - serverless-assume-role: 23 | aws-account-id: << parameters.aws-account-id >> 24 | system-code: << parameters.system-code >> 25 | - run: 26 | name: Create and test Heroku review app 27 | command: npx dotcom-tool-kit deploy:review 28 | - persist-workspace 29 | -------------------------------------------------------------------------------- /orb/src/jobs/heroku-staging.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | 6 | executor: << parameters.executor >> 7 | 8 | steps: 9 | - attach-workspace 10 | - run: 11 | name: Deploy to staging 12 | command: npx dotcom-tool-kit deploy:staging 13 | - persist-workspace 14 | -------------------------------------------------------------------------------- /orb/src/jobs/publish-tag.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | 6 | executor: << parameters.executor >> 7 | 8 | steps: 9 | - attach-workspace 10 | - run: 11 | name: Publish to npm 12 | command: npx dotcom-tool-kit publish:tag 13 | -------------------------------------------------------------------------------- /orb/src/jobs/publish.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | 6 | executor: << parameters.executor >> 7 | 8 | steps: 9 | - attach-workspace 10 | - run: 11 | name: Publish to npm 12 | command: npx dotcom-tool-kit publish:tag 13 | -------------------------------------------------------------------------------- /orb/src/jobs/setup.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | npm-version: 6 | description: This value is deprecated and no longer used. We now use Node's bundled version of npm. 7 | default: "" 8 | type: string 9 | 10 | executor: << parameters.executor >> 11 | 12 | steps: 13 | - attach-workspace 14 | - when: 15 | condition: << parameters.npm-version >> 16 | steps: 17 | - run: sudo npm install -g npm@$PARAM_NPM_VERSION 18 | - node/install-packages 19 | - persist-workspace: 20 | path: . 21 | -------------------------------------------------------------------------------- /orb/src/jobs/test.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | executor: 3 | default: default 4 | type: executor 5 | 6 | executor: << parameters.executor >> 7 | 8 | steps: 9 | - attach-workspace 10 | - run: 11 | name: Run tests 12 | command: "npx dotcom-tool-kit test:ci" 13 | environment: 14 | JEST_JUNIT_OUTPUT: test-results/jest/results.xml # TODO change depending on which test plugin is running? or actually, move this into the plugin? or do it as tool kit config. hmm 15 | MOCHA_FILE: test-results/mocha/results.xml 16 | - store_test_results: 17 | path: test-results 18 | - store_artifacts: 19 | path: test-results 20 | destination: test-results 21 | -------------------------------------------------------------------------------- /orb/version.txt: -------------------------------------------------------------------------------- 1 | 5.2.1 2 | -------------------------------------------------------------------------------- /plugins/aws/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | AwsAssumeRole: './lib/tasks/assume-role' 3 | 4 | version: 2 5 | -------------------------------------------------------------------------------- /plugins/aws/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/aws", 3 | "version": "0.1.10", 4 | "main": "lib", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "FT.com Platforms Team ", 10 | "license": "ISC", 11 | "description": "", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "plugins/aws" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/aws", 19 | "files": [ 20 | "/lib", 21 | ".toolkitrc.yml" 22 | ], 23 | "engines": { 24 | "node": "18.x || 20.x || 22.x" 25 | }, 26 | "peerDependencies": { 27 | "dotcom-tool-kit": "4.x" 28 | }, 29 | "dependencies": { 30 | "@aws-sdk/client-sts": "^3.812.0", 31 | "@dotcom-tool-kit/base": "^1.2.3", 32 | "@dotcom-tool-kit/error": "^4.1.1", 33 | "@dotcom-tool-kit/state": "^4.3.2", 34 | "zod": "^3.24.4" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /plugins/aws/readme.md: -------------------------------------------------------------------------------- 1 | # dotcom-tool-kit/aws 2 | 3 | ## Installation & Usage 4 | 5 | With Tool Kit [already set up](https://github.com/financial-times/dotcom-tool-kit#installing-and-using-tool-kit), install this plugin as a dev dependency: 6 | 7 | ```sh 8 | npm install --save-dev @dotcom-tool-kit/aws 9 | ``` 10 | 11 | And add it to your repo's `.toolkitrc.yml`: 12 | 13 | ```yml 14 | plugins: 15 | - '@dotcom-tool-kit/aws' 16 | ``` 17 | 18 | 19 | ## Tasks 20 | 21 | ### `AwsAssumeRole` 22 | 23 | Assume an AWS IAM role for use in future tasks 24 | #### Task options 25 | 26 | | Property | Description | Type | 27 | | :----------------- | :------------------------------- | :----------------------------------------------- | 28 | | **`roleArn`** (\*) | the ARN of an IAM role to assume | `string` (_regex: `/^arn:aws:iam::\d+:role\//`_) | 29 | 30 | _(\*) Required._ 31 | 32 | -------------------------------------------------------------------------------- /plugins/aws/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/error" 10 | }, 11 | { 12 | "path": "../../lib/base" 13 | }, 14 | { 15 | "path": "../../lib/state" 16 | } 17 | ], 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/babel/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | Babel: './lib/tasks/babel' 3 | 4 | commands: 5 | 'build:local': Babel 6 | 'build:ci': Babel 7 | 'build:remote': Babel 8 | 9 | version: 2 10 | -------------------------------------------------------------------------------- /plugins/babel/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/babel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/babel", 3 | "version": "4.3.5", 4 | "description": "", 5 | "author": "FT.com Platforms Team ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 10 | "directory": "plugins/babel" 11 | }, 12 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/main/plugins/babel", 13 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 14 | "scripts": { 15 | "test": "cd ../../ ; npx jest --silent --projects plugins/babel" 16 | }, 17 | "keywords": [], 18 | "dependencies": { 19 | "@dotcom-tool-kit/base": "^1.2.3", 20 | "@dotcom-tool-kit/error": "^4.1.1", 21 | "@dotcom-tool-kit/logger": "^4.2.2", 22 | "fast-glob": "^3.3.3", 23 | "tslib": "^2.8.1", 24 | "zod": "^3.24.4" 25 | }, 26 | "files": [ 27 | "/lib", 28 | ".toolkitrc.yml" 29 | ], 30 | "volta": { 31 | "extends": "../../package.json" 32 | }, 33 | "devDependencies": { 34 | "@babel/preset-env": "^7.27.2", 35 | "@jest/globals": "^29.7.0", 36 | "winston": "^3.17.0" 37 | }, 38 | "peerDependencies": { 39 | "@babel/core": "7.x", 40 | "dotcom-tool-kit": "4.x" 41 | }, 42 | "engines": { 43 | "node": "18.x || 20.x || 22.x" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /plugins/babel/test/files/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "node": 12 8 | } 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /plugins/babel/test/files/index.js: -------------------------------------------------------------------------------- 1 | export function test(x) { 2 | return x ?? 0 3 | } 4 | -------------------------------------------------------------------------------- /plugins/babel/test/tasks/babel.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from '@jest/globals' 2 | import Babel from '../../src/tasks/babel' 3 | import { promises as fs } from 'fs' 4 | import * as path from 'path' 5 | import winston, { Logger } from 'winston' 6 | 7 | const logger = winston as unknown as Logger 8 | 9 | const testDirectory = path.join(__dirname, '../files') 10 | const outputPath = path.join(testDirectory, 'lib') 11 | 12 | describe('babel', () => { 13 | it('should transpile the file', async () => { 14 | const task = new Babel( 15 | logger, 16 | 'Babel', 17 | {}, 18 | { 19 | envName: 'development', 20 | files: path.join(testDirectory, 'index.js'), 21 | outputPath, 22 | configFile: path.join(testDirectory, 'babel.config.json') 23 | } 24 | ) 25 | await task.run({ cwd: process.cwd(), command: 'build:local' }) 26 | const transpiled = await fs.readFile(path.join(outputPath, 'index.js'), 'utf8') 27 | expect(transpiled).toMatchInlineSnapshot(` 28 | ""use strict"; 29 | 30 | Object.defineProperty(exports, "__esModule", { 31 | value: true 32 | }); 33 | exports.test = test; 34 | function test(x) { 35 | return x !== null && x !== void 0 ? x : 0; 36 | }" 37 | `) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /plugins/babel/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "outDir": "lib", 6 | "rootDir": "src" 7 | }, 8 | "references": [ 9 | { 10 | "path": "../../lib/error" 11 | }, 12 | { 13 | "path": "../../lib/logger" 14 | }, 15 | { 16 | "path": "../../lib/base" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/backend-heroku-app/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/npm' 3 | - '@dotcom-tool-kit/circleci-deploy' 4 | - '@dotcom-tool-kit/heroku' 5 | - '@dotcom-tool-kit/node' 6 | 7 | commands: 8 | 'run:local': Node 9 | 'deploy:review': HerokuReview 10 | 'deploy:staging': HerokuStaging 11 | 'deploy:production': HerokuProduction 12 | 'teardown:staging': HerokuTeardown 13 | 14 | version: 2 15 | -------------------------------------------------------------------------------- /plugins/backend-heroku-app/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/backend-heroku-app/index.js -------------------------------------------------------------------------------- /plugins/backend-heroku-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/backend-heroku-app", 3 | "version": "4.1.17", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "FT.com Platforms Team ", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@dotcom-tool-kit/circleci-deploy": "^4.1.17", 11 | "@dotcom-tool-kit/heroku": "^4.2.5", 12 | "@dotcom-tool-kit/node": "^4.3.4", 13 | "@dotcom-tool-kit/npm": "^4.2.15" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 18 | "directory": "plugins/backend-heroku-app" 19 | }, 20 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 21 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/backend-heroku-app", 22 | "peerDependencies": { 23 | "dotcom-tool-kit": "4.x" 24 | }, 25 | "engines": { 26 | "node": "18.x || 20.x || 22.x" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /plugins/backend-heroku-app/readme.md: -------------------------------------------------------------------------------- 1 | # @dotcom-tool-kit/backend-heroku-app 2 | 3 | A bootstrap plugin that provides the minimum required Tool Kit plugins for a "backend" (aka an [API](https://github.com/Financial-Times/next/wiki/Naming-Conventions#apis)) that is deployed to Heroku. The plugins are: 4 | 5 | - [`@dotcom-tool-kit/npm`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/npm) 6 | - [`@dotcom-tool-kit/circleci-deploy`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/circleci-deploy) 7 | - [`@dotcom-tool-kit/heroku`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/heroku) 8 | - [`@dotcom-tool-kit/node`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/node) 9 | 10 | This bootstrap plugin is also preconfigured to run the `Node` task on the hook `run:local`, and binds the tasks defined by the `heroku` plugin to the hooks defined by `circleci-deploy`. 11 | 12 | ## Installation 13 | 14 | With Tool Kit [already set up](https://github.com/financial-times/dotcom-tool-kit#installing-and-using-tool-kit), install this plugin as a dev dependency: 15 | 16 | ```sh 17 | npm install --save-dev @dotcom-tool-kit/backend-heroku-app 18 | ``` 19 | 20 | And add it to your repo's `.toolkitrc.yml`: 21 | 22 | ```yaml 23 | plugins: 24 | - '@dotcom-tool-kit/backend-heroku-app' 25 | ``` 26 | -------------------------------------------------------------------------------- /plugins/backend-serverless-app/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/npm' 3 | - '@dotcom-tool-kit/circleci-deploy' 4 | - '@dotcom-tool-kit/serverless' 5 | - '@dotcom-tool-kit/node' 6 | 7 | commands: 8 | 'run:local': ServerlessRun 9 | 'deploy:review': ServerlessProvision 10 | 'deploy:production': ServerlessDeploy 11 | 'teardown:review': ServerlessTeardown 12 | 13 | version: 2 14 | -------------------------------------------------------------------------------- /plugins/backend-serverless-app/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/backend-serverless-app/index.js -------------------------------------------------------------------------------- /plugins/backend-serverless-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/backend-serverless-app", 3 | "version": "4.1.18", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "FT.com Platforms Team ", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@dotcom-tool-kit/circleci-deploy": "^4.1.17", 11 | "@dotcom-tool-kit/node": "^4.3.4", 12 | "@dotcom-tool-kit/npm": "^4.2.15", 13 | "@dotcom-tool-kit/serverless": "^3.4.3" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 18 | "directory": "plugins/backend-serverless-app" 19 | }, 20 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 21 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/backend-serverless-app", 22 | "peerDependencies": { 23 | "dotcom-tool-kit": "4.x" 24 | }, 25 | "engines": { 26 | "node": "18.x || 20.x || 22.x" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /plugins/backend-serverless-app/readme.md: -------------------------------------------------------------------------------- 1 | # @dotcom-tool-kit/backend-serverless-app 2 | 3 | A bootstrap plugin that provides the minimum required Tool Kit plugins for a "backend" (aka an [API](https://github.com/Financial-Times/next/wiki/Naming-Conventions#apis)) that is deployed to AWS. The plugins are: 4 | 5 | - [`@dotcom-tool-kit/npm`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/npm) 6 | - [`@dotcom-tool-kit/circleci-deploy`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/circleci-deploy) 7 | - [`@dotcom-tool-kit/serverless`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/serverless) 8 | - [`@dotcom-tool-kit/node`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/node) 9 | 10 | This bootstrap plugin is also preconfigured to run the `ServerlessRun` task on the hook `run:local`, and binds the tasks defined by the `serverless` plugin to the hooks defined by `circleci-deploy`. 11 | 12 | ## Installation 13 | 14 | With Tool Kit [already set up](https://github.com/financial-times/dotcom-tool-kit#installing-and-using-tool-kit), install this plugin as a dev dependency: 15 | 16 | ```sh 17 | npm install --save-dev @dotcom-tool-kit/backend-serverless-app 18 | ``` 19 | 20 | And add it to your repo's `.toolkitrc.yml`: 21 | 22 | ```yaml 23 | plugins: 24 | - '@dotcom-tool-kit/backend-serverless-app' 25 | ``` 26 | -------------------------------------------------------------------------------- /plugins/circleci-deploy/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/circleci-deploy/index.js -------------------------------------------------------------------------------- /plugins/circleci-deploy/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/circleci-deploy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/circleci-deploy", 3 | "version": "4.1.17", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/circleci-deploy" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/circleci": "^7.6.6", 14 | "tslib": "^2.8.1" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 19 | "directory": "plugins/circleci-deploy" 20 | }, 21 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 22 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/circleci-deploy", 23 | "files": [ 24 | "/lib", 25 | ".toolkitrc.yml" 26 | ], 27 | "volta": { 28 | "extends": "../../package.json" 29 | }, 30 | "devDependencies": { 31 | "winston": "^3.17.0" 32 | }, 33 | "peerDependencies": { 34 | "dotcom-tool-kit": "4.x" 35 | }, 36 | "engines": { 37 | "node": "18.x || 20.x || 22.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/circleci-deploy/test/circleci-deploy.test.ts: -------------------------------------------------------------------------------- 1 | import * as YAML from 'yaml' 2 | import path from 'path' 3 | import CircleCi from '@dotcom-tool-kit/circleci/lib/circleci-config' 4 | import winston, { Logger } from 'winston' 5 | import { loadConfig } from 'dotcom-tool-kit/lib/config' 6 | import { loadHookInstallations } from 'dotcom-tool-kit/lib/install' 7 | 8 | const logger = winston as unknown as Logger 9 | 10 | describe('circleci-deploy', () => { 11 | describe('config integration test', () => { 12 | it('should generate a .circleci/config.yml with the base config from circleci-deploy/.toolkitrc.yml', async () => { 13 | const config = await loadConfig(logger, { root: path.resolve(__dirname, '..') }) 14 | const hookInstallationsPromise = loadHookInstallations(logger, config).then((validated) => 15 | validated.unwrap('hooks were invalid') 16 | ) 17 | 18 | await expect(hookInstallationsPromise).resolves.toEqual([expect.any(CircleCi)]) 19 | 20 | const installation = (await hookInstallationsPromise)[0] 21 | 22 | expect(YAML.stringify(await installation.install())).toMatchSnapshot() 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /plugins/circleci-deploy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../circleci" 13 | } 14 | ], 15 | "include": [ 16 | "src/**/*" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /plugins/circleci-npm/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/circleci' 3 | - '@dotcom-tool-kit/npm' 4 | 5 | commands: 6 | 'publish:tag': NpmPublish 7 | 8 | options: 9 | hooks: 10 | - CircleCi: 11 | jobs: 12 | - name: publish-tag 13 | command: 'publish:tag' 14 | workflows: 15 | - name: 'tool-kit' 16 | jobs: 17 | - name: 'publish-tag' 18 | requires: 19 | - 'test' 20 | splitIntoMatrix: false 21 | custom: 22 | context: 'npm-publish-token' 23 | filters: 24 | branches: 25 | ignore: '/.*/' 26 | 27 | version: 2 28 | -------------------------------------------------------------------------------- /plugins/circleci-npm/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/circleci-npm/index.js -------------------------------------------------------------------------------- /plugins/circleci-npm/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/circleci-npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/circleci-npm", 3 | "version": "6.1.17", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/circleci-npm" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/circleci": "^7.6.6", 14 | "@dotcom-tool-kit/npm": "^4.2.15", 15 | "tslib": "^2.8.1" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 20 | "directory": "plugins/circleci-npm" 21 | }, 22 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 23 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/circleci-npm", 24 | "files": [ 25 | "/lib", 26 | ".toolkitrc.yml" 27 | ], 28 | "peerDependencies": { 29 | "dotcom-tool-kit": "4.x" 30 | }, 31 | "engines": { 32 | "node": "18.x || 20.x || 22.x" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plugins/circleci-npm/test/circleci-npm.test.ts: -------------------------------------------------------------------------------- 1 | import * as YAML from 'yaml' 2 | import path from 'path' 3 | import CircleCi from '@dotcom-tool-kit/circleci/lib/circleci-config' 4 | import winston, { Logger } from 'winston' 5 | import { loadConfig } from 'dotcom-tool-kit/lib/config' 6 | import { loadHookInstallations } from 'dotcom-tool-kit/lib/install' 7 | 8 | const logger = winston as unknown as Logger 9 | 10 | describe('circleci-npm', () => { 11 | describe('config integration test', () => { 12 | it('should generate a .circleci/config.yml with the base config from circleci-npm/.toolkitrc.yml', async () => { 13 | const config = await loadConfig(logger, { root: path.resolve(__dirname, '..') }) 14 | const hookInstallationsPromise = loadHookInstallations(logger, config).then((validated) => 15 | validated.unwrap('hooks were invalid') 16 | ) 17 | 18 | await expect(hookInstallationsPromise).resolves.toEqual(expect.arrayContaining([expect.any(CircleCi)])) 19 | 20 | const installation = (await hookInstallationsPromise)[0] 21 | 22 | expect(YAML.stringify(await installation.install())).toMatchSnapshot() 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /plugins/circleci-npm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../circleci" 13 | }, 14 | { 15 | "path": "../npm" 16 | } 17 | ], 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/circleci/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | installs: 2 | CircleCi: 3 | entryPoint: './lib/circleci-config' 4 | managesFiles: 5 | - '.circleci/config.yml' 6 | 7 | optionsSchema: './lib/schemas/plugin' 8 | 9 | options: 10 | hooks: 11 | - CircleCi: 12 | jobs: 13 | - name: build 14 | command: 'build:ci' 15 | - name: test 16 | command: 'test:ci' 17 | workflows: 18 | - name: 'tool-kit' 19 | jobs: 20 | - name: build 21 | requires: 22 | - 'setup' 23 | - name: test 24 | requires: 25 | - 'build' 26 | runOnRelease: true 27 | - name: 'nightly' 28 | jobs: 29 | - name: build 30 | requires: 31 | - 'setup' 32 | - name: test 33 | requires: 34 | - 'build' 35 | 36 | init: 37 | - './lib/init-env-vars' 38 | 39 | version: 2 40 | -------------------------------------------------------------------------------- /plugins/circleci/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/circleci/src/init-env-vars.ts: -------------------------------------------------------------------------------- 1 | import { writeState } from '@dotcom-tool-kit/state' 2 | import { Init } from '@dotcom-tool-kit/base' 3 | 4 | export default class CircleCIEnvVars extends Init { 5 | async init() { 6 | const envVars = { 7 | branch: process.env.CIRCLE_BRANCH, 8 | repo: process.env.CIRCLE_PROJECT_REPONAME, 9 | version: process.env.CIRCLE_SHA1, 10 | tag: process.env.CIRCLE_TAG, 11 | buildNumber: process.env.CIRCLE_BUILD_NUM 12 | } 13 | 14 | if (process.env.CIRCLECI) { 15 | this.logger.info(`writing circle ci environment variables to state...`) 16 | writeState('ci', envVars) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /plugins/circleci/test/files/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - @dotcom-tool-kit/circleci 3 | -------------------------------------------------------------------------------- /plugins/circleci/test/files/managed/without-job/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # CONFIG GENERATED BY DOTCOM-TOOL-KIT, DO NOT EDIT BY HAND 2 | jobs: {} 3 | -------------------------------------------------------------------------------- /plugins/circleci/test/files/managed/without-workflow-job/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # CONFIG GENERATED BY DOTCOM-TOOL-KIT, DO NOT EDIT BY HAND 2 | workflows: 3 | tool-kit: 4 | jobs: [] 5 | nightly: 6 | jobs: [] 7 | -------------------------------------------------------------------------------- /plugins/circleci/test/files/unmanaged/with-workflow-job/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | workflows: 2 | tool-kit: 3 | jobs: 4 | - tool-kit/that-job: 5 | requires: [] 6 | executor: node 7 | - tool-kit/test-job: 8 | requires: 9 | - tool-kit/that-job 10 | executor: node 11 | -------------------------------------------------------------------------------- /plugins/circleci/test/files/unmanaged/without-tool-kit/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | workflows: 2 | jobs: 3 | - not this job -------------------------------------------------------------------------------- /plugins/circleci/test/files/unmanaged/without-workflow-job/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | workflows: 2 | tool-kit: 3 | jobs: 4 | - not this job 5 | -------------------------------------------------------------------------------- /plugins/circleci/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../../lib/base" 6 | }, 7 | { 8 | "path": "../../lib/conflict" 9 | }, 10 | { 11 | "path": "../../lib/error" 12 | }, 13 | { 14 | "path": "../../lib/logger" 15 | }, 16 | { 17 | "path": "../../lib/plugin" 18 | }, 19 | { 20 | "path": "../../lib/state" 21 | } 22 | ], 23 | "compilerOptions": { 24 | "outDir": "lib", 25 | "rootDir": "src" 26 | }, 27 | "include": ["src/**/*"] 28 | } 29 | -------------------------------------------------------------------------------- /plugins/cloudsmith/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/cloudsmith", 3 | "version": "1.1.2", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "plugins/cloudsmith" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/cloudsmith", 19 | "files": [ 20 | "/lib", 21 | ".toolkitrc.yml" 22 | ], 23 | "engines": { 24 | "node": "18.x || 20.x", 25 | "npm": "7.x || 8.x || 9.x || 10.x" 26 | }, 27 | "peerDependencies": { 28 | "dotcom-tool-kit": "4.x" 29 | }, 30 | "dependencies": { 31 | "tslib": "^2.8.1", 32 | "zod": "^3.24.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plugins/cloudsmith/readme.md: -------------------------------------------------------------------------------- 1 | # @dotcom-tool-kit/cloudsmith 2 | 3 | ## Installation & Usage 4 | 5 | With Tool Kit [already set up](https://github.com/financial-times/dotcom-tool-kit#installing-and-using-tool-kit), install this plugin as a dev dependency: 6 | 7 | ```sh 8 | npm install --save-dev @dotcom-tool-kit/cloudsmith 9 | ``` 10 | 11 | And add it to your repo's `.toolkitrc.yml`: 12 | 13 | ```yml 14 | plugins: 15 | - '@dotcom-tool-kit/cloudsmith' 16 | ``` 17 | 18 | 19 | ## Plugin-wide options 20 | 21 | ### `@dotcom-tool-kit/cloudsmith` 22 | 23 | | Property | Description | Type | 24 | | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :------- | 25 | | `serviceAccount` | the Cloudsmith service account. this will probably be your team name followed by the permissions access, e.g., cp-reliability-read-write. | `string` | 26 | 27 | _All properties are optional._ 28 | 29 | -------------------------------------------------------------------------------- /plugins/cloudsmith/src/schema.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod' 2 | 3 | export default z.object({ 4 | serviceAccount: z 5 | .string() 6 | .optional() 7 | .describe( 8 | 'the Cloudsmith service account. this will probably be your team name followed by the permissions access, e.g., cp-reliability-read-write.' 9 | ) 10 | }) 11 | -------------------------------------------------------------------------------- /plugins/cloudsmith/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [], 4 | "compilerOptions": { 5 | "outDir": "lib", 6 | "rootDir": "src" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /plugins/commitlint/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | Commitlint: './lib/tasks/commitlint.js' 3 | 4 | commands: 5 | 'git:commitmsg': Commitlint 6 | 7 | version: 2 8 | -------------------------------------------------------------------------------- /plugins/commitlint/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/commitlint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/commitlint", 3 | "version": "1.2.12", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/commitlint" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/logger": "^4.2.2" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 19 | "directory": "plugins/commitlint" 20 | }, 21 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 22 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/commitlint", 23 | "files": [ 24 | "/lib", 25 | ".toolkitrc.yml" 26 | ], 27 | "volta": { 28 | "extends": "../../package.json" 29 | }, 30 | "peerDependencies": { 31 | "@commitlint/cli": ">=16.x <=19.x", 32 | "dotcom-tool-kit": "4.x" 33 | }, 34 | "engines": { 35 | "node": "18.x || 20.x || 22.x" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plugins/commitlint/readme.md: -------------------------------------------------------------------------------- 1 | # @dotcom-tool-kit/commitlint 2 | 3 | Tool Kit plugin to run [commitlint](https://commitlint.js.org/) 4 | 5 | ## Installation & Usage 6 | 7 | With Tool Kit [already set up](https://github.com/financial-times/dotcom-tool-kit#installing-and-using-tool-kit), install this plugin as a dev dependency: 8 | 9 | ```sh 10 | npm install --save-dev @dotcom-tool-kit/commitlint 11 | ``` 12 | 13 | And add it to your repo's `.toolkitrc.yml`: 14 | 15 | ```yml 16 | plugins: 17 | - '@dotcom-tool-kit/commitlint' 18 | ``` 19 | 20 | By default the plugin will run on the `git:commitmsg` command. There are no further configurations in Tool Kit, [use one of the config file locations that Commitlint supports](https://commitlint.js.org/reference/configuration). 21 | 22 | 23 | ## Tasks 24 | 25 | ### `Commitlint` 26 | 27 | Lint commit messages. 28 | 29 | -------------------------------------------------------------------------------- /plugins/commitlint/src/tasks/commitlint.ts: -------------------------------------------------------------------------------- 1 | import { fork } from 'child_process' 2 | import { hookFork, waitOnExit } from '@dotcom-tool-kit/logger' 3 | import { Task, TaskRunContext } from '@dotcom-tool-kit/base' 4 | 5 | const commitlintCLIPath = require.resolve('.bin/commitlint') 6 | 7 | export default class Commitlint extends Task { 8 | static description = 'Lint commit messages.' 9 | 10 | async run({ cwd }: TaskRunContext): Promise { 11 | const child = fork(commitlintCLIPath, ['--edit'], { silent: true, cwd }) 12 | hookFork(this.logger, 'commitlint', child) 13 | return waitOnExit('commitlint', child) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugins/commitlint/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../../lib/logger" 6 | }, 7 | { 8 | "path": "../../lib/base" 9 | } 10 | ], 11 | "include": ["src/**/*"], 12 | "compilerOptions": { 13 | "outDir": "lib", 14 | "rootDir": "src", 15 | "types": ["node"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plugins/component/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/npm' 3 | - '@dotcom-tool-kit/circleci-npm' 4 | 5 | version: 2 6 | -------------------------------------------------------------------------------- /plugins/component/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/component/index.js -------------------------------------------------------------------------------- /plugins/component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/component", 3 | "version": "5.1.17", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "FT.com Platforms Team ", 8 | "license": "ISC", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 12 | "directory": "plugins/component" 13 | }, 14 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 15 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/component", 16 | "files": [ 17 | ".toolkitrc.yml" 18 | ], 19 | "peerDependencies": { 20 | "dotcom-tool-kit": "4.x" 21 | }, 22 | "dependencies": { 23 | "@dotcom-tool-kit/circleci-npm": "^6.1.17", 24 | "@dotcom-tool-kit/npm": "^4.2.15" 25 | }, 26 | "engines": { 27 | "node": "18.x || 20.x || 22.x" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /plugins/component/readme.md: -------------------------------------------------------------------------------- 1 | # @dotcom-tool-kit/component 2 | 3 | A bootstrap plugin that provides the minimum required Tool Kit plugins for a "component" (aka an [npm module](https://github.com/Financial-Times/next/wiki/Naming-Conventions#npm-modules)). The plugins are: 4 | 5 | - [`@dotcom-tool-kit/npm`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/npm) 6 | - [`@dotcom-tool-kit/circleci-npm`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/circleci-npm) 7 | 8 | ## Installation 9 | 10 | With Tool Kit [already set up](https://github.com/financial-times/dotcom-tool-kit#installing-and-using-tool-kit), install this plugin as a dev dependency: 11 | 12 | ```sh 13 | npm install --save-dev @dotcom-tool-kit/component 14 | ``` 15 | 16 | And add it to your repo's `.toolkitrc.yml`: 17 | 18 | ```yaml 19 | plugins: 20 | - '@dotcom-tool-kit/component' 21 | ``` 22 | -------------------------------------------------------------------------------- /plugins/component/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | } 11 | ], 12 | "include": ["src/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /plugins/containerised-app-with-assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/containerised-app-with-assets", 3 | "version": "0.2.3", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1" 6 | }, 7 | "keywords": [], 8 | "author": "FT.com Platforms Team ", 9 | "license": "ISC", 10 | "description": "", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 14 | "directory": "plugins/containerised-app-with-assets" 15 | }, 16 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 17 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/containerised-app-with-assets", 18 | "files": [ 19 | ".toolkitrc.yml" 20 | ], 21 | "engines": { 22 | "node": "18.x || 20.x || 22.x" 23 | }, 24 | "peerDependencies": { 25 | "dotcom-tool-kit": "^4.8.0" 26 | }, 27 | "dependencies": { 28 | "@dotcom-tool-kit/containerised-app": "^0.2.3", 29 | "@dotcom-tool-kit/upload-assets-to-s3": "^4.3.6", 30 | "@dotcom-tool-kit/webpack": "^4.3.4", 31 | "zod": "^3.24.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /plugins/containerised-app/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/aws' 3 | - '@dotcom-tool-kit/circleci-deploy' 4 | - '@dotcom-tool-kit/cloudsmith' 5 | - '@dotcom-tool-kit/docker' 6 | - '@dotcom-tool-kit/doppler' 7 | - '@dotcom-tool-kit/hako' 8 | - '@dotcom-tool-kit/node' 9 | 10 | commands: 11 | 'run:local': 12 | - Node 13 | 'deploy:review': 14 | - DockerAuthCloudsmith 15 | - DockerBuild 16 | - DockerPush 17 | - AwsAssumeRole: 18 | roleArn: !toolkit/option '@dotcom-tool-kit/containerised-app.awsRoleArnStaging' 19 | - HakoDeploy: 20 | asReviewApp: true 21 | environments: !toolkit/option '@dotcom-tool-kit/containerised-app.hakoReviewEnvironments' 22 | 'deploy:staging': 23 | - DockerAuthCloudsmith 24 | - DockerBuild 25 | - DockerPush 26 | - AwsAssumeRole: 27 | roleArn: !toolkit/option '@dotcom-tool-kit/containerised-app.awsRoleArnStaging' 28 | - HakoDeploy: 29 | environments: !toolkit/option '@dotcom-tool-kit/containerised-app.hakoStagingEnvironments' 30 | 'deploy:production': 31 | - DockerAuthCloudsmith 32 | - AwsAssumeRole: 33 | roleArn: !toolkit/option '@dotcom-tool-kit/containerised-app.awsRoleArnProduction' 34 | - HakoDeploy: 35 | environments: !toolkit/option '@dotcom-tool-kit/containerised-app.hakoProductionEnvironments' 36 | 37 | optionsSchema: ./schema 38 | version: 2 39 | -------------------------------------------------------------------------------- /plugins/containerised-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/containerised-app", 3 | "version": "0.2.3", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1" 6 | }, 7 | "keywords": [], 8 | "author": "FT.com Platforms Team ", 9 | "license": "ISC", 10 | "description": "", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 14 | "directory": "plugins/containerised-app" 15 | }, 16 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 17 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/containerised-app", 18 | "files": [ 19 | ".toolkitrc.yml", 20 | "schema.js" 21 | ], 22 | "engines": { 23 | "node": "18.x || 20.x || 22.x" 24 | }, 25 | "peerDependencies": { 26 | "dotcom-tool-kit": "^4.8.0" 27 | }, 28 | "dependencies": { 29 | "@dotcom-tool-kit/aws": "^0.1.10", 30 | "@dotcom-tool-kit/circleci-deploy": "^4.1.17", 31 | "@dotcom-tool-kit/cloudsmith": "^1.1.2", 32 | "@dotcom-tool-kit/docker": "^0.4.4", 33 | "@dotcom-tool-kit/doppler": "^2.2.3", 34 | "@dotcom-tool-kit/hako": "^0.1.15", 35 | "@dotcom-tool-kit/logger": "^4.2.2", 36 | "@dotcom-tool-kit/node": "^4.3.4", 37 | "zod": "^3.24.4" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/cypress/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/package-json-hook' 3 | 4 | tasks: 5 | Cypress: './lib/tasks/cypress' 6 | 7 | commands: 8 | 'test:review': Cypress 9 | 'test:staging': Cypress 10 | 'e2e:local': Cypress 11 | 12 | options: 13 | hooks: 14 | - PackageJson: 15 | scripts: 16 | e2e-test: 'e2e:local' 17 | 18 | version: 2 19 | -------------------------------------------------------------------------------- /plugins/cypress/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/cypress", 3 | "version": "5.3.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "plugins/cypress" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/cypress", 19 | "files": [ 20 | "/lib", 21 | ".toolkitrc.yml" 22 | ], 23 | "peerDependencies": { 24 | "dotcom-tool-kit": "4.x" 25 | }, 26 | "engines": { 27 | "node": "18.x || 20.x || 22.x" 28 | }, 29 | "dependencies": { 30 | "@dotcom-tool-kit/base": "^1.2.3", 31 | "@dotcom-tool-kit/doppler": "^2.2.3", 32 | "@dotcom-tool-kit/logger": "^4.2.2", 33 | "@dotcom-tool-kit/package-json-hook": "^5.2.4", 34 | "@dotcom-tool-kit/state": "^4.3.2", 35 | "zod": "^3.24.4" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plugins/cypress/src/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/cypress/src/index.ts -------------------------------------------------------------------------------- /plugins/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../../lib/logger" 13 | }, 14 | { 15 | "path": "../../lib/state" 16 | }, 17 | { 18 | "path": "../../lib/doppler" 19 | } 20 | ], 21 | "include": ["src/**/*"] 22 | } 23 | -------------------------------------------------------------------------------- /plugins/docker/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | DockerAuthCloudsmith: './lib/tasks/auth-cloudsmith' 3 | DockerBuild: './lib/tasks/build' 4 | DockerPush: './lib/tasks/push' 5 | 6 | optionsSchema: './lib/schema' 7 | 8 | version: 2 9 | -------------------------------------------------------------------------------- /plugins/docker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/docker", 3 | "version": "0.4.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/error": "^4.1.1", 15 | "@dotcom-tool-kit/logger": "^4.2.2", 16 | "@dotcom-tool-kit/state": "^4.3.2", 17 | "tslib": "^2.8.1", 18 | "zod": "^3.24.4" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 23 | "directory": "plugins/docker" 24 | }, 25 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 26 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/docker", 27 | "files": [ 28 | "/lib", 29 | ".toolkitrc.yml" 30 | ], 31 | "engines": { 32 | "node": "18.x || 20.x || 22.x" 33 | }, 34 | "peerDependencies": { 35 | "dotcom-tool-kit": "4.x" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plugins/docker/src/schema.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod' 2 | 3 | const DockerImageSchema = z.object({ 4 | definition: z 5 | .string() 6 | .default('Dockerfile') 7 | .describe('The path to the Dockerfile definition file for this image'), 8 | platform: z 9 | .string() 10 | .default('linux/arm64') 11 | .describe('The platform to target when building the Docker image, e.g. linux/arm64 or linux/amd64'), 12 | registry: z 13 | .string() 14 | .default('docker.packages.ft.com/cp-container-registry') 15 | .describe('The registry that the Docker image is pushed to, excluding name and tag'), 16 | name: z 17 | .string() 18 | .regex(/^[a-z][a-z-]+$/, 'Image name must only contain lowercase alphabetic characters and hyphens') 19 | .describe('The name of the image, excluding a tag') 20 | }) 21 | 22 | export default z.object({ 23 | images: z.record(z.string(), DockerImageSchema) 24 | }) 25 | -------------------------------------------------------------------------------- /plugins/docker/src/tasks/auth-cloudsmith.ts: -------------------------------------------------------------------------------- 1 | import { hookFork, waitOnExit } from '@dotcom-tool-kit/logger' 2 | import { spawn } from 'node:child_process' 3 | import { Task } from '@dotcom-tool-kit/base' 4 | import { ToolKitError } from '@dotcom-tool-kit/error' 5 | 6 | export default class DockerAuthCloudsmith extends Task { 7 | async run() { 8 | try { 9 | const auth = { 10 | username: process.env.CLOUDSMITH_SERVICE_ACCOUNT, 11 | password: process.env.CLOUDSMITH_OIDC_TOKEN, 12 | registry: 'docker.packages.ft.com' 13 | } 14 | 15 | if (!auth.username || !auth.password) { 16 | throw new Error( 17 | 'No CloudSmith service account found. Install @dotcom-tool-kit/cloudsmith and use the serviceAccount option' 18 | ) 19 | } 20 | 21 | const child = spawn('docker', ['login', '--username', auth.username, '--password-stdin', auth.registry]) 22 | child.stdin.setDefaultEncoding('utf-8') 23 | child.stdin.write(auth.password) 24 | child.stdin.end() 25 | hookFork(this.logger, 'docker-login', child) 26 | await waitOnExit('docker-login', child) 27 | } catch (err) { 28 | // We wrap non-ToolKitError errors to ensure that they exit the process 29 | if (err instanceof Error && !(err instanceof ToolKitError)) { 30 | const error = new ToolKitError(err.message) 31 | error.exitCode = 1 32 | throw error 33 | } 34 | throw err 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plugins/docker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/error" 10 | }, 11 | { 12 | "path": "../../lib/base" 13 | }, 14 | { 15 | "path": "../../lib/logger" 16 | }, 17 | { 18 | "path": "../../lib/state" 19 | } 20 | ], 21 | "include": ["src/**/*"] 22 | } 23 | -------------------------------------------------------------------------------- /plugins/eslint/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | Eslint: './lib/tasks/eslint' 3 | 4 | commands: 5 | 'test:local': Eslint 6 | 'test:ci': Eslint 7 | 'test:staged': Eslint 8 | 9 | version: 2 10 | -------------------------------------------------------------------------------- /plugins/eslint/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/eslint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/eslint", 3 | "version": "4.3.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/eslint" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/error": "^4.1.1", 15 | "@dotcom-tool-kit/logger": "^4.2.2", 16 | "tslib": "^2.8.1", 17 | "zod": "^3.24.4" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 22 | "directory": "plugins/eslint" 23 | }, 24 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 25 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/eslint", 26 | "devDependencies": { 27 | "@jest/globals": "^29.7.0", 28 | "@types/eslint": "^7.2.13", 29 | "@types/temp": "^0.9.4", 30 | "eslint": "^8.15.0", 31 | "temp": "^0.9.4", 32 | "winston": "^3.17.0" 33 | }, 34 | "files": [ 35 | "/lib", 36 | ".toolkitrc.yml" 37 | ], 38 | "volta": { 39 | "extends": "../../package.json" 40 | }, 41 | "peerDependencies": { 42 | "dotcom-tool-kit": "4.x", 43 | "eslint": "7.x || 8.x" 44 | }, 45 | "engines": { 46 | "node": "18.x || 20.x || 22.x" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /plugins/eslint/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "outDir": "lib", 6 | "rootDir": "src" 7 | }, 8 | "references": [ 9 | { 10 | "path": "../../lib/error" 11 | }, 12 | { 13 | "path": "../../lib/logger" 14 | }, 15 | { 16 | "path": "../../lib/base" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/frontend-app/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/webpack' 3 | - '@dotcom-tool-kit/backend-heroku-app' 4 | - '@dotcom-tool-kit/upload-assets-to-s3' 5 | 6 | commands: 7 | 'run:local': 8 | - Webpack: 9 | envName: development # run a webpack compile before starting the server because dotcom-server-asset-loader expects a manifest to exist 10 | - Node 11 | - Webpack: 12 | envName: development 13 | watch: true 14 | 15 | version: 2 16 | -------------------------------------------------------------------------------- /plugins/frontend-app/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/frontend-app/index.js -------------------------------------------------------------------------------- /plugins/frontend-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/frontend-app", 3 | "version": "4.1.17", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "FT.com Platforms Team ", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@dotcom-tool-kit/backend-heroku-app": "^4.1.17", 11 | "@dotcom-tool-kit/upload-assets-to-s3": "^4.3.6", 12 | "@dotcom-tool-kit/webpack": "^4.3.4" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 17 | "directory": "plugins/frontend-app" 18 | }, 19 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 20 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/frontend-app", 21 | "volta": { 22 | "extends": "../../package.json" 23 | }, 24 | "peerDependencies": { 25 | "dotcom-tool-kit": "4.x" 26 | }, 27 | "engines": { 28 | "node": "18.x || 20.x || 22.x" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /plugins/frontend-app/readme.md: -------------------------------------------------------------------------------- 1 | # @dotcom-tool-kit/frontend-app 2 | 3 | A bootstrap plugin that provides the minimum required Tool Kit plugins for a "frontend" (aka an [App or Page](https://github.com/Financial-Times/next/wiki/Naming-Conventions#apps)). The plugins are: 4 | 5 | - [`@dotcom-tool-kit/webpack`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/webpack) 6 | - [`@dotcom-tool-kit/backend-heroku-app`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/backend-heroku-app) 7 | - [`@dotcom-tool-kit/upload-assets-to-s3`](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/upload-assets-to-s3) 8 | 9 | This bootstrap plugin is also preconfigured to run the `Node`, `WebpackDevelopment`, and `WebpackWatch` tasks on the hook `run:local`. 10 | 11 | ## Installation 12 | 13 | With Tool Kit [already set up](https://github.com/financial-times/dotcom-tool-kit#installing-and-using-tool-kit), install this plugin as a dev dependency: 14 | 15 | ```sh 16 | npm install --save-dev @dotcom-tool-kit/frontend-app 17 | ``` 18 | 19 | And add it to your repo's `.toolkitrc.yml`: 20 | 21 | ```yaml 22 | plugins: 23 | - '@dotcom-tool-kit/frontend-app' 24 | ``` 25 | -------------------------------------------------------------------------------- /plugins/hako/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | tasks: 4 | HakoDeploy: './lib/tasks/deploy' 5 | HakoDelete: './lib/tasks/delete' 6 | 7 | -------------------------------------------------------------------------------- /plugins/hako/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/hako", 3 | "version": "0.1.15", 4 | "main": "lib", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "FT.com Platforms Team ", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dotcom-tool-kit/base": "^1.2.3", 13 | "@dotcom-tool-kit/error": "^4.1.1", 14 | "@dotcom-tool-kit/logger": "^4.2.2", 15 | "@dotcom-tool-kit/state": "^4.3.2", 16 | "tslib": "^2.8.1", 17 | "zod": "^3.24.4" 18 | }, 19 | "description": "", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 23 | "directory": "plugins/hako" 24 | }, 25 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 26 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/hako", 27 | "files": [ 28 | "/lib", 29 | ".toolkitrc.yml" 30 | ], 31 | "engines": { 32 | "node": "18.x || 20.x || 22.x" 33 | }, 34 | "peerDependencies": { 35 | "dotcom-tool-kit": "4.x" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plugins/hako/src/hako.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod' 2 | 3 | export const hakoImageName = 'docker.packages.ft.com/financial-times-internal-releases/hako-cli:0.2.7-beta' 4 | 5 | export const HakoEnvironmentName = z.string().transform((val, ctx) => { 6 | const match = val.match(/-(prod|test)-(eu|us)$/) 7 | if (!match) { 8 | ctx.addIssue({ 9 | code: z.ZodIssueCode.invalid_string, 10 | validation: 'regex', 11 | message: 'Hako environment name must end with a stage and region, e.g., -prod-eu' 12 | }) 13 | return z.NEVER 14 | } 15 | return { 16 | name: val, 17 | stage: match[1], 18 | region: match[2] 19 | } 20 | }) 21 | export type HakoEnvironment = z.output 22 | 23 | export const hakoRegions: Record = { 24 | eu: 'eu-west-1', 25 | us: 'us-east-1' 26 | } 27 | export const hakoDomains: Record = { 28 | prod: 'ft-com-prod.ftweb.tech', 29 | test: 'ft-com-test.ftweb.tech' 30 | } 31 | -------------------------------------------------------------------------------- /plugins/hako/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/error" 10 | }, 11 | { 12 | "path": "../../lib/base" 13 | }, 14 | { 15 | "path": "../../lib/logger" 16 | }, 17 | { 18 | "path": "../../lib/state" 19 | } 20 | ], 21 | "include": ["src/**/*"] 22 | } 23 | -------------------------------------------------------------------------------- /plugins/heroku/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/package-json-hook' 3 | - '@dotcom-tool-kit/npm' 4 | - '@dotcom-tool-kit/doppler' # required so the create script knows we need its options 5 | 6 | tasks: 7 | HerokuProduction: './lib/tasks/production' 8 | HerokuStaging: './lib/tasks/staging' 9 | HerokuReview: './lib/tasks/review' 10 | HerokuTeardown: './lib/tasks/teardown' 11 | 12 | commands: 13 | 'cleanup:remote': NpmPrune 14 | 15 | optionsSchema: './lib/schema' 16 | 17 | options: 18 | hooks: 19 | - PackageJson: 20 | scripts: 21 | heroku-postbuild: 22 | - 'build:remote' 23 | - 'release:remote' 24 | - 'cleanup:remote' 25 | 26 | version: 2 27 | -------------------------------------------------------------------------------- /plugins/heroku/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/heroku/src/createBuild.ts: -------------------------------------------------------------------------------- 1 | import type { Logger } from 'winston' 2 | import heroku, { extractHerokuError } from './herokuClient' 3 | import type { HerokuApiResBuild } from 'heroku-client' 4 | import { getRepoDetails } from './githubApi' 5 | 6 | async function createBuild(logger: Logger, appName: string): Promise { 7 | logger.info(`getting latest tarball path for ${appName}...`) 8 | const { branch, source_blob } = await getRepoDetails(logger) 9 | logger.info(`creating new build for ${appName} from ${branch || source_blob.version}...`) 10 | const buildInfo = await heroku 11 | .post(`/apps/${appName}/builds`, { 12 | body: { 13 | source_blob: { 14 | ...source_blob, 15 | checksum: null 16 | } 17 | } 18 | }) 19 | .catch(extractHerokuError(`creating new build for app ${appName}`)) 20 | return buildInfo 21 | } 22 | 23 | export { createBuild } 24 | -------------------------------------------------------------------------------- /plugins/heroku/src/getHerokuReviewApp.ts: -------------------------------------------------------------------------------- 1 | import heroku, { extractHerokuError } from './herokuClient' 2 | import type { HerokuApiResGetReview } from 'heroku-client' 3 | import type { Logger } from 'winston' 4 | import { readState } from '@dotcom-tool-kit/state' 5 | import { ToolKitError } from '@dotcom-tool-kit/error' 6 | import { repeatedCheckForSuccessStatus } from './repeatedCheckForSuccessStatus' 7 | 8 | async function getHerokuReviewApp(logger: Logger, pipelineId: string): Promise { 9 | const state = readState('ci') 10 | 11 | if (!state) { 12 | throw new ToolKitError('could not find CI') //TODO - remidating actions? 13 | } 14 | 15 | const branch = state.branch 16 | const reviewApps = await heroku 17 | .get(`/pipelines/${pipelineId}/review-apps`) 18 | .catch(extractHerokuError(`getting review apps for pipeline ${pipelineId}`)) 19 | const reviewApp = reviewApps.find( 20 | (instance: { 21 | id: string 22 | app: { 23 | id: string 24 | } 25 | branch: string 26 | status: string 27 | }): boolean => { 28 | return instance.branch === branch && (instance.status === 'created' || instance.status === 'creating') 29 | } 30 | ) 31 | 32 | if (reviewApp?.status === 'creating') { 33 | await repeatedCheckForSuccessStatus(logger, reviewApp.id) 34 | } 35 | 36 | return reviewApp?.app.id 37 | } 38 | 39 | export { getHerokuReviewApp } 40 | -------------------------------------------------------------------------------- /plugins/heroku/src/getHerokuStagingApp.ts: -------------------------------------------------------------------------------- 1 | import { readState, writeState } from '@dotcom-tool-kit/state' 2 | import { ToolKitError } from '@dotcom-tool-kit/error' 3 | import heroku, { extractHerokuError } from './herokuClient' 4 | import type { HerokuApiResGetStaging } from 'heroku-client' 5 | 6 | async function getHerokuStagingApp(): Promise { 7 | const stagingState = readState('staging') 8 | 9 | if (!stagingState) { 10 | throw new ToolKitError('could not find state information for staging') 11 | } 12 | 13 | const appId = stagingState.appIds[0] 14 | const { name: appName } = await heroku 15 | .get(`/apps/${appId}`) 16 | .catch(extractHerokuError(`getting name for app ${appId}`)) 17 | writeState('staging', { 18 | appName 19 | }) 20 | return appName 21 | } 22 | export { getHerokuStagingApp } 23 | -------------------------------------------------------------------------------- /plugins/heroku/src/gtg.ts: -------------------------------------------------------------------------------- 1 | import heroku, { extractHerokuError } from './herokuClient' 2 | import type { HerokuApiResGetGtg } from 'heroku-client' 3 | import type { Logger } from 'winston' 4 | import { ToolKitError } from '@dotcom-tool-kit/error' 5 | import { waitForOk } from '@dotcom-tool-kit/wait-for-ok' 6 | import { State, writeState } from '@dotcom-tool-kit/state' 7 | 8 | async function gtg(logger: Logger, appIdOrName: string, environment: keyof State): Promise { 9 | const appDetails = await heroku 10 | .get(`/apps/${appIdOrName}`) 11 | .catch(extractHerokuError(`getting details for app ${appIdOrName}`)) 12 | // save name to state file so we don't need to translate app ID again 13 | writeState(environment, { 14 | appName: appDetails.name, 15 | url: appDetails.web_url ?? undefined 16 | }) 17 | 18 | if (!appDetails.web_url) { 19 | const error = new ToolKitError(`app ${appIdOrName} has no web URL associated with it`) 20 | error.details = 21 | 'please send #cp-platforms-team the name/ID of the app so we can understand why this would happen' 22 | throw error 23 | } 24 | 25 | const gtgUrl = new URL(appDetails.web_url) 26 | gtgUrl.pathname = '/__gtg' 27 | return waitForOk(logger, gtgUrl.href) 28 | } 29 | 30 | export { gtg } 31 | -------------------------------------------------------------------------------- /plugins/heroku/src/herokuClient.ts: -------------------------------------------------------------------------------- 1 | import { ToolKitError } from '@dotcom-tool-kit/error' 2 | import { styles } from '@dotcom-tool-kit/logger' 3 | import Heroku, { HerokuError } from 'heroku-client' 4 | 5 | const HEROKU_AUTH_TOKEN = process.env.HEROKU_AUTH_TOKEN 6 | 7 | export default new Heroku({ token: HEROKU_AUTH_TOKEN }) 8 | 9 | // the Heroku client doesn't give particularly useful error messages, so we require an additional context 10 | // string when handling errors coming from it 11 | export function extractHerokuError(context: string): (err: unknown) => never { 12 | return (err) => { 13 | if ((err as HerokuError).statusCode) { 14 | const upstream = err as HerokuError 15 | const error = new ToolKitError('there was an error calling the Heroku API') 16 | error.details = `received the error ${styles.errorHighlight(upstream.message)} when ${context}` 17 | if (upstream.body) { 18 | error.details += `\n\nresponse from Heroku was:\n${styles.errorHighlight(upstream.body.message)}` 19 | if (upstream.body.url) { 20 | error.details += `\nfurther information available at ${styles.URL(upstream.body.url)}` 21 | } 22 | } 23 | throw error 24 | } 25 | throw err 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /plugins/heroku/src/promoteStagingToProduction.ts: -------------------------------------------------------------------------------- 1 | import heroku, { extractHerokuError } from './herokuClient' 2 | import { ToolKitError } from '@dotcom-tool-kit/error' 3 | import { readState } from '@dotcom-tool-kit/state' 4 | import { gtg } from './gtg' 5 | import type { Logger } from 'winston' 6 | import { HerokuApiResPost } from 'heroku-client' 7 | 8 | async function promoteStagingToProduction(logger: Logger, slug: string): Promise { 9 | const state = readState(`production`) 10 | 11 | if (!state) { 12 | throw new ToolKitError('Could not find production state information') 13 | } 14 | 15 | const appIds = state.appIds 16 | 17 | logger.info(`updating slug id ${slug} on production app ${appIds}`) 18 | 19 | const latestRelease = appIds.map((appId) => 20 | heroku 21 | .post(`/apps/${appId}/releases`, { 22 | body: { 23 | slug 24 | } 25 | }) 26 | .catch(extractHerokuError(`promoting app ID ${appId} to production`)) 27 | .then((response) => gtg(logger, response.app.name, 'production')) 28 | ) 29 | 30 | return Promise.all(latestRelease) 31 | } 32 | 33 | export { promoteStagingToProduction } 34 | -------------------------------------------------------------------------------- /plugins/heroku/src/repeatedCheckForBuildSuccess.ts: -------------------------------------------------------------------------------- 1 | import pRetry from 'p-retry' 2 | import heroku, { extractHerokuError } from './herokuClient' 3 | import type { HerokuApiResBuild } from 'heroku-client' 4 | import type { Logger } from 'winston' 5 | 6 | const NUM_RETRIES = process.env.HEROKU_BUILD_NUM_RETRIES 7 | ? parseInt(process.env.HEROKU_BUILD_NUM_RETRIES, 10) 8 | : 60 9 | 10 | async function repeatedCheckForBuildSuccess( 11 | logger: Logger, 12 | appName: string, 13 | buildId: string 14 | ): Promise { 15 | async function checkForSuccessStatus() { 16 | const buildInfo = await heroku 17 | .get(`/apps/${appName}/builds/${buildId}`) 18 | .catch(extractHerokuError(`getting info for build ${buildId} for app ${appName}`)) 19 | logger.debug(`build status: ${buildInfo.status}`) 20 | if (buildInfo.status !== 'succeeded' || buildInfo.slug === null) { 21 | throw new Error(`Build for app ${appName} not yet finished`) 22 | } 23 | return buildInfo.slug.id 24 | } 25 | 26 | const result = await pRetry(checkForSuccessStatus, { 27 | onFailedAttempt: (error) => { 28 | const { attemptNumber, retriesLeft } = error 29 | logger.warn(`attempt ${attemptNumber} failed. There are ${retriesLeft} retries left.`) 30 | }, 31 | factor: 1, 32 | retries: NUM_RETRIES, 33 | minTimeout: 10 * 1000 34 | }) 35 | 36 | return result 37 | } 38 | 39 | export { repeatedCheckForBuildSuccess } 40 | -------------------------------------------------------------------------------- /plugins/heroku/src/repeatedCheckForSuccessStatus.ts: -------------------------------------------------------------------------------- 1 | import pRetry from 'p-retry' 2 | import heroku, { extractHerokuError } from './herokuClient' 3 | import type { HerokuApiResGetReview } from 'heroku-client' 4 | import type { Logger } from 'winston' 5 | 6 | const NUM_RETRIES = process.env.HEROKU_REVIEW_APP_NUM_RETRIES 7 | ? parseInt(process.env.HEROKU_REVIEW_APP_NUM_RETRIES, 10) 8 | : 60 9 | 10 | async function repeatedCheckForSuccessStatus(logger: Logger, reviewAppId: string): Promise { 11 | async function checkForSuccessStatus() { 12 | logger.debug(`review app id: ${reviewAppId}`) 13 | const reviewApp = await heroku 14 | .get(`/review-apps/${reviewAppId}`) 15 | .catch(extractHerokuError(`getting review app ${reviewAppId}`)) 16 | logger.debug(`review app status: ${reviewApp.status}`) 17 | if (reviewApp.status === 'deleted') throw new pRetry.AbortError(`Review app was deleted`) 18 | if (reviewApp.status !== 'created') { 19 | throw new Error(`App build for app id: ${reviewAppId} not yet finished`) 20 | } 21 | return true 22 | } 23 | 24 | await pRetry(checkForSuccessStatus, { 25 | onFailedAttempt: (error) => { 26 | const { attemptNumber, retriesLeft } = error 27 | logger.warn(`attempt ${attemptNumber} failed. There are ${retriesLeft} retries left.`) 28 | }, 29 | factor: 1, 30 | retries: NUM_RETRIES, 31 | minTimeout: 10 * 1000 32 | }) 33 | } 34 | 35 | export { repeatedCheckForSuccessStatus } 36 | -------------------------------------------------------------------------------- /plugins/heroku/src/scaleDyno.ts: -------------------------------------------------------------------------------- 1 | import heroku, { extractHerokuError } from './herokuClient' 2 | import { ToolKitError } from '@dotcom-tool-kit/error' 3 | import type { HerokuApiResPatch } from 'heroku-client' 4 | import type { Logger } from 'winston' 5 | 6 | async function scaleDyno( 7 | logger: Logger, 8 | appName: string, 9 | quantity: number, 10 | type = 'web', 11 | size?: string 12 | ): Promise { 13 | logger.info(`scaling ${type} dyno for ${appName}...`) 14 | const appFormation = await heroku 15 | .patch(`/apps/${appName}/formation`, { 16 | body: { 17 | updates: [ 18 | { 19 | quantity, 20 | size, 21 | type 22 | } 23 | ] 24 | } 25 | }) 26 | .catch(extractHerokuError(`updating scaling for app ${appName}`)) 27 | if (appFormation.some((formation) => formation.type === type && formation.quantity === quantity)) { 28 | return 29 | } else { 30 | throw new ToolKitError(`something went wrong with scaling the dyno`) 31 | } 32 | } 33 | 34 | export { scaleDyno } 35 | -------------------------------------------------------------------------------- /plugins/heroku/src/schema.ts: -------------------------------------------------------------------------------- 1 | import { styles } from '@dotcom-tool-kit/logger' 2 | import * as z from 'zod' 3 | 4 | export default z 5 | .object({ 6 | pipeline: z 7 | .string() 8 | .describe( 9 | "the ID of your app's Heroku pipeline. this can be found at https://dashboard.heroku.com/pipelines/[PIPELINE_ID]" 10 | ) 11 | }) 12 | .passthrough() 13 | .refine((options) => !('scaling' in options), { 14 | message: `the option ${styles.code('scaling')} has moved to ${styles.code( 15 | `options.tasks.${styles.task('HerokuProduction')}.scaling` 16 | )}` 17 | }) 18 | -------------------------------------------------------------------------------- /plugins/heroku/src/setStagingSlug.ts: -------------------------------------------------------------------------------- 1 | import type { Logger } from 'winston' 2 | import heroku, { extractHerokuError } from './herokuClient' 3 | import { ToolKitError } from '@dotcom-tool-kit/error' 4 | import { HerokuApiResPost } from 'heroku-client' 5 | import { writeState } from '@dotcom-tool-kit/state' 6 | 7 | async function setStagingSlug(logger: Logger, appName: string, slug: string): Promise { 8 | try { 9 | logger.info(`updating slug id ${slug} on ${appName}`) 10 | 11 | const { 12 | slug: { id: slugId } 13 | } = await heroku 14 | .post(`/apps/${appName}/releases`, { 15 | body: { 16 | slug 17 | } 18 | }) 19 | .catch(extractHerokuError(`setting slug for app ${appName}`)) 20 | writeState('staging', { 21 | slugId 22 | }) 23 | } catch (err) { 24 | const error = new ToolKitError(`there was an error with setting the slug on ${appName}`) 25 | if (err instanceof Error) { 26 | error.details = err.message 27 | } 28 | throw error 29 | } 30 | } 31 | 32 | export { setStagingSlug } 33 | -------------------------------------------------------------------------------- /plugins/heroku/src/tasks/teardown.ts: -------------------------------------------------------------------------------- 1 | import { Task } from '@dotcom-tool-kit/base' 2 | import { readState } from '@dotcom-tool-kit/state' 3 | import { styles } from '@dotcom-tool-kit/logger' 4 | import { scaleDyno } from '../scaleDyno' 5 | import { ToolKitError } from '@dotcom-tool-kit/error' 6 | 7 | export default class HerokuTeardown extends Task { 8 | static description = "Scale down the Heroku staging app once it's no longer needed." 9 | 10 | async run(): Promise { 11 | //scale down staging 12 | const state = readState('staging') 13 | 14 | if (!state || !state.appName) { 15 | throw new ToolKitError( 16 | `Could not find state for staging, check that ${styles.hook('deploy:staging')} ran successfully` 17 | ) 18 | } 19 | 20 | const appName = state.appName 21 | 22 | try { 23 | await scaleDyno(this.logger, appName, 0) 24 | } catch { 25 | throw new ToolKitError(`Unable to scale down dyno for ${styles.app(appName)}`) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /plugins/heroku/test/repeatedCheckForBuildSuccess.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, jest } from '@jest/globals' 2 | import { repeatedCheckForBuildSuccess } from '../src/repeatedCheckForBuildSuccess' 3 | import heroku from '../src/herokuClient' 4 | import winston, { Logger } from 'winston' 5 | 6 | const logger = winston as unknown as Logger 7 | 8 | const appName = 'test-app-name' 9 | const buildId = 'test-build-id' 10 | 11 | const buildInfo = { 12 | id: 'test-build-id', 13 | status: 'succeeded', 14 | slug: { 15 | id: 'test-slug-id' 16 | } 17 | } 18 | 19 | const mockHerokuGet = jest.spyOn(heroku, 'get') 20 | 21 | describe('repeatedCheckForBuildSuccess', () => { 22 | it('calls heroku api with the app name', async () => { 23 | mockHerokuGet.mockImplementationOnce(async () => Promise.resolve(buildInfo)) 24 | 25 | await repeatedCheckForBuildSuccess(logger, appName, buildId) 26 | 27 | expect(heroku.get).toHaveBeenCalledWith(`/apps/${appName}/builds/${buildId}`) 28 | }) 29 | 30 | it('throws an error if unsuccessful', async () => { 31 | mockHerokuGet.mockImplementationOnce(async () => Promise.reject()) 32 | 33 | await expect(repeatedCheckForBuildSuccess(logger, appName, buildId)).rejects.toThrowError() 34 | }) 35 | 36 | it('returns slug id if the build is successful', async () => { 37 | mockHerokuGet.mockImplementationOnce(async () => Promise.resolve(buildInfo)) 38 | 39 | await expect(repeatedCheckForBuildSuccess(logger, appName, buildId)).resolves.toEqual(buildInfo.slug.id) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /plugins/heroku/test/repeatedCheckForSuccessStatus.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, jest } from '@jest/globals' 2 | import { repeatedCheckForSuccessStatus } from '../src/repeatedCheckForSuccessStatus' 3 | import heroku from '../src/herokuClient' 4 | import winston, { Logger } from 'winston' 5 | 6 | const logger = winston as unknown as Logger 7 | 8 | const reviewAppId = 'review-app-id' 9 | 10 | const reviewApp = { 11 | id: 'test-id', 12 | branch: 'test-branch', 13 | status: 'created', 14 | app: { 15 | id: reviewAppId 16 | } 17 | } 18 | 19 | jest.mock('../src/herokuClient', () => { 20 | const originalModule = jest.requireActual('../src/herokuClient') as object 21 | return { 22 | __esmodule: true, 23 | ...originalModule, 24 | get: jest.fn(async () => reviewApp) 25 | } 26 | }) 27 | 28 | describe('repeatedCheckForSuccessStatus', () => { 29 | it('calls heroku api with the review app id', async () => { 30 | await repeatedCheckForSuccessStatus(logger, reviewAppId) 31 | 32 | expect(heroku.get).toHaveBeenCalledWith(`/review-apps/${reviewAppId}`) 33 | }) 34 | 35 | it('throws an error if the app was deleted', async () => { 36 | reviewApp.status = 'deleted' 37 | 38 | await expect(repeatedCheckForSuccessStatus(logger, reviewAppId)).rejects.toThrowError() 39 | }) 40 | 41 | it('returns true if the review app is successfully polled', async () => { 42 | reviewApp.status = 'created' 43 | 44 | await expect(repeatedCheckForSuccessStatus(logger, reviewAppId)).resolves 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /plugins/heroku/test/scaleDyno.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, jest } from '@jest/globals' 2 | import { scaleDyno } from '../src/scaleDyno' 3 | import heroku from '../src/herokuClient' 4 | import winston, { Logger } from 'winston' 5 | 6 | const logger = winston as unknown as Logger 7 | 8 | const appName = 'test-staging-app-name' 9 | 10 | const response = [ 11 | { 12 | quantity: 1, 13 | type: 'web' 14 | } 15 | ] 16 | 17 | jest.mock('../src/herokuClient', () => { 18 | const originalModule = jest.requireActual('../src/herokuClient') as object 19 | return { 20 | __esmodule: true, 21 | ...originalModule, 22 | patch: jest.fn(async (path: string) => { 23 | if (!path.includes('test-staging-app-name')) { 24 | throw new Error() 25 | } 26 | return response 27 | }) 28 | } 29 | }) 30 | 31 | describe('scaleDyno', () => { 32 | it('makes an api call to heroku with the app name', async () => { 33 | await scaleDyno(logger, appName, 1) 34 | 35 | expect(heroku.patch).toHaveBeenCalledTimes(1) 36 | expect(heroku.patch).toHaveBeenCalledWith('/apps/test-staging-app-name/formation', expect.anything()) 37 | }) 38 | 39 | it('throws an error if scaling fails', async () => { 40 | await expect(scaleDyno(logger, 'incorrect-app-name', 1)).rejects.toThrow() 41 | }) 42 | 43 | it('resolves succesfully if scaling completes', async () => { 44 | await expect(scaleDyno(logger, appName, 1)).resolves.not.toThrow() 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /plugins/heroku/test/setStagingSlug.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, jest } from '@jest/globals' 2 | import { setStagingSlug } from '../src/setStagingSlug' 3 | import heroku from '../src/herokuClient' 4 | import winston, { Logger } from 'winston' 5 | 6 | const logger = winston as unknown as Logger 7 | const mockHerokuPost = jest.spyOn(heroku, 'post') 8 | const appName = 'test-app-name' 9 | const slug = 'test-slug-id' 10 | 11 | describe('setStagingSlug', () => { 12 | it('posts to heroku api with app name and slug id', async () => { 13 | mockHerokuPost.mockImplementation(async () => Promise.resolve({ slug: { id: 'test-slug-id' } })) 14 | 15 | await setStagingSlug(logger, appName, slug) 16 | 17 | expect(heroku.post).toBeCalledWith(`/apps/${appName}/releases`, { body: { slug } }) 18 | }) 19 | 20 | it('throws an error if unsuccessful', async () => { 21 | mockHerokuPost.mockImplementation(async () => Promise.reject()) 22 | 23 | await expect(setStagingSlug(logger, appName, slug)).rejects.toThrowError() 24 | }) 25 | 26 | it('resolves if successful', async () => { 27 | mockHerokuPost.mockImplementation(async () => Promise.resolve({ slug: { id: 'test-slug-id' } })) 28 | 29 | await expect(setStagingSlug(logger, appName, slug)).resolves.not.toThrow() 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /plugins/heroku/test/tasks/teardown.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, jest } from '@jest/globals' 2 | import Teardown from '../../src/tasks/teardown' 3 | import * as utils from '../../src/scaleDyno' 4 | import winston, { Logger } from 'winston' 5 | 6 | const logger = winston as unknown as Logger 7 | 8 | const appName = 'staging-app-name' 9 | 10 | jest.mock('@dotcom-tool-kit/state', () => { 11 | return { 12 | readState: jest.fn(() => ({ appName })) 13 | } 14 | }) 15 | 16 | const mockScaleDyno = jest.spyOn(utils, 'scaleDyno') 17 | 18 | describe('teardown', () => { 19 | it('should call scaleDyno with app name', async () => { 20 | mockScaleDyno.mockImplementationOnce(() => Promise.resolve()) 21 | const task = new Teardown(logger, 'HerokuTeardown', {}) 22 | await task.run() 23 | 24 | expect(utils.scaleDyno).toHaveBeenCalledWith(expect.anything(), appName, 0) 25 | }) 26 | 27 | it('should resolve if succesfully completed', async () => { 28 | mockScaleDyno.mockImplementationOnce(() => Promise.resolve()) 29 | const task = new Teardown(logger, 'HerokuTeardown', {}) 30 | await expect(task.run()).resolves.not.toThrow() 31 | }) 32 | 33 | it('should return a rejected promise if it completes unsuccessfully', async () => { 34 | mockScaleDyno.mockImplementationOnce(() => Promise.reject()) 35 | const task = new Teardown(logger, 'HerokuTeardown', {}) 36 | await expect(task.run()).rejects.toThrowError() 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /plugins/heroku/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../../lib/wait-for-ok" 6 | }, 7 | { 8 | "path": "../../lib/state" 9 | }, 10 | { 11 | "path": "../../lib/error" 12 | }, 13 | { 14 | "path": "../../lib/doppler" 15 | }, 16 | { 17 | "path": "../../plugins/package-json-hook" 18 | }, 19 | { 20 | "path": "../npm" 21 | }, 22 | { 23 | "path": "../../lib/base" 24 | }, 25 | { 26 | "path": "../../lib/logger" 27 | } 28 | ], 29 | "compilerOptions": { 30 | "outDir": "lib", 31 | "rootDir": "src" 32 | }, 33 | "include": ["src/**/*"] 34 | } 35 | -------------------------------------------------------------------------------- /plugins/husky-npm/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/package-json-hook' 3 | 4 | options: 5 | hooks: 6 | - PackageJson: 7 | husky.hooks: 8 | pre-commit: 'git:precommit' 9 | commit-msg: 'git:commitmsg' 10 | 11 | version: 2 12 | -------------------------------------------------------------------------------- /plugins/husky-npm/README.md: -------------------------------------------------------------------------------- 1 | # @dotcom-tool-kit/husky-npm 2 | 3 | A plugin to add git hooks to your project via [husky](https://typicode.github.io/husky/#/). These hooks can be configured with different tasks as your project requires. 4 | 5 | ## Installation 6 | 7 | With Tool Kit [already set up](https://github.com/financial-times/dotcom-tool-kit#installing-and-using-tool-kit), install this plugin as a dev dependency: 8 | 9 | ```sh 10 | npm install --save-dev @dotcom-tool-kit/husky-npm 11 | ``` 12 | 13 | And add it to your repo's `.toolkitrc.yml`: 14 | 15 | ```yaml 16 | plugins: 17 | - '@dotcom-tool-kit/husky-npm' 18 | ``` 19 | 20 | Install this plugin's hooks: 21 | 22 | ```sh 23 | npx dotcom-tool-kit --install 24 | ``` 25 | 26 | ## Options 27 | 28 | none 29 | 30 | ## Hooks 31 | 32 | | Event | Description | Installed to...| Default tasks | 33 | |-|-|-|-| 34 | | `git:precommit` | installs git's pre-commit hook | package.json | `LintStaged` | 35 | | `git:commitmsg` | installs git's commit-msg hook | package.json | none | 36 | -------------------------------------------------------------------------------- /plugins/husky-npm/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/husky-npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/husky-npm", 3 | "version": "5.1.12", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/package-json-hook": "^5.2.4", 14 | "tslib": "^2.8.1" 15 | }, 16 | "peerDependencies": { 17 | "dotcom-tool-kit": "4.x", 18 | "husky": "4.x" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 23 | "directory": "plugins/husky-npm" 24 | }, 25 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 26 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/husky-npm", 27 | "files": [ 28 | "/lib", 29 | ".toolkitrc.yml" 30 | ], 31 | "volta": { 32 | "extends": "../../package.json" 33 | }, 34 | "engines": { 35 | "node": "18.x || 20.x || 22.x" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plugins/husky-npm/src/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/husky-npm/src/index.ts -------------------------------------------------------------------------------- /plugins/husky-npm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../plugins/package-json-hook" 10 | } 11 | ], 12 | "include": ["src/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /plugins/jest/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | Jest: './lib/tasks/jest' 3 | 4 | commands: 5 | 'test:local': Jest 6 | 'test:ci': Jest # TODO provide default mode option here 7 | 8 | version: 2 9 | -------------------------------------------------------------------------------- /plugins/jest/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/jest", 3 | "version": "4.3.5", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/jest" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/logger": "^4.2.2", 15 | "tslib": "^2.8.1", 16 | "zod": "^3.24.4" 17 | }, 18 | "peerDependencies": { 19 | "dotcom-tool-kit": "4.x", 20 | "jest-cli": "27.x || 28.x || 29.x" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 25 | "directory": "plugins/jest" 26 | }, 27 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 28 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/jest", 29 | "files": [ 30 | "/lib", 31 | ".toolkitrc.yml" 32 | ], 33 | "devDependencies": { 34 | "winston": "^3.17.0" 35 | }, 36 | "engines": { 37 | "node": "18.x || 20.x || 22.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/jest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../../lib/logger" 13 | } 14 | ], 15 | "include": ["src/**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /plugins/lint-staged-npm/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/lint-staged' 3 | - '@dotcom-tool-kit/husky-npm' 4 | - '@dotcom-tool-kit/package-json-hook' 5 | 6 | optionsSchema: './lib/schema' 7 | 8 | options: 9 | hooks: 10 | - PackageJson: 11 | lint-staged: 12 | !toolkit/option '@dotcom-tool-kit/lint-staged-npm.testGlob': 13 | commands: 'test:staged' 14 | trailingString: '--' 15 | !toolkit/option '@dotcom-tool-kit/lint-staged-npm.formatGlob': 16 | commands: 'format:staged' 17 | trailingString: '--' 18 | 19 | version: 2 20 | -------------------------------------------------------------------------------- /plugins/lint-staged-npm/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/lint-staged-npm/index.js -------------------------------------------------------------------------------- /plugins/lint-staged-npm/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/lint-staged-npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/lint-staged-npm", 3 | "version": "4.2.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/husky-npm": "^5.1.12", 14 | "@dotcom-tool-kit/lint-staged": "^5.2.12", 15 | "@dotcom-tool-kit/package-json-hook": "^5.2.4", 16 | "tslib": "^2.8.1", 17 | "zod": "^3.24.4" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 22 | "directory": "plugins/lint-staged-npm" 23 | }, 24 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 25 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/lint-staged-npm", 26 | "files": [ 27 | "/lib", 28 | ".toolkitrc.yml" 29 | ], 30 | "volta": { 31 | "extends": "../../package.json" 32 | }, 33 | "peerDependencies": { 34 | "dotcom-tool-kit": "4.x" 35 | }, 36 | "engines": { 37 | "node": "18.x || 20.x || 22.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/lint-staged-npm/src/schema.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod' 2 | 3 | export default z.object({ 4 | testGlob: z.string().default('**/*.js'), 5 | formatGlob: z.string().default('**/*.js') 6 | }) 7 | -------------------------------------------------------------------------------- /plugins/lint-staged-npm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [], 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /plugins/lint-staged/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | LintStaged: './lib/tasks/lint-staged' 3 | 4 | commands: 5 | 'git:precommit': LintStaged 6 | 7 | version: 2 8 | -------------------------------------------------------------------------------- /plugins/lint-staged/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/lint-staged", 3 | "version": "5.2.12", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/package-json-hook": "^5.2.4", 15 | "@dotcom-tool-kit/logger": "^4.2.2", 16 | "lint-staged": "^11.2.3", 17 | "tslib": "^2.8.1" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 22 | "directory": "plugins/lint-staged" 23 | }, 24 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 25 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/lint-staged", 26 | "files": [ 27 | "/lib", 28 | ".toolkitrc.yml" 29 | ], 30 | "volta": { 31 | "extends": "../../package.json" 32 | }, 33 | "peerDependencies": { 34 | "dotcom-tool-kit": "4.x" 35 | }, 36 | "engines": { 37 | "node": "18.x || 20.x || 22.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/lint-staged/src/declarations.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | 3 | declare module 'lint-staged' { 4 | export interface LintStagedOptions { 5 | allowEmpty?: boolean 6 | concurrent?: boolean | number 7 | config?: any 8 | configPath?: string 9 | cwd?: string 10 | debug?: boolean 11 | maxArgLength?: number 12 | quiet?: boolean 13 | relative?: boolean 14 | shell?: boolean | string 15 | stash?: boolean 16 | verbose?: boolean 17 | } 18 | 19 | type LogFunction = (...params: any[]) => void 20 | type Logger = { 21 | error: LogFunction 22 | log: LogFunction 23 | warn: LogFunction 24 | } 25 | 26 | export default function lintStaged(opts?: LintStagedOptions, logger?: Logger): Promise 27 | } 28 | -------------------------------------------------------------------------------- /plugins/lint-staged/src/tasks/lint-staged.ts: -------------------------------------------------------------------------------- 1 | import { ToolKitError } from '@dotcom-tool-kit/error' 2 | import { hookConsole } from '@dotcom-tool-kit/logger' 3 | import { Task, TaskRunContext } from '@dotcom-tool-kit/base' 4 | import lintStaged from 'lint-staged' 5 | 6 | export default class LintStaged extends Task { 7 | static description = 'Run `lint-staged` in your repo, for use with git hooks.' 8 | 9 | async run({ cwd }: TaskRunContext): Promise { 10 | this.logger.info('running lint-staged') 11 | const unhook = hookConsole(this.logger, 'lint-staged') 12 | const wasSuccessful = await lintStaged({ cwd }).finally(unhook) 13 | 14 | if (!wasSuccessful) { 15 | const error = new ToolKitError('lint-staged encountered errors') 16 | throw error 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /plugins/lint-staged/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../plugins/package-json-hook" 10 | }, 11 | { 12 | "path": "../../lib/logger" 13 | }, 14 | { 15 | "path": "../../lib/error" 16 | } 17 | ], 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/mocha/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | Mocha: './lib/tasks/mocha' 3 | 4 | commands: 5 | 'test:local': Mocha 6 | 'test:ci': Mocha 7 | 8 | version: 2 9 | -------------------------------------------------------------------------------- /plugins/mocha/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/mocha/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/mocha", 3 | "version": "4.4.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/mocha" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/error": "^4.1.1", 15 | "@dotcom-tool-kit/logger": "^4.2.2", 16 | "glob": "^10.4.5", 17 | "tslib": "^2.8.1", 18 | "zod": "^3.24.4" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 23 | "directory": "plugins/mocha" 24 | }, 25 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 26 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/mocha", 27 | "devDependencies": { 28 | "@jest/globals": "^29.7.0", 29 | "@types/glob": "^7.1.3", 30 | "@types/mocha": "^8.2.2", 31 | "winston": "^3.17.0" 32 | }, 33 | "files": [ 34 | "/lib", 35 | ".toolkitrc.yml" 36 | ], 37 | "volta": { 38 | "extends": "../../package.json" 39 | }, 40 | "peerDependencies": { 41 | "dotcom-tool-kit": "4.x", 42 | "mocha": ">=6.x <=11.x" 43 | }, 44 | "engines": { 45 | "node": "18.x || 20.x || 22.x" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /plugins/mocha/src/tasks/mocha.ts: -------------------------------------------------------------------------------- 1 | import { hookFork, waitOnExit } from '@dotcom-tool-kit/logger' 2 | import { Task, TaskRunContext } from '@dotcom-tool-kit/base' 3 | import { glob } from 'glob' 4 | import { fork } from 'child_process' 5 | import * as z from 'zod' 6 | const mochaCLIPath = require.resolve('mocha/bin/mocha') 7 | 8 | const MochaSchema = z 9 | .object({ 10 | files: z.string().default('test/**/*.js').describe('A file path glob to Mocha tests.'), 11 | configPath: z 12 | .string() 13 | .optional() 14 | .describe( 15 | "Path to the [Mocha config file](https://mochajs.org/#configuring-mocha-nodejs). Uses Mocha's own [config resolution](https://mochajs.org/#priorities) by default." 16 | ) 17 | }) 18 | .describe('Runs `mocha` to execute tests.') 19 | export { MochaSchema as schema } 20 | 21 | export default class Mocha extends Task<{ task: typeof MochaSchema }> { 22 | async run({ cwd }: TaskRunContext): Promise { 23 | const files = await glob(this.options.files, { cwd }) 24 | 25 | const args = ['--color', ...files] 26 | if (this.options.configPath) { 27 | args.unshift(`--config=${this.options.configPath}`) 28 | } 29 | this.logger.info(`running mocha ${args.join(' ')}`) 30 | const child = fork(mochaCLIPath, args, { silent: true, cwd }) 31 | hookFork(this.logger, 'mocha', child) 32 | return waitOnExit('mocha', child) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plugins/mocha/test/files/fail/test.js: -------------------------------------------------------------------------------- 1 | describe('mocha test', () => 2 | it('should fail', () => { 3 | throw new Error('waaah') 4 | })) 5 | -------------------------------------------------------------------------------- /plugins/mocha/test/files/pass/test.js: -------------------------------------------------------------------------------- 1 | describe('mocha test', () => 2 | it('should pass', () => { 3 | console.log('Hello world!') 4 | })) 5 | -------------------------------------------------------------------------------- /plugins/mocha/test/tasks/mocha.test.ts: -------------------------------------------------------------------------------- 1 | // The standard Jest types clash with the mocha types in the global scope so 2 | // explicitly import them instead. 3 | import { describe, it, expect } from '@jest/globals' 4 | import * as path from 'path' 5 | import Mocha from '../../src/tasks/mocha' 6 | import winston, { Logger } from 'winston' 7 | 8 | const logger = winston as unknown as Logger 9 | 10 | describe('mocha', () => { 11 | it('should succeed with passing tests', async () => { 12 | const task = new Mocha( 13 | logger, 14 | 'Mocha', 15 | {}, 16 | { 17 | files: path.resolve(__dirname, '../files/pass') + '/**/*.js' 18 | } 19 | ) 20 | 21 | await task.run({ command: 'test:local', cwd: '' }) 22 | }) 23 | 24 | it('should throw with failing tests', async () => { 25 | const task = new Mocha( 26 | logger, 27 | 'Mocha', 28 | {}, 29 | { 30 | files: path.resolve(__dirname, '../files/fail') + '/**/*.js' 31 | } 32 | ) 33 | 34 | await expect(task.run({ command: 'test:local', cwd: '' })).rejects.toThrowErrorMatchingInlineSnapshot( 35 | `"mocha process returned a non-zero exit code (1)"` 36 | ) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /plugins/mocha/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "references": [ 4 | { 5 | "path": "../../lib/logger" 6 | }, 7 | { 8 | "path": "../../lib/base" 9 | } 10 | ], 11 | "include": ["src/**/*"], 12 | "compilerOptions": { 13 | "outDir": "lib", 14 | "rootDir": "src", 15 | "types": ["node", "mocha"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plugins/monorepo/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | init: 4 | - ./lib/load-workspace-configs.js 5 | 6 | tasks: 7 | WorkspaceCommand: ./lib/tasks/workspace-command.js 8 | -------------------------------------------------------------------------------- /plugins/monorepo/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/monorepo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/monorepo", 3 | "version": "0.1.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "plugins/monorepo" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/monorepo", 19 | "files": [ 20 | "/lib", 21 | ".toolkitrc.yml" 22 | ], 23 | "engines": { 24 | "node": "18.x || 20.x", 25 | "npm": "7.x || 8.x || 9.x" 26 | }, 27 | "volta": { 28 | "extends": "../../package.json" 29 | }, 30 | "peerDependencies": { 31 | "dotcom-tool-kit": "4.x" 32 | }, 33 | "dependencies": { 34 | "@dotcom-tool-kit/base": "^1.2.3", 35 | "@dotcom-tool-kit/logger": "^4.2.2", 36 | "@npmcli/map-workspaces": "^4.0.2", 37 | "minimatch": "^10.0.1", 38 | "pluralize": "^8.0.0", 39 | "zod": "^3.24.4" 40 | }, 41 | "devDependencies": { 42 | "@dotcom-tool-kit/config": "^1.1.1", 43 | "@relmify/jest-serializer-strip-ansi": "1.0.2", 44 | "@types/npmcli__map-workspaces": "^3.0.4", 45 | "@types/pluralize": "^0.0.33" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/invalid/a/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - @dotcom-tool-kit/webpack 3 | 4 | commands: 5 | build:local: 6 | - Webpack: 7 | envName: development 8 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/invalid/a/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@monorepo-plugin-tests/a" 3 | } 4 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/invalid/b/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - @dotcom-tool-kit/serverless 3 | 4 | # deliberately missing plugin options to fail validation 5 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/invalid/b/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@monorepo-plugin-tests/b" 3 | } 4 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/invalid/c/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - @dotcom-tool-kit/heroku 3 | 4 | # deliberately missing plugin options to fail validation 5 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/invalid/c/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@monorepo-plugin-tests/c" 3 | } 4 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/invalid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "workspaces": [ 3 | "./a", 4 | "./b", 5 | "./c" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/successful/a/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - @dotcom-tool-kit/webpack 3 | 4 | commands: 5 | build:local: 6 | - Webpack: 7 | envName: development 8 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/successful/a/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@monorepo-plugin-tests/a" 3 | } 4 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/successful/b/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - @dotcom-tool-kit/babel 3 | 4 | commands: 5 | build:local: 6 | - Babel: 7 | envName: development 8 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/successful/b/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@monorepo-plugin-tests/b" 3 | } 4 | -------------------------------------------------------------------------------- /plugins/monorepo/test/files/successful/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "workspaces": [ 3 | "./a", 4 | "./b" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /plugins/monorepo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../../lib/config" 13 | }, 14 | { 15 | "path": "../../lib/logger" 16 | }, 17 | { 18 | "path": "../../core/cli" 19 | } 20 | ], 21 | "include": ["src/**/*"] 22 | } 23 | -------------------------------------------------------------------------------- /plugins/n-test/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | NTest: './lib/tasks/n-test' 3 | 4 | commands: 5 | 'test:review': NTest 6 | 'test:staging': NTest 7 | 8 | version: 2 9 | -------------------------------------------------------------------------------- /plugins/n-test/__mocks__/puppeteer.ts: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' 2 | 3 | let status = 200 4 | 5 | const mockResponse = { 6 | status: jest.fn(() => status) 7 | } 8 | 9 | export const __setResponseStatus = (newStatus: number): void => { 10 | status = newStatus 11 | } 12 | 13 | const mockPage = { 14 | cookies: jest.fn().mockReturnValue([]), 15 | setViewport: jest.fn(), 16 | on: jest.fn(), 17 | setRequestInterception: jest.fn(), 18 | setDefaultNavigationTimeout: jest.fn(), 19 | goto: jest.fn().mockReturnValue(mockResponse), 20 | close: jest.fn() 21 | } 22 | 23 | const mockBrowser = { 24 | newPage: jest.fn().mockReturnValue(mockPage), 25 | close: jest.fn() 26 | } 27 | 28 | const mockLaunch = jest.fn().mockReturnValue(mockBrowser) 29 | 30 | export const launch = mockLaunch 31 | -------------------------------------------------------------------------------- /plugins/n-test/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | ...base.config, 6 | transform: { 7 | '^.+\\.tsx?$': [ 8 | 'ts-jest', 9 | { 10 | ...base.tsJestConfig, 11 | tsconfig: path.resolve(__dirname, './tsconfig.test.json') 12 | } 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugins/n-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/n-test", 3 | "version": "4.3.5", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/n-test" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/error": "^4.1.1", 15 | "@dotcom-tool-kit/logger": "^4.2.2", 16 | "@dotcom-tool-kit/state": "^4.3.2", 17 | "@financial-times/n-test": "^8.0.0", 18 | "tslib": "^2.8.1", 19 | "zod": "^3.24.4" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 24 | "directory": "plugins/n-test" 25 | }, 26 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 27 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/n-test", 28 | "devDependencies": { 29 | "@jest/globals": "^29.7.0", 30 | "@types/jest": "^29.5.14", 31 | "winston": "^3.17.0" 32 | }, 33 | "files": [ 34 | "/lib", 35 | ".toolkitrc.yml" 36 | ], 37 | "volta": { 38 | "extends": "../../package.json" 39 | }, 40 | "peerDependencies": { 41 | "dotcom-tool-kit": "4.x" 42 | }, 43 | "engines": { 44 | "node": "18.x || 20.x || 22.x" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /plugins/n-test/src/types/n-test.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@financial-times/n-test' { 2 | type SmokeTestOptions = { 3 | browsers?: string[] 4 | host?: string 5 | config?: string 6 | interactive?: boolean 7 | header?: { [name: string]: string } 8 | } 9 | 10 | export class SmokeTest { 11 | constructor(options: SmokeTestOptions) 12 | run(sets?: string[]): Promise 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /plugins/n-test/test/files/smoke.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | name: 'test', 4 | urls: { 5 | '/': 200 6 | } 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /plugins/n-test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "outDir": "lib", 6 | "rootDir": "src" 7 | }, 8 | "references": [ 9 | { 10 | "path": "../../lib/error" 11 | }, 12 | { 13 | "path": "../../lib/state" 14 | }, 15 | { 16 | "path": "../../lib/logger" 17 | }, 18 | { 19 | "path": "../../lib/base" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /plugins/n-test/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "paths": { 5 | "puppeteer": ["__mocks__/puppeteer"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /plugins/next-router/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/doppler' 3 | 4 | tasks: 5 | NextRouter: './lib/tasks/next-router' 6 | 7 | commands: 8 | run:local: 9 | - NextRouter 10 | 11 | optionsSchema: './lib/schema' 12 | 13 | version: 2 14 | -------------------------------------------------------------------------------- /plugins/next-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/next-router", 3 | "version": "4.3.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/doppler": "^2.2.3", 15 | "@dotcom-tool-kit/error": "^4.1.1", 16 | "@dotcom-tool-kit/logger": "^4.2.2", 17 | "@dotcom-tool-kit/state": "^4.3.2", 18 | "ft-next-router": "^4.0.2", 19 | "tslib": "^2.8.1", 20 | "zod": "^3.24.4" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 25 | "directory": "plugins/next-router" 26 | }, 27 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 28 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/next-router", 29 | "files": [ 30 | "/lib", 31 | ".toolkitrc.yml" 32 | ], 33 | "peerDependencies": { 34 | "dotcom-tool-kit": "4.x" 35 | }, 36 | "engines": { 37 | "node": "18.x || 20.x || 22.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/next-router/src/schema.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod' 2 | 3 | export default z.object({ 4 | appName: z 5 | .string() 6 | .describe( 7 | "The system's `name` field as it appears in [next-service-registry](https://next-registry.ft.com/v2). **This is often different to its Biz Ops system code**, so be sure to check." 8 | ) 9 | }) 10 | -------------------------------------------------------------------------------- /plugins/next-router/src/types/ft-next-router.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ft-next-router' { 2 | type Service = { 3 | service: string 4 | port: number 5 | } 6 | 7 | export function register(args: Service): Promise 8 | } 9 | -------------------------------------------------------------------------------- /plugins/next-router/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/error" 10 | }, 11 | { 12 | "path": "../../lib/state" 13 | }, 14 | { 15 | "path": "../../lib/logger" 16 | }, 17 | { 18 | "path": "../../lib/base" 19 | }, 20 | { 21 | "path": "../../lib/doppler" 22 | } 23 | ], 24 | "include": ["src/**/*"] 25 | } 26 | -------------------------------------------------------------------------------- /plugins/node-test/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | NodeTest: './lib/tasks/node-test' 3 | 4 | commands: 5 | 'test:ci': NodeTest 6 | 'test:local': NodeTest 7 | 8 | version: 2 9 | -------------------------------------------------------------------------------- /plugins/node-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/node-test", 3 | "version": "1.0.5", 4 | "main": "lib", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "FT.com Platforms Team ", 10 | "license": "ISC", 11 | "description": "Run tests via the Node.js built-in test runner", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "plugins/node-test" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/node-test", 19 | "files": [ 20 | "/lib", 21 | ".toolkitrc.yml" 22 | ], 23 | "engines": { 24 | "node": "20.x || 22.x" 25 | }, 26 | "volta": { 27 | "extends": "../../package.json" 28 | }, 29 | "peerDependencies": { 30 | "dotcom-tool-kit": "4.x" 31 | }, 32 | "dependencies": { 33 | "@dotcom-tool-kit/base": "^1.2.3", 34 | "@dotcom-tool-kit/error": "^4.1.1", 35 | "@dotcom-tool-kit/logger": "^4.2.2", 36 | "glob": "^10.4.5", 37 | "tslib": "^2.8.1", 38 | "zod": "^3.24.4" 39 | }, 40 | "devDependencies": { 41 | "@types/node": "^20.17.28" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /plugins/node-test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../../lib/error" 13 | }, 14 | { 15 | "path": "../../lib/logger" 16 | } 17 | ], 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/node/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/doppler' 3 | 4 | tasks: 5 | Node: './lib/tasks/node' 6 | 7 | commands: 8 | 'run:local': Node 9 | 10 | version: 2 11 | -------------------------------------------------------------------------------- /plugins/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/node", 3 | "version": "4.3.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/doppler": "^2.2.3", 15 | "@dotcom-tool-kit/error": "^4.1.1", 16 | "@dotcom-tool-kit/state": "^4.3.2", 17 | "get-port": "^5.1.1", 18 | "tslib": "^2.8.1", 19 | "wait-port": "^1.1.0", 20 | "zod": "^3.24.4" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 25 | "directory": "plugins/node" 26 | }, 27 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 28 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/node", 29 | "files": [ 30 | "/lib", 31 | ".toolkitrc.yml" 32 | ], 33 | "peerDependencies": { 34 | "dotcom-tool-kit": "4.x" 35 | }, 36 | "engines": { 37 | "node": "18.x || 20.x || 22.x" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../../lib/error" 13 | }, 14 | { 15 | "path": "../../lib/logger" 16 | }, 17 | { 18 | "path": "../../lib/doppler" 19 | }, 20 | { 21 | "path": "../../lib/state" 22 | } 23 | ], 24 | "include": ["src/**/*"] 25 | } 26 | -------------------------------------------------------------------------------- /plugins/nodemon/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/doppler' 3 | 4 | tasks: 5 | Nodemon: './lib/tasks/nodemon' 6 | 7 | commands: 8 | 'run:local': Nodemon 9 | 10 | version: 2 11 | -------------------------------------------------------------------------------- /plugins/nodemon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/nodemon", 3 | "version": "4.2.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/doppler": "^2.2.3", 15 | "@dotcom-tool-kit/error": "^4.1.1", 16 | "@dotcom-tool-kit/state": "^4.3.2", 17 | "get-port": "^5.1.1", 18 | "tslib": "^2.8.1", 19 | "zod": "^3.24.4" 20 | }, 21 | "peerDependencies": { 22 | "dotcom-tool-kit": "4.x", 23 | "nodemon": "2.x" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 28 | "directory": "plugins/nodemon" 29 | }, 30 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 31 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/nodemon", 32 | "files": [ 33 | "/lib", 34 | ".toolkitrc.yml" 35 | ], 36 | "devDependencies": { 37 | "@types/nodemon": "^1.19.6" 38 | }, 39 | "engines": { 40 | "node": "18.x || 20.x || 22.x" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /plugins/nodemon/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../core/cli" 10 | }, 11 | { 12 | "path": "../../lib/base" 13 | }, 14 | { 15 | "path": "../../lib/error" 16 | }, 17 | { 18 | "path": "../../lib/logger" 19 | }, 20 | { 21 | "path": "../../lib/state" 22 | }, 23 | { 24 | "path": "../../lib/doppler" 25 | } 26 | ], 27 | "include": ["src/**/*"] 28 | } 29 | -------------------------------------------------------------------------------- /plugins/npm/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/package-json-hook' 3 | 4 | tasks: 5 | NpmPrune: './lib/tasks/prune' 6 | NpmPublish: './lib/tasks/publish' 7 | 8 | options: 9 | hooks: 10 | - PackageJson: 11 | scripts: 12 | build: 'build:local' 13 | test: 'test:local' 14 | start: 'run:local' 15 | 16 | version: 2 17 | -------------------------------------------------------------------------------- /plugins/npm/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/npm", 3 | "version": "4.2.15", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/npm" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/error": "^4.1.1", 15 | "@dotcom-tool-kit/package-json-hook": "^5.2.4", 16 | "@dotcom-tool-kit/state": "^4.3.2", 17 | "libnpmpack": "^9.0.3", 18 | "libnpmpublish": "^5.0.1", 19 | "pacote": "^12.0.3", 20 | "tar": "^7.4.3", 21 | "tslib": "^2.8.1" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 26 | "directory": "plugins/npm" 27 | }, 28 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 29 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/npm", 30 | "devDependencies": { 31 | "@npm/types": "^1.0.2", 32 | "@types/libnpmpublish": "^4.0.6", 33 | "@types/pacote": "^11.1.3", 34 | "winston": "^3.17.0" 35 | }, 36 | "files": [ 37 | "/lib", 38 | ".toolkitrc.yml" 39 | ], 40 | "volta": { 41 | "extends": "../../package.json" 42 | }, 43 | "peerDependencies": { 44 | "dotcom-tool-kit": "4.x" 45 | }, 46 | "engines": { 47 | "node": "18.x || 20.x || 22.x" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /plugins/npm/readme.md: -------------------------------------------------------------------------------- 1 | # @dotcom-tool-kit/npm 2 | 3 | This plugin is for managing Tool Kit hooks that are run from npm scripts (such as `npm run test`). 4 | 5 | This plugin will be installed as a dependency of the [frontend-app](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/frontend-app), [backend-heroku-app](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/backend-heroku-app), and [component](https://github.com/Financial-Times/dotcom-tool-kit/tree/main/plugins/component) plugins so you do not need to install it separately if you are using either of those plugins. 6 | 7 | ## Installation 8 | 9 | Install `@dotcom-tool-kit/npm` as a `devDependency` in your app: 10 | 11 | ```sh 12 | npm install --save-dev @dotcom-tool-kit/npm 13 | ``` 14 | 15 | Add the plugin to your [Tool Kit configuration](https://github.com/financial-times/dotcom-tool-kit/blob/main/readme.md#configuring-tool-kit): 16 | 17 | ```yaml 18 | plugins: 19 | - '@dotcom-tool-kit/npm' 20 | ``` 21 | 22 | 23 | ## Tasks 24 | 25 | ### `NpmPrune` 26 | 27 | Prune development npm dependencies. 28 | 29 | ### `NpmPublish` 30 | 31 | Publish package to the npm registry. 32 | 33 | -------------------------------------------------------------------------------- /plugins/npm/src/tasks/prune.ts: -------------------------------------------------------------------------------- 1 | import { Task, TaskRunContext } from '@dotcom-tool-kit/base' 2 | import { ToolKitError } from '@dotcom-tool-kit/error' 3 | import { hookFork, waitOnExit } from '@dotcom-tool-kit/logger' 4 | import { spawn } from 'node:child_process' 5 | 6 | export default class NpmPrune extends Task { 7 | static description = 'Prune development npm dependencies.' 8 | 9 | async run({ cwd }: TaskRunContext): Promise { 10 | try { 11 | this.logger.verbose('pruning dev dependencies...') 12 | const child = spawn('npm', ['prune', '--production'], { 13 | cwd 14 | }) 15 | 16 | hookFork(this.logger, 'npm', child) 17 | return waitOnExit('npm', child) 18 | } catch (err) { 19 | const error = new ToolKitError('unable to prune dev dependencies') 20 | if (err instanceof Error) { 21 | error.details = err.message 22 | } 23 | throw error 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /plugins/npm/src/types/declarations.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'libnpmpack' { 2 | export default function pack(packagePath: string): Promise 3 | } 4 | -------------------------------------------------------------------------------- /plugins/npm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/error" 10 | }, 11 | { 12 | "path": "../../plugins/package-json-hook" 13 | }, 14 | { 15 | "path": "../../lib/base" 16 | }, 17 | { 18 | "path": "../../lib/state" 19 | } 20 | ], 21 | "include": ["src/**/*"] 22 | } 23 | -------------------------------------------------------------------------------- /plugins/package-json-hook/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | installs: 2 | PackageJson: 3 | entryPoint: './lib/package-json-helper' 4 | managesFiles: 5 | - 'package.json' 6 | 7 | version: 2 8 | -------------------------------------------------------------------------------- /plugins/package-json-hook/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/package-json-hook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/package-json-hook", 3 | "version": "5.2.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/package-json-hook" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/conflict": "^1.0.1", 15 | "@dotcom-tool-kit/plugin": "^1.1.0", 16 | "@financial-times/package-json": "^4.0.0", 17 | "lodash": "^4.17.21", 18 | "tslib": "^2.8.1", 19 | "zod": "^3.24.4" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 24 | "directory": "plugins/package-json-hook" 25 | }, 26 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 27 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/package-json-hook", 28 | "peerDependencies": { 29 | "zod": "^3.24.4" 30 | }, 31 | "devDependencies": { 32 | "@jest/globals": "^29.7.0", 33 | "@types/lodash": "^4.17.17", 34 | "winston": "^3.17.0" 35 | }, 36 | "files": [ 37 | "/lib", 38 | ".toolkitrc.yml" 39 | ], 40 | "volta": { 41 | "extends": "../../package.json" 42 | }, 43 | "engines": { 44 | "node": "18.x || 20.x || 22.x" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /plugins/package-json-hook/test/files/existing-hook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "test-hook": "dotcom-tool-kit another:hook" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /plugins/package-json-hook/test/files/multiple-hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "test-hook": "dotcom-tool-kit test:hook another:hook" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /plugins/package-json-hook/test/files/with-hook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "test-hook": "dotcom-tool-kit test:hook" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /plugins/package-json-hook/test/files/without-hook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": {} 3 | } 4 | -------------------------------------------------------------------------------- /plugins/package-json-hook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../../lib/conflict" 13 | }, 14 | { 15 | "path": "../../lib/plugin" 16 | } 17 | ], 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/prettier/.gitignore: -------------------------------------------------------------------------------- 1 | test/files/unformatted.ts 2 | -------------------------------------------------------------------------------- /plugins/prettier/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - '@dotcom-tool-kit/package-json-hook' 3 | 4 | tasks: 5 | Prettier: './lib/tasks/prettier' 6 | 7 | commands: 8 | 'format:local': Prettier 9 | 'format:staged': Prettier 10 | 11 | options: 12 | hooks: 13 | - PackageJson: 14 | scripts: 15 | format: 'format:local' 16 | 17 | version: 2 18 | -------------------------------------------------------------------------------- /plugins/prettier/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/prettier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/prettier", 3 | "version": "4.3.5", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/prettier" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@dotcom-tool-kit/base": "^1.2.3", 14 | "@dotcom-tool-kit/error": "^4.1.1", 15 | "@dotcom-tool-kit/logger": "^4.2.2", 16 | "@dotcom-tool-kit/package-json-hook": "^5.2.4", 17 | "fast-glob": "^3.3.3", 18 | "prettier": "^2.2.1", 19 | "tslib": "^2.8.1", 20 | "zod": "^3.24.4" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 25 | "directory": "plugins/prettier" 26 | }, 27 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 28 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/prettier", 29 | "files": [ 30 | "/lib", 31 | ".toolkitrc.yml" 32 | ], 33 | "devDependencies": { 34 | "@types/prettier": "^2.7.3", 35 | "winston": "^3.17.0" 36 | }, 37 | "volta": { 38 | "extends": "../../package.json" 39 | }, 40 | "peerDependencies": { 41 | "dotcom-tool-kit": "4.x" 42 | }, 43 | "engines": { 44 | "node": "18.x || 20.x || 22.x" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /plugins/prettier/test/.prettierrc-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": false, 3 | "useTabs": false, 4 | "bracketSpacing": true, 5 | "arrowParens": "always", 6 | "trailingComma": "none", 7 | "semi": false 8 | } 9 | -------------------------------------------------------------------------------- /plugins/prettier/test/files/fixtures/formatted-config-file.ts: -------------------------------------------------------------------------------- 1 | console.log({ 2 | one: "one", 3 | two: "two", 4 | three: { 5 | four: "four", 6 | five: {} 7 | }, 8 | six: "six" 9 | }) 10 | 11 | console.log({ 12 | hello: ["one", "two", "three"] 13 | }) 14 | -------------------------------------------------------------------------------- /plugins/prettier/test/files/fixtures/formatted-default.ts: -------------------------------------------------------------------------------- 1 | console.log({ 2 | one: 'one', 3 | two: 'two', 4 | three: { 5 | four: 'four', 6 | five: {} 7 | }, 8 | six: 'six' 9 | }) 10 | 11 | console.log({ 12 | hello: ['one', 'two', 'three'] 13 | }) 14 | -------------------------------------------------------------------------------- /plugins/prettier/test/files/fixtures/unformatted.ts: -------------------------------------------------------------------------------- 1 | console.log( { 2 | one: "one", 3 | two: 'two', 4 | three: { 5 | four: 'four', 6 | five:{ } 7 | 8 | }, 9 | six:'six' 10 | }); 11 | 12 | console.log({ 13 | hello: ["one", 'two', 14 | 'three',] 15 | }); 16 | -------------------------------------------------------------------------------- /plugins/prettier/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "outDir": "lib", 6 | "rootDir": "src" 7 | }, 8 | "references": [ 9 | { 10 | "path": "../../lib/error" 11 | }, 12 | { 13 | "path": "../../lib/logger" 14 | }, 15 | { 16 | "path": "../../lib/base" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/serverless/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | ServerlessRun: './lib/tasks/run' 3 | ServerlessDeploy: './lib/tasks/deploy' 4 | ServerlessProvision: './lib/tasks/provision' 5 | ServerlessTeardown: './lib/tasks/teardown' 6 | 7 | optionsSchema: './lib/schema' 8 | 9 | version: 2 10 | -------------------------------------------------------------------------------- /plugins/serverless/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/serverless", 3 | "version": "3.4.3", 4 | "description": "a plugin to manage and deploy apps using AWS Serverless", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "plugins/serverless" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/serverless", 19 | "files": [ 20 | "/lib", 21 | ".toolkitrc.yml" 22 | ], 23 | "peerDependencies": { 24 | "dotcom-tool-kit": "4.x", 25 | "serverless-offline": "12.x || 13.x" 26 | }, 27 | "dependencies": { 28 | "@dotcom-tool-kit/base": "^1.2.3", 29 | "@dotcom-tool-kit/doppler": "^2.2.3", 30 | "@dotcom-tool-kit/error": "^4.1.1", 31 | "@dotcom-tool-kit/logger": "^4.2.2", 32 | "@dotcom-tool-kit/state": "^4.3.2", 33 | "get-port": "^5.1.1", 34 | "tslib": "^2.8.1", 35 | "wait-port": "^1.1.0", 36 | "zod": "^3.24.4" 37 | }, 38 | "engines": { 39 | "node": "18.x || 20.x || 22.x" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /plugins/serverless/src/schema.ts: -------------------------------------------------------------------------------- 1 | import * as z from 'zod' 2 | 3 | import { styles } from '@dotcom-tool-kit/logger' 4 | 5 | const movedPluginOptions = >( 6 | option: string, 7 | task: string, 8 | newName = option 9 | ) => 10 | [ 11 | (options: T) => !(option in options), 12 | { 13 | message: `the option ${styles.code(option)} has moved to ${styles.code( 14 | `options.tasks.${styles.task(task)}.${newName}` 15 | )}` 16 | } 17 | ] as const 18 | 19 | export default z 20 | .object({ 21 | awsAccountId: z 22 | .string() 23 | .describe( 24 | 'the ID of the AWS account you wish to deploy to (account IDs can be found at the [FT login page](https://awslogin.in.ft.com/))' 25 | ), 26 | systemCode: z.string().describe('the system code for your app'), 27 | regions: z 28 | .array(z.string()) 29 | .default(['eu-west-1']) 30 | .describe('an array of AWS regions you want to deploy to'), 31 | configPath: z 32 | .string() 33 | .optional() 34 | .describe( 35 | 'path to your serverless config file. If this is not provided, Serverless defaults to `./serverless.yml` but [other config fomats are accepted](https://www.serverless.com/framework/docs/providers/aws/guide/intro#alternative-configuration-format)' 36 | ) 37 | }) 38 | .passthrough() 39 | .refine(...movedPluginOptions('useVault', 'ServerlessRun', 'useDoppler')) 40 | .refine(...movedPluginOptions('ports', 'ServerlessRun')) 41 | -------------------------------------------------------------------------------- /plugins/serverless/src/tasks/deploy.ts: -------------------------------------------------------------------------------- 1 | import { hookFork, waitOnExit } from '@dotcom-tool-kit/logger' 2 | import { Task, TaskRunContext } from '@dotcom-tool-kit/base' 3 | import { spawn } from 'child_process' 4 | import type ServerlessSchema from '../schema' 5 | 6 | export default class ServerlessDeploy extends Task<{ plugin: typeof ServerlessSchema }> { 7 | static description = 'Deploy a serverless function' 8 | 9 | async run({ cwd }: TaskRunContext): Promise { 10 | const { configPath, regions, systemCode } = this.pluginOptions 11 | 12 | for (const region of regions) { 13 | this.logger.verbose('starting the child serverless process...') 14 | const args = [ 15 | 'deploy', 16 | '--region', 17 | region, 18 | '--stage', 19 | 'prod', 20 | '--aws-profile', 21 | `CircleCI-role-${systemCode}` 22 | ] 23 | if (configPath) { 24 | args.push('--config', configPath) 25 | } 26 | 27 | const child = spawn('serverless', args, { 28 | env: process.env, 29 | cwd 30 | }) 31 | 32 | hookFork(this.logger, 'serverless', child) 33 | await waitOnExit('serverless', child) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /plugins/serverless/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/base" 10 | }, 11 | { 12 | "path": "../../lib/doppler" 13 | }, 14 | { 15 | "path": "../../lib/logger" 16 | } 17 | ], 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/typescript/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | TypeScript: './lib/tasks/typescript' 3 | 4 | commands: 5 | # TODO add options here once we support per-command-assignment options 6 | 'build:local': TypeScript 7 | 'build:ci': TypeScript 8 | 'build:remote': TypeScript 9 | 'run:local': TypeScript # watch 10 | 'test:local': TypeScript # noEmit 11 | 12 | version: 2 13 | -------------------------------------------------------------------------------- /plugins/typescript/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/typescript", 3 | "version": "3.3.4", 4 | "description": "", 5 | "main": "lib", 6 | "scripts": { 7 | "test": "cd ../../ ; npx jest --silent --projects plugins/typescript" 8 | }, 9 | "keywords": [], 10 | "author": "FT.com Platforms Team ", 11 | "license": "ISC", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 15 | "directory": "plugins/typescript" 16 | }, 17 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 18 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/tree/main/plugins/typescript", 19 | "files": [ 20 | "/lib", 21 | ".toolkitrc.yml" 22 | ], 23 | "peerDependencies": { 24 | "dotcom-tool-kit": "4.x", 25 | "typescript": "3.x || 4.x || 5.x" 26 | }, 27 | "dependencies": { 28 | "@dotcom-tool-kit/base": "^1.2.3", 29 | "@dotcom-tool-kit/logger": "^4.2.2", 30 | "zod": "^3.24.4" 31 | }, 32 | "devDependencies": { 33 | "@jest/globals": "^29.7.0", 34 | "typescript": "^5.8.3", 35 | "winston": "^3.17.0" 36 | }, 37 | "engines": { 38 | "node": "18.x || 20.x || 22.x" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /plugins/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/logger" 10 | }, 11 | { 12 | "path": "../../lib/base" 13 | } 14 | ], 15 | "include": ["src/**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /plugins/upload-assets-to-s3/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | UploadAssetsToS3: './lib/tasks/upload-assets-to-s3' 3 | 4 | commands: 5 | 'release:remote': UploadAssetsToS3 6 | 7 | version: 2 8 | -------------------------------------------------------------------------------- /plugins/upload-assets-to-s3/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/upload-assets-to-s3/test/files/nested/test.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Financial-Times/dotcom-tool-kit/b11fdc86f98667b140166618ae9acdc1acaa94d9/plugins/upload-assets-to-s3/test/files/nested/test.js.gz -------------------------------------------------------------------------------- /plugins/upload-assets-to-s3/test/files/test.css: -------------------------------------------------------------------------------- 1 | color: red; 2 | -------------------------------------------------------------------------------- /plugins/upload-assets-to-s3/test/files/test.js: -------------------------------------------------------------------------------- 1 | console.log('Hello world!') 2 | -------------------------------------------------------------------------------- /plugins/upload-assets-to-s3/test/files/test.json: -------------------------------------------------------------------------------- 1 | [{ "test": true }] 2 | -------------------------------------------------------------------------------- /plugins/upload-assets-to-s3/test/files/test.ts: -------------------------------------------------------------------------------- 1 | let hello: string = 'hello world!' 2 | console.log(hello) 3 | -------------------------------------------------------------------------------- /plugins/upload-assets-to-s3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../../lib/error" 10 | }, 11 | { 12 | "path": "../../lib/logger" 13 | }, 14 | { 15 | "path": "../../lib/base" 16 | } 17 | ], 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /plugins/webpack/.gitignore: -------------------------------------------------------------------------------- 1 | test/files/dist 2 | -------------------------------------------------------------------------------- /plugins/webpack/.toolkitrc.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | Webpack: './lib/tasks/webpack' 3 | 4 | commands: 5 | 'build:local': 6 | Webpack: 7 | envName: development 8 | 'build:ci': 9 | Webpack: 10 | envName: production 11 | 'build:remote': 12 | Webpack: 13 | envName: production 14 | 'run:local': 15 | Webpack: 16 | envName: development 17 | watch: true 18 | 19 | version: 2 20 | -------------------------------------------------------------------------------- /plugins/webpack/jest.config.js: -------------------------------------------------------------------------------- 1 | const base = require('../../jest.config.base') 2 | 3 | module.exports = { 4 | ...base.config 5 | } 6 | -------------------------------------------------------------------------------- /plugins/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dotcom-tool-kit/webpack", 3 | "version": "4.3.4", 4 | "main": "lib", 5 | "description": "", 6 | "author": "FT.com Platforms Team ", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/financial-times/dotcom-tool-kit.git", 11 | "directory": "plugins/webpack" 12 | }, 13 | "homepage": "https://github.com/financial-times/dotcom-tool-kit/main/plugins/webpack", 14 | "bugs": "https://github.com/financial-times/dotcom-tool-kit/issues", 15 | "keywords": [], 16 | "scripts": { 17 | "test": "cd ../../ ; npx jest --silent --projects plugins/webpack" 18 | }, 19 | "dependencies": { 20 | "@dotcom-tool-kit/base": "^1.2.3", 21 | "@dotcom-tool-kit/error": "^4.1.1", 22 | "@dotcom-tool-kit/logger": "^4.2.2", 23 | "tslib": "^2.8.1", 24 | "webpack-cli": "^4.6.0", 25 | "zod": "^3.24.4" 26 | }, 27 | "peerDependencies": { 28 | "dotcom-tool-kit": "4.x", 29 | "webpack": "4.x.x || 5.x.x" 30 | }, 31 | "devDependencies": { 32 | "@jest/globals": "^29.7.0", 33 | "webpack": "^5.99.9", 34 | "winston": "^3.17.0" 35 | }, 36 | "files": [ 37 | "/lib", 38 | ".toolkitrc.yml" 39 | ], 40 | "volta": { 41 | "extends": "../../package.json" 42 | }, 43 | "engines": { 44 | "node": "18.x || 20.x || 22.x" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /plugins/webpack/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.settings.json", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "outDir": "lib", 6 | "rootDir": "src" 7 | }, 8 | "references": [ 9 | { 10 | "path": "../../lib/logger" 11 | }, 12 | { 13 | "path": "../../lib/base" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. 4 | 5 | # Checklist: 6 | 7 | - [ ] My branch has been rebased onto the latest commit on main (don't merge main into your branch) 8 | - [ ] My commit messages are [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/), for example: `feat(circleci): add support for nightly workflows`, `fix: set Heroku app name for staging apps too` 9 | -------------------------------------------------------------------------------- /scripts/circleci-publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | pattern='^([[:alnum:]-]*)-v[[:digit:]]{1,}\.[[:digit:]]{1,}\.[[:digit:]]{1,}(-[[:alpha:]]{1,}\.[[:digit:]]{1,}){0,1}$' 6 | if [[ ! $CIRCLE_TAG =~ $pattern ]]; then 7 | echo "cannot parse git tag $CIRCLE_TAG from CircleCI" 8 | exit 1 9 | fi 10 | 11 | workspace=${BASH_REMATCH[1]} 12 | if [[ $workspace = "orb" ]]; then 13 | echo "skipping orb package" 14 | exit 0 15 | elif [[ $workspace != 'dotcom-tool-kit' ]]; then 16 | workspace="@dotcom-tool-kit/$workspace" 17 | fi 18 | 19 | npm publish --workspace "$workspace" --access=public "$@" 20 | -------------------------------------------------------------------------------- /scripts/clean-up-packages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # third argument for the hook is a flag telling us if this was a branch move or a file checkout 4 | readonly isBranchMove="$3" 5 | 6 | if [ "$isBranchMove" = "1" ]; then 7 | echo "changed branches, cleaning up zombie packages..." 8 | for package in core/* plugins/* lib/*; do 9 | if ! [ -f "$package/package.json" ] && [ "$package" != "core/sandbox" ]; then 10 | echo "deleting $package" 11 | rm -rf "$package" 12 | fi 13 | done 14 | fi 15 | -------------------------------------------------------------------------------- /scripts/generate-and-commit-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | if git diff --quiet --exit-code && git diff --cached --quiet --exit-code; then 6 | HAS_CHANGES="no" 7 | else 8 | HAS_CHANGES="yes" 9 | fi 10 | 11 | if [ "$HAS_CHANGES" == "yes" ]; then 12 | git stash --staged --quiet 13 | fi 14 | 15 | npm run build 16 | node ./scripts/generate-docs.js 17 | 18 | if ! git diff --quiet --exit-code; then 19 | git commit -m 'docs: automatically regenerate schema docs' plugins/\*/readme.md 20 | 21 | echo '' 22 | echo -e $'\e[31m!! automatically generated documentation has been regenerated and committed. please push again\e[0m' 23 | echo '' 24 | 25 | if [ "$HAS_CHANGES" == "yes" ]; then 26 | git stash pop 27 | fi 28 | 29 | exit 1 30 | fi 31 | 32 | if [ "$HAS_CHANGES" == "yes" ]; then 33 | git stash pop 34 | fi 35 | -------------------------------------------------------------------------------- /tsconfig.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "declaration": true, 6 | "declarationMap": true, 7 | "importHelpers": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /types/financial-times__package-json/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@financial-times/package-json' { 2 | export type DependencyField = 3 | | 'dependencies' 4 | | 'devDependencies' 5 | | 'optionalDependencies' 6 | | 'peerDependencies' 7 | 8 | export interface IDependency { 9 | pkg: string 10 | version: string 11 | field: DependencyField 12 | } 13 | 14 | export interface IChangelog { 15 | event: E 16 | field: string 17 | previousValue?: string 18 | alreadyExisted: boolean 19 | meta: Record 20 | } 21 | 22 | export class PackageJson { 23 | requireScript(options: Record): IChangelog<'requireScript'> 24 | requireDependency(options: IDependency): IChangelog<'requireDependency'> 25 | removeDependency(options: Omit): IChangelog<'removeDependency'> 26 | hasChangesToWrite(): boolean 27 | writeChanges(): boolean 28 | getField(name: string): T 29 | setField(field: string, value: T): IChangelog<'setField'> 30 | } 31 | 32 | export default function loadPackageJson(options: Record): PackageJson 33 | } 34 | -------------------------------------------------------------------------------- /types/financial-times__package-json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@types/financial-times__package-json", 3 | "private": true, 4 | "version": "1.9.0", 5 | "description": "", 6 | "types": "index.d.ts", 7 | "keywords": [], 8 | "author": "", 9 | "license": "ISC" 10 | } 11 | --------------------------------------------------------------------------------