Metadata-Version: 2.4
Name: git-epic
Version: 0.1.0
Summary: Epic-based Git workflows and GitLab merge requests
Author: Wonderteam
Project-URL: Repository, https://gitlab.wonderbeauties.com/wonderteam/git-epic
Project-URL: Issues, https://gitlab.wonderbeauties.com/wonderteam/git-epic/-/issues
Keywords: git,cli,gitlab,epic,workflow
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == "dev"
Requires-Dist: ruff<1,>=0.11; extra == "dev"
Requires-Dist: twine<7,>=6; extra == "dev"

# git-epic

A Python CLI for our epic-based Git workflow, with no runtime Python dependencies.
Git discovers the `git-epic`
executable automatically, so commands use `git epic …`.

## Install

Requires Python 3.11+, Git 2.31+, and [glab](https://gitlab.com/gitlab-org/cli).
Authenticate once with your GitLab host:

```sh
glab auth login --hostname gitlab.wonderbeauties.com
```

After the first PyPI release, install with [pipx](https://pipx.pypa.io/):

```sh
pipx install git-epic
pipx ensurepath
git epic -h
```

For local development before publication:

```sh
pipx install .
pipx ensurepath
git epic -h
```

If `ensurepath` changes your PATH, open a new terminal. Upgrade with
`pipx upgrade git-epic`. The package is not yet published; `pipx install .` works
from a local checkout today.

## Commands

Use `git epic -h` for CLI help. Git reserves `git epic --help` for a system manual
page; this MVP does not install one. Direct invocation also supports
`git-epic --help`.

| Command | Behavior |
| --- | --- |
| `git epic init` | Write shared `.git-epic.json`; defaults to `production`, `origin`, and GitLab. |
| `git epic start <name>` | Fetch production; create and switch to local `epic/<name>`. |
| `git epic task start <name>` | From an epic, fetch and fast-forward remote epic changes if possible; create `task/<epic>/<name>`. |
| `git epic task submit` | Push committed task changes; create or return its parent epic MR. Publish the local parent epic if it is not yet on the remote. |
| `git epic sync` | From an epic, fetch and merge production. Push the result yourself or with `release`. |
| `git epic status` | Fetch; show current task/epic, labelled local/remote epic drift, related open MRs (including task MRs when on an epic), and their latest reported CI status. |
| `git epic release` | Push committed epic changes; create or return an MR targeting production. **Never merges or deploys.** |

GitLab calls PRs **merge requests (MRs)**. Submission commands also push new commits
when returning an existing MR; they do not create a duplicate.

Names are single segments: letters, numbers, dots, underscores, and hyphens, starting
with a letter or number. Git's branch-name restrictions also apply.

For repositories using a different production branch or remote:

```sh
git epic init --production main --remote origin
git add .git-epic.json
git commit -m "Configure git-epic"
git push
```

Commit this file on production before creating epics so all branches and teammates
inherit it. It contains no credentials. Existing configuration is never overwritten;
edit the file to change settings. An initial production commit must already exist on
the remote; `init` does not bootstrap an empty application repository.

## Typical workflow

```sh
git epic start checkout
git epic task start payment
# Implement and commit your task.
git epic task submit
# Review, CI, and merge the task MR in GitLab.
git switch epic/checkout
# Bring merged tasks into the local epic.
git fetch origin
git merge --ff-only origin/epic/checkout
git epic sync
git push origin epic/checkout
# Deploy OTF and obtain QA approval through the existing team process.
git epic release
# Review, required CI, and merge in GitLab; CI/CD deploys production.
```

OTF deployment and QA approval are external to this CLI. Approval belongs to an
exact commit/build. If the candidate or production changes, update and revalidate
before merging. `release` refuses an epic that is behind current production, but
the hosting platform/CI must also enforce release checks at merge time. The CLI
does not record or verify QA approval.

## Git behavior

- Branch and submission operations require a clean working tree and no unfinished
  merge/rebase/cherry-pick/revert. `status` allows uncommitted changes.
- Fetch updates remote-tracking refs. No command force-pushes, resets, deletes a
  branch, merges an MR, or deploys.
- `start` creates a local epic. Submission publishes branches with ordinary pushes;
  non-fast-forward pushes are rejected rather than overwriting teammates' work.
- `task start` stops if local and remote epic histories have diverged. Reconcile
  the epic using Git before retrying. `release` similarly requires your local epic
  to include remote epic work before a push can succeed.
- If `sync` conflicts, resolve and commit, or use `git merge --abort`.
- An MR/API failure after a push leaves the branch published; retry the command.
- GitLab authentication is managed by `glab`, and Git authentication by Git.

## Development and release

```sh
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev]'
ruff check .
ruff format --check .
python -m unittest discover -s tests -v
python -m build
python -m twine check --strict dist/*.whl dist/*.tar.gz
```

Tests create temporary local Git repositories and a fake `glab`; they never create
real MRs or touch production. GitLab API behavior is delegated to the installed
`glab`. CI checks Python 3.11 and 3.14, builds a wheel and source distribution,
validates metadata with Twine, and smoke-tests the installed wheel via Git.

## Publish to PyPI

### Local publication helper

From a clean, committed checkout with a configured Git upstream, run:

```sh
./publish.sh
```

The helper uses `uv` to prompt for a PEP 440 version (press Enter to keep the
current version for the first release), update the version and README release-tag
examples, run tests, clean and build distributions, validate with Twine, upload
to PyPI, and commit/push the release changes. Set `PYPI_API_TOKEN` or
`TWINE_PASSWORD` in your shell; otherwise Twine prompts for authentication.
It does not create a release tag or trigger the CI publication job.

### GitLab CI publication

The distribution name is **`git-epic`**, import package is **`git_epic`**, and CLI
entry point is **`git-epic`**. Version is defined once in
`src/git_epic/__init__.py`.

1. Create a PyPI account and an API token. For the first release, a token with
   account scope is needed; after creating the project, use a project-scoped token.
2. In GitLab CI/CD variables, add **`PYPI_API_TOKEN`** as a masked, protected
   variable. Protect the release tag pattern (`v*`) so tagged jobs can access it.
3. Update the version, then push a matching tag, for example `v0.1.0`.
4. After verification and build jobs pass, manually run **`publish:pypi`**.

The job uploads only the validated wheel and source distribution from the build
job. A release tag must match the package version. PyPI does not allow reusing
an uploaded version: use a new version for subsequent releases.

An optional **`publish:testpypi`** job uses **`TESTPYPI_API_TOKEN`** and the separate
TestPyPI registry. TestPyPI accounts and tokens are independent of PyPI. A test
installation uses `pipx install --index-url https://test.pypi.org/simple/ git-epic`.

For a manual upload of locally built, validated distributions:

```sh
export TWINE_USERNAME=__token__
# Set TWINE_PASSWORD to your PyPI API token in your shell.
python -m twine upload --non-interactive dist/*.whl dist/*.tar.gz
```

Credentials are supplied by the shell or CI, never stored in project files. PyPI
Trusted Publishing currently supports GitLab.com, not this self-managed GitLab
host, so the release jobs use API tokens. Package-name availability is determined
by PyPI at upload time. A public PyPI release includes the source code even when
the GitLab repository is private.
