mirror of
https://github.com/libgit2/libgit2.git
synced 2026-01-25 11:06:32 +00:00
Merge pull request #6706 from u-quark/signature-use-env-vars
Use environment variables when creating signatures
This commit is contained in:
@@ -39,7 +39,7 @@ int lg2_commit(git_repository *repo, int argc, char **argv)
|
||||
git_index *index;
|
||||
git_object *parent = NULL;
|
||||
git_reference *ref = NULL;
|
||||
git_signature *signature;
|
||||
git_signature *author_signature, *committer_signature;
|
||||
|
||||
/* Validate args */
|
||||
if (argc < 3 || strcmp(opt, "-m") != 0) {
|
||||
@@ -63,21 +63,23 @@ int lg2_commit(git_repository *repo, int argc, char **argv)
|
||||
|
||||
check_lg2(git_tree_lookup(&tree, repo, &tree_oid), "Error looking up tree", NULL);
|
||||
|
||||
check_lg2(git_signature_default(&signature, repo), "Error creating signature", NULL);
|
||||
check_lg2(git_signature_default_from_env(&author_signature, &committer_signature, repo),
|
||||
"Error creating signature", NULL);
|
||||
|
||||
check_lg2(git_commit_create_v(
|
||||
&commit_oid,
|
||||
repo,
|
||||
"HEAD",
|
||||
signature,
|
||||
signature,
|
||||
author_signature,
|
||||
committer_signature,
|
||||
NULL,
|
||||
comment,
|
||||
tree,
|
||||
parent ? 1 : 0, parent), "Error creating commit", NULL);
|
||||
|
||||
git_index_free(index);
|
||||
git_signature_free(signature);
|
||||
git_signature_free(author_signature);
|
||||
git_signature_free(committer_signature);
|
||||
git_tree_free(tree);
|
||||
git_object_free(parent);
|
||||
git_reference_free(ref);
|
||||
|
||||
@@ -123,14 +123,14 @@ int lg2_init(git_repository *repo, int argc, char *argv[])
|
||||
*/
|
||||
static void create_initial_commit(git_repository *repo)
|
||||
{
|
||||
git_signature *sig;
|
||||
git_signature *author_sig = NULL, *committer_sig = NULL;
|
||||
git_index *index;
|
||||
git_oid tree_id, commit_id;
|
||||
git_tree *tree;
|
||||
|
||||
/** First use the config to initialize a commit signature for the user. */
|
||||
|
||||
if (git_signature_default(&sig, repo) < 0)
|
||||
if ((git_signature_default_from_env(&author_sig, &committer_sig, repo) < 0))
|
||||
fatal("Unable to create a commit signature.",
|
||||
"Perhaps 'user.name' and 'user.email' are not set");
|
||||
|
||||
@@ -162,14 +162,15 @@ static void create_initial_commit(git_repository *repo)
|
||||
*/
|
||||
|
||||
if (git_commit_create_v(
|
||||
&commit_id, repo, "HEAD", sig, sig,
|
||||
&commit_id, repo, "HEAD", author_sig, committer_sig,
|
||||
NULL, "Initial commit", tree, 0) < 0)
|
||||
fatal("Could not create the initial commit", NULL);
|
||||
|
||||
/** Clean up so we don't leak memory. */
|
||||
|
||||
git_tree_free(tree);
|
||||
git_signature_free(sig);
|
||||
git_signature_free(author_sig);
|
||||
git_signature_free(committer_sig);
|
||||
}
|
||||
|
||||
static void usage(const char *error, const char *arg)
|
||||
|
||||
@@ -108,7 +108,7 @@ static int cmd_push(git_repository *repo, struct opts *opts)
|
||||
if (opts->argc)
|
||||
usage("push does not accept any parameters");
|
||||
|
||||
check_lg2(git_signature_default(&signature, repo),
|
||||
check_lg2(git_signature_default_from_env(&signature, NULL, repo),
|
||||
"Unable to get signature", NULL);
|
||||
check_lg2(git_stash_save(&stashid, repo, signature, NULL, GIT_STASH_DEFAULT),
|
||||
"Unable to save stash", NULL);
|
||||
|
||||
@@ -226,7 +226,7 @@ static void action_create_tag(tag_state *state)
|
||||
check_lg2(git_revparse_single(&target, repo, opts->target),
|
||||
"Unable to resolve spec", opts->target);
|
||||
|
||||
check_lg2(git_signature_default(&tagger, repo),
|
||||
check_lg2(git_signature_default_from_env(&tagger, NULL, repo),
|
||||
"Unable to create signature", NULL);
|
||||
|
||||
check_lg2(git_tag_create(&oid, repo, opts->tag_name,
|
||||
|
||||
@@ -48,6 +48,42 @@ GIT_EXTERN(int) git_signature_new(git_signature **out, const char *name, const c
|
||||
*/
|
||||
GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email);
|
||||
|
||||
/**
|
||||
* Create a new author and/or committer signatures with default
|
||||
* information based on the configuration and environment variables.
|
||||
*
|
||||
* If `author_out` is set, it will be populated with the author
|
||||
* information. The `GIT_AUTHOR_NAME` and `GIT_AUTHOR_EMAIL`
|
||||
* environment variables will be honored, and `user.name` and
|
||||
* `user.email` configuration options will be honored if the
|
||||
* environment variables are unset. For timestamps, `GIT_AUTHOR_DATE`
|
||||
* will be used, otherwise the current time will be used.
|
||||
*
|
||||
* If `committer_out` is set, it will be populated with the
|
||||
* committer information. The `GIT_COMMITTER_NAME` and
|
||||
* `GIT_COMMITTER_EMAIL` environment variables will be honored,
|
||||
* and `user.name` and `user.email` configuration options will
|
||||
* be honored if the environment variables are unset. For timestamps,
|
||||
* `GIT_COMMITTER_DATE` will be used, otherwise the current time will
|
||||
* be used.
|
||||
*
|
||||
* If neither `GIT_AUTHOR_DATE` nor `GIT_COMMITTER_DATE` are set,
|
||||
* both timestamps will be set to the same time.
|
||||
*
|
||||
* It will return `GIT_ENOTFOUND` if either the `user.name` or
|
||||
* `user.email` are not set and there is no fallback from an environment
|
||||
* variable. One of `author_out` or `committer_out` must be set.
|
||||
*
|
||||
* @param author_out pointer to set the author signature, or NULL
|
||||
* @param committer_out pointer to set the committer signature, or NULL
|
||||
* @param repo repository pointer
|
||||
* @return 0 on success, GIT_ENOTFOUND if config is missing, or error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_signature_default_from_env(
|
||||
git_signature **author_out,
|
||||
git_signature **committer_out,
|
||||
git_repository *repo);
|
||||
|
||||
/**
|
||||
* Create a new action signature with default user and now timestamp.
|
||||
*
|
||||
@@ -56,6 +92,10 @@ GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const c
|
||||
* based on that information. It will return GIT_ENOTFOUND if either the
|
||||
* user.name or user.email are not set.
|
||||
*
|
||||
* Note that these do not examine environment variables, only the
|
||||
* configuration files. Use `git_signature_default_from_env` to
|
||||
* consider the environment variables.
|
||||
*
|
||||
* @param out new signature
|
||||
* @param repo repository pointer
|
||||
* @return 0 on success, GIT_ENOTFOUND if config is missing, or error code
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "repository.h"
|
||||
#include "git2/common.h"
|
||||
#include "posix.h"
|
||||
#include "date.h"
|
||||
|
||||
void git_signature_free(git_signature *sig)
|
||||
{
|
||||
@@ -152,15 +153,10 @@ int git_signature__pdup(git_signature **dest, const git_signature *source, git_p
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_signature_now(git_signature **sig_out, const char *name, const char *email)
|
||||
static void current_time(time_t *now_out, int *offset_out)
|
||||
{
|
||||
time_t now;
|
||||
time_t offset;
|
||||
struct tm *utc_tm;
|
||||
git_signature *sig;
|
||||
struct tm _utc;
|
||||
|
||||
*sig_out = NULL;
|
||||
struct tm _utc, *utc_tm;
|
||||
|
||||
/*
|
||||
* Get the current time as seconds since the epoch and
|
||||
@@ -170,18 +166,26 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema
|
||||
* us that time as seconds since the epoch. The difference
|
||||
* between its return value and 'now' is our offset to UTC.
|
||||
*/
|
||||
time(&now);
|
||||
utc_tm = p_gmtime_r(&now, &_utc);
|
||||
time(now_out);
|
||||
utc_tm = p_gmtime_r(now_out, &_utc);
|
||||
utc_tm->tm_isdst = -1;
|
||||
offset = (time_t)difftime(now, mktime(utc_tm));
|
||||
offset = (time_t)difftime(*now_out, mktime(utc_tm));
|
||||
offset /= 60;
|
||||
|
||||
if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
|
||||
return -1;
|
||||
*offset_out = (int)offset;
|
||||
}
|
||||
|
||||
*sig_out = sig;
|
||||
int git_signature_now(
|
||||
git_signature **sig_out,
|
||||
const char *name,
|
||||
const char *email)
|
||||
{
|
||||
time_t now;
|
||||
int offset;
|
||||
|
||||
return 0;
|
||||
current_time(&now, &offset);
|
||||
|
||||
return git_signature_new(sig_out, name, email, now, offset);
|
||||
}
|
||||
|
||||
int git_signature_default(git_signature **out, git_repository *repo)
|
||||
@@ -201,6 +205,120 @@ int git_signature_default(git_signature **out, git_repository *repo)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int user_from_env(
|
||||
git_signature **out,
|
||||
git_repository *repo,
|
||||
const char *name_env_var,
|
||||
const char *email_env_var,
|
||||
const char *date_env_var,
|
||||
time_t default_time,
|
||||
int default_offset)
|
||||
{
|
||||
int error;
|
||||
git_config *cfg;
|
||||
const char *name, *email, *date;
|
||||
git_time_t timestamp;
|
||||
int offset;
|
||||
git_str name_env = GIT_STR_INIT;
|
||||
git_str email_env = GIT_STR_INIT;
|
||||
git_str date_env = GIT_STR_INIT;
|
||||
|
||||
if ((error = git_repository_config_snapshot(&cfg, repo)) < 0)
|
||||
return error;
|
||||
|
||||
/* Check if the environment variable for the name is set */
|
||||
if (!(git__getenv(&name_env, name_env_var))) {
|
||||
name = git_str_cstr(&name_env);
|
||||
} else {
|
||||
/* or else read the configuration value. */
|
||||
if ((error = git_config_get_string(&name, cfg, "user.name")) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check if the environment variable for the email is set. */
|
||||
if (!(git__getenv(&email_env, email_env_var))) {
|
||||
email = git_str_cstr(&email_env);
|
||||
} else {
|
||||
if ((error = git_config_get_string(&email, cfg, "user.email")) == GIT_ENOTFOUND) {
|
||||
git_error *last_error;
|
||||
|
||||
git_error_save(&last_error);
|
||||
|
||||
if ((error = git__getenv(&email_env, "EMAIL")) < 0) {
|
||||
git_error_restore(last_error);
|
||||
error = GIT_ENOTFOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
email = git_str_cstr(&email_env);
|
||||
git_error_free(last_error);
|
||||
} else if (error < 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the environment variable for the timestamp is set */
|
||||
if (!(git__getenv(&date_env, date_env_var))) {
|
||||
date = git_str_cstr(&date_env);
|
||||
|
||||
if ((error = git_date_offset_parse(×tamp, &offset, date)) < 0)
|
||||
goto done;
|
||||
} else {
|
||||
timestamp = default_time;
|
||||
offset = default_offset;
|
||||
}
|
||||
|
||||
error = git_signature_new(out, name, email, timestamp, offset);
|
||||
|
||||
done:
|
||||
git_config_free(cfg);
|
||||
git_str_dispose(&name_env);
|
||||
git_str_dispose(&email_env);
|
||||
git_str_dispose(&date_env);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_signature_default_from_env(
|
||||
git_signature **author_out,
|
||||
git_signature **committer_out,
|
||||
git_repository *repo)
|
||||
{
|
||||
git_signature *author = NULL, *committer = NULL;
|
||||
time_t now;
|
||||
int offset;
|
||||
int error;
|
||||
|
||||
GIT_ASSERT_ARG(author_out || committer_out);
|
||||
GIT_ASSERT_ARG(repo);
|
||||
|
||||
current_time(&now, &offset);
|
||||
|
||||
if (author_out &&
|
||||
(error = user_from_env(&author, repo, "GIT_AUTHOR_NAME",
|
||||
"GIT_AUTHOR_EMAIL", "GIT_AUTHOR_DATE",
|
||||
now, offset)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (committer_out &&
|
||||
(error = user_from_env(&committer, repo, "GIT_COMMITTER_NAME",
|
||||
"GIT_COMMITTER_EMAIL", "GIT_COMMITTER_DATE",
|
||||
now, offset)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (author_out)
|
||||
*author_out = author;
|
||||
|
||||
if (committer_out)
|
||||
*committer_out = committer;
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git__free(author);
|
||||
git__free(committer);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_signature__parse(git_signature *sig, const char **buffer_out,
|
||||
const char *buffer_end, const char *header, char ender)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender);
|
||||
void git_signature__writebuf(git_str *buf, const char *header, const git_signature *sig);
|
||||
bool git_signature__equal(const git_signature *one, const git_signature *two);
|
||||
|
||||
int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -858,7 +858,7 @@ static git_time_t approxidate_str(const char *date,
|
||||
return update_tm(&tm, &now, 0);
|
||||
}
|
||||
|
||||
int git_date_parse(git_time_t *out, const char *date)
|
||||
int git_date_offset_parse(git_time_t *out, int *out_offset, const char *date)
|
||||
{
|
||||
time_t time_sec;
|
||||
git_time_t timestamp;
|
||||
@@ -866,6 +866,7 @@ int git_date_parse(git_time_t *out, const char *date)
|
||||
|
||||
if (!parse_date_basic(date, ×tamp, &offset)) {
|
||||
*out = timestamp;
|
||||
*out_offset = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -876,6 +877,13 @@ int git_date_parse(git_time_t *out, const char *date)
|
||||
return error_ret;
|
||||
}
|
||||
|
||||
int git_date_parse(git_time_t *out, const char *date)
|
||||
{
|
||||
int offset;
|
||||
|
||||
return git_date_offset_parse(out, &offset, date);
|
||||
}
|
||||
|
||||
int git_date_rfc2822_fmt(git_str *out, git_time_t time, int offset)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
@@ -10,9 +10,21 @@
|
||||
#include "util.h"
|
||||
#include "str.h"
|
||||
|
||||
/*
|
||||
* Parse a string into a value as a git_time_t with a timezone offset.
|
||||
*
|
||||
* Sample valid input:
|
||||
* - "yesterday"
|
||||
* - "July 17, 2003"
|
||||
* - "2003-7-17 08:23i+03"
|
||||
*/
|
||||
extern int git_date_offset_parse(git_time_t *out, int *out_offset, const char *date);
|
||||
|
||||
/*
|
||||
* Parse a string into a value as a git_time_t.
|
||||
*
|
||||
* Timezone offset is ignored.
|
||||
*
|
||||
* Sample valid input:
|
||||
* - "yesterday"
|
||||
* - "July 17, 2003"
|
||||
|
||||
@@ -153,3 +153,83 @@ void test_commit_signature__pos_and_neg_zero_offsets_dont_match(void)
|
||||
git_signature_free((git_signature *)with_neg_zero);
|
||||
git_signature_free((git_signature *)with_pos_zero);
|
||||
}
|
||||
|
||||
static git_repository *g_repo;
|
||||
|
||||
void test_commit_signature__initialize(void)
|
||||
{
|
||||
g_repo = cl_git_sandbox_init("empty_standard_repo");
|
||||
}
|
||||
|
||||
void test_commit_signature__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
g_repo = NULL;
|
||||
}
|
||||
|
||||
void test_commit_signature__from_env(void)
|
||||
{
|
||||
git_signature *author_sign, *committer_sign;
|
||||
git_config *cfg, *local;
|
||||
cl_git_pass(git_repository_config(&cfg, g_repo));
|
||||
cl_git_pass(git_config_open_level(&local, cfg, GIT_CONFIG_LEVEL_LOCAL));
|
||||
/* No configuration value is set and no environment variable */
|
||||
cl_setenv("EMAIL", NULL);
|
||||
cl_setenv("GIT_AUTHOR_NAME", NULL);
|
||||
cl_setenv("GIT_AUTHOR_EMAIL", NULL);
|
||||
cl_setenv("GIT_COMMITTER_NAME", NULL);
|
||||
cl_setenv("GIT_COMMITTER_EMAIL", NULL);
|
||||
cl_git_fail(git_signature_default_from_env(&author_sign, &committer_sign, g_repo));
|
||||
/* Name is read from configuration and email is read from fallback EMAIL
|
||||
* environment variable */
|
||||
cl_git_pass(git_config_set_string(local, "user.name", "Name (config)"));
|
||||
cl_setenv("EMAIL", "email-envvar@example.com");
|
||||
cl_git_pass(git_signature_default_from_env(&author_sign, &committer_sign, g_repo));
|
||||
cl_assert_equal_s("Name (config)", author_sign->name);
|
||||
cl_assert_equal_s("email-envvar@example.com", author_sign->email);
|
||||
cl_assert_equal_s("Name (config)", committer_sign->name);
|
||||
cl_assert_equal_s("email-envvar@example.com", committer_sign->email);
|
||||
cl_setenv("EMAIL", NULL);
|
||||
git_signature_free(author_sign);
|
||||
git_signature_free(committer_sign);
|
||||
/* Environment variables have precedence over configuration */
|
||||
cl_git_pass(git_config_set_string(local, "user.email", "config@example.com"));
|
||||
cl_setenv("GIT_AUTHOR_NAME", "Author (envvar)");
|
||||
cl_setenv("GIT_AUTHOR_EMAIL", "author-envvar@example.com");
|
||||
cl_setenv("GIT_COMMITTER_NAME", "Committer (envvar)");
|
||||
cl_setenv("GIT_COMMITTER_EMAIL", "committer-envvar@example.com");
|
||||
cl_git_pass(git_signature_default_from_env(&author_sign, &committer_sign, g_repo));
|
||||
cl_assert_equal_s("Author (envvar)", author_sign->name);
|
||||
cl_assert_equal_s("author-envvar@example.com", author_sign->email);
|
||||
cl_assert_equal_s("Committer (envvar)", committer_sign->name);
|
||||
cl_assert_equal_s("committer-envvar@example.com", committer_sign->email);
|
||||
git_signature_free(author_sign);
|
||||
git_signature_free(committer_sign);
|
||||
/* When environment variables are not set we can still read from
|
||||
* configuration */
|
||||
cl_setenv("GIT_AUTHOR_NAME", NULL);
|
||||
cl_setenv("GIT_AUTHOR_EMAIL", NULL);
|
||||
cl_setenv("GIT_COMMITTER_NAME", NULL);
|
||||
cl_setenv("GIT_COMMITTER_EMAIL", NULL);
|
||||
cl_git_pass(git_signature_default_from_env(&author_sign, &committer_sign, g_repo));
|
||||
cl_assert_equal_s("Name (config)", author_sign->name);
|
||||
cl_assert_equal_s("config@example.com", author_sign->email);
|
||||
cl_assert_equal_s("Name (config)", committer_sign->name);
|
||||
cl_assert_equal_s("config@example.com", committer_sign->email);
|
||||
git_signature_free(author_sign);
|
||||
git_signature_free(committer_sign);
|
||||
/* We can also override the timestamp with an environment variable */
|
||||
cl_setenv("GIT_AUTHOR_DATE", "1971-02-03 04:05:06+01");
|
||||
cl_setenv("GIT_COMMITTER_DATE", "1988-09-10 11:12:13-01");
|
||||
cl_git_pass(git_signature_default_from_env(&author_sign, &committer_sign, g_repo));
|
||||
cl_assert_equal_i(34398306, author_sign->when.time); /* 1971-02-03 03:05:06 UTC */
|
||||
cl_assert_equal_i(60, author_sign->when.offset);
|
||||
cl_assert_equal_i(589896733, committer_sign->when.time); /* 1988-09-10 12:12:13 UTC */
|
||||
cl_assert_equal_i(-60, committer_sign->when.offset);
|
||||
git_signature_free(author_sign);
|
||||
git_signature_free(committer_sign);
|
||||
cl_setenv("GIT_AUTHOR_DATE", NULL);
|
||||
cl_setenv("GIT_COMMITTER_DATE", NULL);
|
||||
git_config_free(local);
|
||||
git_config_free(cfg);
|
||||
}
|
||||
|
||||
@@ -20,3 +20,11 @@ void test_date_date__invalid_date(void)
|
||||
cl_git_fail(git_date_parse(&d, ""));
|
||||
cl_git_fail(git_date_parse(&d, "NEITHER_INTEGER_NOR_DATETIME"));
|
||||
}
|
||||
|
||||
void test_date_date__offset(void)
|
||||
{
|
||||
git_time_t d;
|
||||
int offset;
|
||||
cl_git_pass(git_date_offset_parse(&d, &offset, "1970-1-1 01:00:00+03"));
|
||||
cl_assert_equal_i(offset, 3*60);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user