Compare commits
4 Commits
1.0.0-alph
...
1.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86ac444781 | ||
|
|
8e7703652e | ||
|
|
a53aab7ccf | ||
|
|
e8ce789943 |
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Lint
|
||||
on: [push, pull_request]
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
||||
28
README.md
28
README.md
@@ -109,6 +109,34 @@ Stopping local database containers...
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
The goal of LDD is to speed up the process of setting up new projects and synchronizing a common system configuration across multiple environments. That's why we don't plan to support deep customization options.
|
||||
|
||||
However, there are some common use cases that require a bit more flexibility, so the following features may help.
|
||||
|
||||
### Project config files
|
||||
|
||||
Each project usually requires its own database, and you will probably need to run most commands against it, depending on the project you are working on.
|
||||
|
||||
The closest available `ldd.json` file is used to load the configuration for the current project:
|
||||
|
||||
```json
|
||||
{
|
||||
"dbName": "my-awesome-app"
|
||||
}
|
||||
```
|
||||
|
||||
With the configuration above, any command will default to `my-awesome-app` as the `<db_name>` argument value if nothing is passed manually:
|
||||
|
||||
```bash
|
||||
$ yarn ldd create
|
||||
Loading configuration from: /MyProjects/my-awesome-app/ldd.json
|
||||
Creating a new DB named "my-awesome-app"...
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
### ENV variables
|
||||
|
||||
We hope you never have to use them, but just in case, here are some ENV vars you can set on your machine to customize the behavior of the application:
|
||||
|
||||
- `LDD_DB_IMAGE_TAG` (default: `latest`): we use the official [MariaDB](https://hub.docker.com/_/mariadb) Docker image. You can pick a different tag if you wish.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mep-agency/local-dev-db",
|
||||
"version": "1.0.0-alpha9",
|
||||
"version": "1.0.0-alpha10",
|
||||
"private": false,
|
||||
"description": "A zero-config local MariaDB instance for local development (using Docker)",
|
||||
"author": "Marco Lipparini <developer@liarco.net>",
|
||||
|
||||
47
src/config.ts
Normal file
47
src/config.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const JSON_CONFIG_FILE_NAME = 'ldd.json';
|
||||
|
||||
const DEFAULT_CONFIG: JsonConfiguration = {
|
||||
dbName: undefined,
|
||||
};
|
||||
|
||||
interface JsonConfiguration {
|
||||
dbName?: string;
|
||||
}
|
||||
|
||||
const findAndReadConfig = () => {
|
||||
try {
|
||||
let startdir = process.cwd();
|
||||
let userConfigData = '{}';
|
||||
|
||||
while (true) {
|
||||
var list = fs.readdirSync(startdir);
|
||||
|
||||
if (list.indexOf(JSON_CONFIG_FILE_NAME) != -1) {
|
||||
// Found
|
||||
console.info(`Loading configuration from: ${path.join(startdir, JSON_CONFIG_FILE_NAME)}`);
|
||||
|
||||
userConfigData = fs.readFileSync(path.join(startdir, JSON_CONFIG_FILE_NAME)).toString();
|
||||
break;
|
||||
} else if (startdir == '/') {
|
||||
// Root dir, file not found
|
||||
break;
|
||||
} else {
|
||||
startdir = path.normalize(path.join(startdir, '..'));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...DEFAULT_CONFIG,
|
||||
...JSON.parse(userConfigData),
|
||||
} as JsonConfiguration;
|
||||
} catch (e) {
|
||||
console.error('ERROR: Failed reading LDD configuration file...');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
export default findAndReadConfig();
|
||||
67
src/index.ts
67
src/index.ts
@@ -5,8 +5,19 @@ import { dockerCommand } from 'docker-cli-js';
|
||||
import mysql from 'mysql';
|
||||
|
||||
import packageInfo from '../package.json';
|
||||
import config from './config';
|
||||
|
||||
let PACKAGE_INSTALLATION_PATH = `${__dirname}/../..`;
|
||||
const PACKAGE_INSTALLATION_PATH = `${__dirname}/../..`;
|
||||
|
||||
interface DockerImagesCommandResult {
|
||||
images: {
|
||||
repository: string;
|
||||
tag: string;
|
||||
'image id': string;
|
||||
created: string;
|
||||
size: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
const dockerCompose: typeof dockerCommand = async (command, options) => {
|
||||
try {
|
||||
@@ -65,12 +76,32 @@ program.name('ldd').description(packageInfo.description).version(packageInfo.ver
|
||||
program
|
||||
.command('start')
|
||||
.description('Starts your local DB server')
|
||||
.action(async (str, options) => {
|
||||
.action(async () => {
|
||||
console.info('Starting local database containers...');
|
||||
|
||||
const requiredImages = [
|
||||
`mariadb:${process.env.LDD_DB_IMAGE_TAG ?? 'latest'}`,
|
||||
`phpmyadmin:${process.env.LDD_PMA_IMAGE_TAG ?? 'latest'}`,
|
||||
];
|
||||
|
||||
const availableImagesImages = ((await dockerCommand('images', { echo: false })) as DockerImagesCommandResult).images
|
||||
.map((imageData) => `${imageData.repository}:${imageData.tag}`)
|
||||
.filter((imageName) => requiredImages.includes(imageName));
|
||||
|
||||
const missingImages = requiredImages.filter((requiredImage) => !availableImagesImages.includes(requiredImage));
|
||||
|
||||
if (missingImages.length > 0) {
|
||||
console.info('');
|
||||
console.info('The following images will be downloaded as they are required but not available:');
|
||||
missingImages.map((image) => console.info(` - ${image}`));
|
||||
console.info('');
|
||||
console.info('This may take some time, please wait...');
|
||||
}
|
||||
|
||||
await dockerCompose('up -d');
|
||||
|
||||
console.info('');
|
||||
console.info('Done!');
|
||||
console.info(`A PhpMyAdmin instance is running on: http://127.0.0.1:${process.env.LDD_PMA_PORT ?? 8010}`);
|
||||
});
|
||||
|
||||
@@ -106,7 +137,7 @@ program
|
||||
program
|
||||
.command('create')
|
||||
.description('Creates a new database')
|
||||
.argument('<db_name>', 'The database name')
|
||||
.argument(config.dbName !== undefined ? '[db_name]' : '<db_name>', 'The database name', config.dbName)
|
||||
.action(async (databaseName) => {
|
||||
const username = databaseName;
|
||||
const userPwd = `${databaseName}-pwd`;
|
||||
@@ -126,15 +157,18 @@ program
|
||||
program
|
||||
.command('drop')
|
||||
.description('Drops the given database and its default user (if they exist)')
|
||||
.argument('<db_name>', 'The database name')
|
||||
.action(async (databaseName) => {
|
||||
.argument(config.dbName !== undefined ? '[db_name]' : '<db_name>', 'The database name', config.dbName)
|
||||
.option('-f,--force', 'Skip safety confirmation', false)
|
||||
.action(async (databaseName, options) => {
|
||||
const username = databaseName;
|
||||
const userPwd = `${databaseName}-pwd`;
|
||||
|
||||
const confirmation = await confirm({
|
||||
message: `This action will delete your database "${databaseName}" and cannot be reverted. Are you sure?`,
|
||||
default: false,
|
||||
});
|
||||
const confirmation =
|
||||
options.force === true ||
|
||||
(await confirm({
|
||||
message: `This action will delete your database "${databaseName}" and cannot be reverted. Are you sure?`,
|
||||
default: false,
|
||||
}));
|
||||
|
||||
if (confirmation !== true) {
|
||||
console.info('Aborting...');
|
||||
@@ -174,7 +208,7 @@ program
|
||||
program
|
||||
.command('dump')
|
||||
.description('Creates a SQL dump file of the given database')
|
||||
.argument('<db_name>', 'The database name')
|
||||
.argument(config.dbName !== undefined ? '[db_name]' : '<db_name>', 'The database name', config.dbName)
|
||||
.action(async (databaseName) => {
|
||||
const now = new Date();
|
||||
const month = now.getMonth().toString().padStart(2, '0');
|
||||
@@ -200,11 +234,14 @@ program
|
||||
.command('import')
|
||||
.description('Runs all queries from the given SQL file')
|
||||
.argument('<sql_file_path>', 'The SQL file to import')
|
||||
.action(async (sqlFilePath) => {
|
||||
const confirmation = await confirm({
|
||||
message: 'This action will execute any SQL statement found in the given file and cannot be reverted. Are you sure?',
|
||||
default: false,
|
||||
});
|
||||
.option('-f,--force', 'Skip safety confirmation', false)
|
||||
.action(async (sqlFilePath, options) => {
|
||||
const confirmation =
|
||||
options.force === true ||
|
||||
(await confirm({
|
||||
message: 'This action will execute any SQL statement found in the given file and cannot be reverted. Are you sure?',
|
||||
default: false,
|
||||
}));
|
||||
|
||||
if (confirmation !== true) {
|
||||
console.info('Aborting...');
|
||||
|
||||
Reference in New Issue
Block a user