litestream 0.13.0-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ <!DOCTYPE html>
2
+ <html class="h-full">
3
+ <head>
4
+ <title>Litestream</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= render "layouts/litestream/style" %>
9
+ </head>
10
+ <body class="h-full flex flex-col dark:bg-gray-900 dark:text-white">
11
+ <main class="container mx-auto max-w-4xl mt-4 px-2 grow">
12
+ <%= content_for?(:content) ? yield(:content) : yield %>
13
+ </main>
14
+
15
+ <footer class="container mx-auto mt-24 flex items-center justify-between border-t dark:border-gray-800 px-2 py-4 text-base">
16
+ <p>
17
+ <code><strong>Litestream</strong></code>&nbsp;&nbsp;|&nbsp;
18
+ Made by <a href="https://twitter.com/fractaledmind" class="text-blue-500 hover:underline decoration-blue-500">@fractaledmind</a> and <a href="https://github.com/fractaledmind/litestream-ruby/graphs/contributors" class="text-blue-500 hover:underline decoration-blue-500">friends</a>! Want to help? It's <a href="https://github.com/fractaledmind/litestream-ruby" class="text-blue-500 hover:underline decoration-blue-500">open source</a>!
19
+ </p>
20
+ </footer>
21
+
22
+ <div class="fixed top-0 left-0 right-0 text-center py-2">
23
+ <% if notice.present? %>
24
+ <p id="notice"
25
+ class="py-2 px-3 bg-green-50 dark:bg-green-900 text-green-500 font-medium rounded-lg inline-block"
26
+ data-controller="fade">
27
+ <%= notice.html_safe %>
28
+ </p>
29
+ <% end %>
30
+
31
+ <% if alert.present? %>
32
+ <p id="alert"
33
+ class="py-2 px-3 bg-red-50 dark:bg-red-900 text-red-500 font-medium rounded-lg inline-block"
34
+ data-controller="fade">
35
+ <%= alert.html_safe %>
36
+ </p>
37
+ <% end %>
38
+ </div>
39
+
40
+ <script nonce="<%= content_security_policy_nonce %>">
41
+ function fadeOut(element) {
42
+ element.classList.add('transition-opacity')
43
+ setTimeout(
44
+ () => {
45
+ element.classList.add('opacity-0')
46
+ element.remove()
47
+ },
48
+ 5000
49
+ )
50
+ }
51
+ document.querySelectorAll('[data-controller="fade"]').forEach(element => {
52
+ fadeOut(element);
53
+ });
54
+ </script>
55
+ </body>
56
+ </html>
@@ -0,0 +1,121 @@
1
+ <section id="process_<%= @process[:pid] %>" class="space-y-6">
2
+ <div class="flex items-center justify-between">
3
+ <h1 class="flex items-baseline gap-2 text-2xl font-bold">
4
+ Litestream
5
+
6
+ <% if @process[:status] == "sleeping" %>
7
+ <small class="inline-flex rounded-full px-2 text-sm font-semibold bg-yellow-100 text-yellow-800">
8
+ <%= @process[:status] %>
9
+ </small>
10
+ <% elsif @process[:status] %>
11
+ <small class="inline-flex rounded-full px-2 text-sm font-semibold bg-green-100 text-green-800">
12
+ <%= @process[:status] %>
13
+ </small>
14
+ <% else %>
15
+ <small class="inline-flex rounded-full px-2 text-sm font-semibold bg-red-100 text-red-800">
16
+ not running
17
+ </small>
18
+ <% end %>
19
+ </h1>
20
+
21
+ <% if @process[:status] %>
22
+ <small class="text-base">
23
+ #<code><%= @process[:pid] %></code>
24
+ </small>
25
+ <% end %>
26
+ </div>
27
+
28
+ <% if @process[:status] %>
29
+ <dl class="grid grid-cols-[fit-content(100%)_1fr] gap-x-4">
30
+ <dt class="font-bold">Started at</dt>
31
+ <dd class="">
32
+ <abbr title="<%= @process[:started] %>" class="underline decoration-dashed decoration-gray-500 cursor-help">
33
+ <time datetime="<%= @process[:started] %>"><%= @process[:started].to_formatted_s(:db) %></time>
34
+ </abbr>
35
+ </dd>
36
+ </dl>
37
+ <% end %>
38
+ </section>
39
+ <br>
40
+ <br>
41
+
42
+ <section id="databases" class="">
43
+ <div class="mb-3 flex items-center justify-between border-b dark:border-gray-800">
44
+ <h2 class="text-2xl font-bold">Databases</h2>
45
+ <p class="text-right">Total: <strong><%= @databases.size %></strong></p>
46
+ </div>
47
+
48
+ <ul class="list-[square] list-outside ml-4">
49
+ <% @databases.each do |database| %>
50
+ <li>
51
+ <div class="flex items-center justify-between">
52
+ <h2 class="text-lg font-bold">
53
+ <code><%= database['path'] %></code>
54
+ </h2>
55
+ <%= button_to "Restore", restorations_path, class: "rounded-md bg-slate-800 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-700", params: { database: database['path'] } %>
56
+ </div>
57
+
58
+ <br />
59
+ <section id="generations" class="ml-6">
60
+ <% database['generations'].each do |generation| %>
61
+ <details id="<%= generation['generation'] %>" open="open">
62
+ <summary class="cursor-pointer rounded p-2 hover:bg-gray-50 dark:hover:bg-gray-800">
63
+ <code><%= generation['generation'] %></code>
64
+ (<em><%= generation['lag'] %> lag</em>)
65
+ </summary>
66
+
67
+ <dl class="ml-7 grid grid-cols-[fit-content(100%)_1fr] gap-x-4">
68
+ <dt class="font-bold">Start</dt>
69
+ <dd class="">
70
+ <abbr title="<%= generation['start'] %>" class="underline decoration-dashed decoration-gray-500 cursor-help">
71
+ <time datetime="<%= generation['start'] %>"><%= DateTime.parse(generation['start']).to_formatted_s(:db) %></time>
72
+ </abbr>
73
+ </dd>
74
+
75
+ <dt class="font-bold">End</dt>
76
+ <dd class="">
77
+ <abbr title="<%= generation['end'] %>" class="underline decoration-dashed decoration-gray-500 cursor-help">
78
+ <time datetime="<%= generation['end'] %>"><%= DateTime.parse(generation['end']).to_formatted_s(:db) %></time>
79
+ </abbr>
80
+ </dd>
81
+
82
+ <div class="col-span-2">
83
+ <dt class="font-bold">Snapshots</dt>
84
+ <dd class="">
85
+ <table class="min-w-full divide-y divide-gray-300">
86
+ <thead>
87
+ <tr>
88
+ <th scope="col" class="whitespace-nowrap px-2 py-2 text-left text-sm font-semibold text-gray-900 dark:text-gray-100">Created at</th>
89
+ <th scope="col" class="whitespace-nowrap px-2 py-2 text-right text-sm font-semibold text-gray-900 dark:text-gray-100">Index</th>
90
+ <th scope="col" class="whitespace-nowrap px-2 py-2 text-right text-sm font-semibold text-gray-900 dark:text-gray-100">Size</th>
91
+ </tr>
92
+ </thead>
93
+
94
+ <tbody class="bg-white dark:bg-gray-900">
95
+ <% generation['snapshots'].each do |snapshot| %>
96
+ <tr class="align-top even:bg-gray-50 dark:even:bg-gray-800">
97
+ <td scope="col" class="whitespace-nowrap px-2 py-2 text-sm text-gray-900 dark:text-gray-100">
98
+ <abbr title="<%= snapshot['created'] %>" class="underline decoration-dashed decoration-gray-500 cursor-help">
99
+ <time datetime="<%= snapshot['created'] %>"><%= DateTime.parse(snapshot['created']).to_formatted_s(:db) %></time>
100
+ </abbr>
101
+ </td>
102
+ <td scope="col" class="whitespace-nowrap px-2 py-2 text-sm text-gray-900 dark:text-gray-100 text-right">
103
+ <%= snapshot['index'] %>
104
+ </td>
105
+ <td scope="col" class="whitespace-nowrap px-2 py-2 text-sm text-gray-900 dark:text-gray-100 text-right">
106
+ <%= number_to_human_size snapshot['size'] %>
107
+ </td>
108
+ </tr>
109
+ <% end %>
110
+ </tbody>
111
+ </table>
112
+ </dd>
113
+ </div>
114
+ </dl>
115
+ </details>
116
+ <% end %>
117
+ </section>
118
+ </li>
119
+ <% end %>
120
+ </ul>
121
+ </section>
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ Litestream::Engine.routes.draw do
2
+ get "/" => "processes#show", :as => :root
3
+
4
+ resource :process, only: [:show], path: ""
5
+ resources :restorations, only: [:create]
6
+ end
Binary file
data/exe/litestream ADDED
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+ # because rubygems shims assume a gem's executables are Ruby
3
+
4
+ require "litestream/commands"
5
+
6
+ begin
7
+ command = [Litestream::Commands.executable, *ARGV]
8
+ exec(*command)
9
+ rescue Litestream::Commands::UnsupportedPlatformException, Litestream::Commands::ExecutableNotFoundException => e
10
+ warn("ERROR: " + e.message)
11
+ exit 1
12
+ end
@@ -0,0 +1,159 @@
1
+ require_relative "upstream"
2
+ require "logfmt"
3
+
4
+ module Litestream
5
+ module Commands
6
+ DEFAULT_DIR = File.expand_path(File.join(__dir__, "..", "..", "exe"))
7
+ GEM_NAME = "litestream"
8
+
9
+ # raised when the host platform is not supported by upstream litestream's binary releases
10
+ UnsupportedPlatformException = Class.new(StandardError)
11
+
12
+ # raised when the litestream executable could not be found where we expected it to be
13
+ ExecutableNotFoundException = Class.new(StandardError)
14
+
15
+ # raised when LITESTREAM_INSTALL_DIR does not exist
16
+ DirectoryNotFoundException = Class.new(StandardError)
17
+
18
+ # raised when a litestream command requires a database argument but it isn't provided
19
+ DatabaseRequiredException = Class.new(StandardError)
20
+
21
+ # raised when a litestream command fails
22
+ CommandFailedException = Class.new(StandardError)
23
+
24
+ class << self
25
+ def platform
26
+ [:cpu, :os].map { |m| Gem::Platform.local.send(m) }.join("-")
27
+ end
28
+
29
+ def executable(exe_path: DEFAULT_DIR)
30
+ litestream_install_dir = ENV["LITESTREAM_INSTALL_DIR"]
31
+ if litestream_install_dir
32
+ if File.directory?(litestream_install_dir)
33
+ warn "NOTE: using LITESTREAM_INSTALL_DIR to find litestream executable: #{litestream_install_dir}"
34
+ exe_path = litestream_install_dir
35
+ exe_file = File.expand_path(File.join(litestream_install_dir, "litestream"))
36
+ else
37
+ raise DirectoryNotFoundException, <<~MESSAGE
38
+ LITESTREAM_INSTALL_DIR is set to #{litestream_install_dir}, but that directory does not exist.
39
+ MESSAGE
40
+ end
41
+ else
42
+ if Litestream::Upstream::NATIVE_PLATFORMS.keys.none? { |p| Gem::Platform.match_gem?(Gem::Platform.new(p), GEM_NAME) }
43
+ raise UnsupportedPlatformException, <<~MESSAGE
44
+ litestream-ruby does not support the #{platform} platform
45
+ Please install litestream following instructions at https://litestream.io/install
46
+ MESSAGE
47
+ end
48
+
49
+ exe_file = Dir.glob(File.expand_path(File.join(exe_path, "*", "litestream"))).find do |f|
50
+ Gem::Platform.match_gem?(Gem::Platform.new(File.basename(File.dirname(f))), GEM_NAME)
51
+ end
52
+ end
53
+
54
+ if exe_file.nil? || !File.exist?(exe_file)
55
+ raise ExecutableNotFoundException, <<~MESSAGE
56
+ Cannot find the litestream executable for #{platform} in #{exe_path}
57
+
58
+ If you're using bundler, please make sure you're on the latest bundler version:
59
+
60
+ gem install bundler
61
+ bundle update --bundler
62
+
63
+ Then make sure your lock file includes this platform by running:
64
+
65
+ bundle lock --add-platform #{platform}
66
+ bundle install
67
+
68
+ See `bundle lock --help` output for details.
69
+
70
+ If you're still seeing this message after taking those steps, try running
71
+ `bundle config` and ensure `force_ruby_platform` isn't set to `true`. See
72
+ https://github.com/fractaledmind/litestream-ruby#check-bundle_force_ruby_platform
73
+ for more details.
74
+ MESSAGE
75
+ end
76
+
77
+ exe_file
78
+ end
79
+
80
+ def replicate(async: false, **argv)
81
+ execute("replicate", argv, async: async, tabled_output: false)
82
+ end
83
+
84
+ def restore(database, async: false, **argv)
85
+ raise DatabaseRequiredException, "database argument is required for restore command, e.g. litestream:restore -- --database=path/to/database.sqlite" if database.nil?
86
+ argv.stringify_keys!
87
+
88
+ execute("restore", argv, database, async: async, tabled_output: false)
89
+ end
90
+
91
+ def databases(async: false, **argv)
92
+ execute("databases", argv, async: async, tabled_output: true)
93
+ end
94
+
95
+ def generations(database, async: false, **argv)
96
+ raise DatabaseRequiredException, "database argument is required for generations command, e.g. litestream:generations -- --database=path/to/database.sqlite" if database.nil?
97
+
98
+ execute("generations", argv, database, async: async, tabled_output: true)
99
+ end
100
+
101
+ def snapshots(database, async: false, **argv)
102
+ raise DatabaseRequiredException, "database argument is required for snapshots command, e.g. litestream:snapshots -- --database=path/to/database.sqlite" if database.nil?
103
+
104
+ execute("snapshots", argv, database, async: async, tabled_output: true)
105
+ end
106
+
107
+ def wal(database, async: false, **argv)
108
+ raise DatabaseRequiredException, "database argument is required for wal command, e.g. litestream:wal -- --database=path/to/database.sqlite" if database.nil?
109
+
110
+ execute("wal", argv, database, async: async, tabled_output: true)
111
+ end
112
+
113
+ private
114
+
115
+ def execute(command, argv = {}, database = nil, async: false, tabled_output: false)
116
+ cmd = prepare(command, argv, database)
117
+ results = run(cmd, async: async, tabled_output: tabled_output)
118
+
119
+ if Array === results && results.one? && results[0]["level"] == "ERROR"
120
+ raise CommandFailedException, "Failed to execute `#{cmd.join(" ")}`; Reason: #{results[0]["error"]}"
121
+ else
122
+ results
123
+ end
124
+ end
125
+
126
+ def prepare(command, argv = {}, database = nil)
127
+ ENV["LITESTREAM_REPLICA_BUCKET"] ||= Litestream.replica_bucket
128
+ ENV["LITESTREAM_REPLICA_REGION"] ||= Litestream.replica_region
129
+ ENV["LITESTREAM_REPLICA_ENDPOINT"] ||= Litestream.replica_endpoint
130
+ ENV["LITESTREAM_ACCESS_KEY_ID"] ||= Litestream.replica_key_id
131
+ ENV["LITESTREAM_SECRET_ACCESS_KEY"] ||= Litestream.replica_access_key
132
+
133
+ args = {
134
+ "--config" => Litestream.config_path.to_s
135
+ }.merge(argv.stringify_keys).to_a.flatten.compact
136
+ cmd = [executable, command, *args, database].compact
137
+ puts cmd.inspect if ENV["DEBUG"]
138
+
139
+ cmd
140
+ end
141
+
142
+ def run(cmd, async: false, tabled_output: false)
143
+ if async
144
+ # To release the resources of the Ruby process, just fork and exit.
145
+ # The forked process executes litestream and replaces itself.
146
+ exec(*cmd) if fork.nil?
147
+ else
148
+ stdout = `#{cmd.join(" ")}`.chomp
149
+ tabled_output ? text_table_to_hashes(stdout) : stdout.split("\n").map { Logfmt.parse(_1) }
150
+ end
151
+ end
152
+
153
+ def text_table_to_hashes(string)
154
+ keys, *rows = string.split("\n").map { _1.split(/\s+/) }
155
+ rows.map { keys.zip(_1).to_h }
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/engine"
4
+
5
+ module Litestream
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace Litestream
8
+
9
+ config.litestream = ActiveSupport::OrderedOptions.new
10
+
11
+ # Load the `litestream:install` generator into the host Rails app
12
+ generators do
13
+ require_relative "generators/litestream/install_generator"
14
+ end
15
+
16
+ initializer "litestream.config" do
17
+ config.litestream.each do |name, value|
18
+ Litestream.public_send(:"#{name}=", value)
19
+ end
20
+ end
21
+
22
+ initializer "deprecator" do |app|
23
+ app.deprecators[:litestream] = Litestream.deprecator
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module Litestream
6
+ module Generators
7
+ class InstallGenerator < ::Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def copy_config_file
11
+ template "config.yml.erb", "config/litestream.yml"
12
+ end
13
+
14
+ def copy_initializer_file
15
+ template "initializer.rb", "config/initializers/litestream.rb"
16
+ end
17
+
18
+ private
19
+
20
+ def production_sqlite_databases
21
+ ActiveRecord::Base
22
+ .configurations
23
+ .configs_for(env_name: "production", include_hidden: true)
24
+ .select { |config| ["sqlite3", "litedb"].include? config.adapter }
25
+ .map(&:database)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ # This is the actual configuration file for litestream.
2
+ #
3
+ # You can either use the generated `config/initializers/litestream.rb`
4
+ # file to configure the litestream-ruby gem, which will populate these
5
+ # ENV variables when using the `rails litestream:replicate` command.
6
+ #
7
+ # Or, if you prefer, manually manage ENV variables and this configuration file.
8
+ # In that case, simply ensure that the ENV variables are set before running the
9
+ # `replicate` command.
10
+ #
11
+ # For more details, see: https://litestream.io/reference/config/
12
+ dbs:
13
+ <%- production_sqlite_databases.each do |database| -%>
14
+ - path: <%= database %>
15
+ replicas:
16
+ - type: s3
17
+ bucket: $LITESTREAM_REPLICA_BUCKET
18
+ path: <%= database %>
19
+ access-key-id: $LITESTREAM_ACCESS_KEY_ID
20
+ secret-access-key: $LITESTREAM_SECRET_ACCESS_KEY
21
+ <%- end -%>
@@ -0,0 +1,33 @@
1
+ # Use this hook to configure the litestream-ruby gem.
2
+ # All configuration options will be available as environment variables, e.g.
3
+ # config.replica_bucket becomes LITESTREAM_REPLICA_BUCKET
4
+ # This allows you to configure Litestream using Rails encrypted credentials,
5
+ # or some other mechanism where the values are only available at runtime.
6
+
7
+ Rails.application.configure do
8
+ # An example of using Rails encrypted credentials to configure Litestream.
9
+ # litestream_credentials = Rails.application.credentials.litestream
10
+
11
+ # Replica-specific bucket location.
12
+ # This will be your bucket's URL without the `https://` prefix.
13
+ # For example, if you used DigitalOcean Spaces, your bucket URL could look like:
14
+ # https://myapp.fra1.digitaloceanspaces.com
15
+ # And so you should set your `replica_bucket` to:
16
+ # myapp.fra1.digitaloceanspaces.com
17
+ # Litestream supports Azure Blog Storage, Backblaze B2, DigitalOcean Spaces,
18
+ # Scaleway Object Storage, Google Cloud Storage, Linode Object Storage, and
19
+ # any SFTP server.
20
+ # In this example, we are using Rails encrypted credentials to store the URL to
21
+ # our storage provider bucket.
22
+ # config.litestream.replica_bucket = litestream_credentials&.replica_bucket
23
+
24
+ # Replica-specific authentication key.
25
+ # Litestream needs authentication credentials to access your storage provider bucket.
26
+ # In this example, we are using Rails encrypted credentials to store the access key ID.
27
+ # config.litestream.replica_key_id = litestream_credentials&.replica_key_id
28
+
29
+ # Replica-specific secret key.
30
+ # Litestream needs authentication credentials to access your storage provider bucket.
31
+ # In this example, we are using Rails encrypted credentials to store the secret access key.
32
+ # config.litestream.replica_access_key = litestream_credentials&.replica_access_key
33
+ end
@@ -0,0 +1,14 @@
1
+ module Litestream
2
+ module Upstream
3
+ VERSION = "v0.3.13"
4
+
5
+ # rubygems platform name => upstream release filename
6
+ NATIVE_PLATFORMS = {
7
+ "aarch64-linux" => "litestream-#{VERSION}-linux-arm64.tar.gz",
8
+ "arm64-darwin" => "litestream-#{VERSION}-darwin-arm64.zip",
9
+ "arm64-linux" => "litestream-#{VERSION}-linux-arm64.tar.gz",
10
+ "x86_64-darwin" => "litestream-#{VERSION}-darwin-amd64.zip",
11
+ "x86_64-linux" => "litestream-#{VERSION}-linux-amd64.tar.gz"
12
+ }
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Litestream
2
+ VERSION = "0.13.0"
3
+ end