heroku-app-info 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 21aaa9d6492d1bb3f44800ebf2364ed78439058a145f3734ebbcd3a618b25593
4
+ data.tar.gz: d631cd3b3cbac5408d0bb13a3ce66d5ea46e1e16c50de893a5be8d58d2324415
5
+ SHA512:
6
+ metadata.gz: b5f77cdab3c2a803b84f9523ae79a50528c3e246ce7320088007fb09f4d3b4f3c6671c6123b9863910fbbddfe70d04fc4f4e983933e7aaf4406f859b993cefa9
7
+ data.tar.gz: e3dc1e1ca87a0133db37a397f1518c9f88337b640d80e5ac4ca21084e7f2a909703c2760989424b494cdc27d3bbe15af3d20aad1aa20c260b94123937bb903d2
data/.dockerignore ADDED
@@ -0,0 +1,12 @@
1
+ **/.DS_Store
2
+ **/*~
3
+ **/*.bak
4
+ **/*.pid
5
+ **/.*
6
+ **/_yardoc/
7
+ **/coverage/
8
+ **/doc/
9
+ **/pkg/
10
+ **/spec
11
+ **/tmp/
12
+ **/example/
data/.editorconfig ADDED
@@ -0,0 +1,5 @@
1
+ [*]
2
+ indent_size = 2
3
+ indent_style = space
4
+ insert_final_newline = true
5
+ trim_trailing_whitespace = true
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ require:
2
+ - standard
3
+
4
+ inherit_gem:
5
+ standard: config/base.yml
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
3
+ ruby_version: 2.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-06-27
4
+
5
+ - Initial release /w Heroku CLI
data/Dockerfile ADDED
@@ -0,0 +1,16 @@
1
+ ARG RUBY_VERSION
2
+ FROM ruby:$RUBY_VERSION-slim
3
+
4
+ VOLUME /workspace
5
+ WORKDIR /workspace
6
+
7
+ ARG PKG_VERSION
8
+ ARG GEM_HOST
9
+
10
+ RUN apt-get update && apt-get install -y curl gnupg2 && \
11
+ curl https://cli-assets.heroku.com/install-ubuntu.sh | sh && \
12
+ curl -O http://host.docker.internal/heroku-app-info-$PKG_VERSION.gem && \
13
+ gem install heroku-app-info-$PKG_VERSION.gem && \
14
+ rm heroku-app-info-$PKG_VERSION.gem
15
+
16
+ ENTRYPOINT ["heroku-app-info"]
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in heroku-system-info.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
11
+ gem "minitest-power_assert", "~> 0.3.1"
12
+ gem "minitest-reporters", "~> 1.7"
13
+
14
+ gem "solargraph", "~> 0.50.0"
15
+
16
+ gem "standard", "~> 1.3"
17
+
18
+ gem "webrick", "~> 1.8"
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ Copyright 2024 wtnabe
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
+
5
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
+
7
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
+
9
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # HerokuAppInfo
2
+
3
+ Provides the ability to list Heroku apps that can be accessed by your account, to retrieve detailed information about each app, database version, and other information, and to store this as JSON.
4
+
5
+ It is primarily intended to be used for creating maintenance plans for Stack and others.
6
+
7
+ ## Installation
8
+
9
+ Install the gem and add to the application's Gemfile by executing:
10
+
11
+ $ bundle add heroku-app-info
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ $ gem install heroku-app-info
16
+
17
+ And, at the moment it depends on the Heroku CLI.
18
+
19
+ [The Heroku CLI \| Heroku Dev Center](https://devcenter.heroku.com/articles/heroku-cli)
20
+
21
+ ## Usage
22
+
23
+ ```
24
+ $ heroku-app-info -h
25
+ ```
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/wtnabe/heroku-app-info.
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+ require "yard"
6
+
7
+ Rake::TestTask.new(:spec) do |t|
8
+ t.libs << "spec"
9
+ t.libs << "lib"
10
+ t.test_files = FileList["spec/**/*_spec.rb"]
11
+ end
12
+
13
+ require "standard/rake"
14
+
15
+ YARD::Rake::YardocTask.new do |t|
16
+ t.files = [
17
+ "exe/*",
18
+ "lib/**/*.rb"
19
+ ]
20
+ end
21
+
22
+ task default: %i[spec standard]
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require_relative "../lib/heroku_app_info"
4
+
5
+ module HerokuAppInfo
6
+ def main
7
+ Cli.new(root: File.dirname(__dir__)).execute
8
+ end
9
+ module_function :main
10
+ end
11
+
12
+ HerokuAppInfo.main
@@ -0,0 +1 @@
1
+ require_relative "heroku_app_info"
@@ -0,0 +1,77 @@
1
+ require_relative "fetcher"
2
+ require_relative "options"
3
+ require_relative "parser"
4
+ require_relative "writer"
5
+
6
+ module HerokuAppInfo
7
+ class Cli
8
+ def initialize(root:)
9
+ @root = root
10
+ end
11
+ attr_reader :root
12
+
13
+ def execute
14
+ @options = Options.new(ARGV.dup)
15
+
16
+ set_authz! if @options.authz
17
+
18
+ @fetcher = Fetcher.new(@options)
19
+ @parser = Parser.new
20
+ if @options.show_all?
21
+ show_all_apps
22
+ else
23
+ apps =
24
+ if @options.apps.nil?
25
+ @parser.apps(@fetcher.all_apps)
26
+ else
27
+ @options.apps
28
+ end
29
+ dump_app_and_pg_details(apps)
30
+ end
31
+ end
32
+
33
+ #
34
+ # set API KEY to Environment Variable
35
+ #
36
+ # Currently, Heroku Account require MFA, so the login and password method is not supported
37
+ #
38
+ def set_authz!
39
+ ENV["HEROKU_API_KEY"] = @options.authz
40
+ end
41
+
42
+ #
43
+ # show all apps
44
+ #
45
+ def show_all_apps
46
+ puts @fetcher.all_apps
47
+ end
48
+
49
+ #
50
+ # dump each app and pg info
51
+ #
52
+ def dump_app_and_pg_details(apps)
53
+ writer = Writer.new(@options)
54
+
55
+ apps.each { |app|
56
+ raw_app = @fetcher.app(app)
57
+ writer.write(app, raw_app) if @options.raw_output?
58
+ app_info = @parser.parse(raw_app)
59
+ if has_pg?(app_info)
60
+ raw_db = @fetcher.pg(app)
61
+ writer.write(app, raw_db, sort: "pg") if @options.raw_output?
62
+ db_info = @parser.parse(raw_db)
63
+ writer.write(app, db_info, sort: "pg")
64
+ end
65
+ writer.write(app, app_info)
66
+ }
67
+ end
68
+
69
+ #
70
+ # @param [Hash] app_info
71
+ # @return [Boolean]
72
+ #
73
+ def has_pg?(app_info)
74
+ app_info.has_key?("Addons") && app_info["Addons"].grep(/postgresql/).size > 0
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,22 @@
1
+ module HerokuAppInfo
2
+ class ContentResolver
3
+ #
4
+ # @param [String] name
5
+ # @param [String, Hash] info
6
+ # @param [String, nil] sort
7
+ # @return [Array] filename, content
8
+ #
9
+ def self.resolve(name, info, sort: nil)
10
+ ext = info.is_a?(String) ? ".txt" : ".json"
11
+
12
+ [
13
+ [name, sort].compact.join("_") + ext,
14
+ if info.is_a?(String)
15
+ info
16
+ else
17
+ JSON.generate(info)
18
+ end
19
+ ]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,79 @@
1
+ require_relative "content_resolver"
2
+ require "tty-which"
3
+
4
+ #
5
+ # require Heroku-cli
6
+ #
7
+
8
+ module HerokuAppInfo
9
+ class Fetcher
10
+ #
11
+ # @param [Options] options
12
+ #
13
+ def initialize(options = nil)
14
+ @options = options
15
+ raise HerokuCliNotFound if TTY::Which.which("heroku").nil?
16
+ end
17
+
18
+ #
19
+ # @param [String] app
20
+ # @return [String]
21
+ #
22
+ def app(app)
23
+ if (path = cache_file(app: app))
24
+ File.read(path)
25
+ else
26
+ `heroku apps:info #{app}`
27
+ end
28
+ end
29
+
30
+ #
31
+ # @param [String] app
32
+ # @return [String]
33
+ #
34
+ def pg(app)
35
+ if (path = cache_file(app: app, sort: "pg"))
36
+ File.read(path)
37
+ else
38
+ `heroku pg -a #{app}`
39
+ end
40
+ end
41
+
42
+ #
43
+ # @param [String] app
44
+ # @return [String]
45
+ #
46
+ def addons(app)
47
+ if (path = cache_file(app: app))
48
+ File.read(path)
49
+ else
50
+ `heroku addons -a #{app}`
51
+ end
52
+ end
53
+
54
+ #
55
+ # @return [String]
56
+ #
57
+ def all_apps
58
+ if (path = cache_file)
59
+ File.read(path)
60
+ else
61
+ `heroku apps -A`
62
+ end
63
+ end
64
+
65
+ #
66
+ # @param [String] app
67
+ # @param [String] sort
68
+ # @return [String, false]
69
+ #
70
+ def cache_file(app: nil, sort: nil)
71
+ return false if @options.no_cache?
72
+
73
+ filename, = ContentResolver.resolve(app || "apps", "", sort: sort)
74
+ path = File.join(@options.out_dir, filename)
75
+
76
+ (File.exist?(path) && File.readable?(path)) ? path : false
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,76 @@
1
+ require "optparse"
2
+
3
+ module HerokuAppInfo
4
+ class Options
5
+ #
6
+ # @param [Array] argv
7
+ #
8
+ def initialize(argv)
9
+ defaults!
10
+ @parser = cli_parser
11
+ @rest = @parser.parse(argv)
12
+ end
13
+ attr_reader :apps, :out_dir, :authz, :rest
14
+
15
+ #
16
+ # @return [Boolean]
17
+ #
18
+ def show_all?
19
+ @show_all
20
+ end
21
+
22
+ #
23
+ # @return [Boolean]
24
+ #
25
+ def no_cache?
26
+ @no_cache
27
+ end
28
+
29
+ def raw_output?
30
+ @raw_output
31
+ end
32
+
33
+ def defaults!
34
+ @out_dir = Dir.pwd
35
+ end
36
+
37
+ def help
38
+ @parser.help
39
+ end
40
+
41
+ #
42
+ # @return [OptionParser]
43
+ #
44
+ def cli_parser
45
+ OptionParser.new do |opt|
46
+ opt.on("-a", "--app-list APP,APP,APP") do |apps|
47
+ @apps = apps.split(",")
48
+ end
49
+ opt.on("--app-list-from FILE") do |file|
50
+ if File.exist?(file) && File.readable?(file)
51
+ @apps = File.read(file).lines(chomp: true)
52
+ end
53
+ end
54
+ opt.on("-n", "--no-cache") do
55
+ @no_cache = true
56
+ end
57
+ opt.on("-o", "--out-dir [DIR]") do |dir|
58
+ if File.exist?(dir) && File.directory?(dir) && File.writable?(dir)
59
+ @out_dir = dir
60
+ end
61
+ end
62
+ opt.on("-r", "--raw-output") do
63
+ @raw_output = true
64
+ end
65
+ opt.on("-s", "--show-all-apps", "not include detail") do
66
+ @show_all = true
67
+ end
68
+ opt.on("-z", "--authorizarion AUTH") do |auth|
69
+ @authz = auth
70
+ end
71
+
72
+ opt.version = VERSION
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,108 @@
1
+ require "yaml"
2
+
3
+ module HerokuAppInfo
4
+ class Parser
5
+ #
6
+ # simple app list
7
+ #
8
+ # @param [String] apps
9
+ # @return [Array]
10
+ #
11
+ def apps(apps)
12
+ apps.lines(chomp: true).grep_v(/\A===/).map { |e|
13
+ /\A([^ ]+)/ =~ e
14
+ $1
15
+ }.compact
16
+ end
17
+
18
+ #
19
+ # @param [String] text
20
+ # @return [Hash]
21
+ #
22
+ def parse(text)
23
+ info = {}
24
+
25
+ last_item = nil
26
+ title = nil
27
+ text.lines(chomp: true).each { |line|
28
+ next if line.size < 1
29
+
30
+ data = parse_line(line.rstrip)
31
+
32
+ case data[:item]
33
+ when :continuation
34
+ info[last_item] << data[:content]
35
+ when :title
36
+ title = data[:content]
37
+ else
38
+ multi_item = app_multi_items.select { |item, type| item == data[:item] }
39
+ if multi_item.size > 0
40
+ case multi_item.values.first.to_s
41
+ when "Array"
42
+ info[data[:item]] = [data[:content]]
43
+ when "Hash"
44
+ info[data[:item]] = parse_hash_item(data[:content])
45
+ end
46
+ else
47
+ info[data[:item]] = data[:content]
48
+ end
49
+ end
50
+ last_item = data[:item] unless data[:item] == :continuation
51
+ }
52
+
53
+ info
54
+ end
55
+
56
+ #
57
+ # @param [String] line
58
+ # @return [Hash]
59
+ #
60
+ def parse_line(line)
61
+ case line
62
+ # title line
63
+ when /\A=== (.+)\z/
64
+ {
65
+ item: :title,
66
+ content: $1
67
+ }
68
+ # data line
69
+ when /\A(?:([A-Z][^:]+):)?(?: +(.+))?\z/
70
+ if !$1 && $2 # continuation
71
+ {
72
+ item: :continuation,
73
+ content: $2
74
+ }
75
+ else # normal
76
+ {
77
+ item: $1,
78
+ content: $2
79
+ }
80
+ end
81
+ end
82
+ end
83
+
84
+ #
85
+ # @param [String, nil] item
86
+ # @return [Hash]
87
+ #
88
+ def parse_hash_item(item)
89
+ if item.nil?
90
+ {}
91
+ else
92
+
93
+ YAML.safe_load(item.split(",").map(&:strip).join("\n"))
94
+ end
95
+ end
96
+
97
+ #
98
+ # @return [Hash]
99
+ #
100
+ def app_multi_items
101
+ {
102
+ "Addons" => Array,
103
+ "Collaborators" => Array,
104
+ "Dynos" => Hash
105
+ }
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HerokuAppInfo
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,25 @@
1
+ require_relative "content_resolver"
2
+ require "json"
3
+
4
+ module HerokuAppInfo
5
+ class Writer
6
+ #
7
+ # @param [Options] options
8
+ #
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ #
14
+ # @param [String] app
15
+ # @param [String, Hash] info
16
+ # @param [String, nil] sort
17
+ # @return [Integer]
18
+ #
19
+ def write(app, info, sort: nil)
20
+ filename, content = ContentResolver.resolve(app, info, sort: sort)
21
+
22
+ File.write(File.join(@options.out_dir, filename), content)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ module HerokuAppInfo
2
+ class Error < ::StandardError; end
3
+ class HerokuCliNotFound < Error; end # standard:disable Layout/EmptyLineBetweenDefs
4
+ end
5
+
6
+ Dir.glob("#{File.dirname(__FILE__)}/heroku_app_info/**/*.rb").sort.each { |f| require f }
@@ -0,0 +1,4 @@
1
+ module HerokuAppInfo
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heroku-app-info
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - wtnabe
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-06-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: tty-which
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.5.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.5.0
27
+ description: store Application Stack, PotgsreSQL version, etc. mainly for upgrade
28
+ management.
29
+ email:
30
+ - 18510+wtnabe@users.noreply.github.com
31
+ executables:
32
+ - heroku-app-info
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".dockerignore"
37
+ - ".editorconfig"
38
+ - ".rubocop.yml"
39
+ - ".standard.yml"
40
+ - CHANGELOG.md
41
+ - Dockerfile
42
+ - Gemfile
43
+ - LICENSE
44
+ - README.md
45
+ - Rakefile
46
+ - exe/heroku-app-info
47
+ - lib/heroku-app-info.rb
48
+ - lib/heroku_app_info.rb
49
+ - lib/heroku_app_info/cli.rb
50
+ - lib/heroku_app_info/content_resolver.rb
51
+ - lib/heroku_app_info/fetcher.rb
52
+ - lib/heroku_app_info/options.rb
53
+ - lib/heroku_app_info/parser.rb
54
+ - lib/heroku_app_info/version.rb
55
+ - lib/heroku_app_info/writer.rb
56
+ - sig/heroku_app_info/version.rbs
57
+ homepage: https://github.com/wtnabe/heroku-app-info
58
+ licenses: []
59
+ metadata:
60
+ allowed_push_host: https://rubygems.org
61
+ homepage_uri: https://github.com/wtnabe/heroku-app-info
62
+ source_code_uri: https://github.com/wtnabe/heroku-app-info
63
+ changelog_uri: https://github.com/wtnabe/heroku-app-info/blob/main/CHANGELOG.md
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.6.0
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.4.10
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Gather and Store Heroku App infos.
83
+ test_files: []