discharger 0.3.3 → 0.3.5

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: 22ff9b0cc383ea213a6a8766f713ef4afa97a114c679c76b37954f73872b74e3
4
- data.tar.gz: a3cfa43c5c9665704e13bce7a3bb19e36ccd4810b700b149a2646815fcca9f6a
3
+ metadata.gz: 97070aca58c88a6f43e34f7d45bec964122a6db867e9bd5333988a082bb41d47
4
+ data.tar.gz: 0f39ea2c1136914a33666593af2bdaeed6f358a34364f9ffcb9b1557976c3fae
5
5
  SHA512:
6
- metadata.gz: 0e01ea39ed9eba5c973951784d44b5488a1f6f00effc59a1192d5f1f3cfd2aafbe14895a1d6ce920520acb7c501bc79720febc7360bed35a301c742ab30728ad
7
- data.tar.gz: 309f1fd0b7ea3d78814aedd8c8bb945e0c63806260007dc8391126863254d4f19d87adc0e9ec7cdd584c3173982fd69465e87c06a13a97f90870aa60d93b2f6d
6
+ metadata.gz: 617ed8ed6727d1e1817e7859ce8121553399bd1ec0c5c70b2002d93eb0273915e112d5b30d6ba2e045e18ff0b4fd1bf075cc78d0000f0b8abdc360102d14cb0c
7
+ data.tar.gz: d8470a667ff24259e5efc590ad63fb7be3884e31ce3e35a5e3b8d22fdb3a399fe4948b3e8e465d2b4aee34e24e2ef25fce86453a041083abb6947b6b3454d7f7
data/CHANGELOG.md CHANGED
@@ -5,19 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
- ## [0.3.3] - 2026-07-22
8
+ ## [0.3.5] - 2026-08-10
9
9
 
10
10
  ### Added
11
11
 
12
- - Post-release runbook announcement in the Slack release thread, from Reissue's runbook_file (0cf9620)
12
+ - github_packages setup step storing bundler credentials for a private GitHub Packages gem source via the gh CLI (2cff4b6)
13
13
 
14
- ## [0.3.2] - 2026-07-15
14
+ ## [0.3.4] - 2026-07-22
15
15
 
16
- ### Changed
17
-
18
- - Treat generated pg_tools setup as opt-in for apps that use structure.sql or PostgreSQL CLI workflows.
19
-
20
- ### Fixed
16
+ ### Added
21
17
 
22
- - Retry `brew bundle` once before failing setup, so transient Homebrew lock contention (e.g. two `bin/setup` runs on the same host) doesn't hard-fail the whole run.
23
- - pg-tools wrappers no longer forward localhost -h/-p flags into docker exec, where the host-mapped port doesn't exist; remote hosts still pass through untouched (a3869d2)
18
+ - Runbook announcement on stage builds, previewing post-release steps before production (c77b552)
data/README.md CHANGED
@@ -112,8 +112,10 @@ Runbook: Run \`rake data:cleanup\` after deploy"
112
112
  ```
113
113
 
114
114
  `rake release:prepare` finalizes the runbook alongside the changelog, so the release tag
115
- records exactly which steps that version needs. After the production release, Discharger
116
- posts the checklist to Slack as a reply in the release thread, right after the changelog:
115
+ records exactly which steps that version needs. Discharger posts the checklist to Slack as
116
+ a reply in the deploy thread both on the stage build (`rake release:build` /
117
+ `rake release:stage`, threaded under "Building…") and after the production release
118
+ (threaded under the release announcement, right after the changelog):
117
119
 
118
120
  ```
119
121
  *Post-release runbook for 1.2.3* — 2 steps
@@ -122,6 +124,9 @@ posts the checklist to Slack as a reply in the release thread, right after the c
122
124
  • Re-index search documents (def5678)
123
125
  ```
124
126
 
127
+ Staging shows the steps as a preview so the team sees what production will need before it
128
+ happens; the production thread repeats them when the release actually lands.
129
+
125
130
  When a release needs no follow-up, the thread says so explicitly rather than staying
126
131
  silent, so nobody has to wonder whether the check was skipped:
127
132
 
@@ -269,6 +274,7 @@ The `steps` array specifies which built-in setup commands to run. Available comm
269
274
  - `brew` - Install Homebrew dependencies
270
275
  - `asdf` - Setup version management with asdf
271
276
  - `git` - Configure git settings
277
+ - `github_packages` - Store bundler credentials for a private GitHub Packages gem source using the GitHub CLI (run before `bundler`; requires a `github_packages.source` config entry)
272
278
  - `bundler` - Install Ruby gems
273
279
  - `yarn` - Install JavaScript packages
