azure-blob 0.4.2 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7eab2543de0e2e4663211095fd459507761bf270038308464723270506f63ac5
4
- data.tar.gz: 1c0b7016bf21df9c1134be50eb2555f14f9f4b76e9a8664e70e003cee5b04208
3
+ metadata.gz: 9a83c96b748a5659438b382a79a1af068c1552f4ee895dd16b863b00758d8d1f
4
+ data.tar.gz: f6def3a97bde3b96ef53618502454b3c3bd0103e07332ed19f817d3298971860
5
5
  SHA512:
6
- metadata.gz: 6f03e61001c0c41ed31661116d5f7ed5d5f95f91e8ded26d40aa72d446119e835c281d852904e00b02f68fd536d0451e4e71401410ab41ec68f7ff97fb030a88
7
- data.tar.gz: b0b1beb93ad7b3950bddb41dfb6e04354e08b46e29f5f3db5d474ddd4964cd09d65776a1f57625d103d1e56e9a0d235579372c74a867fa2080beabcfc757395f
6
+ metadata.gz: 20f51dbd1ea9661fec78d1371f52459aaf00a4a42e7274f375bd81c7cfc89bac9dfba1546af7cf71f7fea5fd8479fa7ceab9605ea1a8d0ceef7099e0212d4738
7
+ data.tar.gz: 74939a0d48677079ac46585276369a4d090d8bcbdd4e0ab84d37a8a48d2e54977dec5541b823f0a08c7642787ce13041a54311963010a5449424f13a181e72de
data/CHANGELOG.md CHANGED
@@ -1,8 +1,18 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.1] 2024-09-09
4
+
5
+ - Remove dev files from the release
6
+
7
+ ## [0.5.0] 2024-09-09
8
+
9
+ - Added support for Managed Identities (Entra ID)
10
+
3
11
  ## [0.4.2] 2024-06-06
4
12
 
5
- Documentation
13
+ - Documentation
14
+ - Fix an issue with integrity check on multi block upload
15
+
6
16
 
7
17
  ## [0.4.1] 2024-05-27
8
18
 
data/README.md CHANGED
@@ -4,7 +4,7 @@ This gem was built to replace azure-storage-blob (deprecated) in Active Storage,
4
4
 
5
5
  ## Active Storage
6
6
 
7
- ## Migration
7
+ ### Migration
8
8
  To migrate from azure-storage-blob to azure-blob:
9
9
 
10
10
  1. Replace `azure-storage-blob` in your Gemfile with `azure-blob`
@@ -12,6 +12,29 @@ To migrate from azure-storage-blob to azure-blob:
12
12
  3. Change the `AzureStorage` service to `AzureBlob` in your Active Storage config (`config/storage.yml`)
13
13
  4. Restart or deploy the app.
14
14
 
15
+ ### Managed Identity (Entra ID)
16
+
17
+ AzureBlob supports managed identities on :
18
+ - Azure VM
19
+ - App Service
20
+ - Azure Functions (Untested but should work)
21
+ - Azure Containers (Untested but should work)
22
+
23
+ AKS support will likely require more work. Contributions are welcome.
24
+
25
+ To authenticate through managed identities instead of a shared key, omit `storage_access_key` from your `storage.yml` file.
26
+
27
+ It is recommended to add the identity's `principal_id` to the config.
28
+
29
+ ActiveStorage config example:
30
+
31
+ ```
32
+ prod:
33
+ service: AzureBlob
34
+ container: container_name
35
+ storage_account_name: account_name
36
+ principal_id: 71b34410-4c50-451d-b456-95ead1b18cce
37
+ ```
15
38
 
16
39
  ## Standalone
17
40
 
@@ -42,6 +65,42 @@ For the full list of methods: https://www.rubydoc.info/gems/azure-blob/AzureBlob
42
65
 
43
66
  ### Dev environment
44
67
 
