├── .bowerrc ├── .dockerignore ├── .gitignore ├── .versioneer.yml ├── CHANGELOG.md ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ ├── .keep │ │ ├── feature-git.jpg │ │ ├── feature-groups.jpg │ │ └── feature-webhooks.jpg │ ├── javascripts │ │ ├── application.js │ │ ├── catalog.js │ │ ├── dashboard.js │ │ ├── help.js │ │ ├── log.js │ │ └── noty.js │ └── stylesheets │ │ ├── application.css │ │ ├── catalog.scss │ │ ├── dashboard.scss │ │ ├── help.scss │ │ ├── includes.scss │ │ ├── log.scss │ │ └── styles.scss ├── controllers │ ├── application_controller.rb │ ├── catalog_controller.rb │ ├── concerns │ │ └── .keep │ ├── dashboard_controller.rb │ ├── group_controller.rb │ ├── help_controller.rb │ └── log_controller.rb ├── helpers │ ├── application_helper.rb │ ├── catalog_helper.rb │ ├── dashboard_helper.rb │ └── log_helper.rb ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── application_record.rb │ ├── catalog │ │ ├── git_repo.rb │ │ ├── node_package.rb │ │ ├── oracle_java.rb │ │ ├── ruby_gem.rb │ │ ├── web_diff.rb │ │ ├── web_scraper.rb │ │ └── yarn_package.rb │ ├── catalog_entry.rb │ ├── catalog_log_entry.rb │ ├── catalog_webhook.rb │ ├── concerns │ │ └── .keep │ ├── group.rb │ └── instance.rb └── views │ ├── catalog │ ├── index.html.erb │ └── view.html.erb │ ├── group │ └── index.erb │ ├── help │ ├── api.html.erb │ └── index.html.erb │ ├── layouts │ ├── application.html.erb │ └── rails_admin │ │ ├── _navigation.html.haml │ │ └── pjax.html.haml │ ├── log │ ├── index.html.erb │ └── index.rss.builder │ ├── rails_admin │ └── main │ │ ├── refresh_catalog.html.erb │ │ ├── refresh_entry.html.erb │ │ ├── reload_defaults.html.erb │ │ └── trigger_webhook.erb │ └── shared │ ├── _demo_jumbo.erb │ ├── _entry_badge_embed.erb │ ├── _entry_links.erb │ └── _entry_version_history.erb ├── bin ├── bundle ├── rails ├── rake ├── setup ├── spring ├── update └── versioneer ├── bower.json ├── config.ru ├── config ├── application.rb ├── boot.rb ├── brakeman.ignore ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── config.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── rails_admin.rb │ ├── scheduler.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── secrets.yml ├── settings.yml ├── settings │ ├── development.yml │ ├── production.yml │ └── test.yml ├── spring.rb └── version.rb ├── data └── .keep ├── db ├── migrate │ ├── .keep │ ├── 20161122073159_create_catalog_webhooks.rb │ ├── 20161122210533_create_groups.rb │ ├── 20161126024305_add_no_log_to_catalog_entries.rb │ ├── 20161126045910_add_hidden_to_catalog_entries.rb │ ├── 20170114014452_add_command_option_to_catalog_webhooks.rb │ └── 20170123064208_add_description_to_catalog_entries.rb ├── schema.rb └── seeds.rb ├── docker-compose.yml ├── lib ├── assets │ └── .keep ├── rails_admin │ ├── refresh_catalog.rb │ ├── refresh_entry.rb │ ├── reload_defaults.rb │ └── trigger_webhook.rb └── tasks │ ├── .keep │ ├── auto_annotate_models.rake │ ├── catalog.rake │ ├── release.rake │ └── webhooks.rake ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── rubygem ├── .gitignore ├── .travis.yml ├── .versioneer.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin │ ├── console │ ├── setup │ └── versioneer ├── exe │ └── latestver ├── latestver.gemspec ├── lib │ ├── latestver.rb │ └── latestver │ │ ├── cli_helpers.rb │ │ ├── client.rb │ │ ├── client_error.rb │ │ └── version.rb └── test │ ├── latestver_test.rb │ └── test_helper.rb ├── test ├── controllers │ ├── .keep │ ├── catalog_controller_test.rb │ ├── help_controller_test.rb │ └── log_controller_test.rb ├── fixtures │ ├── .keep │ ├── catalog_entries.yml │ ├── catalog_log_entries.yml │ ├── catalog_webhooks.yml │ ├── external_links.yml │ ├── groups.yml │ └── instances.yml ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── catalog_entry_test.rb │ ├── catalog_log_entry_test.rb │ ├── catalog_webhook_test.rb │ ├── external_link_test.rb │ ├── group_test.rb │ └── instance_test.rb └── test_helper.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.bowerrc: -------------------------------------------------------------------------------- 1 | {"directory": "vendor/assets/bower_components"} 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | tmp 3 | test 4 | node_modules 5 | bower_components 6 | rubygem 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | *.sqlite3 12 | *.sqlite3-journal 13 | 14 | # Ignore logfiles, tempfiles, and generated docs. 15 | /log/* 16 | !/log/.keep 17 | /tmp 18 | /.yardoc 19 | /doc 20 | 21 | # Ignore local settings files. 22 | config/settings.local.yml 23 | config/settings/*.local.yml 24 | config/environments/*.local.yml 25 | 26 | # Ignore local app data. 27 | /data 28 | /version.lock 29 | /vendor/assets/bower_components 30 | -------------------------------------------------------------------------------- /.versioneer.yml: -------------------------------------------------------------------------------- 1 | # Versioneer Project Configuration 2 | # https://git.io/versioneer-help 3 | 4 | type: Git 5 | bump_segment: minor 6 | prereleases: 7 | - alpha 8 | - beta 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.7 (2017-01-22) 2 | 3 | * __`NEW`__ Add support for dependency badges. 4 | * __`NEW`__ Add variable substitution to external links. 5 | * __`NEW`__ Add ability to test webhooks. 6 | * __`NEW`__ Add support for custom curl/command webhooks. 7 | * __`~~~`__ Improve navigation and add help text. 8 | * __`~~~`__ API documentation improvements. 9 | * __`!!!`__ Fix OracleJava entries >=JDK8. (Add support for download hashes.) 10 | * __`!!!`__ Fix default entry - GitLab WebScraper. 11 | * __`!!!`__ Fix routing for special chars and template substitutions. 12 | 13 | 14 | ### 1.6 (2016-12-27) 15 | 16 | * __`NEW`__ Add internal version tracking and changelog. 17 | * __`!!!`__ Fix Java catalog entries for archived versions. (<= JDK7) 18 | * __`!!!`__ Fix handling of dereference markers in Git tags. 19 | 20 | 21 | ### 1.5 (2016-12-08) 22 | 23 | * __`NEW`__ Allow "latest" version shortcut when creating instances. 24 | * __`NEW`__ Cache-control options. 25 | * __`NEW`__ Add entry links to Catalog Update Log. 26 | 27 | 28 | ### 1.4 (2016-11-25) 29 | 30 | * __`NEW`__ Add option to exclude Catalog Entries from update log. 31 | * __`NEW`__ Add option to hide Catalog Entries from main listing. 32 | * __`NEW`__ Add more parsing options to WebScraper. 33 | * __`NEW`__ Allow referencing other catalog versions in WebScraper urls. 34 | * __`~~~`__ Improve instance sorting. 35 | 36 | 37 | ### 1.3 (2016-11-23) 38 | 39 | * __`NEW`__ Add clone function to Catalog Entries. 40 | * __`NEW`__ Add substitutions to WebScraper urls. 41 | 42 | 43 | ### 1.2 (2016-11-22) 44 | 45 | * __`NEW`__ Add support for triggering webhooks on catalog changes. 46 | * __`NEW`__ Add API support for path querying. 47 | * __`NEW`__ Add Gradle to default WebScrapers. 48 | * __`NEW`__ Add REFRESH_ENABLED check to CatalogEntry. 49 | * __`~~~`__ Improve group management. 50 | * __`~~~`__ Improve admin default sorting. 51 | * __`~~~`__ Improvements to autorefresh. 52 | * __`!!!`__ Bugfix for Catalog::WebScraper 53 | 54 | 55 | ### 1.1 (2016-11-21) 56 | 57 | * __`NEW`__ Add ability to refresh single catalog entry. 58 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.5 2 | MAINTAINER BinaryBabel OSS 3 | 4 | RUN curl -sL https://deb.nodesource.com/setup_11.x | bash - \ 5 | && apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs \ 6 | && apt-get clean \ 7 | && rm -rf /var/lib/apt/lists/* 8 | 9 | RUN mkdir /app 10 | WORKDIR /app 11 | 12 | VOLUME ["/app/data"] 13 | 14 | EXPOSE 3333/tcp 15 | 16 | ENV RAILS_ENV=production RAILS_SERVE_STATIC_FILES=1 17 | 18 | ADD Gemfile /app/Gemfile 19 | ADD Gemfile.lock /app/Gemfile.lock 20 | RUN bundle install --deployment --without development test 21 | 22 | ADD .bowerrc /app/.bowerrc 23 | ADD bower.json /app/bower.json 24 | RUN npm install -g bower \ 25 | && bower --allow-root install 26 | 27 | ADD . /app 28 | 29 | RUN ./bin/rake assets:precompile 30 | 31 | ENV REFRESH_ENABLED=1 32 | 33 | ENTRYPOINT ["./bin/rake"] 34 | CMD ["start"] 35 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'puma' 4 | 5 | gem 'rails', '~> 5.0.1' 6 | gem 'sqlite3' 7 | gem 'annotate' 8 | gem 'config' 9 | gem 'rufus-scheduler' 10 | gem 'jbuilder', '~> 2.0' 11 | gem 'sdoc', '~> 0.4.0', group: :doc 12 | gem 'versioneer', '~> 0.3' 13 | gem 'sentry-raven' 14 | 15 | # Version checking helpers 16 | gem 'gems' 17 | gem 'git' 18 | gem 'nokogiri' 19 | gem 'mixlib-versioning' 20 | 21 | # Frontend 22 | gem 'jquery-rails' 23 | gem 'turbolinks' 24 | gem 'uglifier', '>= 1.3.0' 25 | gem 'sassc-rails' 26 | gem 'font-awesome-sass', '~> 4.7.0' 27 | gem 'bootstrap-sass', '~> 3.3.6' 28 | gem 'accio_bower' 29 | gem 'rails_admin_clone' 30 | gem 'rails_admin' 31 | gem 'devise' 32 | 33 | group :development, :test do 34 | gem 'byebug' 35 | gem 'pry' 36 | gem 'pry-byebug' 37 | gem 'brakeman', :require => false 38 | gem 'yard', :require => false 39 | gem 'inch', :require => false 40 | gem 'guard', :require => false 41 | gem 'guard-livereload', :require => false 42 | end 43 | 44 | group :development do 45 | gem 'web-console', '~> 3.0' 46 | gem 'spring' 47 | end 48 | 49 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | accio_bower (1.0.0) 5 | rails 6 | sprockets 7 | versioneer 8 | actioncable (5.0.7.1) 9 | actionpack (= 5.0.7.1) 10 | nio4r (>= 1.2, < 3.0) 11 | websocket-driver (~> 0.6.1) 12 | actionmailer (5.0.7.1) 13 | actionpack (= 5.0.7.1) 14 | actionview (= 5.0.7.1) 15 | activejob (= 5.0.7.1) 16 | mail (~> 2.5, >= 2.5.4) 17 | rails-dom-testing (~> 2.0) 18 | actionpack (5.0.7.1) 19 | actionview (= 5.0.7.1) 20 | activesupport (= 5.0.7.1) 21 | rack (~> 2.0) 22 | rack-test (~> 0.6.3) 23 | rails-dom-testing (~> 2.0) 24 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 25 | actionview (5.0.7.1) 26 | activesupport (= 5.0.7.1) 27 | builder (~> 3.1) 28 | erubis (~> 2.7.0) 29 | rails-dom-testing (~> 2.0) 30 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 31 | activejob (5.0.7.1) 32 | activesupport (= 5.0.7.1) 33 | globalid (>= 0.3.6) 34 | activemodel (5.0.7.1) 35 | activesupport (= 5.0.7.1) 36 | activerecord (5.0.7.1) 37 | activemodel (= 5.0.7.1) 38 | activesupport (= 5.0.7.1) 39 | arel (~> 7.0) 40 | activesupport (5.0.7.1) 41 | concurrent-ruby (~> 1.0, >= 1.0.2) 42 | i18n (>= 0.7, < 2) 43 | minitest (~> 5.1) 44 | tzinfo (~> 1.1) 45 | annotate (2.7.4) 46 | activerecord (>= 3.2, < 6.0) 47 | rake (>= 10.4, < 13.0) 48 | arel (7.1.4) 49 | autoprefixer-rails (9.3.1) 50 | execjs 51 | bcrypt (3.1.12) 52 | bindex (0.5.0) 53 | bootstrap-sass (3.3.7) 54 | autoprefixer-rails (>= 5.2.1) 55 | sass (>= 3.3.4) 56 | brakeman (4.3.1) 57 | builder (3.2.3) 58 | byebug (10.0.2) 59 | coderay (1.1.2) 60 | coffee-rails (4.2.2) 61 | coffee-script (>= 2.2.0) 62 | railties (>= 4.0.0) 63 | coffee-script (2.4.1) 64 | coffee-script-source 65 | execjs 66 | coffee-script-source (1.12.2) 67 | concurrent-ruby (1.1.3) 68 | config (1.7.0) 69 | activesupport (>= 3.0) 70 | deep_merge (~> 1.2.1) 71 | dry-validation (>= 0.10.4) 72 | crass (1.0.4) 73 | deep_merge (1.2.1) 74 | devise (4.5.0) 75 | bcrypt (~> 3.0) 76 | orm_adapter (~> 0.1) 77 | railties (>= 4.1.0, < 6.0) 78 | responders 79 | warden (~> 1.2.3) 80 | dry-configurable (0.7.0) 81 | concurrent-ruby (~> 1.0) 82 | dry-container (0.6.0) 83 | concurrent-ruby (~> 1.0) 84 | dry-configurable (~> 0.1, >= 0.1.3) 85 | dry-core (0.4.7) 86 | concurrent-ruby (~> 1.0) 87 | dry-equalizer (0.2.1) 88 | dry-inflector (0.1.2) 89 | dry-logic (0.4.2) 90 | dry-container (~> 0.2, >= 0.2.6) 91 | dry-core (~> 0.2) 92 | dry-equalizer (~> 0.2) 93 | dry-types (0.13.3) 94 | concurrent-ruby (~> 1.0) 95 | dry-container (~> 0.3) 96 | dry-core (~> 0.4, >= 0.4.4) 97 | dry-equalizer (~> 0.2) 98 | dry-inflector (~> 0.1, >= 0.1.2) 99 | dry-logic (~> 0.4, >= 0.4.2) 100 | dry-validation (0.12.2) 101 | concurrent-ruby (~> 1.0) 102 | dry-configurable (~> 0.1, >= 0.1.3) 103 | dry-core (~> 0.2, >= 0.2.1) 104 | dry-equalizer (~> 0.2) 105 | dry-logic (~> 0.4, >= 0.4.0) 106 | dry-types (~> 0.13.1) 107 | em-websocket (0.5.1) 108 | eventmachine (>= 0.12.9) 109 | http_parser.rb (~> 0.6.0) 110 | erubis (2.7.0) 111 | et-orbi (1.1.6) 112 | tzinfo 113 | eventmachine (1.2.7) 114 | execjs (2.7.0) 115 | faraday (0.15.4) 116 | multipart-post (>= 1.2, < 3) 117 | ffi (1.9.25) 118 | font-awesome-rails (4.7.0.4) 119 | railties (>= 3.2, < 6.0) 120 | font-awesome-sass (4.7.0) 121 | sass (>= 3.2) 122 | formatador (0.2.5) 123 | fugit (1.1.6) 124 | et-orbi (~> 1.1, >= 1.1.6) 125 | raabro (~> 1.1) 126 | gems (1.1.1) 127 | json 128 | git (1.5.0) 129 | globalid (0.4.1) 130 | activesupport (>= 4.2.0) 131 | guard (2.15.0) 132 | formatador (>= 0.2.4) 133 | listen (>= 2.7, < 4.0) 134 | lumberjack (>= 1.0.12, < 2.0) 135 | nenv (~> 0.1) 136 | notiffany (~> 0.0) 137 | pry (>= 0.9.12) 138 | shellany (~> 0.0) 139 | thor (>= 0.18.1) 140 | guard-compat (1.2.1) 141 | guard-livereload (2.5.2) 142 | em-websocket (~> 0.5) 143 | guard (~> 2.8) 144 | guard-compat (~> 1.0) 145 | multi_json (~> 1.8) 146 | haml (5.0.4) 147 | temple (>= 0.8.0) 148 | tilt 149 | http_parser.rb (0.6.0) 150 | i18n (1.1.1) 151 | concurrent-ruby (~> 1.0) 152 | inch (0.8.0) 153 | pry 154 | sparkr (>= 0.2.0) 155 | term-ansicolor 156 | yard (~> 0.9.12) 157 | jbuilder (2.8.0) 158 | activesupport (>= 4.2.0) 159 | multi_json (>= 1.2) 160 | jquery-rails (4.3.3) 161 | rails-dom-testing (>= 1, < 3) 162 | railties (>= 4.2.0) 163 | thor (>= 0.14, < 2.0) 164 | jquery-ui-rails (6.0.1) 165 | railties (>= 3.2.16) 166 | json (1.8.6) 167 | kaminari (1.1.1) 168 | activesupport (>= 4.1.0) 169 | kaminari-actionview (= 1.1.1) 170 | kaminari-activerecord (= 1.1.1) 171 | kaminari-core (= 1.1.1) 172 | kaminari-actionview (1.1.1) 173 | actionview 174 | kaminari-core (= 1.1.1) 175 | kaminari-activerecord (1.1.1) 176 | activerecord 177 | kaminari-core (= 1.1.1) 178 | kaminari-core (1.1.1) 179 | listen (3.1.5) 180 | rb-fsevent (~> 0.9, >= 0.9.4) 181 | rb-inotify (~> 0.9, >= 0.9.7) 182 | ruby_dep (~> 1.2) 183 | loofah (2.2.3) 184 | crass (~> 1.0.2) 185 | nokogiri (>= 1.5.9) 186 | lumberjack (1.0.13) 187 | mail (2.7.1) 188 | mini_mime (>= 0.1.1) 189 | method_source (0.9.2) 190 | mini_mime (1.0.1) 191 | mini_portile2 (2.3.0) 192 | minitest (5.11.3) 193 | mixlib-versioning (1.2.2) 194 | multi_json (1.13.1) 195 | multipart-post (2.0.0) 196 | nenv (0.3.0) 197 | nested_form (0.3.2) 198 | nio4r (2.3.1) 199 | nokogiri (1.8.5) 200 | mini_portile2 (~> 2.3.0) 201 | notiffany (0.1.1) 202 | nenv (~> 0.1) 203 | shellany (~> 0.0) 204 | orm_adapter (0.5.0) 205 | pry (0.12.2) 206 | coderay (~> 1.1.0) 207 | method_source (~> 0.9.0) 208 | pry-byebug (3.6.0) 209 | byebug (~> 10.0) 210 | pry (~> 0.10) 211 | puma (3.12.0) 212 | raabro (1.1.6) 213 | rack (2.0.6) 214 | rack-pjax (1.0.0) 215 | nokogiri (~> 1.5) 216 | rack (>= 1.1) 217 | rack-test (0.6.3) 218 | rack (>= 1.0) 219 | rails (5.0.7.1) 220 | actioncable (= 5.0.7.1) 221 | actionmailer (= 5.0.7.1) 222 | actionpack (= 5.0.7.1) 223 | actionview (= 5.0.7.1) 224 | activejob (= 5.0.7.1) 225 | activemodel (= 5.0.7.1) 226 | activerecord (= 5.0.7.1) 227 | activesupport (= 5.0.7.1) 228 | bundler (>= 1.3.0) 229 | railties (= 5.0.7.1) 230 | sprockets-rails (>= 2.0.0) 231 | rails-dom-testing (2.0.3) 232 | activesupport (>= 4.2.0) 233 | nokogiri (>= 1.6) 234 | rails-html-sanitizer (1.0.4) 235 | loofah (~> 2.2, >= 2.2.2) 236 | rails_admin (1.4.2) 237 | builder (~> 3.1) 238 | coffee-rails (~> 4.0) 239 | font-awesome-rails (>= 3.0, < 5) 240 | haml (>= 4.0, < 6) 241 | jquery-rails (>= 3.0, < 5) 242 | jquery-ui-rails (>= 5.0, < 7) 243 | kaminari (>= 0.14, < 2.0) 244 | nested_form (~> 0.3) 245 | rack-pjax (>= 0.7) 246 | rails (>= 4.0, < 6) 247 | remotipart (~> 1.3) 248 | sass-rails (>= 4.0, < 6) 249 | rails_admin_clone (0.0.6) 250 | rails (>= 3.2) 251 | rails_admin (>= 0.4) 252 | railties (5.0.7.1) 253 | actionpack (= 5.0.7.1) 254 | activesupport (= 5.0.7.1) 255 | method_source 256 | rake (>= 0.8.7) 257 | thor (>= 0.18.1, < 2.0) 258 | rake (12.3.1) 259 | rb-fsevent (0.10.3) 260 | rb-inotify (0.9.10) 261 | ffi (>= 0.5.0, < 2) 262 | rdoc (4.3.0) 263 | remotipart (1.4.2) 264 | responders (2.4.0) 265 | actionpack (>= 4.2.0, < 5.3) 266 | railties (>= 4.2.0, < 5.3) 267 | ruby_dep (1.5.0) 268 | rufus-scheduler (3.5.2) 269 | fugit (~> 1.1, >= 1.1.5) 270 | sass (3.7.2) 271 | sass-listen (~> 4.0.0) 272 | sass-listen (4.0.0) 273 | rb-fsevent (~> 0.9, >= 0.9.4) 274 | rb-inotify (~> 0.9, >= 0.9.7) 275 | sass-rails (5.0.7) 276 | railties (>= 4.0.0, < 6) 277 | sass (~> 3.1) 278 | sprockets (>= 2.8, < 4.0) 279 | sprockets-rails (>= 2.0, < 4.0) 280 | tilt (>= 1.1, < 3) 281 | sassc (2.0.0) 282 | ffi (~> 1.9.6) 283 | rake 284 | sassc-rails (2.0.0) 285 | railties (>= 4.0.0) 286 | sassc (>= 2.0) 287 | sprockets (> 3.0) 288 | sprockets-rails 289 | tilt 290 | sdoc (0.4.2) 291 | json (~> 1.7, >= 1.7.7) 292 | rdoc (~> 4.0) 293 | sentry-raven (2.7.4) 294 | faraday (>= 0.7.6, < 1.0) 295 | shellany (0.0.1) 296 | sparkr (0.4.1) 297 | spring (2.0.2) 298 | activesupport (>= 4.2) 299 | sprockets (3.7.2) 300 | concurrent-ruby (~> 1.0) 301 | rack (> 1, < 3) 302 | sprockets-rails (3.2.1) 303 | actionpack (>= 4.0) 304 | activesupport (>= 4.0) 305 | sprockets (>= 3.0.0) 306 | sqlite3 (1.3.13) 307 | temple (0.8.0) 308 | term-ansicolor (1.7.0) 309 | tins (~> 1.0) 310 | thor (0.20.3) 311 | thread_safe (0.3.6) 312 | tilt (2.0.9) 313 | tins (1.20.2) 314 | turbolinks (5.2.0) 315 | turbolinks-source (~> 5.2) 316 | turbolinks-source (5.2.0) 317 | tzinfo (1.2.5) 318 | thread_safe (~> 0.1) 319 | uglifier (4.1.20) 320 | execjs (>= 0.3.0, < 3) 321 | versioneer (0.3.6) 322 | warden (1.2.8) 323 | rack (>= 2.0.6) 324 | web-console (3.7.0) 325 | actionview (>= 5.0) 326 | activemodel (>= 5.0) 327 | bindex (>= 0.4.0) 328 | railties (>= 5.0) 329 | websocket-driver (0.6.5) 330 | websocket-extensions (>= 0.1.0) 331 | websocket-extensions (0.1.3) 332 | yard (0.9.16) 333 | 334 | PLATFORMS 335 | ruby 336 | 337 | DEPENDENCIES 338 | accio_bower 339 | annotate 340 | bootstrap-sass (~> 3.3.6) 341 | brakeman 342 | byebug 343 | config 344 | devise 345 | font-awesome-sass (~> 4.7.0) 346 | gems 347 | git 348 | guard 349 | guard-livereload 350 | inch 351 | jbuilder (~> 2.0) 352 | jquery-rails 353 | mixlib-versioning 354 | nokogiri 355 | pry 356 | pry-byebug 357 | puma 358 | rails (~> 5.0.1) 359 | rails_admin 360 | rails_admin_clone 361 | rufus-scheduler 362 | sassc-rails 363 | sdoc (~> 0.4.0) 364 | sentry-raven 365 | spring 366 | sqlite3 367 | turbolinks 368 | uglifier (>= 1.3.0) 369 | versioneer (~> 0.3) 370 | web-console (~> 3.0) 371 | yard 372 | 373 | BUNDLED WITH 374 | 1.16.3 375 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | ## Uncomment and set this to only include directories you want to watch 5 | # directories %w(app lib config test spec features) \ 6 | # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")} 7 | 8 | ## Note: if you are using the `directories` clause above and you are not 9 | ## watching the project directory ('.'), then you will want to move 10 | ## the Guardfile to a watched dir and symlink it back, e.g. 11 | # 12 | # $ mkdir config 13 | # $ mv Guardfile config/ 14 | # $ ln -s config/Guardfile . 15 | # 16 | # and, you'll have to watch "config/Guardfile" instead of "Guardfile" 17 | 18 | guard 'livereload' do 19 | extensions = { 20 | css: :css, 21 | scss: :css, 22 | sass: :css, 23 | js: :js, 24 | coffee: :js, 25 | html: :html, 26 | png: :png, 27 | gif: :gif, 28 | jpg: :jpg, 29 | jpeg: :jpeg, 30 | # less: :less, # uncomment if you want LESS stylesheets done in browser 31 | } 32 | 33 | rails_view_exts = %w(erb haml slim) 34 | 35 | # file types LiveReload may optimize refresh for 36 | compiled_exts = extensions.values.uniq 37 | watch(%r{public/.+\.(#{compiled_exts * '|'})}) 38 | 39 | extensions.each do |ext, type| 40 | watch(%r{ 41 | (?:app|vendor) 42 | (?:/assets/\w+/(?[^.]+) # path+base without extension 43 | (?\.#{ext})) # matching extension (must be first encountered) 44 | (?:\.\w+|$) # other extensions 45 | }x) do |m| 46 | path = m[1] 47 | "/assets/#{path}.#{type}" 48 | end 49 | end 50 | 51 | # file needing a full reload of the page anyway 52 | watch(%r{app/views/.+\.(#{rails_view_exts * '|'})$}) 53 | watch(%r{app/helpers/.+\.rb}) 54 | watch(%r{config/locales/.+\.yml}) 55 | end 56 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2017 BinaryBabel OSS 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Latestver 2 | 3 | Dependency monitoring web-application featuring webhooks, JSON API, and project-level tracking. 4 | 5 | **Hosted edition available at: [lv.binarybabel.org](https://lv.binarybabel.org)** 6 | 7 | [![Join the chat at https://gitter.im/binarybabel/Latestver](https://badges.gitter.im/binarybabel/Latestver.svg)](https://gitter.im/binarybabel/Latestver?utm_source=badge&utm_medium=badge&utm_content=badge) [![Support](https://beerpay.io/binarybabel/latestver/badge.svg?style=beer)](https://beerpay.io/binarybabel/latestver) 8 | 9 | [![Docker Automated build](https://img.shields.io/docker/automated/binarybabel/latestver.svg)](https://hub.docker.com/r/binarybabel/latestver/) [![Dependency Status](https://gemnasium.com/badges/github.com/binarybabel/latestver.svg)](https://gemnasium.com/github.com/binarybabel/latestver) [![Code Climate](https://codeclimate.com/github/binarybabel/latestver/badges/gpa.svg)](https://codeclimate.com/github/binarybabel/latestver) [![Inline docs](http://inch-ci.org/github/binarybabel/latestver.svg?branch=master)](http://inch-ci.org/github/binarybabel/latestver) [![GPL](https://img.shields.io/aur/license/yaourt.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html) 10 | 11 | 12 | Supports tracking the latest versions of your favorite software via: 13 | 14 | * Git repository tags 15 | * RubyGems 16 | * NPM Packages 17 | * Web-page scraping 18 | 19 | 20 | ## Deploying Latestver Privately 21 | 22 | Latestver is available as a Docker Image: [hub.docker.com/r/binarybabel/latestver](https://hub.docker.com/r/binarybabel/latestver/) 23 | 24 | By default the application will be available from `http://localhost:3333` 25 | 26 | **Running Directly** 27 | 28 | ``` 29 | docker run -p 3333:3333 -v $(pwd):/app/data --name latestver binarybabel/latestver 30 | ``` 31 | 32 | **Using** `docker-compose.yml` 33 | 34 | ``` 35 | version: '2' 36 | services: 37 | app: 38 | image: binarybabel/latestver 39 | volumes: 40 | - .:/app/data 41 | ports: 42 | - "3333:3333" 43 | 44 | ``` 45 | 46 | 47 | ## Customizing 48 | 49 | ### Environment variables and defaults 50 | 51 | Catalog Settings 52 | 53 | * __REFRESH\_ENABLED__ 54 | * default: true - catalog versions refreshed automatically (at startup and set interval) 55 | * __REFRESH\_INTERVAL__ 56 | * default: 1h - how often catalog is refreshed, ex: _(7d, 1h, 15m, 60s)_ 57 | * __CACHE\_CONTROL\_MAX\_AGE__ 58 | * default: 0 - how many seconds browsers or proxies may cache catalog results 59 | * __GA\_TRACKING\_ID__ 60 | * default: none - Enable Google Analytics, ex: _UA-00000000-1_ 61 | 62 | Security Settings 63 | 64 | * __ADMIN\_PASS__ 65 | * no default - if set admin pages are password protected 66 | * __ADMIN\_USER__ 67 | * default: admin 68 | 69 | ### Custom Catalog Entries 70 | 71 | You can create custom catalog models for advanced version checking. 72 | 73 | **Here are some code references:** 74 | 75 | * [Base model](https://github.com/binarybabel/latestver/blob/master/app/models/catalog_entry.rb) - Shows abstract functions and parsing helpers 76 | * [RubyGem model](https://github.com/binarybabel/latestver/blob/master/app/models/catalog/ruby_gem.rb) - Good starting example 77 | * [All other models](https://github.com/binarybabel/latestver/tree/master/app/models/catalog) - More advanced examples 78 | 79 | Your model should be namespaced `module Catalog` and reside within your data volume in a `lib/catalog/` subdirectory. 80 | 81 | 82 | ## Contributing 83 | 84 | 1. Fork this repo on github.com and clone it to your workstation 85 | 2. Create your feature branch (`git checkout -b my-new-feature`) 86 | 3. Commit your changes (`git commit -am 'Added some feature'`) 87 | 4. Push the branch (`git push origin my-new-feature`) 88 | 5. Submit new Pull Request from GitHub 89 | 90 | 91 | ## Author and License 92 | 93 | - **Author:** BinaryBabel OSS ([https://keybase.io/binarybabel](https://keybase.io/binarybabel)) 94 | - **License:** GNU GPL 3 95 | 96 | ``` 97 | This program is free software: you can redistribute it and/or modify 98 | it under the terms of the GNU General Public License as published by 99 | the Free Software Foundation, either version 3 of the License, or 100 | (at your option) any later version. 101 | 102 | This program is distributed in the hope that it will be useful, 103 | but WITHOUT ANY WARRANTY; without even the implied warranty of 104 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 105 | GNU General Public License for more details. 106 | 107 | You should have received a copy of the GNU General Public License 108 | along with this program. If not, see . 109 | ``` 110 | 111 | 112 | ## Project Includes 113 | Latestver utilizes the following open-source projects... 114 | 115 | + Bootstrap -- _[getbootstrap.com](http://getbootstrap.com/)_ 116 | + Font Awesome -- _[fontawesome.io](http://fontawesome.io/)_ 117 | + RailsAdmin -- _[github](https://github.com/sferik/rails_admin)_ 118 | + Shields -- _[shields.io](http://shields.io/)_ 119 | 120 | Other dependencies are enumerated in `Dockerfile` and `Gemfile`. -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | 8 | desc 'Start Latestver application server' 9 | task :start do 10 | refresh = ENV['REFRESH_ENABLED'] 11 | ENV['REFRESH_ENABLED'] = nil 12 | 13 | begin 14 | Rake::Task['db:setup'].invoke 15 | rescue ActiveRecord::ProtectedEnvironmentError 16 | # Ignore, db already setup. 17 | end 18 | 19 | Rake::Task['db:migrate'].invoke 20 | 21 | ENV['REFRESH_ENABLED'] = refresh 22 | system 'rm -f tmp/pids/server.pid' 23 | exec './bin/rails s -p 3333 -b "0.0.0.0"' 24 | end 25 | 26 | namespace :doc do 27 | desc 'Start yard doc server' 28 | task :start do 29 | exec './bin/bundle exec yard server --reload' 30 | end 31 | 32 | desc 'Build yard documentation' 33 | task :build do 34 | exec './bin/bundle exec yard doc' 35 | end 36 | 37 | desc 'Suggest documentation improvements' 38 | task :suggest do 39 | exec './bin/bundle exec inch suggest' 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/feature-git.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/app/assets/images/feature-git.jpg -------------------------------------------------------------------------------- /app/assets/images/feature-groups.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/app/assets/images/feature-groups.jpg -------------------------------------------------------------------------------- /app/assets/images/feature-webhooks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/app/assets/images/feature-webhooks.jpg -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | // require turbolinks 16 | //= require bootstrap-sprockets 17 | //= require datatables/media/js/jquery.dataTables.min 18 | //= require datatables/media/js/dataTables.bootstrap.min 19 | //= require clipboard/dist/clipboard.min 20 | //= require_tree . 21 | -------------------------------------------------------------------------------- /app/assets/javascripts/catalog.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/dashboard.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/help.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/log.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/noty.js: -------------------------------------------------------------------------------- 1 | //= require noty/js/noty/packaged/jquery.noty.packaged.min 2 | 3 | $.noty.defaults = { 4 | layout: 'bottomRight', 5 | theme: 'metroui', 6 | type: 'information', // success, error, warning, information, notification 7 | text: '', // [string|html] can be HTML or STRING 8 | 9 | dismissQueue: true, // [boolean] If you want to use queue feature set this true 10 | force: false, // [boolean] adds notification to the beginning of queue when set to true 11 | maxVisible: 5, // [integer] you can set max visible notification count for dismissQueue true option, 12 | 13 | template: '
', 14 | 15 | timeout: 4000, // [integer|boolean] delay for closing event in milliseconds. Set false for sticky notifications 16 | progressBar: true, // [boolean] - displays a progress bar 17 | 18 | animation: { 19 | open: {opacity: 'toggle'}, // or Animate.css class names like: 'animated bounceInLeft' 20 | close: {height: 'toggle'}, // or Animate.css class names like: 'animated bounceOutLeft' 21 | easing: 'swing', 22 | speed: 400 // opening & closing animation speed 23 | }, 24 | closeWith: ['click'], // ['click', 'button', 'hover', 'backdrop'] // backdrop click will close all notifications 25 | 26 | modal: false, // [boolean] if true adds an overlay 27 | killer: false, // [boolean] if true closes all notifications and shows itself 28 | 29 | callback: { 30 | onShow: function() {}, 31 | afterShow: function() {}, 32 | onClose: function() {}, 33 | afterClose: function() {}, 34 | onCloseClick: function() {} 35 | }, 36 | 37 | buttons: false // [boolean|array] an array of buttons, for creating confirmation dialogs. 38 | }; 39 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any styles 10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new 11 | * file per style scope. 12 | * 13 | *= require includes 14 | *= require_tree . 15 | *= require_self 16 | */ 17 | 18 | -------------------------------------------------------------------------------- /app/assets/stylesheets/catalog.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the catalog controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | body.catalog-view { 6 | .jumbotron { 7 | border: 2px solid #000; 8 | background-color: #fff; 9 | padding: 10px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/assets/stylesheets/dashboard.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the dashboard controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | /* 6 | * Base structure 7 | */ 8 | 9 | /* Move down content because we have a fixed navbar that is 50px tall */ 10 | body { 11 | padding-top: 50px; 12 | } 13 | 14 | /* 15 | * Global add-ons 16 | */ 17 | 18 | .sub-header { 19 | padding-bottom: 10px; 20 | border-bottom: 1px solid #eee; 21 | } 22 | 23 | /* 24 | * Top navigation 25 | * Hide default border to remove 1px line. 26 | */ 27 | .navbar-fixed-top { 28 | border: 0; 29 | } 30 | 31 | /* 32 | * Sidebar 33 | */ 34 | 35 | /* Hide for mobile, show later */ 36 | .sidebar { 37 | display: none; 38 | } 39 | @media (min-width: 768px) { 40 | .sidebar { 41 | position: fixed; 42 | top: 51px; 43 | bottom: 0; 44 | left: 0; 45 | z-index: 1000; 46 | display: block; 47 | padding: 20px; 48 | overflow-x: hidden; 49 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ 50 | background-color: #f5f5f5; 51 | border-right: 1px solid #eee; 52 | } 53 | } 54 | 55 | /* Sidebar navigation */ 56 | .nav-sidebar { 57 | margin-right: -21px; /* 20px padding + 1px border */ 58 | margin-bottom: 20px; 59 | margin-left: -20px; 60 | } 61 | .nav-sidebar > li > a { 62 | padding-right: 20px; 63 | padding-left: 20px; 64 | } 65 | .nav-sidebar > .active > a, 66 | .nav-sidebar > .active > a:hover, 67 | .nav-sidebar > .active > a:focus { 68 | color: #fff; 69 | background-color: #428bca; 70 | } 71 | 72 | /* 73 | * Main content 74 | */ 75 | 76 | .main { 77 | padding: 20px; 78 | } 79 | @media (min-width: 768px) { 80 | .main { 81 | padding-right: 40px; 82 | padding-left: 40px; 83 | } 84 | } 85 | .main .page-header { 86 | margin-top: 0; 87 | } 88 | 89 | /* 90 | * Placeholder dashboard ideas 91 | */ 92 | 93 | .placeholders { 94 | margin-bottom: 30px; 95 | text-align: center; 96 | } 97 | .placeholders h4 { 98 | margin-bottom: 0; 99 | } 100 | .placeholder { 101 | margin-bottom: 20px; 102 | } 103 | .placeholder img { 104 | display: inline-block; 105 | border-radius: 50%; 106 | } 107 | -------------------------------------------------------------------------------- /app/assets/stylesheets/help.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the help controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/includes.scss: -------------------------------------------------------------------------------- 1 | @import "font-awesome-sprockets"; 2 | @import "font-awesome"; 3 | @import "bootstrap-sprockets"; 4 | @import "bootstrap"; 5 | //@import "datatables/media/css/jquery.dataTables.min"; 6 | @import "datatables/media/css/dataTables.bootstrap.min"; 7 | -------------------------------------------------------------------------------- /app/assets/stylesheets/log.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the log controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/styles.scss: -------------------------------------------------------------------------------- 1 | #navbar ul.nav a.product-update { 2 | color: #8cd3f8; 3 | font-weight: 100; 4 | &:hover { 5 | color: #fff; 6 | } 7 | } 8 | 9 | .main a { 10 | border-bottom: 1px dotted #337ab7; 11 | white-space: nowrap; 12 | } 13 | 14 | code.version { 15 | color: #202020; 16 | background-color: #D4F4DE; 17 | } 18 | 19 | label.row-label { 20 | display: block; 21 | padding: 1px 10px; 22 | margin: 0 0 10px; 23 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 24 | font-size: 13px; 25 | font-weight: bold; 26 | line-height: 30px; 27 | text-align: right; 28 | word-break: break-all; 29 | word-wrap: break-word; 30 | white-space: nowrap; 31 | color: #333333; 32 | background-color: #f5f5f5; 33 | border: 1px solid #ccc; 34 | border-radius: 4px; 35 | 36 | @media screen and (max-width: 760px) { 37 | & { 38 | text-align: left; 39 | } 40 | } 41 | } 42 | 43 | div.input-sample { 44 | position: relative; 45 | overflow: hidden; 46 | padding: 0; 47 | line-height: 30px; 48 | border: 1px solid #aaa; 49 | 50 | i { 51 | display: block; 52 | float: left; 53 | padding: 9px 10px 9px 12px; 54 | margin-right: 8px; 55 | background-color: #f5f5f5; 56 | } 57 | code { 58 | display: block; 59 | line-height: inherit; 60 | white-space: pre !important; 61 | overflow-y: auto !important; 62 | background-color: transparent; 63 | color: inherit; 64 | } 65 | input { 66 | display: block; 67 | width: 100%; 68 | line-height: inherit; 69 | font-size: 13px; 70 | padding: 2px 6px; 71 | border: none !important; 72 | } 73 | button.clipboard { 74 | position: absolute; 75 | right: -1px; 76 | top: -1px; 77 | padding: 0; 78 | margin: 0; 79 | background: #fff; 80 | border: none; 81 | overflow: hidden; 82 | i { 83 | border: 1px solid #aaa; 84 | margin: 0 0 0 10px; 85 | } 86 | } 87 | 88 | &.black { 89 | border-color: #000; 90 | background-color: #464646; 91 | color: #fff !important; 92 | i { 93 | background-color: #000; 94 | } 95 | span.token { 96 | background-color: #fff; 97 | color: #000 !important; 98 | border: 1px dotted #000; 99 | padding: 0 2px; 100 | margin: 0 2px; 101 | } 102 | } 103 | 104 | &.blue { 105 | border-color: #337eb7; 106 | color: #000 !important; 107 | i { 108 | background-color: #ebf6fc; 109 | } 110 | code span.token { 111 | background-color: #ebf6fc; 112 | border: 1px dotted #337eb7; 113 | padding: 0 2px; 114 | margin: 0 2px; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | 6 | def cache_this! 7 | if ENV['CACHE_CONTROL_MAX_AGE'].present? 8 | expires_in ENV['CACHE_CONTROL_MAX_AGE'].to_i.seconds, :public => true 9 | end 10 | end 11 | 12 | ActionController::Renderers.add :json do |json, options| 13 | unless json.kind_of?(String) 14 | json = json.as_json(options) if json.respond_to?(:as_json) 15 | json = JSON.pretty_generate(json, options) 16 | end 17 | 18 | if options[:callback].present? 19 | self.content_type ||= Mime::JS 20 | "#{options[:callback]}(#{json})" 21 | else 22 | self.content_type ||= Mime::JSON 23 | json 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/controllers/catalog_controller.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | require 'net/http' 3 | 4 | class CatalogController < ApplicationController 5 | 6 | def index 7 | cache_this! 8 | @catalog = CatalogEntry.order('version_date DESC, name, tag DESC').visible 9 | end 10 | 11 | def view 12 | cache_this! 13 | @entry = CatalogEntry.find_by!(name: params[:name], tag: params[:tag]) 14 | 15 | @data = @entry.api_data 16 | @external_links = @data['external_links'] || [] 17 | @has_links = @external_links.size > 0 18 | 19 | respond_to do |format| 20 | # Default view passthrough. 21 | format.html 22 | 23 | # Output whole entry as JSON. 24 | format.json { render json: @data } 25 | 26 | # Output single field value from entry. 27 | format.text do 28 | if (path = params['p']) 29 | segments = path.split('.') 30 | x = @data 31 | while segments.length > 0 and x 32 | x = x[segments.shift] 33 | end 34 | render plain: x.to_s 35 | else 36 | render plain: 'ERROR: Path not provided.', status: :bad_request 37 | end 38 | end 39 | 40 | # Render entry as status badge. 41 | # Optionally providing a remote version to 42 | # compare and colorize the result, as follows: 43 | # * BLUE (No version) 44 | # * GREEN (Version given is up-to-date) 45 | # * RED (Version given is out-of-date) 46 | format.svg do 47 | badge_label = params['label'] || params['l'] || @entry.label 48 | badge_version = @entry.version 49 | badge_color = 'blue' 50 | if (version = params['v']) 51 | badge_version = URI.encode(version) 52 | badge_color = (version == @entry.version) && 'brightgreen' || 'red' 53 | end 54 | badge_label = badge_label.to_s.gsub('-', '--') 55 | badge_version = badge_version.to_s.gsub('-', '--') 56 | badge_url = "https://img.shields.io/badge/#{badge_label}-#{badge_version}-#{badge_color}.svg" 57 | 58 | if Rails.env.development? or ENV['PROXY_BADGES'] 59 | max_redirects = 5 60 | loop do 61 | raise 'Too many redirects' if max_redirects < 1 62 | uri = URI.parse(badge_url) 63 | req = Net::HTTP::Get.new(uri.path, { 'User-Agent' => 'Wget/1.9.1' }) 64 | rsp = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) } 65 | if rsp.code.to_i.between?(300,307) 66 | max_redirects -= 1 67 | puts rsp['location'] 68 | badge_url = r['location'] 69 | next 70 | end 71 | rsp.value # raise error on bad response 72 | send_data rsp.body, type: rsp['content-type'], disposition: 'inline' 73 | break 74 | end 75 | else 76 | redirect_to badge_url, status: :found 77 | end 78 | end 79 | end 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | class DashboardController < ApplicationController 2 | def index 3 | redirect_to '/catalog', status: :moved_permanently 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/group_controller.rb: -------------------------------------------------------------------------------- 1 | class GroupController < ApplicationController 2 | def index 3 | @group_name = params[:group] 4 | @group = Group.find_by!(name: @group_name) 5 | @instances = Instance.where(:group => @group).joins(:catalog_entry).order('catalog_entries.name, catalog_entries.tag DESC') 6 | end 7 | 8 | def update 9 | index 10 | Instance.transaction do 11 | @instances.each do |i| 12 | k = "ver_#{i.id}".to_sym 13 | v = params[k].to_s 14 | unless v.to_s.empty? or v == i.version 15 | v = nil if %w(l latest).include?(v) 16 | i.update!(v) 17 | end 18 | end 19 | end 20 | render 'index' 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/help_controller.rb: -------------------------------------------------------------------------------- 1 | class HelpController < ApplicationController 2 | def index 3 | cache_this! 4 | end 5 | 6 | def api 7 | cache_this! 8 | 9 | # Attempt to use latest values for examples. 10 | begin 11 | if (entry = CatalogEntry.find_by(name: 'rails', tag: 'latest')) 12 | @ex_rails_latest = entry.version.presence 13 | end 14 | if (entry = CatalogEntry.find_by(name: 'java', tag: 'jdk8')) 15 | @ex_java_download = entry.downloads[:rpm].presence 16 | @ex_java_json = JSON.pretty_generate(entry.api_data) if entry.version 17 | end 18 | rescue 19 | # ignore 20 | end 21 | 22 | @ex_rails_latest ||= '5.0.0' 23 | @ex_java_download ||= 'http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111-linux-x64.rpm' 24 | end 25 | 26 | def version 27 | cache_this! 28 | render plain: Latestver::VERSION 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/controllers/log_controller.rb: -------------------------------------------------------------------------------- 1 | class LogController < ApplicationController 2 | def index 3 | cache_this! 4 | @catalog_log = CatalogLogEntry.order('created_at DESC').all 5 | respond_to do |format| 6 | # Default view passthrough. 7 | format.html 8 | 9 | # Default view passthrough. 10 | format.rss { render :layout => false } 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | require 'gems' 2 | 3 | module ApplicationHelper 4 | def product_update? 5 | begin 6 | latest = CatalogEntry.find_by!(name: 'latestver', tag: 'release') 7 | latest_ver = ::Gem::Version.new(latest.version) 8 | current_ver = ::Gem::Version.new(::Latestver::VERSION) 9 | latest_ver > current_ver 10 | rescue 11 | false 12 | end 13 | end 14 | 15 | def body_c 16 | c = params[:controller].to_s.split('/').last 17 | a = params[:action] 18 | "#{c} #{c}-#{a}" 19 | end 20 | 21 | def active_c(input) 22 | if input 23 | 'active' 24 | else 25 | '' 26 | end 27 | end 28 | 29 | def last_catalog_refresh 30 | time = CatalogEntry.order('refreshed_at DESC').first.try(:refreshed_at) 31 | time && time.to_s || 'Never' 32 | end 33 | 34 | def instance_groups 35 | Group.order(:name).all.map { |g| g.name } 36 | end 37 | 38 | def code_sample(input, opts=nil) 39 | opts = {theme: '', icon: 'terminal', label: ''}.merge(opts.to_h) 40 | (%%
#{opts[:label]}% + 41 | input.gsub(/<([^>]+)>/, '\\1') + 42 | '
').html_safe 43 | end 44 | 45 | def badge_snippet(entry, type='html', version=nil) 46 | data = { 47 | href: catalog_view_url(name: entry.name, tag: entry.tag), 48 | alt: I18n.t('app.nav.title'), 49 | src: catalog_view_api_url(name: entry.name, tag: entry.tag, format: 'svg') 50 | } 51 | data[:src] += "?v=#{version}" if version 52 | case type.to_s 53 | when 'md' 54 | '[![%{alt}](%{src})](%{href})' % data 55 | else 56 | '%{alt}' % data 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /app/helpers/catalog_helper.rb: -------------------------------------------------------------------------------- 1 | module CatalogHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/dashboard_helper.rb: -------------------------------------------------------------------------------- 1 | module DashboardHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/log_helper.rb: -------------------------------------------------------------------------------- 1 | module LogHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/app/mailers/.keep -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/app/models/.keep -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/catalog/git_repo.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'open-uri' 23 | require 'git' 24 | 25 | module Catalog 26 | class GitRepo < CatalogEntry 27 | validates :git_repo_url, 28 | presence: true, 29 | format: {:with => /\Ahttp(s)?:\/\/.+\z/} 30 | 31 | store :data, accessors: [ :git_repo_url ], coder: JSON 32 | 33 | def check_remote_version 34 | case tag 35 | when 'latest' 36 | self.repo_version(git_repo_url) 37 | else 38 | self.repo_version(git_repo_url, scan_short_version(tag)) 39 | end 40 | end 41 | 42 | def default_links 43 | links = [] 44 | if git_repo_url.match(/github/) 45 | links << %( GitHub) 46 | else 47 | links << %( Repository) 48 | end 49 | links 50 | end 51 | 52 | def downloads 53 | links = Hash.new 54 | if version 55 | links['git'] = git_repo_url 56 | if (m = git_repo_url.match(%r{github.com/(.+)\.git})) 57 | links['tgz'] = "https://github.com/#{m[1]}/archive/#{version}.tar.gz" 58 | links['zip'] = "https://github.com/#{m[1]}/archive/#{version}.zip" 59 | end 60 | end 61 | links 62 | end 63 | 64 | def self.reload_defaults! 65 | create_with( 66 | git_repo_url: 'https://github.com/binarybabel/latestver.git' 67 | ).find_or_create_by!(name: 'latestver', tag: 'release') 68 | end 69 | 70 | protected 71 | 72 | def repo_version(repo_url, filter=nil) 73 | g = Git.ls_remote(repo_url) 74 | tags = g['tags'].keys.map do |tag| 75 | # Trim dereference markers https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html 76 | tag.sub(/\^.+\z/, '') 77 | end.uniq 78 | if filter 79 | match_requirement(tags, "~>#{filter}.0") 80 | else 81 | match_requirement(tags, '>= 0') 82 | end 83 | end 84 | 85 | rails_admin do 86 | create do 87 | field :tag do 88 | label 'Tag (Filter)' 89 | end 90 | field :git_repo_url 91 | end 92 | 93 | edit do 94 | field :tag do 95 | label 'Tag (Filter)' 96 | end 97 | field :git_repo_url 98 | end 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /app/models/catalog/node_package.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'open-uri' 23 | 24 | module Catalog 25 | class NodePackage < CatalogEntry 26 | def check_remote_version 27 | case tag 28 | when 'latest' 29 | self.npm_version(name) 30 | else 31 | self.npm_version(name, scan_short_version(tag)) 32 | end 33 | end 34 | 35 | def command_samples 36 | return Hash.new unless version 37 | { 38 | 'install': "npm install -g #{name}", 39 | } 40 | end 41 | 42 | def self.reload_defaults! 43 | end 44 | 45 | protected 46 | 47 | def npm_version(name, filter = 'latest') 48 | if filter == 'latest' 49 | package = JSON.load(open("http://registry.npmjs.org/#{name}/#{filter}")) 50 | scan_version(package['version']) if package.is_a?(Hash) 51 | else 52 | packages = JSON.load(open("http://registry.npmjs.org/#{name}/")) 53 | match_requirement(packages['versions'].keys.compact, "~>#{filter}.0") 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /app/models/catalog/oracle_java.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'open-uri' 23 | 24 | module Catalog 25 | class OracleJava < CatalogEntry 26 | store :data, accessors: [ :download_uuid ], coder: JSON 27 | 28 | def vendor_urls 29 | @vurls ||= { 30 | 'jdk11' => 'https://www.oracle.com/technetwork/java/javase/downloads/jdk11-downloads-5066655.html', 31 | 'jdk8' => 'http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html', 32 | 'jre8' => 'http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html', 33 | 'jdk7' => 'http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html', 34 | 'jre7' => 'http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html', 35 | 'jdk6' => 'http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase6-419409.html', 36 | 'jre6' => 'http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase6-419409.html', 37 | } 38 | end 39 | 40 | def check_remote_version 41 | case tag 42 | when /jdk[0-9]|jre[0-9]/ 43 | raise "Unknown Java tag (#{tag})" unless vendor_urls.include?(tag) 44 | major = scan_number(tag) 45 | html = open(vendor_urls[tag]) { |f| f.read } 46 | # if (m = html.match(%r{/java/jdk/(#{major}u[0-9]+-b[0-9]+)/([a-f0-9]{32})?})) 47 | if (m = html.match(%r{/java/jdk/(#{major}[u\.][0-9]+[\.-]b?[0-9]+(\+[0-9]+)?)/([a-f0-9]{32})?})) 48 | { 49 | version: m[1], 50 | download_uuid: m[3] 51 | } 52 | end 53 | end 54 | end 55 | 56 | def java_type 57 | tag.sub(/[0-9]+/, '') 58 | end 59 | 60 | def version_parsed 61 | { 62 | 'major': version_segments[0], 63 | 'minor': version_segments[1], 64 | 'build': version_segments[2], 65 | 'uuid': download_uuid, 66 | } 67 | end 68 | 69 | def default_links 70 | links = [] 71 | if (url = vendor_urls[tag]) 72 | links << %( Releases) 73 | end 74 | links 75 | end 76 | 77 | def downloads 78 | return Hash.new unless version 79 | uuid = '' 80 | dver = "#{version_segments[0]}u#{version_segments[1]}-" 81 | dsfx = '' 82 | 83 | if version_segments[0].to_i >= 8 84 | uuid = download_uuid.to_s + '/' 85 | end 86 | 87 | if version_segments[0].to_i >= 11 88 | dver = version_segments[0..2].join('.') + '_' 89 | dsfx = '_bin' 90 | end 91 | 92 | { 93 | 'rpm': "http://download.oracle.com/otn-pub/java/jdk/#{version}/#{uuid}#{java_type}-#{dver}linux-x64#{dsfx}.rpm", 94 | 'tgz': "http://download.oracle.com/otn-pub/java/jdk/#{version}/#{uuid}#{java_type}-#{dver}linux-x64#{dsfx}.tar.gz", 95 | 'dmg': "http://download.oracle.com/otn-pub/java/jdk/#{version}/#{uuid}#{java_type}-#{dver}macosx-x64#{dsfx}.dmg", 96 | 'exe': "http://download.oracle.com/otn-pub/java/jdk/#{version}/#{uuid}#{java_type}-#{dver}windows-x64#{dsfx}.exe", 97 | } 98 | end 99 | 100 | def command_samples 101 | return Hash.new unless version 102 | { 103 | 'curl_download': "curl -LOH 'Cookie: oraclelicense=accept-securebackup-cookie' '#{downloads[:rpm]}'", 104 | 'wget_download': "wget --no-check-certificate --no-cookies --header 'Cookie: oraclelicense=accept-securebackup-cookie' '#{downloads[:rpm]}'", 105 | } 106 | end 107 | 108 | def self.reload_defaults! 109 | { 110 | 'java' => %w(jdk11 jdk8) 111 | }.each do |name, tags| 112 | tags.each do |tag| 113 | find_or_create_by!(name: name, tag: tag) 114 | end 115 | end 116 | 117 | if Rails.env.development? 118 | { 119 | 'java' => %w(jdk7 jdk6) 120 | }.each do |name, tags| 121 | tags.each do |tag| 122 | find_or_create_by!(name: name, tag: tag) 123 | end 124 | end 125 | end 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /app/models/catalog/ruby_gem.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'gems' 23 | 24 | module Catalog 25 | class RubyGem < CatalogEntry 26 | def check_remote_version 27 | case tag 28 | when 'latest' 29 | self.gem_version(name) 30 | else 31 | self.gem_version(name, scan_number(tag)) 32 | end 33 | end 34 | 35 | def default_links 36 | [ 37 | %( RubyGems) 38 | ] 39 | end 40 | 41 | def downloads 42 | links = Hash.new 43 | if version 44 | links['gem'] = "https://rubygems.org/downloads/#{name}-#{version}.gem" 45 | end 46 | links 47 | end 48 | 49 | def self.reload_defaults! 50 | { 51 | 'rails' => %w(latest rails5 rails4) 52 | }.each do |name, tags| 53 | tags.each do |tag| 54 | create_with( 55 | external_links: [ 56 | ' Changelog' 57 | ].join("\n") 58 | ).find_or_create_by!(name: name, tag: tag) 59 | end 60 | end 61 | end 62 | 63 | protected 64 | 65 | def gem_version(name, filter=nil) 66 | versions = Gems.versions(name) 67 | raise 'Failed to retrieve gem info.' unless versions 68 | raise versions.to_s unless versions.is_a?(Array) 69 | if filter 70 | match_requirement(versions.map { |y| y['number'].to_s }.compact, "~>#{filter}.0") 71 | else 72 | match_requirement(versions.map { |y| y['number'].to_s }.compact, '>0') 73 | end 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /app/models/catalog/web_diff.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'digest/sha1' 23 | 24 | module Catalog 25 | class WebDiff < WebScraper 26 | def check_remote_version 27 | text = fetch_remote_text 28 | iexp = Regexp.new(include_regex.to_s, Regexp::MULTILINE) 29 | 30 | if (m = text.match(iexp)) 31 | return Digest::SHA1.hexdigest(m[0])[0..5] 32 | end 33 | 34 | nil 35 | end 36 | 37 | def self.reload_defaults! 38 | 39 | end 40 | 41 | rails_admin do 42 | create do 43 | field :web_page_url do 44 | help ::CatalogEntry.template_help 45 | end 46 | field :css_query 47 | field :xpath_query 48 | field :include_regex 49 | end 50 | 51 | edit do 52 | field :web_page_url do 53 | help ::CatalogEntry.template_help 54 | end 55 | field :css_query 56 | field :xpath_query 57 | field :include_regex 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /app/models/catalog/web_scraper.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'open-uri' 23 | 24 | module Catalog 25 | class WebScraper < CatalogEntry 26 | validates :web_page_url, 27 | presence: true, 28 | format: {:with => /\Ahttp(s)?:\/\/.+\z/} 29 | validates_presence_of :include_regex 30 | 31 | store :data, accessors: [:web_page_url, :css_query, :xpath_query, :include_regex, :exclude_regex], coder: JSON 32 | 33 | def check_remote_version 34 | text = fetch_remote_text 35 | 36 | iexp = Regexp.new(include_regex.to_s) 37 | xexp = Regexp.new(exclude_regex.to_s) 38 | 39 | text.split("\n").each do |line| 40 | if (m = line.match(iexp)) and (exclude_regex.to_s.empty? or not line.match(xexp)) 41 | if m.names.include?('ver') 42 | return m['ver'] 43 | elsif prereleases 44 | return scan_short_version(m[0].to_s) 45 | else 46 | return scan_version(m[0].to_s) 47 | end 48 | end 49 | end 50 | 51 | nil 52 | end 53 | 54 | def default_links 55 | [ 56 | %( Website) 57 | ] 58 | end 59 | 60 | def self.reload_defaults! 61 | create_with( 62 | web_page_url: 'http://fontawesome.io/', 63 | css_query: 'div.shameless-self-promotion', 64 | xpath_query: 'text()', 65 | include_regex: '.+' 66 | ).find_or_create_by!(name: 'fontawesome', tag: 'latest') 67 | 68 | create_with( 69 | web_page_url: 'https://gitlab.com/gitlab-org/gitlab-ce/raw/master/CHANGELOG.md', 70 | include_regex: '## v?[0-9\.]+', 71 | exclude_regex: 'unreleased' 72 | ).find_or_create_by!(name: 'gitlab', tag: 'latest') 73 | 74 | create_with( 75 | web_page_url: 'https://docs.gradle.org/current/release-notes', 76 | css_query: 'h3.releaseinfo', 77 | include_regex: '.+' 78 | ).find_or_create_by!(name: 'gradle', tag: 'latest') 79 | end 80 | 81 | protected 82 | 83 | def fetch_remote_text 84 | url = web_page_url.to_s.gsub(/%(?!\{)/, '%%') % template_params 85 | text = open(url) { |f| f.read } 86 | 87 | unless [css_query, xpath_query].all? { |v| v.to_s.empty? } 88 | n = Nokogiri::HTML(text) 89 | unless css_query.to_s.empty? 90 | n = n.at_css(css_query) 91 | end 92 | unless xpath_query.to_s.empty? 93 | n = n.at_xpath(xpath_query) 94 | end 95 | text = n.text 96 | end 97 | 98 | text 99 | end 100 | 101 | rails_admin do 102 | create do 103 | field :web_page_url do 104 | help ::CatalogEntry.template_help 105 | end 106 | field :css_query 107 | field :xpath_query 108 | field :include_regex 109 | field :exclude_regex 110 | end 111 | 112 | edit do 113 | field :web_page_url do 114 | help ::CatalogEntry.template_help 115 | end 116 | field :css_query 117 | field :xpath_query 118 | field :include_regex 119 | field :exclude_regex 120 | end 121 | end 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /app/models/catalog/yarn_package.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'open-uri' 23 | 24 | module Catalog 25 | class YarnPackage < NodePackage 26 | def command_samples 27 | return Hash.new unless version 28 | { 29 | 'install': "yarn add #{name}##{version}", 30 | } 31 | end 32 | 33 | def self.reload_defaults! 34 | { 35 | 'bootstrap' => %w(bootstrap4 bootstrap3) 36 | }.each do |name, tags| 37 | tags.each do |tag| 38 | find_or_create_by!(name: name, tag: tag) 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/models/catalog_entry.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'mixlib/versioning' 23 | 24 | class CatalogEntry < ActiveRecord::Base 25 | validates_presence_of :type 26 | validates :name, :tag, presence: true, 27 | format: {with: /\A[a-z0-9][a-z0-9_\.-]+[a-z0-9]\z/i, message: 'allows letters, numbers, hyphens, underscores, and full-stops.'} 28 | validates :tag, uniqueness: {scope: :name} 29 | 30 | has_many :catalog_log_entries, -> { order 'created_at DESC' }, dependent: :destroy 31 | has_many :catalog_webhooks, dependent: :destroy 32 | has_many :instances, dependent: :destroy 33 | 34 | scope :visible, -> { where('hidden != 1 AND hidden != "t"') } 35 | scope :hidden, -> { where('hidden = 1 OR hidden = "t"') } 36 | 37 | ## 38 | ## =ABSTRACT FUNCTIONS= 39 | ## These should be overridden as necessary by actual catalog model. 40 | ## 41 | 42 | # Required. 43 | # Ex: return '0.0.1' 44 | def check_remote_version 45 | false 46 | end 47 | 48 | # Optional. 49 | # Ex: [' Hello', ...] 50 | def default_links 51 | [] 52 | end 53 | 54 | # Optional. 55 | # Ex: {'tgz': 'http://example.com/file.tgz', ...} 56 | def downloads 57 | {} 58 | end 59 | 60 | # Optional. 61 | # Ex: {'hello': 'echo Hello World'} 62 | def command_samples 63 | {} 64 | end 65 | 66 | # Create "factory" default entries for a catalog type. 67 | # Helps demonstrate how the :name and :tag fields are interpreted 68 | # --------------------------------------------------------------- 69 | # def self.reload_defaults! 70 | # find_or_create_by!(name: name, tag: tag) 71 | # end 72 | 73 | # If your models need additional fields stored in the database... 74 | # --------------------------------------------------------------- 75 | # store :data, accessors: [ :field_a, :field_b ], coder: JSON 76 | 77 | ## 78 | ## =HELPER FUNCTIONS= 79 | ## Useful in parsing or determining latest version. 80 | ## 81 | 82 | # Is input 'exactly' parseable as a version? 83 | def is_version?(input) 84 | Gem::Version.correct?(input) != nil 85 | end 86 | 87 | # Parse a version number (Ex: 0.0.1) out of an input string. 88 | def scan_version(input, as_obj=false) 89 | m = input.to_s.match(/[0-9]+([\.-][0-9A-Za-z_]+)+/) 90 | if m 91 | v = m[0] 92 | if is_version?(v) 93 | as_obj && Gem::Version.new(v) || v 94 | end 95 | end 96 | end 97 | 98 | # Parse a version (as above), mapping it to the original 99 | # input in a passed cache (Hash). 100 | def scan_version_cache(input, cache, as_objs=false) 101 | if (v = scan_version(input, as_objs)) 102 | cache[v] = input 103 | end 104 | v 105 | end 106 | 107 | # Parse a version that may be just a single number. 108 | def scan_short_version(input) 109 | m = input.to_s.match(/[0-9]+([\.-][0-9A-Za-z_]+)?/) 110 | if m 111 | v = m[0] 112 | if is_version?(v) 113 | v 114 | end 115 | end 116 | end 117 | 118 | # Parse the first whole integer number. 119 | def scan_number(input) 120 | m = input.to_s.match(/[0-9]+/) 121 | m && m[0] 122 | end 123 | 124 | # Given a list of possible versions, meet a requirement. 125 | # Useful for matching partial versions pulled from the :tag field. 126 | def match_requirement(list, requirement) 127 | cache = Hash.new 128 | list = list.map do |y| 129 | begin 130 | scan_version_cache(y, cache, true) 131 | rescue ArgumentError 132 | nil 133 | end 134 | end.compact.sort.uniq.reverse 135 | gr = Gem::Requirement.new(requirement) 136 | list.each do |gv| 137 | if gr.satisfied_by?(gv) and (prereleases or not gv.prerelease?) 138 | return cache[gv] # return actual list entry, not just the parsed version 139 | end 140 | end 141 | nil 142 | end 143 | 144 | def templated(name) 145 | template = self.send(name) 146 | template = template.to_s.gsub(/%(?!\{)/, '%%') 147 | params = template_params 148 | counter = 0 149 | while params.size > counter 150 | counter = params.size 151 | begin 152 | return (template % params) 153 | rescue KeyError => e 154 | if (m = e.to_s.match(/key\{(.+)\}/)) 155 | params[m[1].to_sym] = 'UNDEFINED' 156 | end 157 | end 158 | end 159 | end 160 | 161 | ## 162 | ## =CALCULATED ATTRIBUTES= 163 | ## Can be overridden, but normally fend for themselves. 164 | ## 165 | 166 | def version=(v) 167 | super(self.std_version(v)) 168 | end 169 | 170 | def version_parsed 171 | parts = { 172 | 'major': '', 173 | 'minor': '', 174 | 'patch': '', 175 | 'build': '', 176 | 'prerelease': '', 177 | } 178 | begin 179 | if (v = Mixlib::Versioning.parse version) 180 | parts['major'] = v.major 181 | parts['minor'] = v.minor 182 | parts['patch'] = v.patch 183 | parts['build'] = v.build || v.prerelease.sub(/^[a-z]+/, '') 184 | parts['prerelease'] = v.prerelease 185 | end 186 | rescue 187 | # Failed to parse version. 188 | end 189 | parts 190 | end 191 | 192 | def version_segments 193 | return [] unless version 194 | version.gsub(/[^0-9]+/, '-').split('-') 195 | end 196 | 197 | def api_data 198 | external_links = [] 199 | Nokogiri::HTML("#{templated(:external_links)}").css('a').each do |link| 200 | external_links << { 201 | name: link.inner_text.strip, 202 | href: link['href'] 203 | } 204 | end 205 | 206 | { 207 | name: name, 208 | tag: tag, 209 | version: version, 210 | version_parsed: version_parsed, 211 | version_segments: version_segments, 212 | version_updated: version_date, 213 | version_checked: updated_at.strftime('%Y-%m-%d'), 214 | downloads: downloads, 215 | external_links: external_links, 216 | command_samples: command_samples, 217 | catalog_type: type, 218 | api_revision: 20170202 219 | }.deep_stringify_keys 220 | end 221 | 222 | def template_params 223 | params = ::CatalogEntry.all.map { |y| [y.to_param, y.version] }.to_h 224 | params.merge({ 225 | name: name, 226 | tag: tag, 227 | tag_version: scan_version(tag), 228 | tag_major: scan_number(tag), 229 | version: version, 230 | }).symbolize_keys 231 | end 232 | 233 | def self.template_help 234 | '%{name} %{tag} %{tag_version} %{tag_major} %{NAME:TAG}' 235 | end 236 | 237 | def self.model_help 238 | I18n.t 'admin.help.models.catalog_entry' 239 | end 240 | 241 | ## 242 | ## =MAIN FUNCTIONS= 243 | ## 244 | 245 | def label 246 | if tag == 'latest' 247 | name 248 | elsif tag and name and tag.to_s.index(name) === 0 249 | tag 250 | else 251 | "#{name}:#{tag}" 252 | end 253 | end 254 | 255 | def to_param 256 | "#{name}:#{tag}" 257 | end 258 | 259 | def visible 260 | not hidden 261 | end 262 | 263 | def refresh! 264 | begin 265 | v = check_remote_version 266 | unless v.kind_of?(FalseClass) 267 | if (v.kind_of?(String) and not v.empty?) or (v.kind_of?(Hash) and not v[:version].to_s.empty?) 268 | v0 = version.presence 269 | v1 = std_version(v.kind_of?(Hash) && v[:version] || v) 270 | 271 | CatalogEntry.transaction do 272 | self.refreshed_at = DateTime.now 273 | 274 | if v0 != v1 275 | self.version_date = DateTime.now 276 | unless self.no_log 277 | CatalogLogEntry.create!({ 278 | catalog_entry: self, 279 | version_from: v0, 280 | version_to: v1 281 | }) 282 | end 283 | end 284 | 285 | if v.kind_of? Hash 286 | v.delete(:version) 287 | v.each do |k,v| 288 | self.send("#{k}=".to_sym, v) 289 | end 290 | end 291 | self.version = v1 292 | self.last_error = nil 293 | 294 | save! 295 | end 296 | 297 | else 298 | raise 'Remote failed to return new version.' 299 | end 300 | end 301 | rescue => e 302 | reload 303 | self.last_error = e.message 304 | ::Raven.capture_exception(e) 305 | save 306 | raise e 307 | end 308 | end 309 | 310 | def self.reload_defaults! 311 | if (self == ::CatalogEntry) 312 | ::CatalogEntry.descendants.each do |klass| 313 | klass.reload_defaults! 314 | end 315 | end 316 | end 317 | 318 | def self.autorefresh? 319 | ENV['REFRESH_ENABLED'] 320 | end 321 | 322 | def self.autorefresh_interval 323 | ENV['REFRESH_INTERVAL'] || '1h' 324 | end 325 | 326 | protected 327 | 328 | def std_version(v) 329 | if v.presence 330 | v.to_s.sub(/\Av(?=[0-9])/, '') 331 | end 332 | end 333 | 334 | after_create do 335 | links = default_links 336 | if links.any? 337 | self.external_links = links.join("\n") + "\n" + external_links.to_s 338 | save! 339 | end 340 | if CatalogEntry.autorefresh? 341 | begin 342 | refresh! 343 | rescue 344 | # Ignore refresh errors on create. 345 | end 346 | end 347 | end 348 | 349 | def self.configure_admin(klass) 350 | klass.rails_admin do 351 | label klass.to_s.demodulize.titleize 352 | navigation_label I18n.t 'app.nav.catalog' 353 | object_label_method do 354 | :label 355 | end 356 | clone_config do 357 | custom_method :admin_clone 358 | end 359 | list do 360 | sort_by :name 361 | field :name do 362 | sortable 'name, tag' 363 | sort_reverse false 364 | pretty_value do 365 | v = bindings[:view] 366 | o = bindings[:object] 367 | am = ::RailsAdmin::AbstractModel.new(o.class) 368 | v.link_to(value, v.url_for(action: :edit, model_name: am.to_param, id: o.id), class: 'pjax').html_safe 369 | end 370 | end 371 | field :tag do 372 | pretty_value do 373 | v = bindings[:view] 374 | o = bindings[:object] 375 | am = ::RailsAdmin::AbstractModel.new(o.class) 376 | v.link_to(value, v.url_for(action: :edit, model_name: am.to_param, id: o.id), class: 'pjax').html_safe 377 | end 378 | end 379 | field :version 380 | field :type 381 | field :version_date 382 | field :last_error 383 | field :hidden, :boolean do 384 | hide 385 | filterable true 386 | end 387 | end 388 | create do 389 | group :default do 390 | field :name 391 | field :tag do 392 | default_value 'latest' 393 | end 394 | end 395 | group :more do 396 | active false 397 | label 'More Options' 398 | field :prereleases do 399 | visible do 400 | not bindings[:object].kind_of?(::CatalogEntry) 401 | end 402 | end 403 | field :version do 404 | visible do 405 | bindings[:object].kind_of?(::CatalogEntry) 406 | end 407 | end 408 | field :version_date do 409 | visible do 410 | bindings[:object].kind_of?(::CatalogEntry) 411 | end 412 | end 413 | field :type do 414 | read_only do 415 | not bindings[:object].kind_of?(::CatalogEntry) 416 | end 417 | default_value 'CatalogEntry' 418 | help '' 419 | end 420 | field :no_log do 421 | help "Don't post version changes to catalog log" 422 | end 423 | field :hidden 424 | field :external_links do 425 | help 'HTML links (one per line). Type may also auto-add links on create.' 426 | end 427 | field :description 428 | end 429 | end 430 | edit do 431 | group :default do 432 | field :name 433 | field :tag 434 | end 435 | group :advanced do 436 | active true 437 | label 'Advanced' 438 | field :type do 439 | read_only true 440 | help '' 441 | end 442 | field :prereleases 443 | field :version 444 | field :version_date 445 | field :no_log do 446 | help "Don't post version changes to catalog log" 447 | end 448 | field :hidden 449 | field :external_links do 450 | help 'HTML links (one per line) — Vars: ' + ::CatalogEntry.template_help 451 | end 452 | field :description 453 | end 454 | end 455 | end 456 | end 457 | 458 | def admin_clone 459 | self.dup.tap do |entry| 460 | entry.tag = '' 461 | entry.external_links = '' 462 | entry.version = '' 463 | entry.version_date = '' 464 | entry.last_error = '' 465 | end 466 | end 467 | 468 | def self.inherited(subclass) 469 | super(subclass) 470 | subclass.configure_admin(subclass) 471 | end 472 | 473 | configure_admin(self) 474 | end 475 | -------------------------------------------------------------------------------- /app/models/catalog_log_entry.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_log_entries 4 | # 5 | # id :integer not null, primary key 6 | # catalog_entry_id :integer not null 7 | # version_from :string 8 | # version_to :string 9 | # webhook_triggered :boolean default(FALSE) 10 | # created_at :datetime not null 11 | # updated_at :datetime not null 12 | # 13 | # Indexes 14 | # 15 | # index_catalog_log_entries_on_catalog_entry_id (catalog_entry_id) 16 | # 17 | 18 | class CatalogLogEntry < ApplicationRecord 19 | validates_presence_of :catalog_entry 20 | 21 | belongs_to :catalog_entry 22 | 23 | rails_admin do 24 | list do 25 | sort_by :created_at 26 | field :created_at 27 | field :catalog_entry 28 | field :version_from 29 | field :version_to 30 | field :webhook_triggered 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/models/catalog_webhook.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_webhooks 4 | # 5 | # id :integer not null, primary key 6 | # catalog_entry_id :integer not null 7 | # url :string not null 8 | # description :string not null 9 | # last_triggered :datetime 10 | # last_error :string 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # 14 | # Indexes 15 | # 16 | # index_catalog_webhooks_on_catalog_entry_id (catalog_entry_id) 17 | # 18 | 19 | class CatalogWebhook < ApplicationRecord 20 | validates_presence_of :catalog_entry, :url, :command, :description 21 | 22 | belongs_to :catalog_entry 23 | 24 | def trigger! 25 | self.last_triggered = DateTime.now 26 | self.last_error = nil 27 | begin 28 | cmd = self.command.to_s.gsub(/%(?!\{)/, '%%') % catalog_entry.template_params.merge({url: url}) 29 | out = `#{cmd} 2>&1` 30 | if $? != 0 31 | self.last_error = out 32 | end 33 | rescue => e 34 | self.last_error = e.message 35 | end 36 | save! 37 | end 38 | 39 | def self.model_help 40 | I18n.t 'admin.help.models.catalog_webhook' 41 | end 42 | 43 | rails_admin do 44 | navigation_label I18n.t 'app.nav.catalog' 45 | list do 46 | sort_by :catalog_entry 47 | field :catalog_entry do 48 | sortable 'catalog_entries.name, catalog_entries.tag' 49 | sort_reverse false 50 | end 51 | field :description 52 | field :last_triggered 53 | field :last_error 54 | end 55 | create do 56 | field :catalog_entry 57 | field :url 58 | field :description 59 | field :command do 60 | default_value 'curl -f -sS -X POST %{url}' 61 | end 62 | end 63 | edit do 64 | field :catalog_entry 65 | field :url 66 | field :description 67 | field :command 68 | field :last_triggered 69 | field :last_error 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/group.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: groups 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # 8 | 9 | class Group < ApplicationRecord 10 | validates :name, presence: true, uniqueness: true 11 | 12 | has_many :instances, dependent: :destroy 13 | 14 | def to_param 15 | name 16 | end 17 | 18 | def self.model_help 19 | I18n.t 'admin.help.models.group' 20 | end 21 | 22 | rails_admin do 23 | navigation_label I18n.t 'app.nav.groups' 24 | list do 25 | sort_by :name 26 | field :name do 27 | sort_reverse false 28 | end 29 | end 30 | create do 31 | field :name do 32 | help 'Please enter a short title, id or code for your project group.' 33 | end 34 | end 35 | edit do 36 | field :name 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/models/instance.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: instances 4 | # 5 | # id :integer not null, primary key 6 | # group_id :integer not null 7 | # catalog_entry_id :integer not null 8 | # description :string 9 | # version :string 10 | # created_at :datetime not null 11 | # updated_at :datetime not null 12 | # 13 | # Indexes 14 | # 15 | # index_instances_on_catalog_entry_id (catalog_entry_id) 16 | # index_instances_on_group_id (group_id) 17 | # 18 | 19 | class Instance < ActiveRecord::Base 20 | validates_presence_of :catalog_entry, :group 21 | before_validation :use_latest_version 22 | 23 | belongs_to :group 24 | belongs_to :catalog_entry 25 | 26 | def group_enum 27 | Instance.select(:group).distinct.map {|x| [x.group, x.group]}.sort 28 | end 29 | 30 | def name 31 | catalog_entry.try(:name) 32 | end 33 | 34 | def tag 35 | catalog_entry.try(:tag) 36 | end 37 | 38 | def update!(v=nil) 39 | if v 40 | raise "Invalid version: #{v}" unless is_version?(v) 41 | self.version = v 42 | else 43 | self.version = catalog_entry.version 44 | end 45 | save! 46 | end 47 | 48 | def up_to_date? 49 | self.version == catalog_entry.version 50 | end 51 | 52 | def latest_version 53 | catalog_entry.version 54 | end 55 | 56 | after_create do 57 | update! if version == 'latest' 58 | end 59 | 60 | def self.model_help 61 | I18n.t 'admin.help.models.instance' 62 | end 63 | 64 | protected 65 | 66 | def is_version?(value) 67 | Gem::Version.correct?(value) != nil 68 | end 69 | 70 | def use_latest_version 71 | if version === true and catalog_entry 72 | self.version = catalog_entry.version 73 | end 74 | end 75 | 76 | rails_admin do 77 | navigation_label I18n.t 'app.nav.groups' 78 | list do 79 | sort_by :group 80 | field :group do 81 | sortable 'groups.name, catalog_entries.name, catalog_entries.tag' 82 | sort_reverse false 83 | end 84 | field :catalog_entry 85 | field :version 86 | field :description 87 | end 88 | create do 89 | field :group 90 | field :catalog_entry 91 | field :version do 92 | help 'Optional. Current version of this instance. Enter "latest" for most recent version.' 93 | end 94 | field :description 95 | end 96 | edit do 97 | field :group 98 | field :catalog_entry 99 | field :version do 100 | help 'Optional. Current version of this instance. Enter "latest" for most recent version.' 101 | end 102 | field :description 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /app/views/catalog/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'shared/demo_jumbo' %> 2 |
3 |

