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

chore: Automate releases. #14

Merged
merged 7 commits into from
Oct 6, 2020
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
90 changes: 90 additions & 0 deletions .github/release_notes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import collections
import subprocess
import sys
import typing


class Changelog:
def __init__(self, git_log: str):
self._commits = {}
for commit in git_log.split('\n'):
# Spilt the type from the message.
type_ = 'other'
message = commit
if ':' in message:
type_, message = commit.split(':', 1)

# If the change is breaking, note it separately.
if type_.endswith('!'):
type_ = 'breaking'

# Add the commit to the appropriate bucket.
self._commits.setdefault(type_, [])
self._commits[type_].append(message.strip())

@property
def markdown(self):
"""Return the changelog Markdown for the GitHub release."""
answer = ''
headers = collections.OrderedDict((
('breaking', 'Breaking Changes'),
('feat', 'Features'),
('fix', 'Bugfixes'),
('refactor', 'Refactors'),
('docs', 'Documentation'),
))
for key, header in headers.items():
if key in self._commits:
answer += f'## {header}\n\n'
for cmt in self._commits[key]:
answer += f'- {cmt}\n'
answer += '\n'
return answer.strip()


def exec(cmd: typing.List[str]) -> str:
"""Execute the given command and return the output.

If the command returns a non-zero exit status, fail loudly and exit.
"""
proc = subprocess.run(cmd, capture_output=True, text=True)
if proc.returncode != 0:
print(f'Error running {cmd[0]}: {proc.stderr}', file=sys.stderr)
sys.exit(proc.returncode)
return proc.stdout.strip()


if __name__ == '__main__':
# Get the previous tag and the current tag.
revs = exec(['git', 'rev-list', '--simplify-by-decoration',
lukesneeringer marked this conversation as resolved.
Show resolved Hide resolved
'--tags', '--max-count=2']).split('\n')
new_tag, prev_tag = (exec(['git', 'describe', '--tags', r]) for r in revs)
commit_range = f'{prev_tag}..{new_tag}'
if len(sys.argv) > 1:
commit_range = sys.argv[1]

# Get the changelog between those two tags.
cl = Changelog(exec(['git', 'log', commit_range,
'--oneline', '--pretty=format:%s']))

# Print the Markdown using GitHub's special syntax.
#
# Note: %0A must be used for newline.
# https://github.community/t/set-output-truncates-multiline-strings/16852
print('::set-output name=release_notes::{rel_notes}'.format(
rel_notes=cl.markdown.replace('\n', '%0A')
))
60 changes: 60 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
name: release
on:
push:
tags: v[0-9]+.[0-9]+.[0-9]+
jobs:
inspect:
runs-on: ubuntu-latest
container: python:3.8
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get the version from the tag.
id: get_version
run: echo ::set-output name=version::${GITHUB_REF:11}
shell: bash
- name: Get the release notes from the previous release to this one.
id: release_tool
run: python ./.github/release_notes.py
outputs:
version: ${{ steps.get_version.outputs.version }}
release_notes: ${{ steps.release_tool.outputs.release_notes }}
github_release:
runs-on: ubuntu-latest
needs: inspect
steps:
- name: Create the GitHub release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
tag_name: v${{ needs.inspect.outputs.version }}
release_name: aip-site-generator ${{ needs.inspect.outputs.version }}
body: ${{ needs.inspect.outputs.release_notes }}
draft: false
prerelease: false
pypi_release:
runs-on: ubuntu-latest
container: python:3.8
needs:
- inspect
- github_release
steps:
- uses: actions/checkout@v2
- name: Install twine.
run: pip install twine
- name: Set the version number.
run: |
cat > VERSION <<EOF
${{ needs.inspect.outputs.version }}
EOF
- name: Create a source distribution.
run: python setup.py sdist
- name: Upload to PyPI.
run: twine upload dist/*
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
TWINE_NON_INTERACTIVE: 1
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
include README.md CONTRIBUTING.md LICENSE
include README.md CONTRIBUTING.md LICENSE VERSION
recursive-include aip_site/support *
global-exclude *.py[co]
global-exclude __pycache__
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# aip.dev static site generator

Welcome to our NIH static site generator.
This is the site generator for [aip.dev](https://aip.dev) and its forks. It
takes AIP files in a git repository and outputs a static website.

## Why?

We are not fans of rolling our own tools when off-the-shelf alternatives exist.
However, the AIP project has grown sufficiently mature to warrant it.

GitHub Pages normally automatically builds documentation with [Jekyll][], but
as the AIP system has grown, we are beginning to reach the limits of what
Expand All @@ -26,8 +28,10 @@ There are some additional advantages that we unlock with a custom generator:
rather than modifying existing files.
- We can provide useful abstractions for common deviations between companies
(e.g. case systems) that minimize the need to fork AIPs.
- We can customize the Markdown parsing where necessary (tabs, hotlinking,
etc.).

## Grumble. Fine. How does it work?
## How does it work?

This is essentially split into three parts:

Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dev
11 changes: 10 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,31 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import io
import os

from setuptools import find_packages, setup # type: ignore


PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))

with io.open(os.path.join(PACKAGE_ROOT, 'VERSION'), 'r') as version_file:
VERSION = version_file.read().strip()

with io.open(os.path.join(PACKAGE_ROOT, 'README.md'), 'r') as readme_file:
long_description = readme_file.read().strip()

setup(
name='aip-site-generator',
version='0.3.0',
version=VERSION,
license='Apache 2.0',
author='Luke Sneeringer',
author_email='[email protected]',
url='https://github.com/aip-dev/site-generator.git',
packages=find_packages(exclude=['tests']),
description='Static site generator for aip.dev and forks.',
long_description=long_description,
long_description_content_type='text/markdown',
entry_points="""[console_scripts]
aip-site-gen=aip_site.cli:publish
aip-site-serve=aip_site.cli:serve
Expand Down