Documentation
¶
Overview ¶
Package ruby implements omnibump support for Ruby projects using Bundler.
Updates are performed via direct Gemfile.lock text editing — no Ruby or Bundler CLI is required at runtime. This matches the deployment model where omnibump runs in melange build sandboxes without guaranteed toolchain availability.
Manifest files ¶
The package detects Ruby projects by the presence of Gemfile.lock in the target directory. Both Gemfile and Gemfile.lock are listed as manifest files, but only Gemfile.lock is parsed and modified.
Gemfile.lock parsing ¶
The parser handles the three source section types found in Bundler lockfiles:
- GEM — gems from a rubygems remote (e.g. rubygems.org)
- GIT — gems from a git repository
- PATH — gems from a local filesystem path
Within each section, spec lines (4-space indent) identify gems and their versions, while dependency constraint lines (6-space indent) are preserved but not modified during updates.
Update strategy ¶
All updates are direct text replacements: the version in " gemname (oldversion)" is replaced with the new version. Dependency constraint lines are left untouched. Downgrade attempts are detected via semver comparison and skipped with a warning.
Limitations ¶
- No constraint validation: bumping a gem to a version that violates another gem's constraints will produce an inconsistent lockfile.
- No checksum or integrity handling (Gemfile.lock has none, unlike go.sum).
- AnalyzeRemote is not yet implemented.
Example ¶
package main
import (
"fmt"
"github.com/chainguard-dev/omnibump/pkg/languages"
)
func main() {
// Get the Ruby language from the registry.
lang, err := languages.Get("ruby")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("Language:", lang.Name())
}
Output: Language: ruby
Index ¶
- Constants
- Variables
- func DoUpdate(ctx context.Context, packages map[string]*Package, gemfileLockPath string) error
- type Analyzer
- func (a *Analyzer) Analyze(ctx context.Context, projectPath string) (*analyzer.AnalysisResult, error)
- func (a *Analyzer) AnalyzeRemote(_ context.Context, _ map[string][]byte) (*analyzer.RemoteAnalysisResult, error)
- func (a *Analyzer) RecommendStrategy(_ context.Context, _ *analyzer.AnalysisResult, deps []analyzer.Dependency) (*analyzer.Strategy, error)
- type GemPackage
- type Package
- type Ruby
- func (r *Ruby) Detect(_ context.Context, dir string) (bool, error)
- func (r *Ruby) GetManifestFiles() []string
- func (r *Ruby) Name() string
- func (r *Ruby) SupportsAnalysis() bool
- func (r *Ruby) Update(ctx context.Context, cfg *languages.UpdateConfig) error
- func (r *Ruby) Validate(ctx context.Context, cfg *languages.UpdateConfig) error
Examples ¶
Constants ¶
const ( // LanguageRuby is the language identifier. LanguageRuby = "ruby" // ManifestGemfile is the Gemfile manifest filename. ManifestGemfile = "Gemfile" // ManifestGemfileLock is the Gemfile.lock manifest filename. ManifestGemfileLock = "Gemfile.lock" )
Language and manifest constants.
Variables ¶
var ( // ErrGemfileLockNotFound is returned when Gemfile.lock is not found. ErrGemfileLockNotFound = errors.New("gemfile.lock not found") // ErrRemoteAnalysisNotImplemented is returned when remote analysis is not implemented. ErrRemoteAnalysisNotImplemented = errors.New("remote analysis not yet implemented") // ErrValidationFailed is returned when post-update validation detects a version mismatch. ErrValidationFailed = errors.New("validation failed") // ErrPackageNotFound is returned when a dependency is not found in the lockfile. ErrPackageNotFound = errors.New("package not found in Gemfile.lock") )
var ( // ErrInvalidGemfileLock is returned when the lockfile cannot be parsed. ErrInvalidGemfileLock = errors.New("invalid Gemfile.lock format") )
Functions ¶
Types ¶
type Analyzer ¶
type Analyzer struct{}
Analyzer implements the analyzer.Analyzer interface for Ruby projects.
func (*Analyzer) Analyze ¶
func (a *Analyzer) Analyze(ctx context.Context, projectPath string) (*analyzer.AnalysisResult, error)
Analyze performs dependency analysis on a Ruby project.
func (*Analyzer) AnalyzeRemote ¶
func (a *Analyzer) AnalyzeRemote(_ context.Context, _ map[string][]byte) (*analyzer.RemoteAnalysisResult, error)
AnalyzeRemote performs dependency analysis on remotely-fetched Ruby files. Not yet implemented for Ruby — returns error.
func (*Analyzer) RecommendStrategy ¶
func (a *Analyzer) RecommendStrategy(_ context.Context, _ *analyzer.AnalysisResult, deps []analyzer.Dependency) (*analyzer.Strategy, error)
RecommendStrategy always recommends direct updates for Ruby dependencies. Ruby doesn't have a "property" abstraction like Maven.
type GemPackage ¶
type GemPackage struct {
Name string
Version string
Source string
Dependencies []string // Dependency constraints (e.g., "rack (~> 3.0)")
}
GemPackage represents a gem from Gemfile.lock.
func ParseGemfileLock ¶
func ParseGemfileLock(r io.Reader) ([]GemPackage, error)
ParseGemfileLock parses a Gemfile.lock file and returns the list of gem packages. It handles GEM, GIT, PATH, PLATFORMS, and BUNDLED WITH sections.
type Ruby ¶
type Ruby struct{}
Ruby implements the Language interface for Ruby projects.
func (*Ruby) Detect ¶
Detect checks if Ruby manifest files exist in the directory.
Example ¶
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/chainguard-dev/omnibump/pkg/languages/ruby"
)
func main() {
// Create a temporary directory with a Gemfile.lock to simulate a Ruby project.
dir, err := os.MkdirTemp("", "ruby-example-*")
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = os.RemoveAll(dir) }()
lockfile := filepath.Join(dir, "Gemfile.lock")
content := "GEM\n remote: https://rubygems.org/\n specs:\n rack (3.1.8)\n"
if err := os.WriteFile(lockfile, []byte(content), 0o644); err != nil {
fmt.Println("error:", err)
return
}
r := &ruby.Ruby{}
detected, err := r.Detect(context.Background(), dir)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("Detected:", detected)
}
Output: Detected: true
func (*Ruby) GetManifestFiles ¶
GetManifestFiles returns Ruby manifest files.
Example ¶
package main
import (
"fmt"
"github.com/chainguard-dev/omnibump/pkg/languages/ruby"
)
func main() {
r := &ruby.Ruby{}
files := r.GetManifestFiles()
for _, f := range files {
fmt.Println(f)
}
}
Output: Gemfile Gemfile.lock
func (*Ruby) SupportsAnalysis ¶
SupportsAnalysis returns true since Ruby has analysis capabilities.