Skip to content

Commit

Permalink
Add composer.json resolver (#151)
Browse files Browse the repository at this point in the history
* Add composer files resolver
  • Loading branch information
4ernovm authored Nov 23, 2023
1 parent 99a752a commit 8b6def9
Show file tree
Hide file tree
Showing 17 changed files with 413 additions and 6 deletions.
15 changes: 15 additions & 0 deletions build/docker/alpine.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,20 @@ RUN apk --no-cache --update add \

RUN dotnet --version

RUN apk add --no-cache \
git \
php82 \
php82-curl \
php82-mbstring \
php82-openssl \
php82-phar \
&& ln -s /usr/bin/php82 /usr/bin/php

RUN apk add --no-cache --virtual build-dependencies curl && \
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer \
&& apk del build-dependencies

RUN php -v && composer --version

# Put copy at the end to speedup Docker build by caching previous RUNs and run those concurrently
COPY --from=dev /cli/debricked /usr/bin/debricked
17 changes: 17 additions & 0 deletions build/docker/debian.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,22 @@ RUN apt -y update && apt -y upgrade && apt -y install openjdk-11-jre \

RUN dotnet --version

RUN apt update -y && \
apt install lsb-release apt-transport-https ca-certificates software-properties-common -y && \
curl -o /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg && \
sh -c 'echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list' && \
apt -y clean && rm -rf /var/lib/apt/lists/*

RUN apt -y update && apt -y install \
php8.2 \
php8.2-curl \
php8.2-mbstring \
php8.2-phar && \
apt -y clean && rm -rf /var/lib/apt/lists/*

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer

RUN php -v && composer --version

# Put copy at the end to speedup Docker build by caching previous RUNs and run those concurrently
COPY --from=dev /cli/debricked /usr/bin/debricked
47 changes: 47 additions & 0 deletions internal/resolution/pm/composer/cmd_factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package composer

import (
"os"
"os/exec"
"path/filepath"
)

type ICmdFactory interface {
MakeInstallCmd(command string, file string) (*exec.Cmd, error)
}

type IExecPath interface {
LookPath(file string) (string, error)
}

type ExecPath struct {
}

func (ExecPath) LookPath(file string) (string, error) {
return exec.LookPath(file)
}

type CmdFactory struct {
execPath IExecPath
}

func (cmdf CmdFactory) MakeInstallCmd(command string, file string) (*exec.Cmd, error) {
path, err := cmdf.execPath.LookPath(command)

fileDir := filepath.Dir(file)

return &exec.Cmd{
Path: path,
Args: []string{command, "update",
"--no-interaction", // We can't answer any prompts...
"--no-scripts", // Avoid risky scripts
"--ignore-platform-reqs", // We won't run the code, so we don't care about the platform
"--no-autoloader", // We won't execute any code, no need for autoloader
"--no-install", // No need to install packages
"--no-plugins", // We won't run the code, so no plugins needed
"--no-audit", // We don't want to run an audit
},
Dir: fileDir,
Env: os.Environ(),
}, err
}
19 changes: 19 additions & 0 deletions internal/resolution/pm/composer/cmd_factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package composer

import (
"testing"

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

func TestMakeInstallCmd(t *testing.T) {
composerCommand := "composer"
cmd, err := CmdFactory{
execPath: ExecPath{},
}.MakeInstallCmd(composerCommand, "file")
assert.NoError(t, err)
assert.NotNil(t, cmd)
args := cmd.Args
assert.Contains(t, args, "composer")
assert.Contains(t, args, "update")
}
62 changes: 62 additions & 0 deletions internal/resolution/pm/composer/job.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package composer

import (
"github.com/debricked/cli/internal/resolution/job"
)

const (
composer = "composer"
)

type Job struct {
job.BaseJob
install bool
composerCommand string
cmdFactory ICmdFactory
}

func NewJob(
file string,
install bool,
cmdFactory ICmdFactory,
) *Job {
return &Job{
BaseJob: job.NewBaseJob(file),
install: install,
cmdFactory: cmdFactory,
}
}

func (j *Job) Install() bool {
return j.install
}

func (j *Job) Run() {
if j.install {

j.SendStatus("installing dependencies")
_, err := j.runInstallCmd()
if err != nil {
j.Errors().Critical(err)

return
}
}

}

func (j *Job) runInstallCmd() ([]byte, error) {

j.composerCommand = composer
installCmd, err := j.cmdFactory.MakeInstallCmd(j.composerCommand, j.GetFile())
if err != nil {
return nil, err
}

installCmdOutput, err := installCmd.Output()
if err != nil {
return nil, j.GetExitError(err)
}

return installCmdOutput, nil
}
64 changes: 64 additions & 0 deletions internal/resolution/pm/composer/job_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package composer

import (
"errors"
"testing"

jobTestdata "github.com/debricked/cli/internal/resolution/job/testdata"
"github.com/debricked/cli/internal/resolution/pm/composer/testdata"
"github.com/stretchr/testify/assert"
)

const (
badName = "bad-name"
)

func TestNewJob(t *testing.T) {
j := NewJob("file", false, CmdFactory{
execPath: ExecPath{},
})
assert.Equal(t, "file", j.GetFile())
assert.False(t, j.Errors().HasError())
}

func TestRunInstall(t *testing.T) {
cmdFactoryMock := testdata.NewEchoCmdFactory()
j := NewJob("file", false, cmdFactoryMock)

_, err := j.runInstallCmd()
assert.NoError(t, err)

assert.False(t, j.Errors().HasError())
}

func TestInstall(t *testing.T) {
j := Job{install: true}
assert.Equal(t, true, j.Install())

j = Job{install: false}
assert.Equal(t, false, j.Install())
}

func TestRunInstallCmdErr(t *testing.T) {
cmdErr := errors.New("cmd-error")
cmdFactoryMock := testdata.NewEchoCmdFactory()
cmdFactoryMock.MakeInstallErr = cmdErr
j := NewJob("file", true, cmdFactoryMock)

go jobTestdata.WaitStatus(j)
j.Run()

assert.Len(t, j.Errors().GetAll(), 1)
assert.Contains(t, j.Errors().GetAll(), cmdErr)
}

func TestRunInstallCmdOutputErr(t *testing.T) {
cmdMock := testdata.NewEchoCmdFactory()
cmdMock.InstallCmdName = badName
j := NewJob("file", true, cmdMock)

go jobTestdata.WaitStatus(j)
j.Run()

jobTestdata.AssertPathErr(t, j.Errors())
}
23 changes: 23 additions & 0 deletions internal/resolution/pm/composer/pm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package composer

const Name = "composer"

type Pm struct {
name string
}

func NewPm() Pm {
return Pm{
name: Name,
}
}

func (pm Pm) Name() string {
return pm.name
}

func (Pm) Manifests() []string {
return []string{
`composer\.json$`,
}
}
40 changes: 40 additions & 0 deletions internal/resolution/pm/composer/pm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package composer

import (
"regexp"
"testing"

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

func TestNewPm(t *testing.T) {
pm := NewPm()
assert.Equal(t, Name, pm.name)
}

func TestName(t *testing.T) {
pm := NewPm()
assert.Equal(t, Name, pm.Name())
}

func TestManifests(t *testing.T) {
pm := Pm{}
manifests := pm.Manifests()
assert.Len(t, manifests, 1)
manifest := manifests[0]
assert.Equal(t, `composer\.json$`, manifest)
_, err := regexp.Compile(manifest)
assert.NoError(t, err)

cases := map[string]bool{
"composer.json": true,
"composer.lock": false,
"package-lock.json": false,
}
for file, isMatch := range cases {
t.Run(file, func(t *testing.T) {
matched, _ := regexp.MatchString(manifest, file)
assert.Equal(t, isMatch, matched)
})
}
}
29 changes: 29 additions & 0 deletions internal/resolution/pm/composer/strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package composer

import (
"github.com/debricked/cli/internal/resolution/job"
)

type Strategy struct {
files []string
}

func (s Strategy) Invoke() ([]job.IJob, error) {
var jobs []job.IJob
for _, file := range s.files {
jobs = append(jobs, NewJob(
file,
true,
CmdFactory{
execPath: ExecPath{},
},
),
)
}

return jobs, nil
}

func NewStrategy(files []string) Strategy {
return Strategy{files}
}
43 changes: 43 additions & 0 deletions internal/resolution/pm/composer/strategy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package composer

import (
"testing"

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

func TestNewStrategy(t *testing.T) {
s := NewStrategy(nil)
assert.NotNil(t, s)
assert.Len(t, s.files, 0)

s = NewStrategy([]string{})
assert.NotNil(t, s)
assert.Len(t, s.files, 0)

s = NewStrategy([]string{"file"})
assert.NotNil(t, s)
assert.Len(t, s.files, 1)

s = NewStrategy([]string{"file-1", "file-2"})
assert.NotNil(t, s)
assert.Len(t, s.files, 2)
}

func TestInvokeNoFiles(t *testing.T) {
s := NewStrategy([]string{})
jobs, _ := s.Invoke()
assert.Empty(t, jobs)
}

func TestInvokeOneFile(t *testing.T) {
s := NewStrategy([]string{"file"})
jobs, _ := s.Invoke()
assert.Len(t, jobs, 1)
}

func TestInvokeManyFiles(t *testing.T) {
s := NewStrategy([]string{"file-1", "file-2"})
jobs, _ := s.Invoke()
assert.Len(t, jobs, 2)
}
20 changes: 20 additions & 0 deletions internal/resolution/pm/composer/testdata/cmd_factory_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package testdata

import (
"os/exec"
)

type CmdFactoryMock struct {
InstallCmdName string
MakeInstallErr error
}

func NewEchoCmdFactory() CmdFactoryMock {
return CmdFactoryMock{
InstallCmdName: "echo",
}
}

func (f CmdFactoryMock) MakeInstallCmd(command string, file string) (*exec.Cmd, error) {
return exec.Command(f.InstallCmdName), f.MakeInstallErr
}
Loading

0 comments on commit 8b6def9

Please sign in to comment.