├── .gitignore ├── Changelog.md ├── Readme.md ├── config-sample.cfg ├── db └── migrate │ └── .gitkeep ├── get_current.js ├── mongo_migrate ├── template └── migration_template.js └── update_current.js /.gitignore: -------------------------------------------------------------------------------- 1 | db/migrate/*.js 2 | !db/migrate/.gitkeep 3 | config.cfg 4 | current 5 | .tmp_*~ 6 | *# 7 | .#* 8 | 9 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | 0.5.0 / 2011-12-13 2 | ================== 3 | * Added option to pass config file path by parameter - Issue #8 4 | * Changed the command-line argument interface (using optargs) 5 | * Improved documentation 6 | 7 | 0.4.1 / 2011-12-13 8 | ================== 9 | * Fixed all_down to use the current migration logic 10 | 11 | 0.4.0 / 2011-12-13 12 | ================== 13 | * Current logic now uses migration_info collection and version attribute to manage state 14 | 15 | 0.3.0 / 2011-12-09 16 | ================== 17 | * Authentication support via MONGO_USR and MONGO_PWD at config.cfg 18 | * Allow user to only use MONGO_USR and require password when running the migration 19 | 0.2.0 / 2011-12-09 20 | ================== 21 | * runall now is only run without name 22 | * Improved docs (Douglas Campos - qmx) 23 | 24 | 0.1.0 / 2011-12-09 25 | ================== 26 | * Can run individual migration (up and down) 27 | * Can run all migrations at once (up and down) 28 | * Controll the current migration 29 | * Initial version 30 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | MongoDB migrate 2 | =============== 3 | This project aims to be an agnostic migration system for mongodb. Sometimes when you add attributes, you need to give a value for existing documents. Doing this frequently, this tool can help, since it has a straightforward syntax and simplicity. 4 | 5 | Dependencies 6 | ------------ 7 | MongoDB client - the bin file that comes with mongodb. I will soon create an installation script to make things easier. For now you need to download it from mongodb website. - 8 | 9 | Instalation 10 | ----------- 11 | $ git clone git://github.com/emerleite/mongo-migrate.git 12 | $ cd mongo-migrate 13 | $ cp config-sample.cfg config.cfg 14 | 15 | Edit *config.cfg* with your database information 16 | 17 | ### Basic information 18 | By convention, mongo migrate uses the directory db/migrate when generating and running migrations. You can change it, by editing the MIGRATION_DIR configuration at config.cfg. See config.sample.cfg for details. 19 | 20 | ### Configuration 21 | MongoDB migrate uses a configuration file called config.cfg. It has the following options: 22 | 23 | * *MONGO_HOST* - The database host. Default is localhost 24 | * *MONGO_DATABASE* - Required. Do not have default value 25 | * *MIGRATION_DIR* - Where to find migrations. Default is ./db/migrate/ 26 | * *MONGO_USR* - Username. Default is empty 27 | * *MONGO_PWD* - Password. Default is empty 28 | 29 | Note: When you use MONGO_USR and not MONGO_PWD you'll be prompted for password. 30 | 31 | Usage 32 | ----- 33 | Mongo migrate has two modes. `generate` and `run`. The syntax is pretty straightforward as explained above: 34 | 35 | ### Generate 36 | Mongo migrate generates migration in the following format: 37 | 38 | YYYMMDDhhmmssmm_name.js. Ex: 20111209002426_add_person.js 39 | 40 | $ ./mongo_migrate -gf [migration_name] 41 | 42 | This will generate the following file: 43 | 44 | ```js 45 | var migration = { 46 | up: function() { 47 | 48 | }, 49 | down: function() { 50 | 51 | } 52 | }; 53 | 54 | migration[target].call(); 55 | ``` 56 | 57 | Put the code for the changes on up and the code for rollback on down. 58 | 59 | ### Running 60 | Mongo migrate has 2 modes of running. Up and Down. The first make things happen and the second is a rollback. For these 2 modes, we call run one specific migration or all migrations. Mongo migrate controls the current migration, to enable run only the last migrations or just run all. 61 | 62 | ### Options 63 | -g generate 64 | -r run 65 | -t target (up|down) 66 | -f file_name 67 | -c configuration file 68 | 69 | ### One migration 70 | $ ./mongo_migrate -rt up -f [file_name] 71 | $ ./mongo_migrate -rt down -f [file_name] 72 | 73 | The name can be the full name or partial name. Ex: 20111209002426_add_person.js or only add_person. Mongo migrate will first look for a full name and after will fallback to find the file based on the partial name. 74 | 75 | ### All migrations 76 | $ ./mongo_migrate -rt up 77 | $ ./mongo_migrate -rt down 78 | 79 | ### Configuration file 80 | If you want to pass the configuration file path and not use the convention (i.e ./config.cfg), just use the -c option parameter: 81 | 82 | $ ./mongo_migrate -rt up -f migration_file -c /path/to/config/file 83 | $ ./mongo_migrate -rt down -c /path/to/config/file 84 | $ ./mongo_migrate -gf migration_name -c /path/to/config/file 85 | 86 | When running all migrations, mongo migrate will run only the new migrations, because it controls which is the last one. 87 | 88 | To-Do 89 | ----- 90 | * see () 91 | 92 | Author 93 | ------ 94 | 95 | * Emerson Macedo () 96 | 97 | License: 98 | -------- 99 | 100 | (The MIT License) 101 | 102 | Permission is hereby granted, free of charge, to any person obtaining 103 | a copy of this software and associated documentation files (the 104 | 'Software'), to deal in the Software without restriction, including 105 | without limitation the rights to use, copy, modify, merge, publish, 106 | distribute, sublicense, and/or sell copies of the Software, and to 107 | permit persons to whom the Software is furnished to do so, subject to 108 | the following conditions: 109 | 110 | The above copyright notice and this permission notice shall be 111 | included in all copies or substantial portions of the Software. 112 | 113 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 114 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 115 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 116 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 117 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 118 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 119 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 120 | -------------------------------------------------------------------------------- /config-sample.cfg: -------------------------------------------------------------------------------- 1 | MONGO_DATABASE=yourdatabase #required 2 | MONGO_HOST=dbhost #default is localhost 3 | MIGRATION_DIR="your/migrate/dir/" #default is db/migrate/ 4 | MONGO_USR= #default is empty 5 | MONGO_PWD= #default is empty 6 | -------------------------------------------------------------------------------- /db/migrate/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emerleite/mongo-migrate/4b282b038a07c997a521da0f78423d0ba4b7e76c/db/migrate/.gitkeep -------------------------------------------------------------------------------- /get_current.js: -------------------------------------------------------------------------------- 1 | var migration_info = db.migration_info.findOne(); 2 | if (migration_info) { 3 | print(migration_info.version); 4 | } else { 5 | print(0); 6 | } 7 | -------------------------------------------------------------------------------- /mongo_migrate: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ### BEGIN INFO 4 | # Provides: Mongodb migration manager 5 | # Author: Emerson Macedo - http://github.com/emerleite - http://codificando.com 6 | # Description: Generate migration script 7 | ### END INFO 8 | 9 | ### SCRIPT FUNCTIONS 10 | 11 | usage() { 12 | echo "Usage: mongo_migrate [options] [params] 13 | -g generate 14 | -r run 15 | -t target (up or down) 16 | -f migration_file|migration_name 17 | -c configuration file" >&2 18 | echo "" 19 | exit 1 20 | } 21 | 22 | ensure_installed() { 23 | if [ ! `type -P $1` ] 24 | then 25 | echo "$1 is not installed. Please install before continue." 26 | exit 127 27 | fi 28 | } 29 | 30 | ensure_migration_dir() { 31 | if [ ! -d $MIGRATION_DIR ] 32 | then 33 | echo "Migration dir does not exist: $MIGRATION_DIR\n" 34 | exit 1 35 | fi 36 | } 37 | 38 | ensure_database() { 39 | if [ -z $MONGO_DATABASE ]; then 40 | echo "MONGO_DATABASE not defined" 41 | echo "Define it at config.cfg\n" 42 | exit 1 43 | fi 44 | } 45 | 46 | generate_migration_file_name() { 47 | echo "`date -u +%Y%m%d%H%M%S`_$1.js" 48 | } 49 | 50 | get_migration_file_name() { 51 | if [ -e $MIGRATION_DIR$1 ]; 52 | then 53 | echo $MIGRATION_DIR$1 54 | else 55 | echo "$MIGRATION_DIR$(find ./$MIGRATION_DIR -name '[0-9]*' |grep --color=never _$1.js |awk -F/ '{print $NF}')" 56 | fi 57 | } 58 | 59 | check_migration() { 60 | local file=`get_migration_file_name $1` 61 | local total_files=`get_migration_file_name $1 |wc -l` 62 | 63 | if [ ! $total_files = '1' ]; then 64 | echo "You have 2 files, please use the full file name" 65 | echo "Files:" 66 | echo "$file\n" 67 | exit 1 68 | fi 69 | 70 | if [ ! -f $file ]; then 71 | echo "Migration not found at $MIGRATION_DIR\n" 72 | exit 1 73 | fi 74 | } 75 | 76 | generate() { 77 | echo "Generating migration ${1}" 78 | local file=`generate_migration_file_name ${1}` 79 | cp template/migration_template.js $MIGRATION_DIR$file 80 | echo "Migration ${MIGRATION_DIR}${file} generated" 81 | } 82 | 83 | run_migration() { 84 | local name=$1 85 | local target=$2 86 | 87 | echo "running migration ${name} ${target}\n" 88 | ensure_installed mongo 89 | ensure_database 90 | 91 | if [[ $target != "up" && $target != "down" ]];then usage; fi 92 | 93 | local file=`get_migration_file_name ${name}` 94 | 95 | check_migration $name 96 | 97 | mongo $MONGO_HOST/$MONGO_DATABASE $auth --eval "target='${target}'" $file 98 | if [ $? -ne 0 ]; 99 | then 100 | echo "\nerror executing migration.\n" 101 | exit 1 102 | fi 103 | 104 | [ $target = "up" ] && local inc="+1" || local inc="-1" 105 | local migration_name="$(echo $file |awk -F/ '{print $NF}')" 106 | local migration_time="$(echo $migration_name |cut -d_ -f1 |bc)" 107 | 108 | update_current_migration `echo "$migration_time$inc"|bc` 109 | 110 | echo "Migration ${name} ${target} executed\n" 111 | } 112 | 113 | get_current_migration() { 114 | echo "`mongo $MONGO_HOST/$MONGO_DATABASE $auth --quiet ./get_current.js`" 115 | } 116 | 117 | update_current_migration() { 118 | mongo $MONGO_HOST/$MONGO_DATABASE $auth --quiet --eval "current='${1}'" ./update_current.js 119 | } 120 | 121 | all_up() { 122 | local current_migration=`get_current_migration|bc` 123 | 124 | local migrations="$(find $MIGRATION_DIR -name \*.js |sort -n)" 125 | for migration in $migrations 126 | do 127 | local migration_name="$(echo $migration |awk -F/ '{print $NF}')" 128 | local migration_time="$(echo $migration_name |cut -d_ -f1 |bc)" 129 | if [[ $migration_time -gt $current_migration ]]; then 130 | run_migration $migration_name up 131 | fi 132 | done 133 | } 134 | 135 | all_down() { 136 | local current_migration=`get_current_migration|bc` 137 | 138 | local migrations="$(find $MIGRATION_DIR -name \*.js |sort -nr)" 139 | for migration in $migrations 140 | do 141 | local migration_name="$(echo $migration |awk -F/ '{print $NF}')" 142 | local migration_time="$(echo $migration_name |cut -d_ -f1 |bc)" 143 | if [[ $migration_time -lt $current_migration ]]; then 144 | run_migration $migration_name down 145 | fi 146 | done 147 | } 148 | 149 | run_all() { 150 | local target=$1 151 | 152 | if [[ $target = "up" ]]; then 153 | all_up 154 | elif [[ $target = "down" ]]; then 155 | all_down 156 | else 157 | usage 158 | exit 1 159 | fi 160 | } 161 | 162 | ## SCRIPT RUN 163 | 164 | #Convention configs 165 | MONGO_HOST=localhost 166 | MIGRATION_DIR="db/migrate/" 167 | CONFIG_PATH=./config.cfg 168 | 169 | #Parameter extraction 170 | while getopts "hf:c:t:gr" OPT; do 171 | case "$OPT" in 172 | "h") usage;; # exibir ajuda 173 | "f") FILE_NAME=$OPTARG;; 174 | "c") CONFIG_PATH=$OPTARG;; 175 | "g") GENERATE=true;; 176 | "r") RUN=true;; 177 | "t") TARGET=$OPTARG;; 178 | "?") exit -1;; 179 | esac 180 | done 181 | 182 | #Check script invocation 183 | [ -n "$GENERATE" -a -n "$RUN" ] && usage 184 | [ -n "$GENERATE" -a -z "$FILE_NAME" ] && usage 185 | 186 | [ -n "$RUN" -a "$TARGET" != "up" -a "$TARGET" != "down" ] && usage 187 | 188 | #load custom configs (overrides defaults) 189 | source $CONFIG_PATH 190 | 191 | #ensure trailing slash on MIGRATION_DIR 192 | MIGRATION_DIR=${MIGRATION_DIR%/}/ 193 | 194 | #Check migration dir 195 | ensure_migration_dir 196 | #Construct authentication phrase 197 | auth="" 198 | if [ $MONGO_USR ]; then 199 | auth="$auth -u $MONGO_USR " 200 | fi 201 | 202 | if [ $MONGO_PWD ]; then 203 | auth="$auth -p $MONGO_PWD " 204 | fi 205 | 206 | # Script running flow 207 | if [ $GENERATE ]; then 208 | generate $FILE_NAME 209 | elif [ $RUN ]; then 210 | if [ -z "$FILE_NAME" ]; then 211 | run_all $TARGET 212 | else 213 | run_migration $FILE_NAME $TARGET 214 | fi 215 | else 216 | usage 217 | fi 218 | 219 | exit 0 220 | 221 | -------------------------------------------------------------------------------- /template/migration_template.js: -------------------------------------------------------------------------------- 1 | var migration = { 2 | up: function() { 3 | 4 | }, 5 | down: function() { 6 | 7 | } 8 | }; 9 | 10 | migration[target].call(); 11 | -------------------------------------------------------------------------------- /update_current.js: -------------------------------------------------------------------------------- 1 | var migration_info = db.migration_info.findOne(); 2 | if (migration_info) { 3 | migration_info.version = current; 4 | } else { 5 | migration_info = {_id: new ObjectId(), version: current}; 6 | } 7 | db.migration_info.save(migration_info); 8 | --------------------------------------------------------------------------------