From 6d238b5440cb7632e6a0339ffac0e061c98fd031 Mon Sep 17 00:00:00 2001 From: Arjun Sreedharan Date: Thu, 22 May 2025 17:25:57 -0400 Subject: [PATCH] Remove redundant version values from default config Remove PHP_ vars in defaults/options.json. This information is redundant and can be deduced from the buildpack's manifest. Immediate reason: Dependency-update PRs generated by CI ends up in conflict and they do not automerge as each PR tries to update its version value in defaults/options.json CI PR to stop updating options.json: https://github.com/cloudfoundry/buildpacks-ci/pull/432 --- defaults/options.json | 2 +- lib/build_pack_utils/builder.py | 37 +++++++++++++-- src/php/unit/available_versions_test.go | 47 ------------------- tests/test_builder_default_config.py | 60 +++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 51 deletions(-) delete mode 100644 src/php/unit/available_versions_test.go create mode 100644 tests/test_builder_default_config.py diff --git a/defaults/options.json b/defaults/options.json index 07f5ba836..fa157a0c3 100644 --- a/defaults/options.json +++ b/defaults/options.json @@ -1 +1 @@ -{"STACK":"trusty","LIBDIR":"lib","WEBDIR":"htdocs","WEB_SERVER":"httpd","PHP_VM":"php","ADMIN_EMAIL":"admin@localhost","HTTPD_STRIP":false,"HTTPD_MODULES_STRIP":true,"NGINX_STRIP":false,"PHP_STRIP":false,"PHP_MODULES_STRIP":true,"PHP_MODULES":[],"PHP_EXTENSIONS":["bz2","zlib","curl"],"ZEND_EXTENSIONS":[],"PHP_DEFAULT":"8.1.32","PHP_81_LATEST":"8.1.32","PHP_82_LATEST":"8.2.28","PHP_83_LATEST":"8.3.19"} +{"STACK":"trusty","LIBDIR":"lib","WEBDIR":"htdocs","WEB_SERVER":"httpd","PHP_VM":"php","ADMIN_EMAIL":"admin@localhost","HTTPD_STRIP":false,"HTTPD_MODULES_STRIP":true,"NGINX_STRIP":false,"PHP_STRIP":false,"PHP_MODULES_STRIP":true,"PHP_MODULES":[],"PHP_EXTENSIONS":["bz2","zlib","curl"],"ZEND_EXTENSIONS":[]} diff --git a/lib/build_pack_utils/builder.py b/lib/build_pack_utils/builder.py index 6c8c696c8..8904db16d 100644 --- a/lib/build_pack_utils/builder.py +++ b/lib/build_pack_utils/builder.py @@ -3,6 +3,7 @@ import shutil import re import logging + from collections import defaultdict from io import StringIO from subprocess import Popen @@ -45,15 +46,45 @@ def __init__(self, builder): self.builder = builder def default_config(self): - self._merge( - CloudFoundryUtil.load_json_config_file_from( + opts = CloudFoundryUtil.load_json_config_file_from( self.builder._ctx['BP_DIR'], - 'defaults/options.json')) + 'defaults/options.json') + # imported here instead of at the top of the file to prevent ImportError, + # because build_pack_utils is also loaded at launch-time when the + # vendored lib yaml is unavailable. + import yaml + with open(os.path.join(self.builder._ctx['BP_DIR'], 'manifest.yml'), 'r') as f: + manifest = yaml.safe_load(f) + + default_versions = manifest.get('default_versions', []) + php_default = next( + (d['version'] for d in default_versions if d.get('name') == 'php'), + None + ) + + if not php_default: + raise Exception('ERROR: Buildpack could not read default PHP version.') + opts['PHP_DEFAULT'] = php_default + + deps = manifest.get('dependencies', []) + php_versions = [d['version'] for d in deps if d.get('name') == 'php'] + lines = defaultdict(list) + for v in php_versions: + maj, mino, _ = v.split('.', 2) + # Construct keys. e.g. "PHP_83_LATEST" corresponding to PHP 8.3.X + lines[f'PHP_{maj}{mino}_LATEST'].append(v) + for key, vs in lines.items(): + # sort and find the highest patch version for each key + vs.sort(key=lambda s: tuple(map(int, s.split('.')))) + opts[key] = vs[-1] + + self._merge(opts) return self def stack_config(self): stack = os.environ.get('CF_STACK', None) if stack: + # we haven't used this config since cflinuxfs2 self._merge( CloudFoundryUtil.load_json_config_file_from( self.builder._ctx['BP_DIR'], diff --git a/src/php/unit/available_versions_test.go b/src/php/unit/available_versions_test.go deleted file mode 100644 index b424bd609..000000000 --- a/src/php/unit/available_versions_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package unit_test - -import ( - "path/filepath" - "time" - - "github.com/cloudfoundry/libbuildpack" - "github.com/cloudfoundry/libbuildpack/cutlass" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var _ = Describe("Options.JSON", func() { - var versions []string - var defaults map[string]interface{} - BeforeEach(func() { - bpDir, err := cutlass.FindRoot() - Expect(err).NotTo(HaveOccurred()) - - Expect(libbuildpack.NewJSON().Load(filepath.Join(bpDir, "defaults", "options.json"), &defaults)).To(Succeed()) - - manifest, err := libbuildpack.NewManifest(bpDir, nil, time.Now()) - Expect(err).NotTo(HaveOccurred()) - versions = manifest.AllDependencyVersions("php") - }) - - It("PHP_81_LATEST will have the latest 8.1 version", func() { - latest, err := libbuildpack.FindMatchingVersion("8.1.x", versions) - Expect(err).NotTo(HaveOccurred()) - - Expect(defaults["PHP_81_LATEST"]).To(Equal(latest)) - }) - - It("PHP_82_LATEST will have the latest 8.2 version", func() { - latest, err := libbuildpack.FindMatchingVersion("8.2.x", versions) - Expect(err).NotTo(HaveOccurred()) - - Expect(defaults["PHP_82_LATEST"]).To(Equal(latest)) - }) - - It("PHP_DEFAULT will have the latest 8.1 version", func() { - latest, err := libbuildpack.FindMatchingVersion("8.1.x", versions) - Expect(err).NotTo(HaveOccurred()) - - Expect(defaults["PHP_DEFAULT"]).To(Equal(latest)) - }) -}) diff --git a/tests/test_builder_default_config.py b/tests/test_builder_default_config.py new file mode 100644 index 000000000..bd955f712 --- /dev/null +++ b/tests/test_builder_default_config.py @@ -0,0 +1,60 @@ +import unittest +import tempfile +import shutil +import os +import json +from build_pack_utils import utils +from unittest.mock import MagicMock +from lib.build_pack_utils import builder +from nose.tools import eq_, assert_not_in + + +class TestBuilderDefaultConfig(unittest.TestCase): + def setUp(self): + self.bp_dir = tempfile.mkdtemp() + self.manifest_path = os.path.join(self.bp_dir, 'manifest.yml') + self.manifest_content = """--- +default_versions: + - name: php + version: 8.2.7 +dependencies: + - name: php + version: 8.2.7 + - name: php + version: 8.2.9 + - name: php + version: 8.1.20 + - name: php + version: 8.1.15 + - name: php + version: 8.0.30 + - name: nginx + version: 1.21.1 +""" + + with open(self.manifest_path, 'w') as f: + f.write(self.manifest_content) + + ctx = utils.FormattedDict({ + 'BP_DIR': self.bp_dir + }) + self.builder = MagicMock(_ctx=ctx) + self.configurer = builder.Configurer(self.builder) + + def tearDown(self): + shutil.rmtree(self.bp_dir) + + def test_default_config_sets_php_default_and_stream_latest(self): + self.configurer.default_config() + injected = self.builder._ctx + + eq_(injected.get('PHP_DEFAULT'), '8.2.7') + eq_(injected.get('PHP_82_LATEST'), '8.2.9') + eq_(injected.get('PHP_81_LATEST'), '8.1.20') + eq_(injected.get('PHP_80_LATEST'), '8.0.30') + + assert_not_in('PHP_83_LATEST', injected) + + def test_default_config_ignores_non_php_dependencies(self): + self.configurer.default_config() + assert_not_in('NGINX_LATEST', self.builder._ctx)