From 7c20f31c80a7f40abc6e8c5e1e417cb1b3b0f3c8 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Sat, 9 Jul 2022 14:52:28 -0700 Subject: [PATCH 1/9] catalogsite: checkpoint of a (skeletal) catalog html generator. Containing copious hacks in the "test". Not production-ready. But still I still may want to merge this just to move forward. The exact filesystem layout may be subject to change. This is a rebase of an old prototype (from before warpforge v0.2); the exact filesystem I'd like to use nowaday should more closely mirror the paths of the catalog filesystem projection in JSON, just with different extensions (which is not what we have here). Everything else _also_ subject to change :) There's no styling, etc. Future work includes turning this into a real CLI command; giving it parameters for which catalog(s?) to generate content from; teaching it to render and link up certain pieces of metadata (especially, right now, Replays); and probably a lot more. --- pkg/cataloghtml/cataloghtml.go | 180 +++++++++++++++++++++++++++++++++ pkg/cataloghtml/demo_test.go | 36 +++++++ 2 files changed, 216 insertions(+) create mode 100644 pkg/cataloghtml/cataloghtml.go create mode 100644 pkg/cataloghtml/demo_test.go diff --git a/pkg/cataloghtml/cataloghtml.go b/pkg/cataloghtml/cataloghtml.go new file mode 100644 index 00000000..c299596a --- /dev/null +++ b/pkg/cataloghtml/cataloghtml.go @@ -0,0 +1,180 @@ +package cataloghtml + +import ( + "context" + "html/template" + "os" + "path" + "path/filepath" + "reflect" + + "github.com/warpfork/warpforge/pkg/workspace" + "github.com/warpfork/warpforge/wfapi" +) + +type SiteConfig struct { + Ctx context.Context + + // Data Access Broker for getting Catalog info. + // Some functions pass around data in memory, + // but sometimes those objects just contain CIDs, which we'll need to go load. + // This has helper functions that do the loading. + // Arguably should be a parameter, but would end up in almost every single function, so, eh. + Cat_dab workspace.Catalog + + // A plain string for output path prefix is used because golang still lacks + // an interface for filesystem *writing* -- io/fs is only reading. Sigh. + OutputPath string + + // Set to "/" if you'll be publishing at the root of a subdomain. + URLPrefix string +} + +func (cfg SiteConfig) tfuncs() map[string]interface{} { + return map[string]interface{}{ + "string": func(x interface{}) string { // golang would you please shut the fuck up and let me be productive, honestly + // this is for things that are literally typedefs of string but the template package isn't smart enough to be calm about unboxing it. + return reflect.ValueOf(x).String() + }, + "url": func(parts ...string) string { + return path.Join(append([]string{cfg.URLPrefix}, parts...)...) + }, + } +} + +func (cfg SiteConfig) CatalogAndChildrenToHtml() error { + if err := cfg.CatalogToHtml(); err != nil { + return err + } + modNames := cfg.Cat_dab.Modules() + for _, modName := range modNames { + catMod, err := cfg.Cat_dab.GetModule(wfapi.CatalogRef{modName, "", ""}) + if err != nil { + return err + } + if err := cfg.CatalogModuleAndChildrenToHtml(*catMod); err != nil { + return err + } + } + return nil +} + +// CatalogToHtml generates a root page that links to all the modules. +// +// This function has no parameters because it uses the DAB in the SiteConfig entirely. +func (cfg SiteConfig) CatalogToHtml() error { + // Future: It's perhaps a bit odd that this uses the workspace.Catalog object instead of the API object. We probably haven't hammered out appropriate data access helpers yet. + if err := os.MkdirAll(cfg.OutputPath, 0775); err != nil { + return err + } + f, err := os.OpenFile(filepath.Join(cfg.OutputPath, "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) + if err != nil { + return err + } + defer f.Close() + + // TODO: it's completely bork that we don't have access to the CIDs here. workspace.Catalog is Not Good right now. + // TODO: this probably needs sorting to be stable. + // Future: we should have a CID of the entire catalog tree root snapshot somewhere, too. (It should probably use prolly trees or something, though, which is not available as a convenient library yet.) + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` + +
+

catalog

+
+

modules

+ + + `)) + return t.Execute(f, cfg.Cat_dab.Modules()) +} + +func (cfg SiteConfig) CatalogModuleAndChildrenToHtml(catMod wfapi.CatalogModule) error { + if err := cfg.CatalogModuleToHtml(catMod); err != nil { + return err + } + for _, releaseName := range catMod.Releases.Keys { + rel, err := cfg.Cat_dab.GetRelease(wfapi.CatalogRef{catMod.Name, releaseName, ""}) + if err != nil { + return err + } + if err := cfg.ReleaseToHtml(catMod, *rel); err != nil { + return err + } + } + return nil +} + +func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { + if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name)), 0775); err != nil { + return err + } + f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) + if err != nil { + return err + } + defer f.Close() + + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` + +
+ module: +

{{ .Name }}

+
+ (back to root) +

releases

+ +

metadata

+ {{- range $metadataKey := .Metadata.Keys }} +
{{ $metadataKey }}
{{ index $dot.Metadata.Values $metadataKey }}
+ {{- end }} + + `)) + return t.Execute(f, catMod) +} + +func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.CatalogRelease) error { + if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name), string(rel.ReleaseName)), 0775); err != nil { + return err + } + f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), string(rel.ReleaseName), "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) + if err != nil { + return err + } + defer f.Close() + + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` + +
+ module: +

{{ .Module.Name }}

+ release: +

{{ .Release.ReleaseName }}

+
+ (back to root; back to module index) +

items

+ +

metadata

+ {{- range $metadataKey := .Release.Metadata.Keys }} +
{{ $metadataKey }}
{{ index $dot.Metadata.Values $metadataKey }}
+ {{- end }} + + `)) + return t.Execute(f, map[string]interface{}{ + "Module": catMod, + "Release": rel, + }) +} diff --git a/pkg/cataloghtml/demo_test.go b/pkg/cataloghtml/demo_test.go new file mode 100644 index 00000000..340d14c6 --- /dev/null +++ b/pkg/cataloghtml/demo_test.go @@ -0,0 +1,36 @@ +package cataloghtml + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/warpfork/warpforge/pkg/workspace" +) + +func TestWhee(t *testing.T) { + // t.Skip("incomplete") + homedir, err := os.UserHomeDir() + if err != nil { + panic(err) + } + // This is a very sketchy "live" "test" that assumes you've run `warpforge catalog update` before, + // and operates (readonly!) on that real data. + cat_dab, err := workspace.OpenCatalog(os.DirFS("/"), filepath.Join(homedir, ".warpforge/catalogs/warpsys")[1:]) + if err != nil { + panic(err) + } + // Output paths are currently hardcoded and can be seen in the config object below. + // No actual assertions take place on this; the "test" is manually looking at that output. + cfg := SiteConfig{ + Ctx: context.Background(), + Cat_dab: cat_dab, + OutputPath: "/tmp/wf-test-cathtml/", + URLPrefix: "/tmp/wf-test-cathtml/", + } + os.RemoveAll(cfg.OutputPath) + if err := cfg.CatalogAndChildrenToHtml(); err != nil { + panic(err) + } +} From 11768eb04803bc589f58fd9ddf3c273d6af940c3 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Fri, 9 Sep 2022 18:21:16 -0500 Subject: [PATCH 2/9] Serum appeasement --- pkg/cataloghtml/cataloghtml.go | 72 +++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/pkg/cataloghtml/cataloghtml.go b/pkg/cataloghtml/cataloghtml.go index c299596a..19f88410 100644 --- a/pkg/cataloghtml/cataloghtml.go +++ b/pkg/cataloghtml/cataloghtml.go @@ -42,6 +42,15 @@ func (cfg SiteConfig) tfuncs() map[string]interface{} { } } +// CatalogAndChildrenToHtml performs CatalogToHtml, and also +// procedes to invoke the html'ing of all modules within. +// +// Errors: +// +// - warpforge-error-io -- in case of errors writing out the new html content. +// - warpforge-error-internal -- in case of templating errors. +// - warpforge-error-catalog-invalid -- in case the catalog data is invalid. +// - warpforge-error-catalog-parse -- in case the catalog data failed to parse entirely. func (cfg SiteConfig) CatalogAndChildrenToHtml() error { if err := cfg.CatalogToHtml(); err != nil { return err @@ -62,14 +71,19 @@ func (cfg SiteConfig) CatalogAndChildrenToHtml() error { // CatalogToHtml generates a root page that links to all the modules. // // This function has no parameters because it uses the DAB in the SiteConfig entirely. +// +// Errors: +// +// - warpforge-error-io -- in case of errors writing out the new html content. +// - warpforge-error-internal -- in case of templating errors. func (cfg SiteConfig) CatalogToHtml() error { // Future: It's perhaps a bit odd that this uses the workspace.Catalog object instead of the API object. We probably haven't hammered out appropriate data access helpers yet. if err := os.MkdirAll(cfg.OutputPath, 0775); err != nil { - return err + return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err) } f, err := os.OpenFile(filepath.Join(cfg.OutputPath, "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) if err != nil { - return err + return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err) } defer f.Close() @@ -89,9 +103,21 @@ func (cfg SiteConfig) CatalogToHtml() error { `)) - return t.Execute(f, cfg.Cat_dab.Modules()) + if err := t.Execute(f, cfg.Cat_dab.Modules()); err != nil { + return wfapi.ErrorInternal("templating failed", err) + } + return nil } +// CatalogModuleAndChildrenToHtml performs CatalogModuleToHtml, and also +// procedes to invoke the html'ing of all releases within. +// +// Errors: +// +// - warpforge-error-io -- in case of errors writing out the new html content. +// - warpforge-error-internal -- in case of templating errors. +// - warpforge-error-catalog-invalid -- in case the catalog data is invalid. +// - warpforge-error-catalog-parse -- in case the catalog data failed to parse entirely. func (cfg SiteConfig) CatalogModuleAndChildrenToHtml(catMod wfapi.CatalogModule) error { if err := cfg.CatalogModuleToHtml(catMod); err != nil { return err @@ -108,13 +134,21 @@ func (cfg SiteConfig) CatalogModuleAndChildrenToHtml(catMod wfapi.CatalogModule) return nil } +// CatalogModuleToHtml generates a page for a module which enumerates +// and links to all the releases within it, +// as well as enumerates all the metadata attached to the catalog module. +// +// Errors: +// +// - warpforge-error-io -- in case of errors writing out the new html content. +// - warpforge-error-internal -- in case of templating errors. func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name)), 0775); err != nil { - return err + return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err) } f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) if err != nil { - return err + return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err) } defer f.Close() @@ -138,16 +172,31 @@ func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { {{- end }} `)) - return t.Execute(f, catMod) + if err := t.Execute(f, catMod); err != nil { + return wfapi.ErrorInternal("templating failed", err) + } + return nil } +// CatalogModuleToHtml generates a page for a release within a catalog module +// which enumerates all the items within it, +// as well as enumerates all the metadata attached to the release. +// +// Possible but not-yet-implemented future features of this output might include: +// linking better to metadata that references other documents (such as Replays); +// links to neighboring (e.g. forward and previous) releases; etc. +// +// Errors: +// +// - warpforge-error-io -- in case of errors writing out the new html content. +// - warpforge-error-internal -- in case of templating errors. func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.CatalogRelease) error { if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name), string(rel.ReleaseName)), 0775); err != nil { - return err + return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err) } f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), string(rel.ReleaseName), "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) if err != nil { - return err + return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err) } defer f.Close() @@ -173,8 +222,11 @@ func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.Catalo {{- end }} `)) - return t.Execute(f, map[string]interface{}{ + if err := t.Execute(f, map[string]interface{}{ "Module": catMod, "Release": rel, - }) + }); err != nil { + return wfapi.ErrorInternal("templating failed", err) + } + return nil } From dafeadb03fa5f2ad4ba1af94e22fa868961d0557 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Fri, 9 Sep 2022 18:32:44 -0500 Subject: [PATCH 3/9] Use the workspace catalog for this demo test. It's more or less guaranteed to exist. (Also: exists in CI.) --- pkg/cataloghtml/demo_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkg/cataloghtml/demo_test.go b/pkg/cataloghtml/demo_test.go index 340d14c6..191a5ff9 100644 --- a/pkg/cataloghtml/demo_test.go +++ b/pkg/cataloghtml/demo_test.go @@ -10,14 +10,11 @@ import ( ) func TestWhee(t *testing.T) { - // t.Skip("incomplete") - homedir, err := os.UserHomeDir() + cwd, err := os.Getwd() if err != nil { panic(err) } - // This is a very sketchy "live" "test" that assumes you've run `warpforge catalog update` before, - // and operates (readonly!) on that real data. - cat_dab, err := workspace.OpenCatalog(os.DirFS("/"), filepath.Join(homedir, ".warpforge/catalogs/warpsys")[1:]) + cat_dab, err := workspace.OpenCatalog(os.DirFS("/"), filepath.Join(cwd, "../../.warpforge/catalog")[1:]) if err != nil { panic(err) } From d746549d12a1257e7237cff92320e3636e66bd02 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Fri, 9 Sep 2022 21:35:29 -0500 Subject: [PATCH 4/9] cataloghtml: align filenames with v0.2 catalog projection. The idea here being: any time you see something ".html", you can change that to ".json" and get raw data. (If you happened to copy all the raw catalog data into the same directory trees, that is. Which we aren't doing automatically here. But still.) --- pkg/cataloghtml/cataloghtml.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/cataloghtml/cataloghtml.go b/pkg/cataloghtml/cataloghtml.go index 19f88410..a698095a 100644 --- a/pkg/cataloghtml/cataloghtml.go +++ b/pkg/cataloghtml/cataloghtml.go @@ -98,7 +98,7 @@ func (cfg SiteConfig) CatalogToHtml() error {

