Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
378 changes: 378 additions & 0 deletions .github/workflows/update-dependencies-from-metadata.yml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ SPDX-FileCopyrightText = "© 2025 Idiap Research Institute <contact@idiap.ch>"
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = "scripts/.util/tools.json"
path = "scripts/**"
precedence = "override"
SPDX-FileCopyrightText = "Copyright (c) 2013-Present CloudFoundry.org Foundation, Inc. All Rights Reserved."
SPDX-License-Identifier = "Apache-2.0"
Expand Down
16 changes: 16 additions & 0 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
pipinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/pip"
pipenvinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/pipenv"
poetryinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/poetry"
uvinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/uv"

pythonpackagers "github.com/paketo-buildpacks/python-packagers/pkg/packagers/common"
)
Expand Down Expand Up @@ -107,6 +108,21 @@ func Build(
} else {
return packit.BuildResult{}, packit.Fail.WithMessage("missing plan for: %s", entry.Name)
}
case uvinstall.UvEnvPlanEntry:
if parameters, ok := buildParameters[uvinstall.UvEnvPlanEntry]; ok {
uvResult, err := uvinstall.Build(
parameters.(uvinstall.UvBuildParameters),
commonBuildParameters,
)(context)

if err != nil {
return packit.BuildResult{}, err
}

layers = append(layers, uvResult.Layers...)
} else {
return packit.BuildResult{}, packit.Fail.WithMessage("missing plan for: %s", entry.Name)
}
default:
return packit.BuildResult{}, packit.Fail.WithMessage("unknown plan: %s", entry.Name)
}
Expand Down
21 changes: 21 additions & 0 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
pipenvfakes "github.com/paketo-buildpacks/python-packagers/pkg/packagers/pipenv/fakes"
poetryinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/poetry"
poetryfakes "github.com/paketo-buildpacks/python-packagers/pkg/packagers/poetry/fakes"
uvinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/uv"
uvfakes "github.com/paketo-buildpacks/python-packagers/pkg/packagers/uv/fakes"

"github.com/sclevine/spec"

Expand Down Expand Up @@ -65,6 +67,9 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
poetryInstallProcess *poetryfakes.InstallProcess
poetryPythonPathProcess *poetryfakes.PythonPathLookupProcess

// uv
uvRunner *uvfakes.Runner

buildParameters pkgcommon.CommonBuildParameters

plans []packit.BuildpackPlan
Expand Down Expand Up @@ -105,6 +110,9 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
poetryPythonPathProcess = &poetryfakes.PythonPathLookupProcess{}
poetryPythonPathProcess.ExecuteCall.Returns.String = "some-python-path"

// uv
uvRunner = &uvfakes.Runner{}

buildParameters = pkgcommon.CommonBuildParameters{
SbomGenerator: pkgcommon.Generator{},
Clock: chronos.DefaultClock,
Expand All @@ -129,6 +137,9 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
InstallProcess: poetryInstallProcess,
PythonPathLookupProcess: poetryPythonPathProcess,
},
uvinstall.UvEnvPlanEntry: uvinstall.UvBuildParameters{
Runner: uvRunner,
},
}

build = pythonpackagers.Build(logger, buildParameters, packagerParameters)
Expand Down Expand Up @@ -162,6 +173,9 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
{
Name: poetryinstall.PoetryVenv,
},
{
Name: uvinstall.UvEnvPlanEntry,
},
},
},
packit.BuildpackPlan{
Expand Down Expand Up @@ -194,6 +208,13 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
},
},
},
packit.BuildpackPlan{
Entries: []packit.BuildpackPlanEntry{
{
Name: uvinstall.UvEnvPlanEntry,
},
},
},
}
})

Expand Down
60 changes: 33 additions & 27 deletions detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
pipinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/pip"
pipenvinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/pipenv"
poetryinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/poetry"
uvinstall "github.com/paketo-buildpacks/python-packagers/pkg/packagers/uv"
)

