Repository: pre-commit/pre-commit

Issue:
Pre-push hook checks only when pushing to certain remote
I have two remotes configured in my repository (`origin` and `upstream`) and I want my pre-push hook to fire and do the configured checks only when we push to `upstream`, bypassing all checks when we push to remote `origin`. Is this possible without using git `--no-verify` option?
I was checking for ways to obtain the remote from `pre-commit`as a parameter and pass it to my local hook to bypass the checks in case remote doesn't match `upstream` but I haven't been very successful. Any ideas on how to do this?

Thank you so much for all the great work you are doing with this project.


Discussion:
(moved to the proper project, `pre-commit/pre-commit-hooks` is for the builtin hook executables)
pre-commit currently doesn't expose that information (but it could!)

I think the only way it would be able to do so would be as an environment variable, here's roughly what you'd have to change to make this work:

1. in [`hook-tmpl`](https://github.com/pre-commit/pre-commit/blob/ef583d2fde05e631c59d6637e433bf86dd0e6331/pre_commit/resources/hook-tmpl#L125-L159) capture the values of `sys.argv[1]` (remote name) and `sys.argv[2]` (remote url) -- add these to `opts` in some way
2. in [`pre_commit/main.py`](https://github.com/pre-commit/pre-commit/blob/ef583d2fde05e631c59d6637e433bf86dd0e6331/pre_commit/main.py#L82-L113) add options for these two values (probably `--push-remote-name` and `--push-remote-url` or some such -- probably add these near the other hook-specific options like `--commit-msg-filename`)
3. in [`pre_commit/commands/run.py`](https://github.com/pre-commit/pre-commit/blob/ef583d2fde05e631c59d6637e433bf86dd0e6331/pre_commit/commands/run.py#L257-L260) expose those new environment variables when the values are set
4. in [`tests/commands/install_uninstall_test.py`](https://github.com/pre-commit/pre-commit/blob/ef583d2fde05e631c59d6637e433bf86dd0e6331/tests/commands/install_uninstall_test.py#L550-L561) write a test which exercises this (I've linked a related `pre-push` test)

let me know if you think this feature would be helpful or not and if you'd want to work on it
That's great! Yes, I'd like to work on it if that's OK for you.
sure!
After checking your proposed changes and the code a bit better I think this solution does not work for my case since the env vars will only be available (or so it seems looking at the code) through `pre-commit run --push-remote-name` and this is not so interesting for me since I do not trigger the pre-push hook with run, I just use pre-commit to install my hooks.

I would need a way to either inform my local hook about the remote we are pushing to (to be able act accordingly in the hook) or a way to skip the pre-push hook when pushing to a certain remotes (or the opossite, run it only when pushing to certain remote, both would work).

Again thank you so much for the quick replies and all the support.
so `hook-tmpl` is the thing that gets templated into `.git/hooks/pre-push` -- so it would be available
(basically the way `pre-commit` works is it templates that script into the `.git/hooks` directory, which eventually calls `pre-commit run ...` with the proper arguments)
@dmbarreiro would that work for you?
Yest, it does! I think I have the code ready, I am figuring out how could I test it, I want to check the variable is available in the hook. I was trying to find similar test cases for other env vars like `--origin` and `--source` but couldn't find any. I will continue looking when I have some more time but testing is the only part missing I think.
oops looks like `--origin` and `--source` are untested

you can probably write a little shell hook and `echo` the variables and then run the hook using `verbose: true` or `--verbose` (probably can even get away with a `local` hook if you don't want to set up a full repository in `testing/resources`)
let me know if you need help writing a test, happy to supply that (you could start a WIP PR probably and I can give some pointers / code)

Resolving patch:
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -312,6 +312,10 @@ def run(
         environ['PRE_COMMIT_ORIGIN'] = args.origin
         environ['PRE_COMMIT_SOURCE'] = args.source
 
+    if args.remote_name and args.remote_url:
+        environ['PRE_COMMIT_REMOTE_NAME'] = args.remote_name
+        environ['PRE_COMMIT_REMOTE_URL'] = args.remote_url
+
     with contextlib.ExitStack() as exit_stack:
         if stash:
             exit_stack.enter_context(staged_files_only(store.directory))
diff --git a/pre_commit/main.py b/pre_commit/main.py
--- a/pre_commit/main.py
+++ b/pre_commit/main.py
@@ -101,6 +101,10 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
         '--commit-msg-filename',
         help='Filename to check when running during `commit-msg`',
     )
+    parser.add_argument(
+        '--remote-name', help='Remote name used by `git push`.',
+    )
+    parser.add_argument('--remote-url', help='Remote url used by `git push`.')
     parser.add_argument(
         '--hook-stage', choices=C.STAGES, default='commit',
         help='The stage during which the hook is fired.  One of %(choices)s',


Test patch:
diff --git a/testing/util.py b/testing/util.py
--- a/testing/util.py
+++ b/testing/util.py
@@ -67,6 +67,8 @@ def run_opts(
         hook=None,
         origin='',
         source='',
+        remote_name='',
+        remote_url='',
         hook_stage='commit',
         show_diff_on_failure=False,
         commit_msg_filename='',
@@ -81,6 +83,8 @@ def run_opts(
         hook=hook,
         origin=origin,
         source=source,
+        remote_name=remote_name,
+        remote_url=remote_url,
         hook_stage=hook_stage,
         show_diff_on_failure=show_diff_on_failure,
         commit_msg_filename=commit_msg_filename,
diff --git a/tests/commands/install_uninstall_test.py b/tests/commands/install_uninstall_test.py
--- a/tests/commands/install_uninstall_test.py
+++ b/tests/commands/install_uninstall_test.py
@@ -15,6 +15,7 @@
 from pre_commit.util import cmd_output
 from pre_commit.util import make_executable
 from pre_commit.util import resource_text
+from testing.fixtures import add_config_to_repo
 from testing.fixtures import git_dir
 from testing.fixtures import make_consuming_repo
 from testing.fixtures import remove_config_from_repo
@@ -512,9 +513,9 @@ def test_installed_from_venv(tempdir_factory, store):
         assert NORMAL_PRE_COMMIT_RUN.match(output)
 
 
-def _get_push_output(tempdir_factory, opts=()):
+def _get_push_output(tempdir_factory, remote='origin', opts=()):
     return cmd_output_mocked_pre_commit_home(
-        'git', 'push', 'origin', 'HEAD:new_branch', *opts,
+        'git', 'push', remote, 'HEAD:new_branch', *opts,
         tempdir_factory=tempdir_factory,
         retcode=None,
     )[:2]
@@ -589,6 +590,33 @@ def test_pre_push_new_upstream(tempdir_factory, store):
         assert 'Passed' in output
 
 
+def test_pre_push_environment_variables(tempdir_factory, store):
+    config = {
+        'repo': 'local',
+        'hooks': [
+            {
+                'id': 'print-remote-info',
+                'name': 'print remote info',
+                'entry': 'bash -c "echo remote: $PRE_COMMIT_REMOTE_NAME"',
+                'language': 'system',
+                'verbose': True,
+            },
+        ],
+    }
+
+    upstream = git_dir(tempdir_factory)
+    clone = tempdir_factory.get()
+    cmd_output('git', 'clone', upstream, clone)
+    add_config_to_repo(clone, config)
+    with cwd(clone):
+        install(C.CONFIG_FILE, store, hook_types=['pre-push'])
+
+        cmd_output('git', 'remote', 'rename', 'origin', 'origin2')
+        retc, output = _get_push_output(tempdir_factory, remote='origin2')
+        assert retc == 0
+        assert '\nremote: origin2\n' in output
+
+
 def test_pre_push_integration_empty_push(tempdir_factory, store):
     upstream = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
     path = tempdir_factory.get()
diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py
--- a/tests/commands/run_test.py
+++ b/tests/commands/run_test.py
@@ -456,8 +456,11 @@ def test_origin_source_error_msg_error(
     assert b'Specify both --origin and --source.' in printed
 
 
-def test_origin_source_both_ok(cap_out, store, repo_with_passing_hook):
-    args = run_opts(origin='master', source='master')
+def test_all_push_options_ok(cap_out, store, repo_with_passing_hook):
+    args = run_opts(
+        origin='master', source='master',
+        remote_name='origin', remote_url='https://example.com/repo',
+    )
     ret, printed = _do_run(cap_out, store, repo_with_passing_hook, args)
     assert ret == 0
     assert b'Specify both --origin and --source.' not in printed