modules

@@ -146,7 +146,7 @@ func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name)), 0775); err != nil { return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err) } - f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) + f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), "_module.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) if err != nil { return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err) } @@ -163,7 +163,7 @@ func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error {
    {{- $dot := . -}} {{- range $releaseKey := .Releases.Keys }} -
  • {{ $releaseKey }} (cid: {{ index $dot.Releases.Values $releaseKey }})
  • +
  • {{ $releaseKey }} (cid: {{ index $dot.Releases.Values $releaseKey }})
  • {{- end }}

metadata

@@ -191,10 +191,10 @@ func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { // - warpforge-error-io -- in case of errors writing out the new html content. // - warpforge-error-internal -- in case of templating errors. func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.CatalogRelease) error { - if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name), string(rel.ReleaseName)), 0775); err != nil { + if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name), "_releases"), 0775); err != nil { return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err) } - f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), string(rel.ReleaseName), "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) + f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), "_releases", string(rel.ReleaseName)+".html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) if err != nil { return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err) } @@ -208,7 +208,7 @@ func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.Catalo release:

{{ .Release.ReleaseName }}

- (back to root; back to module index) + (back to root; back to module index)

items

    {{- $dot := .Release -}} From 9b5c70676e0f04eeeed42f3ccca910abd84304a3 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Sun, 11 Sep 2022 16:07:49 -0500 Subject: [PATCH 5/9] cataloghtml: extract templates to files, and embed. --- pkg/cataloghtml/catalogIndex.tmpl.html | 22 +++++++ pkg/cataloghtml/catalogModule.tmpl.html | 18 ++++++ pkg/cataloghtml/catalogRelease.tmpl.html | 20 +++++++ pkg/cataloghtml/cataloghtml.go | 73 +++++------------------- 4 files changed, 75 insertions(+), 58 deletions(-) create mode 100644 pkg/cataloghtml/catalogIndex.tmpl.html create mode 100644 pkg/cataloghtml/catalogModule.tmpl.html create mode 100644 pkg/cataloghtml/catalogRelease.tmpl.html diff --git a/pkg/cataloghtml/catalogIndex.tmpl.html b/pkg/cataloghtml/catalogIndex.tmpl.html new file mode 100644 index 00000000..7d8ad3cd --- /dev/null +++ b/pkg/cataloghtml/catalogIndex.tmpl.html @@ -0,0 +1,22 @@ +{{- /* + This template is for the index page made at the root of the catalog html view. + It just lists and links to all the modules in the catalog. + + A couple of todos for this document: + - We should print CIDs of the modules! + ... But we don't right now because the our accessor functions don't make it easy to get. (Work needed on workspace.Catalog.) + - Farther future: we should have a CID of the entire catalog tree root snapshot somewhere, too. + ... But we don't right now, because that should probably use prolly trees (or some other scalable hash tree thing), which is not available and rigged up as a convenient library yet. + +*/ -}} + +
    +

    catalog

    +
    +

    modules

    + + diff --git a/pkg/cataloghtml/catalogModule.tmpl.html b/pkg/cataloghtml/catalogModule.tmpl.html new file mode 100644 index 00000000..83c621d5 --- /dev/null +++ b/pkg/cataloghtml/catalogModule.tmpl.html @@ -0,0 +1,18 @@ + +
    + module: +

    {{ .Name }}

    +
    + (back to root) +

    releases

    +
      + {{- $dot := . -}} + {{- range $releaseKey := .Releases.Keys }} +
    • {{ $releaseKey }} (cid: {{ index $dot.Releases.Values $releaseKey }})
    • + {{- end }} +
    +

    metadata

    + {{- range $metadataKey := .Metadata.Keys }} +
    {{ $metadataKey }}
    {{ index $dot.Metadata.Values $metadataKey }}
    + {{- end }} + diff --git a/pkg/cataloghtml/catalogRelease.tmpl.html b/pkg/cataloghtml/catalogRelease.tmpl.html new file mode 100644 index 00000000..e09a72ff --- /dev/null +++ b/pkg/cataloghtml/catalogRelease.tmpl.html @@ -0,0 +1,20 @@ + +
    + module: +

    {{ .Module.Name }}

    + release: +

    {{ .Release.ReleaseName }}

    +
    + (back to root; back to module index) +

    items

    +
      + {{- $dot := .Release -}} + {{- range $itemKey := .Release.Items.Keys }} +
    • {{ $itemKey }} : {{ index $dot.Items.Values $itemKey }}
    • + {{- end }} +
    +

    metadata

    + {{- range $metadataKey := .Release.Metadata.Keys }} +
    {{ $metadataKey }}
    {{ index $dot.Metadata.Values $metadataKey }}
    + {{- end }} + diff --git a/pkg/cataloghtml/cataloghtml.go b/pkg/cataloghtml/cataloghtml.go index a698095a..29cad87f 100644 --- a/pkg/cataloghtml/cataloghtml.go +++ b/pkg/cataloghtml/cataloghtml.go @@ -2,6 +2,7 @@ package cataloghtml import ( "context" + _ "embed" "html/template" "os" "path" @@ -12,6 +13,17 @@ import ( "github.com/warpfork/warpforge/wfapi" ) +var ( + //go:embed catalogIndex.tmpl.html + catalogIndexTemplate string + + //go:embed catalogModule.tmpl.html + catalogModuleTemplate string + + //go:embed catalogRelease.tmpl.html + catalogReleaseTemplate string +) + type SiteConfig struct { Ctx context.Context @@ -87,22 +99,7 @@ func (cfg SiteConfig) CatalogToHtml() error { } defer f.Close() - // TODO: it's completely bork that we don't have access to the CIDs here. workspace.Catalog is Not Good right now. - // TODO: this probably needs sorting to be stable. - // Future: we should have a CID of the entire catalog tree root snapshot somewhere, too. (It should probably use prolly trees or something, though, which is not available as a convenient library yet.) - t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` - -
    -

    catalog

    -
    -

    modules

    - - - `)) + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(catalogIndexTemplate)) if err := t.Execute(f, cfg.Cat_dab.Modules()); err != nil { return wfapi.ErrorInternal("templating failed", err) } @@ -152,26 +149,7 @@ func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { } defer f.Close() - t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` - -
    - module: -

    {{ .Name }}

    -
    - (back to root) -

    releases

    -
      - {{- $dot := . -}} - {{- range $releaseKey := .Releases.Keys }} -
    • {{ $releaseKey }} (cid: {{ index $dot.Releases.Values $releaseKey }})
    • - {{- end }} -
    -

    metadata

    - {{- range $metadataKey := .Metadata.Keys }} -
    {{ $metadataKey }}
    {{ index $dot.Metadata.Values $metadataKey }}
    - {{- end }} - - `)) + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(catalogModuleTemplate)) if err := t.Execute(f, catMod); err != nil { return wfapi.ErrorInternal("templating failed", err) } @@ -200,28 +178,7 @@ func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.Catalo } defer f.Close() - t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(` - -
    - module: -

    {{ .Module.Name }}

    - release: -

    {{ .Release.ReleaseName }}

    -
    - (back to root; back to module index) -

    items

    -
      - {{- $dot := .Release -}} - {{- range $itemKey := .Release.Items.Keys }} -
    • {{ $itemKey }} : {{ index $dot.Items.Values $itemKey }}
    • - {{- end }} -
    -

    metadata

    - {{- range $metadataKey := .Release.Metadata.Keys }} -
    {{ $metadataKey }}
    {{ index $dot.Metadata.Values $metadataKey }}
    - {{- end }} - - `)) + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(catalogReleaseTemplate)) if err := t.Execute(f, map[string]interface{}{ "Module": catMod, "Release": rel, From a82f0197a3621ae2a34e673491e97479fde90da6 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Sun, 11 Sep 2022 18:14:09 -0500 Subject: [PATCH 6/9] enhance your calm --- pkg/cataloghtml/cataloghtml.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/cataloghtml/cataloghtml.go b/pkg/cataloghtml/cataloghtml.go index 29cad87f..5113d213 100644 --- a/pkg/cataloghtml/cataloghtml.go +++ b/pkg/cataloghtml/cataloghtml.go @@ -44,8 +44,10 @@ type SiteConfig struct { func (cfg SiteConfig) tfuncs() map[string]interface{} { return map[string]interface{}{ - "string": func(x interface{}) string { // golang would you please shut the fuck up and let me be productive, honestly - // this is for things that are literally typedefs of string but the template package isn't smart enough to be calm about unboxing it. + "string": func(x interface{}) string { + // Very small helper function to stringify things. + // This is useful for things that are literally typedefs of string but the template package isn't smart enough to be calm about unboxing it. + // (It also does return something for values of non-string types, but not something very useful.) return reflect.ValueOf(x).String() }, "url": func(parts ...string) string { From e59761d9a287fb7473e84ceb7621aeb12e5339d0 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Sun, 11 Sep 2022 18:34:34 -0500 Subject: [PATCH 7/9] catalogsite: extract redundant setup actions to function. --- pkg/cataloghtml/cataloghtml.go | 77 ++++++++++++++++------------------ 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/pkg/cataloghtml/cataloghtml.go b/pkg/cataloghtml/cataloghtml.go index 5113d213..77917408 100644 --- a/pkg/cataloghtml/cataloghtml.go +++ b/pkg/cataloghtml/cataloghtml.go @@ -82,32 +82,47 @@ func (cfg SiteConfig) CatalogAndChildrenToHtml() error { return nil } -// CatalogToHtml generates a root page that links to all the modules. -// -// This function has no parameters because it uses the DAB in the SiteConfig entirely. +// doTemplate does the common bits of making files, processing the template, +// and getting the output where it needs to go. // // Errors: // // - warpforge-error-io -- in case of errors writing out the new html content. // - warpforge-error-internal -- in case of templating errors. -func (cfg SiteConfig) CatalogToHtml() error { - // Future: It's perhaps a bit odd that this uses the workspace.Catalog object instead of the API object. We probably haven't hammered out appropriate data access helpers yet. - if err := os.MkdirAll(cfg.OutputPath, 0775); err != nil { +func (cfg SiteConfig) doTemplate(outputPath string, tmpl string, data interface{}) error { + if err := os.MkdirAll(filepath.Dir(outputPath), 0775); err != nil { return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err) } - f, err := os.OpenFile(filepath.Join(cfg.OutputPath, "index.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) + f, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) if err != nil { return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err) } defer f.Close() - t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(catalogIndexTemplate)) - if err := t.Execute(f, cfg.Cat_dab.Modules()); err != nil { + t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(tmpl)) + if err := t.Execute(f, data); err != nil { return wfapi.ErrorInternal("templating failed", err) } return nil } +// CatalogToHtml generates a root page that links to all the modules. +// +// This function has no parameters because it uses the DAB in the SiteConfig entirely. +// +// Errors: +// +// - warpforge-error-io -- in case of errors writing out the new html content. +// - warpforge-error-internal -- in case of templating errors. +func (cfg SiteConfig) CatalogToHtml() error { + // Future: It's perhaps a bit odd that this uses the workspace.Catalog object instead of the API object. We probably haven't hammered out appropriate data access helpers yet. + return cfg.doTemplate( + filepath.Join(cfg.OutputPath, "index.html"), + catalogIndexTemplate, + cfg.Cat_dab.Modules(), + ) +} + // CatalogModuleAndChildrenToHtml performs CatalogModuleToHtml, and also // procedes to invoke the html'ing of all releases within. // @@ -142,20 +157,11 @@ func (cfg SiteConfig) CatalogModuleAndChildrenToHtml(catMod wfapi.CatalogModule) // - warpforge-error-io -- in case of errors writing out the new html content. // - warpforge-error-internal -- in case of templating errors. func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { - if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name)), 0775); err != nil { - return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err) - } - f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), "_module.html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) - if err != nil { - return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err) - } - defer f.Close() - - t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(catalogModuleTemplate)) - if err := t.Execute(f, catMod); err != nil { - return wfapi.ErrorInternal("templating failed", err) - } - return nil + return cfg.doTemplate( + filepath.Join(cfg.OutputPath, string(catMod.Name), "_module.html"), + catalogModuleTemplate, + catMod, + ) } // CatalogModuleToHtml generates a page for a release within a catalog module @@ -171,21 +177,12 @@ func (cfg SiteConfig) CatalogModuleToHtml(catMod wfapi.CatalogModule) error { // - warpforge-error-io -- in case of errors writing out the new html content. // - warpforge-error-internal -- in case of templating errors. func (cfg SiteConfig) ReleaseToHtml(catMod wfapi.CatalogModule, rel wfapi.CatalogRelease) error { - if err := os.MkdirAll(filepath.Join(cfg.OutputPath, string(catMod.Name), "_releases"), 0775); err != nil { - return wfapi.ErrorIo("couldn't mkdir during cataloghtml emission", nil, err) - } - f, err := os.OpenFile(filepath.Join(cfg.OutputPath, string(catMod.Name), "_releases", string(rel.ReleaseName)+".html"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664) - if err != nil { - return wfapi.ErrorIo("couldn't open file for writing during cataloghtml emission", nil, err) - } - defer f.Close() - - t := template.Must(template.New("main").Funcs(cfg.tfuncs()).Parse(catalogReleaseTemplate)) - if err := t.Execute(f, map[string]interface{}{ - "Module": catMod, - "Release": rel, - }); err != nil { - return wfapi.ErrorInternal("templating failed", err) - } - return nil + return cfg.doTemplate( + filepath.Join(cfg.OutputPath, string(catMod.Name), "_releases", string(rel.ReleaseName)+".html"), + catalogReleaseTemplate, + map[string]interface{}{ + "Module": catMod, + "Release": rel, + }, + ) } From 4b745bc263557c1d896351bfc34b7ee255718485 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Sun, 11 Sep 2022 19:04:39 -0500 Subject: [PATCH 8/9] catalogsite: comment on possible alterntaive embed systems. --- pkg/cataloghtml/cataloghtml.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/cataloghtml/cataloghtml.go b/pkg/cataloghtml/cataloghtml.go index 77917408..5e9e5066 100644 --- a/pkg/cataloghtml/cataloghtml.go +++ b/pkg/cataloghtml/cataloghtml.go @@ -22,6 +22,9 @@ var ( //go:embed catalogRelease.tmpl.html catalogReleaseTemplate string + + // FUTURE: consider the use of `embed.FS` and `template.ParseFS()`, if there grow to be many files here. + // It has slightly less compile-time safety checks on filenames, though. ) type SiteConfig struct { From 90edaf39f83d88107a745e31903d2dc961c60749 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Mon, 12 Sep 2022 20:36:20 -0500 Subject: [PATCH 9/9] cataloghtml: wire up css. Just a dummy file, but correctly wired. --- pkg/cataloghtml/catalogIndex.tmpl.html | 5 +++++ pkg/cataloghtml/catalogModule.tmpl.html | 5 +++++ pkg/cataloghtml/catalogRelease.tmpl.html | 5 +++++ pkg/cataloghtml/cataloghtml.go | 11 +++++++++++ pkg/cataloghtml/css.css | 5 +++++ 5 files changed, 31 insertions(+) create mode 100644 pkg/cataloghtml/css.css diff --git a/pkg/cataloghtml/catalogIndex.tmpl.html b/pkg/cataloghtml/catalogIndex.tmpl.html index 7d8ad3cd..e294bb3f 100644 --- a/pkg/cataloghtml/catalogIndex.tmpl.html +++ b/pkg/cataloghtml/catalogIndex.tmpl.html @@ -10,6 +10,10 @@ */ -}} + + + +

    catalog

    @@ -19,4 +23,5 @@

    modules

  • {{ $moduleName }}
  • {{- end }}
+ diff --git a/pkg/cataloghtml/catalogModule.tmpl.html b/pkg/cataloghtml/catalogModule.tmpl.html index 83c621d5..aa187a82 100644 --- a/pkg/cataloghtml/catalogModule.tmpl.html +++ b/pkg/cataloghtml/catalogModule.tmpl.html @@ -1,4 +1,8 @@ + + + +
module:

{{ .Name }}

@@ -15,4 +19,5 @@

metadata

{{- range $metadataKey := .Metadata.Keys }}
{{ $metadataKey }}
{{ index $dot.Metadata.Values $metadataKey }}
{{- end }} + diff --git a/pkg/cataloghtml/catalogRelease.tmpl.html b/pkg/cataloghtml/catalogRelease.tmpl.html index e09a72ff..bc214cb5 100644 --- a/pkg/cataloghtml/catalogRelease.tmpl.html +++ b/pkg/cataloghtml/catalogRelease.tmpl.html @@ -1,4 +1,8 @@ + + + +
module:

{{ .Module.Name }}

@@ -17,4 +21,5 @@

metadata

{{- range $metadataKey := .Release.Metadata.Keys }}
{{ $metadataKey }}
{{ index $dot.Metadata.Values $metadataKey }}
{{- end }} + diff --git a/pkg/cataloghtml/cataloghtml.go b/pkg/cataloghtml/cataloghtml.go index 5e9e5066..ef2a7e62 100644 --- a/pkg/cataloghtml/cataloghtml.go +++ b/pkg/cataloghtml/cataloghtml.go @@ -23,6 +23,9 @@ var ( //go:embed catalogRelease.tmpl.html catalogReleaseTemplate string + //go:embed css.css + cssBody []byte + // FUTURE: consider the use of `embed.FS` and `template.ParseFS()`, if there grow to be many files here. // It has slightly less compile-time safety checks on filenames, though. ) @@ -61,6 +64,8 @@ func (cfg SiteConfig) tfuncs() map[string]interface{} { // CatalogAndChildrenToHtml performs CatalogToHtml, and also // procedes to invoke the html'ing of all modules within. +// Additionally, it does all the other "once" things +// (namely, outputs a copy of the css). // // Errors: // @@ -69,9 +74,15 @@ func (cfg SiteConfig) tfuncs() map[string]interface{} { // - warpforge-error-catalog-invalid -- in case the catalog data is invalid. // - warpforge-error-catalog-parse -- in case the catalog data failed to parse entirely. func (cfg SiteConfig) CatalogAndChildrenToHtml() error { + // Emit catalog index. if err := cfg.CatalogToHtml(); err != nil { return err } + // Emit the "once" stuff. + if err := os.WriteFile(filepath.Join(cfg.OutputPath, "css.css"), cssBody, 0644); err != nil { + return wfapi.ErrorIo("couldn't open file for css as part of cataloghtml emission", nil, err) + } + // Emit all modules within. modNames := cfg.Cat_dab.Modules() for _, modName := range modNames { catMod, err := cfg.Cat_dab.GetModule(wfapi.CatalogRef{modName, "", ""}) diff --git a/pkg/cataloghtml/css.css b/pkg/cataloghtml/css.css new file mode 100644 index 00000000..d6d5d222 --- /dev/null +++ b/pkg/cataloghtml/css.css @@ -0,0 +1,5 @@ +/* +body { + color: #F00; +} +*/