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.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.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 @@
+
+
+
+ Base Builder
+
+ <%= 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" } }
+ ) %>
+
+ Select the base builder image for Cloud Native Buildpacks
+
+
+
+
+
+
+ <%= render "projects/build_configurations/buildpack_item" %>
+
+
+
+
+
Add Buildpacks
+
Select buildpacks to add to your build configuration.
+
+
+
+
Selected Buildpacks
+
+
No buildpacks selected
+
+
+
+
+
Official Buildpacks
+
+
+
+
+
+
+
Search Registry
+ <%= render "build_packs/search" %>
+
+
+
+
+
+
+
+ Cancel
+ Add Selected
+
+
+
+
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 @@
+
+
+
+ Dockerfile path
+
+ <%= bc_form.text_field(
+ :dockerfile_path,
+ class: "input input-bordered w-full focus:outline-offset-0",
+ value: build_configuration.dockerfile_path
+ ) %>
+
+ * Required
+
+
+
+
+ Build context directory
+
+ <%= 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'"
) %>
+
+ If this is left blank, a container registry will be automatically created for you.
+
+
+
+ <%= 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
-
-
-
- Container registry URL
-
- <%= form.text_field :container_registry_url, class: "input input-bordered w-full focus:outline-offset-0", value: "" %>
-
- If this is left blank, Github Container Registry will be used
-
-
<% 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" %>
-
- * Required
-
- <% 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" %>
-
- * Required
-
- <% 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" %>
@@ -45,17 +34,6 @@
<%= form.text_field :predeploy_command, class: "input input-bordered w-full focus:outline-offset-0" %>
<% end %>
- <%= render(FormFieldComponent.new(label: "Container registry URL")) do %>
- <%= form.text_field(
- :container_registry_url,
- class: "input input-bordered w-full focus:outline-offset-0",
- value: form.object.attributes["container_registry_url"],
- ) %>
-
- If this is left blank, <%= project.github? ? "Github" : "Gitlab" %> Container Registry will be used
-
- <% end %>
-
<% if Flipper.enabled?(:build_configuration, current_account) %>
<%= render(FormFieldComponent.new(label: "Build configuration")) do %>
<%= render "projects/build_configurations/form", form: form, project: project %>
diff --git a/db/migrate/20251102192149_create_build_packs.rb b/db/migrate/20251102192149_create_build_packs.rb
index 94f98d76..1a04c1ec 100644
--- a/db/migrate/20251102192149_create_build_packs.rb
+++ b/db/migrate/20251102192149_create_build_packs.rb
@@ -2,10 +2,11 @@ class CreateBuildPacks < ActiveRecord::Migration[7.2]
def change
create_table :build_packs do |t|
t.references :build_configuration, null: false, foreign_key: true
- t.string :reference_type, null: false # registry, docker, git, url, path
+ t.integer :reference_type, null: false # registry, docker, git, url, path
t.string :namespace # for registry buildpacks
t.string :name # for registry buildpacks
t.string :version
+ t.integer :build_order, null: false
t.text :uri # for git, url, path, or docker references
t.jsonb :details, default: {}
@@ -14,5 +15,6 @@ class CreateBuildPacks < ActiveRecord::Migration[7.2]
add_index :build_packs, [ :build_configuration_id, :reference_type, :namespace, :name ], name: 'index_build_packs_on_config_type_namespace_name'
add_index :build_packs, [ :build_configuration_id, :uri ], name: 'index_build_packs_on_config_uri'
+ add_column :build_configurations, :buildpack_base_builder, :string
end
end
diff --git a/db/migrate/20251103000229_move_docker_fields_from_project_to_build_configuration.rb b/db/migrate/20251103000229_move_docker_fields_from_project_to_build_configuration.rb
new file mode 100644
index 00000000..8a247469
--- /dev/null
+++ b/db/migrate/20251103000229_move_docker_fields_from_project_to_build_configuration.rb
@@ -0,0 +1,44 @@
+class MoveDockerFieldsFromProjectToBuildConfiguration < ActiveRecord::Migration[7.2]
+ def up
+ # Add build fields to build_configurations (excluding docker_command which is used at runtime)
+ add_column :build_configurations, :context_directory, :string, default: ".", null: false
+ add_column :build_configurations, :dockerfile_path, :string, default: "./Dockerfile", null: false
+
+ # Backfill data from projects to build_configurations
+ reversible do |dir|
+ dir.up do
+ Project.reset_column_information
+ BuildConfiguration.reset_column_information
+
+ Project.find_each do |project|
+ # Create build_configuration if it doesn't exist
+ if project.build_configuration.nil?
+ # Skip projects that don't have the required associations
+ next unless project.project_credential_provider.present?
+
+ BuildConfiguration.create!(
+ project: project,
+ provider: project.project_credential_provider.provider,
+ driver: BuildConfiguration::DEFAULT_BUILDER,
+ image_repository: project.repository_url,
+ context_directory: project.docker_build_context_directory,
+ dockerfile_path: project.dockerfile_path
+ )
+ else
+ # Update existing build_configuration
+ project.build_configuration.update!(
+ context_directory: project.docker_build_context_directory,
+ dockerfile_path: project.dockerfile_path
+ )
+ end
+ end
+ end
+ end
+ end
+
+ def down
+ # Remove build fields from build_configurations
+ remove_column :build_configurations, :context_directory
+ remove_column :build_configurations, :dockerfile_path
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1e252576..9e729c52 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.2].define(version: 2025_10_27_014101) do
+ActiveRecord::Schema[7.2].define(version: 2025_11_03_000229) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -112,11 +112,30 @@ ActiveRecord::Schema[7.2].define(version: 2025_10_27_014101) do
t.bigint "provider_id", null: false
t.integer "build_type", default: 0, null: false
t.string "image_repository", null: false
+ t.string "buildpack_base_builder"
+ t.string "context_directory", default: ".", null: false
+ t.string "dockerfile_path", default: "./Dockerfile", null: false
t.index ["build_cloud_id"], name: "index_build_configurations_on_build_cloud_id"
t.index ["project_id"], name: "index_build_configurations_on_project_id"
t.index ["provider_id"], name: "index_build_configurations_on_provider_id"
end
+ create_table "build_packs", force: :cascade do |t|
+ t.bigint "build_configuration_id", null: false
+ t.integer "reference_type", null: false
+ t.string "namespace"
+ t.string "name"
+ t.string "version"
+ t.integer "build_order", null: false
+ t.text "uri"
+ t.jsonb "details", default: {}
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["build_configuration_id", "reference_type", "namespace", "name"], name: "index_build_packs_on_config_type_namespace_name"
+ t.index ["build_configuration_id", "uri"], name: "index_build_packs_on_config_uri"
+ t.index ["build_configuration_id"], name: "index_build_packs_on_build_configuration_id"
+ end
+
create_table "builds", force: :cascade do |t|
t.bigint "project_id", null: false
t.string "repository_url"
@@ -508,6 +527,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_10_27_014101) do
add_foreign_key "build_configurations", "build_clouds"
add_foreign_key "build_configurations", "projects"
add_foreign_key "build_configurations", "providers"
+ add_foreign_key "build_packs", "build_configurations"
add_foreign_key "builds", "projects"
add_foreign_key "clusters", "accounts"
add_foreign_key "cron_schedules", "services"
diff --git a/package.json b/package.json
index d6b26167..779aed7a 100644
--- a/package.json
+++ b/package.json
@@ -15,13 +15,14 @@
"@codemirror/view": "^6.38.1",
"@gorails/ninja-keys": "^1.2.1",
"@hotwired/stimulus": "^3.2.2",
- "@hotwired/turbo-rails": "^8.0.10",
+ "@hotwired/turbo-rails": "^8.0.20",
"@iconify/tailwind": "^1.1.3",
"@popperjs/core": "^2.11.8",
"@rails/actioncable": "^7.1.0",
"@rails/activestorage": "^7.2.100",
- "@rails/request.js": "^0.0.11",
+ "@rails/request.js": "^0.0.12",
"@rails/ujs": "^7.1.3-4",
+ "@stimulus-components/sortable": "^5.0.3",
"@tailwindcss/aspect-ratio": "^0.4.0",
"@tailwindcss/forms": "^0.5.0",
"@tailwindcss/typography": "^0.5.15",
@@ -44,6 +45,7 @@
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"sass": "^1.79.3",
+ "sortablejs": "^1.15.6",
"stimulus-flatpickr": "^3.0.0-0",
"stimulus-textarea-autogrow": "^4.1.0",
"tailwindcss": "^3.4.1",
diff --git a/public/images/languages/buildpack.png b/public/images/languages/buildpack.png
new file mode 100644
index 00000000..4b6c1278
Binary files /dev/null and b/public/images/languages/buildpack.png differ
diff --git a/public/images/languages/buildpack.webp b/public/images/languages/buildpack.webp
new file mode 100644
index 00000000..b64c21b2
Binary files /dev/null and b/public/images/languages/buildpack.webp differ
diff --git a/public/images/languages/golang.png b/public/images/languages/golang.png
new file mode 100644
index 00000000..2b7afae0
Binary files /dev/null and b/public/images/languages/golang.png differ
diff --git a/public/images/languages/golang.webp b/public/images/languages/golang.webp
new file mode 100644
index 00000000..b29f3bd7
Binary files /dev/null and b/public/images/languages/golang.webp differ
diff --git a/public/images/languages/java.png b/public/images/languages/java.png
new file mode 100644
index 00000000..1bbe0055
Binary files /dev/null and b/public/images/languages/java.png differ
diff --git a/public/images/languages/java.webp b/public/images/languages/java.webp
new file mode 100644
index 00000000..10de5c7d
Binary files /dev/null and b/public/images/languages/java.webp differ
diff --git a/public/images/languages/javascript.png b/public/images/languages/javascript.png
new file mode 100644
index 00000000..3ea9c172
Binary files /dev/null and b/public/images/languages/javascript.png differ
diff --git a/public/images/languages/javascript.webp b/public/images/languages/javascript.webp
new file mode 100644
index 00000000..9b9be6b5
Binary files /dev/null and b/public/images/languages/javascript.webp differ
diff --git a/public/images/languages/php.png b/public/images/languages/php.png
new file mode 100644
index 00000000..146b16fc
Binary files /dev/null and b/public/images/languages/php.png differ
diff --git a/public/images/languages/php.webp b/public/images/languages/php.webp
new file mode 100644
index 00000000..b8aea7e2
Binary files /dev/null and b/public/images/languages/php.webp differ
diff --git a/public/images/languages/python.png b/public/images/languages/python.png
new file mode 100644
index 00000000..eabc2fc5
Binary files /dev/null and b/public/images/languages/python.png differ
diff --git a/public/images/languages/python.webp b/public/images/languages/python.webp
new file mode 100644
index 00000000..f03fead5
Binary files /dev/null and b/public/images/languages/python.webp differ
diff --git a/public/images/languages/ruby.png b/public/images/languages/ruby.png
new file mode 100644
index 00000000..17f74e23
Binary files /dev/null and b/public/images/languages/ruby.png differ
diff --git a/public/images/languages/ruby.webp b/public/images/languages/ruby.webp
new file mode 100644
index 00000000..2b077186
Binary files /dev/null and b/public/images/languages/ruby.webp differ
diff --git a/public/images/languages/rust.png b/public/images/languages/rust.png
new file mode 100644
index 00000000..f6c50bd1
Binary files /dev/null and b/public/images/languages/rust.png differ
diff --git a/public/images/languages/rust.webp b/public/images/languages/rust.webp
new file mode 100644
index 00000000..5f4425f2
Binary files /dev/null and b/public/images/languages/rust.webp differ
diff --git a/public/images/languages/scala.png b/public/images/languages/scala.png
new file mode 100644
index 00000000..71d0c329
Binary files /dev/null and b/public/images/languages/scala.png differ
diff --git a/public/images/languages/scala.webp b/public/images/languages/scala.webp
new file mode 100644
index 00000000..5557463e
Binary files /dev/null and b/public/images/languages/scala.webp differ
diff --git a/resources/build_packs/packs.yaml b/resources/build_packs/packs.yaml
new file mode 100644
index 00000000..31666f68
--- /dev/null
+++ b/resources/build_packs/packs.yaml
@@ -0,0 +1,101 @@
+---
+# Flat list of available buildpacks
+# Key format: namespace/name
+
+paketo-buildpacks/ruby:
+ namespace: paketo-buildpacks
+ name: ruby
+ image: /images/languages/ruby.webp
+ description: "Paketo Ruby Buildpack - A language family buildpack for building Ruby apps"
+ reference_type: registry
+
+heroku/ruby:
+ namespace: heroku
+ name: ruby
+ image: /images/languages/ruby.webp
+ description: "Heroku's buildpack for Ruby applications"
+ reference_type: registry
+
+paketo-buildpacks/python:
+ namespace: paketo-buildpacks
+ name: python
+ image: /images/languages/python.webp
+ description: "Paketo Python Buildpack - A language family buildpack for building Python apps"
+ reference_type: registry
+
+heroku/python:
+ namespace: heroku
+ name: python
+ image: /images/languages/python.webp
+ description: "Heroku's buildpack for Python applications"
+ reference_type: registry
+
+paketo-buildpacks/go:
+ namespace: paketo-buildpacks
+ name: go
+ image: /images/languages/golang.webp
+ description: "Paketo Go Buildpack - A language family buildpack for building Go apps"
+ reference_type: registry
+
+heroku/go:
+ namespace: heroku
+ name: go
+ image: /images/languages/golang.webp
+ description: "Heroku's buildpack for Go applications"
+ reference_type: registry
+
+paketo-buildpacks/java:
+ namespace: paketo-buildpacks
+ name: java
+ image: /images/languages/java.webp
+ description: "Paketo Java Buildpack - A language family buildpack for building Java apps"
+ reference_type: registry
+
+heroku/java:
+ namespace: heroku
+ name: java
+ image: /images/languages/java.webp
+ description: "Heroku's buildpack for Java applications"
+ reference_type: registry
+
+paketo-buildpacks/nodejs:
+ namespace: paketo-buildpacks
+ name: nodejs
+ image: /images/languages/javascript.webp
+ description: "Paketo Node.js Buildpack - A language family buildpack for building Node.js apps"
+ reference_type: registry
+
+heroku/nodejs:
+ namespace: heroku
+ name: nodejs
+ image: /images/languages/javascript.webp
+ description: "Heroku's buildpack for Node.js applications"
+ reference_type: registry
+
+paketo-buildpacks/php:
+ namespace: paketo-buildpacks
+ name: php
+ image: /images/languages/php.webp
+ description: "Paketo PHP Buildpack - A language family buildpack for building PHP apps"
+ reference_type: registry
+
+heroku/php:
+ namespace: heroku
+ name: php
+ image: /images/languages/php.webp
+ description: "Heroku's buildpack for PHP applications"
+ reference_type: registry
+
+paketo-buildpacks/sbt:
+ namespace: paketo-buildpacks
+ name: sbt
+ image: /images/languages/scala.webp
+ description: "Paketo SBT Buildpack - A buildpack for building Scala apps with SBT"
+ reference_type: registry
+
+heroku/scala:
+ namespace: heroku
+ name: scala
+ image: /images/languages/scala.webp
+ description: "Heroku's buildpack for Scala applications"
+ reference_type: registry
\ No newline at end of file
diff --git a/spec/actions/projects/create_spec.rb b/spec/actions/projects/create_spec.rb
index d7e21719..abdaa890 100644
--- a/spec/actions/projects/create_spec.rb
+++ b/spec/actions/projects/create_spec.rb
@@ -74,6 +74,67 @@ RSpec.describe Projects::Create do
expect(subject.project.build_configuration.provider_id).to eq(provider.id)
end
end
+
+ context 'with buildpacks' do
+ let(:params) do
+ ActionController::Parameters.new({
+ project: {
+ name: 'example-repo',
+ branch: 'main',
+ cluster_id: cluster.id,
+ repository_url: 'example/repo',
+ docker_command: 'rails s',
+ container_registry_url: '',
+ project_credential_provider: {
+ provider_id: provider.id
+ },
+ build_configuration: {
+ driver: 'docker',
+ image_repository: 'example/repo',
+ provider_id: provider.id,
+ build_type: 'buildpacks',
+ buildpack_base_builder: 'paketobuildpacks/builder:full',
+ build_packs_attributes: {
+ '0' => {
+ namespace: 'paketo-buildpacks',
+ name: 'ruby',
+ version: '',
+ reference_type: 'registry'
+ },
+ '1' => {
+ namespace: 'paketo-buildpacks',
+ name: 'nodejs',
+ version: '1.2.3',
+ reference_type: 'registry'
+ }
+ }
+ }
+ }
+ })
+ end
+
+ it 'creates build packs associated with build configuration' do
+ expect(subject).to be_success
+ expect(subject.project.build_configuration).to be_persisted
+ expect(subject.project.build_configuration.build_type).to eq('buildpacks')
+ expect(subject.project.build_configuration.buildpack_base_builder).to eq('paketobuildpacks/builder:full')
+
+ build_packs = subject.project.build_configuration.build_packs
+ expect(build_packs.count).to eq(2)
+
+ first_pack = build_packs.first
+ expect(first_pack.namespace).to eq('paketo-buildpacks')
+ expect(first_pack.name).to eq('ruby')
+ expect(first_pack.version).to eq('')
+ expect(first_pack.reference_type).to eq('registry')
+
+ second_pack = build_packs.second
+ expect(second_pack.namespace).to eq('paketo-buildpacks')
+ expect(second_pack.name).to eq('nodejs')
+ expect(second_pack.version).to eq('1.2.3')
+ expect(second_pack.reference_type).to eq('registry')
+ end
+ end
end
context 'for docker hub' do
@@ -97,6 +158,7 @@ RSpec.describe Projects::Create do
expect(subject).to eq([
Projects::ValidateGitRepository,
Projects::ValidateNamespaceAvailability,
+ Projects::InitializeBuildPacks,
Projects::Save,
Projects::RegisterGitWebhook
])
@@ -112,6 +174,7 @@ RSpec.describe Projects::Create do
expect(subject).to eq([
Projects::ValidateGitRepository,
Projects::ValidateNamespaceAvailability,
+ Projects::InitializeBuildPacks,
Projects::Save
])
end
diff --git a/spec/actions/projects/initialize_build_packs_spec.rb b/spec/actions/projects/initialize_build_packs_spec.rb
new file mode 100644
index 00000000..55ff5260
--- /dev/null
+++ b/spec/actions/projects/initialize_build_packs_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Projects::InitializeBuildPacks do
+ let(:provider) { create(:provider, :github) }
+ let(:project) { create(:project) }
+ let(:build_configuration) do
+ create(:build_configuration,
+ project: project,
+ provider: provider,
+ build_type: :buildpacks,
+ buildpack_base_builder: 'paketobuildpacks/builder:full')
+ end
+
+ let(:params) do
+ ActionController::Parameters.new({
+ project: {
+ build_configuration: {
+ build_packs_attributes: {
+ '0' => {
+ namespace: 'paketo-buildpacks',
+ name: 'ruby',
+ version: '0.47.7',
+ reference_type: 'registry'
+ },
+ '1' => {
+ namespace: 'paketo-buildpacks',
+ name: 'nodejs',
+ version: '',
+ reference_type: 'registry'
+ }
+ }
+ }
+ }
+ })
+ end
+
+ let(:context) do
+ {
+ build_configuration: build_configuration,
+ params: params
+ }
+ end
+
+ subject { described_class.execute(context) }
+
+ it 'builds build packs from attributes' do
+ expect { subject }.to change { build_configuration.build_packs.size }.from(0).to(2)
+
+ first_pack = build_configuration.build_packs[0]
+ expect(first_pack.namespace).to eq('paketo-buildpacks')
+ expect(first_pack.name).to eq('ruby')
+ expect(first_pack.version).to eq('0.47.7')
+ expect(first_pack.reference_type).to eq('registry')
+
+ second_pack = build_configuration.build_packs[1]
+ expect(second_pack.namespace).to eq('paketo-buildpacks')
+ expect(second_pack.name).to eq('nodejs')
+ expect(second_pack.version).to eq('')
+ expect(second_pack.reference_type).to eq('registry')
+ end
+end
diff --git a/spec/actions/projects/update_build_packs_spec.rb b/spec/actions/projects/update_build_packs_spec.rb
new file mode 100644
index 00000000..1491ab93
--- /dev/null
+++ b/spec/actions/projects/update_build_packs_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Projects::UpdateBuildPacks do
+ let(:provider) { create(:provider, :github) }
+ let(:project) { create(:project) }
+ let(:build_configuration) do
+ create(:build_configuration,
+ project: project,
+ provider: provider,
+ build_type: :buildpacks,
+ buildpack_base_builder: 'paketobuildpacks/builder:full')
+ end
+
+ let!(:existing_ruby) do
+ create(
+ :build_pack,
+ build_configuration: build_configuration,
+ namespace: 'paketo-buildpacks',
+ name: 'ruby',
+ reference_type: 'registry',
+ build_order: 0,
+ )
+ end
+
+ let!(:existing_nodejs) do
+ create(
+ :build_pack,
+ build_configuration: build_configuration,
+ namespace: 'paketo-buildpacks',
+ name: 'nodejs',
+ reference_type: 'registry',
+ build_order: 1,
+ )
+ end
+
+ let(:params) do
+ ActionController::Parameters.new({
+ project: {
+ build_configuration: {
+ build_packs_attributes: [
+ {
+ namespace: 'paketo-buildpacks',
+ name: 'go',
+ version: '',
+ reference_type: 'registry'
+ },
+ {
+ namespace: 'paketo-buildpacks',
+ name: 'ruby',
+ version: '',
+ reference_type: 'registry'
+ },
+ ]
+ }
+ }
+ })
+ end
+
+ let(:context) do
+ {
+ build_configuration: build_configuration,
+ params: params
+ }
+ end
+
+ let(:buildpack_details_result) do
+ double(
+ result: Buildpacks::Details::BuildpackDetailsResult.new(
+ latest: {
+ version: '0.47.7',
+ namespace: 'paketo-buildpacks',
+ name: 'go',
+ description: 'Go buildpack'
+ },
+ versions: []
+ )
+ )
+ end
+
+ before do
+ allow(Buildpacks::Details).to receive(:execute).and_return(buildpack_details_result)
+ end
+
+ it 'keeps existing build packs, creates new ones, and deletes missing ones' do
+ expect(build_configuration.build_packs.map(&:key)).to eq(['paketo-buildpacks/ruby', 'paketo-buildpacks/nodejs'])
+ described_class.execute(context)
+ build_configuration.build_packs.reload
+ expect(build_configuration.build_packs.map(&:key)).to eq(['paketo-buildpacks/go', 'paketo-buildpacks/ruby'])
+ end
+end
+
diff --git a/spec/actions/projects/update_spec.rb b/spec/actions/projects/update_spec.rb
index 7631224c..af848181 100644
--- a/spec/actions/projects/update_spec.rb
+++ b/spec/actions/projects/update_spec.rb
@@ -9,10 +9,8 @@ RSpec.describe Projects::Update do
name: 'original-name',
branch: 'main',
cluster: cluster,
- docker_build_context_directory: '.',
repository_url: 'original/repo',
docker_command: 'rails s',
- dockerfile_path: 'Dockerfile',
)
end
@@ -24,10 +22,12 @@ RSpec.describe Projects::Update do
name: 'updated-name',
branch: 'develop',
cluster_id: cluster.id,
- docker_build_context_directory: './app',
repository_url: 'updated/repo',
docker_command: 'bundle exec rails s',
- dockerfile_path: 'docker/Dockerfile'
+ build_configuration: {
+ context_directory: './app',
+ dockerfile_path: 'docker/Dockerfile'
+ }
}
})
end
@@ -39,10 +39,10 @@ RSpec.describe Projects::Update do
expect(result).to be_success
expect(result.project.name).to eq('updated-name')
expect(result.project.branch).to eq('develop')
- expect(result.project.docker_build_context_directory).to eq('./app')
+ expect(result.project.build_configuration.context_directory).to eq('./app')
expect(result.project.repository_url).to eq('updated/repo')
expect(result.project.docker_command).to eq('bundle exec rails s')
- expect(result.project.dockerfile_path).to eq('docker/Dockerfile')
+ expect(result.project.build_configuration.dockerfile_path).to eq('docker/Dockerfile')
end
it 'strips and downcases repository_url' do
@@ -64,7 +64,9 @@ RSpec.describe Projects::Update do
driver: 'k8s',
build_cloud_id: build_cloud.id,
provider_id: build_provider.id,
- image_repository: 'updated/repo'
+ image_repository: 'updated/repo',
+ context_directory: './app',
+ dockerfile_path: 'docker/Dockerfile'
}
}
})
diff --git a/spec/factories/build_configurations.rb b/spec/factories/build_configurations.rb
index fe6df744..d93a2bb9 100644
--- a/spec/factories/build_configurations.rb
+++ b/spec/factories/build_configurations.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
#
@@ -29,6 +32,9 @@ FactoryBot.define do
provider
project
driver { :docker }
+ build_type { :dockerfile }
image_repository { "CanineHQ/canine" }
+ context_directory { "." }
+ dockerfile_path { "./Dockerfile" }
end
end
diff --git a/spec/factories/build_packs.rb b/spec/factories/build_packs.rb
index fed04b69..529c8326 100644
--- a/spec/factories/build_packs.rb
+++ b/spec/factories/build_packs.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
@@ -26,7 +27,7 @@
FactoryBot.define do
factory :build_pack do
build_configuration
- reference_type { :registry }
+ reference_type { "registry" }
namespace { "paketo-buildpacks" }
name { "ruby" }
version { "0.47.7" }
@@ -40,25 +41,19 @@ FactoryBot.define do
end
trait :git do
- reference_type { :git }
+ reference_type { "git" }
namespace { nil }
name { nil }
+ version { nil }
uri { "https://github.com/DataDog/heroku-buildpack-datadog.git" }
details { {} }
end
- trait :docker do
- reference_type { :docker }
- namespace { nil }
- name { nil }
- uri { "docker://paketobuildpacks/ruby:0.47.7" }
- details { {} }
- end
-
trait :url do
- reference_type { :url }
+ reference_type { "url" }
namespace { nil }
name { nil }
+ version { nil }
uri { "https://github.com/heroku/buildpacks-ruby/releases/download/v0.1.0/buildpack.tgz" }
details { {} }
end
diff --git a/spec/models/build_configuration_spec.rb b/spec/models/build_configuration_spec.rb
index ce3b99a1..53f58205 100644
--- a/spec/models/build_configuration_spec.rb
+++ b/spec/models/build_configuration_spec.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
#
diff --git a/spec/models/build_pack_spec.rb b/spec/models/build_pack_spec.rb
index f3fd659c..4fca6911 100644
--- a/spec/models/build_pack_spec.rb
+++ b/spec/models/build_pack_spec.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
@@ -28,7 +29,7 @@ require 'rails_helper'
RSpec.describe BuildPack, type: :model do
describe '#reference' do
context 'registry buildpack' do
- let(:build_pack) { create(:build_pack, namespace: 'paketo-buildpacks', name: 'ruby') }
+ let(:build_pack) { create(:build_pack) }
context 'when version is present' do
before { build_pack.update(version: '0.47.7') }
@@ -55,14 +56,6 @@ RSpec.describe BuildPack, type: :model do
end
end
- context 'docker buildpack' do
- let(:build_pack) { create(:build_pack, :docker) }
-
- it 'returns the docker URI' do
- expect(build_pack.reference).to eq('docker://paketobuildpacks/ruby:0.47.7')
- end
- end
-
context 'url buildpack' do
let(:build_pack) { create(:build_pack, :url) }
@@ -91,7 +84,7 @@ RSpec.describe BuildPack, type: :model do
describe '#display_name' do
it 'returns namespace/name for registry buildpacks' do
- build_pack = create(:build_pack, namespace: 'paketo-buildpacks', name: 'ruby')
+ build_pack = create(:build_pack)
expect(build_pack.display_name).to eq('paketo-buildpacks/ruby')
end
diff --git a/yarn.lock b/yarn.lock
index e63b7c53..8df07aee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -97,11 +97,126 @@
style-mod "^4.1.0"
w3c-keyname "^2.2.4"
+"@esbuild/aix-ppc64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz#b57697945b50e99007b4c2521507dc613d4a648c"
+ integrity sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==
+
+"@esbuild/android-arm64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz#1add7e0af67acefd556e407f8497e81fddad79c0"
+ integrity sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==
+
+"@esbuild/android-arm@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.0.tgz#ab7263045fa8e090833a8e3c393b60d59a789810"
+ integrity sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==
+
+"@esbuild/android-x64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.0.tgz#e8f8b196cfdfdd5aeaebbdb0110983460440e705"
+ integrity sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==
+
"@esbuild/darwin-arm64@0.24.0":
version "0.24.0"
resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz"
integrity sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==
+"@esbuild/darwin-x64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz#33087aab31a1eb64c89daf3d2cf8ce1775656107"
+ integrity sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==
+
+"@esbuild/freebsd-arm64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz#bb76e5ea9e97fa3c753472f19421075d3a33e8a7"
+ integrity sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==
+
+"@esbuild/freebsd-x64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz#e0e2ce9249fdf6ee29e5dc3d420c7007fa579b93"
+ integrity sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==
+
+"@esbuild/linux-arm64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz#d1b2aa58085f73ecf45533c07c82d81235388e75"
+ integrity sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==
+
+"@esbuild/linux-arm@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz#8e4915df8ea3e12b690a057e77a47b1d5935ef6d"
+ integrity sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==
+
+"@esbuild/linux-ia32@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz#8200b1110666c39ab316572324b7af63d82013fb"
+ integrity sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==
+
+"@esbuild/linux-loong64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz#6ff0c99cf647504df321d0640f0d32e557da745c"
+ integrity sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==
+
+"@esbuild/linux-mips64el@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz#3f720ccd4d59bfeb4c2ce276a46b77ad380fa1f3"
+ integrity sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==
+
+"@esbuild/linux-ppc64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz#9d6b188b15c25afd2e213474bf5f31e42e3aa09e"
+ integrity sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==
+
+"@esbuild/linux-riscv64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz#f989fdc9752dfda286c9cd87c46248e4dfecbc25"
+ integrity sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==
+
+"@esbuild/linux-s390x@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz#29ebf87e4132ea659c1489fce63cd8509d1c7319"
+ integrity sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==
+
+"@esbuild/linux-x64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz#4af48c5c0479569b1f359ffbce22d15f261c0cef"
+ integrity sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==
+
+"@esbuild/netbsd-x64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz#1ae73d23cc044a0ebd4f198334416fb26c31366c"
+ integrity sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==
+
+"@esbuild/openbsd-arm64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz#5d904a4f5158c89859fd902c427f96d6a9e632e2"
+ integrity sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==
+
+"@esbuild/openbsd-x64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz#4c8aa88c49187c601bae2971e71c6dc5e0ad1cdf"
+ integrity sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==
+
+"@esbuild/sunos-x64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz#8ddc35a0ea38575fa44eda30a5ee01ae2fa54dd4"
+ integrity sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==
+
+"@esbuild/win32-arm64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz#6e79c8543f282c4539db684a207ae0e174a9007b"
+ integrity sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==
+
+"@esbuild/win32-ia32@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz#057af345da256b7192d18b676a02e95d0fa39103"
+ integrity sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==
+
+"@esbuild/win32-x64@0.24.0":
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz#168ab1c7e1c318b922637fad8f339d48b01e1244"
+ integrity sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==
+
"@gorails/ninja-keys@^1.2.1":
version "1.2.1"
resolved "https://registry.npmjs.org/@gorails/ninja-keys/-/ninja-keys-1.2.1.tgz"
@@ -111,23 +226,23 @@
hotkeys-js "3.8.9"
lit "2.2.2"
-"@hotwired/stimulus@^3.0.0", "@hotwired/stimulus@^3.2.1", "@hotwired/stimulus@^3.2.2", "@hotwired/stimulus@>= 3.0.0":
+"@hotwired/stimulus@^3.0.0", "@hotwired/stimulus@^3.2.2":
version "3.2.2"
resolved "https://registry.npmjs.org/@hotwired/stimulus/-/stimulus-3.2.2.tgz"
integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==
-"@hotwired/turbo-rails@^8.0.10":
- version "8.0.10"
- resolved "https://registry.npmjs.org/@hotwired/turbo-rails/-/turbo-rails-8.0.10.tgz"
- integrity sha512-BkERfjTbNwMb9/YQi0RL9+f9zkD+dZH2klEONtGwXrIE3O9BE1937Nn9++koZpDryD4XN3zE5U5ibyWoYJAWBg==
+"@hotwired/turbo-rails@^8.0.20":
+ version "8.0.20"
+ resolved "https://registry.yarnpkg.com/@hotwired/turbo-rails/-/turbo-rails-8.0.20.tgz#a6f6f78591e9868ca1e5e67f4c7d453dbd49a475"
+ integrity sha512-4aYkYF9XMKL7ZZPfgElq15+60osZOwMwhztE4myKQYEzCPvaPUxwZH301tOrBNtWUwOD+TNOm1Hrpeaq22RX9A==
dependencies:
- "@hotwired/turbo" "^8.0.6"
- "@rails/actioncable" "^7.0"
+ "@hotwired/turbo" "^8.0.20"
+ "@rails/actioncable" ">=7.0"
-"@hotwired/turbo@^8.0.6":
- version "8.0.10"
- resolved "https://registry.npmjs.org/@hotwired/turbo/-/turbo-8.0.10.tgz"
- integrity sha512-xen1YhNQirAHlA8vr/444XsTNITC1Il2l/Vx4w8hAWPpI5nQO78mVHNsmFuayETodzPwh25ob2TgfCEV/Loiog==
+"@hotwired/turbo@^8.0.20":
+ version "8.0.20"
+ resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-8.0.20.tgz#068ede648c4db09fed4cf0ac0266788056673f2f"
+ integrity sha512-IilkH/+h92BRLeY/rMMR3MUh1gshIfdra/qZzp/Bl5FmiALD/6sQZK/ecxSbumeyOYiWr/JRI+Au1YQmkJGnoA==
"@iconify/tailwind@^1.1.3":
version "1.1.3"
@@ -251,7 +366,7 @@
"@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9"
-"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5"
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
@@ -286,7 +401,12 @@
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
-"@rails/actioncable@^7.0", "@rails/actioncable@^7.1.0":
+"@rails/actioncable@>=7.0":
+ version "8.1.100"
+ resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-8.1.100.tgz#b1f85f3482425fb91e5eba1deb55152ee0bb2f85"
+ integrity sha512-j4vJQqz51CDVYv2UafKRu4jiZi5/gTnm7NkyL+VMIgEw3s8jtVtmzu9uItUaZccUg9NJ6o05yVyBAHxNfTuCRA==
+
+"@rails/actioncable@^7.1.0":
version "7.2.100"
resolved "https://registry.npmjs.org/@rails/actioncable/-/actioncable-7.2.100.tgz"
integrity sha512-7xtIENf0Yw59AFDM3+xqxPCZxev3QVAqjPmUzmgsB9eL8S/zTpB0IU9srNc7XknzJI4e09XKNnCaJRx3gfYzXA==
@@ -298,10 +418,10 @@
dependencies:
spark-md5 "^3.0.1"
-"@rails/request.js@^0.0.11":
- version "0.0.11"
- resolved "https://registry.npmjs.org/@rails/request.js/-/request.js-0.0.11.tgz"
- integrity sha512-2U3uYS0kbljt+pAstN+LIlZOl7xmOKig5N6FrvtUWO1wq0zR1Hf90fHfD2SYiyV8yH1nyKpoTmbLqWT0xe1zDg==
+"@rails/request.js@^0.0.12":
+ version "0.0.12"
+ resolved "https://registry.yarnpkg.com/@rails/request.js/-/request.js-0.0.12.tgz#3d1f73e7585141d9c4c2149a34476d128eb900bc"
+ integrity sha512-g3//JBja1s04Zflj7IoMLQuXza9i4ZvtLmm0r0dMwh1QQUs6rL2iKUOGGyERfLsd81SnXC5ucfVV//rtsDlEEA==
"@rails/ujs@^7.1.3-4":
version "7.1.3-4"
@@ -313,6 +433,11 @@
resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz"
integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==
+"@stimulus-components/sortable@^5.0.3":
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/@stimulus-components/sortable/-/sortable-5.0.3.tgz#b8b64f954a05497fa6ca00f21557d466a2ebe47b"
+ integrity sha512-GSiu4CX2irR3gIuo3nsSNXlJsEvXVurC2caNTNshlGbsP0sHb70bLn7iiVib7iGSmLJdXdU6wCpD3AEnMKCt2Q==
+
"@tailwindcss/aspect-ratio@^0.4.0":
version "0.4.2"
resolved "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz"
@@ -447,7 +572,7 @@ braces@^3.0.3, braces@~3.0.2:
dependencies:
fill-range "^7.1.1"
-browserslist@^4.23.3, "browserslist@>= 4.21.0":
+browserslist@^4.23.3:
version "4.24.0"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz"
integrity sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==
@@ -467,7 +592,7 @@ caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001663:
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz"
integrity sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==
-chart.js@^4.4.6, chart.js@>=2.8.0, chart.js@4:
+chart.js@4, chart.js@^4.4.6:
version "4.4.6"
resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz"
integrity sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==
@@ -488,37 +613,7 @@ chartkick@^5.0.1:
chartjs-adapter-date-fns ">=3"
date-fns ">=2"
-chokidar@^3.3.0:
- version "3.6.0"
- resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
- integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
- dependencies:
- anymatch "~3.1.2"
- braces "~3.0.2"
- glob-parent "~5.1.2"
- is-binary-path "~2.1.0"
- is-glob "~4.0.1"
- normalize-path "~3.0.0"
- readdirp "~3.6.0"
- optionalDependencies:
- fsevents "~2.3.2"
-
-chokidar@^3.5.2:
- version "3.6.0"
- resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
- integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
- dependencies:
- anymatch "~3.1.2"
- braces "~3.0.2"
- glob-parent "~5.1.2"
- is-binary-path "~2.1.0"
- is-glob "~4.0.1"
- normalize-path "~3.0.0"
- readdirp "~3.6.0"
- optionalDependencies:
- fsevents "~2.3.2"
-
-chokidar@^3.5.3:
+chokidar@^3.3.0, chokidar@^3.5.2, chokidar@^3.5.3:
version "3.6.0"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
@@ -635,7 +730,7 @@ daisyui@^4.12.10:
picocolors "^1"
postcss-js "^4"
-date-fns@>=2, date-fns@>=2.0.0:
+date-fns@>=2:
version "4.1.0"
resolved "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz"
integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
@@ -699,7 +794,7 @@ esbuild-rails@^1.0.7:
dependencies:
fast-glob "^3.2.12"
-esbuild@*, esbuild@^0.24.0:
+esbuild@^0.24.0:
version "0.24.0"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz"
integrity sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==
@@ -764,7 +859,7 @@ fill-range@^7.1.1:
dependencies:
to-regex-range "^5.0.1"
-flatpickr@^4.6.10, flatpickr@>=4.6.2:
+flatpickr@^4.6.10:
version "4.6.13"
resolved "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz"
integrity sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==
@@ -943,7 +1038,7 @@ jackspeak@^3.1.2:
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"
-jiti@^1.21.0, jiti@>=1.21.0:
+jiti@^1.21.0:
version "1.21.6"
resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz"
integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
@@ -1000,15 +1095,6 @@ lit-html@^2.2.0, lit-html@^2.8.0:
dependencies:
"@types/trusted-types" "^2.0.2"
-lit@^2.0.0:
- version "2.8.0"
- resolved "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz"
- integrity sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==
- dependencies:
- "@lit/reactive-element" "^1.6.0"
- lit-element "^3.3.0"
- lit-html "^2.8.0"
-
lit@2.2.2:
version "2.2.2"
resolved "https://registry.npmjs.org/lit/-/lit-2.2.2.tgz"
@@ -1018,6 +1104,15 @@ lit@2.2.2:
lit-element "^3.2.0"
lit-html "^2.2.0"
+lit@^2.0.0:
+ version "2.8.0"
+ resolved "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz"
+ integrity sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==
+ dependencies:
+ "@lit/reactive-element" "^1.6.0"
+ lit-element "^3.3.0"
+ lit-html "^2.8.0"
+
local-time@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/local-time/-/local-time-3.0.2.tgz"
@@ -1270,14 +1365,6 @@ postcss-reporter@^7.0.0:
picocolors "^1.0.0"
thenby "^1.3.4"
-postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.1.1:
- version "6.1.2"
- resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz"
- integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
- dependencies:
- cssesc "^3.0.0"
- util-deprecate "^1.0.2"
-
postcss-selector-parser@6.0.10:
version "6.0.10"
resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz"
@@ -1286,12 +1373,20 @@ postcss-selector-parser@6.0.10:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
+postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.1.1:
+ version "6.1.2"
+ resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz"
+ integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
+ dependencies:
+ cssesc "^3.0.0"
+ util-deprecate "^1.0.2"
+
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
-postcss@^8.0.0, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.47, postcss@>=8.0.9:
+postcss@^8.4.23, postcss@^8.4.47:
version "8.4.47"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz"
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
@@ -1305,7 +1400,7 @@ prettier-plugin-tailwindcss@^0.6.8:
resolved "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.8.tgz"
integrity sha512-dGu3kdm7SXPkiW4nzeWKCl3uoImdd5CTZEJGxyypEPL37Wj0HT2pLqjrvSei1nTeuQfO4PUfjeW5cTUNRLZ4sA==
-prettier@^3.0, prettier@^3.3.3:
+prettier@^3.3.3:
version "3.3.3"
resolved "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz"
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
@@ -1423,7 +1518,12 @@ slash@^5.0.0, slash@^5.1.0:
resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz"
integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==
-source-map-js@^1.2.1, "source-map-js@>=0.6.2 <2.0.0":
+sortablejs@^1.15.6:
+ version "1.15.6"
+ resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.6.tgz#ff93699493f5b8ab8d828f933227b4988df1d393"
+ integrity sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==
+
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
@@ -1544,7 +1644,7 @@ svg.filter.js@^2.0.2:
dependencies:
svg.js "^2.2.5"
-svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5, svg.js@>=2.3.x:
+svg.js@>=2.3.x, svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5:
version "2.7.1"
resolved "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz"
integrity sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==
@@ -1583,7 +1683,7 @@ tailwindcss-stimulus-components@^5.1.0:
resolved "https://registry.npmjs.org/tailwindcss-stimulus-components/-/tailwindcss-stimulus-components-5.1.1.tgz"
integrity sha512-9e3H9WLZoGzWQBVOdYvxqM42MsJ4dfkfdrs3NGV+HYvHbIcF9tukl2MPKVZpBv/pP+puZbJv0lFgdn79IGVApA==
-tailwindcss@^3.4.1, "tailwindcss@>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1", "tailwindcss@>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20", "tailwindcss@>=3.0.0 || insiders || >=4.0.0-alpha.20":
+tailwindcss@^3.4.1:
version "3.4.13"
resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz"
integrity sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==