Skip to content

Commit

Permalink
✨ add openrewrite subcommand (#18)
Browse files Browse the repository at this point in the history
Signed-off-by: Pranav Gaikwad <pgaikwad@redhat.com>
  • Loading branch information
pranavgaikwad authored Aug 10, 2023
1 parent e2573c7 commit 8e5490f
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.vscode/

env.local
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ FROM registry.access.redhat.com/ubi9-minimal as rulesets

RUN microdnf -y install git
RUN git clone https://github.com/konveyor/rulesets
RUN git clone https://github.com/windup/windup-rulesets

# Build the manager binary
FROM golang:1.18 as builder
Expand All @@ -25,9 +26,10 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o kantra main.go

FROM quay.io/konveyor/analyzer-lsp:latest

RUN mkdir /opt/rulesets
RUN mkdir /opt/rulesets /opt/openrewrite /opt/input
COPY --from=builder /workspace/kantra /usr/local/bin/kantra
COPY --from=shim /usr/bin/windup-shim /usr/local/bin
COPY --from=rulesets /rulesets/default/generated /opt/rulesets
COPY --from=rulesets /windup-rulesets/rules/rules-reviewed/openrewrite /opt/openrewrite

ENTRYPOINT ["kantra"]
2 changes: 1 addition & 1 deletion cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func AnalyzeFlags() error {

func readRuleFilesForLabels(label string) ([]string, error) {
var labelsSlice []string
err := filepath.WalkDir(Settings.RuleSetPath, walkRuleSets(Settings.RuleSetPath, label, &labelsSlice))
err := filepath.WalkDir(RulesetPath, walkRuleSets(RulesetPath, label, &labelsSlice))
if err != nil {
return nil, err
}
Expand Down
116 changes: 116 additions & 0 deletions cmd/container.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package cmd

import (
"context"
"fmt"
"io"
"os"
"os/exec"
)

type containerCommand struct {
stdout io.Writer
stderr io.Writer
containerName string
containerImage string
entrypointBin string
entrypointArgs []string
workdir string
// map of source -> dest paths to mount
volumes map[string]string
}

type Option func(c *containerCommand)

func WithContainerImage(i string) Option {
return func(c *containerCommand) {
c.containerImage = i
}
}

func WithContainerName(n string) Option {
return func(c *containerCommand) {
c.containerName = n
}
}

func WithEntrypointBin(b string) Option {
return func(c *containerCommand) {
c.entrypointBin = b
}
}

func WithEntrypointArgs(args ...string) Option {
return func(c *containerCommand) {
c.entrypointArgs = args
}
}

func WithWorkDir(w string) Option {
return func(c *containerCommand) {
c.workdir = w
}
}

func WithVolumes(m map[string]string) Option {
return func(c *containerCommand) {
c.volumes = m
}
}

func WithStdout(o io.Writer) Option {
return func(c *containerCommand) {
c.stdout = o
}
}

func WithStderr(e io.Writer) Option {
return func(c *containerCommand) {
c.stderr = e
}
}

func NewContainerCommand(ctx context.Context, opts ...Option) *exec.Cmd {
c := &containerCommand{
containerImage: Settings.RunnerImage,
entrypointArgs: []string{},
volumes: make(map[string]string),
stdout: os.Stdout,
stderr: os.Stderr,
}

for _, opt := range opts {
opt(c)
}

args := []string{"run", "-it"}
if c.containerName != "" {
args = append(args, "--name")
args = append(args, c.containerName)
}

if c.entrypointBin != "" {
args = append(args, "--entrypoint")
args = append(args, c.entrypointBin)
}

if c.workdir != "" {
args = append(args, "--workdir")
args = append(args, c.workdir)
}

for sourcePath, destPath := range c.volumes {
args = append(args, "-v")
args = append(args, fmt.Sprintf("%s:%s:Z", sourcePath, destPath))
}

args = append(args, c.containerImage)
if len(c.entrypointArgs) > 0 {
args = append(args, c.entrypointArgs...)
}

cmd := exec.CommandContext(ctx, Settings.PodmanBinary, args...)
cmd.Stdout = c.stdout
cmd.Stderr = c.stderr
return cmd
}
144 changes: 144 additions & 0 deletions cmd/openrewrite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package cmd

import (
"context"
"fmt"
"os"
"strings"

"github.com/apex/log"
"github.com/spf13/cobra"
)

type openRewriteCommand struct {
listTargets bool
input string
target string
goal string
miscOpts string
}

func NewOpenRewriteCommand() *cobra.Command {
openRewriteCmd := &openRewriteCommand{}

openRewriteCommand := &cobra.Command{
Use: "openrewrite",

Short: "Transform application source code using OpenRewrite recipes",
PreRun: func(cmd *cobra.Command, args []string) {
if !cmd.Flags().Lookup("list-targets").Changed {
cmd.MarkFlagRequired("input")
cmd.MarkFlagRequired("target")
}
},
RunE: func(cmd *cobra.Command, args []string) error {
err := openRewriteCmd.Validate()
if err != nil {
return err
}
err = openRewriteCmd.Run(cmd.Context())
if err != nil {
log.Errorf("failed to execute openrewrite command", err)
return err
}
return nil
},
}
openRewriteCommand.Flags().BoolVarP(&openRewriteCmd.listTargets, "list-targets", "l", false, "list all available OpenRewrite recipes")
openRewriteCommand.Flags().StringVarP(&openRewriteCmd.target, "target", "t", "", "target openrewrite recipe to use. Run --list-targets to get a list of packaged recipes.")
openRewriteCommand.Flags().StringVarP(&openRewriteCmd.goal, "goal", "g", "dryRun", "target goal")
openRewriteCommand.Flags().StringVarP(&openRewriteCmd.input, "input", "i", "", "path to application source code directory")

return openRewriteCommand
}

func (o *openRewriteCommand) Validate() error {
if o.listTargets {
return nil
}

stat, err := os.Stat(o.input)
if err != nil {
return err
}
if !stat.IsDir() {
log.Errorf("input path %s is not a directory", o.input)
return err
}

if o.target == "" {
return fmt.Errorf("target recipe must be specified")
}

if _, found := recipes[o.target]; !found {
return fmt.Errorf("unsupported target recipe. use --list-targets to get list of all recipes")
}
return nil
}

type recipe struct {
names []string
path string
description string
}

var recipes = map[string]recipe{
"eap8-xml": {
names: []string{"org.jboss.windup.eap8.FacesWebXml"},
path: "eap8/xml/rewrite.yml",
description: "Transform Faces Web XML for EAP8 migration",
},
"jakarta-xml": {
names: []string{"org.jboss.windup.jakarta.javax.PersistenceXml"},
path: "jakarta/javax/xml/rewrite.yml",
description: "Transform Persistence XML for Jakarta migration",
},
"jakarta-bootstrapping": {
names: []string{"org.jboss.windup.jakarta.javax.BootstrappingFiles"},
path: "jakarta/javax/bootstrapping/rewrite.yml",
description: "Transform bootstrapping files for Jakarta migration",
},
"jakarta-imports": {
names: []string{"org.jboss.windup.JavaxToJakarta"},
path: "jakarta/javax/imports/rewrite.yml",
description: "Transform dependencies and imports for Jakarta migration",
},
"quarkus-properties": {
names: []string{"org.jboss.windup.sb-quarkus.Properties"},
path: "quarkus/springboot/properties/rewrite.yml",
description: "Migrate Springboot properties to Quarkus",
},
}

func (o *openRewriteCommand) Run(ctx context.Context) error {
if o.listTargets {
fmt.Printf("%-20s\t%s\n", "NAME", "DESCRIPTION")
for name, recipe := range recipes {
fmt.Printf("%-20s\t%s\n", name, recipe.description)
}
return nil
}

volumes := map[string]string{
o.input: InputPath,
}
args := []string{
"-U", "org.openrewrite.maven:rewrite-maven-plugin:run",
fmt.Sprintf("-Drewrite.configLocation=%s/%s",
OpenRewriteRecipesPath, recipes[o.target].path),
fmt.Sprintf("-Drewrite.activeRecipes=%s",
strings.Join(recipes[o.target].names, ",")),
}
cmd := NewContainerCommand(
ctx,
WithEntrypointArgs(args...),
WithEntrypointBin("/usr/bin/mvn"),
WithVolumes(volumes),
WithWorkDir(InputPath),
)
err := cmd.Run()
if err != nil {
return err
}
return nil
}
12 changes: 10 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Copyright © 2023 NAME HERE <EMAIL ADDRESS>
package cmd

import (
"context"
"log"
"os"

Expand All @@ -17,6 +18,10 @@ var rootCmd = &cobra.Command{
Long: ``,
}

func init() {
rootCmd.AddCommand(NewOpenRewriteCommand())
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
Expand All @@ -25,8 +30,11 @@ func Execute() {
log.Fatal("failed to load global settings")
}

rootCmd.Use = Settings.CommandName
err = rootCmd.Execute()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()

rootCmd.Use = Settings.RootCommandName
err = rootCmd.ExecuteContext(ctx)
if err != nil {
os.Exit(1)
}
Expand Down
15 changes: 12 additions & 3 deletions cmd/settings.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package cmd

import "github.com/codingconcepts/env"
import (
"github.com/codingconcepts/env"
)

var Settings = &Config{}

const (
RulesetPath = "/opt/rulesets"
OpenRewriteRecipesPath = "/opt/openrewrite"
InputPath = "/opt/input"
)

type Config struct {
RuleSetPath string `env:"RULESET_PATH" default:"/opt/rulesets/"`
CommandName string `env:"CMD_NAME" default:"kantra"`
RootCommandName string `env:"CMD_NAME" default:"kantra"`
PodmanBinary string `env:"PODMAN_BIN" default:"/usr/bin/podman"`
RunnerImage string `env:"RUNNER_IMG" default:"quay.io/konveyor/kantra"`
}

func (c *Config) Load() error {
Expand Down
21 changes: 21 additions & 0 deletions cmd/transform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cmd

import (
"github.com/spf13/cobra"
)

func NewTransformCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "transform",

Short: "Transform application source code or windup XML rules",
PreRunE: func(cmd *cobra.Command, args []string) error {
return nil
},
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}
cmd.AddCommand(NewOpenRewriteCommand())
return cmd
}
9 changes: 9 additions & 0 deletions env.local.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

# this is a sample file that contains environment variables used by kantra
# by default, values for these variables are set to work in the docker image
# when running locally, you will need to tweak these values as per your env
# copy this file to `env.local` and source it in your bash session to use CLI

PODMAN_BIN=
RUNNER_IMG=
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package main

import "github.com/konveyor-ecosystem/kantra/cmd"
import (
"github.com/konveyor-ecosystem/kantra/cmd"
)

func main() {
cmd.Execute()
Expand Down

0 comments on commit 8e5490f

Please sign in to comment.