4 | 6 | Catalog 7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% @catalog.each do |entry| %> 20 | 21 | 22 | 23 | 24 | 30 | 33 | 34 | <% end %> 35 | 36 |
NameTagVersionExternal LinksLast Updated
<%= entry.name %><%= entry.tag %><%= entry.version %> 25 | <% unless entry.description.to_s.empty? %> 26 | 27 | <% end %> 28 | <%= render partial: 'shared/entry_links', locals: {entry: entry} %> 29 | 31 | <%= entry.version_date %> 32 |
37 | 38 | 53 | -------------------------------------------------------------------------------- /app/views/catalog/view.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

3 | <%= @entry.label %> 4 |

5 | <% unless @entry.description.to_s.empty? %> 6 |

7 | <%= @entry.description %> 8 |

9 | <% end %> 10 |
11 | 12 |
13 | 19 | <% if @has_links %> 20 |
21 | 22 |   23 | <%= render partial: 'shared/entry_links', locals: {entry: @entry} %> 24 |
25 | <% end %> 26 |
27 | 28 |
29 |

Badges

30 |
31 |
32 |
Blue badges always display the latest version for the catalog entry
33 | <%= badge_snippet(@entry).html_safe %> 34 |

35 | <%= render 'shared/entry_badge_embed' %> 36 |

 