274
280
  - `config` - Copy configuration files
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_command"
4
+
5
+ module Discharger
6
+ module SetupRunner
7
+ module Commands
8
+ # Stores bundler credentials for a private GitHub Packages gem source
9
+ # using the GitHub CLI, so Gemfiles don't need embedded tokens.
10
+ # Credentials are written to the app's .bundle/config — keep .bundle
11
+ # gitignored. Run this step before "bundler" in setup.yml.
12
+ class GithubPackagesCommand < BaseCommand
13
+ def execute
14
+ unless gh_installed?
15
+ log "GitHub CLI (gh) not found; skipping. Install gh and rerun setup or `bundle install` may fail for #{source}"
16
+ return
17
+ end
18
+
19
+ unless authenticated? || login
20
+ log "Not logged in to GitHub; skipping. Run `gh auth login` and rerun setup."
21
+ return
22
+ end
23
+
24
+ username = gh("api", "user", "--jq", ".login")
25
+ token = gh("auth", "token")
26
+ if username.to_s.empty? || token.to_s.empty?
27
+ log "Could not read GitHub credentials from gh; skipping."
28
+ return
29
+ end
30
+
31
+ store_bundler_credentials(username, token)
32
+ if credentials_valid?(username, token)
33
+ log "Configured bundler credentials for #{source}"
34
+ else
35
+ log "#{source} rejected the stored credentials. Your gh token may lack " \
36
+ "the read:packages scope — run `gh auth refresh -s read:packages` and rerun setup."
37
+ end
38
+ end
39
+
40
+ def can_execute?
41
+ !source.to_s.empty?
42
+ end
43
+
44
+ def description
45
+ "Configure GitHub Packages credentials"
46
+ end
47
+
48
+ protected
49
+
50
+ def source
51
+ config.github_packages&.source if config.respond_to?(:github_packages)
52
+ end
53
+
54
+ def gh_installed?
55
+ system_quiet("which gh")
56
+ end
57
+
58
+ def authenticated?
59
+ system_quiet("gh auth status")
60
+ end
61
+
62
+ def login
63
+ return false unless $stdin.tty?
64
+ log "Logging in to GitHub..."
65
+ system("gh", "auth", "login")
66
+ authenticated?
67
+ end
68
+
69
+ def gh(*args)
70
+ require "open3"
71
+ stdout, _stderr, status = Open3.capture3("gh", *args)
72
+ status.success? ? stdout.chomp : nil
73
+ end
74
+
75
+ # Runs bundle config directly instead of through system! so the
76
+ # token never reaches the spinner display or the debug log.
77
+ def store_bundler_credentials(username, token)
78
+ require "open3"
79
+ _stdout, stderr, status = Open3.capture3(
80
+ "bundle", "config", "set", "--local", source, "#{username}:#{token}"
81
+ )
82
+ raise "bundle config set --local #{source} failed: #{stderr}" unless status.success?
83
+ end
84
+
85
+ # Probes the source's compact index with the stored credentials.
86
+ # Only a definite 401/403 counts as invalid — network trouble must
87
+ # not fail setup over an unverifiable token.
88
+ def credentials_valid?(username, token)
89
+ require "net/http"
90
+ require "uri"
91
+ uri = URI.join("#{source.chomp("/")}/", "versions")
92
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https",
93
+ open_timeout: 5, read_timeout: 10) do |http|
94
+ request = Net::HTTP::Get.new(uri)
95
+ request.basic_auth(username, token)
96
+ http.request(request)
97
+ end
98
+ !%w[401 403].include?(response.code)
99
+ rescue => e
100
+ logger&.debug("Could not verify GitHub Packages credentials: #{e.message}")
101
+ true
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -5,12 +5,13 @@ require "yaml"
5
5
  module Discharger
6
6
  module SetupRunner
7
7
  class Configuration
8
- attr_accessor :app_name, :database, :redis, :services, :steps, :custom_steps, :pre_steps
8
+ attr_accessor :app_name, :database, :redis, :github_packages, :services, :steps, :custom_steps, :pre_steps
9
9
 
10
10
  def initialize
11
11
  @app_name = "Application"
12
12
  @database = DatabaseConfig.new
13
13
  @redis = nil
14
+ @github_packages = nil
14
15
  @services = []
15
16
  @steps = []
16
17
  @custom_steps = []
@@ -27,6 +28,7 @@ module Discharger
27
28
  config.app_name = yaml["app_name"] if yaml["app_name"]
28
29
  config.database.from_hash(yaml["database"]) if yaml["database"]
29
30
  config.redis = RedisConfig.new.tap { |r| r.from_hash(yaml["redis"]) } if yaml["redis"]
31
+ config.github_packages = GithubPackagesConfig.new.tap { |g| g.from_hash(yaml["github_packages"]) } if yaml["github_packages"]
30
32
  config.services = yaml["services"] || []
