From 78b442a247e54df6c82dd3a0e0be067869cbc5a9 Mon Sep 17 00:00:00 2001 From: iyi Date: Tue, 16 Jan 2024 10:58:21 +0800 Subject: [PATCH] =?UTF-8?q?init:=20=E5=88=9D=E5=A7=8B=E5=8C=96=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=9B=AE=E5=BD=95&=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 25 ++++++++++ cmd/main.go | 31 +++++++++++++ go.mod | 23 ++++++++++ go.sum | 57 +++++++++++++++++++++++ solana/build.go | 66 ++++++++++++++++++++++++++ solana/detect.go | 90 ++++++++++++++++++++++++++++++++++++ solana/solana.go | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 409 insertions(+) create mode 100644 .gitignore create mode 100644 cmd/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 solana/build.go create mode 100644 solana/detect.go create mode 100644 solana/solana.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f3f143 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +### Go template +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +# ide +.idea diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..c80e18d --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,31 @@ +// Copyright (c) The Amphitheatre Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "os" + + "github.com/buildpacks/libcnb" + "github.com/paketo-buildpacks/libpak/bard" + + "github.com/jjeejj/Buildpack-solana/solana" +) + +func main() { + libcnb.Main( + solana.Detect{}, + solana.Build{Logger: bard.NewLogger(os.Stdout)}, + ) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ee0848e --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module github.com/jjeejj/Buildpack-solana + +go 1.21.5 + +require ( + github.com/buildpacks/libcnb v1.30.1 + github.com/paketo-buildpacks/libpak v1.68.0 +) + +require ( + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/creack/pty v1.1.20 // indirect + github.com/h2non/filetype v1.1.3 // indirect + github.com/heroku/color v0.0.6 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/onsi/gomega v1.30.0 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect + golang.org/x/sys v0.14.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c28af7f --- /dev/null +++ b/go.sum @@ -0,0 +1,57 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/buildpacks/libcnb v1.30.1 h1:airpkZzh8Mlt+SQEMTj0B1r5agDHZvgfpwzpKVMEgZQ= +github.com/buildpacks/libcnb v1.30.1/go.mod h1:JU2fam/ECXw6I5iRnZbfKgjWsrZbzbgvZHR6WBSVHRc= +github.com/creack/pty v1.1.20 h1:VIPb/a2s17qNeQgDnkfZC35RScx+blkKF8GV68n80J4= +github.com/creack/pty v1.1.20/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= +github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= +github.com/heroku/color v0.0.6 h1:UTFFMrmMLFcL3OweqP1lAdp8i1y/9oHqkeHjQ/b/Ny0= +github.com/heroku/color v0.0.6/go.mod h1:ZBvOcx7cTF2QKOv4LbmoBtNl5uB17qWxGuzZrsi1wLU= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/paketo-buildpacks/libpak v1.68.0 h1:unfJscVujFT8WZgUONMh4vp64x+LvAz6kwpPdagYibM= +github.com/paketo-buildpacks/libpak v1.68.0/go.mod h1:Np94HbeUNYcMmaJEnaP5VgqGgaz86RhWA9nONRCN96U= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= +github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/solana/build.go b/solana/build.go new file mode 100644 index 0000000..c36cd94 --- /dev/null +++ b/solana/build.go @@ -0,0 +1,66 @@ +// Copyright (c) The Amphitheatre Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package solana + +import ( + "fmt" + + "github.com/buildpacks/libcnb" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/bard" +) + +type Build struct { + Logger bard.Logger +} + +func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) { + b.Logger.Title(context.Buildpack) + result := libcnb.NewBuildResult() + + cr, err := libpak.NewConfigurationResolver(context.Buildpack, &b.Logger) + if err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to create configuration resolver\n%w", err) + } + + dc, err := libpak.NewDependencyCache(context) + if err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to create dependency cache\n%w", err) + } + dc.Logger = b.Logger + + dr, err := libpak.NewDependencyResolver(context) + if err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to create dependency resolver\n%w", err) + } + + // install hardhat + nodeDependency, err := dr.Resolve("node", "") + if err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err) + } + + hardhatLayer := NewHardhat(nodeDependency, dc) + hardhatLayer.Logger = b.Logger + + enableProcess, _ := cr.Resolve("BP_ENABLE_HARDHAT_PROCESS") + result.Processes, err = hardhatLayer.BuildProcessTypes(enableProcess) + if err != nil { + return libcnb.BuildResult{}, fmt.Errorf("unable to build list of process types\n%w", err) + } + result.Layers = append(result.Layers, hardhatLayer) + + return result, nil +} diff --git a/solana/detect.go b/solana/detect.go new file mode 100644 index 0000000..9d52f87 --- /dev/null +++ b/solana/detect.go @@ -0,0 +1,90 @@ +// Copyright (c) The Amphitheatre Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package solana + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/buildpacks/libcnb" +) + +const ( + PlanEntryHardhat = "solana" +) + +type Detect struct { +} + +func (d Detect) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error) { + found, err := d.hardhatProject(context.Application.Path) + if err != nil { + return libcnb.DetectResult{Pass: false}, fmt.Errorf("unable to detect hardhat requirements\n%w", err) + } + + if !found { + return libcnb.DetectResult{Pass: false}, nil + } + + return libcnb.DetectResult{ + Pass: true, + Plans: []libcnb.BuildPlan{ + { + Provides: []libcnb.BuildPlanProvide{ + {Name: PlanEntryHardhat}, + }, + Requires: []libcnb.BuildPlanRequire{ + {Name: PlanEntryHardhat}, + }, + }, + }, + }, nil +} + +func (d Detect) hardhatProject(appDir string) (bool, error) { + _, err := os.Stat(filepath.Join(appDir, "package.json")) + if os.IsNotExist(err) || err != nil { + return false, fmt.Errorf("unable to determine if package.json exists\n%w", err) + } + + buildDirectory := filepath.Join(appDir, ".") + extension := ".sol" + if err := existsFilesWithExtension(buildDirectory, extension); err != nil { + return false, fmt.Errorf("unable to determine if '%s' exists\n%w", extension, err) + } + return true, nil +} + +func existsFilesWithExtension(directory, extension string) error { + var found bool + err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Check if the file has the specified extension. + if !info.IsDir() && filepath.Ext(path) == extension { + found = true + return nil + } + return nil + }) + + if !found { + return fmt.Errorf("no files with extension '%s' found", extension) + } + return err +} diff --git a/solana/solana.go b/solana/solana.go new file mode 100644 index 0000000..d4d7d7a --- /dev/null +++ b/solana/solana.go @@ -0,0 +1,117 @@ +// Copyright (c) The Amphitheatre Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package solana + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/buildpacks/libcnb" + "github.com/paketo-buildpacks/libpak" + "github.com/paketo-buildpacks/libpak/bard" + "github.com/paketo-buildpacks/libpak/crush" + "github.com/paketo-buildpacks/libpak/effect" + "github.com/paketo-buildpacks/libpak/sherpa" +) + +type Hardhat struct { + LayerContributor libpak.DependencyLayerContributor + Logger bard.Logger + Executor effect.Executor +} + +func NewHardhat(dependency libpak.BuildpackDependency, cache libpak.DependencyCache) Hardhat { + contributor := libpak.NewDependencyLayerContributor(dependency, cache, libcnb.LayerTypes{ + Build: true, + Cache: true, + Launch: true, + }) + return Hardhat{ + LayerContributor: contributor, + Executor: effect.NewExecutor(), + } +} + +func (r Hardhat) Contribute(layer libcnb.Layer) (libcnb.Layer, error) { + r.LayerContributor.Logger = r.Logger + return r.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { + bin := filepath.Join(layer.Path, "bin") + + r.Logger.Bodyf("Expanding %s to %s", artifact.Name(), bin) + if err := crush.Extract(artifact, layer.Path, 1); err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to expand %s\n%w", artifact.Name(), err) + } + + r.Logger.Bodyf("Setting %s in PATH", bin) + if err := os.Setenv("PATH", sherpa.AppendToEnvVar("PATH", ":", bin)); err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to set $PATH\n%w", err) + } + + // install hardhat + r.Logger.Bodyf("Installing hardhat") + if _, err := r.Execute("npm", []string{"install", "hardhat", "--save-dev"}); err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to install hardhat\n%w", err) + } + + // install node_modules + r.Logger.Bodyf("Installing node_modules") + if _, err := r.Execute("npm", []string{"install"}); err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to install node_modules\n%w", err) + } + + // get hardhat version + buf, err := r.Execute("npx", []string{"hardhat", "--version"}) + if err != nil { + return libcnb.Layer{}, fmt.Errorf("unable to get hardhat version\n%w", err) + } + version := strings.TrimSpace(buf.String()) + r.Logger.Bodyf("Checking hardhat version: %s", version) + return layer, nil + }) +} + +func (r Hardhat) Execute(command string, args []string) (*bytes.Buffer, error) { + buf := &bytes.Buffer{} + if err := r.Executor.Execute(effect.Execution{ + Command: command, + Args: args, + Stdout: buf, + Stderr: buf, + }); err != nil { + return buf, fmt.Errorf("%s: %w", buf.String(), err) + } + return buf, nil +} + +func (r Hardhat) BuildProcessTypes(enableProcess string) ([]libcnb.Process, error) { + processes := []libcnb.Process{} + + if enableProcess == "true" { + processes = append(processes, libcnb.Process{ + Type: "web", + Command: "npm", + Arguments: []string{"start"}, + Default: true, + }) + } + return processes, nil +} + +func (r Hardhat) Name() string { + return r.LayerContributor.LayerName() +}