diff mbox series

[v2,1/1] config: learn the "hostname:" includeIf condition

Message ID 20240309181828.45496-2-ignacio@iencinas.com (mailing list archive)
State Superseded
Headers show
Series Add hostname condition to includeIf | expand

Commit Message

Ignacio Encinas Rubio March 9, 2024, 6:18 p.m. UTC
Currently, customizing the configuration depending on the machine running
git has to be done manually.

Add support for a new includeIf keyword "hostname:" to conditionally
include configuration files depending on the hostname.

Signed-off-by: Ignacio Encinas <ignacio@iencinas.com>
---
 Documentation/config.txt  |  9 +++++++++
 config.c                  | 17 ++++++++++++++++
 t/t1305-config-include.sh | 42 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)

Comments

Junio C Hamano March 10, 2024, 4:59 p.m. UTC | #1
Ignacio Encinas <ignacio@iencinas.com> writes:

> +test_expect_success 'conditional include, hostname' '
> +	cat >>.git/config <<-EOF &&
> +	[includeIf "hostname:$(hostname)a"]

This unconditionally runs the $(hostname) command assuming it exists
everywhere, but

    $ git grep '$(hostname' t/
    t/t6500-gc.sh:	hostname=$(hostname || echo unknown) &&

tells us that we should be prepared to meet a platform where such a
command does not exist.

I have a feeling that it is better done with a test prerequisite
than hardcoded "unknown", as xgethostname() at C level may come up
with some random string but it is not guaranteed to be "unknown".

Perhaps have one instance of this before these added tests

	test_lazy_prereq WORKING_HOSTNAME '
		hostname >/dev/null 2>&1
	'

and then start them with

	test_expect_success WORKING_HOSTNAME 'hostname: includeIf' '
		...
	'

or something?  Others may think of a better way to make sure this
test does not cause false failures on platforms only because they
lack working hostname(1) but have a working gethostname(2) and their
xgethostname() may be working fine.

Thanks.
Ignacio Encinas Rubio March 10, 2024, 6:46 p.m. UTC | #2
On 10/3/24 17:59, Junio C Hamano wrote:
> Ignacio Encinas <ignacio@iencinas.com> writes:
> 
>> +test_expect_success 'conditional include, hostname' '
>> +	cat >>.git/config <<-EOF &&
>> +	[includeIf "hostname:$(hostname)a"]
> 
> This unconditionally runs the $(hostname) command assuming it exists
> everywhere, but
> 
>     $ git grep '$(hostname' t/
>     t/t6500-gc.sh:	hostname=$(hostname || echo unknown) &&
> 
> tells us that we should be prepared to meet a platform where such a
> command does not exist.

Oops, it didn't even cross my mind. Thanks for the catch!

> I have a feeling that it is better done with a test prerequisite
> than hardcoded "unknown", as xgethostname() at C level may come up
> with some random string but it is not guaranteed to be "unknown".

I agree. Not being able to query the current hostname defeats the
purpose of the tests.

> Perhaps have one instance of this before these added tests
> 
> 	test_lazy_prereq WORKING_HOSTNAME '
> 		hostname >/dev/null 2>&1
> 	'
> 
> and then start them with
> 
> 	test_expect_success WORKING_HOSTNAME 'hostname: includeIf' '
> 		...
> 	'
 
Thanks for providing an example. 

> or something?  Others may think of a better way to make sure this
> test does not cause false failures on platforms only because they
> lack working hostname(1) but have a working gethostname(2) and their
> xgethostname() may be working fine.

I can't think of any room for improvement other than integrating
hostname (or a custom hostname) into git and using it in the tests, but
I doubt it is worth it.

> Thanks.

Thank you. I will wait a couple of days to post the v3 to see if anyone 
has a suggestion.
Junio C Hamano March 11, 2024, 5:39 p.m. UTC | #3
Ignacio Encinas Rubio <ignacio@iencinas.com> writes:

> I can't think of any room for improvement other than integrating
> hostname (or a custom hostname) into git and using it in the tests, but
> I doubt it is worth it.

