From b82298c081630e69d8fcb88fda0dfeeea16c2d91 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 7 Nov 2025 12:28:38 -0800 Subject: [PATCH] added build packs creation --- app/actions/projects/create.rb | 8 +- .../projects/initialize_build_packs.rb | 34 +++ app/actions/projects/update.rb | 3 +- app/actions/projects/update_build_packs.rb | 55 ++++ app/avo/resources/build_pack.rb | 2 +- .../buildpack_fields_controller.js | 256 ++++++++++++++++++ .../buildpack_search_controller.js | 10 +- .../async_search_dropdown_controller.js | 11 +- app/models/build_configuration.rb | 35 ++- app/models/build_pack.rb | 14 +- app/services/builders/build_cloud.rb | 4 +- app/services/builders/docker.rb | 4 +- app/views/build_packs/_search.html.erb | 24 +- .../_buildpack_fields.html.erb | 82 ++++++ .../_buildpack_item.html.erb | 19 ++ .../_dockerfile_fields.html.erb | 25 ++ .../build_configurations/_form.html.erb | 27 +- .../projects/create/_new_form_git.html.erb | 27 -- .../projects/update/_edit_form_git.html.erb | 22 -- .../20251102192149_create_build_packs.rb | 4 +- ...lds_from_project_to_build_configuration.rb | 44 +++ db/schema.rb | 22 +- package.json | 6 +- public/images/languages/buildpack.png | Bin 0 -> 16956 bytes public/images/languages/buildpack.webp | Bin 0 -> 4780 bytes public/images/languages/golang.png | Bin 0 -> 22485 bytes public/images/languages/golang.webp | Bin 0 -> 6906 bytes public/images/languages/java.png | Bin 0 -> 16703 bytes public/images/languages/java.webp | Bin 0 -> 8036 bytes public/images/languages/javascript.png | Bin 0 -> 66488 bytes public/images/languages/javascript.webp | Bin 0 -> 7396 bytes public/images/languages/php.png | Bin 0 -> 32172 bytes public/images/languages/php.webp | Bin 0 -> 4696 bytes public/images/languages/python.png | Bin 0 -> 6013 bytes public/images/languages/python.webp | Bin 0 -> 4222 bytes public/images/languages/ruby.png | Bin 0 -> 57956 bytes public/images/languages/ruby.webp | Bin 0 -> 7606 bytes public/images/languages/rust.png | Bin 0 -> 27538 bytes public/images/languages/rust.webp | Bin 0 -> 6860 bytes public/images/languages/scala.png | Bin 0 -> 22911 bytes public/images/languages/scala.webp | Bin 0 -> 2870 bytes resources/build_packs/packs.yaml | 101 +++++++ spec/actions/projects/create_spec.rb | 63 +++++ .../projects/initialize_build_packs_spec.rb | 63 +++++ .../projects/update_build_packs_spec.rb | 93 +++++++ spec/actions/projects/update_spec.rb | 16 +- spec/factories/build_configurations.rb | 24 +- spec/factories/build_packs.rb | 19 +- spec/models/build_configuration_spec.rb | 21 +- spec/models/build_pack_spec.rb | 15 +- yarn.lock | 252 +++++++++++------ 51 files changed, 1184 insertions(+), 221 deletions(-) create mode 100644 app/actions/projects/initialize_build_packs.rb create mode 100644 app/actions/projects/update_build_packs.rb create mode 100644 app/javascript/controllers/buildpack_fields_controller.js create mode 100644 app/views/projects/build_configurations/_buildpack_fields.html.erb create mode 100644 app/views/projects/build_configurations/_buildpack_item.html.erb create mode 100644 app/views/projects/build_configurations/_dockerfile_fields.html.erb create mode 100644 db/migrate/20251103000229_move_docker_fields_from_project_to_build_configuration.rb create mode 100644 public/images/languages/buildpack.png create mode 100644 public/images/languages/buildpack.webp create mode 100644 public/images/languages/golang.png create mode 100644 public/images/languages/golang.webp create mode 100644 public/images/languages/java.png create mode 100644 public/images/languages/java.webp create mode 100644 public/images/languages/javascript.png create mode 100644 public/images/languages/javascript.webp create mode 100644 public/images/languages/php.png create mode 100644 public/images/languages/php.webp create mode 100644 public/images/languages/python.png create mode 100644 public/images/languages/python.webp create mode 100644 public/images/languages/ruby.png create mode 100644 public/images/languages/ruby.webp create mode 100644 public/images/languages/rust.png create mode 100644 public/images/languages/rust.webp create mode 100644 public/images/languages/scala.png create mode 100644 public/images/languages/scala.webp create mode 100644 resources/build_packs/packs.yaml create mode 100644 spec/actions/projects/initialize_build_packs_spec.rb create mode 100644 spec/actions/projects/update_build_packs_spec.rb diff --git a/app/actions/projects/create.rb b/app/actions/projects/create.rb index 5458d035..352ccc26 100644 --- a/app/actions/projects/create.rb +++ b/app/actions/projects/create.rb @@ -9,9 +9,7 @@ module Projects :repository_url, :branch, :cluster_id, - :docker_build_context_directory, :docker_command, - :dockerfile_path, :container_registry_url, :predeploy_command, :project_fork_status, @@ -58,7 +56,10 @@ module Projects { provider: project.project_credential_provider.provider, driver: BuildConfiguration::DEFAULT_BUILDER, - image_repository: project.repository_url + build_type: :dockerfile, + image_repository: project.repository_url, + context_directory: ".", + dockerfile_path: "./Dockerfile" } end @@ -69,6 +70,7 @@ module Projects end steps << Projects::ValidateNamespaceAvailability + steps << Projects::InitializeBuildPacks steps << Projects::Save # Only register webhook in cloud mode diff --git a/app/actions/projects/initialize_build_packs.rb b/app/actions/projects/initialize_build_packs.rb new file mode 100644 index 00000000..3af4e484 --- /dev/null +++ b/app/actions/projects/initialize_build_packs.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Projects + class InitializeBuildPacks + extend LightService::Action + + expects :build_configuration, :params + promises :build_packs + + def self.fetch_buildpack_details!(build_pack) + result = Buildpacks::Details.execute( + namespace: build_pack.namespace, + name: build_pack.name + ) + build_pack.details = result.result.to_h + build_pack + end + + executed do |context| + build_configuration = context.build_configuration + next context unless build_configuration&.buildpacks? + + build_packs_params = context.params.dig(:project, :build_configuration, :build_packs_attributes) + next context unless build_packs_params + + context.build_packs = build_packs_params.map.with_index do |pack_params, build_order| + build_pack = build_configuration.build_packs.build( + pack_params.permit(:namespace, :name, :version, :reference_type).merge(build_order:) + ) + fetch_buildpack_details!(build_pack) + end + end + end +end diff --git a/app/actions/projects/update.rb b/app/actions/projects/update.rb index 491dc4ac..8026b1bb 100644 --- a/app/actions/projects/update.rb +++ b/app/actions/projects/update.rb @@ -12,7 +12,8 @@ module Projects build_configuration:, params: ).reduce( - Projects::UpdateSave + Projects::UpdateSave, + Projects::UpdateBuildPacks ) end diff --git a/app/actions/projects/update_build_packs.rb b/app/actions/projects/update_build_packs.rb new file mode 100644 index 00000000..425128ec --- /dev/null +++ b/app/actions/projects/update_build_packs.rb @@ -0,0 +1,55 @@ +class Projects::UpdateBuildPacks + extend LightService::Action + + expects :build_configuration, :params + + executed do |context| + build_configuration = context.build_configuration + next context unless build_configuration&.buildpacks? + + build_packs_params = context.params + .dig(:project, :build_configuration, :build_packs_attributes) || [] + + # Create a hash of existing build packs keyed by build_pack.key + existing_packs = {} + build_configuration.build_packs.each do |pack| + existing_packs[pack.key] = pack + end + + # Track which build packs are in the params and their order + incoming_keys = [] + + ActiveRecord::Base.transaction do + # Process each incoming build pack + build_packs_params.each_with_index do |pack_params, build_order| + permitted = pack_params.permit(:namespace, :name, :version, :reference_type) + namespace = permitted[:namespace] + name = permitted[:name] + + key = "#{namespace}/#{name}" + next if namespace.blank? || name.blank? + + incoming_keys << key unless incoming_keys.include?(key) + + if existing_packs[key] + # Build pack already exists, update its order + build_pack = existing_packs[key] + build_pack.build_order = build_order + else + # Build pack doesn't exist, create it and fetch details + build_pack = build_configuration.build_packs.build(permitted.merge(build_order:)) + Projects::InitializeBuildPacks.fetch_buildpack_details!(build_pack) + end + build_pack.save! + end + + # Delete build packs that are not in the incoming params (only persisted ones) + packs_to_delete = build_configuration.build_packs.reject do |pack| + incoming_keys.include?(pack.key) + end + packs_to_delete.each(&:destroy!) + end + + context + end +end \ No newline at end of file diff --git a/app/avo/resources/build_pack.rb b/app/avo/resources/build_pack.rb index 251dfd9a..03b4865e 100644 --- a/app/avo/resources/build_pack.rb +++ b/app/avo/resources/build_pack.rb @@ -4,7 +4,7 @@ class Avo::Resources::BuildPack < Avo::BaseResource # self.search = { # query: -> { query.ransack(id_eq: q, m: "or").result(distinct: false) } # } - + def fields field :id, as: :id field :build_configuration, as: :belongs_to diff --git a/app/javascript/controllers/buildpack_fields_controller.js b/app/javascript/controllers/buildpack_fields_controller.js new file mode 100644 index 00000000..c6f18aeb --- /dev/null +++ b/app/javascript/controllers/buildpack_fields_controller.js @@ -0,0 +1,256 @@ +import { Controller } from "@hotwired/stimulus" +import Sortable from "sortablejs" + +export default class extends Controller { + static targets = ["list", "template", "modal", "baseBuilder", "availableBuildpacks", "selectedBuildpacks"] + static values = { + packs: Object + } + + connect() { + this.selectedPacks = [] + this.initializeSortable() + + // Listen for buildpack selection from search + this.element.addEventListener("buildpack-search:buildpack-selected", this.handleSearchSelection.bind(this)) + } + + disconnect() { + this.element.removeEventListener("buildpack-search:buildpack-selected", this.handleSearchSelection.bind(this)) + } + + handleSearchSelection(event) { + const { namespace, name, version, description } = event.detail + + // Create a pack object with buildpack.webp as the default image + const pack = { + key: `${namespace}/${name}`, + namespace: namespace, + name: name, + version: version || '', + image: '/images/languages/buildpack.webp', + description: description || '' + } + + // Check if already selected + if (!this.selectedPacks.some(p => p.key === pack.key)) { + this.selectedPacks.push(pack) + this.displayAvailableBuildpacks() + this.renderSelectedBuildpacks() + } + } + + initializeSortable() { + if (this.hasListTarget) { + this.sortable = Sortable.create(this.listTarget, { + animation: 150, + handle: ".drag-handle", + ghostClass: "opacity-50" + }) + } + } + + initializeModalSortable() { + if (this.hasSelectedBuildpacksTarget && !this.modalSortable) { + this.modalSortable = Sortable.create(this.selectedBuildpacksTarget, { + animation: 150, + ghostClass: "opacity-50", + onEnd: () => { + this.updateSelectedPacksOrder() + } + }) + } + } + + updateSelectedPacksOrder() { + // Get the current DOM order and update selectedPacks array + const elements = this.selectedBuildpacksTarget.querySelectorAll('[data-key]') + this.selectedPacks = Array.from(elements).map(el => { + const key = el.dataset.key + return { key, ...this.packsValue[key] } + }) + } + + openModal() { + // Repopulate selectedPacks from existing buildpacks in the form + this.selectedPacks = this.getExistingBuildpacks() + this.displayAvailableBuildpacks() + this.renderSelectedBuildpacks() + this.modalTarget.showModal() + // Initialize sortable after modal is shown + setTimeout(() => this.initializeModalSortable(), 100) + } + + closeModal() { + this.modalTarget.close() + } + + getExistingBuildpacks() { + const existingPacks = [] + const cards = this.listTarget.querySelectorAll('.card') + + cards.forEach(card => { + const namespaceInput = card.querySelector('input[name*="[namespace]"]') + const nameInput = card.querySelector('input[name*="[name]"]') + + if (namespaceInput && nameInput) { + const key = `${namespaceInput.value}/${nameInput.value}` + const pack = this.packsValue[key] + if (pack) { + existingPacks.push({ key, ...pack }) + } + } + }) + + return existingPacks + } + + displayAvailableBuildpacks() { + const builder = this.baseBuilderTarget.value + const namespace = this.detectNamespace(builder) + + if (!namespace) { + this.availableBuildpacksTarget.innerHTML = '
Please select a base builder first
' + return + } + + const availablePacks = Object.entries(this.packsValue) + .filter(([key, pack]) => pack.namespace === namespace) + .map(([key, pack]) => ({ key, ...pack })) + + if (availablePacks.length === 0) { + this.availableBuildpacksTarget.innerHTML = '
No buildpacks available for this builder
' + return + } + + this.renderAvailableBuildpacks(availablePacks) + } + + renderAvailableBuildpacks(packs) { + const filteredPacks = packs.filter(pack => + !this.selectedPacks.some(selected => selected.key === pack.key) + ) + + if (filteredPacks.length === 0) { + this.availableBuildpacksTarget.innerHTML = '
All buildpacks selected
' + return + } + + const html = filteredPacks.map(pack => ` +
+ ${pack.key} +
+
${pack.namespace}/${pack.name}
+
${pack.description}
+
+
+ `).join('') + + this.availableBuildpacksTarget.innerHTML = html + } + + renderSelectedBuildpacks() { + if (this.selectedPacks.length === 0) { + this.selectedBuildpacksTarget.innerHTML = '
No buildpacks selected
' + if (this.modalSortable) { + this.modalSortable.destroy() + this.modalSortable = null + } + return + } + + const html = this.selectedPacks.map((pack, index) => ` +
+ ${pack.key} +
+
${pack.namespace}/${pack.name}
+
${pack.description}
+
+ +
+ `).join('') + + this.selectedBuildpacksTarget.innerHTML = html + + // Reinitialize sortable after rendering + if (this.modalSortable) { + this.modalSortable.destroy() + this.modalSortable = null + } + this.initializeModalSortable() + } + + detectNamespace(builder) { + if (!builder) return null + + if (builder.includes("paketo")) { + return "paketo-buildpacks" + } else if (builder.includes("heroku")) { + return "heroku" + } + return null + } + + selectBuildpack(event) { + const key = event.currentTarget.dataset.key + const pack = { key, ...this.packsValue[key] } + + this.selectedPacks.push(pack) + this.displayAvailableBuildpacks() + this.renderSelectedBuildpacks() + } + + deselectBuildpack(event) { + event.stopPropagation() + const index = parseInt(event.currentTarget.dataset.index) + this.selectedPacks.splice(index, 1) + this.displayAvailableBuildpacks() + this.renderSelectedBuildpacks() + } + + addSelectedBuildpacks() { + // Clear existing buildpacks from the list + this.listTarget.innerHTML = '' + + this.selectedPacks.forEach((pack, index) => { + const template = this.templateTarget.content || this.templateTarget + const clone = template.cloneNode(true) + + const img = clone.querySelector('[data-template-image]') + img.src = pack.image + img.alt = pack.key + + const title = clone.querySelector('[data-template-title]') + title.textContent = `${pack.namespace}/${pack.name}` + + const description = clone.querySelector('[data-template-description]') + description.textContent = pack.description + + const namespaceInput = clone.querySelector('[data-template-namespace]') + namespaceInput.value = pack.namespace + + const nameInput = clone.querySelector('[data-template-name]') + nameInput.value = pack.name + + const referenceTypeInput = clone.querySelector('[data-template-reference-type]') + referenceTypeInput.value = pack.reference_type + + const container = document.createElement('div') + container.appendChild(clone) + + this.listTarget.insertAdjacentHTML("beforeend", container.innerHTML) + }) + + this.closeModal() + } + + remove(event) { + event.target.closest(".card").remove() + } +} diff --git a/app/javascript/controllers/buildpack_search_controller.js b/app/javascript/controllers/buildpack_search_controller.js index 904a748f..c2d7cc82 100644 --- a/app/javascript/controllers/buildpack_search_controller.js +++ b/app/javascript/controllers/buildpack_search_controller.js @@ -33,6 +33,14 @@ export default class extends AsyncSearchDropdownController { } onItemSelect(buildpack, itemElement) { - console.log('Selected buildpack:', buildpack) + const latest = buildpack.latest + this.dispatch("buildpack-selected", { + detail: { + namespace: latest.namespace, + name: latest.name, + version: latest.version, + description: latest.description + } + }) } } diff --git a/app/javascript/controllers/components/async_search_dropdown_controller.js b/app/javascript/controllers/components/async_search_dropdown_controller.js index 9f904009..1aea762e 100644 --- a/app/javascript/controllers/components/async_search_dropdown_controller.js +++ b/app/javascript/controllers/components/async_search_dropdown_controller.js @@ -109,9 +109,16 @@ export default class extends Controller { selectItem(item, itemElement) { this.onItemSelect(item, itemElement) + this.clearInput() this.hideDropdown() } + clearInput() { + if (this.input) { + this.input.value = '' + } + } + showDropdown() { this.dropdown.classList.remove('hidden') } @@ -123,9 +130,9 @@ export default class extends Controller { showLoading() { this.dropdown.innerHTML = ` -
  • +
  • - Searching... + Searching...
  • ` this.showDropdown() diff --git a/app/models/build_configuration.rb b/app/models/build_configuration.rb index aecd2e18..a426f20e 100644 --- a/app/models/build_configuration.rb +++ b/app/models/build_configuration.rb @@ -2,15 +2,18 @@ # # Table name: build_configurations # -# id :bigint not null, primary key -# build_type :integer default(0), not null -# driver :integer not null -# image_repository :string not null -# created_at :datetime not null -# updated_at :datetime not null -# build_cloud_id :bigint -# project_id :bigint not null -# provider_id :bigint not null +# id :bigint not null, primary key +# build_type :integer default("dockerfile"), not null +# buildpack_base_builder :string +# context_directory :string default("."), not null +# dockerfile_path :string default("./Dockerfile"), not null +# driver :integer not null +# image_repository :string not null +# created_at :datetime not null +# updated_at :datetime not null +# build_cloud_id :bigint +# project_id :bigint not null +# provider_id :bigint not null # # Indexes # @@ -36,7 +39,7 @@ class BuildConfiguration < ApplicationRecord belongs_to :project belongs_to :build_cloud, optional: true belongs_to :provider - has_many :build_packs, dependent: :destroy + has_many :build_packs, -> { order(:build_order) }, dependent: :destroy validates_presence_of :project, :provider, :driver validates_presence_of :image_repository @@ -46,9 +49,19 @@ class BuildConfiguration < ApplicationRecord } def self.permit_params(params) - params.permit(:image_repository, :driver, :build_cloud_id, :provider_id) + params.permit(:image_repository, :driver, :build_cloud_id, :provider_id, :context_directory, :dockerfile_path, :build_type, :buildpack_base_builder) end + def self.available_buildpacks + packs_file = Rails.root.join("resources", "build_packs", "packs.yaml") + YAML.load_file(packs_file) + end + + enum :build_type, { + dockerfile: 0, + buildpacks: 1 + } + enum :driver, { cloud: 0, docker: 1, diff --git a/app/models/build_pack.rb b/app/models/build_pack.rb index 1e2da77c..78321061 100644 --- a/app/models/build_pack.rb +++ b/app/models/build_pack.rb @@ -3,10 +3,11 @@ # Table name: build_packs # # id :bigint not null, primary key +# build_order :integer not null # details :jsonb # name :string # namespace :string -# reference_type :string not null +# reference_type :integer not null # uri :text # version :string # created_at :datetime not null @@ -27,11 +28,12 @@ class BuildPack < ApplicationRecord VERIFIED_NAMESPACES = %w[io.buildpacks paketo-buildpacks heroku tanzu-buildpacks].freeze belongs_to :build_configuration + validates_presence_of :build_order enum :reference_type, { registry: 0, git: 1, - url: 2, + url: 2 } validates :reference_type, presence: true @@ -69,4 +71,12 @@ class BuildPack < ApplicationRecord uri end end + + def key + "#{namespace}/#{name}" + end + + def static_info + BuildConfiguration.available_buildpacks[key] || {} + end end diff --git a/app/services/builders/build_cloud.rb b/app/services/builders/build_cloud.rb index 15b2b6b0..6e6d2e91 100644 --- a/app/services/builders/build_cloud.rb +++ b/app/services/builders/build_cloud.rb @@ -42,7 +42,7 @@ module Builders command += [ "--push" ] # Push directly to registry command += [ "--progress", "plain" ] command += [ "-t", project.container_image_reference ] - command += [ "-f", File.join(repository_path, project.dockerfile_path) ] + command += [ "-f", File.join(repository_path, project.build_configuration.dockerfile_path) ] # Add build arguments project.environment_variables.each do |envar| @@ -56,7 +56,7 @@ module Builders command += [ "--push" ] # Add build context - command << File.join(repository_path, project.docker_build_context_directory) + command << File.join(repository_path, project.build_configuration.context_directory) command end diff --git a/app/services/builders/docker.rb b/app/services/builders/docker.rb index 41a64aba..f776ea44 100644 --- a/app/services/builders/docker.rb +++ b/app/services/builders/docker.rb @@ -32,7 +32,7 @@ module Builders "--progress=plain", "--platform", "linux/amd64", "-t", project.container_image_reference, - "-f", File.join(repository_path, project.dockerfile_path) + "-f", File.join(repository_path, project.build_configuration.dockerfile_path) ] # Add environment variables to the build command @@ -43,7 +43,7 @@ module Builders docker_build_command.push("--push") # Add the build context directory at the end - docker_build_command.push(File.join(repository_path, project.docker_build_context_directory)) + docker_build_command.push(File.join(repository_path, project.build_configuration.context_directory)) Rails.logger.info("Docker build command: `#{docker_build_command.join(" ")}`") docker_build_command end diff --git a/app/views/build_packs/_search.html.erb b/app/views/build_packs/_search.html.erb index 6a3c2037..4afa64df 100644 --- a/app/views/build_packs/_search.html.erb +++ b/app/views/build_packs/_search.html.erb @@ -1,19 +1,7 @@ -
    - -
    - -
    - -
    - -
    - +
    +
    diff --git a/app/views/projects/build_configurations/_buildpack_fields.html.erb b/app/views/projects/build_configurations/_buildpack_fields.html.erb new file mode 100644 index 00000000..e3d5b173 --- /dev/null +++ b/app/views/projects/build_configurations/_buildpack_fields.html.erb @@ -0,0 +1,82 @@ +
    +
    + + <%= bc_form.select( + :buildpack_base_builder, + options_for_select( + [ + ["heroku/builder-classic:22", "heroku/builder-classic:22"], + ["heroku/builder:22", "heroku/builder:22"], + ["heroku/builder:24", "heroku/builder:24"], + ["heroku/buildpacks:18", "heroku/buildpacks:18"], + ["heroku/buildpacks:20", "heroku/buildpacks:20"], + ["paketobuildpacks/builder-jammy-full:latest", "paketobuildpacks/builder-jammy-full:latest"], + ["paketobuildpacks/builder:full", "paketobuildpacks/builder:full"] + ], + selected: build_configuration.buildpack_base_builder || "heroku/buildpacks:20" + ), + { include_blank: "Select a base builder..." }, + { class: "select select-bordered w-full", data: { buildpack_fields_target: "baseBuilder" } } + ) %> + +
    + +
    + +
    + <% build_configuration.build_packs.each do |build_pack| %> + <%= render "projects/build_configurations/buildpack_item", build_pack: build_pack %> + <% end %> +
    + +
    + + + + + + +
    diff --git a/app/views/projects/build_configurations/_buildpack_item.html.erb b/app/views/projects/build_configurations/_buildpack_item.html.erb new file mode 100644 index 00000000..ef35f8f3 --- /dev/null +++ b/app/views/projects/build_configurations/_buildpack_item.html.erb @@ -0,0 +1,19 @@ +<% build_pack = local_assigns[:build_pack] || BuildPack.new %> +
    +
    +
    + +
    + +
    +
    <%= "#{build_pack.namespace}/#{build_pack.name}" %>
    +
    <%= build_pack.details.dig('latest', 'description') %>
    +
    + +
    + + + +
    \ No newline at end of file diff --git a/app/views/projects/build_configurations/_dockerfile_fields.html.erb b/app/views/projects/build_configurations/_dockerfile_fields.html.erb new file mode 100644 index 00000000..b9955f5f --- /dev/null +++ b/app/views/projects/build_configurations/_dockerfile_fields.html.erb @@ -0,0 +1,25 @@ +
    +
    + + <%= bc_form.text_field( + :dockerfile_path, + class: "input input-bordered w-full focus:outline-offset-0", + value: build_configuration.dockerfile_path + ) %> + +
    +
    + + <%= bc_form.text_field( + :context_directory, + class: "input input-bordered w-full focus:outline-offset-0", + value: build_configuration.context_directory + ) %> +
    +
    diff --git a/app/views/projects/build_configurations/_form.html.erb b/app/views/projects/build_configurations/_form.html.erb index dbd0e337..46893541 100644 --- a/app/views/projects/build_configurations/_form.html.erb +++ b/app/views/projects/build_configurations/_form.html.erb @@ -1,6 +1,6 @@
    - <% build_configuration = project.build_configuration || BuildConfiguration.new(driver: 'docker') %> + <% build_configuration = project.build_configuration || BuildConfiguration.new %> <%= form.fields_for :build_configuration, build_configuration do |bc_form| %>
    <%= render "shared/partials/radio_selector", selected: build_configuration.driver, options: [ @@ -64,6 +64,31 @@ pattern: "[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]/[a-zA-Z0-9._-]+", title: "Must be in the format 'namespace/repo'" ) %> + +
    +
    + <%= render "shared/partials/radio_selector", selected: build_configuration.build_type || "dockerfile", options: [ + { + icon: "skill-icons:docker", + name: "project[build_configuration][build_type]", + label: "Dockerfile", + value: "dockerfile", + description: "Build images using a Dockerfile. Traditional Docker build approach with full control over the build process.", + partial: "projects/build_configurations/dockerfile_fields", + locals: { bc_form:, build_configuration: } + }, + { + icon: "devicon:heroku", + name: "project[build_configuration][build_type]", + label: "Buildpacks", + value: "buildpacks", + description: "Use Cloud Native Buildpacks to automatically detect and build your application. No Dockerfile needed.", + partial: "projects/build_configurations/buildpack_fields", + locals: { bc_form:, build_configuration: } + } + ] %>
    <% end %>
    diff --git a/app/views/projects/create/_new_form_git.html.erb b/app/views/projects/create/_new_form_git.html.erb index b832622f..0fe76093 100644 --- a/app/views/projects/create/_new_form_git.html.erb +++ b/app/views/projects/create/_new_form_git.html.erb @@ -80,16 +80,6 @@ * Required
    - -
    - - <%= form.text_field :container_registry_url, class: "input input-bordered w-full focus:outline-offset-0", value: "" %> - -
    <% end %> @@ -100,23 +90,6 @@ <%= form.check_box :autodeploy, class: "checkbox" %> <% end %> - <%= render(FormFieldComponent.new( - label: "Dockerfile path", - description: "The path to the Dockerfile in your repository." - )) do %> - <%= form.text_field :dockerfile_path, class: "input input-bordered w-full focus:outline-offset-0" %> - - <% end %> - - <%= render(FormFieldComponent.new( - label: "Docker build context directory", - description: "The directory to use as the build context for the Docker build." - )) do %> - <%= form.text_field :docker_build_context_directory, class: "input input-bordered w-full focus:outline-offset-0" %> - <% end %> - <%= render(FormFieldComponent.new( label: "Docker command", description: "The command to run to start the container." diff --git a/app/views/projects/update/_edit_form_git.html.erb b/app/views/projects/update/_edit_form_git.html.erb index 33f2da51..5ea1f06f 100644 --- a/app/views/projects/update/_edit_form_git.html.erb +++ b/app/views/projects/update/_edit_form_git.html.erb @@ -23,17 +23,6 @@ <%= form.check_box :autodeploy, class: "checkbox" %> <% end %> - <%= render(FormFieldComponent.new(label: "Dockerfile path")) do %> - <%= form.text_field :dockerfile_path, class: "input input-bordered w-full focus:outline-offset-0" %> - - <% end %> - - <%= render(FormFieldComponent.new(label: "Docker build context directory")) do %> - <%= form.text_field :docker_build_context_directory, class: "input input-bordered w-full focus:outline-offset-0" %> - <% end %> - <%= render(FormFieldComponent.new(label: "Docker command")) do %> <%= form.text_field :docker_command, class: "input input-bordered w-full focus:outline-offset-0" %>