From 4bc663191b985e7bbc4534d16703c9644abf9969 Mon Sep 17 00:00:00 2001 From: Peter Goldstein Date: Tue, 29 Mar 2022 18:12:50 -0700 Subject: [PATCH] Migrate CI to GitHub actions (#186) Migrate CI to GitHub Actions now that Travis CI.org is inactive. In addition to adding a basic `ci.yml` file, I needed to make several other changes: 1. Loosen the development dependency on `rake` to allow more recent versions 2. Wrap the use of `YAML::load` in the specs in a helper function that allows us to explicitly use `YAML::safe_load` with `OpenStruct` as a permitted class for Ruby 3.1 and up 3. Replace `File.exists?` with `File.exist?` Ruby head is still failing because of a process start race condition between the `PatronTestServer` and the actual specs. In the interest of getting something working quickly I temporarily removed `ruby-head`. All other Rubies in the CI file are green. --- .github/workflows/ci.yml | 26 +++++++++++++++ lib/patron/session.rb | 4 +-- patron.gemspec | 2 +- spec/session_spec.rb | 72 ++++++++++++++++++++++------------------ spec/session_ssl_spec.rb | 50 ++++++++++++++++------------ 5 files changed, 98 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7fb9e4b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby-version: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1] + + name: Specs - Ruby ${{ matrix.ruby-version }} + steps: + - uses: actions/checkout@v2 + - name: Install libcurl + run: | + sudo apt-get update + sudo apt-get -y install libcurl4 libcurl4-openssl-dev + - name: Set up Ruby ${{ matrix.ruby-version }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true # 'bundle install' and cache + - name: Run tests + run: bundle exec rake diff --git a/lib/patron/session.rb b/lib/patron/session.rb index c8abcd5..bcdaf9e 100644 --- a/lib/patron/session.rb +++ b/lib/patron/session.rb @@ -146,9 +146,9 @@ def handle_cookies(file_path = nil) if file_path path = Pathname(file_path).expand_path - if !File.exists?(file_path) && !File.writable?(path.dirname) + if !File.exist?(file_path) && !File.writable?(path.dirname) raise ArgumentError, "Can't create file #{path} (permission error)" - elsif File.exists?(file_path) && !File.writable?(file_path) + elsif File.exist?(file_path) && !File.writable?(file_path) raise ArgumentError, "Can't read or write file #{path} (permission error)" end else diff --git a/patron.gemspec b/patron.gemspec index d236d68..0c4fb5e 100644 --- a/patron.gemspec +++ b/patron.gemspec @@ -35,7 +35,7 @@ SecureTransport-based builds might cause crashes in forking environment. For more info see https://github.com/curl/curl/issues/788 } - spec.add_development_dependency "rake", "~> 12.3.3" + spec.add_development_dependency "rake", ">= 12.3.3" spec.add_development_dependency "bundler" spec.add_development_dependency "rspec", ">= 2.3.0" spec.add_development_dependency "simplecov", "~> 0.10" diff --git a/spec/session_spec.rb b/spec/session_spec.rb index 127691d..5114dbd 100644 --- a/spec/session_spec.rb +++ b/spec/session_spec.rb @@ -7,6 +7,14 @@ describe Patron::Session do + def yaml_load(str) + if RUBY_VERSION >= '3.1.0' + YAML::safe_load(str, permitted_classes: [OpenStruct]) + else + YAML::load(str) + end + end + before(:each) do @session = Patron::Session.new @session.base_url = "http://localhost:9001" @@ -72,7 +80,7 @@ it "should retrieve a url with :get" do response = @session.get("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "GET" end @@ -91,7 +99,7 @@ tmpfile = "/tmp/patron_test.yaml" response = @session.get_file "/test", tmpfile expect(response.body).to be_nil - body = YAML::load_file(tmpfile) + body = yaml_load(File.open(tmpfile).read) expect(body.request_method).to be == "GET" FileUtils.rm tmpfile end @@ -143,33 +151,33 @@ it "should not send the user-agent if it has been deleted from headers" do @session.headers.delete 'User-Agent' response = @session.get("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header["user-agent"]).to be_nil end it "should set the default User-agent" do response = @session.get("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header["user-agent"]).to be == [Patron.user_agent_string] end it "should include custom headers in a request" do response = @session.get("/test", {"User-Agent" => "PatronTest"}) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header["user-agent"]).to be == ["PatronTest"] end it "should include default headers in a request, if they were defined" do @session.headers = {"User-Agent" => "PatronTest"} response = @session.get("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header["user-agent"]).to be == ["PatronTest"] end it "should merge custom headers with session headers" do @session.headers["X-Test"] = "Testing" response = @session.get("/test", {"User-Agent" => "PatronTest"}) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header["user-agent"]).to be == ["PatronTest"] expect(body.header["x-test"]).to be == ["Testing"] end @@ -252,7 +260,7 @@ it "should follow redirects by default" do @session.max_redirects = 1 response = @session.get("/redirect") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(response.status).to be == 200 expect(body.path).to be == "/test" end @@ -285,26 +293,26 @@ it "should send a delete request with :delete" do response = @session.delete("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "DELETE" end it "should send a COPY request with :copy" do response = @session.copy("/test", "/test2") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "COPY" end it "should include a Destination header in COPY requests" do response = @session.copy("/test", "/test2") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header['destination'].first).to be == "/test2" end it "should upload data with :get" do data = "upload data" response = @session.request(:get, "/test", {}, :data => data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "GET" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -312,7 +320,7 @@ it "should call to_s on the data being uploaded via GET if it is not already a String" do data = 12345 response = @session.request(:get, "/test", {}, :data => data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "GET" end @@ -323,7 +331,7 @@ # you can have very deeply going queries, which are still technically GETs data = Random.new.bytes(1024 * 24) response = @session.request(:get, "/test", {}, :data => data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "GET" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -331,7 +339,7 @@ it "should upload data with :put" do data = Random.new.bytes(1024 * 24) response = @session.put("/test", data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "PUT" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -342,7 +350,7 @@ data.flush; data.rewind response = @session.put("/test", data, {'Expect' => ''}) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "PUT" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -350,14 +358,14 @@ it "should upload data with :patch" do data = "upload data" response = @session.patch("/testpatch", data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.body).to eq("upload data") end it "should upload data with :delete" do data = "upload data" response = @session.request(:delete, "/test", {}, :data => data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "DELETE" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -368,7 +376,7 @@ it "should upload a file with :put" do response = @session.put_file("/test", "LICENSE") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "PUT" end @@ -378,21 +386,21 @@ it "should use chunked encoding when uploading a file with :put" do response = @session.put_file("/test", "LICENSE") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header['transfer-encoding'].first).to be == "chunked" end it "should call to_s on the data being uploaded via POST if it is not already a String" do data = 12345 response = @session.post("/testpost", data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body['body']).to eq("12345") end it "should upload data with :post" do data = "upload data" response = @session.post("/test", data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "POST" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -400,7 +408,7 @@ it "should POST a hash of arguments as a urlencoded form" do data = {:foo => 123, 'baz' => '++hello world++'} response = @session.post("/testpost", data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body['content_type']).to be == "application/x-www-form-urlencoded" expect(body['body']).to match(/baz=%2B%2Bhello%20world%2B%2B/) expect(body['body']).to match(/foo=123/) @@ -412,13 +420,13 @@ it "should upload a file with :post" do response = @session.post_file("/test", "LICENSE") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "POST" end it "should upload a multipart with :post" do response = @session.post_multipart("/test", { :test_data => "123" }, { :test_file => "LICENSE" } ) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "POST" end @@ -428,7 +436,7 @@ it "should use chunked encoding when uploading a file with :post" do response = @session.post_file("/test", "LICENSE") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header['transfer-encoding'].first).to be == "chunked" end @@ -436,7 +444,7 @@ @session.username = "foo" @session.password = "bar" response = @session.get("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header['authorization']).to be == [encode_authz("foo", "bar")] end @@ -455,12 +463,12 @@ it "should handle cookies if set" do @session.handle_cookies response = @session.get("/setcookie").body - expect(YAML::load(response).header['cookie'].first).to be == "session_id=foo123" + expect(yaml_load(response).header['cookie'].first).to be == "session_id=foo123" end it "should not handle cookies by default" do response = @session.get("/setcookie").body - expect(YAML::load(response).header).to_not include('cookie') + expect(yaml_load(response).header).to_not include('cookie') end it "should ignore a wrong Content-Length when asked to" do @@ -505,7 +513,7 @@ expect { response = @session.get("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) }.to_not raise_error expect(body.request_method).to be == "GET" @@ -524,13 +532,13 @@ it "should serialize query params and append them to the url" do response = @session.request(:get, "/test", {}, :query => {:foo => "bar"}) - request = YAML::load(response.body) + request = yaml_load(response.body) expect(request.path + '?' + request.query_string).to be == "/test?foo=bar" end it "should merge parameters in the :query option with pre-existing query parameters" do response = @session.request(:get, "/test?foo=bar", {}, :query => {:baz => "quux"}) - request = YAML::load(response.body) + request = yaml_load(response.body) expect(request.path + '?' + request.query_string).to be == "/test?foo=bar&baz=quux" end diff --git a/spec/session_ssl_spec.rb b/spec/session_ssl_spec.rb index da6952e..cd3a524 100644 --- a/spec/session_ssl_spec.rb +++ b/spec/session_ssl_spec.rb @@ -6,6 +6,14 @@ describe Patron::Session do + def yaml_load(str) + if RUBY_VERSION >= '3.1.0' + YAML::safe_load(str, permitted_classes: [OpenStruct]) + else + YAML::load(str) + end + end + before(:each) do @session = Patron::Session.new @session.base_url = "https://localhost:9043" @@ -14,7 +22,7 @@ it "should retrieve a url with :get" do response = @session.get("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "GET" end @@ -22,7 +30,7 @@ tmpfile = "/tmp/patron_test.yaml" response = @session.get_file "/test", tmpfile expect(response.body).to be_nil - body = YAML::load_file(tmpfile) + body = yaml_load(File.open(tmpfile).read) expect(body.request_method).to be == "GET" FileUtils.rm tmpfile end @@ -39,7 +47,7 @@ pid = fork do response = @session.get_file "/test", tmpfile expect(response.body).to be_nil - body = YAML::load_file(tmpfile) + body = yaml_load(File.open(tmpfile).read) expect(body.request_method).to be == "GET" FileUtils.rm tmpfile end @@ -58,14 +66,14 @@ it "should include custom headers in a request" do response = @session.get("/test", {"User-Agent" => "PatronTest"}) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header["user-agent"]).to be == ["PatronTest"] end it "should merge custom headers with session headers" do @session.headers["X-Test"] = "Testing" response = @session.get("/test", {"User-Agent" => "PatronTest"}) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header["user-agent"]).to be == ["PatronTest"] expect(body.header["x-test"]).to be == ["Testing"] end @@ -80,7 +88,7 @@ it "should follow redirects by default" do @session.max_redirects = 1 response = @session.get("/redirect") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(response.status).to be == 200 expect(body.path).to be == "/test" end @@ -107,26 +115,26 @@ it "should send a delete request with :delete" do response = @session.delete("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "DELETE" end it "should send a COPY request with :copy" do response = @session.copy("/test", "/test2") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "COPY" end it "should include a Destination header in COPY requests" do response = @session.copy("/test", "/test2") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header['destination'].first).to be == "/test2" end it "should upload data with :get" do data = "upload data" response = @session.request(:get, "/test", {}, :data => data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "GET" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -134,7 +142,7 @@ it "should upload data with :put" do data = "upload data" response = @session.put("/test", data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "PUT" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -145,7 +153,7 @@ it "should upload a file with :put" do response = @session.put_file("/test", "LICENSE") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "PUT" end @@ -155,14 +163,14 @@ it "should use chunked encoding when uploading a file with :put" do response = @session.put_file("/test", "LICENSE") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header['transfer-encoding'].first).to be == "chunked" end it "should upload data with :post" do data = "upload data" response = @session.post("/test", data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "POST" expect(body.header['content-length']).to be == [data.size.to_s] end @@ -170,7 +178,7 @@ it "should post a hash of arguments as a urlencoded form" do data = {:foo => 123, 'baz' => '++hello world++'} response = @session.post("/testpost", data) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body['content_type']).to be == "application/x-www-form-urlencoded" expect(body['body']).to match(/baz=%2B%2Bhello%20world%2B%2B/) expect(body['body']).to match(/foo=123/) @@ -182,13 +190,13 @@ it "should upload a file with :post" do response = @session.post_file("/test", "LICENSE") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "POST" end it "should upload a multipart with :post" do response = @session.post_multipart("/test", { :test_data => "123" }, { :test_file => "LICENSE" } ) - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "POST" end @@ -198,19 +206,19 @@ it "should use chunked encoding when uploading a file with :post" do response = @session.post_file("/test", "LICENSE") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.header['transfer-encoding'].first).to be == "chunked" end it "should handle cookies if set" do @session.handle_cookies response = @session.get("/setcookie").body - expect(YAML::load(response).header['cookie'].first).to be == "session_id=foo123" + expect(yaml_load(response).header['cookie'].first).to be == "session_id=foo123" end it "should not handle cookies by default" do response = @session.get("/setcookie").body - expect(YAML::load(response).header).to_not include('cookie') + expect(yaml_load(response).header).to_not include('cookie') end it "should raise exception if cookie store is not writable or readable" do @@ -241,7 +249,7 @@ @session.insecure = nil @session.cacert = File.join(__dir__, 'support', 'certs', 'cacert.pem') response = @session.get("/test") - body = YAML::load(response.body) + body = yaml_load(response.body) expect(body.request_method).to be == "GET" end