Ah, that is a thought.  We have t/helper that builds "test-tool"
just for that, and exposing the output of xhostname() does sounds
like a reasonable way to go.  It would roughly involve

 * Add t/helper/test-xhostname.c that defines cmd__xhostname() and
   writes the result of calling xhostname() to its standard output.

 * Plumb it through by adding it to a few places:

   - t/helper/test-tool.h wants the extern definition.
   - t/helper/test-tool.c wants it in its cmds[] array.
   - Makefile wants to list it in TEST_BUILTIN_OBJS

 * Then use "test-tool xhostname" in your tests, instead of
   "hostname".

You can run

    $ git grep chmtime ':!t/*.sh"

to find places that needed to be touched when a similar internal
tool "chmtime" was added.
Ignacio Encinas Rubio March 13, 2024, 9:53 p.m. UTC | #4
On 11/3/24 18:39, Junio C Hamano wrote:
> Ignacio Encinas Rubio <ignacio@iencinas.com> writes:
> 
>> I can't think of any room for improvement other than integrating
>> hostname (or a custom hostname) into git and using it in the tests, but
>> I doubt it is worth it.
> 
> Ah, that is a thought.  We have t/helper that builds "test-tool"
> just for that, and exposing the output of xhostname() does sounds
> like a reasonable way to go.  It would roughly involve

Great! I hadn't noticed "test-tool". Just to double-check, what name do
we want to use for this? xhostname, hostname, xgethostname, gethostname?

If I didn't miss something, the only place the test use hostname is in 

    $ git grep '$(hostname' t/
    t/t6500-gc.sh:	hostname=$(hostname || echo unknown) &&

as you previously pointed out. So my plan is:

1. Extend test-tool, migrate t6500-gc.sh to test-tool xhostname(*)
2. Update my v2 to use "test-tool xhostname(*)"

(*) or however we want to name it

>  * Add t/helper/test-xhostname.c that defines cmd__xhostname() and
>    writes the result of calling xhostname() to its standard output.
> 
>  * Plumb it through by adding it to a few places:
> 
>    - t/helper/test-tool.h wants the extern definition.
>    - t/helper/test-tool.c wants it in its cmds[] array.
>    - Makefile wants to list it in TEST_BUILTIN_OBJS
> 
>  * Then use "test-tool xhostname" in your tests, instead of
>    "hostname".
> 
> You can run
> 
>     $ git grep chmtime ':!t/*.sh"
> 
> to find places that needed to be touched when a similar internal
> tool "chmtime" was added.

Thank you very much for the pointers, they were very helpful!
Jeff King March 16, 2024, 6:57 a.m. UTC | #5
On Sat, Mar 09, 2024 at 07:18:28PM +0100, Ignacio Encinas wrote:

> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index e3a74dd1c19d..9a22fd260935 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -186,6 +186,11 @@ As for the naming of this keyword, it is for forwards compatibility with
>  a naming scheme that supports more variable-based include conditions,
>  but currently Git only supports the exact keyword described above.
>  
> +`hostname`::
> +	The data that follows the keyword `hostname:` is taken to be a
> +	pattern with standard globbing wildcards. If the current
> +	hostname matches the pattern, the include condition is met.

Do we need to define "hostname" in more detail here? Specifically, I'm
wondering whether the result will be a FQDN or not (i.e., the output of
"hostname" vs "hostname -f"). Looking at the code I think it will just
be the short name returned. That's probably OK, but it may be worth
documenting.

-Peff
Ignacio Encinas Rubio March 16, 2024, 11:19 a.m. UTC | #6
On 16/3/24 7:57, Jeff King wrote:
> On Sat, Mar 09, 2024 at 07:18:28PM +0100, Ignacio Encinas wrote:
> 
>> diff --git a/Documentation/config.txt b/Documentation/config.txt
>> index e3a74dd1c19d..9a22fd260935 100644
>> --- a/Documentation/config.txt
>> +++ b/Documentation/config.txt
>> @@ -186,6 +186,11 @@ As for the naming of this keyword, it is for forwards compatibility with
>>  a naming scheme that supports more variable-based include conditions,
>>  but currently Git only supports the exact keyword described above.
>>  
>> +`hostname`::
>> +	The data that follows the keyword `hostname:` is taken to be a
>> +	pattern with standard globbing wildcards. If the current
>> +	hostname matches the pattern, the include condition is met.
> 
> Do we need to define "hostname" in more detail here? Specifically, I'm
> wondering whether the result will be a FQDN or not (i.e., the output of
> "hostname" vs "hostname -f"). Looking at the code I think it will just
> be the short name returned. That's probably OK, but it may be worth
> documenting.

