diff --git a/Gitea.Declarative.Lib/Gitea.Declarative.Lib.fsproj b/Gitea.Declarative.Lib/Gitea.Declarative.Lib.fsproj
index 10a1102..b543b82 100644
--- a/Gitea.Declarative.Lib/Gitea.Declarative.Lib.fsproj
+++ b/Gitea.Declarative.Lib/Gitea.Declarative.Lib.fsproj
@@ -24,6 +24,7 @@
+
diff --git a/Gitea.Declarative.Lib/Gitea.fs b/Gitea.Declarative.Lib/Gitea.fs
index cf0ccd8..b46dc27 100644
--- a/Gitea.Declarative.Lib/Gitea.fs
+++ b/Gitea.Declarative.Lib/Gitea.fs
@@ -198,3 +198,102 @@ module Gitea =
)
|> Async.Parallel
|> fun a -> async.Bind (a, Array.iter id >> async.Return)
+
+ let reconcileUserErrors
+ (log : ILogger)
+ (getUserInput : string -> string)
+ (client : Gitea.Client)
+ (m : Map>)
+ =
+ let userInputLock = obj ()
+
+ m
+ |> Map.toSeq
+ |> Seq.map (fun (User user, err) ->
+ match err with
+ | AlignmentError.DoesNotExist desired ->
+ async {
+ let rand = Random ()
+
+ let pwd =
+ Array.init 15 (fun _ -> rand.Next (65, 65 + 25) |> byte)
+ |> System.Text.Encoding.ASCII.GetString
+
+ let options = Gitea.CreateUserOption ()
+ options.Email <- desired.Email
+ options.Username <- user
+ options.FullName <- user
+
+ options.Visibility <-
+ match desired.Visibility with
+ | None -> "public"
+ | Some v -> v
+
+ options.LoginName <- user
+ options.MustChangePassword <- Some true
+ options.Password <- pwd
+ let! _ = client.AdminCreateUser options |> Async.AwaitTask
+
+ lock
+ userInputLock
+ (fun () ->
+ log.LogCritical (
+ "Created user {User} with password {Password}, which you must now change",
+ user,
+ pwd
+ )
+ )
+
+ return ()
+ }
+ | AlignmentError.UnexpectedlyPresent ->
+ async {
+ lock
+ userInputLock
+ (fun () ->
+ let answer =
+ UserInput.getDefaultNo getUserInput $"User %s{user} unexpectedly present. Remove?"
+
+ if answer then
+ client.AdminDeleteUser(user).Result
+ else
+ log.LogCritical ("Refusing to delete user {User}, who is unexpectedly present.", user)
+ )
+ }
+ | AlignmentError.ConfigurationDiffers (desired, actual) ->
+ let updates = UserInfo.Resolve desired actual
+
+ async {
+ lock
+ userInputLock
+ (fun () ->
+ let body = Gitea.EditUserOption ()
+
+ for update in updates do
+ match update with
+ | UserInfoUpdate.Admin (desired, _) -> body.Admin <- desired
+ | UserInfoUpdate.Email (desired, _) -> body.Email <- desired
+ | UserInfoUpdate.Visibility (desired, _) -> body.Visibility <- desired
+ | UserInfoUpdate.Website (desired, actual) ->
+ // Per https://github.com/go-gitea/gitea/issues/17126,
+ // the website parameter can't currently be edited.
+ // This is a bug that is unlikely to be fixed.
+ let actual =
+ match actual with
+ | None -> ""
+ | Some uri -> uri.ToString ()
+
+ log.LogCritical (
+ "User {User} has conflicting website, desired {DesiredWebsite}, existing {ActualWebsite}, which a bug in Gitea means can't be reconciled via the API.",
+ user,
+ desired,
+ actual
+ )
+
+ body.LoginName <- user
+ client.AdminEditUser(user, body).Result |> ignore
+ )
+ }
+ )
+ |> Async.Parallel
+ |> fun a -> async.Bind (a, Array.iter id >> async.Return)
diff --git a/Gitea.Declarative.Lib/UserInput.fs b/Gitea.Declarative.Lib/UserInput.fs
new file mode 100644
index 0000000..46c5347
--- /dev/null
+++ b/Gitea.Declarative.Lib/UserInput.fs
@@ -0,0 +1,15 @@
+namespace Gitea.Declarative
+
+[]
+module internal UserInput =
+
+ let rec getDefaultNo (getUserInput : string -> string) (message : string) : bool =
+ let answer = getUserInput $"{message} (y/N): "
+
+ match answer with
+ | "y"
+ | "Y" -> true
+ | "n"
+ | "N"
+ | "" -> false
+ | _ -> getDefaultNo getUserInput message
diff --git a/Gitea.Declarative/Program.fs b/Gitea.Declarative/Program.fs
index fbb232a..f99c34f 100644
--- a/Gitea.Declarative/Program.fs
+++ b/Gitea.Declarative/Program.fs
@@ -40,116 +40,9 @@ module Program =
m
|> Map.iter (fun (User u) errMap -> errMap |> Map.iter (fun (RepoName r) err -> printfn $"%s{u}: %s{r}: {err}"))
- let rec getUserInputDefaultNo (getUserInput : unit -> string) (message : string) : bool =
- Console.Write $"${message} (y/N): "
- let answer = getUserInput ()
-
- match answer with
- | "y"
- | "Y" -> true
- | "n"
- | "N"
- | "" -> false
- | _ -> getUserInputDefaultNo getUserInput message
-
- let reconcileUserErrors
- (log : ILogger)
- (getUserInput : unit -> string)
- (client : Gitea.Client)
- (m : Map>)
- =
- let userInputLock = obj ()
-
- m
- |> Map.toSeq
- |> Seq.map (fun (User user, err) ->
- match err with
- | AlignmentError.DoesNotExist desired ->
- async {
- let rand = Random ()
-
- let pwd =
- Array.init 15 (fun _ -> rand.Next (65, 65 + 25) |> byte)
- |> System.Text.Encoding.ASCII.GetString
-
- let options = Gitea.CreateUserOption ()
- options.Email <- desired.Email
- options.Username <- user
- options.FullName <- user
-
- options.Visibility <-
- match desired.Visibility with
- | None -> "public"
- | Some v -> v
-
- options.LoginName <- user
- options.MustChangePassword <- Some true
- options.Password <- pwd
- let! _ = client.AdminCreateUser options |> Async.AwaitTask
-
- lock
- userInputLock
- (fun () ->
- log.LogCritical (
- "Created user {User} with password {Password}, which you must now change",
- user,
- pwd
- )
- )
-
- return ()
- }
- | AlignmentError.UnexpectedlyPresent ->
- async {
- lock
- userInputLock
- (fun () ->
- let answer =
- getUserInputDefaultNo getUserInput $"User %s{user} unexpectedly present. Remove?"
-
- if answer then
- client.AdminDeleteUser(user).Result
- else
- log.LogCritical ("Refusing to delete user {User}, who is unexpectedly present.", user)
- )
- }
- | AlignmentError.ConfigurationDiffers (desired, actual) ->
- let updates = UserInfo.Resolve desired actual
-
- async {
- lock
- userInputLock
- (fun () ->
- let body = Gitea.EditUserOption ()
-
- for update in updates do
- match update with
- | UserInfoUpdate.Admin (desired, _) -> body.Admin <- desired
- | UserInfoUpdate.Email (desired, _) -> body.Email <- desired
- | UserInfoUpdate.Visibility (desired, _) -> body.Visibility <- desired
- | UserInfoUpdate.Website (desired, actual) ->
- // Per https://github.com/go-gitea/gitea/issues/17126,
- // the website parameter can't currently be edited.
- // This is a bug that is unlikely to be fixed.
- let actual =
- match actual with
- | None -> ""
- | Some uri -> uri.ToString ()
-
- log.LogCritical (
- "User {User} has conflicting website, desired {DesiredWebsite}, existing {ActualWebsite}, which a bug in Gitea means can't be reconciled via the API.",
- user,
- desired,
- actual
- )
-
- body.LoginName <- user
- client.AdminEditUser(user, body).Result |> ignore
- )
- }
- )
- |> Async.Parallel
- |> fun a -> async.Bind (a, Array.iter id >> async.Return)
+ let getUserInput (s : string) : string =
+ Console.Write s
+ Console.ReadLine ()
[]
let main argv =
@@ -180,7 +73,7 @@ module Program =
}
use loggerProvider = new ConsoleLoggerProvider (options)
- let logger = loggerProvider.CreateLogger "Gitea.App"
+ let logger = loggerProvider.CreateLogger "Gitea.Declarative"
use client = new HttpClient ()
client.BaseAddress <- args.GiteaHost
@@ -189,14 +82,14 @@ module Program =
let client = Gitea.Client client
task {
- Console.WriteLine "Checking users..."
+ logger.LogInformation "Checking users..."
let! userErrors = Gitea.checkUsers config client
match userErrors with
| Ok () -> ()
- | Error errors -> do! reconcileUserErrors logger Console.ReadLine client errors
+ | Error errors -> do! Gitea.reconcileUserErrors logger getUserInput client errors
- Console.WriteLine "Checking repos..."
+ logger.LogInformation "Checking repos..."
let! repoErrors = Gitea.checkRepos config client
match repoErrors with