37 |
38 |
39 |
Green badges will turn red if the set version is no longer the latest
40 | <%= badge_snippet(@entry, 'html', @entry.version).html_safe %> 41 |

42 | <%= render partial: 'shared/entry_badge_embed', locals: {badge_version: @entry.version} %> 43 |

Modify the ?v= query parameter to change the set version.

44 |
45 |
46 | 47 |
48 |

JSON API Usage

49 |

50 | The following example shows how to access this entry as a JSON object. 51 | See the <%= link_to 'API Documentation', api_path %> for more detail, including how to 52 | return the value of a single field. 53 |

54 |
55 | <%= code_sample("curl -s #{catalog_view_api_url(format: 'json')}", theme: 'black') %> 56 |
57 |
<%= JSON.pretty_generate(@data) %>
58 | 59 |
60 |

Version History

61 | <%= render 'shared/entry_version_history' %> 62 | 63 | 77 | -------------------------------------------------------------------------------- /app/views/group/index.erb: -------------------------------------------------------------------------------- 1 |

<%= @group_name %>

2 |
3 | 4 | Input "latest" or "l" to set an instance to the most recent version. 5 |
6 | <%= form_tag() do %> 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <% @instances.each do |i| %> 18 | 19 | 25 | 26 | <% if i.up_to_date? %> 27 | 32 | <% else %> 33 | 43 | <% end %> 44 | 45 | <% end %> 46 | 47 |
Catalog EntryTagVersion
20 | <% unless i.up_to_date? %> 21 | 22 | <% end %> 23 | <%= i.name %> 24 | <%= i.tag %> 28 | <%= text_field_tag("ver_#{i.id}", i.version, class: 'form-control2') %> 29 |   30 | Up-To-Date 31 | 34 | <%= text_field_tag("ver_#{i.id}", i.version, class: 'form-control2') %> 35 |  <=  36 | <%= i.latest_version %> 37 | <% Nokogiri::HTML("#{i.catalog_entry.templated(:external_links)}").css('a').each do |link| 38 | link['target'] = '_blank' 39 | %> 40 | — <%= link.to_s.html_safe %> 41 | <% end %> 42 |
48 |
49 |
50 |
51 | 52 |
53 |
54 | <% end %> 55 | -------------------------------------------------------------------------------- /app/views/help/api.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | Latestver 4 | — 5 | API Usage 6 |
7 |
8 | 9 |
10 | The catalog may also be accessed using command-line tools, or through a simple HTTP API. 11 |
12 | 13 |

