mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
271 lines
12 KiB
Go
271 lines
12 KiB
Go
//go:build ignore
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"go/format"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/licensing"
|
|
)
|
|
|
|
type generatedFeatureEntry struct {
|
|
Key string `json:"key"`
|
|
DisplayName string `json:"displayName"`
|
|
ComparisonName string `json:"comparisonName"`
|
|
ShowInComparisonTable bool `json:"showInComparisonTable"`
|
|
DisplayableInSelfHostedPlan bool `json:"displayableInSelfHostedPlan"`
|
|
Roles generatedFeatureRoles `json:"roles"`
|
|
IncludedIn generatedFeatureTierInclusion `json:"includedIn"`
|
|
}
|
|
|
|
type generatedFeatureRoles struct {
|
|
Community licensing.SelfHostedFeatureRole `json:"community"`
|
|
Relay licensing.SelfHostedFeatureRole `json:"relay"`
|
|
Pro licensing.SelfHostedFeatureRole `json:"pro"`
|
|
}
|
|
|
|
type generatedFeatureTierInclusion struct {
|
|
Community bool `json:"community"`
|
|
Relay bool `json:"relay"`
|
|
Pro bool `json:"pro"`
|
|
}
|
|
|
|
func main() {
|
|
repoRoot, err := os.Getwd()
|
|
if err != nil {
|
|
fail(err)
|
|
}
|
|
|
|
entries := buildGeneratedEntries()
|
|
tierFeatures := map[string][]string{
|
|
"community": append([]string(nil), licensing.TierFeatures[licensing.TierFree]...),
|
|
"relay": append([]string(nil), licensing.TierFeatures[licensing.TierRelay]...),
|
|
"pro": append([]string(nil), licensing.TierFeatures[licensing.TierPro]...),
|
|
}
|
|
|
|
if err := writeFrontendModule(filepath.Join(repoRoot, "frontend-modern/src/utils/selfHostedFeatureCatalog.generated.ts"), entries); err != nil {
|
|
fail(err)
|
|
}
|
|
|
|
pulseProRoot := filepath.Clean(filepath.Join(repoRoot, "..", "pulse-pro"))
|
|
if _, err := os.Stat(pulseProRoot); err == nil {
|
|
if err := writePulseProModule(filepath.Join(pulseProRoot, "license-server/self_hosted_feature_catalog.generated.go"), entries, tierFeatures); err != nil {
|
|
fail(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func buildGeneratedEntries() []generatedFeatureEntry {
|
|
metadata := licensing.AllFeatureMetadata()
|
|
entries := make([]generatedFeatureEntry, 0, len(metadata))
|
|
for _, entry := range metadata {
|
|
roles := generatedFeatureRoles{
|
|
Community: entry.SelfHostedRoles.Community,
|
|
Relay: entry.SelfHostedRoles.Relay,
|
|
Pro: entry.SelfHostedRoles.Pro,
|
|
}
|
|
included := generatedFeatureTierInclusion{
|
|
Community: licensing.TierHasFeature(licensing.TierFree, entry.Key),
|
|
Relay: licensing.TierHasFeature(licensing.TierRelay, entry.Key),
|
|
Pro: licensing.TierHasFeature(licensing.TierPro, entry.Key),
|
|
}
|
|
entries = append(entries, generatedFeatureEntry{
|
|
Key: entry.Key,
|
|
DisplayName: entry.DisplayName,
|
|
ComparisonName: entry.ComparisonName,
|
|
ShowInComparisonTable: entry.ShowInComparisonTable,
|
|
DisplayableInSelfHostedPlan: entry.DisplayableInPlanUI,
|
|
Roles: roles,
|
|
IncludedIn: included,
|
|
})
|
|
}
|
|
return entries
|
|
}
|
|
|
|
func writeFrontendModule(path string, entries []generatedFeatureEntry) error {
|
|
payload, err := json.MarshalIndent(entries, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
buf.WriteString("// This file is generated by scripts/generate-self-hosted-feature-catalog.go.\n")
|
|
buf.WriteString("// Do not edit it manually.\n\n")
|
|
buf.WriteString("export type SelfHostedFeatureRole =\n")
|
|
buf.WriteString(" | 'hidden'\n")
|
|
buf.WriteString(" | 'included'\n")
|
|
buf.WriteString(" | 'primary_pillar'\n")
|
|
buf.WriteString(" | 'included_extra'\n")
|
|
buf.WriteString(" | 'compatibility_only';\n\n")
|
|
buf.WriteString("export interface GeneratedSelfHostedFeatureCatalogEntry {\n")
|
|
buf.WriteString(" key: string;\n")
|
|
buf.WriteString(" displayName: string;\n")
|
|
buf.WriteString(" comparisonName: string;\n")
|
|
buf.WriteString(" showInComparisonTable: boolean;\n")
|
|
buf.WriteString(" displayableInSelfHostedPlan: boolean;\n")
|
|
buf.WriteString(" roles: {\n")
|
|
buf.WriteString(" community: SelfHostedFeatureRole;\n")
|
|
buf.WriteString(" relay: SelfHostedFeatureRole;\n")
|
|
buf.WriteString(" pro: SelfHostedFeatureRole;\n")
|
|
buf.WriteString(" };\n")
|
|
buf.WriteString(" includedIn: {\n")
|
|
buf.WriteString(" community: boolean;\n")
|
|
buf.WriteString(" relay: boolean;\n")
|
|
buf.WriteString(" pro: boolean;\n")
|
|
buf.WriteString(" };\n")
|
|
buf.WriteString("}\n\n")
|
|
buf.WriteString("export const SELF_HOSTED_FEATURE_CATALOG: readonly GeneratedSelfHostedFeatureCatalogEntry[] = ")
|
|
buf.Write(payload)
|
|
buf.WriteString(" as const;\n\n")
|
|
buf.WriteString("export const SELF_HOSTED_FEATURE_CATALOG_BY_KEY: Record<string, GeneratedSelfHostedFeatureCatalogEntry> = Object.fromEntries(\n")
|
|
buf.WriteString(" SELF_HOSTED_FEATURE_CATALOG.map((entry) => [entry.key, entry]),\n")
|
|
buf.WriteString(") as Record<string, GeneratedSelfHostedFeatureCatalogEntry>;\n\n")
|
|
buf.WriteString("export function getSelfHostedFeatureCatalogEntry(\n")
|
|
buf.WriteString(" key?: string | null,\n")
|
|
buf.WriteString("): GeneratedSelfHostedFeatureCatalogEntry | null {\n")
|
|
buf.WriteString(" const normalized = (key || '').trim();\n")
|
|
buf.WriteString(" if (!normalized) {\n")
|
|
buf.WriteString(" return null;\n")
|
|
buf.WriteString(" }\n")
|
|
buf.WriteString(" return SELF_HOSTED_FEATURE_CATALOG_BY_KEY[normalized] || null;\n")
|
|
buf.WriteString("}\n\n")
|
|
buf.WriteString("export function isDisplayableSelfHostedFeatureKey(key?: string | null): boolean {\n")
|
|
buf.WriteString(" const entry = getSelfHostedFeatureCatalogEntry(key);\n")
|
|
buf.WriteString(" if (!entry) {\n")
|
|
buf.WriteString(" return true;\n")
|
|
buf.WriteString(" }\n")
|
|
buf.WriteString(" return entry.displayableInSelfHostedPlan;\n")
|
|
buf.WriteString("}\n\n")
|
|
buf.WriteString("export function getSelfHostedFeaturesForRole(\n")
|
|
buf.WriteString(" tier: 'community' | 'relay' | 'pro',\n")
|
|
buf.WriteString(" role: SelfHostedFeatureRole,\n")
|
|
buf.WriteString("): readonly GeneratedSelfHostedFeatureCatalogEntry[] {\n")
|
|
buf.WriteString(" return SELF_HOSTED_FEATURE_CATALOG.filter((entry) => entry.roles[tier] === role);\n")
|
|
buf.WriteString("}\n")
|
|
|
|
return os.WriteFile(path, buf.Bytes(), 0o644)
|
|
}
|
|
|
|
func writePulseProModule(path string, entries []generatedFeatureEntry, tierFeatures map[string][]string) error {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("// Code generated by ../pulse/scripts/generate-self-hosted-feature-catalog.go; DO NOT EDIT.\n")
|
|
buf.WriteString("package main\n\n")
|
|
buf.WriteString("import \"strings\"\n\n")
|
|
buf.WriteString("type selfHostedFeatureRole string\n\n")
|
|
buf.WriteString("const (\n")
|
|
buf.WriteString("\tselfHostedFeatureRoleHidden selfHostedFeatureRole = \"hidden\"\n")
|
|
buf.WriteString("\tselfHostedFeatureRoleIncluded selfHostedFeatureRole = \"included\"\n")
|
|
buf.WriteString("\tselfHostedFeatureRolePrimaryPillar selfHostedFeatureRole = \"primary_pillar\"\n")
|
|
buf.WriteString("\tselfHostedFeatureRoleIncludedExtra selfHostedFeatureRole = \"included_extra\"\n")
|
|
buf.WriteString("\tselfHostedFeatureRoleCompatibilityOnly selfHostedFeatureRole = \"compatibility_only\"\n")
|
|
buf.WriteString(")\n\n")
|
|
buf.WriteString("type selfHostedFeatureCatalogEntry struct {\n")
|
|
buf.WriteString("\tKey string\n")
|
|
buf.WriteString("\tDisplayName string\n")
|
|
buf.WriteString("\tComparisonName string\n")
|
|
buf.WriteString("\tShowInComparisonTable bool\n")
|
|
buf.WriteString("\tDisplayableInSelfHostedPlan bool\n")
|
|
buf.WriteString("\tCommunityRole selfHostedFeatureRole\n")
|
|
buf.WriteString("\tRelayRole selfHostedFeatureRole\n")
|
|
buf.WriteString("\tProRole selfHostedFeatureRole\n")
|
|
buf.WriteString("\tCommunity bool\n")
|
|
buf.WriteString("\tRelay bool\n")
|
|
buf.WriteString("\tPro bool\n")
|
|
buf.WriteString("}\n\n")
|
|
|
|
buf.WriteString("var selfHostedFeatureCatalog = []selfHostedFeatureCatalogEntry{\n")
|
|
for _, entry := range entries {
|
|
buf.WriteString("\t{\n")
|
|
buf.WriteString(fmt.Sprintf("\t\tKey: %q,\n", entry.Key))
|
|
buf.WriteString(fmt.Sprintf("\t\tDisplayName: %q,\n", entry.DisplayName))
|
|
buf.WriteString(fmt.Sprintf("\t\tComparisonName: %q,\n", entry.ComparisonName))
|
|
buf.WriteString(fmt.Sprintf("\t\tShowInComparisonTable: %t,\n", entry.ShowInComparisonTable))
|
|
buf.WriteString(fmt.Sprintf("\t\tDisplayableInSelfHostedPlan: %t,\n", entry.DisplayableInSelfHostedPlan))
|
|
buf.WriteString(fmt.Sprintf("\t\tCommunityRole: %q,\n", entry.Roles.Community))
|
|
buf.WriteString(fmt.Sprintf("\t\tRelayRole: %q,\n", entry.Roles.Relay))
|
|
buf.WriteString(fmt.Sprintf("\t\tProRole: %q,\n", entry.Roles.Pro))
|
|
buf.WriteString(fmt.Sprintf("\t\tCommunity: %t,\n", entry.IncludedIn.Community))
|
|
buf.WriteString(fmt.Sprintf("\t\tRelay: %t,\n", entry.IncludedIn.Relay))
|
|
buf.WriteString(fmt.Sprintf("\t\tPro: %t,\n", entry.IncludedIn.Pro))
|
|
buf.WriteString("\t},\n")
|
|
}
|
|
buf.WriteString("}\n\n")
|
|
|
|
buf.WriteString("var selfHostedFeatureCatalogByKey = map[string]selfHostedFeatureCatalogEntry{\n")
|
|
for _, entry := range entries {
|
|
buf.WriteString(fmt.Sprintf("\t%q: {\n", entry.Key))
|
|
buf.WriteString(fmt.Sprintf("\t\tKey: %q,\n", entry.Key))
|
|
buf.WriteString(fmt.Sprintf("\t\tDisplayName: %q,\n", entry.DisplayName))
|
|
buf.WriteString(fmt.Sprintf("\t\tComparisonName: %q,\n", entry.ComparisonName))
|
|
buf.WriteString(fmt.Sprintf("\t\tShowInComparisonTable: %t,\n", entry.ShowInComparisonTable))
|
|
buf.WriteString(fmt.Sprintf("\t\tDisplayableInSelfHostedPlan: %t,\n", entry.DisplayableInSelfHostedPlan))
|
|
buf.WriteString(fmt.Sprintf("\t\tCommunityRole: %q,\n", entry.Roles.Community))
|
|
buf.WriteString(fmt.Sprintf("\t\tRelayRole: %q,\n", entry.Roles.Relay))
|
|
buf.WriteString(fmt.Sprintf("\t\tProRole: %q,\n", entry.Roles.Pro))
|
|
buf.WriteString(fmt.Sprintf("\t\tCommunity: %t,\n", entry.IncludedIn.Community))
|
|
buf.WriteString(fmt.Sprintf("\t\tRelay: %t,\n", entry.IncludedIn.Relay))
|
|
buf.WriteString(fmt.Sprintf("\t\tPro: %t,\n", entry.IncludedIn.Pro))
|
|
buf.WriteString("\t},\n")
|
|
}
|
|
buf.WriteString("}\n\n")
|
|
|
|
buf.WriteString("var canonicalSelfHostedTierFeatures = map[string][]string{\n")
|
|
tiers := make([]string, 0, len(tierFeatures))
|
|
for tier := range tierFeatures {
|
|
tiers = append(tiers, tier)
|
|
}
|
|
sort.Strings(tiers)
|
|
for _, tier := range tiers {
|
|
features := append([]string(nil), tierFeatures[tier]...)
|
|
sort.Strings(features)
|
|
buf.WriteString(fmt.Sprintf("\t%q: []string{%s},\n", tier, quoteJoin(features)))
|
|
}
|
|
buf.WriteString("}\n\n")
|
|
|
|
buf.WriteString("func selfHostedFeaturesForRole(tier string, role selfHostedFeatureRole) []selfHostedFeatureCatalogEntry {\n")
|
|
buf.WriteString("\tentries := make([]selfHostedFeatureCatalogEntry, 0)\n")
|
|
buf.WriteString("\tfor _, entry := range selfHostedFeatureCatalog {\n")
|
|
buf.WriteString("\t\tvar current selfHostedFeatureRole\n")
|
|
buf.WriteString("\t\tswitch strings.ToLower(strings.TrimSpace(tier)) {\n")
|
|
buf.WriteString("\t\tcase \"community\":\n")
|
|
buf.WriteString("\t\t\tcurrent = entry.CommunityRole\n")
|
|
buf.WriteString("\t\tcase \"relay\":\n")
|
|
buf.WriteString("\t\t\tcurrent = entry.RelayRole\n")
|
|
buf.WriteString("\t\tcase \"pro\":\n")
|
|
buf.WriteString("\t\t\tcurrent = entry.ProRole\n")
|
|
buf.WriteString("\t\tdefault:\n")
|
|
buf.WriteString("\t\t\tcurrent = selfHostedFeatureRoleHidden\n")
|
|
buf.WriteString("\t\t}\n")
|
|
buf.WriteString("\t\tif current == role {\n")
|
|
buf.WriteString("\t\t\tentries = append(entries, entry)\n")
|
|
buf.WriteString("\t\t}\n")
|
|
buf.WriteString("\t}\n")
|
|
buf.WriteString("\treturn entries\n")
|
|
buf.WriteString("}\n")
|
|
|
|
formatted, err := format.Source(buf.Bytes())
|
|
if err != nil {
|
|
return fmt.Errorf("format pulse-pro module: %w", err)
|
|
}
|
|
return os.WriteFile(path, formatted, 0o644)
|
|
}
|
|
|
|
func quoteJoin(values []string) string {
|
|
quoted := make([]string, 0, len(values))
|
|
for _, value := range values {
|
|
quoted = append(quoted, fmt.Sprintf("%q", value))
|
|
}
|
|
return strings.Join(quoted, ", ")
|
|
}
|
|
|
|
func fail(err error) {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|