initial commit
This commit is contained in:
commit
ce5e2a852f
|
@ -0,0 +1,16 @@
|
|||
# It's possible to specify any number of targets
|
||||
|
||||
# Example configuration
|
||||
# Run 'hamstr mysqldump prod' to target this
|
||||
HAMSTR_PROD_DB=mysql://username:pw@127.0.0.1:3306/databasename
|
||||
HAMSTR_PROD_SSH=username@someserver.com
|
||||
HAMSTR_PROD_SSH_PW=randompassword
|
||||
|
||||
# Run 'hamstr mysqldump stage' to target this
|
||||
HAMSTR_STAGE_DB=
|
||||
HAMSTR_STAGE_SSH=
|
||||
HAMSTR_STAGE_SSH_PW=
|
||||
|
||||
# If you don't need tunnelling you just need
|
||||
# to provide a DB connection string
|
||||
HAMSTR_QA_DB=
|
|
@ -0,0 +1,6 @@
|
|||
.env
|
||||
*.sql
|
||||
/node_modules/
|
||||
.env
|
||||
.DS_Store
|
||||
/dist
|
|
@ -0,0 +1,3 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 'lts/*'
|
|
@ -0,0 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- Initial release.
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Nils Norman Haukås
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,30 @@
|
|||
# HAMSTR
|
||||
|
||||
Move mountains of SQL data.
|
||||
|
||||
## Features
|
||||
|
||||
* Run mysqldump through a ssh tunnel.
|
||||
|
||||
## Installation
|
||||
|
||||
Global installation: `npm i -g hamstr`
|
||||
|
||||
Project installation: `npm i hamstr`
|
||||
|
||||
## Usage
|
||||
|
||||
1. Copy `.env-example` into `.env` and fill out credentials.
|
||||
1. Run `hamstr mysqldump --tunnel prod` to download the database target `PROD` defined in the .env file.
|
||||
1. Run `hamstr --help` to get a functionality overview.
|
||||
|
||||
## Development and test
|
||||
|
||||
Not many tests yet, but we have Typescript.
|
||||
|
||||
1. Run `npm test` to trigger typescript build.
|
||||
* Run `npm build -- --watch` to start a auto-compiling process which builds on file save.
|
||||
|
||||
## License
|
||||
|
||||
Project code is licensed under the MIT license, [see license](LICENSE.txt).
|
|
@ -0,0 +1,96 @@
|
|||
{
|
||||
"name": "hamstr",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "8.10.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.17.tgz",
|
||||
"integrity": "sha512-3N3FRd/rA1v5glXjb90YdYUa+sOB7WrkU2rAhKZnF4TKD86Cym9swtulGuH0p9nxo7fP5woRNa8b0oFTpCO1bg==",
|
||||
"dev": true
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
|
||||
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
|
||||
"integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow=="
|
||||
},
|
||||
"lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"mysql-parse": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/mysql-parse/-/mysql-parse-2.0.3.tgz",
|
||||
"integrity": "sha512-P+iGsXIZFbfU7Im1WRQWG2/OEnBxTMotRgrXV8V1zRNdbUeBHaW4ojtKRf8P1JQ5QfJ3z4bfZFfZXZhxir89/A=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
},
|
||||
"ssh2": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.5.4.tgz",
|
||||
"integrity": "sha1-G/a2soyW6u8mf01sRqWiUXpZnic=",
|
||||
"requires": {
|
||||
"ssh2-streams": "~0.1.15"
|
||||
}
|
||||
},
|
||||
"ssh2-streams": {
|
||||
"version": "0.1.20",
|
||||
"resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.1.20.tgz",
|
||||
"integrity": "sha1-URGNFUVV31Rp7h9n4M8efoosDjo=",
|
||||
"requires": {
|
||||
"asn1": "~0.2.0",
|
||||
"semver": "^5.1.0",
|
||||
"streamsearch": "~0.1.2"
|
||||
}
|
||||
},
|
||||
"streamsearch": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
||||
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
||||
},
|
||||
"tunnel-ssh": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-ssh/-/tunnel-ssh-4.1.4.tgz",
|
||||
"integrity": "sha512-CjBqboGvAbM7iXSX2F95kzoI+c2J81YkrHbyyo4SWNKCzU6w5LfEvXBCHu6PPriYaNvfhMKzD8bFf5Vl14YTtg==",
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
"lodash.defaults": "^4.1.0",
|
||||
"ssh2": "0.5.4"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz",
|
||||
"integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "hamstr",
|
||||
"version": "1.0.0",
|
||||
"description": "Let the hamster move mountains of MySQL data.",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "tsc"
|
||||
},
|
||||
"keywords": [
|
||||
"mysql",
|
||||
"ssh-tunnel",
|
||||
"mysqldump"
|
||||
],
|
||||
"author": "Nils Norman Haukås",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^2.15.1",
|
||||
"dotenv": "^5.0.1",
|
||||
"mysql-parse": "^2.0.3",
|
||||
"tunnel-ssh": "^4.1.4"
|
||||
},
|
||||
"bin": {
|
||||
"hamstr": "./dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^8.10.17",
|
||||
"typescript": "^2.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
declare module 'tunnel-ssh'
|
||||
declare module 'mysql-parse'
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env node
|
||||
import program from 'commander'
|
||||
require('dotenv').config()
|
||||
const pjson = require('../package.json')
|
||||
|
||||
program.version(pjson.version).description(pjson.description)
|
||||
|
||||
program
|
||||
.command('mysqldump <target>')
|
||||
.description('Use mysqldump to download target defined in .env file.')
|
||||
.option('-t --tunnel', 'open an ssh tunnel before running mysqldump')
|
||||
.option(
|
||||
'-p --path <path>',
|
||||
'Path to save mysql dump, default is saving into a file named target.sql'
|
||||
)
|
||||
.action((target, options) => {
|
||||
try {
|
||||
require('./lib').mysqldump({ target, options })
|
||||
} catch (e) {
|
||||
console.error(e.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
||||
|
||||
program.parse(process.argv)
|
||||
|
||||
if (process.argv.slice(2).length === 0) {
|
||||
console.error('Heyo! Run "hamstr --help" for the manual.')
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
import { buildMysqlParams, parseUri } from 'mysql-parse'
|
||||
import { promisify } from 'util'
|
||||
const exec = promisify(require('child_process').exec)
|
||||
const tunnel = promisify(require('tunnel-ssh'))
|
||||
|
||||
interface ProgramInput {
|
||||
target: string
|
||||
options: {
|
||||
tunnel?: Boolean
|
||||
path?: string
|
||||
}
|
||||
}
|
||||
|
||||
interface MysqldumpConfig {
|
||||
uri: string
|
||||
path: string
|
||||
}
|
||||
|
||||
interface TunnelConfig {
|
||||
host: string
|
||||
dstPort: string
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
function buildMysqldumpConfig({
|
||||
target,
|
||||
options
|
||||
}: ProgramInput): MysqldumpConfig {
|
||||
const uri = getOrExit(`HAMSTR_${target}_DB`)
|
||||
const { path = `${target.toLowerCase()}.sql` } = options
|
||||
return { uri, path }
|
||||
}
|
||||
|
||||
function buildTunnelConfig({ target, options }: ProgramInput): TunnelConfig {
|
||||
const [originalstring, username, host] = <Array<string>>/(\w*)@(.*)/g.exec(
|
||||
getOrExit(`HAMSTR_${target}_SSH`)
|
||||
)
|
||||
const password = getOrExit(`HAMSTR_${target}_SSH_PW`)
|
||||
const { port: dstPort } = parseUri(getOrExit(`HAMSTR_${target}_DB`))
|
||||
return { host, dstPort, username, password }
|
||||
}
|
||||
|
||||
async function mysqldump({ target, options }: ProgramInput) {
|
||||
target = target.toUpperCase()
|
||||
if (options.tunnel) {
|
||||
await openTunnel(buildTunnelConfig({ target, options }))
|
||||
}
|
||||
runMysqldump(buildMysqldumpConfig({ target, options }))
|
||||
}
|
||||
|
||||
async function openTunnel(tunnelConfig: TunnelConfig) {
|
||||
await tunnel(tunnelConfig)
|
||||
const { username, host } = tunnelConfig
|
||||
console.log(`Opened tunnel to ${username}@${host}`)
|
||||
}
|
||||
|
||||
async function runMysqldump({ uri, path }: MysqldumpConfig) {
|
||||
const params = buildMysqlParams(uri)
|
||||
console.log(`Running mysqldump, saving data in ${path}`)
|
||||
const child = await exec(`mysqldump ${params} > ${path}`, null)
|
||||
// if we kill node we should kill mysql as well
|
||||
process.on('exit', () => child.kill())
|
||||
}
|
||||
|
||||
function getOrExit(variable: string): string {
|
||||
if (!process.env[variable]) {
|
||||
throw new Error(
|
||||
`Tried looking up ${variable} but could not find it. \n\nDid you define an entry for this in your .env file?`
|
||||
)
|
||||
}
|
||||
return process.env[variable] || ''
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mysqldump
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"outDir": "./dist" /* Redirect output structure to the directory. */,
|
||||
"rootDir":
|
||||
"./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution":
|
||||
"node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue