Skip to content

Instantly share code, notes, and snippets.

@dgreene1
Created July 25, 2019 14:26
Show Gist options
  • Save dgreene1/0506e91c81e7a5b3465b74ea72db1235 to your computer and use it in GitHub Desktop.
Save dgreene1/0506e91c81e7a5b3465b74ea72db1235 to your computer and use it in GitHub Desktop.
How to check that you generated a schema when a TypeOrm entities change
import { execSync } from 'child_process';
import * as fs from 'fs';
// tslint:disable-next-line: no-console
console.log('...');
// tslint:disable-next-line: no-console
console.log('Starting the process of checking if a migration script exists for the current schema...');
const failureArtifactName = 'FAILURE--THIS_PROVES_A_DEV_FORGOT_TO_GENERATE_AND_APPLY_A_SCHEMA';
const walk = (dir: string) : string[] => {
let results: string[] = [];
const list = fs.readdirSync(dir);
list.forEach(file => {
file = dir + '/' + file;
const stat = fs.statSync(file);
if (stat && stat.isDirectory()) {
/* Recursively iterate into a subdirectory */
results = results.concat(walk(file));
} else {
/* Is a file */
results.push(file);
}
});
return results;
}
const findFileInNestedPath = (startingDir: string, fileNameSection: string): string => {
const paths = walk(startingDir);
const results = paths.filter(aPath => aPath.includes(fileNameSection));
if(results.length === 0){
throw new Error(`Could not find a file containing ${fileNameSection} within the ${startingDir} directory`);
}
if(results.length > 1){
throw new Error(`Did not expect to find more than one file. However, we found: ${results.join(' and ')}`);
}
return results[0];
}
const stdout = execSync(
`ts-node ./node_modules/typeorm/cli migration:generate --dir ./src/db/migration --name ${failureArtifactName}`,
);
const outputAStr = stdout.toString();
if (!outputAStr.includes('No changes in database schema were found')) {
const generalErrMsg = 'The developer forgot to create a migration script';
const errorMsg = outputAStr.includes('generated successfully')
? `${generalErrMsg} and/or there were issues: ${outputAStr}`
: generalErrMsg;
// Now delete the artifact since you don't want that to hang around
try{
const pathOfArtifact = findFileInNestedPath('.', failureArtifactName);
fs.unlinkSync(pathOfArtifact);
} catch(err){
// tslint:disable-next-line: no-console
console.warn('Unable to cleanup the artifact due to this error: ' + err);
}
throw new Error(errorMsg);
} else {
// tslint:disable-next-line: no-console
console.log('Success: Schemas are up-to-date with the code.');
}
{
"name": "EXAMPLE REPO NAME",
"version": "1.3.0",
"engines": {
"node": "10.15.3"
},
"description": "THIS IS AN EXAMPLE. PLEASE DON'T COPY AS IS",
"main": "build/index.js",
"nodemonConfig": {
"ignore": [
"serverless/*",
"docs/*",
"docker/*",
"coverage/*"
]
},
"nyc": {
"extension": [
".ts"
],
"exclude": [
"**/*.d.ts",
"**/*_spec.ts",
"build",
"scripts",
"dev-**",
"coverage",
"plopfile.js",
"test/mocks",
"test/fixtures",
"test/util",
"src/api/consumer",
"src/db/entities",
"src/db/migrations",
"src/db/*.ts"
],
"reporter": [
"html",
"lcov",
"text"
],
"all": true
},
"husky": {
"hooks": {
"pre-commit": "lint-staged && yarn pre-commit"
}
},
"lint-staged": {
"*.{js,ts}": [
"prettier --print-width 120 --use-tabs true --single-quote true --trailing-comma all --write",
"git add"
]
},
"scripts": {
"build": "yarn tsoa:gen && tsc && mkdirp .tmp && yarn cp:swagger && yarn cp:configs && yarn cp:testfiles",
"cp:swagger": "cpx \"./src/swagger.yml\" \"./build\"",
"cp:configs": "cpx \"./src/config/*.json\" \"./build/config\"",
"cp:testfiles": "cpx src/test/fixtures/json/ ./build/test/fixtures/json",
"db:init": "docker-compose up -d && echo 'Next you need to navigate to http://localhost:8000 then enter the email and password defined in docker-compose.yml's pgadmin.environment variables then create a server using the settings defined in docker-compose.yml's db.environment. Note: the ip address on windows will not be localhost, it will be whatever the result is of docker inspect postgres --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ' ",
"db:migrate:generate": "echo 'If successful, ignore this message. If it failed, then you forgot to pass a name for this specific migration. Might I recommend the ticket number and branch name?' && ts-node ./node_modules/typeorm/cli migration:generate --dir ./src/db/migration --name",
"db:migrate:run": "ts-node ./node_modules/typeorm/cli migration:run",
"db:migrate:revert": "ts-node ./node_modules/typeorm/cli migration:revert",
"db:migrate:diff": "ts-node ./src/failIfMigrationIsNeeded.ts",
"db:start": "docker-compose start",
"db:stop": "docker-compose stop",
"db:destroy": "docker-compose down --volumes",
"dev-server": "mkdirp .tmp && cross-env NODE_ENV=development TZ=UTC nodemon --inspect --nolazy -r ts-node/register -- src/index.ts",
"dev-server:debug": "cross-env DEBUG=di-* yarn dev-server",
"docker:build": "docker build -t msvc-voice-settings .",
"docker:run": "docker run -p 3000:5555 msvc-voice-settings",
"lint": "tslint \"{serverless,src}/**/*.ts\"",
"start": "cross-env PORT=3000 TZ=UTC node build/index.js",
"start:stage": "cross-env NODE_ENV=stage TZ=UTC node build/index.js",
"tsoa:gen": "yarn tsoa swagger -c ./tsoa.json && yarn tsoa routes -c ./tsoa.json",
"available-routes": "ts-node src/util/available-routes.ts",
"pretest": "yarn run build",
"test": "cross-env NODE_ENV=test PORT=5554 TZ=UTC mocha --exit --require ts-node/register -t 10000 \"./src/**/*_spec.ts\"",
"test:watch": "cross-env NODE_ENV=test PORT=5554 TZ=UTC mocha --watch --watch-extensions ts --exit --require ts-node/register -t 10000 \"./src/**/*_spec.ts\"",
"test:cov": "nyc yarn run test",
"pre-commit": "tsc && yarn db:migrate:diff && yarn lint && yarn run test:no-watch",
"tsnode": "node -r ts-node/register"
},
"repository": "THIS IS AN EXAMPLE. PLEASE DON'T COPY THIS AS IS",
"license": "UNLICENSED",
"private": true,
"contributors": [
],
"dependencies": {
"@types/pino": "^5.8.8",
"@types/uuid": "^3.4.5",
"axios": "^0.18.0",
"boom": "^7.2.0",
"cross-env": "^5.2.0",
"js-joda": "^1.9.3",
"js-joda-timezone": "^2.0.2",
"kcors": "^2.2.1",
"koa": "^2.5.1",
"koa-bodyparser": "^4.2.1",
"koa-busboy": "^1.1.1",
"koa-compose": "^4.1.0",
"koa-compress": "^3.0.0",
"koa-router": "^7.4.0",
"lodash": "^4.17.11",
"nconf": "^0.10.0",
"pg": "^7.4.3",
"pino": "^5.13.0",
"pino-pretty": "^3.2.0",
"prom-client": "^11.0.0",
"query-string": "^6.1.0",
"shortid": "^2.2.8",
"swagger2-koa": "^1.0.3",
"tsoa": "^2.3.81",
"typeorm": "^0.2.13",
"uuid": "^3.3.2"
},
"devDependencies": {
"@types/boom": "^7.2.0",
"@types/debug": "^0.0.30",
"@types/http-proxy": "^1.16.1",
"@types/kcors": "^2.2.3",
"@types/koa": "^2.0.45",
"@types/koa-bodyparser": "^4.2.0",
"@types/koa-compress": "^2.0.8",
"@types/koa-router": "^7.0.28",
"@types/lodash": "^4.14.109",
"@types/nconf": "^0.0.37",
"@types/node": "^10.3.3",
"@types/query-string": "^5.1.0",
"@types/shortid": "^0.0.29",
"axios-mock-adapter": "^1.15.0",
"cpx": "^1.5.0",
"factory.ts": "^0.2.2",
"husky": "^1.0.1",
"koa-sslify": "^2.1.2",
"lint-staged": "^7.1.2",
"mkdirp": "^0.5.1",
"nodemon": "^1.17.5",
"npm": "^6.0.1",
"nyc": "^11.8.0",
"prettier": "^1.12.1",
"ts-node": "^6.0.3",
"tslint": "^5.10.0",
"tslint-config-prettier": "^1.13.0",
"typescript": "^3.3.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment