diff --git a/.circleci/config.yml b/.circleci/config.yml index eaf03ccd..88729b5e 100755 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,6 +2,7 @@ version: 2.1 orbs: win: circleci/windows@4.1 + ruby: circleci/ruby@2.0.0 jobs: test_linux: @@ -9,7 +10,7 @@ jobs: ruby_version: description: 'version tag for the cimg/ruby container' type: string - + machine: image: ubuntu-2004:current @@ -19,11 +20,13 @@ jobs: - run: name: start docker-compose build environment - command: | + command: | sudo ./test/bin/setup_volume_permissions.sh docker-compose up -d echo "Waiting for containers to start..." sleep 10 + environment: + RUBY_VERSION: << parameters.ruby_version >> - run: name: install sql prereqs @@ -58,7 +61,10 @@ jobs: - run: name: test gem command: | - docker exec cimg_ruby bash -c 'bundle exec rake test' + docker exec cimg_ruby bash -c 'bundle exec rake test' + + - store_test_results: + path: test/reports test_windows: parameters: @@ -84,6 +90,8 @@ jobs: - run: name: download and install ruby devkit command: | + $ProgressPreference='SilentlyContinue' + $uri = 'https://api.github.com/repos/oneclick/rubyinstaller2/tags?per_page=200' $releases = ((Invoke-WebRequest $uri) | ConvertFrom-Json).name | select-string -Pattern '<< parameters.ruby_version >>' $target_release = (($releases | Sort-Object -Descending)[0] | Out-String).Trim() @@ -93,7 +101,9 @@ jobs: [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Invoke-WebRequest -UseBasicParsing -uri $download_uri -OutFile ruby-setup.exe - .\ruby-setup.exe /VERYSILENT /NORESTART /DIR=C:/Ruby<< parameters.ruby_version >>-x64 + + echo "Download finished, starting installation of $target_version" + .\ruby-setup.exe /VERYSILENT /NORESTART /ALLUSERS /DIR=C:/Ruby<< parameters.ruby_version >>-x64 - run: name: ruby diagnostics @@ -108,11 +118,10 @@ jobs: rm .\ruby-setup.exe - run: - name: update build env + name: install bundler command: | $Env:PATH = "C:\\Ruby<< parameters.ruby_version >>-x64\\bin;$Env:PATH" - ridk install 2 - gem install bundler + gem install bundler -v 2.3.26 - checkout @@ -127,7 +136,7 @@ jobs: name: bundle install gems command: | $Env:PATH = "C:\\Ruby<< parameters.ruby_version >>-x64\\bin;$Env:PATH" - bundle install + bundle install --path vendor/bundle - save_cache: name: save gem cache @@ -135,49 +144,135 @@ jobs: - ./vendor/bundle key: v1-bundle-<< parameters.ruby_version >>-{{ .Branch }}-{{ checksum "tiny_tds.gemspec" }} + - attach_workspace: + at: artifacts + - run: - name: build openssl - no_output_timeout: 30m + name: install native gem and restore cross-compiled code from gem command: | $Env:PATH = "C:\\Ruby<< parameters.ruby_version >>-x64\\bin;$Env:PATH" - bundle exec rake ports:openssl + $rubyArchitecture = (ruby -e 'puts RUBY_PLATFORM').Trim() + $gemVersion = (Get-Content VERSION).Trim() + + gem install --local --install-dir=./tmp "artifacts/gems/tiny_tds-$gemVersion-$rubyArchitecture.gem" + + # Restore precompiled code + $source = (Resolve-Path ".\tmp\gems\tiny_tds-$gemVersion-$rubyArchitecture\lib\tiny_tds").Path + $destination = (Resolve-Path ".\lib\tiny_tds").Path + Get-ChildItem $source -Recurse -Exclude "*.rb" | Copy-Item -Destination {Join-Path $destination $_.FullName.Substring($source.length)} + + # Restore ports + Copy-Item -Path ".\tmp\gems\tiny_tds-$gemVersion-$rubyArchitecture\ports" -Destination "." -Recurse + + - restore_cache: + name: restore mssql installation file + key: downloads-{{ checksum "test/bin/install-mssql.ps1" }} - run: - name: build libiconv - no_output_timeout: 30m + name: setup mssql command: | - $Env:PATH = "C:\\Ruby<< parameters.ruby_version >>-x64\\bin;$Env:PATH" - bundle exec rake ports:libiconv + .\test\bin\install-mssql.ps1 + + - save_cache: + name: save downloads cache + paths: + - C:\Downloads + key: downloads-{{ checksum "test/bin/install-mssql.ps1" }} - run: - name: build freetds - no_output_timeout: 30m + name: install toxiproxy-server command: | - $Env:PATH = "C:\\Ruby<< parameters.ruby_version >>-x64\\bin;$Env:PATH" - bundle exec rake ports:freetds + choco install toxiproxy-server --version=2.5.0 -y + Start-Process toxiproxy-server - run: - name: build gem - no_output_timeout: 30m + name: test gem command: | $Env:PATH = "C:\\Ruby<< parameters.ruby_version >>-x64\\bin;$Env:PATH" - bundle exec rake ports + bundle exec rake test + environment: + TOXIPROXY_HOST: "localhost" + + - store_test_results: + path: test/reports + + cross_compile_gem: + machine: + image: ubuntu-2004:current + + steps: + - ruby/install: + version: '2.7' + - checkout + - restore_cache: + name: restore gem cache + keys: + - v1-bundle-{{ .Branch }}-{{ checksum "tiny_tds.gemspec" }} + - v1-bundle-{{ .Branch }}- + - v1-bundle- + + - run: + name: bundle install gems + command: | + bundle install --path vendor/bundle + + - save_cache: + name: save gem cache + paths: + - ./vendor/bundle + key: v1-bundle-{{ .Branch }}-{{ checksum "tiny_tds.gemspec" }} + + - run: + name: Write used versions for ports into file + command: | + bundle exec rake ports:version_file + + - restore_cache: + name: restore ports cache + keys: + - ports-{{ checksum ".ports_versions" }} + - ports- + + - run: + name: Build gems + command: | + bundle exec rake gem + bundle exec rake gem:native + + - run: + name: Move gems into separate directory before caching + command: | + mkdir -p artifacts/gems + mv pkg/*.gem artifacts/gems + + - store_artifacts: + path: artifacts/gems + + - save_cache: + name: save ports cache + paths: + - ./ports + key: ports-{{ checksum ".ports_versions" }} + + - persist_to_workspace: + name: save gems into workspace + root: artifacts + paths: + - gems workflows: test_supported_ruby_versions: jobs: - - test_linux: - matrix: + - cross_compile_gem + - test_windows: + requires: + - cross_compile_gem + matrix: &ruby_versions parameters: ruby_version: + - '2.4' - '2.5' - '2.6' - '2.7' - - - test_windows: - matrix: - parameters: - ruby_version: - - '2.5' - - '2.6' - - '2.7' + - test_linux: + matrix: *ruby_versions diff --git a/.gitignore b/.gitignore index 6c1b7e4e..2e1452dd 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ misc /exe/* /ports/* !/ports/patches/ +test/reports +.ports_versions diff --git a/Gemfile b/Gemfile index 313a64f0..851fabc2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,2 @@ source 'https://rubygems.org' gemspec - -group :development do -end - -group :test do - gem 'minitest' -end diff --git a/README.md b/README.md index 45fdbb35..78240925 100644 --- a/README.md +++ b/README.md @@ -407,7 +407,7 @@ For the convenience of Windows users, TinyTDS ships pre-compiled gems for suppor Run the following rake task to compile the gems for Windows. This will check the availability of [Docker](https://www.docker.com/) (and boot2docker on Windows or OS-X) and will give some advice for download and installation. When docker is running, it will download the docker image (once-only) and start the build: ``` -$ rake gem:windows +$ rake gem:native ``` The compiled gems will exist in `./pkg` directory. diff --git a/Rakefile b/Rakefile index 3150afdc..588f2598 100644 --- a/Rakefile +++ b/Rakefile @@ -10,6 +10,7 @@ GEM_PLATFORM_HOSTS = { 'x86-mingw32' => 'i686-w64-mingw32', 'x64-mingw32' => 'x86_64-w64-mingw32' } +RUBY_CC_VERSION="2.7.0:2.6.0:2.5.0:2.4.0".freeze # Add our project specific files to clean for a rebuild CLEAN.include FileList["{ext,lib}/**/*.{so,#{RbConfig::CONFIG['DLEXT']},o}"], diff --git a/docker-compose.yml b/docker-compose.yml index 3a1afd30..17a2a1c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,7 @@ services: network_mode: "host" cimgruby: - image: cimg/ruby:2.7.0 + image: "cimg/ruby:${RUBY_VERSION:-2.7}" container_name: cimg_ruby environment: TESTOPTS: '-v' diff --git a/ext/tiny_tds/extconsts.rb b/ext/tiny_tds/extconsts.rb index 1e1c1bf2..f3c5e249 100644 --- a/ext/tiny_tds/extconsts.rb +++ b/ext/tiny_tds/extconsts.rb @@ -2,7 +2,7 @@ ICONV_VERSION = ENV['TINYTDS_ICONV_VERSION'] || "1.15" ICONV_SOURCE_URI = "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-#{ICONV_VERSION}.tar.gz" -OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.1.1d' +OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.1.1s' OPENSSL_SOURCE_URI = "https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz" FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.1.24" diff --git a/tasks/native_gem.rake b/tasks/native_gem.rake index d19cdb85..8dbe4768 100644 --- a/tasks/native_gem.rake +++ b/tasks/native_gem.rake @@ -1,14 +1,13 @@ # encoding: UTF-8 -desc 'Build the windows binary gems per rake-compiler-dock' -task 'gem:windows' => ['ports:cross'] do +desc 'Build the native binary gems using rake-compiler-dock' +task 'gem:native' => ['ports:cross'] do require 'rake_compiler_dock' # make sure to install our bundle - build = ['bundle'] + sh "bundle package --all" # Avoid repeated downloads of gems by using gem files from the host. - # and finally build the native gem - build << 'rake cross native gem RUBY_CC_VERSION=2.7.0:2.6.0:2.5.0:2.4.0 CFLAGS="-Wall" MAKE="make -j`nproc`"' - - RakeCompilerDock.sh build.join(' && ') + GEM_PLATFORM_HOSTS.keys.each do |plat| + RakeCompilerDock.sh "bundle --local && RUBY_CC_VERSION=#{RUBY_CC_VERSION} rake native:#{plat} gem", platform: plat + end end diff --git a/tasks/ports.rake b/tasks/ports.rake index 86ee1e91..ca63e582 100644 --- a/tasks/ports.rake +++ b/tasks/ports.rake @@ -7,9 +7,11 @@ require_relative 'ports/freetds' require_relative '../ext/tiny_tds/extconsts' namespace :ports do - openssl = Ports::Openssl.new(OPENSSL_VERSION) - libiconv = Ports::Libiconv.new(ICONV_VERSION) - freetds = Ports::Freetds.new(FREETDS_VERSION) + libraries_to_compile = { + openssl: Ports::Openssl.new(OPENSSL_VERSION), + libiconv: Ports::Libiconv.new(ICONV_VERSION), + freetds: Ports::Freetds.new(FREETDS_VERSION) + } directory "ports" CLEAN.include "ports/*mingw32*" @@ -18,51 +20,51 @@ namespace :ports do task :openssl, [:host] do |task, args| args.with_defaults(host: RbConfig::CONFIG['host']) - openssl.files = [OPENSSL_SOURCE_URI] - openssl.host = args.host - openssl.cook - openssl.activate + libraries_to_compile[:openssl].files = [OPENSSL_SOURCE_URI] + libraries_to_compile[:openssl].host = args.host + libraries_to_compile[:openssl].cook + libraries_to_compile[:openssl].activate end task :libiconv, [:host] do |task, args| args.with_defaults(host: RbConfig::CONFIG['host']) - libiconv.files = [ICONV_SOURCE_URI] - libiconv.host = args.host - libiconv.cook - libiconv.activate + libraries_to_compile[:libiconv].files = [ICONV_SOURCE_URI] + libraries_to_compile[:libiconv].host = args.host + libraries_to_compile[:libiconv].cook + libraries_to_compile[:libiconv].activate end task :freetds, [:host] do |task, args| args.with_defaults(host: RbConfig::CONFIG['host']) - freetds.files = [FREETDS_SOURCE_URI] - freetds.host = args.host + libraries_to_compile[:freetds].files = [FREETDS_SOURCE_URI] + libraries_to_compile[:freetds].host = args.host - if openssl + if libraries_to_compile[:openssl] # freetds doesn't have an option that will provide an rpath # so we do it manually - ENV['OPENSSL_CFLAGS'] = "-Wl,-rpath -Wl,#{openssl.path}/lib" + ENV['OPENSSL_CFLAGS'] = "-Wl,-rpath -Wl,#{libraries_to_compile[:openssl].path}/lib" # Add the pkgconfig file with MSYS2'ish path, to prefer our ports build # over MSYS2 system OpenSSL. - ENV['PKG_CONFIG_PATH'] = "#{openssl.path.gsub(/^(\w):/i){"/"+$1.downcase}}/lib/pkgconfig:#{ENV['PKG_CONFIG_PATH']}" - freetds.configure_options << "--with-openssl=#{openssl.path}" + ENV['PKG_CONFIG_PATH'] = "#{libraries_to_compile[:openssl].path.gsub(/^(\w):/i) { "/" + $1.downcase }}/lib/pkgconfig:#{ENV['PKG_CONFIG_PATH']}" + libraries_to_compile[:freetds].configure_options << "--with-openssl=#{libraries_to_compile[:openssl].path}" end - if libiconv - freetds.configure_options << "--with-libiconv-prefix=#{libiconv.path}" + if libraries_to_compile[:libiconv] + libraries_to_compile[:freetds].configure_options << "--with-libiconv-prefix=#{libraries_to_compile[:libiconv].path}" end - freetds.cook - freetds.activate + libraries_to_compile[:freetds].cook + libraries_to_compile[:freetds].activate end - task :compile, [:host] do |task,args| + task :compile, [:host] do |task, args| args.with_defaults(host: RbConfig::CONFIG['host']) puts "Compiling ports for #{args.host}..." - ['openssl','libiconv','freetds'].each do |lib| + libraries_to_compile.keys.each do |lib| Rake::Task["ports:#{lib}"].invoke(args.host) end end @@ -79,6 +81,19 @@ namespace :ports do RakeCompilerDock.sh build.join(' && '), platform: gem_platform end end + + desc "Notes the actual versions for the compiled ports into a file" + task "version_file" do + ports_version = {} + + libraries_to_compile.each do |library, library_recipe| + ports_version[library] = library_recipe.version + end + + File.open(".ports_versions", "w") do |f| + f.write ports_version + end + end end desc 'Build ports and activate libraries for the current architecture.' diff --git a/tasks/ports/libiconv.rb b/tasks/ports/libiconv.rb index e3a3531d..52e0f184 100644 --- a/tasks/ports/libiconv.rb +++ b/tasks/ports/libiconv.rb @@ -8,25 +8,8 @@ def initialize(version) set_patches end - def cook - chdir_for_build do - super - end - self - end - private - # When using rake-compiler-dock on Windows, the underlying Virtualbox shared - # folders don't support symlinks, but libiconv expects it for a build on - # Linux. We work around this limitation by using the temp dir for cooking. - def chdir_for_build - build_dir = ENV['RCD_HOST_RUBY_PLATFORM'].to_s =~ /mingw|mswin|cygwin/ ? '/tmp' : '.' - Dir.chdir(build_dir) do - yield - end - end - def configure_defaults [ "--host=#{@host}", diff --git a/test/bin/install-mssql.ps1 b/test/bin/install-mssql.ps1 new file mode 100644 index 00000000..bc4c7248 --- /dev/null +++ b/test/bin/install-mssql.ps1 @@ -0,0 +1,31 @@ +$ProgressPreference = 'SilentlyContinue' + +if (-not(Test-path "C:\Downloads")) +{ + mkdir "C:\Downloads" +} + +$sqlInstallationFile = "C:\Downloads\sqlexpress.exe" +if (-not(Test-path $sqlInstallationFile -PathType leaf)) +{ + Write-Host "Downloading SQL Express ..." + Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/?linkid=829176" -OutFile "C:\Downloads\sqlexpress.exe" +} + +Write-Host "Installing SQL Express ..." +Start-Process -Wait -FilePath "C:\Downloads\sqlexpress.exe" -ArgumentList /qs, /x:"C:\Downloads\setup" +C:\Downloads\setup\setup.exe /q /ACTION=Install /INSTANCENAME=SQLEXPRESS /FEATURES=SQLEngine /UPDATEENABLED=0 /SQLSVCACCOUNT='NT AUTHORITY\System' /SQLSYSADMINACCOUNTS='BUILTIN\ADMINISTRATORS' /TCPENABLED=1 /NPENABLED=0 /IACCEPTSQLSERVERLICENSETERMS + +Write-Host "Configuring SQL Express ..." +stop-service MSSQL`$SQLEXPRESS +set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.SQLEXPRESS\mssqlserver\supersocketnetlib\tcp\ipall' -name tcpdynamicports -value '' +set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.SQLEXPRESS\mssqlserver\supersocketnetlib\tcp\ipall' -name tcpport -value 1433 +set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.SQLEXPRESS\mssqlserver\' -name LoginMode -value 2 + +Write-Host "Starting SQL Express ..." +start-service MSSQL`$SQLEXPRESS + +Write-Host "Configuring MSSQL for TinyTDS ..." +& sqlcmd -Q "CREATE DATABASE [tinytdstest];" +& sqlcmd -Q "CREATE LOGIN [tinytds] WITH PASSWORD = '', CHECK_POLICY = OFF, DEFAULT_DATABASE = [tinytdstest];" +& sqlcmd -Q "USE [tinytdstest]; CREATE USER [tinytds] FOR LOGIN [tinytds]; EXEC sp_addrolemember N'db_owner', N'tinytds';" diff --git a/test/client_test.rb b/test/client_test.rb index e85098eb..de988d6e 100644 --- a/test/client_test.rb +++ b/test/client_test.rb @@ -154,6 +154,8 @@ class ClientTest < TinyTds::TestCase end it 'raises TinyTds exception with dead connection network failure' do + skip if ruby_windows? + begin client = new_connection timeout: 2, port: 1234, host: ENV['TOXIPROXY_HOST'] assert_client_works(client) diff --git a/tiny_tds.gemspec b/tiny_tds.gemspec index fa50d942..e85ede2a 100644 --- a/tiny_tds.gemspec +++ b/tiny_tds.gemspec @@ -25,6 +25,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rake-compiler', '~> 1.1.0' s.add_development_dependency 'rake-compiler-dock', '~> 1.1.0' s.add_development_dependency 'minitest', '~> 5.14.0' + s.add_development_dependency 'minitest-ci', '~> 3.4.0' s.add_development_dependency 'connection_pool', '~> 2.2.0' s.add_development_dependency 'toxiproxy', '~> 2.0.0' end