Slide 1

Slide 1 text

The cobbler's children have no shoes, or building better tools for ourselves Alex Gaynor - PyCon 2016

Slide 2

Slide 2 text

About me • Director of the Python Software Foundation • Open source contributor • Django, PyPy, CPython, pyca/cryptography, etc. • Washington, D.C. resident • Bagel and deli enthusiast • US Digital Service employee

Slide 3

Slide 3 text

A short history of tools

Slide 4

Slide 4 text

$ git init

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Code review

Slide 7

Slide 7 text

Deployment automation

Slide 8

Slide 8 text

Emerging trends

Slide 9

Slide 9 text

CI for PRs

Slide 10

Slide 10 text

Linting (flake8, bandit, flake8-import-order, etc.)

Slide 11

Slide 11 text

Coverage Tracking

Slide 12

Slide 12 text

livegrep.com

Slide 13

Slide 13 text

github.com/facebook/mention-bot

Slide 14

Slide 14 text

Workflow

Slide 15

Slide 15 text

Build more tailored tools

Slide 16

Slide 16 text

Automation > Process

Slide 17

Slide 17 text

APIs!

Slide 18

Slide 18 text

Issues • Create an issue • Add/remove labels • Add a comment • Assign to someone

Slide 19

Slide 19 text

Pull requests • Send a PR • Assign a PR • Add/remove labels • Leave a code review • Add a commit status

Slide 20

Slide 20 text

Examples

Slide 21

Slide 21 text

$ pip install github3.py

Slide 22

Slide 22 text

import github3 gh_client = github3.login( os.environ["GITHUB_USERNAME"], os.environ["GITHUB_PASSWORD"], )

Slide 23

Slide 23 text

repo = gh_client.repository("django", "django")

Slide 24

Slide 24 text

HTTPS certificate expiration

Slide 25

Slide 25 text

def get_expiration_date(host): ssl_context = ssl.create_default_context() with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: sock = ssl_context.wrap_socket(sock, server_hostname=host) sock.connect((host, 443)) expiration = ssl.cert_time_to_seconds(sock.getpeercert()["notAfter"]) return datetime.datetime.fromtimestamp(expiration)

Slide 26

Slide 26 text

while True: for host in MY_DOMAINS: expiration = get_expiration_date(host) if expiration - datetime.datetime.now() < CUTOFF: file_an_issue(gh_client, host, expiration) time.sleep(3600)

Slide 27

Slide 27 text

def file_an_issue(gh_client, host, expiration): gh_client.create_issue( "django", "django", "Cert expiring soon: {}".format(host), "The cert for `{}` expires on {}, get a new one!".format( host, expiration ), "alex", labels=["ssl-cert"] )

Slide 28

Slide 28 text

Auto-labeling

Slide 29

Slide 29 text

Web hooks

Slide 30

Slide 30 text

def github_webhook(request): event = request.headers.get("X-Github-Event") if event != "pull_request": return Response(status=200) body = json.load(request.stream) if body.get("action") not in {"opened", "reopened", "synchronize"}: return Response(status=200)

Slide 31

Slide 31 text

issue = repo.issue(body["number"]) pr = repo.pull_request(issue.number) changed_files = (f.filename for f in pr.iter_files()) if "django/utils/crypto.py" in changed_files: issue.add_labels("security")

Slide 32

Slide 32 text

Other ideas

Slide 33

Slide 33 text

requirements.txt bumper

Slide 34

Slide 34 text

UI change reviewer

Slide 35

Slide 35 text

Github Web hook UI Reviewer Comment Screenshots

Slide 36

Slide 36 text

Approval process commit status

Slide 37

Slide 37 text

Thanks! Questions? https://github.com/alex https://speakerdeck.com/alex