Skip to content

Commit

Permalink
bundle-uri: parse bundle list in config format
Browse files Browse the repository at this point in the history
When a bundle provider wants to operate independently from a Git remote,
they want to provide a single, consistent URI that users can use in
their 'git clone --bundle-uri' commands. At this point, the Git client
expects that URI to be a single bundle that can be unbundled and used to
bootstrap the rest of the clone from the Git server. This single bundle
cannot be re-used to assist with future incremental fetches.

To allow for the incremental fetch case, teach Git to understand a
bundle list that could be advertised at an independent bundle URI. Such
a bundle list is likely to be inspected by human readers, even if only
by the bundle provider creating the list. For this reason, we can take
our expected "key=value" pairs and instead format them using Git config
format.

Create parse_bundle_list_in_config_format() to parse a file in config
format and convert that into a 'struct bundle_list' filled with its
understanding of the contents.

Be careful to call git_config_from_file_with_options() because the
default action for git_config_from_file() is to die() on a parsing
error. The current warning isn't particularly helpful if it arises to a
user, but it will be made more verbose at a higher layer later.

Update 'test-tool bundle-uri' to take this config file format as input.
It uses a filename instead of stdin because there is no existing way to
parse a FILE pointer in the config machinery. Using
git_config_from_mem() is overly complicated and more likely to introduce
bugs than this simpler version. I would rather have a slightly confusing
test helper than complicated product code.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
  • Loading branch information
derrickstolee committed Jul 25, 2022
1 parent a880cf4 commit c79d0b7
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 9 deletions.
29 changes: 29 additions & 0 deletions bundle-uri.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "run-command.h"
#include "hashmap.h"
#include "pkt-line.h"
#include "config.h"

static int compare_bundles(const void *hashmap_cmp_fn_data,
const struct hashmap_entry *he1,
Expand Down Expand Up @@ -172,6 +173,34 @@ static int bundle_list_update(const char *key, const char *value,
return 0;
}

static int config_to_bundle_list(const char *key, const char *value, void *data)
{
struct bundle_list *list = data;
return bundle_list_update(key, value, list);
}

int parse_bundle_list_in_config_format(const char *uri,
const char *filename,
struct bundle_list *list)
{
int result;
struct config_options opts = {
.error_action = CONFIG_ERROR_ERROR,
};

list->mode = BUNDLE_MODE_NONE;
result = git_config_from_file_with_options(config_to_bundle_list,
filename, list,
&opts);

if (!result && list->mode == BUNDLE_MODE_NONE) {
warning(_("bundle list at '%s' has no mode"), uri);
result = 1;
}

return result;
}

static int find_temp_filename(struct strbuf *name)
{
int fd;
Expand Down
10 changes: 10 additions & 0 deletions bundle-uri.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ int for_all_bundles_in_list(struct bundle_list *list,
struct FILE;
void print_bundle_list(FILE *fp, struct bundle_list *list);

/**
* A bundle URI may point to a bundle list where the key=value
* pairs are provided in config file format. This method is
* exposed publicly for testing purposes.
*/

int parse_bundle_list_in_config_format(const char *uri,
const char *filename,
struct bundle_list *list);

/**
* Fetch data from the given 'uri' and unbundle the bundle data found
* based on that information.
Expand Down
45 changes: 36 additions & 9 deletions t/helper/test-bundle-uri.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,52 @@
#include "strbuf.h"
#include "string-list.h"

static int cmd__bundle_uri_parse_key_values(int argc, const char **argv)
enum input_mode {
KEY_VALUE_PAIRS,
CONFIG_FILE,
};

static int cmd__bundle_uri_parse(int argc, const char **argv, enum input_mode mode)
{
const char *usage[] = {
const char *key_value_usage[] = {
"test-tool bundle-uri parse-key-values <in",
NULL
};
const char *config_usage[] = {
"test-tool bundle-uri parse-config <input>",
NULL
};
struct option options[] = {
OPT_END(),
};
const char **usage = key_value_usage;
struct strbuf sb = STRBUF_INIT;
struct bundle_list list;
int err = 0;

argc = parse_options(argc, argv, NULL, options, usage, 0);
if (argc)
goto usage;
if (mode == CONFIG_FILE)
usage = config_usage;

argc = parse_options(argc, argv, NULL, options, usage,
PARSE_OPT_STOP_AT_NON_OPTION);

init_bundle_list(&list);
while (strbuf_getline(&sb, stdin) != EOF) {
if (bundle_uri_parse_line(&list, sb.buf) < 0)
err = error("bad line: '%s'", sb.buf);

switch (mode) {
case KEY_VALUE_PAIRS:
if (argc)
goto usage;
while (strbuf_getline(&sb, stdin) != EOF) {
if (bundle_uri_parse_line(&list, sb.buf) < 0)
err = error("bad line: '%s'", sb.buf);
}
break;

case CONFIG_FILE:
if (argc != 1)
goto usage;
err = parse_bundle_list_in_config_format("<uri>", argv[0], &list);
break;
}
strbuf_release(&sb);

Expand Down Expand Up @@ -55,7 +80,9 @@ int cmd__bundle_uri(int argc, const char **argv)
goto usage;

if (!strcmp(argv[1], "parse-key-values"))
return cmd__bundle_uri_parse_key_values(argc - 1, argv + 1);
return cmd__bundle_uri_parse(argc - 1, argv + 1, KEY_VALUE_PAIRS);
if (!strcmp(argv[1], "parse-config"))
return cmd__bundle_uri_parse(argc - 1, argv + 1, CONFIG_FILE);
error("there is no test-tool bundle-uri tool '%s'", argv[1]);

usage:
Expand Down
50 changes: 50 additions & 0 deletions t/t5750-bundle-uri-parse.sh
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,54 @@ test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty lines' '
test_cmp_config_output expect actual
'

test_expect_success 'parse config format: just URIs' '
cat >expect <<-\EOF &&
[bundle]
version = 1
mode = all
[bundle "one"]
uri = http://example.com/bundle.bdl
[bundle "two"]
uri = https://example.com/bundle.bdl
[bundle "three"]
uri = file:///usr/share/git/bundle.bdl
EOF
test-tool bundle-uri parse-config expect >actual 2>err &&
test_must_be_empty err &&
test_cmp_config_output expect actual
'

test_expect_success 'parse config format edge cases: empty key or value' '
cat >in1 <<-\EOF &&
= bogus-value
EOF
cat >err1 <<-EOF &&
error: bad config line 1 in file in1
EOF
cat >expect <<-\EOF &&
[bundle]
version = 1
mode = <unknown>
EOF
test_must_fail test-tool bundle-uri parse-config in1 >actual 2>err &&
test_cmp err1 err &&
test_cmp_config_output expect actual &&
cat >in2 <<-\EOF &&
bogus-key =
EOF
cat >err2 <<-EOF &&
warning: bundle list at '\''<uri>'\'' has no mode
EOF
test_must_fail test-tool bundle-uri parse-config in2 >actual 2>err &&
test_cmp err2 err &&
test_cmp_config_output expect actual
'

test_done

0 comments on commit c79d0b7

Please sign in to comment.