CLI Tools

14 | 15 |
 16 |   $ gem install latestver
 17 | 
 18 |   $ latestver get rails
 19 |   <%= @ex_rails_latest %>
 20 | 
 21 |   $ latestver get java:jdk8 -p downloads.rpm
 22 |   <%= @ex_java_download %>
 23 | 
 24 |   $ latestver --help         # for other commands
 25 | 
26 | 27 |
28 |

HTTP API

29 | 30 |
31 |

Entry Single Field

32 |

33 | If you need a specific value from the catalog entry, query it directly to avoid parsing. 34 | Refer to the JSON example below for field paths. 35 |

36 | 37 |
38 | <%= code_sample('/catalog-api//.txt?p=', {icon: 'plug', label: 'GET', theme: 'blue'}) %> 39 |
40 | 41 |
42 | 43 | <%= code_sample("curl -s #{request.base_url}/catalog-api//.txt?p=", theme: 'black') %> 44 |
45 | 46 |
<%= @ex_java_download %>
47 | 48 |
49 |

Entry JSON

50 |

Tip: Click an entry in the Catalog to see its specific output.

51 | 52 |
53 | <%= code_sample('/catalog-api//.json', {icon: 'plug', label: 'GET', theme: 'blue'}) %> 54 |
55 | 56 |
57 | 58 | <%= code_sample("curl -s #{request.base_url}/catalog-api//.json", theme: 'black') %> 59 |
60 | 61 | <% if @ex_java_json %> 62 |
<%= @ex_java_json %>
63 | <% else %> 64 |
{
 65 |   "name": "java",
 66 |   "tag": "jdk8",
 67 |   "version": "8u111-b14",
 68 |   "version_parsed": {
 69 |     "major": "8",
 70 |     "minor": "111",
 71 |     "build": "14"
 72 |   },
 73 |   "version_segments": [
 74 |     "8",
 75 |     "111",
 76 |     "14"
 77 |   ],
 78 |   "version_updated": "2016-11-20",
 79 |   "version_checked": "2016-11-20",
 80 |   "downloads": {
 81 |     "rpm": "http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111-linux-x64.rpm",
 82 |     "tgz": "http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111-linux-x64.tar.gz",
 83 |     "dmg": "http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111-macosx-x64.dmg",
 84 |     "exe": "http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111-windows-x64.exe"
 85 |   },
 86 |   "external_links": [
 87 |     {
 88 |       "name": "Releases",
 89 |       "href": "http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html"
 90 |     }
 91 |   ],
 92 |   "command_samples": {
 93 |     "curl_download": "curl -LOH 'Cookie: oraclelicense=accept-securebackup-cookie' 'http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111-linux-x64.rpm'",
 94 |     "wget_download": "wget --no-check-certificate --no-cookies --header 'Cookie: oraclelicense=accept-securebackup-cookie' 'http://download.oracle.com/otn-pub/java/jdk/8u111-b14/jdk-8u111-linux-x64.rpm'"
 95 |   },
 96 |   "catalog_type": "Catalog::OracleJava",
 97 |   "api_revision": 20170202
 98 | }
