github.com/r2d2-ai/cli@v1.20.0/api/shim.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path"
     9  	"path/filepath"
    10  	"regexp"
    11  	"strings"
    12  
    13  	"github.com/r2d2-ai/cli/common"
    14  	"github.com/r2d2-ai/cli/util"
    15  )
    16  
    17  const (
    18  	fileShimSupportGo string = "shim_support.go"
    19  	fileShimGo        string = "shim.go"
    20  	fileBuildGo       string = "build.go"
    21  	fileMakefile      string = "Makefile"
    22  	dirShim           string = "shim"
    23  )
    24  
    25  var fileSampleShimSupport = filepath.Join("examples", "engine", "shim", fileShimSupportGo)
    26  
    27  var flogoImportPattern = regexp.MustCompile(`^(([^ ]*)[ ]+)?([^@:]*)@?([^:]*)?:?(.*)?$`)
    28  
    29  type ShimBuilder struct {
    30  	appBuilder common.Builder
    31  	shim       string
    32  }
    33  
    34  func (sb *ShimBuilder) Build(project common.AppProject) error {
    35  
    36  	err := backupMain(project)
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	defer shimCleanup(project)
    42  
    43  	err = createShimSupportGoFile(project)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	if Verbose() {
    49  		fmt.Println("Preparing shim...")
    50  	}
    51  	built, err := prepareShim(project, sb.shim)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	if !built {
    57  		fmt.Println("Using go build to build shim...")
    58  
    59  		err := simpleGoBuild(project)
    60  		if err != nil {
    61  			return err
    62  		}
    63  	}
    64  
    65  	return nil
    66  }
    67  
    68  func prepareShim(project common.AppProject, shim string) (bool, error) {
    69  
    70  	buf, err := ioutil.ReadFile(filepath.Join(project.Dir(), fileFlogoJson))
    71  	if err != nil {
    72  		return false, err
    73  	}
    74  
    75  	flogoJSON := string(buf)
    76  
    77  	descriptor, err := util.ParseAppDescriptor(flogoJSON)
    78  	if err != nil {
    79  		return false, err
    80  	}
    81  
    82  	err = registerImports(project, descriptor)
    83  	if err != nil {
    84  		return false, err
    85  	}
    86  
    87  	for _, trgCfg := range descriptor.Triggers {
    88  		if trgCfg.Id == shim {
    89  
    90  			ref := trgCfg.Ref
    91  
    92  			if trgCfg.Ref != "" {
    93  				found := false
    94  				ref, found = GetAliasRef("flogo:trigger", trgCfg.Ref)
    95  				if !found {
    96  					return false, fmt.Errorf("unable to determine ref for trigger: %s", trgCfg.Id)
    97  				}
    98  			}
    99  
   100  			refImport, err := util.NewFlogoImportFromPath(ref)
   101  			if err != nil {
   102  				return false, err
   103  			}
   104  
   105  			impPath, err := project.GetPath(refImport)
   106  			if err != nil {
   107  				return false, err
   108  			}
   109  
   110  			var shimFilePath string
   111  
   112  			shimFilePath = filepath.Join(impPath, dirShim, fileShimGo)
   113  
   114  			if _, err := os.Stat(shimFilePath); err == nil {
   115  
   116  				err = util.CopyFile(shimFilePath, filepath.Join(project.SrcDir(), fileShimGo))
   117  				if err != nil {
   118  					return false, err
   119  				}
   120  
   121  				// Check if this shim based trigger has a gobuild file. If the trigger has a gobuild
   122  				// execute that file, otherwise check if there is a Makefile to execute
   123  				goBuildFilePath := filepath.Join(impPath, dirShim, fileBuildGo)
   124  
   125  				makefilePath := filepath.Join(shimFilePath, dirShim, fileMakefile)
   126  
   127  				if _, err := os.Stat(goBuildFilePath); err == nil {
   128  					fmt.Println("Using build.go to build shim......")
   129  
   130  					err = util.CopyFile(goBuildFilePath, filepath.Join(project.SrcDir(), fileBuildGo))
   131  					if err != nil {
   132  						return false, err
   133  					}
   134  
   135  					// Execute go run gobuild.go
   136  					err = util.ExecCmd(exec.Command("go", "run", fileBuildGo), project.SrcDir())
   137  					if err != nil {
   138  						return false, err
   139  					}
   140  
   141  					return true, nil
   142  				} else if _, err := os.Stat(makefilePath); err == nil {
   143  					//look for Makefile and execute it
   144  					fmt.Println("Using make file to build shim...")
   145  
   146  					err = util.CopyFile(makefilePath, filepath.Join(project.SrcDir(), fileMakefile))
   147  					if err != nil {
   148  						return false, err
   149  					}
   150  
   151  					if Verbose() {
   152  						fmt.Println("Make File:", makefilePath)
   153  					}
   154  
   155  					// Execute make
   156  					cmd := exec.Command("make", "-C", project.SrcDir())
   157  					cmd.Stdout = os.Stdout
   158  					cmd.Stderr = os.Stderr
   159  					cmd.Env = util.ReplaceEnvValue(os.Environ(), "GOPATH", project.Dir())
   160  
   161  					err = cmd.Run()
   162  					if err != nil {
   163  						return false, err
   164  					}
   165  
   166  					return true, nil
   167  				} else {
   168  					return false, nil
   169  				}
   170  			}
   171  
   172  			break
   173  		}
   174  	}
   175  
   176  	return false, fmt.Errorf("unable to to find shim trigger: %s", shim)
   177  }
   178  
   179  func shimCleanup(project common.AppProject) {
   180  
   181  	if Verbose() {
   182  		fmt.Println("Cleaning up shim support files...")
   183  	}
   184  
   185  	err := util.DeleteFile(filepath.Join(project.SrcDir(), fileShimSupportGo))
   186  	if err != nil {
   187  		fmt.Printf("Unable to delete: %s", fileShimSupportGo)
   188  	}
   189  	err = util.DeleteFile(filepath.Join(project.SrcDir(), fileShimGo))
   190  	if err != nil {
   191  		fmt.Printf("Unable to delete: %s", fileShimGo)
   192  	}
   193  	err = util.DeleteFile(filepath.Join(project.SrcDir(), fileBuildGo))
   194  	if err != nil {
   195  		fmt.Printf("Unable to delete: %s", fileBuildGo)
   196  	}
   197  }
   198  
   199  func createShimSupportGoFile(project common.AppProject) error {
   200  
   201  	shimSrcPath := filepath.Join(project.SrcDir(), fileShimSupportGo)
   202  
   203  	if Verbose() {
   204  		fmt.Println("Creating shim support files...")
   205  	}
   206  
   207  	flogoCoreImport, err := util.NewFlogoImportFromPath(flogoCoreRepo)
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	corePath, err := project.GetPath(flogoCoreImport)
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	bytes, err := ioutil.ReadFile(filepath.Join(corePath, fileSampleShimSupport))
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	err = ioutil.WriteFile(shimSrcPath, bytes, 0644)
   223  	if err != nil {
   224  		return err
   225  	}
   226  
   227  	return nil
   228  }
   229  
   230  func registerImports(project common.AppProject, appDesc *util.FlogoAppDescriptor) error {
   231  
   232  	for _, anImport := range appDesc.Imports {
   233  		err := registerImport(project, anImport)
   234  		if err != nil {
   235  			return err
   236  		}
   237  	}
   238  
   239  	return nil
   240  }
   241  
   242  func registerImport(project common.AppProject, anImport string) error {
   243  
   244  	matches := flogoImportPattern.FindStringSubmatch(anImport)
   245  
   246  	parts := strings.Split(matches[3], " ")
   247  
   248  	var alias string
   249  	var ref string
   250  	numParts := len(parts)
   251  	if numParts == 1 {
   252  		ref = parts[0]
   253  		alias = path.Base(ref)
   254  
   255  	} else if numParts == 2 {
   256  		alias = parts[0]
   257  		ref = parts[1]
   258  	} else {
   259  		return fmt.Errorf("invalid import %s", anImport)
   260  	}
   261  
   262  	if alias == "" || ref == "" {
   263  		return fmt.Errorf("invalid import %s", anImport)
   264  	}
   265  
   266  	ct, err := util.GetContribType(project.DepManager(), ref)
   267  	if err != nil {
   268  		return err
   269  	}
   270  
   271  	if ct != "" {
   272  		RegisterAlias(ct, alias, ref)
   273  	}
   274  
   275  	return nil
   276  }
   277  
   278  var aliases = make(map[string]map[string]string)
   279  
   280  func RegisterAlias(contribType string, alias, ref string) {
   281  
   282  	aliasToRefMap, exists := aliases[contribType]
   283  	if !exists {
   284  		aliasToRefMap = make(map[string]string)
   285  		aliases[contribType] = aliasToRefMap
   286  	}
   287  
   288  	aliasToRefMap[alias] = ref
   289  }
   290  
   291  func GetAliasRef(contribType string, alias string) (string, bool) {
   292  	if alias == "" {
   293  		return "", false
   294  	}
   295  
   296  	if alias[0] == '#' {
   297  		alias = alias[1:]
   298  	}
   299  	aliasToRefMap, exists := aliases[contribType]
   300  	if !exists {
   301  		return "", false
   302  	}
   303  
   304  	ref, exists := aliasToRefMap[alias]
   305  	if !exists {
   306  		return "", false
   307  	}
   308  
   309  	return ref, true
   310  }