Skip to content

Commit

Permalink
Merge pull request #151 from zeabur/pan93412/ZEA-1923
Browse files Browse the repository at this point in the history
ZEA-1923: Support Laravel Octane (Swoole) for zbpack
  • Loading branch information
yuaanlin authored Oct 3, 2023
2 parents 2f0949f + c89fafd commit 3a7bdbe
Show file tree
Hide file tree
Showing 7 changed files with 2,498 additions and 25 deletions.
2,309 changes: 2,309 additions & 0 deletions internal/php/__snapshots__/template_test.snap

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions internal/php/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package php

// ConfigLaravelOctaneServer defines what server we should use to run Laravel Octane.
//
// When this config is set, we will use the corresponding server to run the project
// instead of the original Nginx + PHP-FPM stack.
const ConfigLaravelOctaneServer = "laravel.octane.server"
15 changes: 13 additions & 2 deletions internal/php/identify.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,29 @@ func (i *identify) Match(fs afero.Fs) bool {
}

func (i *identify) PlanMeta(options plan.NewPlannerOptions) types.PlanMeta {
config := options.Config

server := config.GetString(ConfigLaravelOctaneServer)

framework := DetermineProjectFramework(options.Source)
phpVersion := GetPHPVersion(options.Source)
deps := DetermineAptDependencies(options.Source)
deps := DetermineAptDependencies(options.Source, server)
app, property := DetermineApplication(options.Source)

return types.PlanMeta{
// Some meta will be added to the plan dynamically later.
meta := types.PlanMeta{
"framework": string(framework),
"phpVersion": phpVersion,
"deps": strings.Join(deps, " "),
"app": string(app),
"property": PropertyToString(property),
}

if framework == types.PHPFrameworkLaravel && server != "" {
meta["octaneServer"] = server
}

return meta
}

var _ plan.Identifier = (*identify)(nil)
31 changes: 25 additions & 6 deletions internal/php/php.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,25 @@ import (
func GenerateDockerfile(meta types.PlanMeta) (string, error) {
phpVersion := meta["phpVersion"]
projectProperty := PropertyFromString(meta["property"])
serverMode := "fpm"

getPhpImage := "FROM docker.io/library/php:" + phpVersion + "-fpm\n"

nginxConf, err := RetrieveNginxConf(meta["app"])
if err != nil {
return "", fmt.Errorf("retrieve nginx conf: %w", err)
// Custom server for Laravel Octane
switch meta["octaneServer"] {
case "": // ignore
case "roadrunner": // unimplemented
case "swoole":
getPhpImage = "FROM docker.io/phpswoole/swoole:php" + phpVersion + "\n" +
"RUN docker-php-ext-install pcntl\n"
serverMode = "swoole"
}

installCMD := fmt.Sprintf(`
RUN apt-get update && apt-get install -y %s && rm -rf /var/lib/apt/lists/*
`, meta["deps"])
if projectProperty&types.PHPPropertyComposer != 0 {
installCMD += `\
installCMD += `
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && sync
Expand All @@ -44,11 +50,18 @@ WORKDIR /var/www
`
}

// generate Nginx config to let it pass the request to php-fpm
copyCommand += `
if serverMode == "fpm" {
// generate Nginx config to let it pass the request to php-fpm
nginxConf, err := RetrieveNginxConf(meta["app"])
if err != nil {
return "", fmt.Errorf("retrieve nginx conf: %w", err)
}

copyCommand += `
RUN rm /etc/nginx/sites-enabled/default
RUN echo "` + nginxConf + `" >> /etc/nginx/sites-enabled/default
`
}

// install dependencies with composer
composerInstallCmd := "\n"
Expand All @@ -69,6 +82,12 @@ RUN composer install --optimize-autoloader --no-dev
startCmd := `
CMD nginx; php-fpm
`
// Custom server for Laravel Octane
if serverMode == "swoole" {
startCmd = `
CMD ["php", "artisan", "octane:start", "--server=swoole", "--host=0.0.0.0", "--port=8080"]
`
}

dockerFile := getPhpImage +
installCMD +
Expand Down
13 changes: 11 additions & 2 deletions internal/php/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,22 @@ var depMap = map[string][]string{
"ext-gmp": {"libgmp-dev"},
}

var baseDep = []string{"nginx", "libicu-dev", "jq", "pkg-config", "unzip", "git"}
var baseDep = []string{"libicu-dev", "jq", "pkg-config", "unzip", "git"}

// DetermineAptDependencies determines the required apt dependencies of the project.
func DetermineAptDependencies(source afero.Fs) []string {
//
// We install Nginx server unless server is "swoole".
func DetermineAptDependencies(source afero.Fs, server string) []string {
// deep copy the base dependencies
dependencies := append([]string{}, baseDep...)

// If Octane Server is not "swoole", we should install Nginx.
//
// TODO: support RoadRunner
if server != "swoole" {
dependencies = append(dependencies, "nginx")
}

composerJSON, err := parseComposerJSON(source)
if err != nil {
return dependencies
Expand Down
45 changes: 30 additions & 15 deletions internal/php/plan_apt_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package php

// due to some internal logics, we need to do blackbox test
import (
"testing"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
)

var baseDepsWithNginx = append(baseDep, "nginx")

func TestDetermineAptDependencies_None(t *testing.T) {
fs := afero.NewMemMapFs()

deps := DetermineAptDependencies(fs)
assert.Equal(t, deps, baseDep)
deps := DetermineAptDependencies(fs, "")
assert.Equal(t, baseDepsWithNginx, deps)
}

func TestDetermineAptDependencies_NoRequire(t *testing.T) {
Expand All @@ -21,8 +22,8 @@ func TestDetermineAptDependencies_NoRequire(t *testing.T) {
"name": "test"
}`), 0o644)

deps := DetermineAptDependencies(fs)
assert.Equal(t, deps, baseDep)
deps := DetermineAptDependencies(fs, "")
assert.Equal(t, baseDepsWithNginx, deps)
}

func TestDetermineAptDependencies_EmptyRequire(t *testing.T) {
Expand All @@ -32,8 +33,8 @@ func TestDetermineAptDependencies_EmptyRequire(t *testing.T) {
"require": {}
}`), 0o644)

deps := DetermineAptDependencies(fs)
assert.Equal(t, deps, baseDep)
deps := DetermineAptDependencies(fs, "")
assert.Equal(t, baseDepsWithNginx, deps)
}

func TestDetermineAptDependencies_RequireOpenssl(t *testing.T) {
Expand All @@ -45,8 +46,8 @@ func TestDetermineAptDependencies_RequireOpenssl(t *testing.T) {
}
}`), 0o644)

deps := DetermineAptDependencies(fs)
assert.Equal(t, deps, append(baseDep, depMap["ext-openssl"]...))
deps := DetermineAptDependencies(fs, "")
assert.Equal(t, append(baseDepsWithNginx, depMap["ext-openssl"]...), deps)
}

func TestDetermineAptDependencies_RequireZip(t *testing.T) {
Expand All @@ -58,8 +59,8 @@ func TestDetermineAptDependencies_RequireZip(t *testing.T) {
}
}`), 0o644)

deps := DetermineAptDependencies(fs)
assert.Equal(t, deps, append(baseDep, depMap["ext-zip"]...))
deps := DetermineAptDependencies(fs, "")
assert.Equal(t, append(baseDepsWithNginx, depMap["ext-zip"]...), deps)
}

func TestDetermineAptDependencies_RequireCurl(t *testing.T) {
Expand All @@ -71,8 +72,8 @@ func TestDetermineAptDependencies_RequireCurl(t *testing.T) {
}
}`), 0o644)

deps := DetermineAptDependencies(fs)
assert.Equal(t, deps, append(baseDep, depMap["ext-curl"]...))
deps := DetermineAptDependencies(fs, "")
assert.Equal(t, append(baseDepsWithNginx, depMap["ext-curl"]...), deps)
}

func TestDetermineAptDependencies_RequireGd(t *testing.T) {
Expand All @@ -84,6 +85,20 @@ func TestDetermineAptDependencies_RequireGd(t *testing.T) {
}
}`), 0o644)

deps := DetermineAptDependencies(fs)
assert.Equal(t, deps, append(baseDep, depMap["ext-gd"]...))
deps := DetermineAptDependencies(fs, "")
assert.Equal(t, append(baseDepsWithNginx, depMap["ext-gd"]...), deps)
}