99 | <% end %> 100 | -------------------------------------------------------------------------------- /app/views/help/index.html.erb: -------------------------------------------------------------------------------- 1 |

Frequently Asked Questions

2 | 3 |

Can I be notified when a Catalog item changes?

4 |

5 | The Latestver log features 6 | an RSS feed which can be used with the free service IFTTT to receive notifications 7 | or trigger automated actions.  Learn how. 8 |

9 |

10 | Privately deployed versions of Latestver features in-built webhooks.  See below. 11 |

12 | 13 |
14 |

How do I add a new Catalog item?

15 |

16 | This is a hosted edition of Latestver where the catalog 17 | items are curated.
You may request new entries by posting a 18 | message to the project's community — 19 | 21 |

22 |

23 | Alternatively... you can deploy your own copy of this software with 24 | Docker 25 | for a custom catalog, webhooks, project tracking, and admin access. 26 |

27 | 28 |
29 |

How does Latestver lookup versions?

30 |

31 | The catalog supports various mechanisms to monitor version updates... 32 |

33 |
    34 |
  • Git repository release tags
  • 35 |
  • RubyGems.org publications
  • 36 |
  • NPM registry entries
  • 37 |
  • Web-page pattern matching/scraping
  • 38 |
  • Ruby modules for custom entries, such as Oracle Java
  • 39 |
40 | 41 |
42 |

How can I deploy my own copy of Latestver?

43 |

44 | Please review the project's README on 45 | GitHub. 47 |

48 | 49 |
50 |

What extra features do privately deployed editions have?

51 | 68 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 23 | <%= t('app.nav.title') %> 24 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 25 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 26 | <%= csrf_meta_tags %> 27 | 28 | 29 | 30 | 78 | 79 |
80 |
81 | <% if instance_groups.any? %> 82 | 89 |
90 | <%= yield %> 91 |
92 | <% else %> 93 |
94 | <%= yield %> 95 |
96 | <% end %> 97 |
98 |
99 | 100 | <% if ENV['DEMO_MODE'] %> 101 | 102 | <% end %> 103 | 104 | <% if ENV['GA_TRACKING_ID'] %> 105 | 114 | <% end %> 115 | 116 | 117 | -------------------------------------------------------------------------------- /app/views/layouts/rails_admin/_navigation.html.haml: -------------------------------------------------------------------------------- 1 | .container-fluid 2 | .navbar-header 3 | %button.navbar-toggle.collapsed{ type: 'button', data: { toggle: 'collapse', target: '#secondary-navigation' } } 4 | %span.sr-only= t('admin.toggle_navigation') 5 | %span.icon-bar 6 | %span.icon-bar 7 | %span.icon-bar 8 | %a.navbar-brand.pjax{href: '/'} 9 | = _get_plugin_name[0] || 'Rails' 10 | %small= _get_plugin_name[1] || 'Admin' 11 | .collapse.navbar-collapse#secondary-navigation 12 | = render partial: 'layouts/rails_admin/secondary_navigation' 13 | -------------------------------------------------------------------------------- /app/views/layouts/rails_admin/pjax.html.haml: -------------------------------------------------------------------------------- 1 | :javascript 2 | $('.nav.nav-pills li.active').removeClass('active'); 3 | $('.nav.nav-pills li[data-model="#{@abstract_model.to_param}"]').addClass('active'); 4 | 5 | %title= "#{@abstract_model.try(:pretty_name) || @page_name} | #{[_get_plugin_name[0] || 'Rails', _get_plugin_name[1] || 'Admin'].join(' ')}" 6 | .page-header 7 | %h1= @page_name 8 | %p.page-description{style:"padding-left: 3px; text-align: justify;"}="#{@abstract_model.try(:model).try(:model_help)}".html_safe 9 | - flash && flash.each do |key, value| 10 | .alert.alert-dismissible{class: flash_alert_class(key)} 11 | %button.close{type: 'button', :'data-dismiss' => "alert"} × 12 | = value 13 | = breadcrumb 14 | %ul.nav.nav-tabs 15 | = menu_for((@abstract_model ? (@object.try(:persisted?) ? :member : :collection) : :root), @abstract_model, @object) 16 | = content_for :contextual_tabs 17 | = yield 18 | -------------------------------------------------------------------------------- /app/views/log/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | RSS 3 |  |  4 | <%= t('app.nav.refreshed') %><%= last_catalog_refresh %> 5 |
6 |

Catalog Update Log

7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% @catalog_log.each do |l| %> 20 | 21 | 22 | 24 | 25 | 26 | 29 | 30 | <% end %> 31 | 32 |
TimestampCatalog EntryVersion ToVersion FromLinks
<%= l.created_at %><%= l.catalog_entry.label %><%= l.version_to %><%= l.version_from || '—' %> 27 | <%= render partial: 'shared/entry_links', locals: {entry: l.catalog_entry} %> 28 |
33 |
34 | -------------------------------------------------------------------------------- /app/views/log/index.rss.builder: -------------------------------------------------------------------------------- 1 | #encoding: UTF-8 2 | 3 | xml.instruct! :xml, :version => '1.0' 4 | xml.rss :version => '2.0' do 5 | xml.channel do 6 | xml.title 'Latestver Update Log' 7 | xml.author 'BinaryBabel OSS' 8 | xml.link 'https://lv.binarybabel.org' 9 | xml.language 'en-us' 10 | 11 | @catalog_log.each do |l| 12 | xml.item do 13 | xml.title "#{l.catalog_entry.label} #{l.version_to}" 14 | xml.category l.catalog_entry.to_param 15 | xml.pubDate l.created_at.to_s(:rfc822) 16 | xml.link catalog_view_url(name: l.catalog_entry.name, tag: l.catalog_entry.tag) 17 | xml.description "#{l.catalog_entry.to_param} updated to version #{l.version_to} from #{l.version_from or 'unknown'}" 18 | xml.guid "clu-#{l.id}" 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/views/rails_admin/main/refresh_catalog.html.erb: -------------------------------------------------------------------------------- 1 | <% if params[:confirm] %> 2 |
<%= `rake catalog:refresh` %>
3 | Return to Catalog 4 | <% else %> 5 |

6 | This function manually refreshes the remote versions of all catalog entries. 7 |

8 |

9 | ( Production deployments handle this automatically as a scheduled task. ) 10 |

