mirror of
https://github.com/Smaug123/gitea-repo-config
synced 2025-10-05 07:28:40 +00:00
Add refresh-auth (#64)
This commit is contained in:
@@ -149,6 +149,16 @@ module Gitea =
|
|||||||
Error (Map.ofArray errors)
|
Error (Map.ofArray errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let private createPushMirrorOption (target : Uri) (githubToken : string) : Gitea.CreatePushMirrorOption =
|
||||||
|
let options = Gitea.CreatePushMirrorOption ()
|
||||||
|
options.SyncOnCommit <- Some true
|
||||||
|
options.RemoteAddress <- target.ToString ()
|
||||||
|
options.RemoteUsername <- githubToken
|
||||||
|
options.RemotePassword <- githubToken
|
||||||
|
options.Interval <- "8h0m0s"
|
||||||
|
|
||||||
|
options
|
||||||
|
|
||||||
let reconcileDifferingConfiguration
|
let reconcileDifferingConfiguration
|
||||||
(logger : ILogger)
|
(logger : ILogger)
|
||||||
(client : IGiteaClient)
|
(client : IGiteaClient)
|
||||||
@@ -369,13 +379,8 @@ module Gitea =
|
|||||||
| Some token ->
|
| Some token ->
|
||||||
async {
|
async {
|
||||||
logger.LogInformation ("Setting up push mirror on {User}:{Repo}", user, repoName)
|
logger.LogInformation ("Setting up push mirror on {User}:{Repo}", user, repoName)
|
||||||
let options = Gitea.CreatePushMirrorOption ()
|
let pushMirrorOption = createPushMirrorOption desired.GitHubAddress token
|
||||||
options.SyncOnCommit <- Some true
|
let! _ = client.RepoAddPushMirror (user, repoName, pushMirrorOption) |> Async.AwaitTask
|
||||||
options.RemoteAddress <- (desired.GitHubAddress : Uri).ToString ()
|
|
||||||
options.RemoteUsername <- token
|
|
||||||
options.RemotePassword <- token
|
|
||||||
options.Interval <- "8h0m0s"
|
|
||||||
let! _ = client.RepoAddPushMirror (user, repoName, options) |> Async.AwaitTask
|
|
||||||
return ()
|
return ()
|
||||||
}
|
}
|
||||||
| Some desired, Some actual ->
|
| Some desired, Some actual ->
|
||||||
@@ -721,3 +726,88 @@ module Gitea =
|
|||||||
)
|
)
|
||||||
|> Async.Parallel
|
|> Async.Parallel
|
||||||
|> fun a -> async.Bind (a, Array.iter id >> async.Return)
|
|> fun a -> async.Bind (a, Array.iter id >> async.Return)
|
||||||
|
|
||||||
|
let toRefresh (client : IGiteaClient) : Async<Map<User, Map<RepoName, Gitea.PushMirror list>>> =
|
||||||
|
async {
|
||||||
|
let! users =
|
||||||
|
Array.getPaginated (fun page limit ->
|
||||||
|
client.AdminGetAllUsers (Some page, Some limit) |> Async.AwaitTask
|
||||||
|
)
|
||||||
|
|
||||||
|
let! results =
|
||||||
|
users
|
||||||
|
|> Seq.map (fun user ->
|
||||||
|
async {
|
||||||
|
let! repos =
|
||||||
|
Array.getPaginated (fun page count ->
|
||||||
|
client.UserListRepos (user.LoginName, Some page, Some count) |> Async.AwaitTask
|
||||||
|
)
|
||||||
|
|
||||||
|
let! pushMirrorResults =
|
||||||
|
repos
|
||||||
|
|> Seq.map (fun r ->
|
||||||
|
async {
|
||||||
|
let! mirrors =
|
||||||
|
Array.getPaginated (fun page count ->
|
||||||
|
Async.AwaitTask (
|
||||||
|
client.RepoListPushMirrors (
|
||||||
|
user.LoginName,
|
||||||
|
r.Name,
|
||||||
|
Some page,
|
||||||
|
Some count
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return RepoName r.Name, mirrors
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> Async.Parallel
|
||||||
|
|
||||||
|
return User user.LoginName, Map.ofArray pushMirrorResults
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> Async.Parallel
|
||||||
|
|
||||||
|
return results |> Map.ofArray
|
||||||
|
}
|
||||||
|
|
||||||
|
let refreshAuth
|
||||||
|
(logger : ILogger)
|
||||||
|
(client : IGiteaClient)
|
||||||
|
(githubToken : string)
|
||||||
|
(instructions : Map<User, Map<RepoName, Gitea.PushMirror list>>)
|
||||||
|
: Async<unit>
|
||||||
|
=
|
||||||
|
instructions
|
||||||
|
|> Map.toSeq
|
||||||
|
|> Seq.collect (fun (User user, repos) ->
|
||||||
|
Map.toSeq repos
|
||||||
|
|> Seq.collect (fun (RepoName repoName, mirrors) ->
|
||||||
|
mirrors
|
||||||
|
|> Seq.map (fun mirror ->
|
||||||
|
async {
|
||||||
|
logger.LogInformation (
|
||||||
|
"Refreshing push mirror on {User}:{Repo} to {PushMirrorRemote}",
|
||||||
|
user,
|
||||||
|
repoName,
|
||||||
|
mirror.RemoteAddress
|
||||||
|
)
|
||||||
|
|
||||||
|
let option = createPushMirrorOption (Uri mirror.RemoteAddress) githubToken
|
||||||
|
|
||||||
|
option.Interval <- mirror.Interval
|
||||||
|
option.SyncOnCommit <- mirror.SyncOnCommit
|
||||||
|
|
||||||
|
let! newMirror = Async.AwaitTask (client.RepoAddPushMirror (user, repoName, option))
|
||||||
|
|
||||||
|
let! deleteOldMirror =
|
||||||
|
Async.AwaitTask (client.RepoDeletePushMirror (user, repoName, mirror.RemoteName))
|
||||||
|
|
||||||
|
return ()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Async.Parallel
|
||||||
|
|> Async.map (Array.iter id)
|
||||||
|
@@ -17,6 +17,8 @@ type IGiteaClient =
|
|||||||
loginName : string * userName : string * page : int64 option * count : int64 option ->
|
loginName : string * userName : string * page : int64 option * count : int64 option ->
|
||||||
Gitea.PushMirror array Task
|
Gitea.PushMirror array Task
|
||||||
|
|
||||||
|
abstract RepoDeletePushMirror : user : string * repo : string * remoteName : string -> unit Task
|
||||||
|
|
||||||
abstract RepoListBranchProtection : loginName : string * userName : string -> Gitea.BranchProtection array Task
|
abstract RepoListBranchProtection : loginName : string * userName : string -> Gitea.BranchProtection array Task
|
||||||
abstract RepoDeleteBranchProtection : user : string * repo : string * branch : string -> unit Task
|
abstract RepoDeleteBranchProtection : user : string * repo : string * branch : string -> unit Task
|
||||||
|
|
||||||
@@ -57,6 +59,9 @@ module IGiteaClient =
|
|||||||
member _.RepoListPushMirrors (loginName, userName, page, count) =
|
member _.RepoListPushMirrors (loginName, userName, page, count) =
|
||||||
client.RepoListPushMirrors (loginName, userName, page, count)
|
client.RepoListPushMirrors (loginName, userName, page, count)
|
||||||
|
|
||||||
|
member _.RepoDeletePushMirror (loginName, repo, remoteName) =
|
||||||
|
client.RepoDeletePushMirror (loginName, repo, remoteName)
|
||||||
|
|
||||||
member _.RepoListBranchProtection (login, user) =
|
member _.RepoListBranchProtection (login, user) =
|
||||||
client.RepoListBranchProtection (login, user)
|
client.RepoListBranchProtection (login, user)
|
||||||
|
|
||||||
|
@@ -18,8 +18,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ArgsCrate.fs" />
|
<Compile Include="ArgsCrate.fs" />
|
||||||
<Compile Include="Result.fs" />
|
<Compile Include="Result.fs" />
|
||||||
|
<Compile Include="Utils.fs" />
|
||||||
<Compile Include="Reconcile.fs" />
|
<Compile Include="Reconcile.fs" />
|
||||||
<Compile Include="OutputSchema.fs" />
|
<Compile Include="OutputSchema.fs" />
|
||||||
|
<Compile Include="RefreshAuth.fs" />
|
||||||
<Compile Include="Verify.fs" />
|
<Compile Include="Verify.fs" />
|
||||||
<Compile Include="Program.fs" />
|
<Compile Include="Program.fs" />
|
||||||
<None Include="..\README.md" Pack="true" PackagePath="/" />
|
<None Include="..\README.md" Pack="true" PackagePath="/" />
|
||||||
|
@@ -22,6 +22,9 @@ module Program =
|
|||||||
ArgsCrate.make OutputSchemaArgs.OfParse OutputSchema.run)
|
ArgsCrate.make OutputSchemaArgs.OfParse OutputSchema.run)
|
||||||
|
|
||||||
"verify", ("Verify a `reconcile` configuration file", ArgsCrate.make VerifyArgs.OfParse Verify.run)
|
"verify", ("Verify a `reconcile` configuration file", ArgsCrate.make VerifyArgs.OfParse Verify.run)
|
||||||
|
|
||||||
|
"refresh-auth",
|
||||||
|
("Refresh authentication for push mirrors", ArgsCrate.make RefreshAuthArgs.OfParse RefreshAuth.run)
|
||||||
|]
|
|]
|
||||||
|> Map.ofArray
|
|> Map.ofArray
|
||||||
|
|
||||||
|
@@ -2,10 +2,7 @@ namespace Gitea.Declarative
|
|||||||
|
|
||||||
open System
|
open System
|
||||||
open System.IO
|
open System.IO
|
||||||
open System.Net.Http
|
|
||||||
open Argu
|
open Argu
|
||||||
open Microsoft.Extensions.Logging.Console
|
|
||||||
open Microsoft.Extensions.Options
|
|
||||||
open Microsoft.Extensions.Logging
|
open Microsoft.Extensions.Logging
|
||||||
|
|
||||||
type RunArgsFragment =
|
type RunArgsFragment =
|
||||||
@@ -61,28 +58,12 @@ module Reconcile =
|
|||||||
|
|
||||||
let config = GiteaConfig.get args.ConfigFile
|
let config = GiteaConfig.get args.ConfigFile
|
||||||
|
|
||||||
let options =
|
|
||||||
let options = ConsoleLoggerOptions ()
|
|
||||||
|
|
||||||
{ new IOptionsMonitor<ConsoleLoggerOptions> with
|
|
||||||
member _.Get _ = options
|
|
||||||
member _.CurrentValue = options
|
|
||||||
|
|
||||||
member _.OnChange _ =
|
|
||||||
{ new IDisposable with
|
|
||||||
member _.Dispose () = ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async {
|
async {
|
||||||
use loggerProvider = new ConsoleLoggerProvider (options)
|
use loggerProvider = Utils.createLoggerProvider ()
|
||||||
let logger = loggerProvider.CreateLogger "Gitea.Declarative"
|
let logger = loggerProvider.CreateLogger "Gitea.Declarative"
|
||||||
|
|
||||||
use client = new HttpClient ()
|
use httpClient = Utils.createHttpClient args.GiteaHost args.GiteaAdminApiToken
|
||||||
client.BaseAddress <- args.GiteaHost
|
let client = Gitea.Client httpClient |> IGiteaClient.fromReal
|
||||||
client.DefaultRequestHeaders.Add ("Authorization", $"token {args.GiteaAdminApiToken}")
|
|
||||||
|
|
||||||
let client = Gitea.Client client |> IGiteaClient.fromReal
|
|
||||||
|
|
||||||
logger.LogInformation "Checking users..."
|
logger.LogInformation "Checking users..."
|
||||||
let! userErrors = Gitea.checkUsers config client
|
let! userErrors = Gitea.checkUsers config client
|
||||||
|
66
Gitea.Declarative/RefreshAuth.fs
Normal file
66
Gitea.Declarative/RefreshAuth.fs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
namespace Gitea.Declarative
|
||||||
|
|
||||||
|
open System
|
||||||
|
open Argu
|
||||||
|
open Microsoft.Extensions.Logging
|
||||||
|
|
||||||
|
type RefreshAuthArgsFragment =
|
||||||
|
| [<ExactlyOnce ; EqualsAssignmentOrSpaced>] Gitea_Host of string
|
||||||
|
| [<ExactlyOnce ; EqualsAssignmentOrSpaced ; CustomAppSettings "GITEA_ADMIN_API_TOKEN">] Gitea_Admin_Api_Token of
|
||||||
|
string
|
||||||
|
| [<Unique ; EqualsAssignmentOrSpaced ; CustomAppSettings "GITHUB_API_TOKEN">] GitHub_Api_Token of string
|
||||||
|
| Dry_Run
|
||||||
|
|
||||||
|
interface IArgParserTemplate with
|
||||||
|
member s.Usage =
|
||||||
|
match s with
|
||||||
|
| Gitea_Host _ -> "the Gitea host, e.g. https://gitea.mydomain.com"
|
||||||
|
| Gitea_Admin_Api_Token _ ->
|
||||||
|
"a Gitea admin user's API token; can be read from the environment variable GITEA_ADMIN_API_TOKEN"
|
||||||
|
| GitHub_Api_Token _ ->
|
||||||
|
"a GitHub API token with read access to every desired sync-from-GitHub repo; can be read from the environment variable GITHUB_API_TOKEN"
|
||||||
|
| Dry_Run -> "don't actually update the mirrors"
|
||||||
|
|
||||||
|
type RefreshAuthArgs =
|
||||||
|
{
|
||||||
|
GiteaHost : Uri
|
||||||
|
GiteaAdminApiToken : string
|
||||||
|
GitHubApiToken : string
|
||||||
|
DryRun : bool
|
||||||
|
}
|
||||||
|
|
||||||
|
static member OfParse
|
||||||
|
(parsed : ParseResults<RefreshAuthArgsFragment>)
|
||||||
|
: Result<RefreshAuthArgs, ArguParseException>
|
||||||
|
=
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GiteaHost = parsed.GetResult RefreshAuthArgsFragment.Gitea_Host |> Uri
|
||||||
|
GiteaAdminApiToken = parsed.GetResult RefreshAuthArgsFragment.Gitea_Admin_Api_Token
|
||||||
|
GitHubApiToken = parsed.GetResult RefreshAuthArgsFragment.GitHub_Api_Token
|
||||||
|
DryRun = parsed.TryGetResult RefreshAuthArgsFragment.Dry_Run |> Option.isSome
|
||||||
|
}
|
||||||
|
|> Ok
|
||||||
|
with :? ArguParseException as e ->
|
||||||
|
Error e
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module RefreshAuth =
|
||||||
|
|
||||||
|
let run (args : RefreshAuthArgs) : Async<int> =
|
||||||
|
async {
|
||||||
|
use httpClient = Utils.createHttpClient args.GiteaHost args.GiteaAdminApiToken
|
||||||
|
let client = Gitea.Client httpClient |> IGiteaClient.fromReal
|
||||||
|
|
||||||
|
use loggerProvider = Utils.createLoggerProvider ()
|
||||||
|
let logger = loggerProvider.CreateLogger "Gitea.Declarative"
|
||||||
|
|
||||||
|
let! instructions = Gitea.toRefresh client
|
||||||
|
|
||||||
|
if args.DryRun then
|
||||||
|
logger.LogInformation ("Stopping due to --dry-run.")
|
||||||
|
else
|
||||||
|
do! Gitea.refreshAuth logger client args.GitHubApiToken instructions
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
32
Gitea.Declarative/Utils.fs
Normal file
32
Gitea.Declarative/Utils.fs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
namespace Gitea.Declarative
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Net.Http
|
||||||
|
open Microsoft.Extensions.Logging.Console
|
||||||
|
open Microsoft.Extensions.Options
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal Utils =
|
||||||
|
|
||||||
|
let createLoggerProvider () =
|
||||||
|
let options =
|
||||||
|
let options = ConsoleLoggerOptions ()
|
||||||
|
|
||||||
|
{ new IOptionsMonitor<ConsoleLoggerOptions> with
|
||||||
|
member _.Get _ = options
|
||||||
|
member _.CurrentValue = options
|
||||||
|
|
||||||
|
member _.OnChange _ =
|
||||||
|
{ new IDisposable with
|
||||||
|
member _.Dispose () = ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new ConsoleLoggerProvider (options)
|
||||||
|
|
||||||
|
let createHttpClient (host : Uri) (apiKey : string) =
|
||||||
|
let client = new HttpClient ()
|
||||||
|
client.BaseAddress <- host
|
||||||
|
client.DefaultRequestHeaders.Add ("Authorization", $"token {apiKey}")
|
||||||
|
|
||||||
|
client
|
@@ -75,6 +75,8 @@ module Client =
|
|||||||
|
|
||||||
member _.RepoAddPushMirror (user, repo, createPushMirrorOption) = failwith "Not implemented"
|
member _.RepoAddPushMirror (user, repo, createPushMirrorOption) = failwith "Not implemented"
|
||||||
|
|
||||||
|
member _.RepoDeletePushMirror (user, repo, remoteName) = failwith "Not implemented"
|
||||||
|
|
||||||
member _.RepoListPushMirrors (loginName, userName, page, count) = failwith "Not implemented"
|
member _.RepoListPushMirrors (loginName, userName, page, count) = failwith "Not implemented"
|
||||||
|
|
||||||
member _.RepoListBranchProtection (loginName, userName) = failwith "Not implemented"
|
member _.RepoListBranchProtection (loginName, userName) = failwith "Not implemented"
|
||||||
|
@@ -35,6 +35,7 @@ type GiteaClientMock =
|
|||||||
UserListRepos : string * int64 option * int64 option -> Gitea.Repository array Task
|
UserListRepos : string * int64 option * int64 option -> Gitea.Repository array Task
|
||||||
|
|
||||||
RepoAddPushMirror : string * string * Gitea.CreatePushMirrorOption -> Gitea.PushMirror Task
|
RepoAddPushMirror : string * string * Gitea.CreatePushMirrorOption -> Gitea.PushMirror Task
|
||||||
|
RepoDeletePushMirror : string * string * string -> unit Task
|
||||||
RepoListPushMirrors : string * string * int64 option * int64 option -> Gitea.PushMirror array Task
|
RepoListPushMirrors : string * string * int64 option * int64 option -> Gitea.PushMirror array Task
|
||||||
|
|
||||||
RepoListBranchProtection : string * string -> Gitea.BranchProtection array Task
|
RepoListBranchProtection : string * string -> Gitea.BranchProtection array Task
|
||||||
@@ -64,6 +65,7 @@ type GiteaClientMock =
|
|||||||
UserListRepos = fun _ -> failwith "Unimplemented"
|
UserListRepos = fun _ -> failwith "Unimplemented"
|
||||||
|
|
||||||
RepoAddPushMirror = fun _ -> failwith "Unimplemented"
|
RepoAddPushMirror = fun _ -> failwith "Unimplemented"
|
||||||
|
RepoDeletePushMirror = fun _ -> failwith "Unimplemented"
|
||||||
RepoListPushMirrors = fun _ -> failwith "Unimplemented"
|
RepoListPushMirrors = fun _ -> failwith "Unimplemented"
|
||||||
|
|
||||||
RepoListBranchProtection = fun _ -> failwith "Unimplemented"
|
RepoListBranchProtection = fun _ -> failwith "Unimplemented"
|
||||||
@@ -96,6 +98,9 @@ type GiteaClientMock =
|
|||||||
member this.RepoListPushMirrors (loginName, userName, page, count) =
|
member this.RepoListPushMirrors (loginName, userName, page, count) =
|
||||||
this.RepoListPushMirrors (loginName, userName, page, count)
|
this.RepoListPushMirrors (loginName, userName, page, count)
|
||||||
|
|
||||||
|
member this.RepoDeletePushMirror (loginName, userName, remoteName) =
|
||||||
|
this.RepoDeletePushMirror (loginName, userName, remoteName)
|
||||||
|
|
||||||
member this.RepoListBranchProtection (login, user) =
|
member this.RepoListBranchProtection (login, user) =
|
||||||
this.RepoListBranchProtection (login, user)
|
this.RepoListBranchProtection (login, user)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user