func TestDetermineAptDependencies_Swoole(t *testing.T) {
fs := afero.NewMemMapFs()

deps := DetermineAptDependencies(fs, "swoole")
assert.Equal(t, baseDep, deps)
}

func TestDetermineAptDependencies_Unknown(t *testing.T) {
fs := afero.NewMemMapFs()

deps := DetermineAptDependencies(fs, "unknown")
assert.Equal(t, baseDepsWithNginx, deps)
}
103 changes: 103 additions & 0 deletions internal/php/template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package php_test

import (
"testing"

"github.com/gkampitakis/go-snaps/snaps"
"github.com/stretchr/testify/assert"
"github.com/zeabur/zbpack/internal/php"
"github.com/zeabur/zbpack/pkg/types"
)

func TestTemplate(t *testing.T) {
t.Parallel()

phpVersion := []string{
"8.1",
"8.2",
"7",
}
framework := []string{
string(types.PHPFrameworkNone),
string(types.PHPFrameworkLaravel),
string(types.PHPFrameworkThinkphp),
string(types.PHPFrameworkCodeigniter),
}
deps := []string{
"nginx",
"nginx,owo",
}
property := []string{
php.PropertyToString(types.PHPPropertyNone),
php.PropertyToString(types.PHPPropertyComposer),
}
octaneServer := []string{
"",
"roadrunner",
"swoole",
}

for _, v := range phpVersion {
v := v
for _, d := range deps {
d := d
for _, f := range framework {
f := f
for _, p := range property {
p := p
t.Run(v+"-"+f+"-"+d+"-"+p, func(t *testing.T) {
t.Parallel()

dockerfile, err := php.GenerateDockerfile(types.PlanMeta{
"phpVersion": v,
"framework": f,
"deps": d,
"app": string(types.PHPApplicationDefault),
"property": p,
})

assert.NoError(t, err)
snaps.MatchSnapshot(t, dockerfile)
})

if f == string(types.PHPFrameworkLaravel) {
for _, o := range octaneServer {
o := o

t.Run(v+"-"+f+"-"+d+"-"+p+"+os-"+o, func(t *testing.T) {
t.Parallel()

dockerfile, err := php.GenerateDockerfile(types.PlanMeta{
"phpVersion": v,
"framework": f,
"deps": d,
"app": string(types.PHPApplicationDefault),
"property": p,
"octaneServer": o,
})

assert.NoError(t, err)
snaps.MatchSnapshot(t, dockerfile)
})
}
}
}
}
}
}
}

func TestTemplate_AcgFaka(t *testing.T) {
t.Parallel()

dockerfile, err := php.GenerateDockerfile(types.PlanMeta{
"phpVersion": "8.2",
"framework": string(types.PHPFrameworkLaravel),
"deps": "nginx",
"app": string(types.PHPApplicationAcgFaka),
"property": php.PropertyToString(types.PHPPropertyComposer),
})

assert.NoError(t, err)
snaps.MatchSnapshot(t, dockerfile)
}

0 comments on commit 3a7bdbe

Please sign in to comment.