Browse Source

Merge remote-tracking branch 'origin/main' into escaped-semi

pull/1253/head
Ross McFarland 7 months ago
parent
commit
60cb637c92
No known key found for this signature in database GPG Key ID: 943B179E15D3B22A
10 changed files with 354 additions and 13 deletions
  1. +0
    -0
      .changelog/.create_dir
  2. +4
    -0
      .changelog/acc2596fb367494db070e6c06abf705a.md
  3. +1
    -0
      .git_hooks_pre-commit
  4. +24
    -0
      .github/workflows/changelog.yml
  5. +5
    -0
      CHANGELOG.md
  6. +4
    -1
      CONTRIBUTING.md
  7. +1
    -1
      README.md
  8. +6
    -3
      octodns/processor/acme.py
  9. +306
    -5
      script/changelog
  10. +3
    -3
      tests/test_octodns_processor_acme.py

+ 0
- 0
.changelog/.create_dir View File


+ 4
- 0
.changelog/acc2596fb367494db070e6c06abf705a.md View File

@ -0,0 +1,4 @@
---
type: none
---
Adding changelog management infra and doc

+ 1
- 0
.git_hooks_pre-commit View File

@ -10,3 +10,4 @@ ROOT=$(dirname "$GIT")
"$ROOT/script/lint"
"$ROOT/script/format" --check --quiet || (echo "Formatting check failed, run ./script/format" && exit 1)
"$ROOT/script/coverage"
"$ROOT/script/changelog" check

+ 24
- 0
.github/workflows/changelog.yml View File

@ -0,0 +1,24 @@
name: OctoDNS Changelog
on:
pull_request:
workflow_dispatch:
jobs:
changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Fetch main
run: git fetch origin main --depth 1
- name: Setup python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Changelog Check
run: |
./script/changelog check

+ 5
- 0
CHANGELOG.md View File

@ -1,3 +1,8 @@
## v1.11.? - 2025-??-?? - ???
* Correct type-o in name of AcmeManagingProcessor, backwards compatible alias
in place
## v1.11.0 - 2025-02-03 - Cleanup & deprecations with meta planning
* Deprecation warning for Source.populate w/o the lenient param, to be removed


+ 4
- 1
CONTRIBUTING.md View File