Thanks for pointing it out. I agree that it should be further clarified. 

Indeed, I was referring to the short name reported by gethostname(2), 
which should agree with "hostname". What do you think about

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 9a22fd260935..268a9fab7be0 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -189,7 +189,8 @@ but currently Git only supports the exact keyword described above.
 `hostname`::
        The data that follows the keyword `hostname:` is taken to be a
        pattern with standard globbing wildcards. If the current
-       hostname matches the pattern, the include condition is met.
+       hostname (output of gethostname(2)) matches the
+       pattern, the include condition is met.
Taylor Blau March 16, 2024, 4 p.m. UTC | #7
On Sat, Mar 16, 2024 at 12:19:44PM +0100, Ignacio Encinas Rubio wrote:
>
>
> On 16/3/24 7:57, Jeff King wrote:
> > On Sat, Mar 09, 2024 at 07:18:28PM +0100, Ignacio Encinas wrote:
> >
> >> diff --git a/Documentation/config.txt b/Documentation/config.txt
> >> index e3a74dd1c19d..9a22fd260935 100644
> >> --- a/Documentation/config.txt
> >> +++ b/Documentation/config.txt
> >> @@ -186,6 +186,11 @@ As for the naming of this keyword, it is for forwards compatibility with
> >>  a naming scheme that supports more variable-based include conditions,
> >>  but currently Git only supports the exact keyword described above.
> >>
> >> +`hostname`::
> >> +	The data that follows the keyword `hostname:` is taken to be a
> >> +	pattern with standard globbing wildcards. If the current
> >> +	hostname matches the pattern, the include condition is met.
> >
> > Do we need to define "hostname" in more detail here? Specifically, I'm
> > wondering whether the result will be a FQDN or not (i.e., the output of
> > "hostname" vs "hostname -f"). Looking at the code I think it will just
> > be the short name returned. That's probably OK, but it may be worth
> > documenting.
>
> Thanks for pointing it out. I agree that it should be further clarified.
>
> Indeed, I was referring to the short name reported by gethostname(2),
> which should agree with "hostname". What do you think about
>
> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index 9a22fd260935..268a9fab7be0 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -189,7 +189,8 @@ but currently Git only supports the exact keyword described above.
>  `hostname`::
>         The data that follows the keyword `hostname:` is taken to be a
>         pattern with standard globbing wildcards. If the current
> -       hostname matches the pattern, the include condition is met.
> +       hostname (output of gethostname(2)) matches the

Hmm. gethostname(2)'s manual page isn't overly specific on the details
here, either.

I admittedly don't love the idea of documenting this implementation
detail (that is, that we are calling gethostname() and using its output
to compare against). I think it's fine to say instead, "the short
hostname", and leave it at that.

Alternatively, you could say "If the machine's short hostname (as
opposed to a fully-qualified hostname, as returned by `hostname -f`)
matches the pattern [...]".

I think I have a vague preference towards the latter.

Thanks,
Taylor
Taylor Blau March 16, 2024, 4:01 p.m. UTC | #8
Hi Ignacio,

On Sat, Mar 09, 2024 at 07:18:28PM +0100, Ignacio Encinas wrote:
> ---
>  Documentation/config.txt  |  9 +++++++++
>  config.c                  | 17 ++++++++++++++++
>  t/t1305-config-include.sh | 42 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 68 insertions(+)

I took a look at this patch and felt that it was all very sensible. I
left one comment in reply to the sub-thread with you and Peff with some
minor suggestions on the documentation.

Otherwise, the code changes here all look reasonable to me.

