-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGenerateConfig.fsx
More file actions
213 lines (178 loc) · 8.76 KB
/
Copy pathGenerateConfig.fsx
File metadata and controls
213 lines (178 loc) · 8.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/env dotnet fsi
// =============================================================================
// Multi-Subdomain Config Generator & YML Sync Tool
// =============================================================================
// Generates mkdocs.yml for each subdomain from F# sources
// Also provides bidirectional YML ↔ .fs synchronization
//
// Usage:
// dotnet fsi GenerateConfig.fsx all # Generate all configs
// dotnet fsi GenerateConfig.fsx main # Generate main only
// dotnet fsi GenerateConfig.fsx docs # Generate docs only
// dotnet fsi GenerateConfig.fsx app # Generate app only
// dotnet fsi GenerateConfig.fsx blog # Generate blog only
// dotnet fsi GenerateConfig.fsx sync # Sync YML → .fs
// =============================================================================
open System
open System.IO
open System.Text.RegularExpressions
// =============================================================================
// Config Generation Functions
// =============================================================================
let generate (folder: string) (moduleName: string) (outputName: string) =
let fsPath = Path.Combine(folder, "zensical.fs")
if not (File.Exists(fsPath)) then
printfn "❌ %s not found" fsPath
false
else
let code = sprintf """
#load "%s"
open %s
System.IO.File.WriteAllText("%s", render())
""" fsPath moduleName (Path.Combine(folder, outputName))
let tempFile = Path.GetTempFileName() + ".fsx"
File.WriteAllText(tempFile, code)
let psi = new System.Diagnostics.ProcessStartInfo("dotnet", sprintf "fsi %s" tempFile)
psi.WorkingDirectory <- Environment.CurrentDirectory
psi.RedirectStandardOutput <- true
psi.RedirectStandardError <- true
psi.UseShellExecute <- false
use proc = System.Diagnostics.Process.Start(psi)
proc.WaitForExit()
File.Delete(tempFile)
if proc.ExitCode = 0 then
printfn " ✓ %s/%s" folder outputName
true
else
printfn " ❌ Failed to generate %s/%s" folder outputName
printfn " %s" (proc.StandardError.ReadToEnd())
false
let generateAll() =
printfn ""
printfn "╔════════════════════════════════════════════════════════════╗"
printfn "║ Generating Zensical Configurations ║"
printfn "╚════════════════════════════════════════════════════════════╝"
printfn ""
let results = [
generate "main" "Main.Zensical" "zensical.toml"
generate "docs" "Docs.Zensical" "zensical.toml"
generate "app" "App.Zensical" "zensical.toml"
generate "blog" "Blog.Zensical" "zensical.toml"
]
printfn ""
if List.forall id results then
printfn "✅ All configurations generated successfully!"
else
printfn "⚠️ Some configurations failed to generate."
printfn ""
// =============================================================================
// YML → .fs Sync Functions
// =============================================================================
/// Convert a kebab-case name to PascalCase module name
let toPascalCase (name: string) =
name.Split([|'-'; '_'|], StringSplitOptions.RemoveEmptyEntries)
|> Array.map (fun part ->
if String.IsNullOrEmpty(part) then ""
else Char.ToUpper(part.[0]).ToString() + part.[1..])
|> String.concat ""
/// Determine the module path based on file name
let getModulePath (fileName: string) =
match fileName with
| "dependabot" -> "Config.Dependabot"
| name when name.StartsWith("deploy-") ->
let suffix = name.Substring(7) |> toPascalCase
sprintf "Config.Workflows.Deploy%s" suffix
| name when name.StartsWith("pr-") ->
let suffix = name.Substring(3) |> toPascalCase
sprintf "Config.Workflows.Pr%s" suffix
| name when name.Contains("dependency") ->
"Config.Workflows.DependencyUpdate"
| name when name.StartsWith("build-and-deploy") ->
"Config.Workflows.BuildAndDeploy"
| name when name.StartsWith("hello-world") ->
"Workflows.HelloWorld"
| _ ->
sprintf "Config.Workflows.%s" (toPascalCase fileName)
/// Check if a YML file should be synced (not sharpendabot)
let shouldSync (fileName: string) =
let name = Path.GetFileNameWithoutExtension(fileName)
name <> "sharpendabot"
/// Escape content for F# triple-quoted string
let escapeForTripleQuoted (content: string) =
if content.Contains("""") then
let parts = content.Split([|""""|], StringSplitOptions.None)
parts |> String.concat """ + "\"\"\"" + """
else
content
/// Generate the F# source file content
let generateFsContent (modulePath: string) (ymlContent: string) (sourceFile: string) =
let escapedContent = escapeForTripleQuoted ymlContent
sprintf """module %s
/// <summary>
/// Auto-generated from %s
/// This workflow was encoded back from its YML form.
/// </summary>
let content = """
%s
"""
let render() = content
""" modulePath sourceFile escapedContent
/// Sync a single YML file to its .fs equivalent
let syncYmlFile (ymlPath: string) (configDir: string) =
let fileName = Path.GetFileNameWithoutExtension(ymlPath)
let fsPath = Path.Combine(configDir, fileName + ".fs")
printfn " Syncing: %s → %s" (Path.GetFileName(ymlPath)) (Path.GetFileName(fsPath))
let ymlContent = File.ReadAllText(ymlPath)
let modulePath = getModulePath fileName
let fsContent = generateFsContent modulePath ymlContent (Path.GetFileName(ymlPath))
File.WriteAllText(fsPath, fsContent)
printfn " ✓ Encoded: %s" fsPath
/// Sync all YML files to .fs
let syncAllYml() =
printfn ""
printfn "╔════════════════════════════════════════════════════════════╗"
printfn "║ Syncing YML → .fs ║"
printfn "╚════════════════════════════════════════════════════════════╝"
printfn ""
let workflowsDir = ".github/workflows"
let configDir = ".github/config"
if not (Directory.Exists(workflowsDir)) then
printfn "❌ Workflows directory not found: %s" workflowsDir
()
else
Directory.CreateDirectory(configDir) |> ignore
let ymlFiles = Directory.GetFiles(workflowsDir, "*.yml")
let mutable syncedCount = 0
for ymlFile in ymlFiles do
if shouldSync ymlFile then
try
syncYmlFile ymlFile configDir
syncedCount <- syncedCount + 1
with
| ex ->
printfn " ✗ Failed to sync %s: %s" (Path.GetFileName(ymlFile)) ex.Message
else
printfn " ⏭ Skipped (authority): %s" (Path.GetFileName(ymlFile))
printfn ""
printfn "✅ Synced %d workflow(s)" syncedCount
printfn ""
// =============================================================================
// Entry Point
// =============================================================================
match Environment.GetCommandLineArgs() |> Array.tryLast with
| Some "all" | None -> generateAll()
| Some "main" -> generate "main" "Main.Zensical" "zensical.toml" |> ignore
| Some "docs" -> generate "docs" "Docs.Zensical" "zensical.toml" |> ignore
| Some "app" -> generate "app" "App.Zensical" "zensical.toml" |> ignore
| Some "blog" -> generate "blog" "Blog.Zensical" "zensical.toml" |> ignore
| Some "sync" -> syncAllYml()
| Some arg ->
printfn "Unknown argument: %s" arg
printfn ""
printfn "Usage:"
printfn " dotnet fsi GenerateConfig.fsx all # Generate all configs"
printfn " dotnet fsi GenerateConfig.fsx main # Generate main only"
printfn " dotnet fsi GenerateConfig.fsx docs # Generate docs only"
printfn " dotnet fsi GenerateConfig.fsx app # Generate app only"
printfn " dotnet fsi GenerateConfig.fsx blog # Generate blog only"
printfn " dotnet fsi GenerateConfig.fsx sync # Sync YML → .fs"