envless-env 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +13 -0
- data/README.md +117 -0
- data/exe/envless-env +6 -0
- data/lib/envless/env/api.rb +35 -0
- data/lib/envless/env/binding.rb +26 -0
- data/lib/envless/env/cache.rb +54 -0
- data/lib/envless/env/cache_writer.rb +34 -0
- data/lib/envless/env/coerce.rb +53 -0
- data/lib/envless/env/credentials.rb +27 -0
- data/lib/envless/env/crypto.rb +35 -0
- data/lib/envless/env/decryptor.rb +35 -0
- data/lib/envless/env/environment_writer.rb +43 -0
- data/lib/envless/env/errors.rb +47 -0
- data/lib/envless/env/files.rb +44 -0
- data/lib/envless/env/injected.rb +50 -0
- data/lib/envless/env/isolated.rb +45 -0
- data/lib/envless/env/keyring.rb +54 -0
- data/lib/envless/env/load.rb +5 -0
- data/lib/envless/env/loader.rb +83 -0
- data/lib/envless/env/messages.rb +36 -0
- data/lib/envless/env/override.rb +17 -0
- data/lib/envless/env/rails.rb +21 -0
- data/lib/envless/env/serialisation.rb +50 -0
- data/lib/envless/env/settings.rb +39 -0
- data/lib/envless/env/store.rb +91 -0
- data/lib/envless/env/target.rb +19 -0
- data/lib/envless/env/types/collect.rb +59 -0
- data/lib/envless/env/types/command.rb +64 -0
- data/lib/envless/env/types/declared.rb +43 -0
- data/lib/envless/env/types/render.rb +52 -0
- data/lib/envless/env/types.rb +6 -0
- data/lib/envless/env/update_notice.rb +64 -0
- data/lib/envless/env/values.rb +90 -0
- data/lib/envless/env/version.rb +7 -0
- data/lib/envless/env.rb +63 -0
- data/sig/envless/env/loading.rbs +84 -0
- data/sig/envless/env/settings.rbs +84 -0
- data/sig/envless/env/state.rbs +70 -0
- data/sig/envless/env/types.rbs +56 -0
- data/sig/envless/env/values.rbs +75 -0
- data/sig/envless/env.rbs +66 -0
- data/sig/manifest.yaml +2 -0
- metadata +101 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Envless
|
|
4
|
+
module Env
|
|
5
|
+
module UpdateNotice
|
|
6
|
+
HOST = "rubygems.org"
|
|
7
|
+
PATH = "/api/v1/versions/envless-env/latest.json"
|
|
8
|
+
CHANGELOG = "https://envless.cloud/docs/pull/ruby#changelog"
|
|
9
|
+
TIMEOUT = 2
|
|
10
|
+
LOCK = Mutex.new
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def check
|
|
14
|
+
Thread.new { announce(latest) } if claim
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def claim
|
|
19
|
+
LOCK.synchronize do
|
|
20
|
+
if @checked || disabled?
|
|
21
|
+
false
|
|
22
|
+
else
|
|
23
|
+
@checked = true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def disabled?
|
|
29
|
+
!ENV.fetch(Settings::ENV_VARS[:DISABLE_UPDATE_NOTICE], "").empty? || !$stdout.tty?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def latest
|
|
33
|
+
response = Net::HTTP.start(HOST, 443, use_ssl: true, open_timeout: TIMEOUT, read_timeout: TIMEOUT,
|
|
34
|
+
write_timeout: TIMEOUT) { |http| http.request_get(PATH) }
|
|
35
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
36
|
+
|
|
37
|
+
version = JSON.parse(response.body.to_s)["version"]
|
|
38
|
+
version.is_a?(String) ? version : nil
|
|
39
|
+
rescue
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def announce(latest)
|
|
44
|
+
return unless latest && newer?(latest, VERSION)
|
|
45
|
+
|
|
46
|
+
warn format(Messages::UPDATE_AVAILABLE, latest:, current: VERSION, changelog: CHANGELOG)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def newer?(candidate, current)
|
|
50
|
+
left = parts(candidate)
|
|
51
|
+
right = parts(current)
|
|
52
|
+
return false if left.nil? || right.nil?
|
|
53
|
+
|
|
54
|
+
(left <=> right) == 1
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def parts(value)
|
|
58
|
+
numbers = value.strip.delete_prefix("v").split(".").map { |part| Integer(part, 10, exception: false) }
|
|
59
|
+
(numbers.size == 3 && numbers.none?(&:nil?)) ? numbers.compact : nil
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Envless
|
|
4
|
+
module Env
|
|
5
|
+
class Values
|
|
6
|
+
UNSET = Object.new.freeze
|
|
7
|
+
READABLE = /\A[A-Z][A-Za-z0-9_]*\z/
|
|
8
|
+
|
|
9
|
+
include Serialisation
|
|
10
|
+
|
|
11
|
+
def initialize(store, optional: false, client: false)
|
|
12
|
+
@store = store
|
|
13
|
+
@optional = optional
|
|
14
|
+
@client = client
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def [](name)
|
|
19
|
+
key = name.to_s
|
|
20
|
+
raise NotLoadedError, Messages::NOT_LOADED unless @store.readable?
|
|
21
|
+
return absent(key) unless @store.has?(key)
|
|
22
|
+
raise ServerOnlyError, key unless visible?(key)
|
|
23
|
+
|
|
24
|
+
Coerce.call(@store.raw(key).to_s, @store.meta_of(key)[:type])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def []=(_name, _value)
|
|
28
|
+
raise ReadOnlyError
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fetch(name, default = UNSET)
|
|
32
|
+
key = name.to_s
|
|
33
|
+
return self[key] if key?(key)
|
|
34
|
+
return yield(key) if block_given?
|
|
35
|
+
return default unless default.equal?(UNSET)
|
|
36
|
+
|
|
37
|
+
raise MissingVariableError, key
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def key?(name)
|
|
41
|
+
key = name.to_s
|
|
42
|
+
@store.readable? && @store.has?(key) && visible?(key)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def keys
|
|
46
|
+
@store.keys.select { |name| visible?(name) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def optional
|
|
50
|
+
Values.new(@store, optional: true, client: @client)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def client
|
|
54
|
+
Values.new(@store, optional: @optional, client: true)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def client?
|
|
58
|
+
@client
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def optional?
|
|
62
|
+
@optional
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def method_missing(name, *arguments, &block)
|
|
66
|
+
text = name.to_s
|
|
67
|
+
return self[text] if arguments.empty? && block.nil? && READABLE.match?(text)
|
|
68
|
+
raise ReadOnlyError if text.end_with?("=") && READABLE.match?(text.chomp("="))
|
|
69
|
+
|
|
70
|
+
super
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def respond_to_missing?(name, include_private = false)
|
|
74
|
+
(READABLE.match?(name.to_s) && key?(name)) || super
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def visible?(name)
|
|
80
|
+
!@client || @store.meta_of(name)[:visibility] == Settings::VISIBILITY[:CLIENT]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def absent(key)
|
|
84
|
+
return nil if @optional
|
|
85
|
+
|
|
86
|
+
raise MissingVariableError, key
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/envless/env.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
|
+
require "monitor"
|
|
6
|
+
require "net/http"
|
|
7
|
+
require "time"
|
|
8
|
+
require "envless/sdk"
|
|
9
|
+
|
|
10
|
+
%w[
|
|
11
|
+
version settings messages errors files binding credentials target keyring api cache cache_writer decryptor
|
|
12
|
+
injected store environment_writer update_notice loader coerce serialisation values override isolated crypto
|
|
13
|
+
].each { |name| require_relative "env/#{name}" }
|
|
14
|
+
|
|
15
|
+
module Envless
|
|
16
|
+
module Env
|
|
17
|
+
ENCRYPTION_PARAMETERS = Envless::ENCRYPTION_PARAMETERS
|
|
18
|
+
STORE = Store.new
|
|
19
|
+
LOADER = Loader.new(STORE, EnvironmentWriter.new)
|
|
20
|
+
VALUES = Values.new(STORE)
|
|
21
|
+
|
|
22
|
+
extend Crypto
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
def load(revalidate: false)
|
|
26
|
+
LOADER.load(revalidate:)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def values
|
|
30
|
+
VALUES
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def optional
|
|
34
|
+
VALUES.optional
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def client
|
|
38
|
+
VALUES.client
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def override(values)
|
|
42
|
+
layer = values.to_h { |name, value| [name.to_s, value.to_s] }.freeze
|
|
43
|
+
STORE.push(layer)
|
|
44
|
+
return Override.new(STORE, layer) unless block_given?
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
yield
|
|
48
|
+
ensure
|
|
49
|
+
STORE.pop(layer)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def fetch_environment(token:, project:, environment:, product: nil, passphrase: nil, key: nil, workspace_id: nil,
|
|
54
|
+
endpoint: nil, adapter: nil)
|
|
55
|
+
Isolated.call(token:, project:, environment:, product:, passphrase:, key:, workspace_id:, endpoint:, adapter:)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.env
|
|
61
|
+
Env::VALUES
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module Envless
|
|
2
|
+
module Env
|
|
3
|
+
class Binding < Data
|
|
4
|
+
attr_reader workspace: String
|
|
5
|
+
attr_reader product: String?
|
|
6
|
+
attr_reader project: String
|
|
7
|
+
attr_reader environments: Array[String]
|
|
8
|
+
|
|
9
|
+
def self.new: (workspace: String, product: String?, project: String, environments: Array[String]) -> Binding
|
|
10
|
+
def self.read: (?String directory) -> Binding?
|
|
11
|
+
def self.listed?: (untyped environments) -> bool
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Target < Data
|
|
15
|
+
attr_reader workspace: String?
|
|
16
|
+
attr_reader product: String?
|
|
17
|
+
attr_reader project: String
|
|
18
|
+
attr_reader environment: String
|
|
19
|
+
|
|
20
|
+
def self.new: (workspace: String?, product: String?, project: String, environment: String) -> Target
|
|
21
|
+
def self.resolve: (?Binding? binding) -> Target
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Keyring
|
|
25
|
+
PINNED: Regexp
|
|
26
|
+
DEFAULT_ITERATIONS: Integer
|
|
27
|
+
|
|
28
|
+
@key: Envless::WorkspaceKey
|
|
29
|
+
@passphrase: String?
|
|
30
|
+
@workspace_id: String?
|
|
31
|
+
|
|
32
|
+
def self.from_environment: (Api api) -> Keyring
|
|
33
|
+
def self.imported: (String raw) -> Keyring
|
|
34
|
+
def self.derived: (String passphrase, String? workspace_id) -> Keyring
|
|
35
|
+
def initialize: (Envless::WorkspaceKey key, ?passphrase: String?, ?workspace_id: String?) -> void
|
|
36
|
+
def decrypt: (String value) -> String
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Api
|
|
40
|
+
FAILURES: Array[singleton(StandardError)]
|
|
41
|
+
|
|
42
|
+
@client: Envless::Client
|
|
43
|
+
|
|
44
|
+
def initialize: (String token, ?endpoint: String?, ?adapter: Envless::_Adapter?) -> void
|
|
45
|
+
def workspace_id: () -> String?
|
|
46
|
+
def variables: (Target target) -> Array[variable]
|
|
47
|
+
def close: () -> nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module Cache
|
|
51
|
+
def self?.path: (Target target) -> String?
|
|
52
|
+
def self?.sanitize: (String slug) -> String
|
|
53
|
+
def self?.read: (Target target) -> { variables: Array[variable], cached_at: String }?
|
|
54
|
+
def self?.entry_of: (untyped entry) -> variable?
|
|
55
|
+
def self?.fresh?: (String cached_at, ?Time now) -> bool
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
module CacheWriter
|
|
59
|
+
def self?.write: (Target target, Array[variable] variables, ?Time now) -> void
|
|
60
|
+
def self?.payload: (Target target, Array[variable] variables, Time now) -> Hash[Symbol, untyped]
|
|
61
|
+
def self?.replace: (String location, String content) -> void
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
module Decryptor
|
|
65
|
+
class Outcome < Data
|
|
66
|
+
attr_reader entries: Array[[String, String]]?
|
|
67
|
+
attr_reader failed: String?
|
|
68
|
+
|
|
69
|
+
def self.new: (entries: Array[[String, String]]?, failed: String?) -> Outcome
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
FAILURES: Array[singleton(StandardError)]
|
|
73
|
+
|
|
74
|
+
def self?.call: (Array[variable] variables, Keyring keyring) -> Outcome
|
|
75
|
+
def self?.attempt: (Keyring keyring, String value) -> String?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
module Isolated
|
|
79
|
+
def self?.call: (token: String, project: String, environment: String, ?product: String?, ?passphrase: String?, ?key: String?, ?workspace_id: String?, ?endpoint: String?, ?adapter: Envless::_Adapter?) -> Hash[String, String]
|
|
80
|
+
def self?.decrypted: (Api api, Keyring keyring, String? product, String project, String environment) -> Hash[String, String]
|
|
81
|
+
def self?.keyring_of: (Api api, String? passphrase, String? key, String? workspace_id) -> Keyring
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module Envless
|
|
2
|
+
module Env
|
|
3
|
+
module Settings
|
|
4
|
+
ENV_VARS: { TOKEN: String, KEY: String, PASSPHRASE: String, PRODUCT: String, PROJECT: String, ENV: String, DISABLE_UPDATE_NOTICE: String }
|
|
5
|
+
MACHINE_TOKEN_PREFIX: String
|
|
6
|
+
VAR_TYPES: { STRING: String, NUMBER: String, BOOLEAN: String }
|
|
7
|
+
VISIBILITY: { SERVER: String, CLIENT: String }
|
|
8
|
+
DEFAULT_TYPE: String
|
|
9
|
+
DEFAULT_VISIBILITY: String
|
|
10
|
+
CACHE_MAX_AGE: Integer
|
|
11
|
+
CACHE_NO_PRODUCT: String
|
|
12
|
+
CACHE_UNSAFE_CHARACTERS: Regexp
|
|
13
|
+
CONFIG_DIR: String
|
|
14
|
+
CONFIG_FILE: String
|
|
15
|
+
CACHE_DIR: String
|
|
16
|
+
ENVLESS_FILE: String
|
|
17
|
+
DIR_MODE: Integer
|
|
18
|
+
FILE_MODE: Integer
|
|
19
|
+
PAGE_LIMIT: Integer
|
|
20
|
+
INJECTED: { NAME: String, TYPE_KEY: String, VISIBILITY_KEY: String }
|
|
21
|
+
UNSAFE_PROCESS_ENV_NAMES: Array[String]
|
|
22
|
+
UNSAFE_NAMES: Array[String]
|
|
23
|
+
USER_AGENT: String
|
|
24
|
+
LOG_PREFIX: String
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module Messages
|
|
28
|
+
TOKEN_MISSING: String
|
|
29
|
+
TOKEN_NOT_MACHINE: String
|
|
30
|
+
PROJECT_MISSING: String
|
|
31
|
+
ENVIRONMENT_MISSING: String
|
|
32
|
+
KEY_MATERIAL_MISSING: String
|
|
33
|
+
KEY_INVALID: String
|
|
34
|
+
WORKSPACE_UNRESOLVED: String
|
|
35
|
+
FETCH_FAILED: String
|
|
36
|
+
DECRYPT_FAILED: String
|
|
37
|
+
NOT_LOADED: String
|
|
38
|
+
MISSING_VARIABLE: String
|
|
39
|
+
READ_ONLY: String
|
|
40
|
+
SERVER_ONLY: String
|
|
41
|
+
NOT_SERIALISABLE: String
|
|
42
|
+
UNSAFE_REFUSED: String
|
|
43
|
+
UNWRITABLE: String
|
|
44
|
+
ISOLATED_NO_TOKEN: String
|
|
45
|
+
ISOLATED_NO_TARGET: String
|
|
46
|
+
ISOLATED_NO_KEY_MATERIAL: String
|
|
47
|
+
ISOLATED_NO_WORKSPACE: String
|
|
48
|
+
ISOLATED_DECRYPT_FAILED: String
|
|
49
|
+
UPDATE_AVAILABLE: String
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
module Files
|
|
53
|
+
def self?.json: (String path) -> Hash[String, untyped]?
|
|
54
|
+
def self?.home: () -> String?
|
|
55
|
+
def self?.config_dir: () -> String?
|
|
56
|
+
def self?.text?: (untyped value) -> bool
|
|
57
|
+
def self?.variable: (String name) -> String?
|
|
58
|
+
def self?.utf8: (String text) -> String
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
module Credentials
|
|
62
|
+
def self?.token: () -> String?
|
|
63
|
+
def self?.machine_token: () -> String
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
module UpdateNotice
|
|
67
|
+
HOST: String
|
|
68
|
+
PATH: String
|
|
69
|
+
CHANGELOG: String
|
|
70
|
+
TIMEOUT: Integer
|
|
71
|
+
LOCK: Thread::Mutex
|
|
72
|
+
|
|
73
|
+
self.@checked: bool
|
|
74
|
+
|
|
75
|
+
def self.check: () -> nil
|
|
76
|
+
def self.claim: () -> bool
|
|
77
|
+
def self.disabled?: () -> bool
|
|
78
|
+
def self.latest: () -> String?
|
|
79
|
+
def self.announce: (String? latest) -> void
|
|
80
|
+
def self.newer?: (String candidate, String current) -> bool
|
|
81
|
+
def self.parts: (String value) -> Array[Integer]?
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Envless
|
|
2
|
+
module Env
|
|
3
|
+
module Injected
|
|
4
|
+
TYPES: Array[String]
|
|
5
|
+
VISIBILITIES: Array[String]
|
|
6
|
+
|
|
7
|
+
def self?.seed: () -> [Hash[String, String], Hash[String, meta]]?
|
|
8
|
+
def self?.parse: (String raw) -> untyped
|
|
9
|
+
def self?.meta_of: (untyped entry) -> meta
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Store
|
|
13
|
+
@values: Hash[String, String]
|
|
14
|
+
@meta: Hash[String, meta]
|
|
15
|
+
@overrides: Array[Hash[String, String]]
|
|
16
|
+
@loaded: bool
|
|
17
|
+
@injected: bool
|
|
18
|
+
@seeded: bool
|
|
19
|
+
@lock: Monitor
|
|
20
|
+
|
|
21
|
+
def initialize: () -> void
|
|
22
|
+
def replace: (Hash[String, String] record, Hash[String, meta] meta) -> void
|
|
23
|
+
def has?: (String name) -> bool
|
|
24
|
+
def raw: (String name) -> String?
|
|
25
|
+
def meta_of: (String name) -> meta
|
|
26
|
+
def keys: () -> Array[String]
|
|
27
|
+
def readable?: () -> bool
|
|
28
|
+
def injected?: () -> bool
|
|
29
|
+
def snapshot: () -> Hash[String, String]
|
|
30
|
+
def push: (Hash[String, String] layer) -> void
|
|
31
|
+
def pop: (Hash[String, String] layer) -> nil
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def guarded: [T] () { () -> T } -> T
|
|
36
|
+
def default_meta: () -> meta
|
|
37
|
+
def seed: () -> void
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class EnvironmentWriter
|
|
41
|
+
@owned: Array[String]
|
|
42
|
+
|
|
43
|
+
def initialize: () -> void
|
|
44
|
+
def write: (Hash[String, String] record) -> void
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def writable?: (String name, String value) -> bool
|
|
49
|
+
def report: (Array[String] refused, Array[String] unwritable) -> void
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class Loader
|
|
53
|
+
@store: Store
|
|
54
|
+
@writer: EnvironmentWriter
|
|
55
|
+
@lock: Thread::Mutex
|
|
56
|
+
@memo: Hash[String, String]?
|
|
57
|
+
|
|
58
|
+
def initialize: (Store store, EnvironmentWriter writer) -> void
|
|
59
|
+
def load: (?revalidate: bool) -> Hash[String, String]
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def fresh: (bool revalidate) -> Hash[String, String]
|
|
64
|
+
def from_cache: (Target target, Keyring keyring) -> Hash[String, String]?
|
|
65
|
+
def fetched: (Api api, Target target, Keyring keyring) -> Hash[String, String]
|
|
66
|
+
def fetch: (Api api, Target target) -> Array[variable]
|
|
67
|
+
def finish: (Array[[String, String]] entries, Array[variable] sources) -> Hash[String, String]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Envless
|
|
2
|
+
module Env
|
|
3
|
+
module Types
|
|
4
|
+
class Declared
|
|
5
|
+
RUBY_TYPES: Hash[String, String]
|
|
6
|
+
LITERAL: Regexp
|
|
7
|
+
|
|
8
|
+
attr_reader name: String
|
|
9
|
+
attr_reader type: String
|
|
10
|
+
attr_reader visibility: String
|
|
11
|
+
|
|
12
|
+
def self.new: (name: String, type: String, visibility: String) -> instance
|
|
13
|
+
def self.build: (String name, String? type, String? visibility) -> Declared
|
|
14
|
+
def self.known: (String? value, Hash[Symbol, String] allowed, String fallback) -> String
|
|
15
|
+
def self.merged: (Array[Declared] declared) -> Array[Declared]
|
|
16
|
+
def rbs: (bool optional) -> String
|
|
17
|
+
def client?: () -> bool
|
|
18
|
+
def method?: () -> bool
|
|
19
|
+
def literal?: () -> bool
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module Collect
|
|
23
|
+
def self?.call: (?project: String?, ?product: String?, ?env: String?) -> Array[Declared]
|
|
24
|
+
def self?.from_manifest: () -> Array[Declared]?
|
|
25
|
+
def self?.from_api: (String token, String project, Array[String] slugs, String? product) -> Array[Declared]
|
|
26
|
+
def self?.declared: (Api api, Target target) -> Array[Declared]
|
|
27
|
+
def self?.first: (*String? candidates) -> String?
|
|
28
|
+
def self?.slugs: (String? value) -> Array[String]?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module Render
|
|
32
|
+
HEADER: Array[String]
|
|
33
|
+
VIEWS: Hash[String, { client: bool, optional: bool, views: Array[String] }]
|
|
34
|
+
FALLBACK: String
|
|
35
|
+
|
|
36
|
+
def self?.call: (Array[Declared] declared) -> String
|
|
37
|
+
def self?.untyped: (Array[Declared] entries) -> Array[String]
|
|
38
|
+
def self?.view: (String name, { client: bool, optional: bool, views: Array[String] } view, Array[Declared] entries) -> Array[String]
|
|
39
|
+
def self?.index: (Array[Declared] entries, bool optional) -> Array[String]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module Command
|
|
43
|
+
OUTPUT: String
|
|
44
|
+
PREFIX: String
|
|
45
|
+
USAGE: String
|
|
46
|
+
RUN_HINT: String
|
|
47
|
+
|
|
48
|
+
def self?.call: (Array[String] arguments, ?out: IO, ?err: IO) -> Integer
|
|
49
|
+
def self?.parse: (Array[String] arguments) -> Hash[Symbol, untyped]
|
|
50
|
+
def self?.run: (Hash[Symbol, untyped] options, IO out, IO err) -> Integer
|
|
51
|
+
def self?.write: (String path, String text, Integer count, bool check, IO out, IO err) -> Integer
|
|
52
|
+
def self?.report: (IO stream, String message, Integer code) -> Integer
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Envless
|
|
2
|
+
module Env
|
|
3
|
+
module Coerce
|
|
4
|
+
WHITESPACE: String
|
|
5
|
+
DECIMAL: Regexp
|
|
6
|
+
INFINITY: Regexp
|
|
7
|
+
RADIX: Regexp
|
|
8
|
+
BASES: Hash[String, Integer]
|
|
9
|
+
TRUE_VALUES: Array[String]
|
|
10
|
+
|
|
11
|
+
def self?.call: (String value, String type) -> value
|
|
12
|
+
def self?.number: (String value) -> (Integer | Float)
|
|
13
|
+
def self?.trim: (String text) -> String
|
|
14
|
+
def self?.radix_number: (String digits, Integer base) -> (Integer | Float)
|
|
15
|
+
def self?.integral: (Float parsed, String text) -> (Integer | Float)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
interface _View
|
|
19
|
+
def client?: () -> bool
|
|
20
|
+
def optional?: () -> bool
|
|
21
|
+
def keys: () -> Array[String]
|
|
22
|
+
def []: (String | Symbol name) -> value?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module Serialisation : _View
|
|
26
|
+
def to_h: () -> Hash[String, value?]
|
|
27
|
+
def snapshot: () -> Hash[String, value?]
|
|
28
|
+
def as_json: (*untyped) -> Hash[String, value?]
|
|
29
|
+
def to_json: (*untyped) -> String
|
|
30
|
+
def marshal_dump: () -> bot
|
|
31
|
+
def encode_with: (untyped coder) -> bot
|
|
32
|
+
def dup: () -> self
|
|
33
|
+
def clone: (?freeze: bool?) -> self
|
|
34
|
+
def inspect: () -> String
|
|
35
|
+
def to_s: () -> String
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class Values
|
|
39
|
+
UNSET: Object
|
|
40
|
+
READABLE: Regexp
|
|
41
|
+
|
|
42
|
+
include Serialisation
|
|
43
|
+
|
|
44
|
+
@store: Store
|
|
45
|
+
@optional: bool
|
|
46
|
+
@client: bool
|
|
47
|
+
|
|
48
|
+
def initialize: (Store store, ?optional: bool, ?client: bool) -> void
|
|
49
|
+
def []: (String | Symbol name) -> value?
|
|
50
|
+
def []=: (String | Symbol name, untyped value) -> bot
|
|
51
|
+
def fetch: (String | Symbol name, ?untyped default) ?{ (String) -> untyped } -> untyped
|
|
52
|
+
def key?: (String | Symbol name) -> bool
|
|
53
|
+
def keys: () -> Array[String]
|
|
54
|
+
def optional: () -> Values
|
|
55
|
+
def client: () -> Values
|
|
56
|
+
def client?: () -> bool
|
|
57
|
+
def optional?: () -> bool
|
|
58
|
+
def method_missing: (Symbol name, *untyped arguments) ?{ (*untyped) -> untyped } -> untyped
|
|
59
|
+
def respond_to_missing?: (Symbol | String name, ?bool include_private) -> bool
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def visible?: (String name) -> bool
|
|
64
|
+
def absent: (String key) -> nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class Override
|
|
68
|
+
@store: Store
|
|
69
|
+
@layer: Hash[String, String]
|
|
70
|
+
|
|
71
|
+
def initialize: (Store store, Hash[String, String] layer) -> void
|
|
72
|
+
def restore: () -> nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
data/sig/envless/env.rbs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Envless
|
|
2
|
+
def self.env: () -> Env::Values
|
|
3
|
+
|
|
4
|
+
module Env
|
|
5
|
+
VERSION: String
|
|
6
|
+
ENCRYPTION_PARAMETERS: untyped
|
|
7
|
+
STORE: Store
|
|
8
|
+
LOADER: Loader
|
|
9
|
+
VALUES: Values
|
|
10
|
+
|
|
11
|
+
type value = String | Integer | Float | bool
|
|
12
|
+
type meta = { type: String, visibility: String }
|
|
13
|
+
type variable = { name: String, value: String, type: String?, visibility: String? }
|
|
14
|
+
|
|
15
|
+
extend Crypto
|
|
16
|
+
|
|
17
|
+
def self.load: (?revalidate: bool) -> Hash[String, String]
|
|
18
|
+
def self.values: () -> Values
|
|
19
|
+
def self.optional: () -> Values
|
|
20
|
+
def self.client: () -> Values
|
|
21
|
+
def self.override: (Hash[String | Symbol, untyped] values) -> Override
|
|
22
|
+
| [T] (Hash[String | Symbol, untyped] values) { () -> T } -> T
|
|
23
|
+
def self.fetch_environment: (token: String, project: String, environment: String, ?product: String?, ?passphrase: String?, ?key: String?, ?workspace_id: String?, ?endpoint: String?, ?adapter: Envless::_Adapter?) -> Hash[String, String]
|
|
24
|
+
|
|
25
|
+
module Crypto
|
|
26
|
+
def encrypt: (String plaintext, String passphrase, String workspace_unique_id) -> String
|
|
27
|
+
def decrypt: (String ciphertext, String passphrase, String workspace_unique_id) -> String
|
|
28
|
+
def encrypt_with_key: (Envless::WorkspaceKey | String key, String plaintext) -> String
|
|
29
|
+
def decrypt_with_key: (Envless::WorkspaceKey | String key, String ciphertext) -> String
|
|
30
|
+
def workspace_key: (String passphrase, String workspace_unique_id) -> Envless::WorkspaceKey
|
|
31
|
+
def import_workspace_key: (String material) -> Envless::WorkspaceKey
|
|
32
|
+
def passphrase_strength: (untyped passphrase) -> Envless::Validation
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Error < Envless::Error
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class LoadError < Error
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class NotLoadedError < Error
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class VariableError < Error
|
|
45
|
+
attr_reader variable: String
|
|
46
|
+
|
|
47
|
+
def initialize: (String variable, String template) -> void
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class MissingVariableError < VariableError
|
|
51
|
+
def initialize: (String variable) -> void
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class ServerOnlyError < VariableError
|
|
55
|
+
def initialize: (String variable) -> void
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class ReadOnlyError < Error
|
|
59
|
+
def initialize: (?String message) -> void
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class SerialisationError < Error
|
|
63
|
+
def initialize: (?String message) -> void
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/sig/manifest.yaml
ADDED