Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow passed authentication params on init #41

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.8]
python-version: [3.7, 3.8, 3.9, '3.10', 3.11]

steps:
- uses: actions/checkout@v1
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com),
and this project adheres to [Semantic Versioning](https://semver.org).

## [3.5.0] - 2023-05-16
### Added
- Added support to pass in a github token, username and password as named parameters

## [3.4.0] - 2023-02-24
### Added
- Added method to return the github client

## [3.3.0] - 2022-05-12
### Added
- Added support for serialization options when dumping a file

## [3.2.0] - 2021-03-26
### Added
- Added plain text file support
Expand Down
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,24 +130,48 @@ if __name__ == '__main__':

# Dependencies
- `config.yaml` (required) - list of repositories you wish to modify
- `GIT_USERNAME` (required) - your Github username
- `GIT_PASSWORD` (required) - Github personal access token that grants write access to the specified repositories
- `GIT_USERNAME` (optional) - your Github username
- `GIT_PASSWORD` (optional) - your Github password or Personal Access Token
- `GIT_TOKEN` (optional) - Github Personal Access Token that grants write access to the specified repositories

# Authentication
Two methods of authentication are available:
- Using a Personal Access Token
- Using a Github Username & Password

A Github Personal Access Token, Github Username and Github Password can also be passed in via the `token=`, `username=` and `password=` named parameters. The passed value will always take precedence over any environment variable. (Added in `3.5.0`)

## Authentication - Personal Access Token
A Personal Access Token can be used in two ways:
- Setting the `GIT_TOKEN` environment variable
- Passing the `token=` named parameter

The Personal Access Token must have `write` access to any specified repositories you wish to submit changes to.

## Authentication - Github Username & Password
A Github Username and Password combination can be used in two ways:
- Setting the `GIT_USERNAME` and `GIT_PASSWORD` environment variables
- Passing the `username=` and `password=` parameters

The user must have `write` access to any specified repositories you wish to submit changes to.

The `GIT_PASSWORD` or `password=` may also contain a Personal Access Token instead of the account password.

# Development
The simplest way to hit the ground running if you want to contribute with code is using docker.

Launch a python container
```
```shell
localhost$ docker run --rm -it -v $(pwd):$(pwd) -w $(pwd) python:3.7-stretch bash
```

Install the project and test dependencies in developer mode
```
```shell
container# pip install -e .[test]
```

Run the tests
```
```shell
container# pytest
=========================================== test session starts ============================================
platform linux -- Python 3.7.1, pytest-4.5.0, py-1.8.0, pluggy-0.11.0
Expand All @@ -170,3 +194,4 @@ collected 33 items
## Contributors
- [Jeremy Chavez](https://github.com/kaosx5s)
- [Etienne Grignon](https://github.com/sharpyy)
- [Anshul Vohra](https://github.com/AnshulV98)
18 changes: 10 additions & 8 deletions gordian/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import datetime
import logging
import os
import sys
import time
from retry import retry
from gordian.files import *
from gordian.files.plaintext_file import PlainTextFile
Expand All @@ -16,7 +14,7 @@

class Repo:

def __init__(self, repo_name, github_api_url=None, branch=None, github=None, files=None, semver_label=None, target_branch='master', fork=False):
def __init__(self, repo_name, github_api_url=None, branch=None, github=None, files=None, semver_label=None, target_branch='master', fork=False, token=None, username=None, password=None):
if github_api_url is None:
self.github_api_url = BASE_URL
else:
Expand All @@ -25,12 +23,16 @@ def __init__(self, repo_name, github_api_url=None, branch=None, github=None, fil
if github is not None:
self._github = github
else:
if "GIT_TOKEN" in os.environ:
logger.debug('Using git token')
self._github = Github(base_url=self.github_api_url, login_or_token=os.environ['GIT_TOKEN'])
username = os.getenv('GIT_USERNAME', username)
password = os.getenv('GIT_PASSWORD', password)
token = os.getenv('GIT_TOKEN', token)

if token:
logger.debug('Using git token for authentication')
self._github = Github(base_url=self.github_api_url, login_or_token=token)
else:
logger.debug('Using git username and password')
self._github = Github(base_url=self.github_api_url, login_or_token=os.environ['GIT_USERNAME'], password=os.environ['GIT_PASSWORD'])
logger.debug('Using git username and password for authentication')
self._github = Github(base_url=self.github_api_url, login_or_token=username, password=password)

if files is None:
files = []
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
setup_reqs = ["pytest", "pytest-cov", "pytest-runner", "flake8"]
setuptools.setup(
name="gordian",
version="3.4.0",
version="3.5.0",
author="Intuit",
author_email="[email protected]",
description="A tool to search and replace files in a Git repo",
Expand Down
57 changes: 50 additions & 7 deletions tests/test_repo.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest
import pytest
import os
from gordian.repo import Repo
from unittest.mock import MagicMock, patch, call
from gordian.files import YamlFile
Expand Down Expand Up @@ -86,15 +87,15 @@ def test_get_files(self):
self.repo._source_repo.get_contents.assert_has_calls([call('', 'refs/heads/target'), call('directory', 'refs/heads/target')])
self.assertEquals(self.repo.files, [repository_file])

def test__set_target_branch(self):
def test_set_target_branch(self):
self.repo._set_target_branch('master')
self.assertEqual(self.repo.source_branch, 'refs/heads/master')

def test__set_target_branch_source_branch(self):
def test_set_target_branch_source_branch(self):
self.repo._set_target_branch('master', 'something')
self.assertEqual(self.repo.source_branch, 'refs/heads/something')

def test__set_target_branch_reset_file_cache(self):
def test_set_target_branch_reset_file_cache(self):
self.repo._set_target_branch('master')
cached_files = ['cached_file', 'cached_file', 'cached_file']
self.repo.files = cached_files
Expand Down Expand Up @@ -127,29 +128,71 @@ def test_create_pr_no_labels(self):
pr.set_labels.assert_not_called()
repo._source_repo.create_pull.assert_not_called()

def test__get_new_version_major(self):
def test_get_new_version_major(self):
version_file = MagicMock()
version_file.decoded_content = '1.2.3'.encode('utf-8')
self.repo.version_file = version_file
self.repo.semver_label = 'major'
self.repo._get_new_version()
self.assertEqual(self.repo.new_version, '2.0.0')

def test__get_new_version_minor(self):
def test_get_new_version_minor(self):
version_file = MagicMock()
version_file.decoded_content = '1.2.3'.encode('utf-8')
self.repo.version_file = version_file
self.repo.semver_label = 'minor'
self.repo._get_new_version()
self.assertEqual(self.repo.new_version, '1.3.0')

def test__get_new_version_patch(self):
def test_get_new_version_patch(self):
version_file = MagicMock()
version_file.decoded_content = '1.2.3'.encode('utf-8')
self.repo.version_file = version_file
self.repo.semver_label = 'patch'
self.repo._get_new_version()
self.assertEqual(self.repo.new_version, '1.2.4')

@patch('gordian.repo.Github')
def test_init_with_passed_token(self, mock_git):
Repo('test_repo', token='abcdef')
args = {'login_or_token': 'abcdef', 'base_url': 'https://api.github.com'}
mock_git.assert_called_with(**args)

@patch.dict(os.environ, {'GIT_TOKEN': '12345'})
@patch('gordian.repo.Github')
def test_init_with_token_from_env(self, mock_git):
Repo('test_repo')
args = {'login_or_token': '12345', 'base_url': 'https://api.github.com'}

mock_git.assert_called_with(**args)

@patch.dict(os.environ, {'GIT_USERNAME': 'test-user', 'GIT_PASSWORD': 'test-pass'})
@patch('gordian.repo.Github')
def test_init_with_user_pass_env(self, mock_git):
Repo('test_repo')
args = {'login_or_token':'test-user', 'password':'test-pass', 'base_url': 'https://api.github.com'}

mock_git.assert_called_with(**args)

@patch('gordian.repo.Github')
def test_create_file(self, mock_git):
repo = Repo('test_repo', github=mock_git)
repo.create_file('test', 'test', 'test file create')

repo._source_repo.create_file.assert_called_once()
self.assertTrue(repo.dirty)

@patch('gordian.repo.Github')
def test_delete_file(self, mock_git):
mock_file = MagicMock()
repo = Repo('test_repo', github=mock_git)
repo.delete_file(mock_file, 'test file delete')

repo._source_repo.delete_file.assert_called_once()
self.assertTrue(repo.dirty)

def test__get_github_client(self):
self.assertIsNotNone(self.repo.get_github_client)
repo = Repo('test_repo', branch='', github=self.mock_git)

self.assertIsNotNone(repo.get_github_client())
self.assertEqual(repo.get_github_client(), self.mock_git)
Loading