aboutsummaryrefslogtreecommitdiff
path: root/src/Commands/PublishCommand.cs
blob: 33a73299435c4148fff229e055e14512fc6ad85d (plain)
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

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;

using Typin.Console;
using Typin.Attributes;

using VNLib.Tools.Build.Executor.Model;
using VNLib.Tools.Build.Executor.Constants;
using VNLib.Tools.Build.Executor.Publishing;

namespace VNLib.Tools.Build.Executor.Commands
{
    [Command("publish", Description = "Runs publishig build steps on a completed build")]
    public sealed class PublishCommand(BuildPipeline pipeline, ConfigManager bm) : BaseCommand(pipeline, bm)
    {
        [CommandOption("minio", Description = "The path to upload the build artifacts")]
        public string? MinioPath { get; set; }

        [CommandOption("ftp", Description = "The FTP server address to upload the build artifacts. Enables FTP mode over s3")]
        public string? FtpServerAddress { get; set; }

        [CommandOption("sign", 's', Description = "Enables gpg signing of build artifacts")]
        public bool Sign { get; set; } = false;

        [CommandOption("gpg-key", 'k', Description = "Optional key to use when signing, otherwise uses the GPG default signing key")]
        public string? GpgKey { get; set; }

        [CommandOption("sleet-path", 'F', Description = "Specifies the Sleet feed index path")]
        public string? SleetPath { get; set; }

        [CommandOption("dry-run", 'd', Description = "Executes all publish steps without pushing the changes to the remote server")]
        public bool DryRun { get; set; }

        [CommandOption("output", 'o', Description = "Specifies the output directory for the published modules")]
        public string? CustomOutDir { get; set; }

        public override async ValueTask ExecStepsAsync(IConsole console)
        {
            //Specify custom output dir
            (Config.Index as Dirs)!.OutputDir = BuildDirs.GetOrCreateDir(Constants.Config.OUTPUT_DIR, CustomOutDir);

            IUploadManager uploads = GetUploadManager(console);
            IFeedManager? feed = Feeds.FirstOrDefault();

            //Optional gpg signer for signing published artifacts
            BuildPublisher pub = new(Config, new GpgSigner(Sign, GpgKey));

            console.WithForegroundColor(
                ConsoleColor.DarkGreen, 
                static o => o.Output.WriteLine("Publishing modules")
            );

            //Run publish steps
            await pipeline.OnPublishingAsync()
                .ConfigureAwait(false);

            console.WithForegroundColor(
                ConsoleColor.DarkGreen, 
                static o => o.Output.WriteLine("Preparing module output for upload")
            );

            //Prepare the output 
            await pipeline.PrepareOutputAsync(pub)
                .ConfigureAwait(false);

            //Run upload
            await pipeline.ManualUpload(pub, uploads)
                .ConfigureAwait(false);

            //Publish feeds
            if (feed is not null)
            {
                console.WithForegroundColor(
                    ConsoleColor.DarkGreen, 
                    static o => o.Output.WriteLine("Uploading feeds...")
                );

                //Exec feed upload
                await uploads.UploadDirectoryAsync(feed.FeedOutputDir);
            }

            console.WithForegroundColor(
                ConsoleColor.Green, 
                static o => o.Output.WriteLine("Upload build complete")
            );
        }

        public override IFeedManager[] Feeds => SleetPath is null ? [] : [SleetFeedManager.GetSleetFeed(SleetPath)];

        private MultiUploadManager GetUploadManager(IConsole console)
        {
            try
            {
                IUploadManager[] uploadMan = [];

                if (!string.IsNullOrWhiteSpace(MinioPath))
                {
                    console.Output.WriteLine("Creating Minio publisher");

                    uploadMan = [MinioUploadManager.Create(MinioPath), ..uploadMan];
                }
                
                if (!string.IsNullOrWhiteSpace(FtpServerAddress))
                {
                    console.Output.WriteLine("Using FTP publisher");

                    uploadMan = [FtpUploadManager.Create(FtpServerAddress), .. uploadMan];
                }

                if(uploadMan.Length == 0)
                {
                    console.WithForegroundColor(
                        ConsoleColor.DarkYellow,
                        static o => o.Output.WriteLine("No upload manager specified, output will be skipped")
                    );
                }

                return new MultiUploadManager(uploadMan);
            }
            catch(UriFormatException urie)
            {
                throw new BuildFailedException("Invalid server address", urie);
            }
        }

        private sealed class MultiUploadManager(params IUploadManager[] managers) : IUploadManager
        {
            private readonly IUploadManager[] _managers = managers;

            public async Task UploadDirectoryAsync(string path)
            {
                IEnumerable<Task> tasks = _managers.Select(m => m.UploadDirectoryAsync(path));
                
                await Task.WhenAll(tasks);
            }
        }
    }
}