@ -17,8 +17,11 @@ This project uses the [GitHub Flow](https://guides.github.com/introduction/flow/
0. Create a new branch: `git checkout -b my-branch-name`
0. Make your change, add tests, and make sure the tests still pass
0. Make sure that `./script/lint` passes without any warnings
0. Run `./script/format` to make sure your changes follow Python's preferred
coding style
0. Run `./script/changelog create ...` to add a changelog entry to your PR
0. Make sure that coverage is at :100:% `./script/coverage` and open `htmlcov/index.html`
* You can open PRs for :eyes: & discussion prior to this
* You can open a draft PR for :eyes: & discussion prior to this
0. Push to your fork and submit a pull request
We will handle updating the version, tagging the release, and releasing the gem. Please don't bump the version or otherwise attempt to take on these administrative internal tasks as part of your pull request.


+ 1
- 1
README.md View File

@ -330,7 +330,7 @@ Similar to providers, but can only serve to populate records into a zone, cannot
| Processor | Description |
|--|--|
| [AcmeMangingProcessor](/octodns/processor/acme.py) | Useful when processes external to octoDNS are managing acme challenge DNS records, e.g. LetsEncrypt |
| [AcmeManagingProcessor](/octodns/processor/acme.py) | Useful when processes external to octoDNS are managing acme challenge DNS records, e.g. LetsEncrypt |
| [AutoArpa](/octodns/processor/arpa.py) | See [Automatic PTR generation](#automatic-ptr-generation) below |
| [EnsureTrailingDots](/octodns/processor/trailing_dots.py) | Processor that ensures ALIAS, CNAME, DNAME, MX, NS, PTR, and SRVs have trailing dots |
| [ExcludeRootNsChanges](/octodns/processor/filter.py) | Filter that errors or warns on planned root/APEX NS records changes. |


+ 6
- 3
octodns/processor/acme.py View File

@ -7,14 +7,14 @@ from logging import getLogger
from .base import BaseProcessor
class AcmeMangingProcessor(BaseProcessor):
log = getLogger('AcmeMangingProcessor')
class AcmeManagingProcessor(BaseProcessor):
log = getLogger('AcmeManagingProcessor')
def __init__(self, name):
'''
processors:
acme:
class: octodns.processor.acme.AcmeMangingProcessor
class: octodns.processor.acme.AcmeManagingProcessor
...
@ -59,3 +59,6 @@ class AcmeMangingProcessor(BaseProcessor):
existing.remove_record(record)
return existing
AcmeMangingProcessor = AcmeManagingProcessor

+ 306
- 5
script/changelog View File

@ -1,7 +1,308 @@
#!/bin/bash
#!/usr/bin/env python
set -e
from argparse import ArgumentParser, RawTextHelpFormatter
from datetime import datetime
from importlib import import_module
from io import StringIO
from json import loads
from os import getcwd, listdir, makedirs, remove
from os.path import basename, isdir, join
from subprocess import PIPE, run
from sys import argv, exit, path, stderr
from uuid import uuid4
VERSION=v$(grep __version__ octodns/__init__.py | sed -e "s/^[^']*'//" -e "s/'$//")
echo $VERSION
git log --pretty="%h - %cr - %s (%an)" "${VERSION}..HEAD"
from yaml import safe_load_all
def create(argv):
prog = basename(argv.pop(0))
parser = ArgumentParser(
prog=f'{prog} create',
description='Creates a new changelog entry.',
add_help=True,
formatter_class=RawTextHelpFormatter,
)
parser.add_argument(
'-t',
'--type',
choices=('none', 'patch', 'minor', 'major'),
required=True,
help='''The scope of the change.
* patch - This is a bug fix
* minor - This adds new functionality or makes changes in a fully backwards
compatible way
* major - This includes substantial new functionality and/or changes that break
compatibility and may require careful migration
* none - This change does not need to be mentioned in the changelog
See https://semver.org/ for more info''',
)
parser.add_argument(
'md',
metavar='change-description-markdown',
nargs='+',
help='''A short description of the changes in this PR, suitable as an entry in
CHANGELOG.md. Should be a single line. Can include simple markdown formatting
and links.''',
)
args = parser.parse_args(argv)
if not isdir('.changelog'):
makedirs('.changelog')
filepath = join('.changelog', f'{uuid4().hex}.md')
with open(filepath, 'w') as fh:
fh.write('---\ntype: ')
fh.write(args.type)
fh.write('\n---\n')
fh.write(' '.join(args.md))
print(
f'Created {filepath}, it can be further edited and should be committed to your branch.'
)
def check(argv):
if isdir('.changelog'):
result = run(
['git', 'diff', '--name-only', 'origin/main', '.changelog/'],
check=False,
stdout=PIPE,
)
entries = {
l
for l in result.stdout.decode('utf-8').split()
if l.endswith('.md')
}
if not result.returncode and entries:
exit(0)
print(
'PR is missing required changelog file, run ./script/changelog create',
file=stderr,
)
exit(1)
def _get_current_version(module_name):
cwd = getcwd()
path.append(cwd)
module = import_module(module_name)
return tuple(int(v) for v in module.__version__.split('.', 2))
class _ChangeMeta:
_pr_cache = None
@classmethod
def get(cls, filepath):
if cls._pr_cache is None:
result = run(
[
'gh',
'pr',
'list',
'--base',
'main',
'--state',
'merged',
'--limit=50',
'--json',
'files,mergedAt,number',
],
check=True,
stdout=PIPE,
)
cls._pr_cache = {}
for pr in loads(result.stdout):
for file in pr['files']:
path = file['path']
if path.startswith('.changelog'):
cls._pr_cache[path] = (
pr['number'],
datetime.fromisoformat(pr['mergedAt']).replace(
tzinfo=None
),
)
try:
return cls._pr_cache[filepath]
except KeyError:
return None, datetime(year=1970, month=1, day=1)
def _get_changelogs():
ret = []
dirname = '.changelog'
for filename in listdir(dirname):
if not filename.endswith('.md'):
continue
filepath = join(dirname, filename)
with open(filepath) as fh:
data, md = safe_load_all(fh)
pr, time = _ChangeMeta.get(filepath)
if not pr:
continue
ret.append(
{
'filepath': filepath,
'md': md,
'pr': pr,
'time': time,
'type': data.get('type', '').lower(),
}
)
ordering = {'major': 0, 'minor': 1, 'patch': 2, 'none': 3, '': 3}
ret.sort(key=lambda c: (ordering[c['type']], c['time']))
return ret
def _get_new_version(current_version, changelogs):
try:
bump_type = changelogs[0]['type']
except IndexError:
return None
new_version = list(current_version)
if bump_type == 'major':
new_version[0] += 1
new_version[1] = 0
new_version[2] = 0
elif bump_type == 'minor':
new_version[1] += 1
new_version[2] = 0
else:
new_version[2] += 1
return tuple(new_version)
def _format_version(version):
return '.'.join(str(v) for v in version)
def bump(argv):
prog = basename(argv.pop(0))
parser = ArgumentParser(
prog=f'{prog} bump',
description='Builds a changelog update and calculates a new version number.',
add_help=True,
)
parser.add_argument(
'--make-changes',
action='store_true',
help='Write changelog update and bump version number',
)
args = parser.parse_args(argv)
buf = StringIO()
cwd = getcwd()
module_name = basename(cwd).replace('-', '_')
buf.write('## ')
current_version = _get_current_version(module_name)
changelogs = _get_changelogs()
new_version = _get_new_version(current_version, changelogs)
if not new_version:
print('No changelog entries found that would bump, nothing to do')
exit(1)
new_version = _format_version(new_version)
buf.write(new_version)
buf.write(' - ')
buf.write(datetime.now().strftime('%Y-%m-%d'))
buf.write(' - ')
buf.write(' '.join(argv[1:]))
buf.write('\n')
current_type = None
for changelog in changelogs:
md = changelog['md']
if not md:
continue
_type = changelog['type']
if _type != current_type:
buf.write('\n')
buf.write(_type.capitalize())
buf.write(':\n')
current_type = _type
buf.write('* ')
buf.write(md)
pr = changelog['pr']
if pr:
pr = str(pr)
buf.write(' [#')
buf.write(pr)
buf.write('](https://github.com/octodns/')
buf.write(module_name)
buf.write('/pull/')
buf.write(pr)
buf.write(')')
buf.write('\n')
buf.write('\n')
if not args.make_changes:
print(f'New version number {new_version}\n')
print(buf.getvalue())
exit(0)
with open('CHANGELOG.md') as fh:
existing = fh.read()
with open('CHANGELOG.md', 'w') as fh:
fh.write(buf.getvalue())
fh.write(existing)
with open(f'{module_name}/__init__.py') as fh:
existing = fh.read()
current_version = _format_version(current_version)
with open(f'{module_name}/__init__.py', 'w') as fh:
fh.write(existing.replace(current_version, new_version))
for changelog in changelogs:
remove(changelog['filepath'])
cmds = {'create': create, 'check': check, 'bump': bump}
def general_usage(msg=None):
global cmds
exe = basename(argv[0])
cmds = ','.join(sorted(cmds.keys()))
print(f'usage: {exe} {{{cmds}}} ...')
if msg:
print(msg)
else:
print(
'''
Creates and checks or changelog entries, located in the .changelog directory.
Additionally supports updating CHANGELOG.md and bumping the package version
based on one or more entries in that directory.
'''
)
try:
cmd = cmds[argv[1].lower()]
argv.pop(1)
except IndexError:
general_usage('missing command')
exit(1)
except KeyError:
if argv[1] in ('-h', '--help', 'help'):
general_usage()
exit(0)
general_usage(f'unknown command "{argv[1]}"')
exit(1)
cmd(argv)

+ 3
- 3
tests/test_octodns_processor_acme.py View File

@ -4,7 +4,7 @@
from unittest import TestCase
from octodns.processor.acme import AcmeMangingProcessor
from octodns.processor.acme import AcmeManagingProcessor
from octodns.record import Record
from octodns.zone import Zone
@ -46,9 +46,9 @@ records = {
}
class TestAcmeMangingProcessor(TestCase):
class TestAcmeManagingProcessor(TestCase):
def test_process_zones(self):
acme = AcmeMangingProcessor('acme')
acme = AcmeManagingProcessor('acme')
source = Zone(zone.name, [])
# Unrelated stuff that should be untouched


Loading…
Cancel
Save