~yerinalexey/gobin

ref: c6e4af15a5c5b8494d48b5d3122a17bc9980b963 gobin/gobin-migrate -rwxr-xr-x 2.6 KiB
c6e4af15Alexey Yerin gobin-migrate: wrap migrations in transaction 6 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/bin/sh

# Migrator script for gobin

set -e

migrations_path=./migrations
config=config.toml

usage() {
  cat <<EOF
Usage: gobin-migrate [-m PATH] [-c CONFIG] COMMAND [ARGUMENTS]

Options:
  -m PATH     - set migrations directory (default: ./migrations)
  -c CONFIG   - config file location (default: config.toml)

Commands:
  apply NAME  - run a migration against the databse
  revert NAME - revert a migration
  up          - run all pending migrations
EOF
  exit 1
}

while getopts c:m: opt; do
  case "$opt" in
    c)
      config="$OPTARG"
      ;;
    m)
      migrations_path="$OPTARG"
      ;;
    *)
      usage
      ;;
  esac
done

shift $(($OPTIND - 1))

parse_config() {
  # Disclaimer: really poor TOML parser, do not use under any
  # circumstances

  # gobin accepts both databaseUri or database_uri
  database_uri="$(awk -F '=' '/^(databaseUri|database_uri)/ { gsub(/[ "]/, ""); print $2 }' "$config")"

  if [ -z "$database_uri" ]; then
    printf 'Complicated TOML or databaseUri (database_uri) field is missing\n'
    exit 1
  fi
}

parse_config

# Helper functions
run_sql() {
  psql "$@" "$database_uri"
}

is_migrated() {
  name="$1"

  printf '%s' "$migrations" | fgrep "$name" >/dev/null
}

apply_migration() {
  name="$1"

  if is_migrated "$name"; then
    printf '%s: already applied, skipping\n' "$name"
    return
  fi

  file="$migrations_path/$name/up.sql"

  if [ ! -f "$file" ]; then
    printf 'Migration has not been found: %s\n' "$file"
    exit 1
  fi

  printf 'Applying migration %s\n' "$name"

  run_sql -c "begin;
$(cat "$file");
insert into _gobin_migrations (name) values ('$name');
commit;"
}

revert_migration() {
  name="$1"

  if ! is_migrated "$name"; then
    printf '%s: not applied, skipping\n' "$name"
    return
  fi

  file="$migrations_path/$name/down.sql"

  if [ ! -f "$file" ]; then
    printf 'Reverse migration has not been found: %s\n' "$file"
    exit 1
  fi

  printf 'Reverting migration %s\n' "$name"

  run_sql -c "begin;
$(cat "$file");
delete from _gobin_migrations where name='$name';
commit;"
}

up_migrations() {
  for name in `ls "$migrations_path"`; do
    apply_migration "$name"
  done
}

# Create migrations table
run_sql -c '
set client_min_messages = error;
create table if not exists _gobin_migrations (
  name varchar(255) primary key,
  created_at timestamp default now()
);' >/dev/null

migrations="$(run_sql --csv -c 'select name from _gobin_migrations;' | \
  tail -n +2)"

case "$1" in
  apply)
    name="$2"

    [ -z "$name" ] && usage

    apply_migration "$name"
    ;;
  revert)
    name="$2"

    [ -z "$name" ] && usage

    revert_migration "$name"
    ;;
  up)
    up_migrations
    ;;
  *)
    usage
    ;;
esac