Modernize gdal-ruby packaging, multi-arch CI, and Fulcrum coverage - #1
Modernize gdal-ruby packaging, multi-arch CI, and Fulcrum coverage#1treyhyde wants to merge 5 commits into
Conversation
Align the gem for GitHub Packages release-please publishing, replace Travis with amd64/arm64 CI, fix Ruby 3 SWIG allocator warnings and GDAL 3.13 compile breaks, and add fixture specs that mirror Fulcrum shapefile import usage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Stop ignoring .tool-versions per review, and load the local gem via bundle exec -Ilib in the CI smoke step after specs pass. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Modernizes the gdal (gdal-ruby) gem for current Fulcrum usage by updating packaging/release automation, multi-arch CI, and adding fixture-based specs that mirror Fulcrum’s shapefile importer call surface.
Changes:
- Replaced Travis with GitHub Actions CI across Ubuntu amd64/arm64 and macOS arm64; added release-please + publish workflow to GitHub Packages.
- Modernized gemspec/metadata and aligned versioning to
3.1.0. - Updated native extension build/config and SWIG runtime glue for Ruby 3.2+ warnings and GDAL 3.13 compilation; added Fulcrum-parity specs + fixtures.
Reviewed changes
Copilot reviewed 30 out of 46 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/support/fulcrum_shapefile_importer.rb | Adds a small adapter module to mirror Fulcrum’s shapefile import call surface for specs. |
| spec/spec_helper.rb | Introduces shared RSpec setup and fixture path constant used across new specs. |
| spec/ruby3_warnings_spec.rb | Adds a regression spec to ensure Ruby 3.2+ SWIG allocator warnings are not emitted. |
| spec/gdal_spec.rb | Updates core gem specs for version reporting, WKT→GeoJSON, and require entrypoints. |
| spec/fulcrum_shapefile_import_spec.rb | Adds fixture-driven coverage approximating Fulcrum’s shapefile import usage. |
| spec/fixtures/shapefiles/polygons/polygons.prj | Adds projection fixtures for polygon shapefile tests. |
| spec/fixtures/shapefiles/points/points.prj | Adds projection fixtures for point shapefile tests (incl. 3D). |
| spec/fixtures/shapefiles/multipoint/multipoint.prj | Adds projection fixtures for multipoint shapefile tests. |
| spec/fixtures/shapefiles/lines/lines.prj | Adds projection fixtures for line shapefile tests. |
| spec/fixtures/shapefiles/flat/points.prj | Adds projection fixtures for “flat” point shapefile tests. |
| release-please-config.json | Configures release-please for Ruby releases and changelog generation. |
| lib/gdal-ruby/version.rb | Aligns in-repo version to 3.1.0 and adds sync guidance comments. |
| gdal.gemspec | Modernizes gemspec metadata, required Ruby version, files list, and dev deps. |
| ext/gdal-ruby/osr/osr.cpp | Updates SWIG runtime class allocation behavior and fixes GDAL 3.13 compile issues. |
| ext/gdal-ruby/osr/extconf.rb | Switches OSR extension build to shared extconf helper. |
| ext/gdal-ruby/ogr/ogr.cpp | Updates SWIG runtime class allocation behavior for Ruby 3.2+. |
| ext/gdal-ruby/ogr/extconf.rb | Switches OGR extension build to shared extconf helper. |
| ext/gdal-ruby/gdalconst/gdalconst.c | Updates SWIG runtime class allocation behavior for Ruby 3.2+. |
| ext/gdal-ruby/gdalconst/extconf.rb | Switches gdalconst extension build to shared extconf helper. |
| ext/gdal-ruby/gdal/gdal.cpp | Updates SWIG runtime class allocation behavior and applies GDAL 3.13 const-correctness workaround. |
| ext/gdal-ruby/gdal/extconf.rb | Switches GDAL extension build to shared extconf helper. |
| ext/gdal-ruby/extconf_helper.rb | Adds a unified extconf helper for GDAL detection/flags across extensions. |
| Rakefile | Updates spec task to ensure compilation and consistent RSpec options. |
| README.md | Updates docs for new CI, install path (GitHub Packages), and long-term migration guidance. |
| CHANGELOG.md | Adds 3.1.0 release notes and cleans up older entries formatting. |
| .travis.yml | Removes obsolete Travis CI configuration. |
| .tool-versions | Pins local Ruby version for asdf/tooling consistency. |
| .release-please-manifest.json | Adds release-please manifest tracking current version. |
| .gitignore | Ignores RSpec examples persistence file. |
| .github/workflows/release.yml | Adds release-please + gem publishing workflow and GitHub Release asset upload. |
| .github/workflows/ci.yml | Adds multi-arch CI workflow installing GDAL and running specs. |
Comments suppressed due to low confidence (3)
spec/support/fulcrum_shapefile_importer.rb:1
OGRLayer#get_featureexpects a Feature ID (FID), not a positional “cursor index”. For shapefiles where FIDs aren’t 0..N-1 (or contain gaps), this can returnnilor the wrong feature, causing failures or incorrect imports. Prefer iterating withreset_reading+get_next_feature(matching Fulcrum’s “next_feature loop”), or useset_next_by_index(index)+get_next_featureif you need index-based access.
spec/support/fulcrum_shapefile_importer.rb:1each_featuretypically implies yielding to a block (or returning an Enumerator), but this implementation builds and returns an Array. To avoid confusion for future readers/maintainers, either rename it to something likefeatures/read_features, or implement block-yielding behavior (and return an Enumerator when no block is given).
spec/support/fulcrum_shapefile_importer.rb:1- Assigning local variables to
nilinensuredoesn’t deterministically release native GDAL/OGR resources; it’s effectively a no-op unless you rely on a later GC cycle. If explicit cleanup is required, call the relevant close/destroy method exposed by the bindings; otherwise, removing thisensureblock will reduce misleading “resource cleanup” signaling.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Use Open3 for gdal-config, CSLDuplicate for metadata, ABS macro for UTM zone, and sequential OGR feature iteration in Fulcrum-shaped specs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Also tightened the Fulcrum-shaped helper based on the low-confidence notes:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 46 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
spec/support/fulcrum_shapefile_importer.rb:1
- Inside the feature loop,
field_info(lyr)andfield_count(lyr)are recomputed for every row even though layer schema is constant. Movinginfo = field_info(lyr)andcount = field_count(lyr)outside thewhileloop will reduce repeated native calls and speed up larger shapefiles. This also mirrors the same field-extraction logic already present inread_feature, which could be factored into a shared helper to avoid drift between the two paths.
ext/gdal-ruby/gdal/gdal.cpp:2184 CSLDuplicate(...)allocates a new string list; these functions return the allocatedchar**without an obvious correspondingCSLDestroy(...)on the Ruby/SWIG side. If the SWIG typemap for returnedchar **does not free withCSLDestroy, this becomes a per-call memory leak. Consider changing the wrapper/typemap so the duplicated list is destroyed after conversion to Ruby (or ensure the return typemap frees using the correct GDAL deallocator).
/* GDAL 3+ returns CSLConstList. Duplicate so Ruby owns a mutable char**
without aliasing GDAL's const storage (avoids const_cast UB on mutate). */
return CSLDuplicate(GDALGetMetadata(self, pszDomain));
}
SWIGINTERN char **GDALMajorObjectShadow_GetMetadata_List(GDALMajorObjectShadow *self,char const *pszDomain=""){
return CSLDuplicate(GDALGetMetadata(self, pszDomain));
}
gdal.gemspec:28
- RubyGems metadata fields like
github_repoare typically expected to be anhttps://github.com/...URL; using anssh://URI may not be recognized or linked correctly by RubyGems consumers/tools. Preferhttps://github.com/fulcrumapp/gdal-ruby(or a canonical git URL) for better compatibility.
gem.metadata = {
"bug_tracker_uri" => "https://github.com/fulcrumapp/gdal-ruby/issues",
"changelog_uri" => "https://github.com/fulcrumapp/gdal-ruby/blob/main/CHANGELOG.md",
"homepage_uri" => gem.homepage,
"source_code_uri" => "https://github.com/fulcrumapp/gdal-ruby",
"github_repo" => "ssh://github.com/fulcrumapp/gdal-ruby",
"allowed_push_host" => "https://rubygems.pkg.github.com/fulcrumapp"
}
README.md:69
- The README usage example requires
gdal-ruby/ogr, but the rest of this PR adds coverage aroundrequire \"gdal\"as the primary entrypoint (and the gem name isgdal). Consider updating the example (or adding an additional example) to userequire \"gdal\"so the docs match the supported/verified require path.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 46 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
spec/support/fulcrum_shapefile_importer.rb:1
schema_columnscomputesinfo = field_info(layer)but then callsfield_count(layer)which callsfield_info(layer)again, doing extra native calls unnecessarily. Sinceinfois already available, prefer usinginfo.get_field_count.timeshere to avoid redundant layer-defn lookups.
spec/support/fulcrum_shapefile_importer.rb:1- The field extraction logic (type switch + accessor selection) is duplicated here and again in
read_features. This duplication increases the chance of the Fulcrum-mirroring behavior drifting between the two code paths. Consider extracting a small helper (e.g.,read_attrs(feature, layer_defn)) that takesinfo/field_countonce and is used by both methods.
spec/support/fulcrum_shapefile_importer.rb:1 field_info(lyr)andfield_count(lyr)are invariant for a layer and don’t need to be recomputed for every feature. Movinginfo = field_info(lyr)andcount = info.get_field_countoutside thewhileloop avoids repeated native calls and makes the loop intent clearer.
spec/spec_helper.rb:1- Defining
FIXTURESas a top-level constant can cause accidental coupling across specs/support files and makes it easier to collide with other constants if the suite grows. Consider namespacing it (e.g.,SpecSupport::FIXTURES) or exposing it viaRSpec.configurationto keep global namespace clean.
Destroy CSLDuplicate results after SWIG converts GetMetadata to Ruby. Also use https github_repo metadata, require "gdal" in README, namespace spec fixtures, and dedupe Fulcrum importer field extraction. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 46 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
spec/ruby3_warnings_spec.rb:1
layer.get_feature(0)assumes the first feature’s FID is0, but OGR’sget_featuretakes an FID (which is not guaranteed to be 0..N-1). This can make the warning-regression spec fail depending on how the fixture was created. Usereset_reading+get_next_feature, orset_next_by_index(0)+get_next_feature, to make the test index-based and stable.
.github/workflows/release.yml:62- The release pipeline publishes the gem immediately after
gem buildwithout running the specs (or even a compile verification via the Rake tasks). Given the native-extension surface and the goal of preventing Ruby/GDAL drift regressions, it’s safer to runbundle exec rake spec(or at leastbundle exec rake compile) in the publish job before pushing, so broken releases don’t get published.
- name: Build gem
run: |
gem build gdal.gemspec
ls -la gdal-*.gem
- name: Publish to GitHub Packages
env:
GEM_HOST_API_KEY: Bearer ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p ~/.gem
printf '%s\n' '---' ":github: ${GEM_HOST_API_KEY}" > ~/.gem/credentials
chmod 0600 ~/.gem/credentials
gem push --key github --host https://rubygems.pkg.github.com/fulcrumapp gdal-*.gem
Make gem.files resilient without git, fix GITHUB_TOKEN expression in release.yml, run specs before gem push, and use index-based feature access in the Ruby 3 warning regression. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 46 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
spec/support/fulcrum_shapefile_importer.rb:1
String#force_encoding("UTF-8")does not validate or transcode bytes; it can produce strings marked UTF-8 that still contain invalid UTF-8 sequences, which can later break JSON serialization, DB writes, or string operations. If the intent is “ensure valid UTF-8 text,” use a transcoding approach (e.g.,encode("UTF-8", invalid: :replace, undef: :replace, replace: ...)) rather thanforce_encoding.
ext/gdal-ruby/gdal/gdal.cpp:4953CSLDestroy(result)is only reached on the normal path. If any Ruby API call in the conversion loop raises (e.g.,rb_hash_aset,rb_str_new2), the function can exit via exception and leak the duplicated CSL list. Consider wrapping the Ruby conversion inrb_ensure(or equivalent) soCSLDestroy(result)always runs, even when an exception is raised.
/* GetMetadata_Dict duplicates via CSLDuplicate; free after Ruby conversion. */
CSLDestroy(result);
ext/gdal-ruby/osr/osr.cpp:15
- This
ABSmacro evaluatesxmultiple times, which is unsafe ifxhas side effects (and can also overflow forINT_MIN). Prefer using an existing GDAL/CPL helper (e.g.,CPL_ABSif available) or an inline function/template to ensure single evaluation and safer behavior.
#define ABS(x) ((x) < 0 ? -(x) : (x))
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 46 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
gdal.gemspec:46
- When
git ls-filesis unavailable and the gemspec falls back toDir.glob(..., File::FNM_DOTMATCH), this will include hidden file entries that aren’t explicitly excluded (notably.git/*if present, plus other dotfiles). Consider explicitly rejecting.git/(and possibly other common hidden paths) in the fallback list, or switching the fallback to an allowlist (e.g.,lib/,ext/,README.md,LICENSE, etc.) to avoid accidental packaging of repository metadata.
Dir.glob("**/*", File::FNM_DOTMATCH)
end
tracked
.reject(&:empty?)
.reject { |f| f == "." || f == ".." || f.end_with?("/.") || f.end_with?("/..") }
.reject { |f| File.directory?(f) }
.reject do |f|
f.start_with?("spec/", "test/", ".github/", "tmp/", "pkg/") ||
f == ".travis.yml" ||
f.end_with?(".bundle", ".so", ".o", ".gem")
end
ext/gdal-ruby/extconf_helper.rb:57
- If
gdal-configfails, the raised error omits stderr, which can make diagnosing CI/build failures difficult (e.g., missing shared libs, bad shebang, permission issues). Consider usingOpen3.capture3and including stderr (and possibly the exit status) in the exception message so failures are actionable.
def gdal_config_output(gdal_config, *args)
output, status = Open3.capture2(gdal_config, *args)
raise "failed to run #{gdal_config} #{args.join(' ')}" unless status.success?
output
end
Why
This gem is an old SWIG dump that still blocks Fulcrum on modern Ruby/GDAL and multi-arch builds. Version drift (repo said
2.0.0while Fulcrum locked3.0.0), dead Travis CI, Ruby 3 SWIG allocator warnings in Rails logs, and GDAL 3.13 compile breaks all make it hard to keep as an internal dependency. We also need a real release path to GitHub Packages.What changed
rubygems.pkg.github.com/fulcrumappwhen a release PR merges tomain>= 3.1, aligned version to3.1.0T_DATAallocator warnings on requireCSLConstListconst-correctness,ABSshim, no-std=c++17on pure-Cgdalconst)Import::Formats::Shapefilecall surface (open, OFT/WKB constants, field accessors,flatten_to_2d,export_to_json, point/line/polygon/multipoint)Verification
Locally on arm64 / GDAL 3.13.1 / Ruby 3.3.4:
Notes for reviewers
rgeo-shapefileand retire this dependency.libgdalat compile time.gdal 3.1.0to Packages.