railbow 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +95 -0
- data/README.md +121 -3
- data/lib/railbow/calendar.rb +89 -0
- data/lib/railbow/config.rb +3 -1
- data/lib/railbow/demo/status_demo.rb +22 -38
- data/lib/railbow/init.rb +23 -1
- data/lib/railbow/multi_db.rb +87 -0
- data/lib/railbow/params.rb +74 -1
- data/lib/railbow/status/ghosts.rb +144 -0
- data/lib/railbow/status/git_data.rb +327 -0
- data/lib/railbow/status/help.rb +121 -0
- data/lib/railbow/status/printer.rb +324 -0
- data/lib/railbow/status/section.rb +551 -0
- data/lib/railbow/table/column.rb +10 -2
- data/lib/railbow/table/renderer.rb +106 -48
- data/lib/railbow/table/theme.rb +3 -2
- data/lib/railbow/tasks/migrate_status.rake +30 -765
- data/lib/railbow/version.rb +1 -1
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d599ddd7c1bd3a12893312ab22f40ff75def87f5d85d42dd2ee5a11da7183225
|
|
4
|
+
data.tar.gz: 4413568aafb22c8cf2b1025bbd6f90696f4da6f3bcead307c48ffeac3e96f04c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a02d2dda50ba6fa9ae4b04d166ebc51148532dcfccd5ab1a7777179d14767d2bb87b2fc4a9a5d3aadbb0eeae114a68f5365cd3bfd0d27286958b192f5fa43967
|
|
7
|
+
data.tar.gz: 5ad80b381822e25a9078093f82c8b52deccae5940334ce45645883e2d4c06a81070e2710876f58fc8af949cc3b96009369e141eb9945c3a1941233561c31f486
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,101 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2026-07-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Multi-database support for `db:migrate:status`. Rails runs the task once per
|
|
13
|
+
database; railbow now wraps that loop and renders the databases together, as
|
|
14
|
+
one section each in `database.yml` order with column widths shared so the
|
|
15
|
+
tables line up. The section header names the database by its `database.yml`
|
|
16
|
+
name, which is what you configure and filter by, rather than the database
|
|
17
|
+
name Rails reports.
|
|
18
|
+
- A database with nothing in the time window and nothing pending collapses to a
|
|
19
|
+
one-line summary instead of an empty table, so the `cache`, `queue` and
|
|
20
|
+
`cable` databases of a stock Rails 8 app stop drowning out your own. Anything
|
|
21
|
+
it hides (pending migrations, ghosts) is counted in the line, along with the
|
|
22
|
+
flag that reveals them.
|
|
23
|
+
- Databases sharing a `migrations_paths` (horizontal sharding) merge into a
|
|
24
|
+
single table carrying a status glyph per database, making drift between
|
|
25
|
+
shards visible at a glance. A dim `·` marks a version one shard has never
|
|
26
|
+
seen.
|
|
27
|
+
- `RBW_DB` (config key `db`) selects and shapes a multi-database run:
|
|
28
|
+
`only:<name>` and `skip:<name>` (both repeatable) pick databases, `full`
|
|
29
|
+
draws the quiet ones in full, and `inline` merges everything into one table
|
|
30
|
+
ordered by version with a `Db` column, so the calendar spans the whole
|
|
31
|
+
application instead of restarting per database. Filtered-out databases are
|
|
32
|
+
named in a footer rather than silently dropped.
|
|
33
|
+
|
|
34
|
+
- `RBW_SINCE_MIN` (config key `since_min`, default 10) makes the time window a
|
|
35
|
+
soft limit: whatever `since` leaves, the floor tops it back up to this many
|
|
36
|
+
migrations. A database with five migrations now shows all five rather than
|
|
37
|
+
hiding the two that happen to be old and reporting an empty period. The
|
|
38
|
+
hidden-count line says `showing the last 10` when the floor overrode the
|
|
39
|
+
window, and a multi-database section header reads `last 10 of 50` rather than
|
|
40
|
+
`10 of 50`, so a table holding rows older than the window it names still
|
|
41
|
+
reads honestly in both views. `RBW_SINCE_MIN=0` restores the old hard cutoff.
|
|
42
|
+
- `RBW_DB=focus`, **the default**, expands only the first database and
|
|
43
|
+
summarizes the rest in a line each, so a multi-database app is one table to
|
|
44
|
+
read rather than several. A database holding pending migrations is expanded
|
|
45
|
+
anyway: the summary exists to hide what needs no action, and pending work is
|
|
46
|
+
the opposite of that. An empty `RBW_DB` (or `db: ""`) expands every database
|
|
47
|
+
that has recent activity, `full` expands everything including quiet ones, and
|
|
48
|
+
`focus` never applies to an `inline` run, which exists to hold every database
|
|
49
|
+
at once.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- Git lookups are now shared across a run: authors, landed dates and branch
|
|
54
|
+
origins are resolved once per migrations directory instead of once per
|
|
55
|
+
database. `RBW_HELP=1` prints the help once for the whole run rather than
|
|
56
|
+
once per database.
|
|
57
|
+
- `db:migrate:status` internals moved out of the rake file into
|
|
58
|
+
`Railbow::Status` (`Section`, `Printer`, `GitData`, `Ghosts`, `Help`). No
|
|
59
|
+
output changed: a single-database app renders byte for byte what it did
|
|
60
|
+
before, which a golden fixture now enforces.
|
|
61
|
+
|
|
62
|
+
## [0.4.0] - 2026-07-28
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- `RBW_CALENDAR=wdividers` gives every ISO week its own separator row, the
|
|
67
|
+
fuller counterpart to `wticks`. A week that opens a new month is left to the
|
|
68
|
+
month row rather than being announced twice. The default stays `wticks`, so
|
|
69
|
+
existing output is unchanged until you opt in.
|
|
70
|
+
- `RBW_CALENDAR=counts` appends `· N migrations` to every separator row: how
|
|
71
|
+
many migrations that section holds, counted up to the next separator.
|
|
72
|
+
- `RBW_CALENDAR=wlabel:<fmt>` sets the strftime pattern for week rows,
|
|
73
|
+
alongside the existing `label:` for month rows. It defaults to the month
|
|
74
|
+
pattern, so the week number sits at the same offset on every separator row
|
|
75
|
+
rather than shifting left when no new month is announced.
|
|
76
|
+
- `railbow demo status` now honors `RBW_CALENDAR`, so the calendar modes can be
|
|
77
|
+
tried without a Rails project.
|
|
78
|
+
- An empty `RBW_CALENDAR` (month separators, no week markers) is now documented
|
|
79
|
+
in the help output, the `railbow init` template, and the README. It always
|
|
80
|
+
worked; nobody could have guessed it.
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
|
|
84
|
+
- Calendar separator rows are drawn in a muted purple (256-color 97) instead of
|
|
85
|
+
the brighter 141, so they frame the migrations without competing with them.
|
|
86
|
+
- Migrations that are not applied (`down`) now render as a greyed-out row in
|
|
87
|
+
`db:migrate:status`, instead of signalling their state through the status
|
|
88
|
+
column alone. The row keeps its layout and its status glyph keeps its color;
|
|
89
|
+
everything else drops its own colors, so pending migrations read as inactive
|
|
90
|
+
next to the applied ones.
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
|
|
94
|
+
- Rows whose last column wrapped were indented with too wide a blank prefix:
|
|
95
|
+
building the row mutated the very cell that the wrapped-line indent is
|
|
96
|
+
measured from. Reachable from `routes`, `about`, and `stats`.
|
|
97
|
+
- A separator label wider than its column no longer pushes the rest of the row
|
|
98
|
+
sideways. Separator rows are drawn as empty walls with the label laid over
|
|
99
|
+
them, so a long label spills into the blank space to its right.
|
|
100
|
+
- `RBW_HELP` output for `db:migrate:status` uses plain hyphens instead of
|
|
101
|
+
em-dashes, matching the rest of the user-facing text.
|
|
102
|
+
|
|
8
103
|
## [0.3.0] - 2026-07-23
|
|
9
104
|
|
|
10
105
|
### Added
|
data/README.md
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
- **Notes** - `rails notes` with git blame, author colors, date filtering, and sorting
|
|
22
22
|
- **About** - polished `rails about` output
|
|
23
23
|
- **Git Integration** - authors, diffs, branch origin, landing dates, uncommitted file indicators
|
|
24
|
-
- **Calendar View** - month separators
|
|
24
|
+
- **Calendar View** - month separators plus week ticks or full week rows, with optional per-section counts
|
|
25
|
+
- **Multiple Databases** - read one table by default, the rest summarized in a line each (and expanded when they have migrations pending); sharded databases merged with a status per shard
|
|
25
26
|
- **Ghost Recovery** - uses the [mighost](https://github.com/amberpixels/mighost) gem (optional) to recover names, authors, and branch badges for `NO FILE` migrations
|
|
26
27
|
- **Smart Defaults** - auto-disables in CI, piped output, `NO_COLOR`, and LLM agents
|
|
27
28
|
|
|
@@ -103,8 +104,121 @@ Out of the box you get:
|
|
|
103
104
|
- **Landing dates** - `⤻ Mar 04` badge when a migration was merged to main after its creation
|
|
104
105
|
- **Branch badges** - `⎇ PS-142` showing the source branch/ticket
|
|
105
106
|
- **Affected tables** - color-coded table names extracted from migration files
|
|
106
|
-
- **Time filtering** - only the last 70 days shown by default (`since: 70d`)
|
|
107
|
+
- **Time filtering** - only the last 70 days shown by default (`since: 70d`), as a soft limit: never fewer than 10 migrations, however old (`since_min: 10`)
|
|
107
108
|
- **Your migrations highlighted** - rows authored by you are visually distinct
|
|
109
|
+
- **Pending migrations greyed out** - a `down` row keeps its status glyph but loses its colors
|
|
110
|
+
|
|
111
|
+
#### Weekly view
|
|
112
|
+
|
|
113
|
+
By default each new ISO week is marked with a tick on the date column
|
|
114
|
+
(`calendar: wticks`). For a fuller weekly breakdown, `wdividers` gives every
|
|
115
|
+
week its own separator row, and `counts` appends how many migrations sit under
|
|
116
|
+
each separator, week or month:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
RBW_CALENDAR=wdividers,counts rake db:migrate:status
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
Feb 2026 W06 · 2 migrations
|
|
124
|
+
↑↑ │ 20260202145343 │ 2026-02-02 14:53:43 │ Add weight field to animals ● animals
|
|
125
|
+
↑↑ │ 20260203134528 │ 2026-02-03 13:45:28 │ Create vaccination records ● vaccination_records
|
|
126
|
+
Feb 2026 W07 · 1 migration
|
|
127
|
+
↑↑ │ 20260210183902 │ 2026-02-10 18:39:02 │ Create pet tags ● pet_tags
|
|
128
|
+
Mar 2026 W10 · 1 migration
|
|
129
|
+
↑↑ │ 20260303120000 │ 2026-03-03 12:00:00 │ Add breed restrictions to… ● adoption_policies
|
|
130
|
+
Mar 2026 W11 · 1 migration
|
|
131
|
+
↑↑ │ 20260313132325 │ 2026-03-13 13:23:25 │ Create veterinary appointm… ⎇ PS-142 ● veterinary_appointments
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Every separator carries the month, so the week number always sits at the same
|
|
135
|
+
offset instead of shifting left on rows that open no new month, and every
|
|
136
|
+
separator is drawn in the same muted purple. Both labels are `strftime`
|
|
137
|
+
patterns: `label:` for months, `wlabel:` for weeks (defaulting to the same
|
|
138
|
+
pattern).
|
|
139
|
+
|
|
140
|
+
`RBW_CALENDAR` is the list of *week* markers, so an empty value leaves you with
|
|
141
|
+
month separators and nothing else:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
view: "calendar,tables"
|
|
145
|
+
calendar: "" # month separators only
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Dropping `calendar` from `view` instead removes the month separators too.
|
|
149
|
+
|
|
150
|
+
#### Multiple databases
|
|
151
|
+
|
|
152
|
+
Rails runs `db:migrate:status` once per database, so a multi-database app used
|
|
153
|
+
to get one blind table per database: widths that did not line up, the help
|
|
154
|
+
printed once per database, and every git lookup repeated. Railbow now renders
|
|
155
|
+
the whole run together.
|
|
156
|
+
|
|
157
|
+
By default you read one table: the first database in `database.yml` is rendered
|
|
158
|
+
in full and every other one is summarized in a line each (`db: focus`).
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
📊 2 databases · primary, raw
|
|
162
|
+
|
|
163
|
+
──── primary · myapp_development ──── 122 of 1500 ──────────────────────
|
|
164
|
+
↑↑ │ 20260721130000 │ 2026-07-21 13:00:00 │ Partition sms notifications ● sms_notifications
|
|
165
|
+
↓↓ │ 20260727170000 │ 2026-07-27 17:00:00 │ Add cr push columns to lines ● lines
|
|
166
|
+
|
|
167
|
+
⋯ raw · 5 migrations, all applied · latest Jul 22 2026
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**A database with migrations pending is always expanded**, however focused the
|
|
171
|
+
run is. The summary exists to hide what needs no action, and pending work is
|
|
172
|
+
the opposite of that.
|
|
173
|
+
|
|
174
|
+
Setting `db` to an empty value expands every database that has recent activity,
|
|
175
|
+
each as its own section with column widths shared so the tables line up:
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
db: "" # or RBW_DB= on the command line
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
──── primary · myapp_development ──── 122 of 1500 ──────────────────────
|
|
183
|
+
↑↑ │ 20260721130000 │ 2026-07-21 13:00:00 │ Partition sms notifications ● sms_notifications
|
|
184
|
+
|
|
185
|
+
──── raw · raw_development ─────────────────────────────────────────────
|
|
186
|
+
↑↑ │ 20260722120000 │ 2026-07-22 12:00:00 │ Drop archived activity logs ● archived_activity_logs
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
A section header counts what it is showing: `122 of 1500` means 122 fell inside
|
|
190
|
+
the time window. When the window found too few and the `since_min` floor topped
|
|
191
|
+
the section back up, it reads `last 10 of 50` instead, because the rows are
|
|
192
|
+
then the newest ten rather than a window's worth.
|
|
193
|
+
|
|
194
|
+
Even then, a database with nothing in the time window collapses to one line
|
|
195
|
+
rather than an empty table, which is what keeps the `cache`, `queue` and
|
|
196
|
+
`cable` databases of a stock Rails 8 app from drowning out your own:
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
⋯ cache · 5 migrations, all applied · latest Feb 12 2024
|
|
200
|
+
⋯ queue · 12 migrations, 3 pending outside the 70d window - RBW_SINCE=all
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Databases that share a `migrations_paths` (horizontal sharding) run the same
|
|
204
|
+
files, so they merge into one table carrying a status per database. Drift
|
|
205
|
+
between shards is then visible at a glance, and `·` marks a version a shard has
|
|
206
|
+
never seen:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
──── primary + primary_shard_one · db/migrate ──────────────────────────
|
|
210
|
+
↑↑ ↑↑ │ 20260601151200 │ 2026-06-01 15:12:00 │ Add owner id to pets
|
|
211
|
+
↑↑ ↓↓ │ 20260726143000 │ 2026-07-26 14:30:00 │ Add policy number to bills
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`RBW_DB` controls the rest: `only:<name>` and `skip:<name>` (both repeatable)
|
|
215
|
+
pick databases, `full` draws every section in full and overrides `focus`, and
|
|
216
|
+
`inline` merges everything into a single table ordered by version with a `Db`
|
|
217
|
+
column, so the calendar spans the whole application rather than restarting per
|
|
218
|
+
database. `focus` never applies to an inline run - a merged table exists to
|
|
219
|
+
hold every database at once.
|
|
220
|
+
|
|
221
|
+
A single-database app sees none of this: its output is unchanged.
|
|
108
222
|
|
|
109
223
|
If the [mighost](https://github.com/amberpixels/mighost) gem is installed (`gem "mighost", group: [:development, :test]`), migrations whose files were deleted (e.g. after switching branches) show up with a 👻 status and their recovered name instead of a bare `********** NO FILE **********` row - Railbow talks to it through the stable `Mighost::API`. Each ghost row carries the most informative badge mighost can provide: `≡ <version>` when the migration was re-timestamped and lives on under another version (shown with a calmer 🪦 status), `⌥ <branch>` when a branch still holds the file, or `✂ deleted in:<sha>` pointing at the commit that removed it. Ghosts dismissed via `mighost:dismiss` (or hidden by `hide_superseded`) render as plain `NO FILE`.
|
|
110
224
|
|
|
@@ -153,10 +267,12 @@ rake railbow:init
|
|
|
153
267
|
|
|
154
268
|
```yaml
|
|
155
269
|
since: 70d
|
|
270
|
+
since_min: 10
|
|
156
271
|
date: rel
|
|
157
272
|
git: "author:me,diff,mask:auto"
|
|
158
273
|
view: "calendar,tables"
|
|
159
274
|
calendar: wticks
|
|
275
|
+
db: focus
|
|
160
276
|
|
|
161
277
|
compact: "maxw:120"
|
|
162
278
|
|
|
@@ -178,10 +294,12 @@ Every option can also be set via `RBW_*` environment variables, which override c
|
|
|
178
294
|
| `RBW_PLAIN` | `1` | Disable all formatting |
|
|
179
295
|
| `RBW_FORCE` | `1` | Force formatting even when piped, in CI, or run by an LLM agent (`RBW_PLAIN=1` still wins) |
|
|
180
296
|
| `RBW_SINCE` | `2mo`, `70d`, `1y`, `all` | Filter migrations by time period |
|
|
297
|
+
| `RBW_SINCE_MIN` | `10`, `0` | Never show fewer than this many migrations, however old (default 10; `0` disables) |
|
|
181
298
|
| `RBW_DATE` | `full`, `rel`, `short`, `custom(%b %d)` | Date display format |
|
|
182
299
|
| `RBW_GIT` | `author:me,diff,mask:auto` | Git integration options |
|
|
183
300
|
| `RBW_VIEW` | `calendar,tables` | Enable calendar view and table detection |
|
|
184
|
-
| `RBW_CALENDAR` | `wticks` |
|
|
301
|
+
| `RBW_CALENDAR` | `wticks`, `wdividers,counts`, `` (empty) | Week markers: tick marks, full week separator rows, per-section counts. Empty means month separators only |
|
|
302
|
+
| `RBW_DB` | `focus` (default), `` (empty), `only:primary`, `skip:cache`, `full`, `inline` | Multi-database runs: focus one database, expand them all, pick some, or merge them into one table |
|
|
185
303
|
| `RBW_COMPACT` | `oneline,dense,noheader,maxw:80` | Compact display options |
|
|
186
304
|
| `RBW_VERB` | `GET,POST` | Filter routes by HTTP method |
|
|
187
305
|
| `RBW_SORT` | `file`, `date` | Sort order for notes |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
|
|
5
|
+
module Railbow
|
|
6
|
+
# Turns an ordered list of migration versions into the calendar furniture that
|
|
7
|
+
# gives the status table its shape: a separator row when the month changes,
|
|
8
|
+
# optionally one when the ISO week changes, and optionally a tick mark on the
|
|
9
|
+
# date column at each week boundary.
|
|
10
|
+
#
|
|
11
|
+
# A separator introduces the rows beneath it and runs until the next separator,
|
|
12
|
+
# so the optional count always answers "how many migrations in this section".
|
|
13
|
+
module Calendar
|
|
14
|
+
DEFAULT_MONTH_LABEL = "%b %Y W%V"
|
|
15
|
+
# Week rows carry the month too, so the week number sits at the same offset
|
|
16
|
+
# on every separator row rather than jumping left when the month is absent.
|
|
17
|
+
DEFAULT_WEEK_LABEL = DEFAULT_MONTH_LABEL
|
|
18
|
+
|
|
19
|
+
# What the renderer needs to draw the calendar: a label per separator row,
|
|
20
|
+
# and which rows open a week (for tick marks). Both keyed by row index.
|
|
21
|
+
class Furniture
|
|
22
|
+
attr_reader :separators, :tick_rows
|
|
23
|
+
|
|
24
|
+
def initialize
|
|
25
|
+
@separators = {}
|
|
26
|
+
@tick_rows = Set.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
def none
|
|
33
|
+
Furniture.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def build(versions, weeks: false, ticks: false, counts: false,
|
|
37
|
+
month_label: DEFAULT_MONTH_LABEL, week_label: DEFAULT_WEEK_LABEL)
|
|
38
|
+
dates = versions.map { |v| parse_date(v) }
|
|
39
|
+
furniture = none
|
|
40
|
+
|
|
41
|
+
dates.each_with_index do |date, i|
|
|
42
|
+
prev = (i > 0) ? dates[i - 1] : nil
|
|
43
|
+
next unless date && prev
|
|
44
|
+
|
|
45
|
+
opens_week = week_key(prev) != week_key(date)
|
|
46
|
+
|
|
47
|
+
if month_key(prev) != month_key(date)
|
|
48
|
+
furniture.separators[i] = date.strftime(month_label)
|
|
49
|
+
elsif weeks && opens_week
|
|
50
|
+
furniture.separators[i] = date.strftime(week_label)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
furniture.tick_rows << i if ticks && opens_week
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
append_counts(furniture.separators, dates.size) if counts
|
|
57
|
+
furniture
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_date(version)
|
|
61
|
+
v = version.to_s
|
|
62
|
+
Date.new(v[0..3].to_i, v[4..5].to_i, v[6..7].to_i)
|
|
63
|
+
rescue Date::Error
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def month_key(date)
|
|
68
|
+
[date.year, date.month]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# cwyear, not year: the ISO week of Jan 1 can belong to the year before.
|
|
72
|
+
def week_key(date)
|
|
73
|
+
[date.cwyear, date.cweek]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def append_counts(separators, total)
|
|
77
|
+
indices = separators.keys.sort
|
|
78
|
+
indices.each_with_index do |index, nth|
|
|
79
|
+
section_end = indices[nth + 1] || total
|
|
80
|
+
separators[index] = "#{separators[index]} · #{migration_count(section_end - index)}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Spelled out: a bare number next to a week label reads as part of the date.
|
|
85
|
+
def migration_count(count)
|
|
86
|
+
"#{count} #{(count == 1) ? "migration" : "migrations"}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/railbow/config.rb
CHANGED
|
@@ -14,10 +14,12 @@ module Railbow
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"since" => "70d",
|
|
17
|
+
"since_min" => 10,
|
|
17
18
|
"git" => "author:all,diff,mask:auto",
|
|
18
19
|
"author_format" => "short",
|
|
19
20
|
"view" => "calendar,tables",
|
|
20
|
-
"calendar" => "wticks"
|
|
21
|
+
"calendar" => "wticks",
|
|
22
|
+
"db" => "focus"
|
|
21
23
|
}.freeze
|
|
22
24
|
|
|
23
25
|
@root = nil
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
require "date"
|
|
4
4
|
require_relative "../formatters/base"
|
|
5
5
|
require_relative "../table"
|
|
6
|
+
require_relative "../calendar"
|
|
6
7
|
require_relative "../config"
|
|
8
|
+
require_relative "../params"
|
|
7
9
|
require_relative "fixtures"
|
|
8
10
|
|
|
9
11
|
module Railbow
|
|
@@ -20,7 +22,8 @@ module Railbow
|
|
|
20
22
|
puts "\n#{formatter.emoji(:status)} Database: #{formatter.cyan("db/development.sqlite3")}"
|
|
21
23
|
puts
|
|
22
24
|
|
|
23
|
-
#
|
|
25
|
+
# Fixed for the showcase: author:me, diff, tables, full date. The calendar
|
|
26
|
+
# options below come from the real params, so RBW_CALENDAR still applies.
|
|
24
27
|
date_format = "full"
|
|
25
28
|
tables_enabled = true
|
|
26
29
|
diff_enabled = true
|
|
@@ -28,7 +31,7 @@ module Railbow
|
|
|
28
31
|
git_email = Fixtures::DEMO_USER_EMAIL
|
|
29
32
|
git_name = Fixtures::DEMO_USER_NAME
|
|
30
33
|
|
|
31
|
-
# Latest migration date
|
|
34
|
+
# Latest migration date - for "fresh" landed badge detection
|
|
32
35
|
latest_version = migrations.last[:version]
|
|
33
36
|
latest_mig_date = parse_version_date(latest_version)
|
|
34
37
|
|
|
@@ -43,7 +46,7 @@ module Railbow
|
|
|
43
46
|
name_col_width = needs_name_truncation ? 60 : nil
|
|
44
47
|
|
|
45
48
|
table_columns = [
|
|
46
|
-
Railbow::Table::Column.new(label: "Status", max_width: 6, sticky: true),
|
|
49
|
+
Railbow::Table::Column.new(label: "Status", max_width: 6, sticky: true, accent: true),
|
|
47
50
|
Railbow::Table::Column.new(label: "Migration ID", sticky: true),
|
|
48
51
|
Railbow::Table::Column.new(label: "Created At"),
|
|
49
52
|
Railbow::Table::Column.new(label: "Migration Name",
|
|
@@ -57,7 +60,9 @@ module Railbow
|
|
|
57
60
|
|
|
58
61
|
# Build rows
|
|
59
62
|
highlight_rows = Set.new
|
|
63
|
+
down_rows = Set.new
|
|
60
64
|
rows = migrations.each_with_index.map do |m, idx|
|
|
65
|
+
down_rows << idx if m[:status] == "down"
|
|
61
66
|
colored_status = case m[:status]
|
|
62
67
|
when "up" then formatter.green_bold("up")
|
|
63
68
|
when "down" then formatter.yellow_bold("down")
|
|
@@ -106,40 +111,16 @@ module Railbow
|
|
|
106
111
|
[colored_status, m[:version], created_at, display_name, table_tags]
|
|
107
112
|
end
|
|
108
113
|
|
|
109
|
-
# Calendar
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
v = versions[i]
|
|
120
|
-
date = Date.new(v[0..3].to_i, v[4..5].to_i, v[6..7].to_i)
|
|
121
|
-
separators[i] = date.strftime(calendar_label_fmt)
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Week ticks
|
|
127
|
-
tick_rows = Set.new
|
|
128
|
-
prev_week = nil
|
|
129
|
-
versions.each_with_index do |v, i|
|
|
130
|
-
y = v[0..3].to_i
|
|
131
|
-
m = v[4..5].to_i
|
|
132
|
-
d = v[6..7].to_i
|
|
133
|
-
next if y == 0 || m == 0 || d == 0
|
|
134
|
-
|
|
135
|
-
week = Date.new(y, m, d).cweek
|
|
136
|
-
|
|
137
|
-
if i > 0 && prev_week && week != prev_week
|
|
138
|
-
tick_rows << i
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
prev_week = week
|
|
142
|
-
end
|
|
114
|
+
# Calendar furniture. Driven by the real params, so RBW_CALENDAR can be
|
|
115
|
+
# tried out here without a Rails project.
|
|
116
|
+
calendar = Railbow::Calendar.build(
|
|
117
|
+
migrations.map { |m| m[:version] },
|
|
118
|
+
weeks: Railbow::Params.calendar_wdividers?,
|
|
119
|
+
ticks: Railbow::Params.calendar_wticks?,
|
|
120
|
+
counts: Railbow::Params.calendar_counts?,
|
|
121
|
+
month_label: Railbow::Params.calendar_label,
|
|
122
|
+
week_label: Railbow::Params.calendar_week_label
|
|
123
|
+
)
|
|
143
124
|
|
|
144
125
|
aliases = Railbow::Config.table_aliases
|
|
145
126
|
renderer = Railbow::Table::Renderer.new(
|
|
@@ -148,7 +129,10 @@ module Railbow
|
|
|
148
129
|
compact: {oneline: false, dense: false, noheader: false, maxw: nil, hidden_columns: []},
|
|
149
130
|
aliases: aliases
|
|
150
131
|
)
|
|
151
|
-
puts renderer.render(rows,
|
|
132
|
+
puts renderer.render(rows,
|
|
133
|
+
separators: calendar.separators,
|
|
134
|
+
highlight_rows: highlight_rows, dim_rows: down_rows,
|
|
135
|
+
tick_rows: calendar.tick_rows, tick_col: 2)
|
|
152
136
|
end
|
|
153
137
|
|
|
154
138
|
private
|
data/lib/railbow/init.rb
CHANGED
|
@@ -22,6 +22,11 @@ module Railbow
|
|
|
22
22
|
# Time window for migrations to display (e.g. 30d, 2mo, 1y, all)
|
|
23
23
|
since: "70d"
|
|
24
24
|
|
|
25
|
+
# Never show fewer than this many migrations, however old they are.
|
|
26
|
+
# The window above is a soft limit: a database with five migrations shows
|
|
27
|
+
# all five rather than reporting an empty period. 0 disables the floor.
|
|
28
|
+
since_min: 10
|
|
29
|
+
|
|
25
30
|
# Sort order: file (by filename/version) or date (by timestamp)
|
|
26
31
|
# sort: "file"
|
|
27
32
|
|
|
@@ -43,12 +48,29 @@ module Railbow
|
|
|
43
48
|
# View mode (comma-separated): calendar, tables
|
|
44
49
|
view: "calendar,tables"
|
|
45
50
|
|
|
46
|
-
# Calendar options
|
|
51
|
+
# Calendar options (comma-separated), all require view: calendar.
|
|
52
|
+
# Leave empty ("") for month separators only, with no week markers.
|
|
53
|
+
# wticks - week tick marks on the date column
|
|
54
|
+
# wdividers - a separator row per ISO week
|
|
55
|
+
# counts - append "· N migrations" to separator rows (per section)
|
|
56
|
+
# label:<fmt> - strftime for month separators (default: %b %Y W%V)
|
|
57
|
+
# wlabel:<fmt> - strftime for week separators (default: same as label)
|
|
47
58
|
calendar: "wticks"
|
|
48
59
|
|
|
49
60
|
# Date format: full, rel, short, or custom(%b %d, %Y)
|
|
50
61
|
# date: "full"
|
|
51
62
|
|
|
63
|
+
# Multi-database runs (comma-separated). Leave empty ("") to expand every
|
|
64
|
+
# database into its own section, in database.yml order; a database with
|
|
65
|
+
# nothing in the time window collapses to a one-line summary either way.
|
|
66
|
+
# focus - expand only the first database, summarizing the rest.
|
|
67
|
+
# A database with migrations pending is always expanded
|
|
68
|
+
# only:<name> - render only this database (repeatable)
|
|
69
|
+
# skip:<name> - drop this database (repeatable)
|
|
70
|
+
# full - draw every database in full, overriding focus
|
|
71
|
+
# inline - one merged table ordered by version, with a Db column
|
|
72
|
+
db: "focus"
|
|
73
|
+
|
|
52
74
|
# Compact mode (comma-separated):
|
|
53
75
|
# oneline - one line per migration
|
|
54
76
|
# dense - reduce padding
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Railbow
|
|
4
|
+
# Rails runs db:migrate:status once per database, inside
|
|
5
|
+
# with_temporary_pool_for_each. Each call on its own cannot know whether more
|
|
6
|
+
# databases are coming, so railbow wraps that loop: sections collect into the
|
|
7
|
+
# open batch and the batch renders them together when the loop ends.
|
|
8
|
+
#
|
|
9
|
+
# A batch is inert unless something adds to it, so wrapping the other tasks
|
|
10
|
+
# that share the same Rails helper (db:migrate, db:prepare) costs one object.
|
|
11
|
+
module MultiDb
|
|
12
|
+
KEY = :railbow_multi_db_batch
|
|
13
|
+
|
|
14
|
+
class Batch
|
|
15
|
+
attr_reader :git_cache
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@sections = []
|
|
19
|
+
@skipped = []
|
|
20
|
+
@git_cache = {}
|
|
21
|
+
@help_shown = false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# A database excluded by RBW_DB. Recorded rather than dropped so the run
|
|
25
|
+
# can say what it left out.
|
|
26
|
+
def skip(name)
|
|
27
|
+
@skipped << name.to_s
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Databases sharing a migrations path see the same files, so they merge
|
|
31
|
+
# into one section that carries a status per database.
|
|
32
|
+
def add(section)
|
|
33
|
+
existing = @sections.find { |s| s.same_migrations?(section) }
|
|
34
|
+
if existing
|
|
35
|
+
existing.merge!(section)
|
|
36
|
+
else
|
|
37
|
+
@sections << section
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def claim_help
|
|
42
|
+
return false if @help_shown
|
|
43
|
+
|
|
44
|
+
@help_shown = true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def empty?
|
|
48
|
+
@sections.empty? && @skipped.empty?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def flush
|
|
52
|
+
return if empty?
|
|
53
|
+
|
|
54
|
+
Railbow::Status::Printer.new(@sections, skipped: @skipped).print
|
|
55
|
+
@sections = []
|
|
56
|
+
@skipped = []
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
module_function
|
|
61
|
+
|
|
62
|
+
# Opens a batch for the duration of the block. The flush is in an ensure so
|
|
63
|
+
# that a database aborting mid-run (a missing schema_migrations table, say)
|
|
64
|
+
# still prints the sections collected before it.
|
|
65
|
+
def batch
|
|
66
|
+
previous = Thread.current[KEY]
|
|
67
|
+
batch = Batch.new
|
|
68
|
+
Thread.current[KEY] = batch
|
|
69
|
+
begin
|
|
70
|
+
yield
|
|
71
|
+
ensure
|
|
72
|
+
Thread.current[KEY] = previous
|
|
73
|
+
batch.flush
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def current
|
|
78
|
+
Thread.current[KEY]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# True the first time it is asked within a run. Without a batch every call
|
|
82
|
+
# is its own run, so the answer is always true.
|
|
83
|
+
def claim_help
|
|
84
|
+
current ? current.claim_help : true
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|