// Detect will return a packit.DetectFunc that will be invoked during the
Expand All @@ -22,64 +23,69 @@ import (
// it will pass detection.
func Detect(logger scribe.Emitter) packit.DetectFunc {
return func(context packit.DetectContext) (packit.DetectResult, error) {
plans := []packit.BuildPlan{}

logger.Title("Checking for pip")
pipResult, err := pipinstall.Detect()(context)

if err == nil {
plans = append(plans, pipResult.Plan)
// plans = append(plans, pipResult.Plan)
return packit.DetectResult{
Plan: pipResult.Plan,
}, nil
} else {
logger.Detail("%s", err)
}

logger.Title("Checking for conda")
condaResult, err := conda.Detect()(context)

if err == nil {
plans = append(plans, condaResult.Plan)
// plans = append(plans, condaResult.Plan)
return packit.DetectResult{
Plan: condaResult.Plan,
}, nil
} else {
logger.Detail("%s", err)
}

logger.Title("Checking for pipenv")
pipenvResult, err := pipenvinstall.Detect(
pipenvinstall.NewPipfileParser(),
pipenvinstall.NewPipfileLockParser(),
)(context)

if err == nil {
plans = append(plans, pipenvResult.Plan)
// plans = append(plans, pipenvResult.Plan)
return packit.DetectResult{
Plan: pipenvResult.Plan,
}, nil
} else {
logger.Detail("%s", err)
}

poetryResult, err := poetryinstall.Detect()(context)
logger.Title("Checking for uv")
uvResult, err := uvinstall.Detect()(context)

if err == nil {
plans = append(plans, poetryResult.Plan)
// plans = append(plans, uvResult.Plan)
return packit.DetectResult{
Plan: uvResult.Plan,
}, nil
} else {
logger.Detail("%s", err)
}

if len(plans) == 0 {
return packit.DetectResult{}, packit.Fail.WithMessage("No python packager manager related files found")
}

return packit.DetectResult{
Plan: or(plans...),
}, nil
}
}

func or(plans ...packit.BuildPlan) packit.BuildPlan {
if len(plans) < 1 {
return packit.BuildPlan{}
}
combinedPlan := plans[0]
logger.Title("Checking for poetry")
poetryResult, err := poetryinstall.Detect()(context)

for i := range plans {
if i == 0 {
continue
if err == nil {
// plans = append(plans, poetryResult.Plan)
return packit.DetectResult{
Plan: poetryResult.Plan,
}, nil
} else {
logger.Detail("%s", err)
}
combinedPlan.Or = append(combinedPlan.Or, plans[i])

return packit.DetectResult{}, packit.Fail.WithMessage("No python packager manager related files found")
}
return combinedPlan
}
60 changes: 60 additions & 0 deletions detect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
pip "github.com/paketo-buildpacks/python-packagers/pkg/packagers/pip"
pipenv "github.com/paketo-buildpacks/python-packagers/pkg/packagers/pipenv"
poetry "github.com/paketo-buildpacks/python-packagers/pkg/packagers/poetry"
uv "github.com/paketo-buildpacks/python-packagers/pkg/packagers/uv"

"github.com/sclevine/spec"

Expand Down Expand Up @@ -232,6 +233,65 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
})
})

context("When only a uv.lock file is present", func() {
it.Before(func() {
Expect(os.RemoveAll(filepath.Join(workingDir, "x.py"))).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "uv.lock"), []byte{}, os.ModePerm)).To(Succeed())
})

it("passes detection", func() {
result, err := detect(packit.DetectContext{
WorkingDir: workingDir,
})
Expect(err).NotTo(HaveOccurred())
Expect(result.Plan).To(Equal(packit.BuildPlan{
Provides: []packit.BuildPlanProvision{
{
Name: uv.UvEnvPlanEntry,
},
},
Requires: []packit.BuildPlanRequirement{
{
Name: uv.UvPlanEntry,
Metadata: map[string]interface{}{
"build": true,
},
},
},
}))
})
})

context("When a uv.lock and pyproject.toml file is present", func() {
it.Before(func() {
Expect(os.RemoveAll(filepath.Join(workingDir, "x.py"))).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "pyproject.toml"), []byte{}, os.ModePerm)).To(Succeed())
Expect(os.WriteFile(filepath.Join(workingDir, "uv.lock"), []byte{}, os.ModePerm)).To(Succeed())
})

it("passes detection", func() {
result, err := detect(packit.DetectContext{
WorkingDir: workingDir,
})
Expect(err).NotTo(HaveOccurred())
Expect(result.Plan).To(Equal(packit.BuildPlan{
Provides: []packit.BuildPlanProvision{
{
Name: uv.UvEnvPlanEntry,
},
},
Requires: []packit.BuildPlanRequirement{
{
Name: uv.UvPlanEntry,
Metadata: map[string]interface{}{
"build": true,
},
},
},
}))
})
})