31
33
  config.steps = yaml["steps"] || []
32
34
  config.custom_steps = yaml["custom_steps"] || []
@@ -56,6 +58,18 @@ module Discharger
56
58
  end
57
59
  end
58
60
 
61
+ class GithubPackagesConfig
62
+ attr_accessor :source
63
+
64
+ def initialize
65
+ @source = nil
66
+ end
67
+
68
+ def from_hash(hash)
69
+ @source = hash["source"] if hash["source"]
70
+ end
71
+ end
72
+
59
73
  class RedisConfig
60
74
  attr_accessor :port, :name, :version
61
75
 
@@ -201,6 +201,19 @@ module Discharger
201
201
  items.map { |item| "• #{item}" }.join("\n")
202
202
  end
203
203
 
204
+ # Post the runbook as a reply in a Slack thread, when a runbook is
205
+ # configured. Reuses the same message on the stage build and the
206
+ # production release. Returns the message posted, or nil when there is
207
+ # no thread to reply to or no runbook to announce.
208
+ def post_runbook_to_thread(version, thread_ts)
209
+ return unless thread_ts.present?
210
+ return unless (message = runbook_announcement(version))
211
+
212
+ tasker["#{name}:slack"].reenable
213
+ tasker["#{name}:slack"].invoke(message, release_message_channel, ":clipboard:", thread_ts)
214
+ message
215
+ end
216
+
204
217
  def existing_pr_number(base, head)
205
218
  stdout, _, status = Open3.capture3(
206
219
  "gh", "pr", "list",
@@ -315,10 +328,7 @@ module Discharger
315
328
  tasker["#{name}:slack"].reenable
316
329
  tasker["#{name}:slack"].invoke(text, release_message_channel, ":log:", thread_ts)
317
330
 
318
- if (runbook_message = runbook_announcement(current_version))
319
- tasker["#{name}:slack"].reenable
320
- tasker["#{name}:slack"].invoke(runbook_message, release_message_channel, ":clipboard:", thread_ts)
321
- end
331
+ post_runbook_to_thread(current_version, thread_ts)
322
332
  end
323
333
  # Signal success — no branch switch needed since we stay on working_branch throughout
324
334
  true
@@ -385,6 +395,7 @@ module Discharger
385
395
  ) do
386
396
  current_version = Object.const_get(version_constant)
387
397
  tasker["#{name}:slack"].invoke("Building #{app_name} #{current_version} (#{commit_identifier.call}) on #{staging_branch}.", release_message_channel)
398
+ post_runbook_to_thread(current_version, last_message_ts)
388
399
  syscall ["git checkout #{build_branch}"]
389
400
  end
390
401
  end
@@ -1,3 +1,3 @@
1
1
  module Discharger
2
- VERSION = "0.3.3"
2
+ VERSION = "0.3.5"
3
3
  end
@@ -18,6 +18,14 @@ redis:
18
18
  name: "redis-your-app"
19
19
  version: "latest"
20
20
 
21
+ # Private GitHub Packages gem source (optional)
22
+ # Stores bundler credentials from the GitHub CLI (gh) so the Gemfile
23
+ # doesn't need an embedded token. Add "github_packages" to steps, before
24
+ # "bundler". Credentials are written to .bundle/config — keep .bundle
25
+ # gitignored.
26
+ # github_packages:
27
+ # source: "https://rubygems.pkg.github.com/your-org"
28
+
21
29
  # Pre-Rails steps (run BEFORE Rails loads)
22
30
  # These are for system dependencies and environment setup that must happen
23
31
  # before bundler/Rails can initialize. Use sparingly.
@@ -36,7 +44,7 @@ pre_steps: []
36
44
  # - postgresql_tools # Optional: for apps that use structure.sql or pg_dump/psql directly
37
45
 
38
46
  # Built-in commands to run (empty array runs all available commands)
39
- # Available commands: brew, asdf, git, bundler, yarn, config, docker, pg_tools, env, database
47
+ # Available commands: brew, asdf, git, github_packages, bundler, yarn, config, docker, pg_tools, env, database
40
48
  steps:
41
49
  - brew
42
50
  - asdf
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: discharger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Gay
@@ -122,6 +122,7 @@ files:
122
122
  - lib/discharger/setup_runner/commands/docker_command.rb
123
123
  - lib/discharger/setup_runner/commands/env_command.rb
124
124
  - lib/discharger/setup_runner/commands/git_command.rb
125
+ - lib/discharger/setup_runner/commands/github_packages_command.rb
125
126
  - lib/discharger/setup_runner/commands/pg_tools_command.rb
126
127
  - lib/discharger/setup_runner/commands/yarn_command.rb
127
128
  - lib/discharger/setup_runner/condition_evaluator.rb