11 |
12 | Refresh Now 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/rails_admin/main/refresh_entry.html.erb: -------------------------------------------------------------------------------- 1 | <% if params[:confirm] %> 2 |
<%= `rake catalog:refresh[#{@object.id.to_i}]` %>
3 | Return to Catalog 4 | <% else %> 5 |

6 | This function manually refreshes the remote version of <%= @object.label %> 7 |

8 |

9 | ( Production deployments handle this automatically as a scheduled task. ) 10 |

11 |
12 | Refresh Now 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/rails_admin/main/reload_defaults.html.erb: -------------------------------------------------------------------------------- 1 | <% if params[:confirm] %> 2 | <% Object.const_get(@abstract_model.to_s).reload_defaults! %> 3 |

Success! Default entries reloaded.

4 | Continue 5 | <% else %> 6 |

7 | <% if @abstract_model.to_s == 'CatalogEntry' %> 8 | This function re-adds any missing default/factory/example catalog entries. 9 | <% else %> 10 | This function reloads the default catalog entries for <%= @abstract_model.to_s.demodulize.titleize %>. 11 | <% end %> 12 |

13 |

14 | ( It will not modify or remove existing entries. ) 15 |

16 |
17 | Confirm 18 | <% end %> 19 | -------------------------------------------------------------------------------- /app/views/rails_admin/main/trigger_webhook.erb: -------------------------------------------------------------------------------- 1 | <% if params[:confirm] %> 2 | <% @object.trigger! %> 3 |
<%= @object.last_error || 'SUCCESS' %>
4 | Return to Webhooks 5 | <% else %> 6 |

7 | This function manually triggers the webhook for <%= @object.description %> 8 |

9 |

10 | An HTTP action will be sent to <%= @object.url %> 11 |

12 |
13 | Test Now 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/shared/_demo_jumbo.erb: -------------------------------------------------------------------------------- 1 | <% if ENV['DEMO_MODE'] %> 2 |
3 |

4 | Latestver 5 | automatically monitors updates to dependencies... 6 |

