• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 2271300743

19 Jan 2026 11:42AM UTC coverage: 81.376% (+1.7%) from 79.701%
2271300743

push

gitlab-ci

web-flow
Merge pull request #1879 from lluiscampos/MEN-8687-ci-debian-updates

MEN-8687: Update Debian base images for CI jobs

8791 of 10803 relevant lines covered (81.38%)

20310.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.36
/src/client_shared/conf/conf_cli_help.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <client_shared/conf.hpp>
16

17
#include <functional>
18
#include <iomanip>
19
#include <iostream>
20
#include <iterator>
21
#include <string>
22
#include <vector>
23

24
#include <common/common.hpp>
25

26
namespace mender {
27
namespace client_shared {
28
namespace conf {
29

30
using namespace std;
31

32
namespace common = mender::common;
33

34
const size_t max_width = 78;
35
const string indent = "   ";   // 3 spaces
36
const string separator = "  "; // 2 spaces
37

38
const CliOption help_option = {
39
        .long_option = "help",
40
        .short_option = "h",
41
        .description = "Show help and exit",
42
};
43

44
const vector<CliOption> common_global_options = {
45
        CliOption {
46
                .long_option = "config",
47
                .short_option = "c",
48
                .description = "Configuration FILE path",
49
                .default_value = DefaultPaths.conf_file,
50
                .parameter = "FILE",
51
        },
52
        CliOption {
53
                .long_option = "fallback-config",
54
                .short_option = "b",
55
                .description = "Fallback configuration FILE path",
56
                .default_value = DefaultPaths.fallback_conf_file,
57
                .parameter = "FILE",
58
        },
59
        CliOption {
60
                .long_option = "data",
61
                .description = "Deprecated; alias for --datastore",
62
                .parameter = "DIR",
63
        },
64
        CliOption {
65
                .long_option = "datastore",
66
                .short_option = "d",
67
                .description = "Mender state data DIRECTORY path",
68
                .default_value = DefaultPaths.data_store,
69
                .parameter = "DIR",
70
        },
71
        CliOption {
72
                .long_option = "log-file",
73
                .short_option = "L",
74
                .description = "FILE to log to",
75
                .parameter = "FILE",
76
        },
77
        CliOption {
78
                .long_option = "log-level",
79
                .short_option = "l",
80
                .description = "Set logging level",
81
                .default_value = "info",
82
                .parameter = "LEVEL",
83
        },
84
        CliOption {
85
                .long_option = "trusted-certs",
86
                .short_option = "E",
87
                .description = "Trusted server certificates FILE path",
88
                .parameter = "FILE",
89
        },
90
        CliOption {
91
                .long_option = "skipverify",
92
                .description = "Skip certificate verification",
93
        },
94
        CliOption {
95
                .long_option = "version",
96
                .short_option = "v",
97
                .description = "Print version and exit",
98
        },
99
        help_option,
100
};
101

102
const string common_description_append = R"(Global flag remarks:
103
   - Supported log levels incudes: 'trace', 'debug', 'info', 'warning', 'error', and
104
     'fatal'.
105

106
Environment variables:
107
   - MENDER_CONF_DIR - configuration (default: )"
108
                                                                                 + DefaultPaths.path_conf_dir + R"().
109
   - MENDER_DATA_DIR - identity, inventory and update modules (default: )"
110
                                                                                 + DefaultPaths.path_data_dir + R"().
111
   - MENDER_DATASTORE_DIR - runtime datastore (default: )"
112
                                                                                 + DefaultPaths.data_store + R"().)";
113

114
template <typename InputIterator>
115
using ColumnFormatter = function<string(typename iterator_traits<InputIterator>::value_type)>;
116

117
template <typename InputIterator>
118
void PrintInTwoColumns(
42✔
119
        InputIterator start,
120
        InputIterator end,
121
        ColumnFormatter<InputIterator> column_one_fmt,
122
        ColumnFormatter<InputIterator> column_two_fmt,
123
        ostream &stream) {
124
        // First pass to calculate the max size for the elements in the first column
125
        size_t column_one_size = 0;
126
        for (auto it = start; it != end; ++it) {
310✔
127
                if (column_one_fmt(*it).size() > column_one_size) {
536✔
128
                        column_one_size = column_one_fmt(*it).size();
144✔
129
                }
130
        }
131

132
        // The total with will be the size of the largest element + indent + separator
133
        const size_t column_one_width {column_one_size + indent.size() + separator.size()};
42✔
134
        // The second column takes the rest of the available width
135
        const size_t column_two_width {max_width - column_one_width};
42✔
136
        for (auto it = start; it != end; ++it) {
578✔
137
                stream << indent << setw(static_cast<int>(column_one_size)) << left << column_one_fmt(*it)
536✔
138
                           << separator;
268✔
139
                // Wrap around and align the text for the second column
140
                auto lines = common::JoinStringsMaxWidth(
804✔
141
                        common::SplitString(column_two_fmt(*it), " "), " ", column_two_width);
804✔
142
                stream << lines.front() << endl;
268✔
143
                for_each(lines.begin() + 1, lines.end(), [&stream, column_one_width](const string &l) {
466✔
144
                        stream << setw(static_cast<int>(column_one_width)) << left << " " << l << endl;
99✔
145
                });
146
        }
147
}
42✔
148

149
string PrintArgument(optional<CliArgument> argument, ostream &stream) {
12✔
150
        string cliarg;
151

152
        if (argument.has_value()) {
12✔
153
                if (argument.value().mandatory) {
5✔
154
                        cliarg = " " + argument.value().name;
8✔
155
                } else {
156
                        cliarg = " [" + argument.value().name + "]";
2✔
157
                }
158
        }
159
        return cliarg;
12✔
160
}
161

162
void PrintOptions(const vector<CliOption> &options, ostream &stream) {
27✔
163
        PrintInTwoColumns(
54✔
164
                options.begin(),
165
                options.end(),
166
                [](const CliOption &option) {
405✔
167
                        // Format: --long-option[ PARAM][, -l[ PARAM]]
168
                        string str = "--" + option.long_option;
405✔
169
                        if (!option.parameter.empty()) {
405✔
170
                                str += " " + option.parameter;
504✔
171
                        }
172
                        if (!option.short_option.empty()) {
405✔
173
                                str += ", -" + option.short_option;
630✔
174
                                if (!option.parameter.empty()) {
315✔
175
                                        str += " " + option.parameter;
426✔
176
                                }
177
                        }
178
                        return str;
405✔
179
                },
180
                [](const CliOption &option) {
179✔
181
                        // Format: description[ (default: DEFAULT)]
182
                        string str = option.description;
179✔
183
                        if (!option.default_value.empty()) {
179✔
184
                                str += " (default: " + option.default_value + ")";
134✔
185
                        }
186
                        return str;
179✔
187
                },
188
                stream);
189
}
27✔
190

191
void PrintCommandHelp(const string &cli_name, const CliCommand &command, ostream &stream) {
12✔
192
        stream << "NAME:" << endl;
12✔
193
        stream << indent << cli_name << " " << command.name;
12✔
194
        if (!command.description.empty()) {
12✔
195
                stream << " - " << command.description;
12✔
196
        }
197
        stream << endl << endl;
12✔
198

199
        stream << "USAGE:" << endl;
12✔
200
        stream << indent << cli_name << " [global options] " << command.name + " [command options]";
24✔
201
        stream << PrintArgument(command.argument, stream) << endl << endl;
12✔
202

203
        // Append --help option at the command level
204
        vector<CliOption> options_with_help = command.options;
12✔
205
        options_with_help.push_back(help_option);
12✔
206
        stream << "OPTIONS:" << endl;
12✔
207
        PrintOptions(options_with_help, stream);
12✔
208
}
12✔
209

210
void PrintCliHelp(const CliApp &cli, ostream &stream) {
15✔
211
        stream << "NAME:" << endl;
15✔
212
        stream << indent << cli.name;
15✔
213
        if (!cli.short_description.empty()) {
15✔
214
                stream << " - " << cli.short_description;
15✔
215
        }
216
        stream << endl << endl;
15✔
217

218
        stream << "USAGE:" << endl
15✔
219
                   << indent << cli.name << " [global options] command [command options] [arguments...]";
15✔
220
        stream << endl << endl;
15✔
221

222
        stream << "VERSION:" << endl << indent << kMenderVersion;
15✔
223
        stream << endl << endl;
15✔
224

225
        if (!cli.long_description.empty()) {
15✔
226
                stream << "DESCRIPTION:" << endl << indent << cli.long_description << endl;
15✔
227
                ;
228
                stream << common_description_append;
15✔
229
                stream << endl << endl;
15✔
230
        }
231

232
        stream << "COMMANDS:" << endl;
15✔
233
        PrintInTwoColumns(
30✔
234
                cli.commands.begin(),
235
                cli.commands.end(),
236
                [](const CliCommand &command) { return command.name; },
203✔
237
                [](const CliCommand &command) { return command.description; },
89✔
238
                stream);
239
        stream << endl;
15✔
240

241
        stream << "GLOBAL OPTIONS:" << endl;
15✔
242
        PrintOptions(common_global_options, stream);
15✔
243
}
15✔
244

245
bool FindCmdlineHelpArg(vector<string>::const_iterator start, vector<string>::const_iterator end) {
141✔
246
        bool found = false;
247

248
        conf::CmdlineOptionsIterator opts_iter(
249
                start, end, {}, CommandOptsSetWithoutValue(vector<CliOption> {help_option}));
423✔
250
        auto ex_opt_val = opts_iter.Next();
141✔
251
        while (ex_opt_val && ((ex_opt_val.value().option != "") || (ex_opt_val.value().value != ""))) {
141✔
252
                auto opt_val = ex_opt_val.value();
×
253
                if ((opt_val.option == "--help") || (opt_val.option == "-h")) {
×
254
                        found = true;
255
                        break;
256
                }
257
                ex_opt_val = opts_iter.Next();
×
258
        }
×
259

260
        return found;
141✔
261
}
282✔
262

263
void PrintCliCommandHelp(const CliApp &cli, const string &command_name, ostream &stream) {
19✔
264
        auto match_on_name = [command_name](const CliCommand &cmd) { return cmd.name == command_name; };
145✔
265

266
        auto cmd = std::find_if(cli.commands.begin(), cli.commands.end(), match_on_name);
19✔
267
        if (cmd != cli.commands.end()) {
19✔
268
                PrintCommandHelp(cli.name, *cmd, stream);
12✔
269
        } else {
270
                PrintCliHelp(cli, stream);
7✔
271
        }
272
}
19✔
273

274
const OptsSet OptsSetFromCliOpts(const vector<CliOption> &options, bool without_value) {
743✔
275
        OptsSet opts {};
743✔
276
        for (auto const &opt : options) {
4,452✔
277
                if ((without_value && opt.parameter.empty())
1,925✔
278
                        || (!without_value && !opt.parameter.empty())) {
3,709✔
279
                        if (!opt.long_option.empty()) {
1,925✔
280
                                opts.insert("--" + opt.long_option);
3,850✔
281
                        }
282
                        if (!opt.short_option.empty()) {
1,925✔
283
                                opts.insert("-" + opt.short_option);
2,866✔
284
                        }
285
                }
286
        }
287
        return opts;
743✔
288
}
289

290
const OptsSet GlobalOptsSetWithValue() {
161✔
291
        return OptsSetFromCliOpts(common_global_options, false);
161✔
292
}
293

294
const OptsSet GlobalOptsSetWithoutValue() {
161✔
295
        return OptsSetFromCliOpts(common_global_options, true);
161✔
296
}
297

298
const OptsSet CommandOptsSetWithValue(const vector<CliOption> &options) {
140✔
299
        return OptsSetFromCliOpts(options, false);
140✔
300
}
301

302
const OptsSet CommandOptsSetWithoutValue(const vector<CliOption> &options) {
281✔
303
        return OptsSetFromCliOpts(options, true);
281✔
304
}
305

306
} // namespace conf
307
} // namespace client_shared
308
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc