Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions internal/librarian/librarian.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/googleapis/librarian/internal/librarian/nodejs"
"github.com/googleapis/librarian/internal/librarian/php"
"github.com/googleapis/librarian/internal/librarian/python"
"github.com/googleapis/librarian/internal/librarian/ruby"
"github.com/googleapis/librarian/internal/librarian/rust"
"github.com/googleapis/librarian/internal/tool/protoc"
"github.com/googleapis/librarian/internal/yaml"
Expand Down Expand Up @@ -123,6 +124,8 @@ Examples:
return php.Install(ctx, tools)
case config.LanguagePython:
return python.Install(ctx)
case config.LanguageRuby:
return ruby.Install(ctx, tools)
case config.LanguageRust:
return rust.Install(ctx, tools)
default:
Expand Down
35 changes: 35 additions & 0 deletions internal/librarian/ruby/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,36 @@
package ruby

import (
"errors"
"fmt"
"os/exec"
"path/filepath"

"context"

"github.com/googleapis/librarian/internal/cache"
"github.com/googleapis/librarian/internal/config"
"github.com/googleapis/librarian/internal/tool/gem"
)

const toolsDir = "ruby_tools"

var (
errNoGems = errors.New("no gem tools specified")
errMissingExecutable = errors.New("is not installed or not in PATH, which is required for Ruby tool installation")
)

// Install installs Ruby gem dependencies.
func Install(ctx context.Context, tools *config.Tools) error {
if err := verify(tools); err != nil {
return err
}
if err := gem.Install(ctx, tools.Gem); err != nil {
return err
}
return nil
}

// InstallDir gets the directory where tools should be installed.
func InstallDir() (string, error) {
dir, err := cache.BinDirectory()
Expand All @@ -44,3 +66,16 @@ func binDir() (string, error) {
}
return filepath.Join(installDir, "bin"), nil
}

func verify(tools *config.Tools) error {
if tools == nil || len(tools.Gem) == 0 {
return errNoGems
}

for _, cmd := range []string{"gem"} {
if _, err := exec.LookPath(cmd); err != nil {
return fmt.Errorf("%s %w: %w", cmd, errMissingExecutable, err)
}
}
return nil
}
103 changes: 103 additions & 0 deletions internal/librarian/ruby/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,45 @@
package ruby

import (
"bytes"
"errors"
"os"
"path/filepath"
"testing"

"github.com/googleapis/librarian/internal/config"
)

func TestInstall(t *testing.T) {
stubDir := t.TempDir()
gemStubPath := filepath.Join(stubDir, "gem")
recordFile := filepath.Join(t.TempDir(), "calls.txt")
stubContent := "#!/bin/sh\necho \"$*\" >> \"" + recordFile + "\"\nexit 0\n"
if err := os.WriteFile(gemStubPath, []byte(stubContent), 0o755); err != nil {
t.Fatal(err)
}
t.Setenv("PATH", stubDir+string(filepath.ListSeparator)+os.Getenv("PATH"))
tools := &config.Tools{
Gem: []*config.GemTool{
{
Name: "gapic-generator",
Version: "1.2.3",
},
},
}
if err := Install(t.Context(), tools); err != nil {
t.Fatalf("Install() returned unexpected error: %v", err)
}
data, err := os.ReadFile(recordFile)
if err != nil {
t.Fatalf("failed to read call records: %v", err)
}
want := []byte("install gapic-generator -v 1.2.3 --no-document\n")
if !bytes.Equal(data, want) {
t.Errorf("gem called with = %q, want %q", data, want)
}
}

func TestInstallDir(t *testing.T) {
binDir := t.TempDir()
t.Setenv("LIBRARIAN_BIN", binDir)
Expand All @@ -44,3 +79,71 @@ func TestBinDir(t *testing.T) {
t.Errorf("binDir() = %q, want %q", got, want)
}
}

func TestVerify(t *testing.T) {
stubDir := t.TempDir()
gemStubPath := filepath.Join(stubDir, "gem")
// Create a simple shell script stub for "gem".
stubContent := "#!/bin/sh\nexit 0\n"
if err := os.WriteFile(gemStubPath, []byte(stubContent), 0o755); err != nil {
t.Fatal(err)
}
t.Setenv("PATH", stubDir+string(filepath.ListSeparator)+os.Getenv("PATH"))
tools := &config.Tools{
Gem: []*config.GemTool{
{
Name: "a-gem-tool",
Version: "1.0",
},
},
}

if err := verify(tools); err != nil {
t.Errorf("verify() returned unexpected error: %v", err)
}
}

func TestVerify_Error(t *testing.T) {
for _, test := range []struct {
name string
tools *config.Tools
setup func(t *testing.T)
wantErr error
}{
{
name: "nil tools",
tools: nil,
wantErr: errNoGems,
},
{
name: "empty tools",
tools: &config.Tools{},
wantErr: errNoGems,
},
{
name: "missing gem in path",
tools: &config.Tools{
Gem: []*config.GemTool{
{
Name: "a-gem-tool",
Version: "1.0",
},
},
},
setup: func(t *testing.T) {
t.Setenv("PATH", t.TempDir())
},
wantErr: errMissingExecutable,
},
} {
t.Run(test.name, func(t *testing.T) {
if test.setup != nil {
test.setup(t)
}
err := verify(test.tools)
if !errors.Is(err, test.wantErr) {
t.Fatalf("verify() error = %v, wantErr = %v", err, test.wantErr)
}
})
}
}
Loading