Skip to content

Commit

Permalink
stage_commit_push_changes to allow empty list items_to_stage (#140)
Browse files Browse the repository at this point in the history
change behavior of stage_commit_push_changes to allow empty list items to stage
  • Loading branch information
jdw6359 authored Sep 22, 2021
1 parent 64b5d06 commit 9072ad8
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 11 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.13.0] - 2021-09-22

### Changed

* Change in behavior for `pygitops.operations.stage_commit_push_changes`:
* Previously, the `items_to_stage` optional argument will drive autodiscovery of items to stage if not provided.
* Previously, when no items to stage are discovered, a `PyGitOpsError` is raised.
* The new change in behavior allows `items_to_stage` to be explicitly set to `[]`, indicating that the caller does not want autodiscovery of items to stage to occur, and the list is indeed empty.
* This change caters to more advanced use cases such as [performing automated updates to git submodules where staged changes are intentionally absent](https://wayfair-incubator.github.io/pygitops/making-changes-on-feature-branch/#advanced-example)

## [0.12.1] - 2021-09-21

### Changed

* Restrict the allowable version range of GitPython to >=3.1,<=3.1.18
* Newer versions of GitPython have had significant issues with typehinting that are proving to be incompatible with mypy

Expand Down
55 changes: 51 additions & 4 deletions docs/making-changes-on-feature-branch.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ NEW_FILE_NAME = 'chores.txt'
NEW_BRANCH_NAME = 'adding-chores'
ACTOR = Actor('git-username', '[email protected]')
COMMIT_MESSAGE = 'Adding chores'
SOME_DIRECTORY = 'some-directory'

# create a Repo object in the current directory (see https://gitpython.readthedocs.io/en/stable/tutorial.html#meet-the-repo-type)
REPO = Repo(self.rorepo.working_tree_dir)
repo = Repo(SOME_DIRECTORY)

with feature_branch(REPO, NEW_BRANCH_NAME):
# create a new file with a list of chores
Path(NEW_FILE_NAME).write_text('- [ ] haircut\n- [ ] groceries\n- [ ] dishes')
(Path(SOME_DIRECTORY) / NEW_FILE_NAME).write_text('- [ ] haircut\n- [ ] groceries\n- [ ] dishes')

# stage, commit, and push these changes
stage_commit_push_changes(REPO, NEW_BRANCH_NAME, ACTOR, COMMIT_MESSAGE)
stage_commit_push_changes(repo, NEW_BRANCH_NAME, ACTOR, COMMIT_MESSAGE)
```

Focusing on the portion of the code starting at the line with `with feature_branch(REPO, NEW_BRANCH_NAME):`,
Focusing on the portion of the code starting at the line with `with feature_branch(repo, NEW_BRANCH_NAME):`,
we can generalize the workflow as:

- Create a new, feature branch
Expand All @@ -38,3 +39,49 @@ we can generalize the workflow as:
- Return to the default branch

This is a common pattern when automating git workflows; using `feature_branch` and `stage_commit_push_changes` makes it easy to capture this flow in Python.

## Advanced Example

In the below example, we have a repository that contains [git submodules][git_submodules].

The submodule references a git repository rather than containing its code.

In some cases, you may want to automate updates to this reference.

The below example demonstrates several key concepts:

- Using `repo.git`, we are able to directly execute git commands such as `submodule update --remote` and `add SOME_SUBMODULE_REPO_NAME`
- When we execute `stage_commit_push_changes`, we would like to commit to a feature branch, but no items are available to be staged.
- Here, we explicitly provide the `items_to_stage=[]` to indicate that we intentionally have no items to stage.
- Otherwise, the operation will infer staged items for us, and raise a `PyGitOpsError` when no staged items are discovered.

```python
from pygitops.operations import feature_branch, stage_commit_push_changes
from git import Actor, Repo

NEW_BRANCH_NAME = 'some-new-branch'
ACTOR = Actor('git-username', '[email protected]')
REPO = Repo(SOME_DIRECTORY)
COMMIT_MESSAGE = 'automated submodule update
SOME_SUBMODULE_REPO_NAME = 'some-repo'

# update submodule's reference to remote upstream, push feature branch with changes
with feature_branch(repo, NEW_BRANCH_NAME):

# update the submodules's reference to the remote repo
repo.git.submodule("update", "--remote")

# stage changes ourselves so that we can use the porcelain `git add <file>` command
# tell `stage_commit_push_changes()` to not stage anything itself via `items_to_stage=[]`
repo.git.add(SOME_SUBMODULE_REPO_NAME)

stage_commit_push_changes(
repo,
NEW_BRANCH_NAME,
ACTOR,
COMMIT_MESSAGE,
items_to_stage=[],
)
```

[git_submodules]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
14 changes: 8 additions & 6 deletions pygitops/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,25 @@ def stage_commit_push_changes(
:param branch_name: Feature branch from which changes will be committed and pushed.
:param actor: The Github actor with which to perform the commit operation.
:param commit_message: Text to be used as the commit message.
:param items_to_stage: List of files and directories that will be staged for commit, if None will include all changes.
:raises PyGitOpsStagedItemsError: There were no items to stage.
:param items_to_stage: List of files and directories that will be staged for commit, will be inferred if parameter not provided.
Please use an empty list to signal that there are intentionally no items to stage, and items_to_stage should not be inferred.
:raises PyGitOpsStagedItemsError: Items to stage are not present or could not be determined.
:raises PyGitOpsError: There was an error staging, committing, or pushing code.
"""
index = repo.index
workdir_path = Path(repo.working_dir)

# We will determine items_to_stage if the parameter was not provided.
if items_to_stage is None:
untracked_file_paths = [Path(f) for f in repo.untracked_files]
items_to_stage = untracked_file_paths + [
Path(f.a_path) for f in index.diff(None)
]

if not items_to_stage:
raise PyGitOpsStagedItemsError(
"There are no items to stage, cannot perform commit operation"
)
if not items_to_stage:
raise PyGitOpsStagedItemsError(
"There are no items to stage, cannot perform commit operation"
)

# stage and commit changes using the provided actor.
for item in items_to_stage:
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = pygitops
url = https://github.com/wayfair-incubator/pygitops
author = Josh Woodward
author_email = [email protected]
version = 0.12.1
version = 0.13.0
description = Wrapper for low-level git logic. Useful for systems automating git operations.
long_description = file: README.md
long_description_content_type = text/markdown
Expand Down

0 comments on commit 9072ad8

Please sign in to comment.