Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 119 additions & 31 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ version: 2.1

orbs:
win: circleci/windows@4.1
ruby: circleci/ruby@2.0.0

jobs:
test_linux:
parameters:
ruby_version:
description: 'version tag for the cimg/ruby container'
type: string

machine:
image: ubuntu-2004:current

Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -108,11 +114,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

Expand All @@ -127,57 +132,140 @@ 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
paths:
- ./vendor/bundle
key: v1-bundle-<< parameters.ruby_version >>-{{ .Branch }}-{{ checksum "tiny_tds.gemspec" }}

- restore_cache:
name: restore cross-compiled code
keys:
- compiled-<< pipeline.git.revision >>

- run:
name: build openssl
no_output_timeout: 30m
name: restore cross-compiled code
command: |
$Env:PATH = "C:\\Ruby<< parameters.ruby_version >>-x64\\bin;$Env:PATH"
bundle exec rake ports:openssl

$rubyArchitecture = (ruby -e 'puts RUBY_PLATFORM').Trim()
$source = "C:\home\circleci\project\tmp\$rubyArchitecture\stage\lib\tiny_tds"
$destination = ".\lib\tiny_tds"
Get-ChildItem $source -Recurse -Exclude "*.rb" | Copy-Item -Destination {Join-Path $destination $_.FullName.Substring($source.length)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I recommend using relative paths if possible
  2. Generally this job step seems a bit strange, and that's related to my understanding of moving the ports code to a separate job. If it were to happen that CircleCI cleared the workflow cache after the ports job ran but before this job ran, wouldn't that mean that this job would fail? Is it guaranteed that won't happen? What is the recommended way to transfer files between jobs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it were to happen that CircleCI cleared the workflow cache after the ports job ran but before this job ran, wouldn't that mean that this job would fail? Is it guaranteed that won't happen? What is the recommended way to transfer files between jobs?

Good point. So the Windows job has a requires declaration on the precompile job. So in case that one fails, the Windows jobs wouldn't even start.

Regarding cache, I did some more research here and looks like workspaces are the better data store, as they are meant to pass build artifacts down in CI. Didn't know about that before, but it's implement now with 2a071ad. This also gets rid of the absolute paths.


- restore_cache:
name: restore mssql installation file
key: downloads-{{ checksum "test/bin/install-mssql.ps1" }}
Comment on lines +167 to +169

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify the benefit of caching this installation script? It's just another source file in the repo, so it should already be available on disk in the job step.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't cache the PowerShell script, but the C:/Downloads folder where I download the MSSQL installation file into. As the URL is hardcoded into this script, I thought it was fine to use the script for the checksum.

Potential side-effect is that if you change something in the script, it'll re-download the installation file. But it was also continue to use the cache even if Microsoft updates the SQL installation file on their side. I wouldn't say it's too important that the CI uses the latest version and rather take the 2 minute speed-up from caching the file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't cache the PowerShell script, but the C:/Downloads folder where I download the MSSQL installation file into

Oh yeah... duh.


- 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

- restore_cache:
name: restore ports cache
keys:
- ports-libiconv115-openssl111s-freetds1124

- run:
name: build gem
no_output_timeout: 30m
name: copy ports cache into project
command: |
Move-Item C:\home\circleci\project\ports .

- run:
name: test gem
command: |
$Env:PATH = "C:\\Ruby<< parameters.ruby_version >>-x64\\bin;$Env:PATH"
bundle exec rake ports

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, oops

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" }}

- restore_cache:
name: restore ports cache
keys:
- ports-libiconv115-openssl111s-freetds1124

- run:
name: Build gems
command: |
bundle exec rake gem
bundle exec rake gem:native

# Move gem files to a separate directory, as CircleCI artifacts don't support wildcards for paths
mkdir pkg_artifacts
mv pkg/*.gem pkg_artifacts

- store_artifacts:
path: pkg_artifacts

@aharpervc aharpervc Jan 11, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting... here's some other options for discussion:

  1. do this as is
  2. store the whole pkg dir (and including the other files besides .gems)
  3. run the equivalent of find ./pkg ! -name "*.gem" -delete to delete non-.gem files
  4. tell rake gem to put the .gems somewhere in a folder by themselves

I'm not fully opposed to the current approach, although would you consider calling the new subfolder "gems" since that's what'll go in (rather than pkg_artifacts)?

Also, I recommend having the mkdir/move be a separate job step. Generally my preference is to have more single-purpose job steps rather than job steps with multiple commands.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented your suggestions with 741d1c5.


- save_cache:
name: save ports cache
paths:
- ./ports
key: ports-libiconv115-openssl111s-freetds1124

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally wanted to use the checksum of extconsts.rb for the ports cache, right now I hardcoded the used version into the name of the cache, but it could also be an option to check if we manage to get the same checksum on both systems. I could further investigate here in a follow-up to make the cache key not dependent on a hardcoded value.

I'm trying to brainstorm alternate approaches here... even something like v1-ports-{{ checksum "extconsts.rb" }} seems like an improvement, although that ignores the fact that those versions are overridable via env var, so another brainstorm: a new task rake ports:versions_file that's basically File.write '.ports_versions' { "iconv=#{ICONV_VERSION}\nfreetds=#{FREETDS_VERSION}\n" }, etc. then our CI config could have {{ checksum ".ports_versions" }}.

Also, can you clarify why it's necessary to switch from one job step per ports to compiling them all in one step? I preferred the version where we were able to see each of those individually. If it was just for caching, I assume we could still do restore cache, iconv, freetds, openssl, save cache, since each one of those individual steps would (or wouldn't) benefit from the restored cache, and would all be present by the time save_cache ran.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9d12f78 implements what you suggest with file the base the cache on. Let me know what you think about the code changes. I didn't want to repeat the library names again, that was my main concern behind the changes.

Also, can you clarify why it's necessary to switch from one job step per ports to compiling them all in one step?

You kind of mentioned my intention behind this below:

(I also recognize the counterpoint, which is the final fat gem that we publish is cross compiled, so why not do it that same way here... I'm conflicted).

I'll give a more detailed answer in that conversation.


- save_cache:
name: save cross-compiled-code
paths:
- ./tmp
key: compiled-<< pipeline.git.revision >>

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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ misc
/exe/*
/ports/*
!/ports/patches/
test/reports
7 changes: 0 additions & 7 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,2 @@
source 'https://rubygems.org'
gemspec

group :development do
end

group :test do
gem 'minitest'
end
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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}"],
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 1 addition & 1 deletion ext/tiny_tds/extconsts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
13 changes: 6 additions & 7 deletions tasks/native_gem.rake
Original file line number Diff line number Diff line change
@@ -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
17 changes: 0 additions & 17 deletions tasks/ports/libiconv.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
andyundso marked this conversation as resolved.
# 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}",
Expand Down
31 changes: 31 additions & 0 deletions test/bin/install-mssql.ps1
Original file line number Diff line number Diff line change
@@ -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 ..."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely agree it'd be great to test on Windows (especially since the job name is already "test windows"... oops... 🤦‍♂️). However, installing SQL Server is SUCH a headache. Are we able to use Docker for this? I don't think the Windows container is available any longer, so we'd need to use the Linux image, which ought to be possible by connecting to it via setup_remote_docker, I think

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the Windows container are no longer available. But also Windows containers were quite a pain to set up, I can see why it was easier for Microsoft to just push WSL2 😁

https://circleci.com/docs/building-docker-images/#accessing-services doesn't allow to access services from the primary executor. It looks like it's a detached machine somewhere else in the CircleCI cloud.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That said, I use a similar version of this script at work and has been working without any changes for more than a year now. I also use it for SQL server 2019 and 2022, so in case the pipeline should be extended at some point to test across different SQL server versions, it should be possible with little modification.

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';"
2 changes: 2 additions & 0 deletions test/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tiny_tds.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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