bundle-uri: allow relative URLs in bundle lists

Bundle providers may want to distribute that data across multiple CDNs.
This might require a change in the base URI, all the way to the domain
name. If all bundles require an absolute URI in their 'uri' value, then
every push to a CDN would require altering the table of contents to
match the expected domain and exact location within it.

Allow a bundle list to specify a relative URI for the bundles. This URI
is based on where the client received the bundle list. For a list
provided in the 'bundle-uri' protocol v2 command, the Git remote URI is
the base URI. Otherwise, the bundle list was provided from an HTTP URI
not using the Git protocol, and that URI is the base URI. This allows
easier distribution of bundle data.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Derrick Stolee 2022-12-22 15:14:15 +00:00 committed by Junio C Hamano
parent 9ea5796495
commit ebc3947955
5 changed files with 116 additions and 1 deletions

View File

@ -7,6 +7,7 @@
#include "hashmap.h"
#include "pkt-line.h"
#include "config.h"
#include "remote.h"
static int compare_bundles(const void *hashmap_cmp_fn_data,
const struct hashmap_entry *he1,
@ -49,6 +50,7 @@ void clear_bundle_list(struct bundle_list *list)
for_all_bundles_in_list(list, clear_remote_bundle_info, NULL);
hashmap_clear_and_free(&list->bundles, struct remote_bundle_info, ent);
free(list->baseURI);
}
int for_all_bundles_in_list(struct bundle_list *list,
@ -163,7 +165,7 @@ static int bundle_list_update(const char *key, const char *value,
if (!strcmp(subkey, "uri")) {
if (bundle->uri)
return -1;
bundle->uri = xstrdup(value);
bundle->uri = relative_url(list->baseURI, value, NULL);
return 0;
}
@ -190,6 +192,18 @@ int bundle_uri_parse_config_format(const char *uri,
.error_action = CONFIG_ERROR_ERROR,
};
if (!list->baseURI) {
struct strbuf baseURI = STRBUF_INIT;
strbuf_addstr(&baseURI, uri);
/*
* If the URI does not end with a trailing slash, then
* remove the filename portion of the path. This is
* important for relative URIs.
*/
strbuf_strip_file_from_path(&baseURI);
list->baseURI = strbuf_detach(&baseURI, NULL);
}
result = git_config_from_file_with_options(config_to_bundle_list,
filename, list,
&opts);

View File

@ -61,6 +61,20 @@ struct bundle_list {
int version;
enum bundle_list_mode mode;
struct hashmap bundles;
/**
* The baseURI of a bundle_list is the URI that provided the list.
*
* In the case of the 'bundle-uri' protocol v2 command, the base
* URI is the URI of the Git remote.
*
* Otherwise, the bundle list was downloaded over HTTP from some
* known URI. 'baseURI' is set to that value.
*
* The baseURI is used as the base for any relative URIs
* advertised by the bundle list at that location.
*/
char *baseURI;
};
void init_bundle_list(struct bundle_list *list);

View File

@ -40,6 +40,8 @@ static int cmd__bundle_uri_parse(int argc, const char **argv, enum input_mode mo
init_bundle_list(&list);
list.baseURI = xstrdup("<uri>");
switch (mode) {
case KEY_VALUE_PAIRS:
if (argc != 1)

View File

@ -30,6 +30,58 @@ test_expect_success 'bundle_uri_parse_line() just URIs' '
test_cmp_config_output expect actual
'
test_expect_success 'bundle_uri_parse_line(): relative URIs' '
cat >in <<-\EOF &&
bundle.one.uri=bundle.bdl
bundle.two.uri=../bundle.bdl
bundle.three.uri=sub/dir/bundle.bdl
EOF
cat >expect <<-\EOF &&
[bundle]
version = 1
mode = all
[bundle "one"]
uri = <uri>/bundle.bdl
[bundle "two"]
uri = bundle.bdl
[bundle "three"]
uri = <uri>/sub/dir/bundle.bdl
EOF
test-tool bundle-uri parse-key-values in >actual 2>err &&
test_must_be_empty err &&
test_cmp_config_output expect actual
'
test_expect_success 'bundle_uri_parse_line(): relative URIs and parent paths' '
cat >in <<-\EOF &&
bundle.one.uri=bundle.bdl
bundle.two.uri=../bundle.bdl
bundle.three.uri=../../bundle.bdl
EOF
cat >expect <<-\EOF &&
[bundle]
version = 1
mode = all
[bundle "one"]
uri = <uri>/bundle.bdl
[bundle "two"]
uri = bundle.bdl
[bundle "three"]
uri = <uri>/../bundle.bdl
EOF
# TODO: We would prefer if parsing a bundle list would not cause
# a die() and instead would give a warning and allow the rest of
# a Git command to continue. This test_must_fail is necessary for
# now until the interface for relative_url() allows for reporting
# an error instead of die()ing.
test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
grep "fatal: cannot strip one component off url" err
'
test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty key or value' '
cat >in <<-\EOF &&
=bogus-value
@ -136,6 +188,36 @@ test_expect_success 'parse config format: just URIs' '
test_cmp_config_output expect actual
'
test_expect_success 'parse config format: relative URIs' '
cat >in <<-\EOF &&
[bundle]
version = 1
mode = all
[bundle "one"]
uri = bundle.bdl
[bundle "two"]
uri = ../bundle.bdl
[bundle "three"]
uri = sub/dir/bundle.bdl
EOF
cat >expect <<-\EOF &&
[bundle]
version = 1
mode = all
[bundle "one"]
uri = <uri>/bundle.bdl
[bundle "two"]
uri = bundle.bdl
[bundle "three"]
uri = <uri>/sub/dir/bundle.bdl
EOF
test-tool bundle-uri parse-config in >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

View File

@ -1538,6 +1538,9 @@ int transport_get_remote_bundle_uri(struct transport *transport)
if (git_config_get_bool("transfer.bundleuri", &value) || !value)
return 0;
if (!transport->bundles->baseURI)
transport->bundles->baseURI = xstrdup(transport->url);
if (!vtable->get_bundle_uri)
return error(_("bundle-uri operation not supported by protocol"));