context("When no python related files are present", func() {
it.Before(func() {
Expect(os.RemoveAll(filepath.Join(workingDir, "x.py"))).To(Succeed())
Expand Down
5 changes: 1 addition & 4 deletions integration.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
"index.docker.io/paketobuildpacks/builder-jammy-buildpackless-base:latest",
"index.docker.io/paketobuildpacks/ubuntu-noble-builder-buildpackless:latest"
],
"miniconda": "index.docker.io/paketobuildpacks/miniconda",
"cpython": "index.docker.io/paketobuildpacks/cpython",
"pip": "index.docker.io/paketobuildpacks/pip",
"pipenv": "index.docker.io/paketobuildpacks/pipenv",
"poetry": "index.docker.io/paketobuildpacks/poetry",
"python-installers": "github.com/idiap/python-installers",
"build-plan": "index.docker.io/paketocommunity/build-plan"
}
22 changes: 4 additions & 18 deletions integration/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,14 @@ type BuildpackInfo struct {
type TestSettings struct {
Buildpacks struct {
// Dependency buildpacks
Miniconda struct {
Online string
Offline string
}
CPython struct {
Online string
Offline string
}
Pip struct {
Online string
Offline string
}
Pipenv struct {
PythonInstallers struct {
Online string
Offline string
}
Poetry struct {
Online string
}
BuildPlan struct {
Online string
}
Expand All @@ -57,11 +46,8 @@ type TestSettings struct {
}

Config struct {
Miniconda string `json:"miniconda"`
CPython string `json:"cpython"`
Pip string `json:"pip"`
Pipenv string `json:"pipenv"`
Poetry string `json:"poetry"`
BuildPlan string `json:"build-plan"`
CPython string `json:"cpython"`
PythonInstallers string `json:"python-installers"`
BuildPlan string `json:"build-plan"`
}
}
4 changes: 2 additions & 2 deletions integration/packagers/conda_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func condaTestDefault(t *testing.T, context spec.G, it spec.S) {
image, logs, err = pack.WithNoColor().Build.
WithPullPolicy("never").
WithBuildpacks(
settings.Buildpacks.Miniconda.Online,
settings.Buildpacks.PythonInstallers.Online,
settings.Buildpacks.PythonPackagers.Online,
settings.Buildpacks.BuildPlan.Online,
).
Expand Down Expand Up @@ -106,7 +106,7 @@ func condaTestDefault(t *testing.T, context spec.G, it spec.S) {
image, logs, err = pack.WithNoColor().Build.
WithPullPolicy("never").
WithBuildpacks(
settings.Buildpacks.Miniconda.Online,
settings.Buildpacks.PythonInstallers.Online,
settings.Buildpacks.PythonPackagers.Online,
settings.Buildpacks.BuildPlan.Online,
).
Expand Down
8 changes: 4 additions & 4 deletions integration/packagers/conda_layer_reuse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func condaTestLayerReuse(t *testing.T, context spec.G, it spec.S) {
firstImage, logs, err = pack.WithNoColor().Build.
WithPullPolicy("never").
WithBuildpacks(
settings.Buildpacks.Miniconda.Online,
settings.Buildpacks.PythonInstallers.Online,
settings.Buildpacks.PythonPackagers.Online,
settings.Buildpacks.BuildPlan.Online,
).
Expand All @@ -98,7 +98,7 @@ func condaTestLayerReuse(t *testing.T, context spec.G, it spec.S) {
secondImage, logs, err = pack.WithNoColor().Build.
WithPullPolicy("never").
WithBuildpacks(
settings.Buildpacks.Miniconda.Online,
settings.Buildpacks.PythonInstallers.Online,
settings.Buildpacks.PythonPackagers.Online,
settings.Buildpacks.BuildPlan.Online,
).
Expand Down Expand Up @@ -139,7 +139,7 @@ func condaTestLayerReuse(t *testing.T, context spec.G, it spec.S) {
firstImage, logs, err = pack.WithNoColor().Build.
WithPullPolicy("never").
WithBuildpacks(
settings.Buildpacks.Miniconda.Online,
settings.Buildpacks.PythonInstallers.Online,
settings.Buildpacks.PythonPackagers.Online,
settings.Buildpacks.BuildPlan.Online,
).
Expand All @@ -163,7 +163,7 @@ func condaTestLayerReuse(t *testing.T, context spec.G, it spec.S) {
secondImage, logs, err = pack.WithNoColor().Build.
WithPullPolicy("never").
WithBuildpacks(
settings.Buildpacks.Miniconda.Online,
settings.Buildpacks.PythonInstallers.Online,
settings.Buildpacks.PythonPackagers.Online,
settings.Buildpacks.BuildPlan.Online,
).
Expand Down
2 changes: 1 addition & 1 deletion integration/packagers/conda_lock_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func condaTestLockFile(t *testing.T, context spec.G, it spec.S) {
image, logs, err = pack.WithNoColor().Build.
WithPullPolicy("never").
WithBuildpacks(
settings.Buildpacks.Miniconda.Online,
settings.Buildpacks.PythonInstallers.Online,
settings.Buildpacks.PythonPackagers.Online,
settings.Buildpacks.BuildPlan.Online,
).
Expand Down
Loading
Loading