Thanks,
Taylor
Ignacio Encinas Rubio March 16, 2024, 4:46 p.m. UTC | #9
On 16/3/24 17:00, Taylor Blau wrote:
> On Sat, Mar 16, 2024 at 12:19:44PM +0100, Ignacio Encinas Rubio wrote:
>>
>>
>> On 16/3/24 7:57, Jeff King wrote:
>>> On Sat, Mar 09, 2024 at 07:18:28PM +0100, Ignacio Encinas wrote:
>>>
>>>> diff --git a/Documentation/config.txt b/Documentation/config.txt
>>>> index e3a74dd1c19d..9a22fd260935 100644
>>>> --- a/Documentation/config.txt
>>>> +++ b/Documentation/config.txt
>>>> @@ -186,6 +186,11 @@ As for the naming of this keyword, it is for forwards compatibility with
>>>>  a naming scheme that supports more variable-based include conditions,
>>>>  but currently Git only supports the exact keyword described above.
>>>>
>>>> +`hostname`::
>>>> +	The data that follows the keyword `hostname:` is taken to be a
>>>> +	pattern with standard globbing wildcards. If the current
>>>> +	hostname matches the pattern, the include condition is met.
>>>
>>> Do we need to define "hostname" in more detail here? Specifically, I'm
>>> wondering whether the result will be a FQDN or not (i.e., the output of
>>> "hostname" vs "hostname -f"). Looking at the code I think it will just
>>> be the short name returned. That's probably OK, but it may be worth
>>> documenting.
>>
>> Thanks for pointing it out. I agree that it should be further clarified.
>>
>> Indeed, I was referring to the short name reported by gethostname(2),
>> which should agree with "hostname". What do you think about
>>
>> diff --git a/Documentation/config.txt b/Documentation/config.txt
>> index 9a22fd260935..268a9fab7be0 100644
>> --- a/Documentation/config.txt
>> +++ b/Documentation/config.txt
>> @@ -189,7 +189,8 @@ but currently Git only supports the exact keyword described above.
>>  `hostname`::
>>         The data that follows the keyword `hostname:` is taken to be a
>>         pattern with standard globbing wildcards. If the current
>> -       hostname matches the pattern, the include condition is met.
>> +       hostname (output of gethostname(2)) matches the
> 
> Hmm. gethostname(2)'s manual page isn't overly specific on the details
> here, either.
> 
> I admittedly don't love the idea of documenting this implementation
> detail (that is, that we are calling gethostname() and using its output
> to compare against). I think it's fine to say instead, "the short
> hostname", and leave it at that.

I agree it isn't too descriptive, but the reason I chose to do it was
because this doesn't seem thoroughly documented anywhere:

hostname(1):

  hostname will print the name of the system as returned by the gethostname(2) function.

       -s, --short
              Display the short host name. This is the host name cut at the first dot.

I have only superficial knowledge about the terminology, but from what I
have read, it seems like we're actually reading the "nodename" (see
uname(2)), which shouldn't but can contain dots ".", which "hostname -s"
will trim, but "hostname" won't.

After seeing all this and the huge potential for confusing everybody, I
chose the easy way out.

I'm ok with saying "short hostname" but I'm not terribly happy with it
as it won't match "hostname -s" if "nodename" has dots (it will always
match "hostname" from what I have seen in the hostname source code from
the debian package which I assume everyone uses).

Do you think this is worth worrying about? Or people with "nodename"s
making "hostname" and "hostname --short" disagree should know that by
short hostname we mean "hostname" and not "hostname --short".

I might be missing something, but I somehow find all of this pretty
confusing.

> Alternatively, you could say "If the machine's short hostname (as
> opposed to a fully-qualified hostname, as returned by `hostname -f`)
> matches the pattern [...]".
> 
> I think I have a vague preference towards the latter.
> Thanks,
> Taylor

Thank you for the review!
Ignacio Encinas Rubio March 16, 2024, 4:50 p.m. UTC | #10
On 16/3/24 17:01, Taylor Blau wrote:
> Hi Ignacio,
> 
> On Sat, Mar 09, 2024 at 07:18:28PM +0100, Ignacio Encinas wrote:
>> ---
>>  Documentation/config.txt  |  9 +++++++++
>>  config.c                  | 17 ++++++++++++++++
>>  t/t1305-config-include.sh | 42 +++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 68 insertions(+)
> 
> I took a look at this patch and felt that it was all very sensible. I
> left one comment in reply to the sub-thread with you and Peff with some
> minor suggestions on the documentation.
>
> Otherwise, the code changes here all look reasonable to me.

Thanks for the review!
 
> Thanks,
> Taylor
Junio C Hamano March 16, 2024, 5:02 p.m. UTC | #11
Jeff King <peff@peff.net> writes:

> Do we need to define "hostname" in more detail here? Specifically, I'm
> wondering whether the result will be a FQDN or not (i.e., the output of
> "hostname" vs "hostname -f"). Looking at the code I think it will just
> be the short name returned. That's probably OK, but it may be worth
> documenting.

