standup_md 1.0.0 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2734d31b2ce72d31aceeb789f562b015c4f0e7422b3a755992df7c1f293a7051
4
- data.tar.gz: 651d1f6f9a613366b90e2b26ba83b8e05dd80dbc5d8bde3a0fcc7d07b57acef1
3
+ metadata.gz: 47c9542a3be072fc9067823aeeaf6c41741e657f7c628476cf1811c3699978f0
4
+ data.tar.gz: 165f18178d4e7038f2c2511e0b7e0e907671c6819f9158fc601c29ebd18ac3be
5
5
  SHA512:
6
- metadata.gz: 5df3037a20d107f82cc34533f89ab8c31a2ba13eb8180b5931b052c500c5c1f7c4922d22ffed254c5fb9e9d7623b5bb207ed5d5c0c22773bcc972a91bba061d4
7
- data.tar.gz: c99e5cf930d4dcf84e6089d458321b00f427ab0c124ecf3b02b8e98d79ba416d5c629793f6f77a8cf1a0337b05ed620600833f1ad8fa2775d7e6eb7205ec22ed
6
+ metadata.gz: e56ad3a3e3e633f235af7078708ebb27f1a184cd6a0bd94a1b52299e82de2b75842f169e9495003f26c49907a9f0eb9b5c8d52511bf6cc39e1978f60848a2ca8
7
+ data.tar.gz: 043a1a0119e0921196840fae06143f33a87dbbe4e9b62c381c671defd1695c6e1d5694174872a15932b63c42119dd19764cff992cd3c0f456220f0c9474d15d6
@@ -10,6 +10,8 @@ on:
10
10
  jobs:
11
11
  test:
12
12
  runs-on: ubuntu-22.04
13
+ env:
14
+ BUNDLER_VERSION: 4.0.10
13
15
 
14
16
  strategy:
15
17
  fail-fast: false
@@ -17,14 +19,13 @@ jobs:
17
19
  ruby-version: ['3.2', '3.3', '4.0']
18
20
 
19
21
  steps:
20
- - uses: actions/checkout@v3
22
+ - uses: actions/checkout@v4
21
23
 
22
- # Set up Ruby and Bundler
23
24
  - name: Set up Ruby ${{ matrix.ruby-version }}
24
25
  uses: ruby/setup-ruby@v1
25
26
  with:
26
27
  ruby-version: ${{ matrix.ruby-version }}
27
- bundler: ${{ matrix.ruby-version == '4.0' && '4.0.9' || '2.4.13' }}
28
+ bundler: ${{ env.BUNDLER_VERSION }}
28
29
  bundler-cache: true
29
30
 
30
31
  - name: Print Ruby and Bundler versions
@@ -32,8 +33,5 @@ jobs:
32
33
  ruby -v
33
34
  bundle -v
34
35
 
35
- - name: Install dependencies
36
- run: bundle install --jobs 4 --retry 3
37
-
38
36
  - name: Run tests