68
+ A dev environment is supplied through Nix with [devenv](https://devenv.sh/).
69
+
70
+ 1. Install [devenv](https://devenv.sh/).
71
+ 2. Enter the dev environment by cd into the repo and running `devenv shell` (or `direnv allow` if you are a direnv user).
72
+ 3. Log into azure CLI with `az login`
73
+ 4. `terraform init`
74
+ 5. `terraform apply` This will generate the necessary infrastructure on azure.
75
+ 6. Generate devenv.local.nix with your private key and container information: `generate-env-file`
76
+ 7. If you are using direnv, the environment will reload automatically. If not, exit the shell and reopen it by hitting <C-d> and running `devenv shell` again.
77
+
78
+ #### Entra ID
79
+
80
+ To test with Entra ID, the `AZURE_ACCESS_KEY` environment variable must be unset and the code must be ran or proxied through a VPS with the proper roles.
81
+
82
+ For cost saving, the terraform variable `create_vm` is false by default.
83
+ To create the VPS, Create a var file `var.tfvars` containing:
84
+
85
+ ```
86
+ create_vm = true
87
+ ```
88
+ and re-apply terraform: `terraform apply -var-file=var.tfvars`.
89
+
90
+ This will create the VPS and required managed identities.
91
+
92
+ `bin/rake test_azure_vm` and `bin/rake test_app_service` will establish a VPN connection to the VM or App service container and run the test suite. You might be prompted for a sudo password when the VPN starts (sshuttle).
93
+
94
+ After you are done, run terraform again without the var file (`terraform apply`) to destroy the VPS and App service application.
95
+
96
+ #### Cleanup
97
+
98
+ Some tests copied over from Rails don't clean after themselves. A rake task is provided to empty your containers and keep cost low: `bin/rake flush_test_container`
99
+
100
+ #### Run without devenv/nix
101
+
102
+ If you prefer not using devenv/nix:
103
+
45
104
  Ensure your version of Ruby fit the minimum version in `azure-blob.gemspec`
46
105
 
47
106
  and setup those Env variables:
@@ -51,19 +110,6 @@ and setup those Env variables:
51
110
  - `AZURE_PRIVATE_CONTAINER`
52
111
  - `AZURE_PUBLIC_CONTAINER`
53
112
 
54
-
55
- A dev environment setup is also supplied through Nix with [devenv](https://devenv.sh/).
56
-
57
- To use the Nix environment:
58
- 1. install [devenv](https://devenv.sh/)
59
- 2. Copy `devenv.local.nix.example` to `devenv.local.nix`
60
- 3. Insert your azure credentials into `devenv.local.nix`
61
- 4. Start the shell with `devenv shell` or with [direnv](https://direnv.net/).
62
-
63
- ### Tests
64
-
65
- `bin/rake test`
66
-
67
113
  ## License
68
114
 
69
115
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -2,21 +2,57 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "minitest/test_task"
5
- require 'azure_blob'
5
+ require "azure_blob"
6
+ require_relative "test/support/app_service_vpn"
7
+ require_relative "test/support/azure_vm_vpn"
6
8
 
7
- Minitest::TestTask.create
9
+ Minitest::TestTask.create(:test_rails) do
10
+ self.test_globs = [ "test/rails/**/test_*.rb",
11
+ "test/rails/**/*_test.rb", ]
12
+ end
13
+
14
+ Minitest::TestTask.create(:test_client) do
15
+ self.test_globs = [ "test/client/**/test_*.rb",
16
+ "test/client/**/*_test.rb", ]
17
+ end
8
18
 
9
19
  task default: %i[test]
10
20
 
21
+ task :test do
22
+ Rake::Task["test_client"].execute
23
+ Rake::Task["test_rails"].execute
24
+ end
25
+
26
+ task :test_app_service do |t|
27
+ vpn = AppServiceVpn.new
28
+ ENV["IDENTITY_ENDPOINT"] = vpn.endpoint
29
+ ENV["IDENTITY_HEADER"] = vpn.header
30
+ Rake::Task["test_entra_id"].execute
31
+ ensure
32
+ vpn.kill
33
+ end
34
+
35
+ task :test_azure_vm do |t|
36
+ vpn = AzureVmVpn.new
37
+ Rake::Task["test_entra_id"].execute
38
+ ensure
39
+ vpn.kill
40
+ end
41
+
42
+ task :test_entra_id do |t|
43
+ ENV["AZURE_ACCESS_KEY"] = nil
44
+ Rake::Task["test"].execute
45
+ end
46
+
11
47
  task :flush_test_container do |t|
12
48
  AzureBlob::Client.new(
13
49
  account_name: ENV["AZURE_ACCOUNT_NAME"],
14
50
  access_key: ENV["AZURE_ACCESS_KEY"],
15
51
  container: ENV["AZURE_PRIVATE_CONTAINER"],
16
- ).delete_prefix ''
52
+ ).delete_prefix ""
17
53
  AzureBlob::Client.new(
18
54
  account_name: ENV["AZURE_ACCOUNT_NAME"],
19
55
  access_key: ENV["AZURE_ACCESS_KEY"],
20
56
  container: ENV["AZURE_PUBLIC_CONTAINER"],
21
- ).delete_prefix ''
57
+ ).delete_prefix ""
22
58
  end
@@ -35,7 +35,7 @@ module ActiveStorage
35
35
  class Service::AzureBlobService < Service
36
36
  attr_reader :client, :container, :signer
37
37
 
38
- def initialize(storage_account_name:, storage_access_key:, container:, public: false, **options)
38
+ def initialize(storage_account_name:, storage_access_key: nil, container:, public: false, **options)
39
39
  @container = container
40
40
  @public = public
41
41
  @client = AzureBlob::Client.new(
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "signer"
4
3
  require_relative "block_list"
5
4
  require_relative "blob_list"
6
5
  require_relative "blob"
7
6
  require_relative "http"
7
+ require_relative "shared_key_signer"
8
+ require_relative "entra_id_signer"
8
9
  require "time"
9
10
  require "base64"
10
11
 
@@ -12,10 +13,13 @@ module AzureBlob
12
13
  # AzureBlob Client class. You interact with the Azure Blob api
13
14
  # through an instance of this class.
14
15
  class Client
15
- def initialize(account_name:, access_key:, container:)
16
+ def initialize(account_name:, access_key:, container:, **options)
16
17
  @account_name = account_name
17
18
  @container = container
18
- @signer = Signer.new(account_name:, access_key:)
19
+
20
+ @signer = !access_key.nil? && !access_key.empty? ?
21
+ AzureBlob::SharedKeySigner.new(account_name:, access_key:) :
22
+ AzureBlob::EntraIdSigner.new(account_name:, **options.slice(:principal_id))
19
23
  end
20
24
 
21
25
  # Create a blob of type block. Will automatically split the the blob in multiple block and send the blob in pieces (blocks) if the blob is too big.
@@ -0,0 +1,115 @@
1
+ require "base64"
2
+ require "openssl"
3
+ require "net/http"
4
+ require "rexml/document"
5
+
6
+ require_relative "canonicalized_resource"
7
+ require_relative "identity_token"
8
+
9
+ require_relative "user_delegation_key"
10
+
11
+ module AzureBlob
12
+ class EntraIdSigner # :nodoc:
13
+ attr_reader :token
14
+ attr_reader :account_name
15
+
16
+ def initialize(account_name:, principal_id: nil)
17
+ @token = AzureBlob::IdentityToken.new(principal_id:)
18
+ @account_name = account_name
19
+ end
20
+
21
+ def authorization_header(uri:, verb:, headers: {})
22
+ "Bearer #{token}"
23
+ end
24
+
25
+ def sas_token(uri, options = {})
26
+ to_sign = [
27
+ options[:permissions],
28
+ options[:start],
29
+ options[:expiry],
30
+ CanonicalizedResource.new(uri, account_name, url_safe: false, service_name: :blob),
31
+ delegation_key.signed_oid,
32
+ delegation_key.signed_tid,
33
+ delegation_key.signed_start,
34
+ delegation_key.signed_expiry,
35
+ delegation_key.signed_service,
36
+ delegation_key.signed_version,
37
+ nil,
38
+ nil,
39
+ nil,
40
+ options[:ip],
41
+ options[:protocol],
42
+ SAS::Version,
43
+ SAS::Resources::Blob,
44
+ nil,
45
+ nil,
46
+ nil,
47
+ options[:content_disposition],
48
+ nil,
49
+ nil,
50
+ options[:content_type],
51
+ ].join("\n")
52
+
53
+ query = {
54
+ SAS::Fields::Permissions => options[:permissions],
55
+ SAS::Fields::Start => options[:start],
56
+ SAS::Fields::Expiry => options[:expiry],
57
+
58
+ SAS::Fields::SignedObjectId => delegation_key.signed_oid,
59
+ SAS::Fields::SignedTenantId => delegation_key.signed_tid,
60
+ SAS::Fields::SignedKeyStartTime => delegation_key.signed_start,
61
+ SAS::Fields::SignedKeyExpiryTime => delegation_key.signed_expiry,
62
+ SAS::Fields::SignedKeyService => delegation_key.signed_service,
63
+ SAS::Fields::Signedkeyversion => delegation_key.signed_version,
64
+
65
+
66
+ SAS::Fields::SignedIp => options[:ip],
67
+ SAS::Fields::SignedProtocol => options[:protocol],
68
+ SAS::Fields::Version => SAS::Version,
69
+ SAS::Fields::Resource => SAS::Resources::Blob,
70
+
71
+ SAS::Fields::Disposition => options[:content_disposition],
72
+ SAS::Fields::Type => options[:content_type],
73
+ SAS::Fields::Signature => sign(to_sign, key: delegation_key.to_s),
74
+
75
+ }.reject { |_, value| value.nil? }
76
+
77
+ URI.encode_www_form(**query)
78
+ end
79
+
80
+ private
81
+
82
+ def delegation_key
83
+ @delegation_key ||= UserDelegationKey.new(account_name:, signer: self)
84
+ end
85
+
86
+ def sign(body, key:)
87
+ Base64.strict_encode64(OpenSSL::HMAC.digest("sha256", key, body))
88
+ end
89
+
90
+ module SAS # :nodoc:
91
+ Version = "2024-05-04"
92
+ module Fields # :nodoc:
93
+ Permissions = :sp
94
+ Version = :sv
95
+ Start = :st
96
+ Expiry = :se
97
+ Resource = :sr
98
+ Signature = :sig
99
+ Disposition = :rscd
100
+ Type = :rsct
101
+ SignedObjectId = :skoid
102
+ SignedTenantId = :sktid
103
+ SignedKeyStartTime = :skt
104
+ SignedKeyExpiryTime = :ske
105
+ SignedKeyService = :sks
106
+ Signedkeyversion = :skv
107
+ SignedIp = :sip
108
+ SignedProtocol = :spr
109
+ end
110
+ module Resources # :nodoc:
111
+ Blob = :b
112
+ end
113
+ end
114
+ end
115
+ end
@@ -7,7 +7,14 @@ require "rexml"
7
7
 
8
8
  module AzureBlob
9
9
  class Http # :nodoc:
10
- class Error < AzureBlob::Error; end
10
+ class Error < AzureBlob::Error
11
+ attr_reader :body, :status
12
+ def initialize(body: nil, status: nil)
13
+ @body = body
14
+ @status = status
15
+ super(body)
16
+ end
17
+ end
11
18
  class FileNotFoundError < Error; end
12
19
  class ForbidenError < Error; end
13
20
  class IntegrityError < Error; end
@@ -44,6 +51,15 @@ module AzureBlob
44
51
  true
45
52
  end
46
53
 
54
+ def post(content)
55
+ sign_request("POST") if signer
56
+ @response = http.start do |http|
57
+ http.post(uri, content, headers)
58
+ end
59
+ raise_error unless success?
60
+ response.body
61
+ end
62
+
47
63
  def head
48
64
  sign_request("HEAD") if signer
49
65
  @response = http.start do |http|
@@ -91,7 +107,7 @@ module AzureBlob
91
107
  end
92
108
 
93
109
  def raise_error
94
- raise error_from_response.new(@response.body)
110
+ raise error_from_response.new(body: @response.body, status: @response.code&.to_i)
95
111
  end
96
112
 
97
113
  def status
@@ -0,0 +1,65 @@
1
+ require "json"
2
+
3
+ module AzureBlob
4
+ class IdentityToken
5
+ RESOURCE_URI = "https://storage.azure.com/"
6
+ EXPIRATION_BUFFER = 600 # 10 minutes
7
+
8
+ IDENTITY_ENDPOINT = ENV["IDENTITY_ENDPOINT"] || "http://169.254.169.254/metadata/identity/oauth2/token"
9
+ API_VERSION = ENV["IDENTITY_ENDPOINT"] ? "2019-08-01" : "2018-02-01"
10
+
11
+ def initialize(principal_id: nil)
12
+ @identity_uri = URI.parse(IDENTITY_ENDPOINT)
13
+ params = {
14
+ 'api-version': API_VERSION,
15
+ resource: RESOURCE_URI,
16
+ }
17
+ params[:principal_id] = principal_id if principal_id
18
+ @identity_uri.query = URI.encode_www_form(params)
19
+ end
20
+
21
+ def to_s
22
+ refresh if expired?
23
+ token
24
+ end
25
+
26
+ private
27
+
28
+ def expired?
29
+ token.nil? || Time.now >= (expiration - EXPIRATION_BUFFER)
30
+ end
31
+
32
+ def refresh
33
+ headers = { "Metadata" => "true" }
34
+ headers["X-IDENTITY-HEADER"] = ENV["IDENTITY_HEADER"] if ENV["IDENTITY_HEADER"]
35
+
36
+ attempt = 0
37
+ begin
38
+ attempt += 1
39
+ response = JSON.parse(AzureBlob::Http.new(identity_uri, headers).get)
40
+ rescue AzureBlob::Http::Error => error
41
+ if should_retry?(error, attempt)
42
+ attempt = 1 if error.status == 410
43
+ delay = exponential_backoff(error, attempt)
44
+ Kernel.sleep(delay)
45
+ retry
46
+ end
47
+ raise
48
+ end
49
+ @token = response["access_token"]
50
+ @expiration = Time.at(response["expires_on"].to_i)
51
+ end
52
+
53
+ def should_retry?(error, attempt)
54
+ is_500 = error.status/500 == 1
55
+ (is_500 || [ 404, 408, 410, 429 ].include?(error.status)) && attempt < 5
56
+ end
57
+
58
+ def exponential_backoff(error, attempt)
59
+ EXPONENTIAL_BACKOFF[attempt -1] || raise(AzureBlob::Error.new("Exponential backoff out of bounds!"))
60
+ end
61
+ EXPONENTIAL_BACKOFF = [ 2, 6, 14, 30 ]
62
+
63
+ attr_reader :identity_uri, :expiration, :token
64
+ end
65
+ end
@@ -6,7 +6,7 @@ require_relative "canonicalized_headers"
6
6
  require_relative "canonicalized_resource"
7
7
 
8
8
  module AzureBlob
9
- class Signer # :nodoc:
9
+ class SharedKeySigner # :nodoc:
10
10
  def initialize(account_name:, access_key:)
11
11
  @account_name = account_name
12
12
  @access_key = Base64.decode64(access_key)
@@ -71,12 +71,12 @@ module AzureBlob
71
71
  URI.encode_www_form(**query)
72
72
  end
73
73
 
74
+ private
75
+
74
76
  def sign(body)
75
77
  Base64.strict_encode64(OpenSSL::HMAC.digest("sha256", access_key, body))
76
78
  end
77
79
 
78
- private
79
-
80
80
  def sanitize_headers(headers)
81
81
  headers = headers.dup
82
82
  headers[:"Content-Length"] = nil if headers[:"Content-Length"].to_i == 0
@@ -0,0 +1,67 @@
1
+ require_relative "http"
2
+
3
+ module AzureBlob
4
+ class UserDelegationKey # :nodoc:
5
+ EXPIRATION = 25200 # 7 hours
6
+ EXPIRATION_BUFFER = 3600 # 1 hours
7
+ def initialize(account_name:, signer:)
8
+ @uri = URI.parse(
9
+ "https://#{account_name}.blob.core.windows.net/?restype=service&comp=userdelegationkey"
10
+ )
11
+
12
+ @signer = signer
13
+
14
+ refresh
15
+ end
16
+
17
+ def to_s
18
+ user_delegation_key
19
+ end
20
+
21
+ def refresh
22
+ return unless expired?
23
+ now = Time.now.utc
24
+
25
+
26
+ start = now.iso8601
27
+ @expiration = (now + EXPIRATION)
28
+ expiry = @expiration.iso8601
29
+
30
+ content = <<-XML.gsub!(/[[:space:]]+/, " ").strip!
31
+ <?xml version="1.0" encoding="utf-8"?>
32
+ <KeyInfo>
33
+ <Start>#{start}</Start>
34
+ <Expiry>#{expiry}</Expiry>
35
+ </KeyInfo>
36
+ XML
37
+
38
+ response = Http.new(uri, signer:).post(content)
39
+
40
+ doc = REXML::Document.new(response)
41
+
42
+ @signed_oid = doc.get_elements("/UserDelegationKey/SignedOid").first.get_text.to_s
43
+ @signed_tid = doc.get_elements("/UserDelegationKey/SignedTid").first.get_text.to_s
44
+ @signed_start = doc.get_elements("/UserDelegationKey/SignedStart").first.get_text.to_s
45
+ @signed_expiry = doc.get_elements("/UserDelegationKey/SignedExpiry").first.get_text.to_s
46
+ @signed_service = doc.get_elements("/UserDelegationKey/SignedService").first.get_text.to_s
47
+ @signed_version = doc.get_elements("/UserDelegationKey/SignedVersion").first.get_text.to_s
48
+ @user_delegation_key = Base64.decode64(doc.get_elements("/UserDelegationKey/Value").first.get_text.to_s)
49
+ end
50
+
51
+ attr_reader :signed_oid,
52
+ :signed_tid,
53
+ :signed_start,
54
+ :signed_expiry,
55
+ :signed_service,
56
+ :signed_version,
57
+ :user_delegation_key
58
+
59
+ private
60
+
61
+ def expired?
62
+ expiration.nil? || Time.now >= (expiration - EXPIRATION_BUFFER)
63
+ end
64
+
65
+ attr_reader :uri, :user_delegation_key, :signer, :expiration
66
+ end
67
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AzureBlob
4
- VERSION = "0.4.2"
4
+ VERSION = "0.5.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: azure-blob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joé Dupuis
@@ -31,18 +31,10 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
- - ".envrc"
35
- - ".rubocop.yml"
36
- - ".standard.yml"
37
34
  - CHANGELOG.md
38
35
  - LICENSE.txt
39
36
  - README.md
40
37
  - Rakefile
41
- - azure-blob.gemspec
42
- - devenv.local.nix.example
43
- - devenv.lock
44
- - devenv.nix
45
- - devenv.yaml
46
38
  - lib/active_storage/service/azure_blob_service.rb
47
39
  - lib/azure_blob.rb
48
40
  - lib/azure_blob/blob.rb
@@ -52,15 +44,19 @@ files:
52
44
  - lib/azure_blob/canonicalized_resource.rb
53
45
  - lib/azure_blob/client.rb
54
46
  - lib/azure_blob/const.rb
47
+ - lib/azure_blob/entra_id_signer.rb
55
48
  - lib/azure_blob/errors.rb
56
49
  - lib/azure_blob/http.rb
50
+ - lib/azure_blob/identity_token.rb
57
51
  - lib/azure_blob/metadata.rb
58
- - lib/azure_blob/signer.rb
52
+ - lib/azure_blob/shared_key_signer.rb
53
+ - lib/azure_blob/user_delegation_key.rb
59
54
  - lib/azure_blob/version.rb
60
55
  homepage: https://github.com/testdouble/azure-blob
61
56
  licenses:
62
57
  - MIT
63
58
  metadata:
59
+ rubygems_mfa_required: 'true'
64
60
  homepage_uri: https://github.com/testdouble/azure-blob
65
61
  source_code_uri: https://github.com/testdouble/azure-blob
66
62
  changelog_uri: https://github.com/testdouble/azure-blob/blob/main/CHANGELOG.md
data/.envrc DELETED
@@ -1,3 +0,0 @@
1
- source_url "https://raw.githubusercontent.com/cachix/devenv/d1f7b48e35e6dee421cfd0f51481d17f77586997/direnvrc" "sha256-YBzqskFZxmNb3kYVoKD9ZixoPXJh1C9ZvTLGFRkauZ0="
2
-
3
- use devenv
data/.rubocop.yml DELETED
@@ -1,25 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 3.1
3
-
4
- # Omakase Ruby styling for Rails
5
- inherit_gem: { rubocop-rails-omakase: rubocop.yml }
6
-
7
- # Overwrite or add rules to create your own house style
8
- #
9
- # # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
10
- # Layout/SpaceInsideArrayLiteralBrackets:
11
- # Enabled: false
12
-
13
- Style/TrailingCommaInArrayLiteral:
14
- Enabled: true
15
- EnforcedStyleForMultiline: consistent_comma
16
-
17
- Style/TrailingCommaInHashLiteral:
18
- Enabled: true
19
- EnforcedStyleForMultiline: consistent_comma
20
-
21
-
22
- Rails/AssertNot:
23
- Enabled: false
24
- Rails/RefuteMethods:
25
- Enabled: false
data/.standard.yml DELETED
@@ -1,3 +0,0 @@
1
- # For available configuration options, see:
2
- # https://github.com/standardrb/standard
3
- ruby_version: 3.1
data/azure-blob.gemspec DELETED
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/azure_blob/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "azure-blob"
7
- spec.version = AzureBlob::VERSION
8
- spec.authors = [ "Joé Dupuis" ]
9
- spec.email = [ "joe@dupuis.io" ]
10
-
11
- spec.summary = "Azure blob client"
12
- spec.homepage = "https://github.com/testdouble/azure-blob"
13
- spec.license = "MIT"
14
- spec.required_ruby_version = ">= 3.1"
15
-
16
- spec.metadata["homepage_uri"] = spec.homepage
17
- spec.metadata["source_code_uri"] = spec.homepage
18
- spec.metadata["changelog_uri"] = "https://github.com/testdouble/azure-blob/blob/main/CHANGELOG.md"
19
-
20
- spec.add_dependency "rexml"
21
-
22
- # Specify which files should be added to the gem when it is released.
23
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
- spec.files = Dir.chdir(__dir__) do
25
- `git ls-files -z`.split("\x0").reject do |f|
26
- (File.expand_path(f) == __FILE__) ||
27
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
28
- end
29
- end
30
- spec.bindir = "exe"
31
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
- spec.require_paths = [ "lib" ]
33
- end
@@ -1,8 +0,0 @@
1
- {pkgs, lib, ...}:{
2
- env = {
3
- AZURE_ACCOUNT_NAME = "";
4
- AZURE_ACCESS_KEY = "";
5
- AZURE_PRIVATE_CONTAINER = "";
6
- AZURE_PUBLIC_CONTAINER = "";
7
- };
8
- }
data/devenv.lock DELETED
@@ -1,242 +0,0 @@
1
- {
2
- "nodes": {
3
- "devenv": {
4
- "locked": {
5
- "dir": "src/modules",
6
- "lastModified": 1715593316,
7
- "narHash": "sha256-S7XatU9uV3q9bVBcg/ER0VMQcnPZprrVlN209ne7LDw=",
8
- "owner": "cachix",
9
- "repo": "devenv",
10
- "rev": "725c90407ef53cc2a1b53701c6d2d0745cf2484f",
11
- "type": "github"
12
- },
13
- "original": {
14
- "dir": "src/modules",
15
- "owner": "cachix",
16
- "repo": "devenv",
17
- "type": "github"
18
- }
19
- },
20
- "flake-compat": {
21
- "flake": false,
22
- "locked": {
23
- "lastModified": 1696426674,
24
- "owner": "edolstra",
25
- "repo": "flake-compat",
26
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
27
- "treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
28
- "type": "github"
29
- },
30
- "original": {
31
- "owner": "edolstra",
32
- "repo": "flake-compat",
33
- "type": "github"
34
- }
35
- },
36
- "flake-compat_2": {
37
- "flake": false,
38
- "locked": {
39
- "lastModified": 1696426674,
40
- "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
41
- "owner": "edolstra",
42
- "repo": "flake-compat",
43
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
44
- "type": "github"
45
- },
46
- "original": {
47
- "owner": "edolstra",
48
- "repo": "flake-compat",
49
- "type": "github"
50
- }
51
- },
52
- "flake-utils": {
53
- "inputs": {
54
- "systems": "systems"
55
- },
56
- "locked": {
57
- "lastModified": 1710146030,
58
- "owner": "numtide",
59
- "repo": "flake-utils",
60
- "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
61
- "treeHash": "bd263f021e345cb4a39d80c126ab650bebc3c10c",
62
- "type": "github"
63
- },
64
- "original": {
65
- "owner": "numtide",
66
- "repo": "flake-utils",
67
- "type": "github"
68
- }
69
- },
70
- "flake-utils_2": {
71
- "inputs": {
72
- "systems": "systems_2"
73
- },
74
- "locked": {
75
- "lastModified": 1710146030,
76
- "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
77
- "owner": "numtide",
78
- "repo": "flake-utils",
79
- "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
80
- "type": "github"
81
- },
82
- "original": {
83
- "owner": "numtide",
84
- "repo": "flake-utils",
85
- "type": "github"
86
- }
87
- },
88
- "gitignore": {
89
- "inputs": {
90
- "nixpkgs": [
91
- "pre-commit-hooks",
92
- "nixpkgs"
93
- ]
94
- },
95
- "locked": {
96
- "lastModified": 1709087332,
97
- "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
98
- "owner": "hercules-ci",
99
- "repo": "gitignore.nix",
100
- "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
101
- "type": "github"
102
- },
103
- "original": {
104
- "owner": "hercules-ci",
105
- "repo": "gitignore.nix",
106
- "type": "github"
107
- }
108
- },
109
- "nixpkgs": {
110
- "locked": {
111
- "lastModified": 1715542476,
112
- "owner": "NixOS",
113
- "repo": "nixpkgs",
114
- "rev": "44072e24566c5bcc0b7aa9178a0104f4cfffab19",
115
- "treeHash": "3f9021e4c33de6fe59b88ac8c3019fc49136dc2a",
116
- "type": "github"
117
- },
118
- "original": {
119
- "owner": "NixOS",
120
- "ref": "nixos-23.11",
121
- "repo": "nixpkgs",
122
- "type": "github"
123
- }
124
- },
125
- "nixpkgs-ruby": {
126
- "inputs": {
127
- "flake-compat": "flake-compat",
128
- "flake-utils": "flake-utils",
129
- "nixpkgs": "nixpkgs_2"
130
- },
131
- "locked": {
132
- "lastModified": 1713939467,
133
- "owner": "bobvanderlinden",
134
- "repo": "nixpkgs-ruby",
135
- "rev": "c1ba161adf31119cfdbb24489766a7bcd4dbe881",
136
- "treeHash": "0d32620317b29f94d6718684f030dd2fc2f30cb2",
137
- "type": "github"
138
- },
139
- "original": {
140
- "owner": "bobvanderlinden",
141
- "repo": "nixpkgs-ruby",
142
- "type": "github"
143
- }
144
- },
145
- "nixpkgs-stable": {
146
- "locked": {
147
- "lastModified": 1710695816,
148
- "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
149
- "owner": "NixOS",
150
- "repo": "nixpkgs",
151
- "rev": "614b4613980a522ba49f0d194531beddbb7220d3",
152
- "type": "github"
153
- },
154
- "original": {
155
- "owner": "NixOS",
156
- "ref": "nixos-23.11",
157
- "repo": "nixpkgs",
158
- "type": "github"
159
- }
160
- },
161
- "nixpkgs_2": {
162
- "locked": {
163
- "lastModified": 1715542476,
164
- "owner": "NixOS",
165
- "repo": "nixpkgs",
166
- "rev": "44072e24566c5bcc0b7aa9178a0104f4cfffab19",
167
- "treeHash": "3f9021e4c33de6fe59b88ac8c3019fc49136dc2a",
168
- "type": "github"
169
- },
170
- "original": {
171
- "owner": "NixOS",
172
- "ref": "nixos-23.11",
173
- "repo": "nixpkgs",
174
- "type": "github"
175
- }
176
- },
177
- "pre-commit-hooks": {
178
- "inputs": {
179
- "flake-compat": "flake-compat_2",
180
- "flake-utils": "flake-utils_2",
181
- "gitignore": "gitignore",
182
- "nixpkgs": [
183
- "nixpkgs"
184
- ],
185
- "nixpkgs-stable": "nixpkgs-stable"
186
- },
187
- "locked": {
188
- "lastModified": 1715609711,
189
- "narHash": "sha256-/5u29K0c+4jyQ8x7dUIEUWlz2BoTSZWUP2quPwFCE7M=",
190
- "owner": "cachix",
191
- "repo": "pre-commit-hooks.nix",
192
- "rev": "c182c876690380f8d3b9557c4609472ebfa1b141",
193
- "type": "github"
194
- },
195
- "original": {
196
- "owner": "cachix",
197
- "repo": "pre-commit-hooks.nix",
198
- "type": "github"
199
- }
200
- },
201
- "root": {
202
- "inputs": {
203
- "devenv": "devenv",
204
- "nixpkgs": "nixpkgs",
205
- "nixpkgs-ruby": "nixpkgs-ruby",
206
- "pre-commit-hooks": "pre-commit-hooks"
207
- }
208
- },
209
- "systems": {
210
- "locked": {
211
- "lastModified": 1681028828,
212
- "owner": "nix-systems",
213
- "repo": "default",
214
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
215
- "treeHash": "cce81f2a0f0743b2eb61bc2eb6c7adbe2f2c6beb",
216
- "type": "github"
217
- },
218
- "original": {
219
- "owner": "nix-systems",
220
- "repo": "default",
221
- "type": "github"
222
- }
223
- },
224
- "systems_2": {
225
- "locked": {
226
- "lastModified": 1681028828,
227
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
228
- "owner": "nix-systems",
229
- "repo": "default",
230
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
231
- "type": "github"
232
- },
233
- "original": {
234
- "owner": "nix-systems",
235
- "repo": "default",
236
- "type": "github"
237
- }
238
- }
239
- },
240
- "root": "root",
241
- "version": 7
242
- }
data/devenv.nix DELETED
@@ -1,12 +0,0 @@
1
- { pkgs, ... }:
2
-
3
- {
4
- packages = with pkgs; [
5
- git
6
- libyaml
7
- ];
8
-
9
-
10
- languages.ruby.enable = true;
11
- languages.ruby.version = "3.1.5";
12
- }
data/devenv.yaml DELETED
@@ -1,6 +0,0 @@
1
- allowUnfree: true
2
- inputs:
3
- nixpkgs:
4
- url: github:NixOS/nixpkgs/nixos-23.11
5
- nixpkgs-ruby:
6
- url: github:bobvanderlinden/nixpkgs-ruby