azure-blob 0.4.2 → 0.5.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 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