public async Task Connect(string name = "") { if (string.IsNullOrEmpty(name)) { name = Option.CurrentRemote; } Logger.LogInformation($"Connect to remote {name}."); if (Option.Remotes.TryGetValue(name, out var remote)) { Logger.LogInformation($"Detect remote {remote.Name} ({Enum.GetName(typeof(RemoteType), remote.Type)})."); switch (remote.Type) { case RemoteType.LocalFS: Remote = new FileSystemBlogService( new PhysicalFileProvider(remote.Uri).AsFileProvider()); break; case RemoteType.RemoteFS: { var client = HttpClientFactory.CreateClient(); client.BaseAddress = new Uri(remote.Uri); Remote = new FileSystemBlogService( new HttpFileProvider(client)); } break; case RemoteType.Api: { var client = HttpClientFactory.CreateClient(); client.BaseAddress = new Uri(remote.Uri); Remote = new ApiBlogService(client); } break; case RemoteType.Git: { FSBuilder builder = new FSBuilder(Environment.CurrentDirectory); Logger.LogInformation("Pull git repository."); try { using var repo = new Repository(GitTempFolder); // Credential information to fetch LibGit2Sharp.PullOptions options = new LibGit2Sharp.PullOptions(); // User information to create a merge commit var signature = new LibGit2Sharp.Signature( new Identity("AcBlog.Tools.Sdk", "tools.sdk@acblog"), DateTimeOffset.Now); // Pull LibGit2Sharp.Commands.Pull(repo, signature, options); } catch { builder.EnsureDirectoryEmpty(GitTempFolder); Repository.Clone(remote.Uri, GitTempFolder); } Remote = new FileSystemBlogService( new PhysicalFileProvider(Path.Join(Environment.CurrentDirectory, GitTempFolder)).AsFileProvider()); } break; } Remote.PostService.Context.Token = remote.Token; Option.CurrentRemote = name; await SaveOption(); } else { throw new Exception("No remote"); } }
// full: delete diff post for api remote public async Task Push(string name = "", bool full = false) { if (string.IsNullOrEmpty(name)) { name = Option.CurrentRemote; } Logger.LogInformation($"Push to remote {name}."); if (Option.Remotes.TryGetValue(name, out var remote)) { Logger.LogInformation($"Detect remote {remote.Name} ({Enum.GetName(typeof(RemoteType), remote.Type)})."); switch (remote.Type) { case RemoteType.LocalFS: { await toLocalFS(remote); } break; case RemoteType.RemoteFS: { throw new NotSupportedException("Not support pushing to remote file system, please push to local file system and sync to remote."); } case RemoteType.Api: { await Connect(name); Logger.LogInformation($"Fetch remote posts."); HashSet <string> remoteIds = (await Remote.PostService.All()).ToHashSet(); foreach (var item in await Local.PostService.GetAllPosts()) { if (item is null) { continue; } Logger.LogInformation($"Loaded {item.Id}: {item.Title}"); if (remoteIds.Contains(item.Id)) { var result = await Remote.PostService.Update(item); if (result) { Logger.LogInformation($"Updated {item.Id}"); } else { Logger.LogError($"Failed to update {item.Id}"); } } else { var result = await Remote.PostService.Create(item); if (result is null) { Logger.LogError($"Failed to create {item.Id}"); } else { Logger.LogInformation($"Created {item.Id}"); } } remoteIds.Remove(item.Id); } if (full) { foreach (var v in remoteIds) { var result = await Remote.PostService.Delete(v); if (result) { Logger.LogInformation($"Deleted {v}."); } else { Logger.LogError($"Failed to deleted {v}."); } } } } break; case RemoteType.Git: { await Connect(name); string tempDist = Path.Join(Environment.CurrentDirectory, "temp/dist"); Logger.LogInformation("Generate data."); await toLocalFS(new RemoteOption { Uri = tempDist, Type = RemoteType.LocalFS, Name = remote.Name }); FSExtensions.CopyDirectory(tempDist, GitTempFolder); Logger.LogInformation("Load git config."); string userName = Option.Properties[$"remote.{remote.Name}.git.username"], password = Option.Properties[$"remote.{remote.Name}.git.password"]; { if (string.IsNullOrEmpty(userName)) { userName = ConsoleExtensions.Input("Input username: "******"Input password: "******"Commit to git."); LibGit2Sharp.Commands.Stage(repo, "*"); var signature = new LibGit2Sharp.Signature( new Identity("AcBlog.Tools.Sdk", "tools.sdk@acblog"), DateTimeOffset.Now); repo.Commit(DateTimeOffset.Now.ToString(), signature, signature, new CommitOptions { AllowEmptyCommit = true }); Logger.LogInformation($"Push to {repo.Head.RemoteName}."); PushOptions options = new LibGit2Sharp.PushOptions(); options.CredentialsProvider = new CredentialsHandler( (url, usernameFromUrl, types) => new UsernamePasswordCredentials() { Username = string.IsNullOrEmpty(userName) ? usernameFromUrl : userName, Password = password }); repo.Network.Push(repo.Head, options); } } break; } } else { throw new Exception("No remote"); } async Task toLocalFS(RemoteOption remote) { FSBuilder fsBuilder = new FSBuilder(remote.Uri); fsBuilder.EnsureDirectoryEmpty(); List <Post> posts = new List <Post>(); foreach (var item in await Local.PostService.GetAllPosts()) { if (item is null) { continue; } Logger.LogInformation($"Loaded {item.Id}: {item.Title}"); posts.Add(item); } Logger.LogInformation("Build data."); { BlogOptions options = await Local.GetOptions(); BlogBuilder builder = new BlogBuilder(options, Path.Join(remote.Uri)); await builder.Build(); } { PostRepositoryBuilder builder = new PostRepositoryBuilder(posts, Path.Join(remote.Uri, "posts")); await builder.Build(); } { var baseAddress = Option.Properties[$"remote.{remote.Name}.generator.baseAddress"]; if (!string.IsNullOrEmpty(baseAddress)) { Logger.LogInformation("Build sitemap."); var sub = fsBuilder.CreateSubDirectoryBuilder("Site"); { var siteMapBuilder = await Local.BuildSitemap(baseAddress); using var st = sub.GetFileRewriteStream("sitemap.xml"); using var writer = XmlWriter.Create(st); siteMapBuilder.Build().WriteTo(writer); } Logger.LogInformation("Build feed."); { var feed = await Local.BuildSyndication(baseAddress); using var st = sub.GetFileRewriteStream("atom.xml"); using var writer = XmlWriter.Create(st); feed.GetAtom10Formatter().WriteTo(writer); } } } } }
// full: delete diff post for api remote public async Task Push(string name = "", bool full = false) { if (string.IsNullOrEmpty(name)) { name = Option.CurrentRemote; } Logger.LogInformation($"Push to remote {name}."); if (Option.Remotes.TryGetValue(name, out var remote)) { Logger.LogInformation($"Detect remote {remote.Name} ({Enum.GetName(typeof(RemoteType), remote.Type)})."); switch (remote.Type) { case RemoteType.LocalFS: { await toLocalFS(remote); } break; case RemoteType.RemoteFS: { throw new NotSupportedException("Not support pushing to remote file system, please push to local file system and sync to remote."); } case RemoteType.Api: { await Connect(name); Logger.LogInformation($"Fetch remote posts."); await Remote.SetOptions(await Local.GetOptions()); await SyncRecordRepository(Local.PostService, Remote.PostService, full); await SyncRecordRepository(Local.PageService, Remote.PageService, full); await SyncRecordRepository(Local.LayoutService, Remote.LayoutService, full); } break; case RemoteType.Git: { await Connect(name); string tempDist = Path.Join(Environment.CurrentDirectory, "temp/dist"); Logger.LogInformation("Generate data."); await toLocalFS(new RemoteOption { Uri = tempDist, Type = RemoteType.LocalFS, Name = remote.Name }); FSExtensions.CopyDirectory(tempDist, GitTempFolder); Logger.LogInformation("Load git config."); string userName = Option.Properties[$"remote.{remote.Name}.git.username"], password = Option.Properties[$"remote.{remote.Name}.git.password"]; { if (string.IsNullOrEmpty(userName)) { userName = ConsoleExtensions.Input("Input username: "******"Input password: "******"Commit to git."); LibGit2Sharp.Commands.Stage(repo, "*"); var signature = new LibGit2Sharp.Signature( new Identity("AcBlog.Tools.Sdk", "tools.sdk@acblog"), DateTimeOffset.Now); repo.Commit(DateTimeOffset.Now.ToString(), signature, signature, new CommitOptions { AllowEmptyCommit = true }); Logger.LogInformation($"Push to {repo.Head.RemoteName}."); PushOptions options = new PushOptions { CredentialsProvider = new CredentialsHandler( (url, usernameFromUrl, types) => new UsernamePasswordCredentials() { Username = string.IsNullOrEmpty(userName) ? usernameFromUrl : userName, Password = password }) }; repo.Network.Push(repo.Head, options); } } break; } } else { throw new Exception("No remote"); } async Task toLocalFS(RemoteOption remote) { FSBuilder fsBuilder = new FSBuilder(remote.Uri); fsBuilder.EnsureDirectoryEmpty(); List <Post> posts = new List <Post>(); await foreach (var item in Local.PostService.GetAllItems().IgnoreNull()) { Logger.LogInformation($"Loaded Post {item.Id}: {item.Title}"); posts.Add(item); } List <Layout> layouts = new List <Layout>(); await foreach (var item in Local.LayoutService.GetAllItems().IgnoreNull()) { Logger.LogInformation($"Loaded Layout {item.Id}"); layouts.Add(item); } List <Page> pages = new List <Page>(); await foreach (var item in Local.PageService.GetAllItems().IgnoreNull()) { Logger.LogInformation($"Loaded Page {item.Id}: {item.Title}"); pages.Add(item); } var baseAddress = Option.Properties[$"remote.{remote.Name}.generator.baseAddress"]; List <Data.Models.File> files = new List <Data.Models.File>(); { string path = Path.Join(Environment.CurrentDirectory, AssetsPath); if (Directory.Exists(path)) { foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)) { var id = Path.GetRelativePath(Environment.CurrentDirectory, file).Replace('\\', '/'); Data.Models.File f = new Data.Models.File { Id = id, Uri = string.IsNullOrWhiteSpace(baseAddress) ? $"/{id}" : $"{baseAddress.TrimEnd('/')}/{id}" }; files.Add(f); } } } Logger.LogInformation("Build data."); { BlogOptions options = await Local.GetOptions(); BlogBuilder builder = new BlogBuilder(options, Path.Join(remote.Uri)); await builder.Build(); await builder.BuildPosts(posts); await builder.BuildLayouts(layouts); await builder.BuildPages(pages); await builder.BuildFiles(files); } { if (!string.IsNullOrEmpty(baseAddress)) { Logger.LogInformation("Build sitemap."); var sub = fsBuilder.CreateSubDirectoryBuilder("Site"); { var siteMapBuilder = await Local.BuildSitemap(baseAddress); await using var st = sub.GetFileRewriteStream("sitemap.xml"); await using var writer = XmlWriter.Create(st, new XmlWriterSettings { Async = true }); siteMapBuilder.Build().WriteTo(writer); } Logger.LogInformation("Build feed."); { var feed = await Local.BuildSyndication(baseAddress); await using (var st = sub.GetFileRewriteStream("atom.xml")) { await using var writer = XmlWriter.Create(st, new XmlWriterSettings { Async = true }); feed.GetAtom10Formatter().WriteTo(writer); } await using (var st = sub.GetFileRewriteStream("rss.xml")) { await using var writer = XmlWriter.Create(st, new XmlWriterSettings { Async = true }); feed.GetRss20Formatter().WriteTo(writer); } } } } { string assetsPath = Path.Join(Environment.CurrentDirectory, AssetsPath); if (Directory.Exists(assetsPath)) { Logger.LogInformation("Copy assets."); FSExtensions.CopyDirectory(assetsPath, Path.Join(remote.Uri, AssetsPath)); } } } }