7 |
8 |
9 |
10 | 31 |
32 | 48 |
49 | 50 | <% end %> 51 | -------------------------------------------------------------------------------- /app/views/shared/_entry_badge_embed.erb: -------------------------------------------------------------------------------- 1 | <% 2 | # Params 3 | entry ||= @entry 4 | badge_version ||= nil 5 | 6 | # Options 7 | badge_formats = { 8 | html: 'HTML', 9 | md: 'Markdown', 10 | } 11 | 12 | id_prefix = "ce_#{entry.id}_#{badge_version.to_s.gsub(/[^0-9A-Z]/i, '')}_" 13 | %> 14 | 20 |
21 | <% badge_formats.each do |fmt, title| %> 22 |
23 |
24 |
25 |
26 | <%= title %> 27 | <%= text_field_tag( 28 | id_prefix + fmt.to_s + '_input', 29 | badge_snippet(entry, fmt.to_s, badge_version), 30 | class: 'form-control') %> 31 | 32 | 37 | 38 |
39 |
40 |
41 |
42 | <% end %> 43 |
44 | -------------------------------------------------------------------------------- /app/views/shared/_entry_links.erb: -------------------------------------------------------------------------------- 1 | <% 2 | # Params 3 | entry ||= @entry 4 | %> 5 | 6 | <% Nokogiri::HTML("#{entry.templated(:external_links)}").css('a').each do |link| 7 | link['target'] = '_blank' 8 | %> 9 | <%= link.to_s.html_safe %>   10 | <% end %> 11 | -------------------------------------------------------------------------------- /app/views/shared/_entry_version_history.erb: -------------------------------------------------------------------------------- 1 | <% 2 | # Params 3 | entry ||= @entry 4 | %> 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% entry.catalog_log_entries.each do |l| %> 19 | 20 | 21 | 23 | 24 | 25 | 28 | 29 | <% end %> 30 | 31 |
TimestampCatalog EntryVersion ToVersion FromLinks
<%= l.created_at %><%= l.catalog_entry.label %><%= l.version_to %><%= l.version_from || '—' %> 26 | <%= render partial: 'shared/entry_links', locals: {entry: l.catalog_entry} %> 27 |
32 |
33 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) 11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } 12 | gem 'spring', match[1] 13 | require 'spring/binstub' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /bin/versioneer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'versioneer' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("versioneer", "versioneer") 18 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "latestver", 3 | "authors": [ 4 | "oss@binarybabel.org" 5 | ], 6 | "description": "", 7 | "main": "", 8 | "license": "MIT", 9 | "homepage": "", 10 | "private": true, 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "vendor/assets/bower_components", 16 | "test", 17 | "tests" 18 | ], 19 | "dependencies": { 20 | "noty": "^2.4.0", 21 | "datatables": "^1.10.13", 22 | "clipboard": "^1.5.16" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | #\ -p 3333 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | require_relative 'version' 10 | 11 | module Latestver 12 | class Application < Rails::Application 13 | # Settings in config/environments/* take precedence over those specified here. 14 | # Application configuration should go into files in config/initializers 15 | # -- all .rb files in that directory are automatically loaded. 16 | 17 | # Include libs from data directory. 18 | config.enable_dependency_loading = true 19 | config.autoload_paths << Rails.root.join('data', 'lib') 20 | config.eager_load_paths << Rails.root.join('data', 'lib') 21 | end 22 | end 23 | 24 | Raven.configure do |config| 25 | config.logger.level = ::Logger::WARN 26 | end 27 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/brakeman.ignore: -------------------------------------------------------------------------------- 1 | { 2 | "ignored_warnings": [ 3 | { 4 | "warning_type": "Command Injection", 5 | "warning_code": 14, 6 | "fingerprint": "0d6014c97e909f5885be4d77471ec5ff45fde65878b34d40a0dbcec3522c51f2", 7 | "check_name": "Execute", 8 | "message": "Possible command injection", 9 | "file": "app/models/catalog_webhook.rb", 10 | "line": 29, 11 | "link": "http://brakemanscanner.org/docs/warning_types/command_injection/", 12 | "code": "`#{(self.command.to_s.gsub(/%/, \"%%\") % catalog_entry.template_params.merge(:url => url))} 2>&1`", 13 | "render_path": null, 14 | "location": { 15 | "type": "method", 16 | "class": "CatalogWebhook", 17 | "method": "trigger!" 18 | }, 19 | "user_input": "(self.command.to_s.gsub(/%/, \"%%\") % catalog_entry.template_params.merge(:url => url))", 20 | "confidence": "Medium", 21 | "note": "Intentional/Acceptable risk vs reward Dockerized" 22 | }, 23 | { 24 | "warning_type": "Cross Site Scripting", 25 | "warning_code": 2, 26 | "fingerprint": "29e6dca47e965e13f917397ff12cbeb8c2451b4ddc8c67bf5e13000773a09d5c", 27 | "check_name": "CrossSiteScripting", 28 | "message": "Unescaped model attribute", 29 | "file": "app/views/catalog/view.html.erb", 30 | "line": 40, 31 | "link": "http://brakemanscanner.org/docs/warning_types/cross_site_scripting", 32 | "code": "badge_snippet(CatalogEntry.find_by!(:name => params[:name], :tag => params[:tag]), \"html\", CatalogEntry.find_by!(:name => params[:name], :tag => params[:tag]).version)", 33 | "render_path": [{"type":"controller","class":"CatalogController","method":"view","line":21,"file":"app/controllers/catalog_controller.rb"}], 34 | "location": { 35 | "type": "template", 36 | "template": "catalog/view" 37 | }, 38 | "user_input": "CatalogEntry.find_by!(:name => params[:name], :tag => params[:tag])", 39 | "confidence": "Weak", 40 | "note": "Snippet output safe" 41 | }, 42 | { 43 | "warning_type": "Command Injection", 44 | "warning_code": 14, 45 | "fingerprint": "3f74096866285c97753049ff79f759d0f0ca71a4261f4af0bcf83906f1238a94", 46 | "check_name": "Execute", 47 | "message": "Possible command injection", 48 | "file": "app/views/rails_admin/main/refresh_entry.html.erb", 49 | "line": 2, 50 | "link": "http://brakemanscanner.org/docs/warning_types/command_injection/", 51 | "code": "`rake catalog:refresh[#{@object.id.to_i}]`", 52 | "render_path": null, 53 | "location": { 54 | "type": "template", 55 | "template": "rails_admin/main/refresh_entry" 56 | }, 57 | "user_input": "@object.id.to_i", 58 | "confidence": "Medium", 59 | "note": "Integer inputs only." 60 | }, 61 | { 62 | "warning_type": "Cross Site Scripting", 63 | "warning_code": 2, 64 | "fingerprint": "eb7ba0dd989d6616ed7ed6e7d039bb8f514854b3b8c7efa0f02abf56c4ba32c3", 65 | "check_name": "CrossSiteScripting", 66 | "message": "Unescaped model attribute", 67 | "file": "app/views/catalog/view.html.erb", 68 | "line": 33, 69 | "link": "http://brakemanscanner.org/docs/warning_types/cross_site_scripting", 70 | "code": "badge_snippet(CatalogEntry.find_by!(:name => params[:name], :tag => params[:tag]))", 71 | "render_path": [{"type":"controller","class":"CatalogController","method":"view","line":21,"file":"app/controllers/catalog_controller.rb"}], 72 | "location": { 73 | "type": "template", 74 | "template": "catalog/view" 75 | }, 76 | "user_input": "CatalogEntry.find_by!(:name => params[:name], :tag => params[:tag])", 77 | "confidence": "Weak", 78 | "note": "Snippet output safe" 79 | } 80 | ], 81 | "updated": "2017-02-03 22:59:50 -0800", 82 | "brakeman_version": "3.5.0" 83 | } 84 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: data/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: data/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: data/production.sqlite3 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | # Debug mode disables concatenation and preprocessing of assets. 41 | # This option may cause significant delays in view rendering with a large 42 | # number of complex assets. 43 | config.assets.debug = true 44 | 45 | # Suppress logger output for asset requests. 46 | config.assets.quiet = true 47 | 48 | # Raises error for missing translations 49 | # config.action_view.raise_on_missing_translations = true 50 | 51 | # Use an evented file watcher to asynchronously detect changes in source code, 52 | # routes, locales, etc. This feature depends on the listen gem. 53 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker 54 | end 55 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Disable serving static files from the `/public` folder by default since 18 | # Apache or NGINX already handles this. 19 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 20 | config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{ENV['CACHE_CONTROL_MAX_AGE'] || '3600'}" } 21 | 22 | # Compress JavaScripts and CSS. 23 | config.assets.js_compressor = :uglifier 24 | # config.assets.css_compressor = :sass 25 | 26 | # Do not fallback to assets pipeline if a precompiled asset is missed. 27 | config.assets.compile = false 28 | 29 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 30 | 31 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 32 | # config.action_controller.asset_host = 'http://assets.example.com' 33 | 34 | # Specifies the header that your server uses for sending files. 35 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 36 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 37 | 38 | # Mount Action Cable outside main process or domain 39 | # config.action_cable.mount_path = nil 40 | # config.action_cable.url = 'wss://example.com/cable' 41 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 42 | 43 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 44 | # config.force_ssl = true 45 | 46 | # Use the lowest log level to ensure availability of diagnostic information 47 | # when problems arise. 48 | config.log_level = :debug 49 | 50 | # Prepend all log lines with the following tags. 51 | config.log_tags = [ :request_id ] 52 | 53 | # Use a different cache store in production. 54 | # config.cache_store = :mem_cache_store 55 | 56 | # Use a real queuing backend for Active Job (and separate queues per environment) 57 | # config.active_job.queue_adapter = :resque 58 | # config.active_job.queue_name_prefix = "latestver_#{Rails.env}" 59 | config.action_mailer.perform_caching = false 60 | 61 | # Ignore bad email addresses and do not raise email delivery errors. 62 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 63 | # config.action_mailer.raise_delivery_errors = false 64 | 65 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 66 | # the I18n.default_locale when a translation cannot be found). 67 | config.i18n.fallbacks = true 68 | 69 | # Send deprecation notices to registered listeners. 70 | config.active_support.deprecation = :notify 71 | 72 | # Use default logging formatter so that PID and timestamp are not suppressed. 73 | config.log_formatter = ::Logger::Formatter.new 74 | 75 | # Use a different logger for distributed setups. 76 | # require 'syslog/logger' 77 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 78 | 79 | if ENV["RAILS_LOG_TO_STDOUT"].present? 80 | logger = ActiveSupport::Logger.new(STDOUT) 81 | logger.formatter = config.log_formatter 82 | config.logger = ActiveSupport::TaggedLogging.new(logger) 83 | end 84 | 85 | # Do not dump schema after migrations. 86 | config.active_record.dump_schema_after_migration = false 87 | end 88 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/config.rb: -------------------------------------------------------------------------------- 1 | Config.setup do |config| 2 | config.const_name = "Settings" 3 | end -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Once upgraded flip defaults one by one to migrate to the new default. 6 | # 7 | # Read the Rails 5.0 release notes for more info on each option. 8 | 9 | # Enable per-form CSRF tokens. Previous versions had false. 10 | Rails.application.config.action_controller.per_form_csrf_tokens = false 11 | 12 | # Enable origin-checking CSRF mitigation. Previous versions had false. 13 | Rails.application.config.action_controller.forgery_protection_origin_check = false 14 | 15 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 16 | # Previous versions had false. 17 | ActiveSupport.to_time_preserves_timezone = false 18 | 19 | # Require `belongs_to` associations by default. Previous versions had false. 20 | Rails.application.config.active_record.belongs_to_required_by_default = false 21 | 22 | # Do not halt callback chains when a callback returns false. Previous versions had true. 23 | ActiveSupport.halt_callback_chains_on_return_false = true 24 | -------------------------------------------------------------------------------- /config/initializers/rails_admin.rb: -------------------------------------------------------------------------------- 1 | require_relative Rails.root.join('lib', 'rails_admin', 'reload_defaults.rb') 2 | RailsAdmin::Config::Actions.register(RailsAdmin::ReloadDefaults) 3 | 4 | require_relative Rails.root.join('lib', 'rails_admin', 'refresh_catalog.rb') 5 | RailsAdmin::Config::Actions.register(RailsAdmin::RefreshCatalog) 6 | 7 | require_relative Rails.root.join('lib', 'rails_admin', 'refresh_entry.rb') 8 | RailsAdmin::Config::Actions.register(RailsAdmin::RefreshEntry) 9 | 10 | require_relative Rails.root.join('lib', 'rails_admin', 'trigger_webhook.rb') 11 | RailsAdmin::Config::Actions.register(RailsAdmin::TriggerWebhook) 12 | 13 | RailsAdmin.config do |config| 14 | 15 | ### Popular gems integration 16 | 17 | ## == Devise == 18 | # config.authenticate_with do 19 | # warden.authenticate! scope: :admin 20 | # end 21 | # config.current_user_method(&:current_user) 22 | 23 | ## == Cancan == 24 | # config.authorize_with :cancan 25 | 26 | ## == Pundit == 27 | # config.authorize_with :pundit 28 | 29 | ## == PaperTrail == 30 | # config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0 31 | 32 | ### More at https://github.com/sferik/rails_admin/wiki/Base-configuration 33 | 34 | config.authorize_with do 35 | unless Rails.application.secrets.admin_pass.to_s.empty? 36 | authenticate_or_request_with_http_basic('Login required') do |user, pass| 37 | user == Rails.application.secrets.admin_user && pass == Rails.application.secrets.admin_pass 38 | end 39 | end 40 | end 41 | 42 | config.browser_validations = false 43 | 44 | config.actions do 45 | dashboard # mandatory 46 | index # mandatory 47 | new do 48 | except ['CatalogLogEntry'] 49 | end 50 | # export 51 | bulk_delete 52 | show 53 | edit do 54 | except ['CatalogLogEntry'] 55 | end 56 | clone do 57 | except ['Group', 'Instance', 'CatalogLogEntry'] 58 | end 59 | delete 60 | show_in_app 61 | 62 | refresh_catalog 63 | reload_defaults 64 | refresh_entry 65 | trigger_webhook 66 | end 67 | end 68 | 69 | module RailsAdmin 70 | module Config 71 | module Fields 72 | class Association < RailsAdmin::Config::Fields::Base 73 | register_instance_option :pretty_value do 74 | v = bindings[:view] 75 | [value].flatten.select(&:present?).collect do |associated| 76 | amc = polymorphic? ? RailsAdmin.config(associated) : associated_model_config # perf optimization for non-polymorphic associations 77 | am = amc.abstract_model 78 | wording = associated.send(amc.object_label_method) 79 | can_see = false #!am.embedded? && (show_action = v.action(:show, am, associated)) 80 | can_see ? v.link_to(wording, v.url_for(action: show_action.action_name, model_name: am.to_param, id: associated.id), class: 'pjax') : ERB::Util.html_escape(wording) 81 | end.to_sentence.html_safe 82 | end 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /config/initializers/scheduler.rb: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rufus-scheduler' 3 | 4 | Rake::Task.clear 5 | Rails.application.load_tasks 6 | 7 | scheduler = Rufus::Scheduler::singleton 8 | 9 | if CatalogEntry.autorefresh? 10 | unless defined?(Rails::Console) 11 | puts "Autorefresh enabled... (#{CatalogEntry.autorefresh_interval})" 12 | 13 | scheduler.in '10s' do 14 | Rake::Task['catalog:refresh'].invoke 15 | Rake::Task['webhooks:trigger'].invoke 16 | end 17 | 18 | scheduler.every (CatalogEntry.autorefresh_interval) do 19 | Rake::Task['catalog:refresh'].execute 20 | Rake::Task['webhooks:trigger'].execute 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_latestver_session' 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | activerecord: 24 | models: 25 | catalog_log_entry: 26 | one: 'Log Entry' 27 | other: 'Log Entries' 28 | catalog_webhook: 29 | one: 'Catalog Webhook' 30 | other: 'Catalog Webhooks' 31 | catalog/web_diff: 32 | one: 'Watcher' 33 | other: 'Watchers' 34 | # group: 35 | # one: 'Project' 36 | # other: 'Projects' 37 | # attributes: 38 | # instance: 39 | # group: 'Project' 40 | app: 41 | nav: 42 | title: 'Latestver' 43 | toggle: 'Toggle navigation' 44 | refreshed: 'Refreshed: ' 45 | admin: 'Admin' 46 | api: 'API' 47 | catalog: 'Catalog' 48 | groups: 'Project Management' 49 | help: 'Help' 50 | log: 'Log' 51 | github: 'GitHub' 52 | admin: 53 | actions: 54 | dashboard: 55 | title: 'Administration' 56 | reload_defaults: 57 | menu: 'Reload Defaults' 58 | breadcrumb: 'Reload Defaults' 59 | title: 'Reload Default Catalog Entries' 60 | refresh_catalog: 61 | menu: 'Refresh Catalog' 62 | breadcrumb: 'Refresh Catalog' 63 | title: 'Refresh Catalog Entries' 64 | refresh_entry: 65 | menu: 'Refresh Entry' 66 | breadcrumb: 'Refresh Entry' 67 | title: 'Refresh Entry' 68 | trigger_webhook: 69 | menu: 'Test Webhook' 70 | breadcrumb: 'Test Webhook' 71 | title: 'Test Webhook' 72 | misc: 73 | refresh: "" 74 | navigation: 'System' 75 | help: 76 | models: 77 | catalog_entry: > 78 | The Latestver Catalog configures which external dependencies you want to track. 79 |
80 | Entries can be displayed directly on the frontend, or used within specific project groups. 81 | catalog_webhook: > 82 | Webhooks are triggered after a catalog refresh updates an entry to a newer version. 83 |
84 | Currently supporting generic HTTP POST or invoking custom CURL commands. 85 | group: > 86 | Groups bundle together a collection of catalog entries and track 87 | the deployed versions. 88 |
89 | The frontend displays groups in the sidebar. When selected the catalog 90 | is filtered to the group, and highlighted to show which instances are out-of-date. 91 | instance: > 92 | Instances record the deployed version of catalog entries in a project group. 93 |
94 | Once added the instance versions may be updated from the frontend interface. 95 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum, this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests, default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3333 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # The code in the `on_worker_boot` will be called if you are using 36 | # clustered mode by specifying a number of `workers`. After each worker 37 | # process is booted this block will be run, if you are using `preload_app!` 38 | # option you will want to use this block to reconnect to any threads 39 | # or connections that may have been created at application boot, Ruby 40 | # cannot share connections between processes. 41 | # 42 | # on_worker_boot do 43 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 44 | # end 45 | 46 | # Allow puma to be restarted by `rails restart` command. 47 | plugin :tmp_restart 48 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | mount RailsAdmin::Engine => '/admin', as: 'rails_admin' 3 | 4 | root 'dashboard#index' 5 | 6 | get 'catalog' => 'catalog#index', :as => 'catalog' 7 | get 'catalog/:name/:tag' => 'catalog#view', :as => 'catalog_view', 8 | :name => /[a-z0-9\._-]+?/i, :tag => /[a-z0-9\._-]+?/i, :format => /html/ 9 | get 'catalog-api/:name/:tag' => 'catalog#view', :as => 'catalog_view_api', 10 | :name => /[a-z0-9\._-]+?/i, :tag => /[a-z0-9\._-]+?/i, :format => /txt|json|svg/ 11 | 12 | get 'group/:group' => 'group#index', :as => :group 13 | post 'group/:group' => 'group#update', :as => :group_update 14 | 15 | get 'log' => 'log#index', :as => 'log', :format => /html|rss/ 16 | get 'help' => 'help#index', :as => 'help' 17 | get 'api' => 'help#api', :as => 'api' 18 | get 'ver' => 'help#version', :as => nil 19 | end 20 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: cb89a9271e1aa3c07a97b1853c73a65400825bbc48981bf11e26e54476d1bbcacd6961899459e5850c97c58f3022941d9f5979596562b9e04306885dfcd325df 15 | admin_user: admin 16 | admin_pass: 17 | 18 | test: 19 | secret_key_base: d12fc89ca092ddfa4a388c53600fc4cdcab0922d8d3497d3134806b35ffba9c924e5b5221428a63805d2fbb8b093a988de22ce43b48bd9fdcf81c250a75ed807 20 | 21 | # Do not keep production secrets in the repository, 22 | # instead read values from the environment. 23 | production: 24 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] || "e13f65b73698e122653d6b59fa7c2b2277702854700f569e0b019888ed8bce995da88e4bae3f612cf465aafdcafb33bc12f9654c4938eb97ef81de4556950387" %> 25 | admin_user: <%= ENV["ADMIN_USER"] || "admin" %> 26 | admin_pass: <%= ENV["ADMIN_PASS"] %> 27 | -------------------------------------------------------------------------------- /config/settings.yml: -------------------------------------------------------------------------------- 1 | branding: 2 | changelog: https://github.com/binarybabel/latestver/blob/master/CHANGELOG.md 3 | docker: https://hub.docker.com/r/binarybabel/latestver/ 4 | github: https://github.com/binarybabel/latestver 5 | gitter: 6 | url: https://gitter.im/binarybabel/Latestver 7 | badge: https://img.shields.io/gitter/room/binarybabel/Latestver.svg?style=flat-square 8 | ifttt: https://github.com/binarybabel/latestver/wiki/IFTTT-Notifications 9 | 10 | -------------------------------------------------------------------------------- /config/settings/development.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/config/settings/development.yml -------------------------------------------------------------------------------- /config/settings/production.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/config/settings/production.yml -------------------------------------------------------------------------------- /config/settings/test.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/config/settings/test.yml -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /config/version.rb: -------------------------------------------------------------------------------- 1 | require 'versioneer' 2 | 3 | module Latestver 4 | RELEASE = '1.7' 5 | begin 6 | VERSION = Versioneer::Config.new(File.expand_path('../../', __FILE__)).to_s 7 | rescue Versioneer::InvalidRepoError 8 | VERSION = self::RELEASE 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /data/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/data/.keep -------------------------------------------------------------------------------- /db/migrate/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/db/migrate/.keep -------------------------------------------------------------------------------- /db/migrate/20161122073159_create_catalog_webhooks.rb: -------------------------------------------------------------------------------- 1 | class CreateCatalogWebhooks < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :catalog_webhooks do |t| 4 | t.references :catalog_entry, foreign_key: true, null: false 5 | t.string :url, null: false 6 | t.string :description, null: false 7 | t.datetime :last_triggered 8 | t.string :last_error 9 | 10 | t.timestamps 11 | end 12 | add_column :catalog_log_entries, :webhook_triggered, :boolean, :default => false 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20161122210533_create_groups.rb: -------------------------------------------------------------------------------- 1 | class CreateGroups < ActiveRecord::Migration[5.0] 2 | def change 3 | drop_table :instances 4 | create_table :groups do |t| 5 | t.string :name, null: false 6 | end 7 | create_table :instances do |t| 8 | t.integer "group_id", null: false 9 | t.integer "catalog_entry_id", null: false 10 | t.string "description" 11 | t.string "version" 12 | t.datetime "created_at", null: false 13 | t.datetime "updated_at", null: false 14 | t.index ["group_id"], name: "index_instances_on_group_id" 15 | t.index ["catalog_entry_id"], name: "index_instances_on_catalog_entry_id" 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20161126024305_add_no_log_to_catalog_entries.rb: -------------------------------------------------------------------------------- 1 | class AddNoLogToCatalogEntries < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :catalog_entries, :no_log, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161126045910_add_hidden_to_catalog_entries.rb: -------------------------------------------------------------------------------- 1 | class AddHiddenToCatalogEntries < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :catalog_entries, :hidden, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170114014452_add_command_option_to_catalog_webhooks.rb: -------------------------------------------------------------------------------- 1 | class AddCommandOptionToCatalogWebhooks < ActiveRecord::Migration[5.0] 2 | def up 3 | add_column :catalog_webhooks, :command, :string 4 | CatalogWebhook.update_all(command: 'curl -f -sS -X POST %{url}') 5 | end 6 | 7 | def down 8 | remove_column :catalog_webhooks, :command 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20170123064208_add_description_to_catalog_entries.rb: -------------------------------------------------------------------------------- 1 | class AddDescriptionToCatalogEntries < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :catalog_entries, :description, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20170123064208) do 14 | 15 | create_table "catalog_entries", force: :cascade do |t| 16 | t.string "name", null: false 17 | t.string "type", null: false 18 | t.string "tag", null: false 19 | t.string "version" 20 | t.date "version_date" 21 | t.boolean "prereleases", default: false 22 | t.text "external_links" 23 | t.text "data" 24 | t.datetime "refreshed_at" 25 | t.string "last_error" 26 | t.string "description" 27 | t.boolean "no_log", default: false 28 | t.boolean "hidden", default: false 29 | t.datetime "created_at", null: false 30 | t.datetime "updated_at", null: false 31 | end 32 | 33 | create_table "catalog_log_entries", force: :cascade do |t| 34 | t.integer "catalog_entry_id", null: false 35 | t.string "version_from" 36 | t.string "version_to" 37 | t.boolean "webhook_triggered", default: false 38 | t.datetime "created_at", null: false 39 | t.datetime "updated_at", null: false 40 | t.index ["catalog_entry_id"], name: "index_catalog_log_entries_on_catalog_entry_id" 41 | end 42 | 43 | create_table "catalog_webhooks", force: :cascade do |t| 44 | t.integer "catalog_entry_id", null: false 45 | t.string "url", null: false 46 | t.string "command", null: false 47 | t.string "description", null: false 48 | t.datetime "last_triggered" 49 | t.string "last_error" 50 | t.datetime "created_at", null: false 51 | t.datetime "updated_at", null: false 52 | t.index ["catalog_entry_id"], name: "index_catalog_webhooks_on_catalog_entry_id" 53 | end 54 | 55 | create_table "groups", force: :cascade do |t| 56 | t.string "name", null: false 57 | end 58 | 59 | create_table "instances", force: :cascade do |t| 60 | t.integer "group_id", null: false 61 | t.integer "catalog_entry_id", null: false 62 | t.string "description" 63 | t.string "version" 64 | t.datetime "created_at", null: false 65 | t.datetime "updated_at", null: false 66 | t.index ["catalog_entry_id"], name: "index_instances_on_catalog_entry_id" 67 | t.index ["group_id"], name: "index_instances_on_group_id" 68 | end 69 | 70 | end 71 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | 9 | Rails.application.eager_load! 10 | ::CatalogEntry.reload_defaults! 11 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | app: 4 | build: . 5 | volumes: 6 | - .:/app/data 7 | ports: 8 | - "3333:3333" 9 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/lib/assets/.keep -------------------------------------------------------------------------------- /lib/rails_admin/refresh_catalog.rb: -------------------------------------------------------------------------------- 1 | module RailsAdmin 2 | class RefreshCatalog < RailsAdmin::Config::Actions::Base 3 | register_instance_option :visible? do 4 | bindings[:abstract_model].to_s == 'CatalogEntry' 5 | end 6 | 7 | register_instance_option :collection do 8 | true 9 | end 10 | 11 | register_instance_option :link_icon do 12 | 'icon-refresh' 13 | end 14 | 15 | register_instance_option :pjax? do 16 | false 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/rails_admin/refresh_entry.rb: -------------------------------------------------------------------------------- 1 | module RailsAdmin 2 | class RefreshEntry < RailsAdmin::Config::Actions::Base 3 | register_instance_option :visible? do 4 | bindings[:object].respond_to? 'refresh!' 5 | end 6 | 7 | register_instance_option :member do 8 | true 9 | end 10 | 11 | register_instance_option :link_icon do 12 | 'icon-refresh' 13 | end 14 | 15 | register_instance_option :pjax? do 16 | false 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/rails_admin/reload_defaults.rb: -------------------------------------------------------------------------------- 1 | module RailsAdmin 2 | class ReloadDefaults < RailsAdmin::Config::Actions::Base 3 | register_instance_option :visible? do 4 | Object.const_get(bindings[:abstract_model].to_s).respond_to? 'reload_defaults!' 5 | end 6 | 7 | register_instance_option :collection do 8 | true 9 | end 10 | 11 | register_instance_option :link_icon do 12 | 'icon-wrench' 13 | end 14 | 15 | register_instance_option :pjax? do 16 | false 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/rails_admin/trigger_webhook.rb: -------------------------------------------------------------------------------- 1 | module RailsAdmin 2 | class TriggerWebhook < RailsAdmin::Config::Actions::Base 3 | register_instance_option :visible? do 4 | bindings[:object].class.to_s == 'CatalogWebhook' 5 | end 6 | 7 | register_instance_option :member do 8 | true 9 | end 10 | 11 | register_instance_option :link_icon do 12 | 'icon-play' 13 | end 14 | 15 | register_instance_option :pjax? do 16 | false 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/auto_annotate_models.rake: -------------------------------------------------------------------------------- 1 | # NOTE: only doing this in development as some production environments (Heroku) 2 | # NOTE: are sensitive to local FS writes, and besides -- it's just not proper 3 | # NOTE: to have a dev-mode tool do its thing in production. 4 | if Rails.env.development? 5 | task :set_annotation_options do 6 | # You can override any of these by setting an environment variable of the 7 | # same name. 8 | Annotate.set_defaults({ 9 | 'position_in_routes' => "before", 10 | 'position_in_class' => "before", 11 | 'position_in_test' => "before", 12 | 'position_in_fixture' => "before", 13 | 'position_in_factory' => "before", 14 | 'show_indexes' => "true", 15 | 'simple_indexes' => "false", 16 | 'model_dir' => "app/models", 17 | 'include_version' => "false", 18 | 'require' => "", 19 | 'exclude_tests' => "false", 20 | 'exclude_fixtures' => "false", 21 | 'exclude_factories' => "false", 22 | 'ignore_model_sub_dir' => "false", 23 | 'skip_on_db_migrate' => "false", 24 | 'format_bare' => "true", 25 | 'format_rdoc' => "false", 26 | 'format_markdown' => "false", 27 | 'sort' => "false", 28 | 'force' => "false", 29 | 'trace' => "false", 30 | }) 31 | end 32 | 33 | Annotate.load_tasks 34 | end 35 | -------------------------------------------------------------------------------- /lib/tasks/catalog.rake: -------------------------------------------------------------------------------- 1 | namespace :catalog do 2 | 3 | desc 'Refresh latest version of catalog entries' 4 | task :refresh, [:filter] => :environment do |t, args| 5 | puts 'Refreshing latest version of catalog entries:' 6 | puts 7 | 8 | filter = args[:filter] 9 | 10 | CatalogEntry.all.each do |entry| 11 | if filter 12 | if filter.match(/\A[0-9]+\Z/) 13 | next unless entry.id.to_s == filter 14 | else 15 | next unless entry.name.to_s.match(Regexp.new(filter, 'i')) 16 | end 17 | end 18 | 19 | puts "Refreshing #{entry.label} ..." 20 | begin 21 | old_version = entry.version 22 | entry.refresh! 23 | if old_version != entry.version 24 | puts "-> WAS #{old_version || '(null)'}" 25 | end 26 | puts "-> NOW #{entry.version}" 27 | rescue => e 28 | puts '!! ERROR !!' 29 | puts e.message 30 | end 31 | puts 32 | end 33 | 34 | puts 'REFRESH COMPLETE.' 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/tasks/release.rake: -------------------------------------------------------------------------------- 1 | if Rails.env.development? 2 | namespace :release do 3 | 4 | ### Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | # File updated by [release:bump] 7 | version_file = 'config/version.rb' 8 | # How to determine current project version programmatically 9 | version_lambda = lambda { Object.const_get(Rails.application.class.name.sub(/::.+$/, ''))::RELEASE } 10 | # Default commit message (%s replaced by version) 11 | commit_message = 'Release %s' 12 | # Changelog file to read/write 13 | changelog_file = 'CHANGELOG.md' 14 | # List of shell commands to try to determine previously released commit hash... 15 | # - the first non-empty command result is accepted 16 | # - changelog will be built from this reference to current HEAD 17 | changelog_ref_sources = ['git describe --abbrev=0 --tags', 'git rev-list HEAD | tail -n 1'] 18 | # Filter and format commit subjects for the changelog 19 | changelog_line_filter = lambda do |line| 20 | case line 21 | when /release|merge/i 22 | nil 23 | when /bug|fix/i 24 | '* __`!!!`__ ' + line 25 | when /add|support|ability/i 26 | '* __`NEW`__ ' + line 27 | when /remove|deprecate/i 28 | '* __`-!-`__ ' + line 29 | else 30 | '* __`~~~`__ ' + line 31 | end 32 | end 33 | 34 | ### Shortcuts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | task :next => [:minor, :changelog] do 37 | puts "** Next steps to release #{version_lambda.call}" 38 | puts '1. %-35s # %s' % ["nano #{changelog_file}", 'Review'] 39 | puts '2. %-35s # %s' % ['rake release:commit', 'Commit + Tag'] 40 | puts '3. %-35s # %s' % ['git push --tags origin master', 'Publish'] 41 | end 42 | task :prep do 43 | Rake::Task['release:bump'].invoke(2, 'pre') 44 | Rake::Task['release:commit'].invoke(false, 'Prepare for next release') 45 | end 46 | 47 | task(:major) { Rake::Task['release:bump'].invoke(1) } 48 | task(:minor) { Rake::Task['release:bump'].invoke(2) } 49 | task(:patch) { Rake::Task['release:bump'].invoke(3) } 50 | 51 | ### Tasks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52 | 53 | desc 'Bump release to next version.' 54 | task :bump, [:version_length, :prerelease_suffix] => :environment do |t, args| 55 | args.with_defaults(:version_length => 3, :prerelease_suffix => nil) 56 | raise ArgumentError, 'Invalid bump version length.' if args[:version_length].to_s.empty? 57 | 58 | version = Gem::Version.new(version_lambda.call) 59 | length = args[:version_length].to_i 60 | (suffix = args[:prerelease_suffix]) and suffix << '1' 61 | 62 | if version.prerelease? 63 | if suffix.to_s.chop == version.segments[-2] 64 | suffix = suffix.chop + (version.segments[-1].to_i + 1).to_s 65 | end 66 | next_version = (version.release.segments + [suffix]).compact.join('.') 67 | else 68 | segments = version.release.segments.slice(0, length) 69 | while segments.size < length + 1 # Version.bump strips last segment 70 | segments << 0 71 | end 72 | next_version = [Gem::Version.new(segments.join('.')).bump.to_s, suffix].compact.join('.') 73 | end 74 | 75 | puts "** Version bump #{version} -> #{next_version}" 76 | system "sed -i '' 's/#{version}/#{next_version}/g' #{version_file}" 77 | system "git diff -U0 #{version_file}" 78 | Kernel.silence_warnings do 79 | load Rails.root.join(*version_file.split('/')) 80 | end 81 | end 82 | 83 | desc 'Generate changelog for current version (from last tagged release)' 84 | task :changelog => :environment do 85 | changelog_ref = '' 86 | changelog_ref_sources.each do |cmd| 87 | changelog_ref = %x{#{cmd} 2> /dev/null}.chomp 88 | break unless changelog_ref.empty? 89 | end 90 | raise 'Failed to determine base git reference for changelog.' if changelog_ref.empty? 91 | 92 | header = '' 93 | footer = '' 94 | if File.file?(changelog_file) 95 | File.open(changelog_file, 'r') do |f| 96 | in_header = true 97 | f.each_line do |line| 98 | if !in_header or line.match(/^##/) 99 | in_header = false 100 | footer << line 101 | else 102 | header << line 103 | end 104 | end 105 | end 106 | end 107 | 108 | current_version = version_lambda.call 109 | content = "### #{current_version} (#{DateTime.now.strftime('%Y-%m-%d')})\n\n" 110 | gitlog = %x{git log #{changelog_ref}...HEAD --pretty=format:'%s' --reverse} 111 | raise gitlog.to_s if $?.exitstatus > 0 112 | gitlog.chomp.split("\n").each do |line| 113 | line = changelog_line_filter.call(line.chomp) 114 | content << line + "\n" if line 115 | end 116 | content << "\n\n" 117 | 118 | puts "** Writing to #{changelog_file}" 119 | puts 120 | puts content 121 | File.write(changelog_file, header + content + footer) 122 | end 123 | 124 | desc 'Commit/tag current release' 125 | task :commit, [:tag, :message] do |t, args| 126 | puts '** Committing release' 127 | current_version = version_lambda.call 128 | files = [version_file, changelog_file].join(' ') 129 | args.with_defaults(:tag => true, :message => commit_message % [current_version]) 130 | system "git reset > /dev/null && git add #{files}" 131 | system "git commit -m '#{args[:message]}'" 132 | if args[:tag] === true or args[:tag] == 'true' 133 | Rake::Task['release:tag'].invoke 134 | end 135 | end 136 | 137 | desc 'Tag current release' 138 | task :tag do 139 | puts '** Tagging release' 140 | current_version = version_lambda.call 141 | tag_mode = `[ -n "$(git config --local --get user.signingkey)" ] && echo "-s" || echo "-a"`.chomp 142 | system "git tag #{tag_mode} v#{current_version} -m ' #{commit_message % [current_version]}'" 143 | system 'git tag -n | tail -n 1' 144 | end 145 | 146 | task :current do 147 | puts version_lambda.call 148 | end 149 | 150 | ### Rake Template Author + Updates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 151 | # 152 | # 0101010 153 | # 0010011 154 | # _ _ 110101 155 | # | | | | 0011 156 | # _ __ ___| | ___ __ _ ___ ___ _ __ __ _| | _____ 0100010 157 | # | '__/ _ \ |/ _ \/ _` / __|/ _ \ | '__/ _` | |/ / _ \ 1010 0010101000001 158 | # | | | __/ | __/ (_| \__ \ __/ _ | | | (_| | < __/ 010101110100111101010010 159 | # |_| \___|_|\___|\__,_|___/\___| (_) |_| \__,_|_|\_\___| 01 0011000100 160 | # 2017.01.01 from 161 | # 0100 162 | # 01001001 binarybabel.org 163 | # 0100111001 000001010001110 164 | # 101 0010010000010100100101 165 | # 00111 0010011110100011001010 166 | # 0110 10000010100111001000100 167 | # 168 | # github.com/binarybabel 169 | # 170 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 171 | 172 | end 173 | end 174 | -------------------------------------------------------------------------------- /lib/tasks/webhooks.rake: -------------------------------------------------------------------------------- 1 | namespace :webhooks do 2 | 3 | desc 'Trigger webhooks based on recent catalog changes' 4 | task :trigger => :environment do |t, args| 5 | puts 'Triggering webhooks:' 6 | puts 7 | 8 | CatalogLogEntry.where('webhook_triggered != 1 AND webhook_triggered != "t"').each do |entry| 9 | puts "[#{entry.catalog_entry.label}]" 10 | entry.catalog_entry.catalog_webhooks.each do |webhook| 11 | puts '------------------------------------------------------------' 12 | puts "-> #{webhook.url}" 13 | webhook.trigger! 14 | if webhook.last_error 15 | puts '!! ERROR !!' 16 | puts webhook.last_error 17 | else 18 | puts 'OK' 19 | end 20 | puts '------------------------------------------------------------' 21 | end 22 | entry.webhook_triggered = true 23 | entry.save! 24 | puts 25 | end 26 | 27 | puts 'WEBHOOKS COMPLETE.' 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/log/.keep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /rubygem/.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | .yardoc 3 | Gemfile.lock 4 | _yardoc/ 5 | coverage/ 6 | doc/ 7 | pkg/ 8 | spec/reports/ 9 | tmp/ 10 | .version.lock 11 | -------------------------------------------------------------------------------- /rubygem/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | rvm: 4 | - 2.3.1 5 | before_install: gem install bundler -v 1.13.6 6 | -------------------------------------------------------------------------------- /rubygem/.versioneer.yml: -------------------------------------------------------------------------------- 1 | # Versioneer Project Configuration 2 | # https://git.io/versioneer-help 3 | 4 | type: Git 5 | bump_segment: minor 6 | prereleases: 7 | - alpha 8 | - beta 9 | -------------------------------------------------------------------------------- /rubygem/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in latestver.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /rubygem/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2017 BinaryBabel OSS 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /rubygem/README.md: -------------------------------------------------------------------------------- 1 | # Latestver RubyGem 2 | 3 | Latestver command-line interface. Works with the hosted edition ([lv.binarybabel.org](https://lv.binarybabel.org)), 4 | or private docker deployments. Project sync coming soon. 5 | 6 | ## Installation 7 | 8 | $ gem install latestver 9 | 10 | ## Usage 11 | 12 | $ latestver --help 13 | 14 | ## Contributing 15 | 16 | The gem is available as open source under the terms of the [GNU GPL 3 License](https://opensource.org/licenses/gpl-3.0). 17 | 18 | Bug reports and pull requests are welcome on GitHub at https://github.com/binarybabel/latestver. 19 | 20 | * After checking out the repo, run `bin/setup` to install dependencies. 21 | * Then, run `rake test` to run the tests. 22 | 23 | ## Author and License 24 | 25 | - **Author:** BinaryBabel OSS ([https://keybase.io/binarybabel](https://keybase.io/binarybabel)) 26 | - **License:** GNU GPL 3 27 | -------------------------------------------------------------------------------- /rubygem/Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rake/testtask' 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << 'test' 6 | t.libs << 'lib' 7 | t.test_files = FileList['test/**/*_test.rb'] 8 | end 9 | 10 | task :default => :test 11 | 12 | task :versioneer do 13 | system './bin/versioneer relock' 14 | end 15 | 16 | Rake::Task[:build].enhance [:versioneer] do 17 | system './bin/versioneer unlock -q' 18 | end 19 | -------------------------------------------------------------------------------- /rubygem/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "latestver" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /rubygem/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /rubygem/bin/versioneer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'versioneer' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("versioneer", "versioneer") 18 | -------------------------------------------------------------------------------- /rubygem/exe/latestver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'bundler/setup' 4 | require 'optparse' 5 | require 'latestver' 6 | 7 | options = { 8 | :server => 'https://lv.binarybabel.org', 9 | :output => 'select', 10 | :select => 'version', 11 | } 12 | 13 | opparser = OptionParser.new do |opts| 14 | opts.banner = [ 15 | '', 16 | 'Latestver CLI lv.binarybabel.org', 17 | '---------------------------------------------', 18 | ' latestver get NAME:TAG', 19 | ' latestver [OPTIONS] ACTION [ARGS]', 20 | '---------------------------------------------', 21 | '', 22 | ' Actions:', 23 | ' get NAME:TAG', 24 | '', 25 | ' Options:' 26 | ].join("\n") 27 | opts.on('-p SUB.FIELD.PATH', 'Dot-path to result field, default: version') do |v| 28 | options[:select] = v 29 | end 30 | opts.on('--server SERVER', 'https://lv.binarybabel.org') do |v| 31 | options[:server] = v 32 | end 33 | opts.on('--json', 'JSON output, clears default path selector') do 34 | options[:output] = 'json' 35 | options[:select] = nil 36 | end 37 | end 38 | opparser.parse! 39 | 40 | @app = Latestver::Client.new(options[:server]) 41 | @cli = Latestver::CliHelpers 42 | 43 | 44 | if ARGV.size > 0 45 | begin 46 | case (action = ARGV.shift) 47 | 48 | when /get/i 49 | data = @app.catalog_get(ARGV[0]) 50 | puts @cli.output(options, data) 51 | 52 | else 53 | puts opparser.help 54 | end 55 | rescue Latestver::ClientError => e 56 | puts @cli.output(options, e) 57 | exit(1) 58 | end 59 | else 60 | puts opparser.help 61 | end 62 | -------------------------------------------------------------------------------- /rubygem/latestver.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'latestver/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'latestver' 8 | spec.version = Latestver::VERSION 9 | spec.authors = ['BinaryBabel OSS'] 10 | spec.email = ['oss@binarybabel.org'] 11 | 12 | spec.summary = 'Latestver command-line interface. Hosted or privately deployed. Project sync coming soon.' 13 | spec.homepage = 'https://lv.binarybabel.org' 14 | spec.license = 'GNU GPL 3' 15 | 16 | spec.files = Dir.glob('{bin,exe,lib}/**/*') + %w( 17 | LICENSE.txt README.md 18 | ) 19 | spec.bindir = 'exe' 20 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 21 | spec.require_paths = ['lib'] 22 | 23 | spec.files += ['version.lock', '.versioneer.yml'] 24 | spec.add_dependency 'versioneer' 25 | 26 | spec.add_runtime_dependency 'faraday' 27 | 28 | spec.add_development_dependency 'bundler', '~> 1.13' 29 | spec.add_development_dependency 'rake', '~> 10.0' 30 | spec.add_development_dependency 'minitest', '~> 5.0' 31 | if RUBY_VERSION > '2.3' 32 | spec.add_development_dependency 'pry', '~> 0.10.0' 33 | spec.add_development_dependency 'pry-byebug', '> 3' 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /rubygem/lib/latestver.rb: -------------------------------------------------------------------------------- 1 | require 'latestver/version' 2 | require 'latestver/client_error' 3 | require 'latestver/client' 4 | require 'latestver/cli_helpers' 5 | 6 | module Latestver 7 | end 8 | -------------------------------------------------------------------------------- /rubygem/lib/latestver/cli_helpers.rb: -------------------------------------------------------------------------------- 1 | module Latestver 2 | class CliHelpers 3 | 4 | def self.output(options, data) 5 | data_out = data 6 | 7 | unless data_out.is_a? Exception 8 | 9 | if options[:select] 10 | s = options[:select].split('.') 11 | while s.length > 0 and not data_out.nil? 12 | data_out = data_out[s.shift] 13 | end 14 | end 15 | 16 | return nil unless data_out 17 | 18 | end 19 | 20 | case options[:output] 21 | when 'json' 22 | if data_out.is_a? String 23 | data_out = { 24 | value: data_out, 25 | error: '', 26 | } 27 | elsif data_out.is_a? Exception 28 | data_out = { 29 | error: data_out.message, 30 | value: '', 31 | } 32 | end 33 | JSON.pretty_generate(data_out) 34 | else 35 | if data_out.is_a? Exception 36 | "ERROR: #{data_out.message}" 37 | else 38 | data_out 39 | end 40 | end 41 | end 42 | 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /rubygem/lib/latestver/client.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'faraday' 3 | 4 | module Latestver 5 | class Client 6 | REGEX_NAME_TAG = /\A([a-z0-9][a-z0-9_\.-]+[a-z0-9]):([a-z0-9][a-z0-9_\.-]+[a-z0-9])\z/i 7 | 8 | attr_reader :server_url 9 | 10 | def initialize(server_url) 11 | @server_url = server_url.to_s.sub(/\/\z/, '') 12 | end 13 | 14 | def catalog_get(name_tag) 15 | if name_tag.index(':').nil? 16 | name_tag = name_tag + ':latest' 17 | end 18 | 19 | raise ArgumentError, "Invalid NAME:TAG, #{name_tag}" unless name_tag.match(REGEX_NAME_TAG) 20 | 21 | name, tag = name_tag.split(':') 22 | response = ::Faraday.get "#{server_url}/catalog-api/#{name}/#{tag}.json" 23 | 24 | if response.status == 200 25 | JSON.parse(response.body) 26 | else 27 | raise ClientError, "Failed to get entry from catalog. #{response.reason_phrase}" 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /rubygem/lib/latestver/client_error.rb: -------------------------------------------------------------------------------- 1 | module Latestver 2 | class ClientError < RuntimeError 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /rubygem/lib/latestver/version.rb: -------------------------------------------------------------------------------- 1 | module Latestver 2 | begin 3 | require 'versioneer' 4 | VERSION = ::Versioneer::Config.new(::File.expand_path('../../../', __FILE__)) 5 | rescue LoadError 6 | VERSION = '0.0.0' 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /rubygem/test/latestver_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LatestverTest < Minitest::Test 4 | def test_that_it_has_a_version_number 5 | refute_nil ::Latestver::VERSION 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /rubygem/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'latestver' 3 | 4 | require 'minitest/autorun' 5 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/catalog_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CatalogControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/help_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class HelpControllerTest < ActionDispatch::IntegrationTest 4 | test "should get index" do 5 | get help_index_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/controllers/log_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LogControllerTest < ActionDispatch::IntegrationTest 4 | test "should get index" do 5 | get log_index_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/catalog_entries.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 23 | 24 | one: 25 | name: MyString 26 | type: 27 | latest: MyString 28 | 29 | two: 30 | name: MyString 31 | type: 32 | latest: MyString 33 | -------------------------------------------------------------------------------- /test/fixtures/catalog_log_entries.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_log_entries 4 | # 5 | # id :integer not null, primary key 6 | # catalog_entry_id :integer not null 7 | # version_from :string 8 | # version_to :string 9 | # webhook_triggered :boolean default(FALSE) 10 | # created_at :datetime not null 11 | # updated_at :datetime not null 12 | # 13 | # Indexes 14 | # 15 | # index_catalog_log_entries_on_catalog_entry_id (catalog_entry_id) 16 | # 17 | 18 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 19 | 20 | one: 21 | catalog_entry: one 22 | version_from: MyString 23 | version_to: MyString 24 | 25 | two: 26 | catalog_entry: two 27 | version_from: MyString 28 | version_to: MyString 29 | -------------------------------------------------------------------------------- /test/fixtures/catalog_webhooks.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_webhooks 4 | # 5 | # id :integer not null, primary key 6 | # catalog_entry_id :integer not null 7 | # url :string not null 8 | # description :string not null 9 | # last_triggered :datetime 10 | # last_error :string 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # 14 | # Indexes 15 | # 16 | # index_catalog_webhooks_on_catalog_entry_id (catalog_entry_id) 17 | # 18 | 19 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 20 | 21 | one: 22 | catalog_entry: one 23 | url: MyString 24 | description: MyString 25 | 26 | two: 27 | catalog_entry: two 28 | url: MyString 29 | description: MyString 30 | -------------------------------------------------------------------------------- /test/fixtures/external_links.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: external_links 4 | # 5 | # id :integer not null, primary key 6 | # catalog_entry_id :integer 7 | # name :string 8 | # href :string 9 | # icon :string 10 | # 11 | 12 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 13 | 14 | one: 15 | name: MyString 16 | href: MyString 17 | icon: MyString 18 | 19 | two: 20 | name: MyString 21 | href: MyString 22 | icon: MyString 23 | -------------------------------------------------------------------------------- /test/fixtures/groups.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: groups 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # 8 | 9 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 10 | 11 | one: 12 | name: MyString 13 | 14 | two: 15 | name: MyString 16 | -------------------------------------------------------------------------------- /test/fixtures/instances.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: instances 4 | # 5 | # id :integer not null, primary key 6 | # group_id :integer not null 7 | # catalog_entry_id :integer not null 8 | # description :string 9 | # version :string 10 | # created_at :datetime not null 11 | # updated_at :datetime not null 12 | # 13 | # Indexes 14 | # 15 | # index_instances_on_catalog_entry_id (catalog_entry_id) 16 | # index_instances_on_group_id (group_id) 17 | # 18 | 19 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 20 | 21 | one: 22 | name: MyString 23 | catalog_entry: 24 | version: MyString 25 | 26 | two: 27 | name: MyString 28 | catalog_entry: 29 | version: MyString 30 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/test/models/.keep -------------------------------------------------------------------------------- /test/models/catalog_entry_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_entries 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # type :string not null 8 | # tag :string not null 9 | # version :string 10 | # version_date :date 11 | # prereleases :boolean default(FALSE) 12 | # external_links :text 13 | # data :text 14 | # refreshed_at :datetime 15 | # last_error :string 16 | # no_log :boolean default(FALSE) 17 | # hidden :boolean default(FALSE) 18 | # created_at :datetime not null 19 | # updated_at :datetime not null 20 | # 21 | 22 | require 'test_helper' 23 | 24 | class CatalogEntryTest < ActiveSupport::TestCase 25 | # test "the truth" do 26 | # assert true 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /test/models/catalog_log_entry_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_log_entries 4 | # 5 | # id :integer not null, primary key 6 | # catalog_entry_id :integer not null 7 | # version_from :string 8 | # version_to :string 9 | # webhook_triggered :boolean default(FALSE) 10 | # created_at :datetime not null 11 | # updated_at :datetime not null 12 | # 13 | # Indexes 14 | # 15 | # index_catalog_log_entries_on_catalog_entry_id (catalog_entry_id) 16 | # 17 | 18 | require 'test_helper' 19 | 20 | class CatalogLogEntryTest < ActiveSupport::TestCase 21 | # test "the truth" do 22 | # assert true 23 | # end 24 | end 25 | -------------------------------------------------------------------------------- /test/models/catalog_webhook_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: catalog_webhooks 4 | # 5 | # id :integer not null, primary key 6 | # catalog_entry_id :integer not null 7 | # url :string not null 8 | # description :string not null 9 | # last_triggered :datetime 10 | # last_error :string 11 | # created_at :datetime not null 12 | # updated_at :datetime not null 13 | # 14 | # Indexes 15 | # 16 | # index_catalog_webhooks_on_catalog_entry_id (catalog_entry_id) 17 | # 18 | 19 | require 'test_helper' 20 | 21 | class CatalogWebhookTest < ActiveSupport::TestCase 22 | # test "the truth" do 23 | # assert true 24 | # end 25 | end 26 | -------------------------------------------------------------------------------- /test/models/external_link_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: external_links 4 | # 5 | # id :integer not null, primary key 6 | # catalog_entry_id :integer 7 | # name :string 8 | # href :string 9 | # icon :string 10 | # 11 | 12 | require 'test_helper' 13 | 14 | class ExternalLinkTest < ActiveSupport::TestCase 15 | # test "the truth" do 16 | # assert true 17 | # end 18 | end 19 | -------------------------------------------------------------------------------- /test/models/group_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: groups 4 | # 5 | # id :integer not null, primary key 6 | # name :string not null 7 | # 8 | 9 | require 'test_helper' 10 | 11 | class GroupTest < ActiveSupport::TestCase 12 | # test "the truth" do 13 | # assert true 14 | # end 15 | end 16 | -------------------------------------------------------------------------------- /test/models/instance_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: instances 4 | # 5 | # id :integer not null, primary key 6 | # group_id :integer not null 7 | # catalog_entry_id :integer not null 8 | # description :string 9 | # version :string 10 | # created_at :datetime not null 11 | # updated_at :datetime not null 12 | # 13 | # Indexes 14 | # 15 | # index_instances_on_catalog_entry_id (catalog_entry_id) 16 | # index_instances_on_group_id (group_id) 17 | # 18 | 19 | require 'test_helper' 20 | 21 | class InstanceTest < ActiveSupport::TestCase 22 | # test "the truth" do 23 | # assert true 24 | # end 25 | end 26 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 7 | fixtures :all 8 | 9 | # Add more helper methods to be used by all tests here... 10 | end 11 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybabel/latestver/05feacca3749481b4d6f1668592e254df744295d/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------