That was my first reaction but there are places where "hostname"
already gives a name that is not "short" at all, without being
invoked with "-f".

For example, the (virtual) workstation I am typing this message on
sits in a $WORK datacenter, where "hostname" gives the same string
as "hostname -f", which looks like "git.c.xxxxxx.tld" ("git" is the
only part I picked myself for it, "c" is shared by those employee
workstations hosted at datacenters, "xxxxxx.tld" is redacted to
conceal the real domain name to protect the culprits ;-).

I think the most honest answer we can give in the documentation is
that we use what gethostname() [*] gives.


[References]

* https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html
Randall S. Becker March 16, 2024, 5:41 p.m. UTC | #12
On Saturday, March 16, 2024 1:03 PM, Junio C Hamano wrote:
>Jeff King <peff@peff.net> writes:
>
>> Do we need to define "hostname" in more detail here? Specifically, I'm
>> wondering whether the result will be a FQDN or not (i.e., the output
>> of "hostname" vs "hostname -f"). Looking at the code I think it will
>> just be the short name returned. That's probably OK, but it may be
>> worth documenting.
>
>That was my first reaction but there are places where "hostname"
>already gives a name that is not "short" at all, without being invoked with
"-f".
>
>For example, the (virtual) workstation I am typing this message on sits in
a $WORK datacenter, where "hostname" gives the same
>string as "hostname -f", which looks like "git.c.xxxxxx.tld" ("git" is the
only part I picked myself for it, "c" is shared by those employee
>workstations hosted at datacenters, "xxxxxx.tld" is redacted to conceal the
real domain name to protect the culprits ;-).
>
>I think the most honest answer we can give in the documentation is that we
use what gethostname() [*] gives.

I think this is probably a good idea and but value should not be cached. My
dev box has a multi-home, multi-cpu IP stack. It makes things really weird
sometimes. For example, hostname replies with:

ztc0.xxxxxxxx.local

and includes the current default IP stack, which is known to DNS, while
uname -n, which I prefer to use when deciding what system I am on during
tests, reports:

xxxxxxxx

I am not sure how meaningful hostname is; however, "hostname -f" is not
portable. However, includeif depending on whatever gethostname() returns is
reasonable, in my opinion, also. I think the series should include a $(uname
-n) option in some form for completeness.

>
>
>[References]
>
>*
https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html

--Randall
Ignacio Encinas Rubio March 16, 2024, 6:05 p.m. UTC | #13
On 16/3/24 18:41, rsbecker@nexbridge.com wrote:
> On Saturday, March 16, 2024 1:03 PM, Junio C Hamano wrote:
>> Jeff King <peff@peff.net> writes:
>>
>>> Do we need to define "hostname" in more detail here? Specifically, I'm
>>> wondering whether the result will be a FQDN or not (i.e., the output
>>> of "hostname" vs "hostname -f"). Looking at the code I think it will
>>> just be the short name returned. That's probably OK, but it may be
>>> worth documenting.
>>
>> That was my first reaction but there are places where "hostname"
>> already gives a name that is not "short" at all, without being invoked with
> "-f".
>>
>> For example, the (virtual) workstation I am typing this message on sits in
> a $WORK datacenter, where "hostname" gives the same
>> string as "hostname -f", which looks like "git.c.xxxxxx.tld" ("git" is the
> only part I picked myself for it, "c" is shared by those employee
>> workstations hosted at datacenters, "xxxxxx.tld" is redacted to conceal the
> real domain name to protect the culprits ;-).
>>
>> I think the most honest answer we can give in the documentation is that we
> use what gethostname() [*] gives.
> 
> I think this is probably a good idea and but value should not be cached. My
> dev box has a multi-home, multi-cpu IP stack. It makes things really weird
> sometimes. For example, hostname replies with:
> 
> ztc0.xxxxxxxx.local
> 
> and includes the current default IP stack, which is known to DNS, while
> uname -n, which I prefer to use when deciding what system I am on during
> tests, reports:
> 
> xxxxxxxx
> 
> I am not sure how meaningful hostname is; however, "hostname -f" is not
> portable. However, includeif depending on whatever gethostname() returns is
> reasonable, in my opinion, also. I think the series should include a $(uname
> -n) option in some form for completeness.

