Repository: wayfair-incubator/pygitops (language: python)

Issue:
Use bump2version
To manage versions in this library, we should use [bump2version](https://pypi.org/project/bump2version/).
development guide - package publish instructions
Our development guide should document how to publish new versions of the package


Discussion / hints:
@fhightower - any concerns if I pick this?


Fix patch:
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
new file mode 100644
index 0000000..df08d81
--- /dev/null
+++ b/.bumpversion.cfg
@@ -0,0 +1,12 @@
+[bumpversion]
+current_version = 0.13.0
+commit = True
+tag = False
+
+[bumpversion:file:docs/index.md]
+search = pygitops - {current_version}
+replace = pygitops - {new_version}
+
+[bumpversion:file:setup.cfg]
+search = version = {current_version}
+replace = version = {new_version}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 44cece1..a5fb308 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -9,3 +9,11 @@ updates:
     directory: "/"
     schedule:
       interval: "daily"
+  - package-ecosystem: "docker"
+    directory: "/docker"
+    schedule:
+      interval: "daily"
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "daily"
diff --git a/.github/workflows/detect_version_bump.yml b/.github/workflows/detect_version_bump.yml
new file mode 100644
index 0000000..12fe39e
--- /dev/null
+++ b/.github/workflows/detect_version_bump.yml
@@ -0,0 +1,36 @@
+name: Version Bump merged
+
+on:
+  pull_request:
+    types:
+      - "closed"
+    branches:
+      - "main"
+
+jobs:
+  trigger-release:
+    runs-on: ubuntu-latest
+    if: github.event.pull_request.merged &&
+        startsWith(github.head_ref, 'bump_version_to_') &&
+        startsWith(github.event.pull_request.title, 'Bump version') &&
+        contains(github.event.pull_request.title, ' → ')
+    environment: Create Release
+    steps:
+      - name: Get new version
+        id: get-new-version
+        run: |
+          NEW_VERSION=$(echo ${{ github.head_ref }} | cut -d _ -f4 )
+          echo "::set-output name=version::$NEW_VERSION"
+      - name: Is prerelease?
+        id: is-prerelease
+        run: |
+          IS_PRERELEASE=$([[ "${{ steps.get-new-version.outputs.version }}" == *-[a-z]* ]] && echo true || echo false)
+          echo "::set-output name=result::$IS_PRERELEASE"
+      - name: Create Release
+        # use hash for security since this has access to the access token
+        uses: ncipollo/release-action@880be3d0a71bc0fa98db60201d2cbdc27324f547 # v1.8.6
+        with:
+          token: ${{ secrets.ACCESS_TOKEN }}
+          tag: v${{ steps.get-new-version.outputs.version }}
+          name: v${{ steps.get-new-version.outputs.version }} Release
+          commit: main
\ No newline at end of file
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 93ad777..b4e7199 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,4 +1,10 @@
 version: '3.4'
+
+x-mount-app-and-user-git-config: &mount-app-and-user-git-config
+  volumes:
+    - ./:/app
+    - ~/.gitconfig:/home/gbq/.gitconfig # allow script to commit as user
+
 services:
 
   # a fully loaded development environment to test new code
@@ -28,3 +34,8 @@ services:
   mike:
     <<: *mkdocs
     entrypoint: "mike"
+
+  bump:
+    <<: *devbox
+    <<: *mount-app-and-user-git-config
+    entrypoint: docker/bump_version.sh
diff --git a/docker/bump_version.sh b/docker/bump_version.sh
new file mode 100755
index 0000000..a047342
--- /dev/null
+++ b/docker/bump_version.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+# Referenced From: https://github.com/wayfair-incubator/columbo/blob/main/docker/bump_version.sh
+
+set -eo pipefail
+
+DEFAULT_BRANCH="main"
+
+function usage() {
+  echo "usage: bump_version.sh major|minor|patch"
+  echo ""
+  echo " major|minor|patch : Part of version string to updated."
+  exit 1
+}
+
+if [[ $# != 1 || ! "${1}" =~ major|minor|patch ]]; then
+  usage
+else
+  PART="${1}"
+fi
+
+CURRENT_BRANCH=$(git branch --show-current)
+CURRENT_STATUS=$(git status --short --untracked-files=no)
+
+if [[ "${CURRENT_BRANCH}" != "${DEFAULT_BRANCH}" ]]; then
+  echo "A version bump must be run from the default branch."
+  echo "Run 'git switch ${DEFAULT_BRANCH}'"
+  exit 2
+elif [[ "$CURRENT_STATUS" != "" ]]; then
+  echo "The working tree has uncommitted changes."
+  echo "Commit or stash the changes before running a version bump."
+  exit 3
+fi
+# Ensure on default branch
+git switch --no-guess main
+# Capture value of new version
+NEW_VERSION=$(bump2version --dry-run --list "${PART}" | grep new_version | sed -r s,"^.*=",,)
+# Create bump branch
+BUMP_BRANCH_NAME="bump_version_to_${NEW_VERSION}"
+git switch -c "${BUMP_BRANCH_NAME}"
+# Update files
+bump2version "${PART}"
+
+# Updating the changelog has to be done manually
+#   - bump2version doesn't support inserting dates https://github.com/c4urself/bump2version/issues/133
+#   - It is not possible to have a multiline string in an INI file where a line after the first line starts with '#'.
+#     The config parser reads it as a comment line.
+TODAY=$(date +%Y-%m-%d)
+sed -i "s/## \[Unreleased\]/## \[Unreleased\]\n\n## \[${NEW_VERSION}\] - ${TODAY}/g" CHANGELOG.md
+
+# Add changelog to bump commit
+git add CHANGELOG.md
+git commit --amend --no-edit
+# Show effected files
+git show --pretty="" --name-only
+
+echo "Run 'git push --set-upstream origin ${BUMP_BRANCH_NAME}' to "
diff --git a/docs/development-guide.md b/docs/development-guide.md
index 05b5ad3..3cc5d3e 100644
--- a/docs/development-guide.md
+++ b/docs/development-guide.md
@@ -86,13 +86,69 @@ grab the version from `__init__.py` without actually importing any dependencies.
 * **requirements.txt** - Lists all direct dependencies (packages imported by the library).
 * **Requirements-test.txt** - Lists all direct requirements needed to run the test suite & lints.
 
-## Publishing the Package
+## Publishing a New Version
 
-TODO: Document package publish process
+Once the package is ready to be released, there are a few things that need to be done:
+
+1. Start with a local clone of the repo on the default branch with a clean working tree.
+2. Run the version bump script with the appropriate part name (`major`, `minor`, or `patch`).
+    Example: `docker-compose run --rm bump minor`
+    
+    This wil create a new branch, updates all affected files with the new version, and commit the changes to the branch.
+
+3. Push the new branch to create a new pull request.
+4. Get the pull request approved.
+5. Merge the pull request to the default branch.
+
+Merging the pull request will trigger a GitHub Action that will create a new release. The creation of this new
+release will trigger a GitHub Action that will trigger a wheel build & a source distributions of the package and push them to
+[PyPI][pypi].
+
+!!! warning
+    The action that uploads the files to PyPI will not run until a repository maintainer acknowledges that the job is
+    ready to run. This is to keep the PyPI publishing token secure. Otherwise, any job would have access to the token. 
+
+In addition to uploading the files to PyPI, the documentation website will be updated to include the new version. If the
+new version is a full release, it will be made the new `latest` version.
 
 ## Continuous Integration Pipeline
 
-TODO: Add CI documentation.
+The Continuous Integration (CI) Pipeline runs to confirm that the repository is in a good state. It will run when 
+someone creates a pull request or when they push new commits to the branch for an existing pull request. The pipeline
+runs multiple different jobs that helps verify the state of the code.
+
+This same pipeline also runs on the default branch when a maintainer merges a pull request.
+
+### Lints
+
+The first set of jobs that run as part of the CI pipline are linters that perform static analysis on the code. This
+includes: [MyPy][mypy-docs], [Black][black-docs], [Isort][isort-docs], [Flake8][flake8-docs], and [Bandit][bandit-docs].
+
+### Tests
+
+The next set of jobs run the unit tests using [PyTest][pytest-docs]. The pipeline runs the tests cases across each
+supported version of Python to ensure compatibility.
+
+For each run of the test cases, the job will record the test results and code coverage information. The pipeline uploads
+the code coverage information to [CodeCov][codecov] to ensure that a pull request doesn't significantly reduce the total
+code coverage percentage or introduce a large amount of code that is untested.
+
+### Distribution Verification
+
+The next set of jobs build the wheel distribution, installs in into a virtual environment, and then runs Python to
+import the library version. This works as a smoke test to ensure that the library can be packaged correctly and used.
+The pipeline runs the tests cases across each supported version of Python to ensure compatibility.
+
+### Documentation
+
+The remaining jobs are all related to documentation.
+
+* A job runs each of the code examples that are used in the documentation to verify they produce the expected results.
+* A job builds the documentation in strict mode so that it will fail if there are any errors. The job records the
+    generated files so that the documentation website can be viewed in its rendered form.
+* When the pipeline is running as a result of a maintainer merging a pull request to the default branch, a job runs that
+    publishes the current state of the documentation to as the `dev` version. This will allow users to view the state of
+    the documentation as it has changed since a maintainer published the `latest` version.
 
 [usage-guide]: usage-guide/fundamentals.md
 [code of conduct]: https://github.com/wayfair-incubator/pygitops/blob/main/CODE_OF_CONDUCT.md
@@ -108,3 +164,5 @@ TODO: Add CI documentation.
 [isort-docs]: https://pycqa.github.io/isort/
 [flake8-docs]: http://flake8.pycqa.org/en/stable/
 [bandit-docs]: https://bandit.readthedocs.io/en/stable/
+[pypi]: https://pypi.org/project/pygitops/
+[codecov]: https://about.codecov.io/
diff --git a/docs/index.md b/docs/index.md
index d1f9cb7..92a8ef7 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,4 +1,4 @@
-# pygitops - 0.12.0
+# pygitops - 0.13.0
 
 [![CI pipeline status](https://github.com/wayfair-incubator/pygitops/workflows/CI/badge.svg?branch=main)][ci]
 


Test patch:
diff --git a/requirements-test.txt b/requirements-test.txt
index 1d25137..d57c3d9 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -1,5 +1,6 @@
 bandit==1.7.0
 black==21.9b0
+bump2version==1.0.1
 flake8==3.9.2
 isort==5.9.3
 mypy==0.910