diff --git a/cmd/info.go b/cmd/info.go index 35c779d..1494590 100644 --- a/cmd/info.go +++ b/cmd/info.go @@ -71,6 +71,7 @@ func (i info) Run(context wisski_distillery.Context) (err error) { context.Printf("Last Update: %v\n", info.LastUpdate.String()) context.Printf("Last Cron: %v\n", info.LastCron.String()) + context.Printf("Drupal Version: %v\n", info.DrupalVersion) context.Printf("Theme: %v\n", info.Theme) context.Printf("Bundles: (count %d)\n", info.Statistics.Bundles.TotalBundles) diff --git a/cmd/provision.go b/cmd/provision.go index 2744efa..3537d67 100644 --- a/cmd/provision.go +++ b/cmd/provision.go @@ -1,10 +1,13 @@ package cmd import ( + "encoding/json" + wisski_distillery "github.com/FAU-CDI/wisski-distillery" "github.com/FAU-CDI/wisski-distillery/internal/cli" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/provision" "github.com/FAU-CDI/wisski-distillery/internal/models" + "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/manager" "github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/tkw1536/goprogram/exit" ) @@ -15,12 +18,26 @@ var Provision wisski_distillery.Command = pv{} type pv struct { PHPVersion string `short:"p" long:"php" description:"specific php version to use for instance. Should be one of '8.0', '8.1'."` OPCacheDevelopment bool `short:"o" long:"opcache-devel" description:"Include opcache development configuration"` + Flavor string `short:"f" long:"flavor" description:"Use specific flavor. Use '--list-flavors' to list flavors. "` + ListFlavors bool `short:"l" long:"list-flavors" description:"List all known flavors"` ContentSecurityPolicy string `short:"c" long:"content-security-policy" description:"Setup ContentSecurityPolicy"` Positionals struct { - Slug string `positional-arg-name:"slug" required:"1-1" description:"slug of instance to create"` + Slug string `positional-arg-name:"slug" description:"slug of instance to create"` } `positional-args:"true"` } +var errMissingSlug = exit.Error{ + ExitCode: exit.ExitCommandArguments, + Message: "must provide a slug", +} + +func (pv pv) AfterParse() error { + if !pv.ListFlavors && pv.Positionals.Slug == "" { + return errMissingSlug + } + return nil +} + func (pv) Description() wisski_distillery.Description { return wisski_distillery.Description{ Requirements: cli.Requirements{ @@ -39,8 +56,13 @@ var errProvisionGeneric = exit.Error{ // TODO: AfterParse to check instance! func (p pv) Run(context wisski_distillery.Context) error { + if p.ListFlavors { + return p.listFlavors(context) + } + instance, err := context.Environment.Provision().Provision(context.Stderr, context.Context, provision.Flags{ - Slug: p.Positionals.Slug, + Slug: p.Positionals.Slug, + Flavor: p.Flavor, System: models.System{ PHP: p.PHPVersion, OpCacheDevelopment: p.OPCacheDevelopment, @@ -48,7 +70,7 @@ func (p pv) Run(context wisski_distillery.Context) error { }, }) if err != nil { - return errProvisionGeneric.WithMessageF(p.Positionals.Slug).Wrap(err) + return errProvisionGeneric.WithMessageF(p.Positionals.Slug).WrapError(err) } // and we're done! @@ -59,3 +81,10 @@ func (p pv) Run(context wisski_distillery.Context) error { return nil } + +func (pv) listFlavors(context wisski_distillery.Context) error { + encoder := json.NewEncoder(context.Stdout) + encoder.SetIndent("", " ") + encoder.Encode(manager.Profiles()) + return nil +} diff --git a/internal/dis/component/provision/provision.go b/internal/dis/component/provision/provision.go index e186730..6ec008c 100644 --- a/internal/dis/component/provision/provision.go +++ b/internal/dis/component/provision/provision.go @@ -30,28 +30,47 @@ type Flags struct { // Slug is the slug of the wisski instance Slug string + // Flavor is the name of the profile to use + Flavor string `json:",omitempty"` + // System is information about the system System models.System } // Profile returns the profile belonging to this provision flags. -func (flags Flags) Profile() manager.Profile { - // TODO: Actually do something here - return manager.Profile{} +func (flags Flags) Profile() (profile manager.Profile) { + // if no flavor was given, apply the default profile + if flags.Flavor == "" { + profile.Apply(manager.LoadDefaultProfile()) + return + } + + // load the selector profile! + profile.Apply(manager.LoadProfile(flags.Flavor)) + return } var ErrInstanceAlreadyExists = errors.New("instance with provided slug already exists") -func (pv *Provision) Validate(flags Flags) error { +func (pv *Provision) validate(flags Flags) error { // check the slug if _, err := pv.dependencies.Instances.IsValidSlug(flags.Slug); err != nil { return err } + // check that we know the flavor + if flags.Flavor != "" && !manager.HasProfile(flags.Flavor) { + return fmt.Errorf("unknown flavor %q", flags.Flavor) + } return nil } // Provision provisions a new docker compose instance. func (pv *Provision) Provision(progress io.Writer, ctx context.Context, flags Flags) (*wisski.WissKI, error) { + // validate that everything is correct + if err := pv.validate(flags); err != nil { + return nil, err + } + // check that it doesn't already exist logging.LogMessage(progress, "Provisioning new WissKI instance %s", flags.Slug) if exists, err := pv.dependencies.Instances.Has(ctx, flags.Slug); err != nil || exists { diff --git a/internal/dis/component/server/admin/admin.go b/internal/dis/component/server/admin/admin.go index 079e582..bd56c73 100644 --- a/internal/dis/component/server/admin/admin.go +++ b/internal/dis/component/server/admin/admin.go @@ -69,11 +69,10 @@ var ( menuProvision = component.MenuItem{Title: "Provision", Path: "/admin/instances/provision/"} - menuInstances = component.MenuItem{Title: "Instances", Path: "/admin/instances/"} - menuInstance = component.DummyMenuItem() - menuRebuild = component.DummyMenuItem() - menuGrants = component.DummyMenuItem() - menuIngredients = component.DummyMenuItem() + menuInstances = component.MenuItem{Title: "Instances", Path: "/admin/instances/"} + menuInstance = component.DummyMenuItem() + menuRebuild = component.DummyMenuItem() + menuGrants = component.DummyMenuItem() ) func (admin *Admin) HandleRoute(ctx context.Context, route string) (handler http.Handler, err error) { diff --git a/internal/dis/component/server/admin/html/instance.html b/internal/dis/component/server/admin/html/instance.html index 3c7d61c..4e47d68 100644 --- a/internal/dis/component/server/admin/html/instance.html +++ b/internal/dis/component/server/admin/html/instance.html @@ -91,14 +91,6 @@ - - - PHP Version - - - {{ .Instance.System.PHP }} - - Docker Base Image @@ -107,6 +99,14 @@ {{ .Instance.System.GetDockerBaseImage }} + + + PHP Version + + + {{ .Instance.System.PHP }} + + OPCache Development Config @@ -143,36 +143,20 @@ - Theme + Drupal Version + + + {{ .Info.DrupalVersion }} + + + + + Default Theme {{ .Info.Theme }} - - - Docker Base Image - - - {{ .Instance.System.GetDockerBaseImage }} - - - - - OPCache Development Config - - - {{ .Instance.System.OpCacheDevelopment }} - - - - - Content Security Policy - - - {{ .Instance.System.ContentSecurityPolicy }} - - diff --git a/internal/dis/component/server/admin/html/instance_system.html b/internal/dis/component/server/admin/html/instance_system.html index e88d0ae..6b72e61 100644 --- a/internal/dis/component/server/admin/html/instance_system.html +++ b/internal/dis/component/server/admin/html/instance_system.html @@ -67,12 +67,23 @@ {{ if not $rebuild }}
-
Profile
+
Flavor
-
- In the future, it will be possible to configure the Drupal, WissKI and Module versions here. - But this is not yet implemented. +
+ + Determine the set of module(s) to install for this WissKI.
+ Changing this after installation may not be possible and in any case requires manual intervention. +
+ + {{ $defaultProfile := .DefaultProfile }} + {{ range $name, $description := .Profiles }} + + {{ end }}
{{ end }} diff --git a/internal/dis/component/server/admin/instance.go b/internal/dis/component/server/admin/instance.go index 9968d86..961a622 100644 --- a/internal/dis/component/server/admin/instance.go +++ b/internal/dis/component/server/admin/instance.go @@ -43,7 +43,6 @@ func (admin *Admin) instance(ctx context.Context) http.Handler { templating.Actions( menuRebuild, menuGrants, - menuIngredients, ), ) diff --git a/internal/dis/component/server/admin/instance_provision.go b/internal/dis/component/server/admin/instance_provision.go index 551f7fd..2f33d05 100644 --- a/internal/dis/component/server/admin/instance_provision.go +++ b/internal/dis/component/server/admin/instance_provision.go @@ -6,6 +6,8 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" + "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/manager" + "github.com/tkw1536/pkglib/collection" _ "embed" ) @@ -26,6 +28,8 @@ func (admin *Admin) instanceProvision(ctx context.Context) http.Handler { return tpl.HTMLHandler(func(r *http.Request) (ipc instanceSystemContext, err error) { ipc.prepare(false) + ipc.DefaultProfile = manager.DefaultProfile() + ipc.Profiles = collection.MapValues(manager.Profiles(), func(_ string, profile manager.Profile) string { return profile.Description }) return ipc, nil }) } diff --git a/internal/dis/component/server/admin/instance_rebuild.go b/internal/dis/component/server/admin/instance_rebuild.go index 3b586d0..75ec07c 100644 --- a/internal/dis/component/server/admin/instance_rebuild.go +++ b/internal/dis/component/server/admin/instance_rebuild.go @@ -36,6 +36,10 @@ type instanceSystemContext struct { Rebuild bool Slug string System models.System + + // list of known profiles and their descriptions + DefaultProfile string + Profiles map[string]string } // prepare prares the given instanceSystemContent diff --git a/internal/dis/component/server/assets/assets_disclaimer.txt b/internal/dis/component/server/assets/assets_disclaimer.txt index 6449aee..38d5be0 100644 --- a/internal/dis/component/server/assets/assets_disclaimer.txt +++ b/internal/dis/component/server/assets/assets_disclaimer.txt @@ -423,7 +423,7 @@ SOFTWARE. ----- -The following software may be included in this product: @parcel/bundler-default, @parcel/cache, @parcel/codeframe, @parcel/compressor-raw, @parcel/config-default, @parcel/core, @parcel/diagnostic, @parcel/events, @parcel/fs, @parcel/fs-search, @parcel/graph, @parcel/hash, @parcel/logger, @parcel/markdown-ansi, @parcel/namer-default, @parcel/node-resolver-core, @parcel/optimizer-css, @parcel/optimizer-htmlnano, @parcel/optimizer-image, @parcel/optimizer-svgo, @parcel/optimizer-swc, @parcel/package-manager, @parcel/packager-css, @parcel/packager-html, @parcel/packager-js, @parcel/packager-raw, @parcel/packager-svg, @parcel/plugin, @parcel/profiler, @parcel/reporter-cli, @parcel/reporter-dev-server, @parcel/reporter-tracer, @parcel/resolver-default, @parcel/runtime-browser-hmr, @parcel/runtime-js, @parcel/runtime-react-refresh, @parcel/runtime-service-worker, @parcel/transformer-babel, @parcel/transformer-css, @parcel/transformer-html, @parcel/transformer-image, @parcel/transformer-js, @parcel/transformer-json, @parcel/transformer-postcss, @parcel/transformer-posthtml, @parcel/transformer-raw, @parcel/transformer-react-refresh-wrap, @parcel/transformer-svg, @parcel/types, @parcel/utils, @parcel/watcher, @parcel/watcher-darwin-arm64, @parcel/workers, parcel. A copy of the source code may be downloaded from https://github.com/parcel-bundler/parcel.git (@parcel/bundler-default), https://github.com/parcel-bundler/parcel.git (@parcel/cache), https://github.com/parcel-bundler/parcel.git (@parcel/codeframe), https://github.com/parcel-bundler/parcel.git (@parcel/compressor-raw), https://github.com/parcel-bundler/parcel.git (@parcel/config-default), https://github.com/parcel-bundler/parcel.git (@parcel/core), https://github.com/parcel-bundler/parcel.git (@parcel/diagnostic), https://github.com/parcel-bundler/parcel.git (@parcel/events), https://github.com/parcel-bundler/parcel.git (@parcel/fs), https://github.com/parcel-bundler/parcel.git (@parcel/fs-search), https://github.com/parcel-bundler/parcel.git (@parcel/graph), https://github.com/parcel-bundler/parcel.git (@parcel/hash), https://github.com/parcel-bundler/parcel.git (@parcel/logger), https://github.com/parcel-bundler/parcel.git (@parcel/markdown-ansi), https://github.com/parcel-bundler/parcel.git (@parcel/namer-default), https://github.com/parcel-bundler/parcel.git (@parcel/node-resolver-core), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-css), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-htmlnano), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-image), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-svgo), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-swc), https://github.com/parcel-bundler/parcel.git (@parcel/package-manager), https://github.com/parcel-bundler/parcel.git (@parcel/packager-css), https://github.com/parcel-bundler/parcel.git (@parcel/packager-html), https://github.com/parcel-bundler/parcel.git (@parcel/packager-js), https://github.com/parcel-bundler/parcel.git (@parcel/packager-raw), https://github.com/parcel-bundler/parcel.git (@parcel/packager-svg), https://github.com/parcel-bundler/parcel.git (@parcel/plugin), https://github.com/parcel-bundler/parcel.git (@parcel/profiler), https://github.com/parcel-bundler/parcel.git (@parcel/reporter-cli), https://github.com/parcel-bundler/parcel.git (@parcel/reporter-dev-server), https://github.com/parcel-bundler/parcel.git (@parcel/reporter-tracer), https://github.com/parcel-bundler/parcel.git (@parcel/resolver-default), https://github.com/parcel-bundler/parcel.git (@parcel/runtime-browser-hmr), https://github.com/parcel-bundler/parcel.git (@parcel/runtime-js), https://github.com/parcel-bundler/parcel.git (@parcel/runtime-react-refresh), https://github.com/parcel-bundler/parcel.git (@parcel/runtime-service-worker), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-babel), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-css), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-html), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-image), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-js), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-json), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-postcss), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-posthtml), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-raw), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-react-refresh-wrap), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-svg), https://github.com/parcel-bundler/parcel.git (@parcel/types), https://github.com/parcel-bundler/parcel.git (@parcel/utils), https://github.com/parcel-bundler/watcher.git (@parcel/watcher), https://github.com/parcel-bundler/watcher.git (@parcel/watcher-darwin-arm64), https://github.com/parcel-bundler/parcel.git (@parcel/workers), https://github.com/parcel-bundler/parcel.git (parcel). This software contains the following license and notice below: +The following software may be included in this product: @parcel/bundler-default, @parcel/cache, @parcel/codeframe, @parcel/compressor-raw, @parcel/config-default, @parcel/core, @parcel/diagnostic, @parcel/events, @parcel/fs, @parcel/fs-search, @parcel/graph, @parcel/hash, @parcel/logger, @parcel/markdown-ansi, @parcel/namer-default, @parcel/node-resolver-core, @parcel/optimizer-css, @parcel/optimizer-htmlnano, @parcel/optimizer-image, @parcel/optimizer-svgo, @parcel/optimizer-swc, @parcel/package-manager, @parcel/packager-css, @parcel/packager-html, @parcel/packager-js, @parcel/packager-raw, @parcel/packager-svg, @parcel/plugin, @parcel/profiler, @parcel/reporter-cli, @parcel/reporter-dev-server, @parcel/reporter-tracer, @parcel/resolver-default, @parcel/runtime-browser-hmr, @parcel/runtime-js, @parcel/runtime-react-refresh, @parcel/runtime-service-worker, @parcel/transformer-babel, @parcel/transformer-css, @parcel/transformer-html, @parcel/transformer-image, @parcel/transformer-js, @parcel/transformer-json, @parcel/transformer-postcss, @parcel/transformer-posthtml, @parcel/transformer-raw, @parcel/transformer-react-refresh-wrap, @parcel/transformer-svg, @parcel/types, @parcel/utils, @parcel/watcher, @parcel/watcher-linux-x64-glibc, @parcel/watcher-linux-x64-musl, @parcel/workers, parcel. A copy of the source code may be downloaded from https://github.com/parcel-bundler/parcel.git (@parcel/bundler-default), https://github.com/parcel-bundler/parcel.git (@parcel/cache), https://github.com/parcel-bundler/parcel.git (@parcel/codeframe), https://github.com/parcel-bundler/parcel.git (@parcel/compressor-raw), https://github.com/parcel-bundler/parcel.git (@parcel/config-default), https://github.com/parcel-bundler/parcel.git (@parcel/core), https://github.com/parcel-bundler/parcel.git (@parcel/diagnostic), https://github.com/parcel-bundler/parcel.git (@parcel/events), https://github.com/parcel-bundler/parcel.git (@parcel/fs), https://github.com/parcel-bundler/parcel.git (@parcel/fs-search), https://github.com/parcel-bundler/parcel.git (@parcel/graph), https://github.com/parcel-bundler/parcel.git (@parcel/hash), https://github.com/parcel-bundler/parcel.git (@parcel/logger), https://github.com/parcel-bundler/parcel.git (@parcel/markdown-ansi), https://github.com/parcel-bundler/parcel.git (@parcel/namer-default), https://github.com/parcel-bundler/parcel.git (@parcel/node-resolver-core), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-css), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-htmlnano), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-image), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-svgo), https://github.com/parcel-bundler/parcel.git (@parcel/optimizer-swc), https://github.com/parcel-bundler/parcel.git (@parcel/package-manager), https://github.com/parcel-bundler/parcel.git (@parcel/packager-css), https://github.com/parcel-bundler/parcel.git (@parcel/packager-html), https://github.com/parcel-bundler/parcel.git (@parcel/packager-js), https://github.com/parcel-bundler/parcel.git (@parcel/packager-raw), https://github.com/parcel-bundler/parcel.git (@parcel/packager-svg), https://github.com/parcel-bundler/parcel.git (@parcel/plugin), https://github.com/parcel-bundler/parcel.git (@parcel/profiler), https://github.com/parcel-bundler/parcel.git (@parcel/reporter-cli), https://github.com/parcel-bundler/parcel.git (@parcel/reporter-dev-server), https://github.com/parcel-bundler/parcel.git (@parcel/reporter-tracer), https://github.com/parcel-bundler/parcel.git (@parcel/resolver-default), https://github.com/parcel-bundler/parcel.git (@parcel/runtime-browser-hmr), https://github.com/parcel-bundler/parcel.git (@parcel/runtime-js), https://github.com/parcel-bundler/parcel.git (@parcel/runtime-react-refresh), https://github.com/parcel-bundler/parcel.git (@parcel/runtime-service-worker), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-babel), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-css), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-html), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-image), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-js), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-json), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-postcss), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-posthtml), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-raw), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-react-refresh-wrap), https://github.com/parcel-bundler/parcel.git (@parcel/transformer-svg), https://github.com/parcel-bundler/parcel.git (@parcel/types), https://github.com/parcel-bundler/parcel.git (@parcel/utils), https://github.com/parcel-bundler/watcher.git (@parcel/watcher), https://github.com/parcel-bundler/watcher.git (@parcel/watcher-linux-x64-glibc), https://github.com/parcel-bundler/watcher.git (@parcel/watcher-linux-x64-musl), https://github.com/parcel-bundler/parcel.git (@parcel/workers), https://github.com/parcel-bundler/parcel.git (parcel). This software contains the following license and notice below: MIT License @@ -4426,7 +4426,7 @@ OTHER DEALINGS IN THE SOFTWARE. ----- -The following software may be included in this product: lightningcss, lightningcss-darwin-arm64. A copy of the source code may be downloaded from https://github.com/parcel-bundler/lightningcss.git (lightningcss), https://github.com/parcel-bundler/lightningcss.git (lightningcss-darwin-arm64). This software contains the following license and notice below: +The following software may be included in this product: lightningcss, lightningcss-linux-x64-gnu, lightningcss-linux-x64-musl. A copy of the source code may be downloaded from https://github.com/parcel-bundler/lightningcss.git (lightningcss), https://github.com/parcel-bundler/lightningcss.git (lightningcss-linux-x64-gnu), https://github.com/parcel-bundler/lightningcss.git (lightningcss-linux-x64-musl). This software contains the following license and notice below: Mozilla Public License Version 2.0 ================================== diff --git a/internal/dis/component/server/assets/assets_dist.go b/internal/dis/component/server/assets/assets_dist.go index e739aa3..582015b 100644 --- a/internal/dis/component/server/assets/assets_dist.go +++ b/internal/dis/component/server/assets/assets_dist.go @@ -30,7 +30,7 @@ var AssetsAdmin = Assets{ // AssetsAdminProvision contains assets for the 'AdminProvision' entrypoint. var AssetsAdminProvision = Assets{ - Scripts: ``, + Scripts: ``, Styles: ``, } diff --git a/internal/dis/component/server/assets/dist/AdminProvision.3981a5f2.js b/internal/dis/component/server/assets/dist/AdminProvision.3981a5f2.js deleted file mode 100644 index 200261c..0000000 --- a/internal/dis/component/server/assets/dist/AdminProvision.3981a5f2.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},t={},n={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in t)return t[e].exports;if(e in n){var o=n[e];delete n[e];var r={id:e,exports:{}};return t[e]=r,o.call(r.exports,r,r.exports),r.exports}var i=Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}).register=function(e,t){n[e]=t},e.parcelRequireafa4=o),o("dK5Bi");var r=o("8vh0V");async function i(e){return await new Promise((t,n)=>{(0,r.createModal)("provision",[JSON.stringify(e)],{bufferSize:0,onClose:(o,r)=>{if(!o){n(Error(r??"unspecified error"));return}t(e.Slug)}})})}let l=document.getElementById("system"),a=document.getElementById("slug"),d=document.getElementById("php"),u=document.getElementById("opcacheDevelopment"),c=document.getElementById("contentsecuritypolicy");l.addEventListener("submit",e=>{e.preventDefault(),i({Slug:a.value,System:{PHP:d.value,OpCacheDevelopment:u.checked,ContentSecurityPolicy:c.value}}).then(e=>{location.href="/admin/instance/"+e}).catch(e=>{console.error(e),location.reload()})}),l.querySelector("fieldset")?.removeAttribute("disabled")}(); \ No newline at end of file diff --git a/internal/dis/component/server/assets/dist/AdminProvision.53660f24.js b/internal/dis/component/server/assets/dist/AdminProvision.53660f24.js new file mode 100644 index 0000000..d5d40dd --- /dev/null +++ b/internal/dis/component/server/assets/dist/AdminProvision.53660f24.js @@ -0,0 +1 @@ +!function(){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},t={},n={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in t)return t[e].exports;if(e in n){var o=n[e];delete n[e];var r={id:e,exports:{}};return t[e]=r,o.call(r.exports,r,r.exports),r.exports}var l=Error("Cannot find module '"+e+"'");throw l.code="MODULE_NOT_FOUND",l}).register=function(e,t){n[e]=t},e.parcelRequireafa4=o),o("dK5Bi");var r=o("8vh0V");async function l(e){return await new Promise((t,n)=>{(0,r.createModal)("provision",[JSON.stringify(e)],{bufferSize:0,onClose:(o,r)=>{if(!o){n(Error(r??"unspecified error"));return}t(e.Slug)}})})}let i=document.getElementById("system"),a=document.getElementById("slug"),u=document.getElementById("php"),c=document.getElementById("opcacheDevelopment"),d=document.getElementById("contentsecuritypolicy");i.addEventListener("submit",e=>{e.preventDefault();let t=document.querySelector('input[name="flavor"]:checked'),n=t instanceof HTMLInputElement?t.value:"";l({Slug:a.value,Flavor:n,System:{PHP:u.value,OpCacheDevelopment:c.checked,ContentSecurityPolicy:d.value}}).then(e=>{location.href="/admin/instance/"+e}).catch(e=>{console.error(e),location.reload()})}),i.querySelector("fieldset")?.removeAttribute("disabled")}(); \ No newline at end of file diff --git a/internal/dis/component/server/assets/dist/AdminProvision.5bc6b324.js b/internal/dis/component/server/assets/dist/AdminProvision.5bc6b324.js new file mode 100644 index 0000000..8542508 --- /dev/null +++ b/internal/dis/component/server/assets/dist/AdminProvision.5bc6b324.js @@ -0,0 +1 @@ +var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},t={},n={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in t)return t[e].exports;if(e in n){var o=n[e];delete n[e];var r={id:e,exports:{}};return t[e]=r,o.call(r.exports,r,r.exports),r.exports}var l=Error("Cannot find module '"+e+"'");throw l.code="MODULE_NOT_FOUND",l}).register=function(e,t){n[e]=t},e.parcelRequireafa4=o),o("8xGhL");var r=o("12vpF");async function l(e){return await new Promise((t,n)=>{(0,r.createModal)("provision",[JSON.stringify(e)],{bufferSize:0,onClose:(o,r)=>{if(!o){n(Error(r??"unspecified error"));return}t(e.Slug)}})})}const i=document.getElementById("system"),a=document.getElementById("slug"),u=document.getElementById("php"),c=document.getElementById("opcacheDevelopment"),d=document.getElementById("contentsecuritypolicy");i.addEventListener("submit",e=>{e.preventDefault();let t=document.querySelector('input[name="flavor"]:checked'),n=t instanceof HTMLInputElement?t.value:"";l({Slug:a.value,Flavor:n,System:{PHP:u.value,OpCacheDevelopment:c.checked,ContentSecurityPolicy:d.value}}).then(e=>{location.href="/admin/instance/"+e}).catch(e=>{console.error(e),location.reload()})}),i.querySelector("fieldset")?.removeAttribute("disabled"); \ No newline at end of file diff --git a/internal/dis/component/server/assets/dist/AdminProvision.7dff6f1a.js b/internal/dis/component/server/assets/dist/AdminProvision.7dff6f1a.js deleted file mode 100644 index 5a727a4..0000000 --- a/internal/dis/component/server/assets/dist/AdminProvision.7dff6f1a.js +++ /dev/null @@ -1 +0,0 @@ -var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},t={},n={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in t)return t[e].exports;if(e in n){var o=n[e];delete n[e];var r={id:e,exports:{}};return t[e]=r,o.call(r.exports,r,r.exports),r.exports}var i=Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}).register=function(e,t){n[e]=t},e.parcelRequireafa4=o),o("8xGhL");var r=o("12vpF");async function i(e){return await new Promise((t,n)=>{(0,r.createModal)("provision",[JSON.stringify(e)],{bufferSize:0,onClose:(o,r)=>{if(!o){n(Error(r??"unspecified error"));return}t(e.Slug)}})})}const l=document.getElementById("system"),a=document.getElementById("slug"),d=document.getElementById("php"),u=document.getElementById("opcacheDevelopment"),c=document.getElementById("contentsecuritypolicy");l.addEventListener("submit",e=>{e.preventDefault(),i({Slug:a.value,System:{PHP:d.value,OpCacheDevelopment:u.checked,ContentSecurityPolicy:c.value}}).then(e=>{location.href="/admin/instance/"+e}).catch(e=>{console.error(e),location.reload()})}),l.querySelector("fieldset")?.removeAttribute("disabled"); \ No newline at end of file diff --git a/internal/dis/component/server/assets/src/entry/AdminProvision/index.ts b/internal/dis/component/server/assets/src/entry/AdminProvision/index.ts index b9114ee..0088ee5 100644 --- a/internal/dis/component/server/assets/src/entry/AdminProvision/index.ts +++ b/internal/dis/component/server/assets/src/entry/AdminProvision/index.ts @@ -13,7 +13,14 @@ const contentSecurityPolicy = document.getElementById('contentsecuritypolicy') a system.addEventListener('submit', (evt) => { evt.preventDefault() - Provision({ Slug: slug.value, System: { PHP: php.value, OpCacheDevelopment: opcacheDevelopment.checked, ContentSecurityPolicy: contentSecurityPolicy.value } }) + const flavorElement = document.querySelector('input[name="flavor"]:checked'); + const flavor = (flavorElement instanceof HTMLInputElement) ? flavorElement.value : ""; + + Provision({ + Slug: slug.value, + Flavor: flavor, + System: { PHP: php.value, OpCacheDevelopment: opcacheDevelopment.checked, ContentSecurityPolicy: contentSecurityPolicy.value } + }) .then(slug => { location.href = '/admin/instance/' + slug }) diff --git a/internal/dis/component/server/assets/src/lib/remote/api.ts b/internal/dis/component/server/assets/src/lib/remote/api.ts index 2beba1e..ceddf9b 100644 --- a/internal/dis/component/server/assets/src/lib/remote/api.ts +++ b/internal/dis/component/server/assets/src/lib/remote/api.ts @@ -6,6 +6,7 @@ import { createModal } from '~/src/lib/remote' */ interface ProvisionFlags { Slug: string + Flavor?: string System: System } diff --git a/internal/status/wisski.go b/internal/status/wisski.go index 1b3e41f..6c7cf54 100644 --- a/internal/status/wisski.go +++ b/internal/status/wisski.go @@ -29,8 +29,8 @@ type WissKI struct { LastUpdate time.Time LastCron time.Time - // current theme - Theme string + DrupalVersion string // current drupal version + Theme string // current default theme // Statistics of the WissKI Statistics Statistics diff --git a/internal/wisski/ingredient/barrel/manager/manager.go b/internal/wisski/ingredient/barrel/manager/manager.go index 5e6eba6..eb8f993 100644 --- a/internal/wisski/ingredient/barrel/manager/manager.go +++ b/internal/wisski/ingredient/barrel/manager/manager.go @@ -1,6 +1,9 @@ package manager import ( + "maps" + "slices" + "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient" "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel" "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/composer" @@ -27,8 +30,81 @@ type Manager struct { } } +// profiles contains the list of default profiles +var ( + defaultProfile = "Drupal 10" + profiles = map[string]Profile{ + "Drupal 9": { + Description: "Legacy Version of Drupal with default packages", + + Drupal: "^9", + WissKI: "", + InstallModules: []string{ + "drupal/inline_entity_form:^1.0@RC", + "drupal/imagemagick", + "drupal/image_effects", + "drupal/colorbox", + }, + EnableModules: []string{ + "drupal/devel:^4.1", + "drupal/geofield:^1.40", + "drupal/geofield_map:^2.85", + "drupal/imce:^2.4", + "drupal/remove_generator:^2.0", + }, + }, + "Drupal 10": { + Description: "Current Version of Drupal with default packages", + + Drupal: "^10", + WissKI: "", + InstallModules: []string{ + + "drupal/inline_entity_form:^1.0@RC", + "drupal/imagemagick", + "drupal/image_effects", + "drupal/colorbox", + }, + EnableModules: []string{ + "drupal/devel:^5.0", + "drupal/geofield:^1.56", + "drupal/geofield_map:^3.0", + "drupal/imce:^3.0", + "drupal/remove_generator:^2.0", + }, + }, + } +) + +// TODO: All of these should move to the config + +func LoadDefaultProfile() Profile { + return LoadProfile(DefaultProfile()) +} + +func Profiles() map[string]Profile { + return maps.Clone(profiles) +} + +func LoadProfile(name string) Profile { + return profiles[name] +} + +func HasProfile(name string) bool { + _, ok := profiles[name] + return ok +} + +func DefaultProfile() string { + return defaultProfile +} + // Profile represents a profile applied to a WissKI instance of the Distillery. type Profile struct { + // Description is a human-readable description for this profile. + // It is only used by the frontend. + Description string + Drupal string // Version of Drupal to use WissKI string // Version of WissKI to use @@ -36,29 +112,25 @@ type Profile struct { EnableModules []string // Modules to be installed and enabled } -// DefaultDrupalVersion is the default drupal version -const DefaultDrupalVersion = "^9.0.0" - -// ApplyDefaults applies the default settings to missing profile settings. -func (profile *Profile) ApplyDefaults() { +// Apply copies over defaults from the other profile to this one. +// If a field is already set, no defaults are copied. +func (profile *Profile) Apply(other Profile) { if profile.Drupal == "" { - profile.Drupal = DefaultDrupalVersion + profile.Drupal = other.Drupal + } + if profile.WissKI == "" { + profile.WissKI = other.WissKI } if profile.InstallModules == nil { - profile.InstallModules = []string{ - "drupal/inline_entity_form:^1.0@RC", - "drupal/imagemagick", - "drupal/image_effects", - "drupal/colorbox", - } + profile.InstallModules = slices.Clone(other.InstallModules) } if profile.EnableModules == nil { - profile.EnableModules = []string{ - "drupal/devel:^4.1", - "drupal/geofield:^1.40", - "drupal/geofield_map:^2.85", - "drupal/imce:^2.4", - "drupal/remove_generator:^2.0", - } + profile.EnableModules = slices.Clone(profile.EnableModules) } } + +// ApplyDefaults loads some set of defaults. +// If all fields are set, no defaults are applied. +func (profile *Profile) ApplyDefaults() { + profile.Apply(profiles[defaultProfile]) +} diff --git a/internal/wisski/ingredient/barrel/manager/provision.go b/internal/wisski/ingredient/barrel/manager/provision.go index 912027f..3acf4ac 100644 --- a/internal/wisski/ingredient/barrel/manager/provision.go +++ b/internal/wisski/ingredient/barrel/manager/provision.go @@ -57,8 +57,6 @@ func (manager *Manager) Provision(ctx context.Context, progress io.Writer, syste manager.dependencies.Barrel.Stack().DownAll(anyways, progress) }() - // Apply the defaults to the flags - flags.ApplyDefaults() return manager.bootstrap(ctx, progress, flags) } @@ -71,7 +69,6 @@ var drushVariants = []string{ // Applies defaults to the flags. func (provision *Manager) bootstrap(ctx context.Context, progress io.Writer, flags Profile) error { // TODO: Check if we can remove the easyrdf patch! - flags.ApplyDefaults() logging.LogMessage(progress, "Creating Composer Project") diff --git a/internal/wisski/ingredient/php/extras/settings.php b/internal/wisski/ingredient/php/extras/settings.php index 504cb3f..1dafaee 100644 --- a/internal/wisski/ingredient/php/extras/settings.php +++ b/internal/wisski/ingredient/php/extras/settings.php @@ -10,7 +10,7 @@ function get_setting($name) { /** sets a setting in 'settings.php' */ function set_setting(string $name, mixed $value): bool { // find settings.php - $filename = DRUPAL_ROOT . "/" . \Drupal::service("site.path") . "/settings.php"; + $filename = DRUPAL_ROOT . "/" . \Drupal::getContainer()->getParameter("site.path") . "/settings.php"; // setup user write permissions for the file $old = fileperms($filename); diff --git a/internal/wisski/ingredient/php/extras/version.go b/internal/wisski/ingredient/php/extras/version.go new file mode 100644 index 0000000..7fb8744 --- /dev/null +++ b/internal/wisski/ingredient/php/extras/version.go @@ -0,0 +1,37 @@ +package extras + +import ( + "context" + + "github.com/FAU-CDI/wisski-distillery/internal/phpx" + "github.com/FAU-CDI/wisski-distillery/internal/status" + "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient" + "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php" + + _ "embed" +) + +// Version implements reading the current drupal version +type Version struct { + ingredient.Base + dependencies struct { + PHP *php.PHP + } +} + +// Get returns the currently active theme +func (v *Version) Get(ctx context.Context, server *phpx.Server) (version string, err error) { + err = v.dependencies.PHP.EvalCode( + ctx, server, &version, "return Drupal::VERSION; ", + ) + return +} + +func (v *Version) Fetch(flags ingredient.FetcherFlags, info *status.WissKI) (err error) { + if flags.Quick { + return + } + + info.DrupalVersion, _ = v.Get(flags.Context, flags.Server) + return +} diff --git a/internal/wisski/ingredients.go b/internal/wisski/ingredients.go deleted file mode 100644 index 9a3ff40..0000000 --- a/internal/wisski/ingredients.go +++ /dev/null @@ -1 +0,0 @@ -package wisski diff --git a/internal/wisski/wisski.go b/internal/wisski/wisski.go index b2e2164..c40d77a 100644 --- a/internal/wisski/wisski.go +++ b/internal/wisski/wisski.go @@ -144,6 +144,7 @@ func (wisski *WissKI) allIngredients(context *lifetime.RegisterContext[ingredien lifetime.Place[*extras.Requirements](context) lifetime.Place[*extras.Adapters](context) lifetime.Place[*extras.Theme](context) + lifetime.Place[*extras.Version](context) lifetime.Place[*users.Users](context) lifetime.Place[*users.UserPolicy](context)