Correct me if I'm wrong, but gethostname() seems to be equivalent to
$(uname -n)

[1] https://git.musl-libc.org/cgit/musl/tree/src/unistd/gethostname.c 
[2] https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/posix/gethostname.c;h=3c50706b5823368a0b3e876491e554461a4d515e;hb=HEAD

>>
>>
>> [References]
>>
>> *
> https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html
> 
> --Randall
>
Randall S. Becker March 16, 2024, 6:49 p.m. UTC | #14
On Saturday, March 16, 2024 2:06 PM, Ignacio Encinas Rubio wrote:
>On 16/3/24 18:41, rsbecker@nexbridge.com wrote:
>> On Saturday, March 16, 2024 1:03 PM, Junio C Hamano wrote:
>>> Jeff King <peff@peff.net> writes:
>>>
>>>> Do we need to define "hostname" in more detail here? Specifically,
>>>> I'm wondering whether the result will be a FQDN or not (i.e., the
>>>> output of "hostname" vs "hostname -f"). Looking at the code I think
>>>> it will just be the short name returned. That's probably OK, but it
>>>> may be worth documenting.
>>>
>>> That was my first reaction but there are places where "hostname"
>>> already gives a name that is not "short" at all, without being
>>> invoked with
>> "-f".
>>>
>>> For example, the (virtual) workstation I am typing this message on
>>> sits in
>> a $WORK datacenter, where "hostname" gives the same
>>> string as "hostname -f", which looks like "git.c.xxxxxx.tld" ("git"
>>> is the
>> only part I picked myself for it, "c" is shared by those employee
>>> workstations hosted at datacenters, "xxxxxx.tld" is redacted to
>>> conceal the
>> real domain name to protect the culprits ;-).
>>>
>>> I think the most honest answer we can give in the documentation is
>>> that we
>> use what gethostname() [*] gives.
>>
>> I think this is probably a good idea and but value should not be
>> cached. My dev box has a multi-home, multi-cpu IP stack. It makes
>> things really weird sometimes. For example, hostname replies with:
>>
>> ztc0.xxxxxxxx.local
>>
>> and includes the current default IP stack, which is known to DNS,
>> while uname -n, which I prefer to use when deciding what system I am
>> on during tests, reports:
>>
>> xxxxxxxx
>>
>> I am not sure how meaningful hostname is; however, "hostname -f" is
>> not portable. However, includeif depending on whatever gethostname()
>> returns is reasonable, in my opinion, also. I think the series should
>> include a $(uname
>> -n) option in some form for completeness.
>
>Correct me if I'm wrong, but gethostname() seems to be equivalent to $(uname -n)

Glibc definitely uses uname, according to its man page, but that is the exception, not the rule. Evidence from my experimentation on various platforms says that the two values may sometimes be the same but the host configuration may be different, particularly if two stacks are on the same machine with different IP addresses. uname does not go to DNS. gethostname() generally (Windows, S/390, NonStop, Linux where glibc is not used), uses DNS as its first attempt to resolve the name.
Jeff King March 18, 2024, 8:17 a.m. UTC | #15
On Sat, Mar 16, 2024 at 10:02:31AM -0700, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > Do we need to define "hostname" in more detail here? Specifically, I'm
> > wondering whether the result will be a FQDN or not (i.e., the output of
> > "hostname" vs "hostname -f"). Looking at the code I think it will just
> > be the short name returned. That's probably OK, but it may be worth
> > documenting.
> 
> That was my first reaction but there are places where "hostname"
> already gives a name that is not "short" at all, without being
> invoked with "-f".

Thanks, that was the vague buzzing in the back of my head that led to
my first comment. It has been a while since I've dealt with this, but I
think in some circles it is a holy war akin to tabs vs spaces. A quick
search shows I am not alone:

  https://serverfault.com/questions/331936/setting-the-hostname-fqdn-or-short-name

So I think we probably need to say something like:

  Depending on how your system is configured, the hostname used for
  matching may be short (e.g., "myhost") or a fully qualified domain
  name ("myhost.example.com").

> I think the most honest answer we can give in the documentation is
> that we use what gethostname() [*] gives.

That is honest, but I wonder if it is very useful to most users, as they
cannot easily see what it returns. It's tempting to give an extra note
like this tacked on to what I said above:

  You can run the hostname(1) tool to see which hostname your system
  uses.

