proctolog 0.1.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 +7 -0
- data/CHANGELOG.md +23 -0
- data/README.md +178 -0
- data/app/controllers/proctolog/acceptances_controller.rb +29 -0
- data/app/controllers/proctolog/application_controller.rb +32 -0
- data/app/controllers/proctolog/bursts_controller.rb +34 -0
- data/app/controllers/proctolog/choices_controller.rb +30 -0
- data/app/controllers/proctolog/code_reviews_controller.rb +26 -0
- data/app/controllers/proctolog/decisions_controller.rb +33 -0
- data/app/controllers/proctolog/entries_controller.rb +14 -0
- data/app/controllers/proctolog/evidence_controller.rb +23 -0
- data/app/controllers/proctolog/freshness_controller.rb +27 -0
- data/app/controllers/proctolog/good_to_go_controller.rb +29 -0
- data/app/controllers/proctolog/memberships_controller.rb +20 -0
- data/app/controllers/proctolog/mentions_controller.rb +16 -0
- data/app/controllers/proctolog/moments_controller.rb +26 -0
- data/app/controllers/proctolog/note_params.rb +24 -0
- data/app/controllers/proctolog/notes_controller.rb +56 -0
- data/app/controllers/proctolog/open_pull_requests_controller.rb +59 -0
- data/app/controllers/proctolog/pictures_controller.rb +27 -0
- data/app/controllers/proctolog/prompts_controller.rb +28 -0
- data/app/controllers/proctolog/pull_request_scoped.rb +21 -0
- data/app/controllers/proctolog/pull_requests_controller.rb +34 -0
- data/app/controllers/proctolog/put_away_controller.rb +19 -0
- data/app/controllers/proctolog/recordings_controller.rb +28 -0
- data/app/controllers/proctolog/rendered_pieces_controller.rb +11 -0
- data/app/controllers/proctolog/review_data_controller.rb +12 -0
- data/app/controllers/proctolog/rules_controller.rb +20 -0
- data/app/controllers/proctolog/scenario_notes_controller.rb +26 -0
- data/app/controllers/proctolog/scenario_scoped.rb +21 -0
- data/app/controllers/proctolog/scenarios_controller.rb +67 -0
- data/app/controllers/proctolog/sitting_servers_controller.rb +30 -0
- data/app/controllers/proctolog/waiting_controller.rb +14 -0
- data/app/controllers/proctolog/with_decisions.rb +28 -0
- data/app/helpers/proctolog/bursts_helper.rb +52 -0
- data/app/helpers/proctolog/decisions_helper.rb +102 -0
- data/app/helpers/proctolog/desk_helper.rb +61 -0
- data/app/helpers/proctolog/mentions_helper.rb +27 -0
- data/app/helpers/proctolog/open_pull_requests_helper.rb +31 -0
- data/app/helpers/proctolog/prompts_helper.rb +21 -0
- data/app/helpers/proctolog/pull_requests_helper.rb +67 -0
- data/app/helpers/proctolog/recordings_helper.rb +30 -0
- data/app/helpers/proctolog/rounds_helper.rb +27 -0
- data/app/helpers/proctolog/scenarios_helper.rb +123 -0
- data/app/helpers/proctolog/standings_helper.rb +80 -0
- data/app/jobs/proctolog/application_job.rb +8 -0
- data/app/jobs/proctolog/ask_for_release_job.rb +9 -0
- data/app/jobs/proctolog/fetch_pictures_ahead_job.rb +36 -0
- data/app/jobs/proctolog/prune_fresh_profiles_job.rb +9 -0
- data/app/jobs/proctolog/read_mentions_ahead_job.rb +12 -0
- data/app/models/proctolog/acceptance.rb +47 -0
- data/app/models/proctolog/app.rb +92 -0
- data/app/models/proctolog/browser.rb +42 -0
- data/app/models/proctolog/burst.rb +268 -0
- data/app/models/proctolog/burst_notices.rb +143 -0
- data/app/models/proctolog/chapters.rb +42 -0
- data/app/models/proctolog/check.rb +122 -0
- data/app/models/proctolog/choices.rb +112 -0
- data/app/models/proctolog/code_review.rb +251 -0
- data/app/models/proctolog/decision_record.rb +12 -0
- data/app/models/proctolog/decisions.rb +199 -0
- data/app/models/proctolog/description.rb +165 -0
- data/app/models/proctolog/difference.rb +138 -0
- data/app/models/proctolog/doctor.rb +127 -0
- data/app/models/proctolog/evidence.rb +29 -0
- data/app/models/proctolog/failure.rb +7 -0
- data/app/models/proctolog/focus.rb +48 -0
- data/app/models/proctolog/fresh_sitting.rb +35 -0
- data/app/models/proctolog/github.rb +353 -0
- data/app/models/proctolog/handover/brief.rb +138 -0
- data/app/models/proctolog/handover/layers.rb +128 -0
- data/app/models/proctolog/handover/places.rb +98 -0
- data/app/models/proctolog/handover/taken.rb +41 -0
- data/app/models/proctolog/handover/worklist.rb +35 -0
- data/app/models/proctolog/handover.rb +123 -0
- data/app/models/proctolog/housekeeping.rb +23 -0
- data/app/models/proctolog/kit.rb +36 -0
- data/app/models/proctolog/marks.rb +130 -0
- data/app/models/proctolog/membership.rb +91 -0
- data/app/models/proctolog/mentions.rb +117 -0
- data/app/models/proctolog/note.rb +94 -0
- data/app/models/proctolog/outside.rb +45 -0
- data/app/models/proctolog/overlay/brief.rb +52 -0
- data/app/models/proctolog/overlay/written.rb +33 -0
- data/app/models/proctolog/overlay.rb +87 -0
- data/app/models/proctolog/owner.rb +16 -0
- data/app/models/proctolog/pick.rb +47 -0
- data/app/models/proctolog/picture.rb +138 -0
- data/app/models/proctolog/pieces_rendering.rb +40 -0
- data/app/models/proctolog/project.rb +118 -0
- data/app/models/proctolog/pull_request.rb +89 -0
- data/app/models/proctolog/recording.rb +194 -0
- data/app/models/proctolog/release.rb +59 -0
- data/app/models/proctolog/rendering.rb +303 -0
- data/app/models/proctolog/reports.rb +66 -0
- data/app/models/proctolog/response_files.rb +55 -0
- data/app/models/proctolog/rounds.rb +305 -0
- data/app/models/proctolog/route.rb +151 -0
- data/app/models/proctolog/rules.rb +121 -0
- data/app/models/proctolog/scenario.rb +126 -0
- data/app/models/proctolog/server.rb +273 -0
- data/app/models/proctolog/since.rb +108 -0
- data/app/models/proctolog/size.rb +30 -0
- data/app/models/proctolog/standing.rb +132 -0
- data/app/models/proctolog/theme.rb +40 -0
- data/app/models/proctolog/unopened/numbers.rb +48 -0
- data/app/models/proctolog/unopened/opening.rb +88 -0
- data/app/models/proctolog/unopened.rb +203 -0
- data/app/models/proctolog/visit.rb +26 -0
- data/app/views/layouts/proctolog/application.html.erb +58 -0
- data/app/views/proctolog/acceptances/new.html.erb +34 -0
- data/app/views/proctolog/bursts/_line.html.erb +28 -0
- data/app/views/proctolog/bursts/_merged.html.erb +23 -0
- data/app/views/proctolog/bursts/_moved.html.erb +16 -0
- data/app/views/proctolog/bursts/_notices.html.erb +39 -0
- data/app/views/proctolog/bursts/_put_away.html.erb +11 -0
- data/app/views/proctolog/bursts/_rules.html.erb +27 -0
- data/app/views/proctolog/bursts/_side.html.erb +49 -0
- data/app/views/proctolog/bursts/_side_stop.html.erb +10 -0
- data/app/views/proctolog/bursts/show.html.erb +54 -0
- data/app/views/proctolog/code_reviews/show.html.erb +33 -0
- data/app/views/proctolog/decisions/_alternative.html.erb +5 -0
- data/app/views/proctolog/decisions/_beneath.html.erb +21 -0
- data/app/views/proctolog/decisions/_box.html.erb +27 -0
- data/app/views/proctolog/decisions/_choices.html.erb +9 -0
- data/app/views/proctolog/decisions/_draft_tools.html.erb +47 -0
- data/app/views/proctolog/decisions/_part.html.erb +61 -0
- data/app/views/proctolog/decisions/_round_data.html.erb +1 -0
- data/app/views/proctolog/decisions/_theme.html.erb +13 -0
- data/app/views/proctolog/decisions/index.html.erb +75 -0
- data/app/views/proctolog/notes/_card.html.erb +19 -0
- data/app/views/proctolog/notes/_template.html.erb +41 -0
- data/app/views/proctolog/open_pull_requests/_side.html.erb +30 -0
- data/app/views/proctolog/open_pull_requests/index.html.erb +106 -0
- data/app/views/proctolog/prompts/show.html.erb +41 -0
- data/app/views/proctolog/pull_requests/_connections.html.erb +65 -0
- data/app/views/proctolog/pull_requests/_finish.html.erb +42 -0
- data/app/views/proctolog/pull_requests/_margin.html.erb +33 -0
- data/app/views/proctolog/pull_requests/_note.html.erb +4 -0
- data/app/views/proctolog/pull_requests/_outline.html.erb +26 -0
- data/app/views/proctolog/pull_requests/_paragraphs.html.erb +9 -0
- data/app/views/proctolog/pull_requests/_scenarios.html.erb +16 -0
- data/app/views/proctolog/pull_requests/_since.html.erb +42 -0
- data/app/views/proctolog/pull_requests/_standing.html.erb +51 -0
- data/app/views/proctolog/pull_requests/_status.html.erb +5 -0
- data/app/views/proctolog/pull_requests/_try.html.erb +122 -0
- data/app/views/proctolog/pull_requests/show.html.erb +145 -0
- data/app/views/proctolog/recordings/_dialog.html.erb +26 -0
- data/app/views/proctolog/recordings/_list.html.erb +8 -0
- data/app/views/proctolog/recordings/_note.html.erb +22 -0
- data/app/views/proctolog/recordings/index.html.erb +33 -0
- data/app/views/proctolog/recordings/show.html.erb +118 -0
- data/app/views/proctolog/scenarios/_dialog.html.erb +22 -0
- data/app/views/proctolog/scenarios/_note.html.erb +18 -0
- data/app/views/proctolog/scenarios/_open.html.erb +9 -0
- data/app/views/proctolog/scenarios/_scenario.html.erb +48 -0
- data/app/views/proctolog/scenarios/_server.html.erb +20 -0
- data/app/views/proctolog/scenarios/index.html.erb +62 -0
- data/bin/proctolog +330 -0
- data/completions/_proctolog +143 -0
- data/config/application.rb +94 -0
- data/config/database.yml +16 -0
- data/config/environment.rb +6 -0
- data/config/initializers/grouse.rb +113 -0
- data/config/initializers/grouse_notes.rb +9 -0
- data/config/keys.yml +6 -0
- data/config/routes.rb +71 -0
- data/config/storage.yml +4 -0
- data/db/migrate/20261019213542_create_grouse_tables.grouse.rb +128 -0
- data/db/migrate/20261019213550_create_active_storage_tables.active_storage.rb +58 -0
- data/docs/manual/app.md +157 -0
- data/docs/manual/bursts.md +129 -0
- data/docs/manual/deciding.md +89 -0
- data/docs/manual/desk.png +0 -0
- data/docs/manual/films.md +20 -0
- data/docs/manual/handover.md +93 -0
- data/docs/manual/pr-format.md +75 -0
- data/docs/manual/reading.md +233 -0
- data/docs/manual/unopened.md +80 -0
- data/docs/manual/updates.md +55 -0
- data/lib/proctolog/installation.rb +125 -0
- data/lib/proctolog/version.rb +7 -0
- data/public/asking.js +50 -0
- data/public/code-review.js +40 -0
- data/public/cursor.js +292 -0
- data/public/decision-screenshots.js +97 -0
- data/public/decisions.css +949 -0
- data/public/decisions.js +1289 -0
- data/public/desk.css +582 -0
- data/public/desk.js +126 -0
- data/public/favicon.png +0 -0
- data/public/keys.js +228 -0
- data/public/mentions.js +389 -0
- data/public/messages.js +103 -0
- data/public/notes.js +731 -0
- data/public/pick.css +99 -0
- data/public/plaque.png +0 -0
- data/public/pr-number.js +11 -0
- data/public/prompt.css +46 -0
- data/public/prompt.js +16 -0
- data/public/pull_request.css +805 -0
- data/public/recording.css +379 -0
- data/public/recording.js +394 -0
- data/public/release.js +25 -0
- data/public/respond.css +260 -0
- data/public/respond.js +95 -0
- data/public/review-drafts.js +949 -0
- data/public/review-rounds.js +1447 -0
- data/public/scenarios.css +253 -0
- data/public/scenarios.js +36 -0
- data/public/schemas/proctolog-response-v1.json +101 -0
- data/public/side.js +133 -0
- data/public/standing.css +478 -0
- data/public/terminal-apl.svg +6 -0
- data/public/terminal-cuneiform.svg +3 -0
- data/public/terminal-grain-dots.svg +6 -0
- data/public/terminal-grain-weave.svg +6 -0
- data/public/terminal-grain-wedges.svg +6 -0
- data/public/terminal-shades.svg +3 -0
- data/public/terminal.css +4426 -0
- data/public/toast.js +26 -0
- data/public/try.js +124 -0
- data/public/viewer.js +808 -0
- data/public/words.js +81 -0
- data/skills/proctolog-handover/SKILL.md +77 -0
- data/skills/proctolog-pr-writing/SKILL.md +228 -0
- data/skills/proctolog-pr-writing/references/example-description.md +33 -0
- metadata +366 -0
data/bin/proctolog
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../lib/proctolog/installation"
|
|
5
|
+
|
|
6
|
+
# A checkout runs in its own bundle, whatever the calling project's. A gem
|
|
7
|
+
# runs on what RubyGems installed with it, which its launcher has loaded.
|
|
8
|
+
if Proctolog::Installation.here.checkout? && ENV["PROCTOLOG_BUNDLED"] != "1"
|
|
9
|
+
require "bundler"
|
|
10
|
+
require "rbconfig"
|
|
11
|
+
|
|
12
|
+
Bundler.with_unbundled_env do
|
|
13
|
+
exec({"BUNDLE_FROZEN" => "true", "BUNDLE_GEMFILE" => File.expand_path("../Gemfile", __dir__), "PROCTOLOG_BUNDLED" => "1"}, RbConfig.ruby, Gem.bin_path("bundler", "bundle"), "exec", RbConfig.ruby, __FILE__, *ARGV)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
require_relative "../config/application"
|
|
18
|
+
require_relative "../app/models/proctolog/project"
|
|
19
|
+
require "rbconfig"
|
|
20
|
+
require "thor"
|
|
21
|
+
|
|
22
|
+
module Proctolog
|
|
23
|
+
# Opens the checkout --project names, or the one the command runs in.
|
|
24
|
+
module Booting
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# A command prints what it was asked for and nothing else, and asks
|
|
28
|
+
# 1Password for no keys. The desk's log, with what Grouse says at boot it
|
|
29
|
+
# is missing, and the keys are for `serve`, which says so in the block.
|
|
30
|
+
def boot!
|
|
31
|
+
Project.current = Project.discover(options[:project] || Dir.pwd)
|
|
32
|
+
Desk.config.log_level = :error
|
|
33
|
+
Desk.config.x.reads_keys = false
|
|
34
|
+
yield Desk.config if block_given?
|
|
35
|
+
Desk.initialize! unless Desk.initialized?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# A pull request as the owner names one: an open one by its number, and
|
|
39
|
+
# one nobody has opened yet by its branch, or by its number, 01. Nil for
|
|
40
|
+
# an open one; the unopened one otherwise, or a sentence that says why not.
|
|
41
|
+
def unopened_by(named)
|
|
42
|
+
if named.match?(/\A[1-9]\d*\z/) then nil
|
|
43
|
+
elsif PullRequest.unopened?(named) then Unopened.find(named) || abort("No pull request waits unopened as #{named}: `proctolog unopened` lists those that do.")
|
|
44
|
+
else Unopened.on(named) || abort("Nothing is written for #{named}. Write its pull_request.md in #{Unopened.folder_of(named)}.")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class BurstCommands < Thor
|
|
50
|
+
include Booting
|
|
51
|
+
|
|
52
|
+
def self.exit_on_failure? = true
|
|
53
|
+
|
|
54
|
+
class_option :project, type: :string, aliases: "-C", desc: "The Git checkout whose bursts to open"
|
|
55
|
+
|
|
56
|
+
desc "new NAME", "Start a burst of your own, kept outside the repository"
|
|
57
|
+
long_desc <<~TEXT
|
|
58
|
+
Writes NAME.yml among your own bursts, beside your marks under ~/.local/share/proctolog, where every
|
|
59
|
+
checkout of this repository on this Mac finds it and Git never does. NAME is lowercase words joined by
|
|
60
|
+
hyphens; it is what `route` and the desk's addresses use. Name its pull requests with --pr, --top and
|
|
61
|
+
--stack, each as many times as needed: an open pull request by its number, one nobody has opened yet by
|
|
62
|
+
its branch, so that the burst still holds it once it is opened.
|
|
63
|
+
TEXT
|
|
64
|
+
option :pr, type: :string, repeatable: true, desc: "A pull request that stands alone, by its number, or its branch while not opened"
|
|
65
|
+
option :top, type: :string, repeatable: true, desc: "The top of a chain, followed down by base branches"
|
|
66
|
+
option :stack, type: :numeric, repeatable: true, desc: "A stack GitHub holds, by its number"
|
|
67
|
+
option :title, type: :string, desc: "Its name on the desk (from NAME when left out)"
|
|
68
|
+
map "new" => :start
|
|
69
|
+
def start(slug)
|
|
70
|
+
boot!
|
|
71
|
+
file = Burst.start(slug, name: options[:title], stacks: options.fetch(:stack, []), tops: listed(options.fetch(:top, [])), pull_requests: listed(options.fetch(:pr, [])))
|
|
72
|
+
puts "Wrote #{Project.current.from_root(file)}. Open it with: proctolog route #{slug}"
|
|
73
|
+
rescue Project::Missing, Failure => e
|
|
74
|
+
abort e.message
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Thor gives every group its own `tree`; the top level's already shows this one's.
|
|
78
|
+
desc "tree", "Print a tree of all available commands", hide: true
|
|
79
|
+
def tree = super
|
|
80
|
+
|
|
81
|
+
no_commands do
|
|
82
|
+
# As a burst file lists them: by number, or by branch while not opened.
|
|
83
|
+
def listed(named) = named.map { it.match?(/\A[1-9]\d*\z/) ? Integer(it) : (PullRequest.unopened?(it) ? unopened_by(it).branch : it) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
desc "list", "List the bursts this checkout opens, and where each is kept"
|
|
87
|
+
def list
|
|
88
|
+
boot!
|
|
89
|
+
Burst.all.each { puts format("%-24s %-5s %s", it.slug, it.own? ? "yours" : "repo", Project.current.from_root(it.file)) }
|
|
90
|
+
rescue Project::Missing, Failure => e
|
|
91
|
+
abort e.message
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class UnopenedCommands < Thor
|
|
96
|
+
include Booting
|
|
97
|
+
|
|
98
|
+
def self.exit_on_failure? = true
|
|
99
|
+
|
|
100
|
+
class_option :project, type: :string, aliases: "-C", desc: "The Git checkout whose pull requests to read"
|
|
101
|
+
default_command :list
|
|
102
|
+
|
|
103
|
+
desc "list", "List the pull requests nobody has opened yet: number, branch, base and title"
|
|
104
|
+
def list
|
|
105
|
+
boot!
|
|
106
|
+
waiting = Unopened.all
|
|
107
|
+
puts "Nothing waits unopened. One is written as #{Unopened.folder}/BRANCH/#{Unopened::FILE}." if waiting.empty?
|
|
108
|
+
waiting.each { puts format("#%-4s %-32s into %-24s %s", it.number, it.branch, it.base, it.title) }
|
|
109
|
+
rescue Project::Missing, Failure => e
|
|
110
|
+
abort e.message
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
desc "folder BRANCH", "Print the folder a pull request nobody has opened yet is written in"
|
|
114
|
+
long_desc <<~TEXT
|
|
115
|
+
Prints where the pull request of BRANCH is written while nobody has opened it, whether anything is there
|
|
116
|
+
yet or not. Write pull_request.md in it: a title and a base as front matter, then the description, with
|
|
117
|
+
its pictures beside it, linked by their file names. The desk reads it from there under a number with a
|
|
118
|
+
leading zero, 01, and once the branch is opened on GitHub, the pull request is GitHub's.
|
|
119
|
+
TEXT
|
|
120
|
+
def folder(branch)
|
|
121
|
+
boot!
|
|
122
|
+
puts Unopened.folder_of(branch)
|
|
123
|
+
rescue Project::Missing => e
|
|
124
|
+
abort e.message
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
desc "tree", "Print a tree of all available commands", hide: true
|
|
128
|
+
def tree = super
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class CLI < Thor
|
|
132
|
+
include Booting
|
|
133
|
+
|
|
134
|
+
def self.exit_on_failure? = true
|
|
135
|
+
|
|
136
|
+
class_option :project, type: :string, aliases: "-C", desc: "The Git checkout whose bursts to open"
|
|
137
|
+
|
|
138
|
+
desc "burst SUBCOMMAND", "Start a burst of your own, or list the bursts there are"
|
|
139
|
+
subcommand "burst", BurstCommands
|
|
140
|
+
|
|
141
|
+
desc "unopened SUBCOMMAND", "List the pull requests nobody has opened yet, or say where one is written"
|
|
142
|
+
subcommand "unopened", UnopenedCommands
|
|
143
|
+
|
|
144
|
+
desc "serve", "Open the desk on http://localhost:4040"
|
|
145
|
+
option :port, type: :numeric, default: 4040, aliases: "-p", desc: "The port to listen on"
|
|
146
|
+
def serve
|
|
147
|
+
boot! do |config|
|
|
148
|
+
config.log_level = :warn
|
|
149
|
+
config.x.reads_keys = true
|
|
150
|
+
end
|
|
151
|
+
require "rackup"
|
|
152
|
+
Release.newer&.then { puts it.news }
|
|
153
|
+
Housekeeping.start
|
|
154
|
+
Rackup::Handler.get("puma").run(Desk, Host: "localhost", Port: options[:port])
|
|
155
|
+
rescue Errno::EADDRINUSE
|
|
156
|
+
abort "Port #{options[:port]} is taken. If that is the desk, it is open at http://localhost:#{options[:port]}; " \
|
|
157
|
+
"for a second one: proctolog serve --port #{options[:port] + 1}"
|
|
158
|
+
rescue Project::Missing => e
|
|
159
|
+
abort e.message
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
desc "route [BURST]", "Print the order to take a burst in"
|
|
163
|
+
def route(burst = nil)
|
|
164
|
+
boot!
|
|
165
|
+
burst ||= Burst.latest&.slug
|
|
166
|
+
Burst.find(burst).route.each do |stop|
|
|
167
|
+
puts format("%-6s #%-5s %s", stop.stacked? ? "#{stop.layer}/#{stop.of}" : "alone", stop.number, stop.title)
|
|
168
|
+
end
|
|
169
|
+
rescue Project::Missing => e
|
|
170
|
+
abort e.message
|
|
171
|
+
rescue Burst::Missing, Github::Unreachable, Failure => e
|
|
172
|
+
abort e.message
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
desc "handover [BURST]", "Hand what the owner decided so far to the agent that runs it"
|
|
176
|
+
long_desc <<~TEXT
|
|
177
|
+
Takes a pass as `e` on the desk does, with no page open: the owner's open notes, their choices on the
|
|
178
|
+
decisions, the code comments nobody resolved and their replies to answers that came back. Writes it as a
|
|
179
|
+
handover folder where the desk keeps them, and says where its brief is: read it, carry it out, and answer
|
|
180
|
+
in the one file it names. The desk takes that answer in, for the owner to confirm.
|
|
181
|
+
|
|
182
|
+
The owner may be halfway through: what they have not reviewed yet stays out, and the brief asks that it
|
|
183
|
+
be left as it is. What an earlier pass handed over and nobody answered is held back, so that two agents
|
|
184
|
+
never do the same work; --again takes it into this pass too. --part narrows the pass to one part of the
|
|
185
|
+
burst. With nothing to hand over, it writes nothing.
|
|
186
|
+
TEXT
|
|
187
|
+
option :part, type: :string, desc: "One part of the burst: its stack's or top's number, or alone"
|
|
188
|
+
option :again, type: :boolean, default: false, desc: "Take again what an earlier pass handed over and nobody answered"
|
|
189
|
+
def handover(burst = nil)
|
|
190
|
+
boot!
|
|
191
|
+
burst = Burst.find(burst || Burst.latest&.slug || abort("#{Project.current.root} has no burst. Start one: proctolog burst new NAME"))
|
|
192
|
+
route = burst.route
|
|
193
|
+
focus = Focus.on(route, options[:part])
|
|
194
|
+
abort "#{burst.name} has no part #{options[:part]}. Its parts: #{route.parts.map(&:to_param).join(", ")}" if options[:part] && focus.whole?
|
|
195
|
+
worklist = Rounds.new(burst, route:, focus:).worklist(batch_id: SecureRandom.uuid, again: options[:again])
|
|
196
|
+
handover = burst.handover(route, focus:, worklist:)
|
|
197
|
+
puts Handover::Taken.new(burst:, worklist:, brief: (handover.write if worklist.items.any?), answer: handover.answer_file)
|
|
198
|
+
rescue Project::Missing, Burst::Missing, Github::Unreachable, Failure => e
|
|
199
|
+
abort e.message
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
desc "check [PULL_REQUEST]", "Check a pull request's description as the desk reads it, or a file's with --file"
|
|
203
|
+
long_desc <<~TEXT
|
|
204
|
+
Reads a description with the desk's own parser and says, line by line, what would keep the desk from
|
|
205
|
+
reading it well (a fault) and what it only advises (a warning). PULL_REQUEST checks an open pull
|
|
206
|
+
request's description as GitHub holds it now, by its number, or one nobody has opened yet by its branch
|
|
207
|
+
or its number (01), with its pictures; --file checks a local Markdown file before it is published.
|
|
208
|
+
GitHub renders each, and keeps nothing of it.
|
|
209
|
+
|
|
210
|
+
Exits 0 with no faults, 1 with faults, and 2 when GitHub could not be asked.
|
|
211
|
+
TEXT
|
|
212
|
+
option :file, type: :string, aliases: "-f", desc: "A local Markdown file to check before it is published"
|
|
213
|
+
def check(named = nil)
|
|
214
|
+
boot!
|
|
215
|
+
github = Github.new(Project.current.github_repository || abort("#{Project.current.root} has no GitHub repository as its origin."))
|
|
216
|
+
named || options[:file] || abort("Name a pull request by its number or its branch, or a file with --file.")
|
|
217
|
+
markdown, rendering = if options[:file]
|
|
218
|
+
File.read(options[:file]).then { [it, github.rendered(it)] }
|
|
219
|
+
elsif (waiting = unopened_by(named))
|
|
220
|
+
[waiting.description.to_s, waiting.rendering]
|
|
221
|
+
else
|
|
222
|
+
github.described(Integer(named))
|
|
223
|
+
end
|
|
224
|
+
Check.new(markdown, rendering:).tap { puts it }.then { exit(it.passed? ? 0 : 1) }
|
|
225
|
+
rescue Github::Unreachable => e
|
|
226
|
+
warn e.message
|
|
227
|
+
exit 2
|
|
228
|
+
rescue Project::Missing, Failure, SystemCallError, ArgumentError => e
|
|
229
|
+
abort e.message
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
desc "themes", "Print the themes a decision may name, with what each means"
|
|
233
|
+
def themes
|
|
234
|
+
boot!
|
|
235
|
+
Theme.all.each { puts format("%-10s %s", it.name, it.meaning) }
|
|
236
|
+
rescue Project::Missing, Failure => e
|
|
237
|
+
abort e.message
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
desc "version", "Print which PRoctolog this is"
|
|
241
|
+
map %w[-v --version] => :version
|
|
242
|
+
def version = puts("PRoctolog #{VERSION}")
|
|
243
|
+
|
|
244
|
+
desc "install", "Link zsh's completion and the agent skills to this PRoctolog"
|
|
245
|
+
long_desc <<~TEXT
|
|
246
|
+
Links zsh's completion into ~/.zsh/completions and the agent skills into ~/.claude/skills and
|
|
247
|
+
~/.agents/skills, from wherever this PRoctolog lives: the gem, or the checkout, which also gets its
|
|
248
|
+
launcher in ~/.local/bin. Leaves anything of the same name that is not PRoctolog's as it is.
|
|
249
|
+
`update` runs it for the version it installs.
|
|
250
|
+
TEXT
|
|
251
|
+
def install
|
|
252
|
+
puts Installation.here.install
|
|
253
|
+
rescue Installation::Refused => e
|
|
254
|
+
abort e.message
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
desc "update", "Install the latest PRoctolog, and link to it"
|
|
258
|
+
long_desc <<~TEXT
|
|
259
|
+
Asks RubyGems for the latest PRoctolog and, when it is newer than this one, installs it the way this one
|
|
260
|
+
was: with `rv tool install` for one rvx runs, with `gem install` otherwise. A checkout is pulled
|
|
261
|
+
forward instead, when it is clean and on origin's own branch, and its bundle installed. Then runs the
|
|
262
|
+
new one's `install`. A desk open now runs on as it was: restart it to run the new one.
|
|
263
|
+
TEXT
|
|
264
|
+
def update
|
|
265
|
+
installation = Installation.here
|
|
266
|
+
return take_with_git(installation) if installation.checkout?
|
|
267
|
+
|
|
268
|
+
Desk.initialize! unless Desk.initialized?
|
|
269
|
+
latest = Release.ask || abort("RubyGems named no PRoctolog: it did not answer, or holds none yet. Try again later.")
|
|
270
|
+
return puts("PRoctolog #{Release.running} is the latest.") unless latest > Release.running
|
|
271
|
+
|
|
272
|
+
installation.rv_tool? ? take_with_rv(latest) : take_with_gem(latest)
|
|
273
|
+
rescue Installation::Refused, RuntimeError => e
|
|
274
|
+
abort e.message
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
no_commands do
|
|
278
|
+
# rv runs as the shell would, not on the Ruby and gems it gave this one.
|
|
279
|
+
def take_with_rv(latest)
|
|
280
|
+
Outside.run do |environment|
|
|
281
|
+
system(environment, "rv", "tool", "install", "proctolog@#{latest}", "--gem-server", "#{Release.server}/", exception: true)
|
|
282
|
+
puts taken(latest)
|
|
283
|
+
# rvx runs the newest it holds, which is now this one; rv 0.7 runs no proctolog@VERSION.
|
|
284
|
+
exec(environment, "rvx", "--no-install", "proctolog", "install")
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# With this Ruby's own gem command, into the gems this one is among.
|
|
289
|
+
def take_with_gem(latest)
|
|
290
|
+
system(RbConfig.ruby, File.join(RbConfig::CONFIG["bindir"], "gem"), "install", "proctolog", "--version", latest.version, "--clear-sources", "--source", Release.server, exception: true)
|
|
291
|
+
puts taken(latest)
|
|
292
|
+
# RubyGems' launcher takes the version to run as its first word.
|
|
293
|
+
exec(RbConfig.ruby, File.join(Gem.bindir, "proctolog"), "_#{latest}_", "install")
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# The checkout's own bundle, on this Ruby, as bin/proctolog runs it.
|
|
297
|
+
def take_with_git(installation)
|
|
298
|
+
puts installation.pull
|
|
299
|
+
Bundler.with_unbundled_env do
|
|
300
|
+
system(RbConfig.ruby, Gem.bin_path("bundler", "bundle"), "install", "--quiet", chdir: installation.root.to_s, exception: true)
|
|
301
|
+
end
|
|
302
|
+
puts "Pulled #{installation.root} forward. Restart the desk to run it."
|
|
303
|
+
exec(RbConfig.ruby, installation.root.join("bin/install").to_s)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def taken(latest) = "Installed PRoctolog #{latest}. Restart the desk to run it: one open now still runs #{Release.running}."
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
desc "doctor", "Say what this Mac and this checkout lack, a line each with the fix"
|
|
310
|
+
long_desc <<~TEXT
|
|
311
|
+
Checks what the desk needs (gh signed in, the launcher on the PATH, a GitHub checkout gh can read, a burst
|
|
312
|
+
file) and what it would like (the PR-writing skill, Chrome, Diffle, ImageMagick, ffmpeg, an app.yml whose
|
|
313
|
+
programs are there, a free port 4040). Lines start ok, !! for a need that is missing, or -- for a nicety
|
|
314
|
+
that is off.
|
|
315
|
+
|
|
316
|
+
Exits 0 when every need is met, 1 when one is not.
|
|
317
|
+
TEXT
|
|
318
|
+
def doctor
|
|
319
|
+
project = begin
|
|
320
|
+
Project.current = Project.discover(options[:project] || Dir.pwd)
|
|
321
|
+
rescue Project::Missing
|
|
322
|
+
nil
|
|
323
|
+
end
|
|
324
|
+
Desk.initialize! unless Desk.initialized?
|
|
325
|
+
Doctor.new(project).tap { puts it }.then { exit(it.fine? ? 0 : 1) }
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
Proctolog::CLI.start
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#compdef proctolog
|
|
2
|
+
|
|
3
|
+
# Completes proctolog: its commands, options and a checkout's burst names,
|
|
4
|
+
# your own ones first, then the repository's from bursts/ and from reviews/,
|
|
5
|
+
# where burst files were kept before; for check and burst new, the open pull
|
|
6
|
+
# requests of the desk's last reading, with their titles, and the branches
|
|
7
|
+
# whose pull requests nobody has opened yet; for unopened folder, every
|
|
8
|
+
# branch of the checkout.
|
|
9
|
+
|
|
10
|
+
# "owner/name" of the checkout's GitHub origin.
|
|
11
|
+
_proctolog_repository() {
|
|
12
|
+
local remote
|
|
13
|
+
remote=$(git -C "$1" remote get-url origin 2>/dev/null)
|
|
14
|
+
print -r -- ${${remote#*github.com[:/]}%.git}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# The open pull requests the desk last read for this checkout, as
|
|
18
|
+
# "number:title", read from the reading it keeps; none when it keeps none.
|
|
19
|
+
_proctolog_pull_requests() {
|
|
20
|
+
local repository reading
|
|
21
|
+
repository=$(_proctolog_repository "$1")
|
|
22
|
+
reading=${PROCTOLOG_CACHE:-$HOME/.cache/proctolog}/$repository/readings/${repository/\//-}.json
|
|
23
|
+
[[ -r $reading ]] || return
|
|
24
|
+
ruby -rjson -e 'JSON.parse(File.read(ARGV[0])).fetch("nodes").each { |node| puts "#{node["number"]}:#{node["title"].tr(":", " ")}" }' "$reading" 2>/dev/null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# The pull requests nobody has opened yet, as "branch:title", read from
|
|
28
|
+
# their folders in the store; the title is the front matter's.
|
|
29
|
+
_proctolog_unopened() {
|
|
30
|
+
local folder
|
|
31
|
+
folder=${PROCTOLOG_DATA:-$HOME/.local/share/proctolog}/$(_proctolog_repository "$1")/unopened
|
|
32
|
+
[[ -d $folder ]] || return
|
|
33
|
+
ruby -ryaml -e 'Dir.glob("**/pull_request.md", base: ARGV[0]).sort.each { |file| title = File.read(File.join(ARGV[0], file))[/\A---\n(.*?)^---/m, 1]&.then { YAML.safe_load(it)&.fetch("title", nil) rescue nil }; puts "#{File.dirname(file)}:#{title.to_s.tr(":", " ")}" }' "$folder" 2>/dev/null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# The checkout's own branches.
|
|
37
|
+
_proctolog_branches() {
|
|
38
|
+
git -C "$1" for-each-ref --format='%(refname:short)' refs/heads/ 2>/dev/null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# A burst's parts, as `handover --part` takes them: its stacks and tops by
|
|
42
|
+
# number, and alone; read from the first of the burst's files it is given.
|
|
43
|
+
_proctolog_parts() {
|
|
44
|
+
[[ -r $1 ]] || return
|
|
45
|
+
ruby -ryaml -e 'burst = YAML.safe_load_file(ARGV[0]) || {}; puts [*burst["stacks"], *burst["tops"], "alone"]' "$1" 2>/dev/null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_proctolog() {
|
|
49
|
+
local root own named
|
|
50
|
+
local -a commands burst_commands unopened_commands bursts pull_requests unopened branches parts project files
|
|
51
|
+
commands=(
|
|
52
|
+
'serve:Open the desk on http://localhost:4040'
|
|
53
|
+
'route:Print the order to take a burst in'
|
|
54
|
+
'handover:Hand what the owner decided so far to the agent that runs it'
|
|
55
|
+
'burst:Start a burst of your own, or list the bursts there are'
|
|
56
|
+
'unopened:List the pull requests nobody has opened yet, or say where one is written'
|
|
57
|
+
'check:Check a description as the desk reads it, of a pull request or a file'
|
|
58
|
+
'themes:Print the themes a decision may name, with what each means'
|
|
59
|
+
'doctor:Say what this Mac and this checkout lack, a line each with the fix'
|
|
60
|
+
'version:Print which PRoctolog this is'
|
|
61
|
+
'install:Link zsh'"'"'s completion and the agent skills to this PRoctolog'
|
|
62
|
+
'update:Install the latest PRoctolog, and link to it'
|
|
63
|
+
'tree:Print a tree of all available commands'
|
|
64
|
+
'help:Describe the commands, or one of them'
|
|
65
|
+
)
|
|
66
|
+
burst_commands=(
|
|
67
|
+
'new:Start a burst of your own, kept outside the repository'
|
|
68
|
+
'list:List the bursts this checkout opens, and where each is kept'
|
|
69
|
+
'help:Describe the burst commands, or one of them'
|
|
70
|
+
)
|
|
71
|
+
unopened_commands=(
|
|
72
|
+
'list:List the pull requests nobody has opened yet'
|
|
73
|
+
'folder:Print the folder a pull request nobody has opened yet is written in'
|
|
74
|
+
'help:Describe the unopened commands, or one of them'
|
|
75
|
+
)
|
|
76
|
+
project=('(-C --project)'{-C,--project}'[Git checkout]:directory:_directories')
|
|
77
|
+
root=$(git -C "$PWD" rev-parse --show-toplevel 2>/dev/null)
|
|
78
|
+
own=${PROCTOLOG_DATA:-$HOME/.local/share/proctolog}/$(_proctolog_repository "$root")/bursts
|
|
79
|
+
bursts=($own/*.yml(N:t:r) $root/.proctolog/{bursts,reviews}/*.yml(N:t:r))
|
|
80
|
+
bursts=(${(u)bursts})
|
|
81
|
+
|
|
82
|
+
_arguments -C '1: :->command' '*:: :->argument'
|
|
83
|
+
|
|
84
|
+
case $state in
|
|
85
|
+
command) _describe -t commands 'command' commands ;;
|
|
86
|
+
argument)
|
|
87
|
+
case $words[1] in
|
|
88
|
+
serve) _arguments '(-p --port)'{-p,--port}'[The port to listen on]:port:' $project ;;
|
|
89
|
+
route) _arguments $project "1:burst:(${bursts})" ;;
|
|
90
|
+
handover)
|
|
91
|
+
named=${${words[2,-1]:#-*}[1]}
|
|
92
|
+
[[ -n $named ]] || named=${${(f)"$(ls -t $own/*.yml(N) $root/.proctolog/{bursts,reviews}/*.yml(N) 2>/dev/null)"}[1]:t:r}
|
|
93
|
+
files=($own/$named.yml(N) $root/.proctolog/{bursts,reviews}/$named.yml(N))
|
|
94
|
+
parts=(${(f)"$(_proctolog_parts "$files[1]")"})
|
|
95
|
+
_arguments $project \
|
|
96
|
+
"--part[One part of the burst, by its stack or top, or alone]:part:(${parts})" \
|
|
97
|
+
'--again[Take again what an earlier pass handed over and nobody answered]' \
|
|
98
|
+
"1:burst:(${bursts})" ;;
|
|
99
|
+
burst)
|
|
100
|
+
if (( CURRENT == 2 )); then
|
|
101
|
+
_describe -t commands 'burst command' burst_commands
|
|
102
|
+
else
|
|
103
|
+
case $words[2] in
|
|
104
|
+
new)
|
|
105
|
+
pull_requests=(${(f)"$(_proctolog_pull_requests "$root")"})
|
|
106
|
+
unopened=(${(f)"$(_proctolog_unopened "$root")"})
|
|
107
|
+
_arguments $project \
|
|
108
|
+
'*--pr[A pull request that stands alone]:pull request:{_describe -t pull-requests "open pull request" pull_requests; _describe -t unopened "not opened yet" unopened}' \
|
|
109
|
+
'*--top[The top of a chain, followed down by base branches]:pull request:{_describe -t pull-requests "open pull request" pull_requests; _describe -t unopened "not opened yet" unopened}' \
|
|
110
|
+
'*--stack[A stack GitHub holds, by its number]:stack number:' \
|
|
111
|
+
'--title[Its name on the desk]:title:' \
|
|
112
|
+
'2:burst name (lowercase words joined by hyphens):' ;;
|
|
113
|
+
list) _arguments $project ;;
|
|
114
|
+
help) _describe -t commands 'burst command' burst_commands ;;
|
|
115
|
+
esac
|
|
116
|
+
fi
|
|
117
|
+
;;
|
|
118
|
+
unopened)
|
|
119
|
+
if (( CURRENT == 2 )); then
|
|
120
|
+
_describe -t commands 'unopened command' unopened_commands
|
|
121
|
+
else
|
|
122
|
+
case $words[2] in
|
|
123
|
+
list) _arguments $project ;;
|
|
124
|
+
folder)
|
|
125
|
+
branches=(${(f)"$(_proctolog_branches "$root")"})
|
|
126
|
+
_arguments $project "2:branch:(${branches})" ;;
|
|
127
|
+
help) _describe -t commands 'unopened command' unopened_commands ;;
|
|
128
|
+
esac
|
|
129
|
+
fi
|
|
130
|
+
;;
|
|
131
|
+
check)
|
|
132
|
+
pull_requests=(${(f)"$(_proctolog_pull_requests "$root")"})
|
|
133
|
+
unopened=(${(f)"$(_proctolog_unopened "$root")"})
|
|
134
|
+
_arguments $project '(-f --file)'{-f,--file}'[A local Markdown file to check before it is published]:file:_files' \
|
|
135
|
+
"1:pull request:{_describe -t pull-requests 'open pull request' pull_requests; _describe -t unopened 'not opened yet' unopened}" ;;
|
|
136
|
+
themes|doctor|version|install|update) _arguments $project ;;
|
|
137
|
+
help) _describe -t commands 'command' commands ;;
|
|
138
|
+
esac
|
|
139
|
+
;;
|
|
140
|
+
esac
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_proctolog "$@"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The PRoctolog desk: a local page over a burst's pull requests.
|
|
4
|
+
# From a checkout it has its own bundle; installed as a gem, the gems RubyGems
|
|
5
|
+
# installed with it. Either way it reads only the selected project's files.
|
|
6
|
+
|
|
7
|
+
require "bundler/setup" if File.exist?(File.expand_path("../Gemfile", __dir__))
|
|
8
|
+
require_relative "../lib/proctolog/version"
|
|
9
|
+
require_relative "../lib/proctolog/installation"
|
|
10
|
+
require "ipaddr"
|
|
11
|
+
require "tmpdir"
|
|
12
|
+
require "action_controller/railtie"
|
|
13
|
+
# Grouse, the overlay the owner writes notes on the desk with, keeps them in a
|
|
14
|
+
# database, records their voice notes and screenshots, and reads them later.
|
|
15
|
+
require "active_record/railtie"
|
|
16
|
+
require "active_storage/engine"
|
|
17
|
+
require "active_job/railtie"
|
|
18
|
+
begin
|
|
19
|
+
require "grouse-rails"
|
|
20
|
+
rescue LoadError
|
|
21
|
+
# Not bundled: the Gemfile's :grouse group is optional, as the gem is private.
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The agent shell has no LANG, and descriptions hold dashes and arrows.
|
|
25
|
+
Encoding.default_external = Encoding.default_internal = Encoding::UTF_8
|
|
26
|
+
|
|
27
|
+
module Proctolog
|
|
28
|
+
class Desk < Rails::Application
|
|
29
|
+
# What is the owner's beyond any one repository: the secret below, and
|
|
30
|
+
# beside it a folder per repository (Project).
|
|
31
|
+
def self.data = Pathname(ENV.fetch("PROCTOLOG_DATA") { File.expand_path("~/.local/share/proctolog") })
|
|
32
|
+
|
|
33
|
+
# What the desk read and can read again: GitHub's answers, pictures, and
|
|
34
|
+
# which PRoctolog is the latest (Release).
|
|
35
|
+
def self.cache = Pathname(ENV.fetch("PROCTOLOG_CACHE") { File.expand_path("~/.cache/proctolog") })
|
|
36
|
+
|
|
37
|
+
# Grouse's notes on the desk itself, with the files they keep
|
|
38
|
+
# (config/database.yml, config/storage.yml): one store for every checkout
|
|
39
|
+
# the desk reviews, as the notes are about the desk. A test process keeps
|
|
40
|
+
# one of its own and drops it when it ends.
|
|
41
|
+
def self.grouse? = defined?(Grouse::Engine).present?
|
|
42
|
+
|
|
43
|
+
def self.grouse_notes
|
|
44
|
+
(Rails.env.test? ? scratch : data).join("grouse_notes").tap(&:mkpath)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# A value made once and kept in the data folder, outside the installed app
|
|
48
|
+
# and Git, for this user alone.
|
|
49
|
+
def self.kept(name)
|
|
50
|
+
file = data.join(name)
|
|
51
|
+
file.exist? ? file.read.strip : yield.tap { file.dirname.mkpath && file.write(it, perm: 0o600) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.scratch = @scratch ||= Pathname(Dir.mktmpdir("proctolog")).tap { |made| at_exit { made.rmtree } }
|
|
55
|
+
|
|
56
|
+
config.root = File.expand_path("..", __dir__)
|
|
57
|
+
config.load_defaults 8.1
|
|
58
|
+
config.eager_load = false
|
|
59
|
+
config.enable_reloading = true
|
|
60
|
+
config.consider_all_requests_local = true
|
|
61
|
+
# Kept outside the installed app so an update does not sign out an open desk.
|
|
62
|
+
config.secret_key_base = kept("secret_key_base") { SecureRandom.hex(64) }
|
|
63
|
+
# Whatever the environment: a page of another site that points a name of
|
|
64
|
+
# its own at this Mac is turned away.
|
|
65
|
+
config.hosts = ["localhost", IPAddr.new("127.0.0.0/8"), IPAddr.new("::1")]
|
|
66
|
+
config.logger = ActiveSupport::Logger.new($stdout)
|
|
67
|
+
# Grouse says at boot what is not set up yet, which its tests need not hear.
|
|
68
|
+
config.log_level = Rails.env.test? ? :error : :warn
|
|
69
|
+
config.cache_store = :memory_store
|
|
70
|
+
# A desk at work keeps what it read of GitHub for its next start. Its tests
|
|
71
|
+
# read a made-up GitHub and must leave no trace of it beside the real one.
|
|
72
|
+
config.x.keeps_readings = !Rails.env.test?
|
|
73
|
+
config.public_file_server.enabled = true
|
|
74
|
+
config.public_file_server.headers = {"cache-control" => "no-cache"}
|
|
75
|
+
|
|
76
|
+
# Jobs run in the desk's own process: the pictures and pull requests it
|
|
77
|
+
# reads ahead of the pages that show them, and Grouse's write-up of a note
|
|
78
|
+
# a moment after it is sent. Its tests queue them and run none they do
|
|
79
|
+
# not ask for.
|
|
80
|
+
config.active_job.queue_adapter = Rails.env.test? ? :test : :async
|
|
81
|
+
# Whether Grouse is given the owner's keys from 1Password (config/keys.yml):
|
|
82
|
+
# when the desk serves, and for Rails' own commands such as grouse:doctor.
|
|
83
|
+
# A `proctolog` command that only prints asks 1Password for nothing.
|
|
84
|
+
config.x.reads_keys = Rails.env.development?
|
|
85
|
+
config.active_storage.service = :local
|
|
86
|
+
config.active_storage.variant_processor = :disabled
|
|
87
|
+
# The migrations are the record; the desk brings its database up to them
|
|
88
|
+
# whenever it boots (config/initializers/grouse_notes.rb).
|
|
89
|
+
config.active_record.dump_schema_after_migration = false
|
|
90
|
+
# A note says which templates drew what the owner pointed at. Only where
|
|
91
|
+
# the desk runs for its owner: its tests read pages without them.
|
|
92
|
+
config.action_view.annotate_rendered_view_with_filenames = Rails.env.development?
|
|
93
|
+
end
|
|
94
|
+
end
|
data/config/database.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# The desk's one database: Grouse's notes on the desk itself, their review
|
|
2
|
+
# sessions and the files Active Storage keeps for them (config/application.rb).
|
|
3
|
+
default: &default
|
|
4
|
+
adapter: sqlite3
|
|
5
|
+
database: <%= Proctolog::Desk.grouse_notes.join("notes.sqlite3") %>
|
|
6
|
+
# The desk's own, wherever it is started: `serve` runs in the checkout under
|
|
7
|
+
# review, whose db/migrate is that app's.
|
|
8
|
+
migrations_paths: <%= Rails.root.join("db/migrate") %>
|
|
9
|
+
max_connections: 5
|
|
10
|
+
timeout: 5000
|
|
11
|
+
|
|
12
|
+
development:
|
|
13
|
+
<<: *default
|
|
14
|
+
|
|
15
|
+
test:
|
|
16
|
+
<<: *default
|