39
37
  run: bundle exec rake
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- standup_md (1.0.0)
4
+ standup_md (2.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -115,7 +115,7 @@ CHECKSUMS
115
115
  standard (1.54.0) sha256=7a4b08f83d9893083c8f03bc486f0feeb6a84d48233b40829c03ef4767ea0100
116
116
  standard-custom (1.0.2) sha256=424adc84179a074f1a2a309bb9cf7cd6bfdb2b6541f20c6bf9436c0ba22a652b
117
117
  standard-performance (1.9.0) sha256=49483d31be448292951d80e5e67cdcb576c2502103c7b40aec6f1b6e9c88e3f2
118
- standup_md (1.0.0)
118
+ standup_md (2.0.0)
119
119
  stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
120
120
  test-unit (3.7.8) sha256=689d1ca53f4d46f678b4e5d68d8e4294f87fc5e8b9238cc4de7c5727e8d65943
121
121
  tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # The Standup Doctor
2
2
  <!-- vi: set ts=4 sw=4 : -->
3
3
  [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fevanthegrayt%2Fstandup_md%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/evanthegrayt/standup_md/goto?ref=master)
4
- [![Gem Version](https://badge.fury.io/rb/standup_md.svg)](https://badge.fury.io/rb/standup_md)
4
+ [![Gem Version](https://img.shields.io/gem/v/standup_md.svg)](https://rubygems.org/gems/standup_md)
5
+ ![Language: Ruby](https://img.shields.io/static/v1?label=language&message=Ruby&color=CC342D&style=flat&logo=ruby&logoColor=CC342D)
5
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
7
 
7
8
  > The cure for all your standup woes.
@@ -13,8 +14,8 @@ You can view the documentation
13
14
  [here](https://evanthegrayt.github.io/standup_md/).
14
15
 
15
16
  ## About
16
- I've now been at two separate companies where we post our daily standups in a
17
- chat client, such as Slack, Mattermost, or Riot. Typing out my standup every day
17
+ I've now been at multiple companies where we post our daily standups in a chat
18
+ client, such as Slack, Mattermost, or Riot. Typing out my standup every day
18
19
  became tedious, as I'd have to look up what I did the day before, copy and paste
19
20
  yesterday's work into a new entry, and add today's tasks. This gem automates
20
21
  most of this process, along with providing means of opening the file in your
@@ -29,7 +30,9 @@ entry exists, it will be added to today's entry as your previous day's work. See
29
30
  this in your own code somehow.
30
31
 
31
32
  ## Installation
32
- If you don't have the permissions to install system-wide gems, you're probabaly
33
+ Requires Ruby 3.2 or newer.
34
+
35
+ If you don't have the permissions to install system-wide gems, you're probably
33
36
  also running an older version of ruby. I recommend installing
34
37
  [rbenv](https://github.com/rbenv/rbenv#installation), and then installing an
35
38
  up-to-date version of ruby.
@@ -131,6 +134,28 @@ your clipboard without even opening your editor.
131
134
  standup -p | pbcopy
132
135
  ```
133
136
 
137
+ #### Post the entry for today to Slack
138
+ You can also post today's entry directly to a chat client. Slack is the default
139
+ adapter, so `standup -P` and `standup -P slack` are equivalent. Tokens are read
140
+ from environment variables at post time, and are not stored in `~/.standuprc`.
141
+ You can set a default channel via the
142
+ [config](#available-config-file-options-and-defaults) via
143
+ `c.post.configure_adapter(:slack, channel: "C123456")`.
144
+
145
+ ```sh
146
+ export STANDUP_MD_SLACK_TOKEN="xoxb-your-token"
147
+ standup -P slack --post-channel C123456
148
+ ```
149
+
150
+ The same posting path is available from Ruby:
151
+
152
+ ```ruby
153
+ file = StandupMD::File.find_by_date(Date.today).load
154
+ entry = file.entries.find(Date.today)
155
+
156
+ StandupMD::Post.post(entry, adapter: :slack, channel: "C123456")
157
+ ```
158
+
134
159
  #### Add entry to file without opening it
135
160
  You can add an entry for today without even opening your editor. Note that, if
136
161
  you have multiple entries, you must separate them with a comma and *no spaces*.
@@ -141,26 +166,17 @@ standup --no-edit --current "Work on this thing","And another thing"
141
166
 
142
167
  ### Customization and Runtime Options
143
168
  You can create a file in your home directory called `~/.standuprc`. Settings
144
- located in this file will override default behavior. This file can also have
145
- settings overwritten at runtime by the use of options. You can view [my config
146
- file](https://github.com/evanthegrayt/dotfiles/blob/master/dotfiles/standuprc)
147
- as an example. Any setting in this file can still be overridden at runtime by
148
- passing flags to the executable.
169
+ located in this file define your application defaults. CLI flags can override
170
+ those defaults for a single invocation, but they do not persist back into
171
+ `StandupMD.config` or `~/.standuprc`. You can view
172
+ [my config file](https://github.com/evanthegrayt/dotfiles/blob/master/dotfiles/standuprc)
173
+ as an example.
149
174
 
150
175
  You'll notice, a lot of settings don't have the ability to be changed at runtime
151
- when calling the executable. This is because the file structure is very
176
+ when calling the executable. This is because the markdown structure is very
152
177
  important, and changing values that affect formatting will cause problems with
153
- the file parser. If you don't want to use a default, make the change in your
154
- config file before you start editing standups. There is an [open
155
- issue](https://github.com/evanthegrayt/standup_md/issues/16) for handling this
156
- for the user, but they're not available yet.
157
-
158
- There are no options to change the headers at runtime because it uses the
159
- headers to detect tasks from previous entries. If changed at runtime, this would
160
- cause errors. For this reason, if you don't like the default headers, change
161
- them in your configuration file after installation, and then try to not change
162
- them again.
163
-
178
+ the markdown parser. If you don't want to use a default, make the change in
179
+ your config file before you start editing standups.
164
180
 
165
181
  #### Available Config File Options and Defaults
166
182
  For command-line usage, this file needs to be named `~/.standuprc`. To use in a
@@ -169,7 +185,7 @@ rails project, create an initializer (`config/initializers/standup_md.rb`).
169
185
  ```ruby
170
186
  StandupMD.configure do |c|
171
187
  # Defaults for how the file is formatted.
172
- # See https://evanthegrayt.github.io/standup_md/doc/StandupMD/Config/Cli.html
188
+ # See https://evanthegrayt.github.io/standup_md/doc/StandupMD/Config/File.html
173
189
  c.file.header_date_format = "%Y-%m-%d"
174
190
  c.file.header_depth = 1
175
191
  c.file.sub_header_depth = 2
@@ -199,38 +215,143 @@ StandupMD.configure do |c|
199
215
  c.cli.edit = true
200
216
  c.cli.write = true
201
217
  c.cli.print = false
218
+ c.cli.post = false
219
+ c.cli.post_adapter = nil
220
+ c.cli.post_channel = nil
202
221
  c.cli.auto_fill_previous = true
203
222
  c.cli.preference_file = ::File.expand_path(::File.join(ENV["HOME"], ".standuprc"))
223
+
224
+ # Defaults for posting standups to chat clients.
225
+ c.post.default_adapter = :slack
226
+ c.post.title = nil
227
+ c.post.configure_adapter(
228
+ :slack,
229
+ channel: "C123456",
230
+ token_env: "STANDUP_MD_SLACK_TOKEN"
231
+ )
204
232
  end
205
233
  ```
206
234
 
207
235
  Any options not set in this file will retain their default values. Note that if
208
- you change `file_name_format`, and don't use a month or year, there will only
236
+ you change `name_format`, and don't use a month or year, there will only
209
237
  ever be one standup file. This could cause issues long-term, as the files will
210
238
  get large over time and possibly cause performance issues.
211
239
 
212
240
 
213
241
  #### Executable Flags
214
- Some of these options can be changed at runtime. They are as follows.
242
+ Some defaults can be overridden for a single CLI invocation. They are as
243
+ follows.
215
244
 
216
- ```
245
+ ```text
217
246
  --current ARRAY List of current entry's tasks
218
- --previous ARRAY List of precious entry's tasks
247
+ --previous ARRAY List of previous entry's tasks
219
248
  --impediments ARRAY List of impediments for current entry
220
249
  --notes ARRAY List of notes for current entry
221
- --sub-header-order ARRAY The order of the sub-headers when writing the file
222
- --indent-width INTEGER Number of spaces used for each nested task level
223
- -f, --file-name-format STRING Date-formattable string to use for standup file name
224
250
  -E, --editor EDITOR Editor to use for opening standup files
225
- -d, --directory DIRECTORY The directories where standup files are located
226
- -w --[no-]write Write current entry if it doesn't exist. Default is true
227
- -a --[no-]auto-fill-previous Auto-generate 'previous' tasks for new entries
228
- -e --[no-]edit Open the file in the editor. Default is true
251
+ -d, --directory DIRECTORY The directory where standup files are located
252
+ -w, --[no-]write Write current entry if it doesn't exist. Default is true
253
+ -a, --[no-]auto-fill-previous Auto-generate 'previous' tasks for new entries
254
+ -e, --[no-]edit Open the file in the editor. Default is true
229
255
  -v, --[no-]verbose Verbose output. Default is false.
230
256
  --zsh-completion Print zsh completion setup instructions
231
257
  -p, --print [DATE] Print current entry.
232
258
  If DATE is passed, will print entry for DATE, if it exists.
233
- DATE must be in the same format as file-name-format
259
+ DATE must be in the same format as the entry header date.
260
+ -P, --post [PLATFORM] Post current entry to a chat client. Defaults to Slack.
261
+ If PLATFORM is passed, use that post adapter.
262
+ --post-channel CHANNEL Channel, room, or conversation to post to
263
+ ```
264
+
265
+ #### Posting and Secrets
266
+ The built-in Slack adapter sends the rendered markdown entry to Slack's
267
+ `chat.postMessage` API. It needs a Slack token with the `chat:write` scope and a
268
+ channel or conversation. Channel-like IDs such as `C123456`, `G123456`, and
269
+ `D123456` are the most reliable values to use. By default, the token is read
270
+ from `STANDUP_MD_SLACK_TOKEN`.
271
+
272
+ The recommended pattern is to keep secret values in the environment and store
273
+ only non-secret adapter defaults in `~/.standuprc`:
274
+
275
+ ```ruby
276
+ StandupMD.configure do |c|
277
+ c.post.configure_adapter(:slack, channel: "C123456")
278
+ end
279
+ ```
280
+
281
+ Most chat clients now prefer messages to come from an installed app or bot
282
+ instead of a long-lived user token. That keeps permissions clearer, but it also
283
+ means the visible sender might be a general name like "StandupMD" rather than
284
+ the person whose update is being posted. Set `c.post.title` to identify the
285
+ standup owner in the message title without changing the markdown files that
286
+ StandupMD parses.
287
+
288
+ ```ruby
289
+ StandupMD.configure do |c|
290
+ c.post.title = "%s - Evan Gray"
291
+ end
292
+ ```
293
+
294
+ The `%s` placeholder is replaced with the normal entry title, usually the entry
295
+ date, so a stored `# 2026-06-27` entry posts as
296
+ `# 2026-06-27 - Evan Gray`.
297
+
298
+ To use a different token variable, set `token_env`.
299
+
300
+ ```ruby
301
+ StandupMD.configure do |c|
302
+ c.post.configure_adapter(
303
+ :slack,
304
+ channel: "C123456",
305
+ token_env: "WORK_SLACK_TOKEN"
306
+ )
307
+ end
308
+ ```
309
+
310
+ #### Custom Post Adapters
311
+ Adapters are registered in `~/.standuprc`. They receive a
312
+ `StandupMD::Post::Message`, which includes the rendered markdown text and the
313
+ channel passed through `StandupMD::Post.post` or `--post-channel`.
314
+
315
+ ```ruby
316
+ class TeamsAdapter
317
+ def initialize(options = {})
318
+ @options = options
319
+ end
320
+
321
+ def post(message)
322
+ channel = message.channel || @options[:channel]
323
+ token = ENV.fetch("TEAMS_TOKEN")
324
+
325
+ # Send message.text to channel with token.
326
+
327
+ StandupMD::Post::Result.success(
328
+ adapter: message.adapter,
329
+ channel: channel
330
+ )
331
+ end
332
+ end
333
+
334
+ StandupMD.configure do |c|
335
+ c.post.register_adapter(:teams, TeamsAdapter)
336
+ c.post.configure_adapter(:teams, channel: "team-channel-id")
337
+ end
338
+ ```
339
+
340
+ Custom adapters can be used from either the CLI or the Ruby API:
341
+
342
+ ```ruby
343
+ StandupMD::Post.post(entry, adapter: :teams, channel: "team-channel-id")
344
+ ```
345
+
346
+ For request-scoped API usage, copy the global defaults and pass the copy into
347
+ the operation:
348
+
349
+ ```ruby
350
+ runtime = StandupMD.config.copy
351
+ runtime.post.default_adapter = :teams
352
+ runtime.post.configure_adapter(:teams, channel: "team-channel-id")
353
+
354
+ StandupMD::Post.post(entry, config: runtime)
234
355
  ```
235
356
 
236
357
  #### Using Existing Standup Files
@@ -250,8 +371,8 @@ they must be in a format that the parser can understand. The default is:
250
371
  ```
251
372
 
252
373
  The order, words, date format, and header level are all customizable, but the
253
- overall format must be the same. If customization is necessary, this must be
254
- done in `~/.standuprc` before execution, or else the parser will error.
374
+ overall format must be the same. If customization is necessary, set the defaults
375
+ or pass a runtime config before reading the file, or else the parser will error.
255
376
 
256
377
  For example, if you wanted the format to be as follows:
257
378
 
@@ -287,9 +408,30 @@ end
287
408
  The API is fully documented in the
288
409
  [RDoc Documentation](https://evanthegrayt.github.io/standup_md/).
289
410
 
290
- This was mainly written as a command line utility, but the API is very robust,
291
- and is available for use in your own projects. A quick example of how to write a
292
- new entry via code could look like the following:
411
+ This was mainly written as a command line utility, but the API is available for
412
+ use in your own projects. `StandupMD.config` stores application defaults. For
413
+ web requests, jobs, or any other multi-call environment, copy those defaults and
414
+ pass the copy into the operation you are running.
415
+
416
+ `StandupMD::File` handles reading and writing files on disk. The markdown parser
417
+ handles markdown strings:
418
+
419
+ ```ruby
420
+ parser = StandupMD::Parsers::Markdown.new
421
+ entries = parser.parse(File.read("2026_06.md"))
422
+ markdown = parser.render(entries, start_date: entries.first.date, end_date: entries.last.date)
423
+ ```
424
+
425
+ ```ruby
426
+ runtime = StandupMD.config.copy
427
+ runtime.file.directory = "/tmp/request-standups"
428
+ runtime.entry.current = ["Work scoped to this request"]
429
+
430
+ file = StandupMD::File.find_by_date(Date.today, config: runtime.file).load
431
+ entry = StandupMD::Entry.create(config: runtime.entry)
432
+ file.entries << entry
433
+ file.write
434
+ ```
293
435
 
294
436
  ### API Examples
295
437
  #### Adding an entry for today
@@ -297,27 +439,29 @@ new entry via code could look like the following:
297
439
  require "standup_md"
298
440
 
299
441
  StandupMD.configure do |c|
300
- c.file.current_header = "Today",
442
+ c.file.current_header = "Today"
301
443
  end
302
444
 
303
- file = StandupMD::File.find_by_date(Date.today)
304
- entry = StandupMD::Entry.create { |e| e.current = ["Stuff I will do today"] }
445
+ file = StandupMD::File.find_by_date(Date.today).load
446
+ entry = StandupMD::Entry.create(current: ["Stuff I will do today"])
305
447
  file.entries << entry
306
448
  file.write
307
449
  ```
308
450
 
309
- The above example was written as such to show how the different pieces of the
310
- API fit together. The code can actually be simplified to the following.
451
+ The above example uses global defaults. To keep runtime choices scoped to one
452
+ request, copy the defaults and pass the copy into each operation.
311
453
 
312
454
  ```ruby
313
455
  require "standup_md"
314
456
 
315
- StandupMD.configure do |c|
316
- c.file.current_header = "Today",
317
- c.entry.current = ["Stuff I will do today"]
318
- end
457
+ runtime = StandupMD.config.copy
458
+ runtime.file.current_header = "Today"
459
+ runtime.entry.current = ["Stuff I will do today"]
319
460
 
320
- StandupMD::File.find_by_date(Date.today).load.write
461
+ file = StandupMD::File.find_by_date(Date.today, config: runtime.file).load
462
+ entry = StandupMD::Entry.create(config: runtime.entry)
463
+ file.entries << entry
464
+ file.write
321
465
  ```
322
466
 
323
467
  #### Finding a past entry
@@ -357,7 +501,7 @@ command, that file will be opened. There's tab completion for this. Lastly,
357
501
  it allows for a few variables to be set for customization.
358
502
 
359
503
  ```vim
360
- g:standup_dir = $HOME . '/.cache/standup_md' " the directory where your file are
504
+ g:standup_dir = $HOME . '/.cache/standup_md' " the directory where your files are
361
505
  g:standup_file = strftime('%Y_%m.md') " the file format to use
362
506
  ```
363
507
 
@@ -4,19 +4,6 @@ _standup_array_values() {
4
4
  _message -e values 'comma-separated list'
5
5
  }
6
6
 
7
- _standup_sub_header_order() {
8
- local -a headers
9
-
10
- headers=(
11
- 'previous:previous entry tasks'
12
- 'current:current entry tasks'
13
- 'impediments:current entry impediments'
14
- 'notes:current entry notes'
15
- )
16
-
17
- _values -s , 'sub-header' $headers
18
- }
19
-
20
7
  _standup_dates() {
21
8
  local directory="${STANDUP_MD_DIR:-$HOME/.cache/standup_md}"
22
9
  local index
@@ -53,6 +40,16 @@ _standup_dates() {
53
40
  fi
54
41
  }
55
42
 
43
+ _standup_post_adapters() {
44
+ local -a adapters
45
+
46
+ adapters=(
47
+ 'slack:Slack chat.postMessage adapter'
48
+ )
49
+
50
+ _describe -t adapters 'post adapter' adapters
51
+ }
52
+
56
53
  _arguments -s -S \
57
54
  '(- *)'{-h,--help}'[show help]' \
58
55
  '(- *)--version[show version]' \
@@ -61,10 +58,6 @@ _arguments -s -S \
61
58
  '--previous[List of previous entry tasks]:tasks:_standup_array_values' \
62
59
  '--impediments[List of impediments for current entry]:impediments:_standup_array_values' \
63
60
  '--notes[List of notes for current entry]:notes:_standup_array_values' \
64
- '--sub-header-order[The order of the sub-headers when writing the file]:sub-header order:_standup_sub_header_order' \
65
- '--indent-width[Number of spaces used for each nested task level]:indent width:' \
66
- '(-f --file-name-format)-f[Date-formattable string to use for standup file name]:strftime format:' \
67
- '(-f --file-name-format)--file-name-format[Date-formattable string to use for standup file name]:strftime format:' \
68
61
  '(-E --editor)-E[Editor to use for opening standup files]:editor:_path_commands' \
69
62
  '(-E --editor)--editor[Editor to use for opening standup files]:editor:_path_commands' \
70
63
  '(-d --directory)-d[The directory where standup files are located]:directory:_directories' \
@@ -78,4 +71,6 @@ _arguments -s -S \
78
71
  '(-v --verbose --no-verbose)'{-v,--verbose}'[use verbose output]' \
79
72
  '(-v --verbose --no-verbose)--no-verbose[disable verbose output]' \
80
73
  '(-p --print)'{-p,--print}'[print current entry]::date:_standup_dates' \
74
+ '(-P --post)'{-P,--post}'[post current entry to a chat client]::platform:_standup_post_adapters' \
75
+ '--post-channel[channel, room, or conversation to post to]:channel:' \
81
76
  '1:standup file date:_standup_dates'