But I'm not sure that it is available everywhere (especially Windows).
I guess we could provide "git config --show-hostname-for-includes" or
something, but that feels like overkill.

Maybe just the "Depending..." note is enough, and people who are
interested in hostname conditionals hopefully know enough to dig further
on their system. What I think we want to avoid is saying nothing, and
then somebody tries "foo.example.com", finds that it doesn't work, and
gets confused with no hints about why.

I guess yet another alternative is to try to qualify the name ourselves
using getaddrinfo(), either unconditionally or if the hostname doesn't
contain a ".". That may involve a DNS lookup, though (if your hostname
isn't in /etc/hosts).

-Peff
diff mbox series

Patch

diff --git a/Documentation/config.txt b/Documentation/config.txt
index e3a74dd1c19d..9a22fd260935 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -186,6 +186,11 @@  As for the naming of this keyword, it is for forwards compatibility with
 a naming scheme that supports more variable-based include conditions,
 but currently Git only supports the exact keyword described above.
 
+`hostname`::
+	The data that follows the keyword `hostname:` is taken to be a
+	pattern with standard globbing wildcards. If the current
+	hostname matches the pattern, the include condition is met.
+
 A few more notes on matching via `gitdir` and `gitdir/i`:
 
  * Symlinks in `$GIT_DIR` are not resolved before matching.
@@ -261,6 +266,10 @@  Example
 	path = foo.inc
 [remote "origin"]
 	url = https://example.com/git
+
+; include only if the hostname of the machine matches some-hostname
+[includeIf "hostname:some-hostname"]
+	path = foo.inc
 ----
 
 Values
diff --git a/config.c b/config.c
index 3cfeb3d8bd99..50b3f6d24c50 100644
--- a/config.c
+++ b/config.c
@@ -317,6 +317,21 @@  static int include_by_branch(const char *cond, size_t cond_len)
 	return ret;
 }
 
+static int include_by_hostname(const char *cond, size_t cond_len)
+{
+	int ret;
+	char my_host[HOST_NAME_MAX + 1];
+	struct strbuf pattern = STRBUF_INIT;
+
+	if (xgethostname(my_host, sizeof(my_host)))
+		return 0;
+
+	strbuf_add(&pattern, cond, cond_len);
+	ret = !wildmatch(pattern.buf, my_host, 0);
+	strbuf_release(&pattern);
+	return ret;
+}
+
 static int add_remote_url(const char *var, const char *value,
 			  const struct config_context *ctx UNUSED, void *data)
 {
@@ -406,6 +421,8 @@  static int include_condition_is_true(const struct key_value_info *kvi,
 	else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
 				   &cond_len))
 		return include_by_remote_url(inc, cond, cond_len);
+	else if (skip_prefix_mem(cond, cond_len, "hostname:", &cond, &cond_len))
+		return include_by_hostname(cond, cond_len);
 
 	/* unknown conditionals are always false */
 	return 0;
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 5cde79ef8c4f..e0a1d51d50ad 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -357,4 +357,46 @@  test_expect_success 'include cycles are detected' '
 	grep "exceeded maximum include depth" stderr
 '
 
+test_expect_success 'conditional include, hostname' '
+	cat >>.git/config <<-EOF &&
+	[includeIf "hostname:$(hostname)a"]
+		path = bar12
+	EOF
+	cat >>.git/bar12 <<-EOF &&
+	[test]
+		twelve=12
+	EOF
+
+	test_must_fail git config test.twelve &&
+
+	cat >>.git/config <<-EOF &&
+	[includeIf "hostname:$(hostname)"]
+		path = bar12
+	EOF
+	echo 12 >expect &&
+	git config test.twelve >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'conditional include, hostname, wildcard' '
+	cat >>.git/config <<-EOF &&
+	[includeIf "hostname:$(hostname)a*"]
+		path = bar13
+	EOF
+	cat >>.git/bar13 <<-EOF &&
+	[test]
+		thirteen = 13
+	EOF
+
+	test_must_fail git config test.thirteen &&
+
+	cat >>.git/config <<-EOF &&
+	[includeIf "hostname:$(hostname)*"]
+		path = bar13
+	EOF
+	echo 13 >expect &&
+	git config test.thirteen >actual &&
+	test_cmp expect actual
+'
+
 test_done