aboutsummaryrefslogtreecommitdiff
path: root/bdep/project-email.cxx
blob: b85a738a8a7a4783c1c178e5db4bb731e8312a4d (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
// file      : bdep/project-email.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <bdep/project-email.hxx>

#include <libbutl/filesystem.mxx>

#include <bdep/diagnostics.hxx>

using namespace butl;

namespace bdep
{
  optional<string>
  project_email (const dir_path& prj)
  {
    optional<string> r;

    // The search order is as follows:
    //
    // BDEP_EMAIL
    // <VCS>
    // EMAIL
    //
    if ((r = getenv ("BDEP_EMAIL")))
      return r;

    // See if this is a VCS repository we recognize.
    //

    // .git can be either a directory or a file in case of a submodule.
    //
    if (entry_exists (prj / ".git",
                      true /* follow_symlinks */,
                      true /* ignore_errors */))
    {
      // In git the author email can be specified with the GIT_AUTHOR_EMAIL
      // environment variable after which things fall back to the committer
      // (GIT_COMMITTER_EMAIL and then the user.email git-config value). The
      // resolved value can be queried with the GIT_AUTHOR_IDENT logical
      // variable.
      //
      process pr;
      bool io (false);
      try
      {
        fdpipe pipe (fdopen_pipe ());

        // If git cannot determine the author name/email, it fails verbosely
        // so we suppress all diagnostics.
        //
        pr = start (0         /* stdin  */,
                    pipe      /* stdout */,
                    fdnull () /* stderr */,
                    "git",
                    "-C", prj,
                    "var",
                    "GIT_AUTHOR_IDENT");

        pipe.out.close ();
        ifdstream is (move (pipe.in), ifdstream::badbit);

        // The output should be a single line in this form:
        //
        // NAME <EMAIL> TIME ZONE
        //
        // For example:
        //
        // John Doe <john@example.org> 1530517726 +0200
        //
        // The <> delimiters are there even if the email is empty so we use
        // them as anchors.
        //
        string l;
        if (!eof (getline (is, l)))
        {
          size_t p1, p2;

          if ((p2 = l.rfind ('>'    )) == string::npos ||
              (p1 = l.rfind ('<', p2)) == string::npos)
            fail << "no email in git-var output" << endf;

          if (++p1 != p2)
            r = string (l, p1, p2 - p1);
        }

        is.close (); // Detect errors.
      }
      catch (const io_error&)
      {
        io = true; // Presumably git failed so check that first.
      }

      if (!pr.wait ())
      {
        const process_exit& e (*pr.exit);

        if (!e.normal ())
          fail << "process git " << e;

        r = nullopt;
      }
      else if (io)
        fail << "unable to read git-var output";

      if (r)
        return r;
    }

    if ((r = getenv ("EMAIL")))
      return r;

    return r;
  }
}