spec2bp v2 -- support in-review specs

New version of spec2bp:

- takes the project and blueprint name as arguments, instead of a file
- supports not-yet-approved specs (use --in-review to set them "Blocked")
- sets series goal as well as milestone targets
- if the BP and approved spec names don't match, you can use --specpath
  to specify the spec location
- if --specpath is not provided, but the blueprint URL looks like it
  points to a spec document, spec2bp will infer path from that instead
- if milestone or priority are already set, the --milestone and
  --priority parameters are optional (current values will be kept if
  parameter is omitted)
- spec2bp will recognize projects from the oslo projectgroup and use
  oslo-specs for those

Example usage:

Glance's super-spec.rst was approved and you want to add it to juno-2,
with Medium priority:

./spec2bp.py glance super-spec --milestone=juno-2 --priority=Medium

Nova's my-awesome-spec.rst is still under review, but you would like
to add the my-awesome-spec blueprint to juno-2 (marked Blocked). Since
it's located in a non-standard path, we specify it using --specpath
parameter:

./spec2bp.py nova --specpath=specs/kilo/approved/my-awesome-spec.rst
  --in-review --milestone=juno-2

my-awesome-spec is now approved. You want to flip all the approval
bits, but also change its priority to High. There is no need to pass
--specpath again, spec2bp will infer it from the blueprint URL field:

./spec2bp.py nova my-awesome-spec --priority=High

Change-Id: Idba1f042ffb836ac782f50d91802b2a6f4ff44c1
This commit is contained in:
Thierry Carrez 2014-07-18 17:05:25 +02:00
parent affe11ba77
commit df2041e4ac
2 changed files with 121 additions and 63 deletions

View File

@ -333,24 +333,32 @@ spec2bp.py
----------
This experimental script facilitates setting blueprint fields for approved
specs. You point it to a spec file in a -specs repository, and it will set the
Approver, definition status, spec URL, priority and milestone as you specify.
By default priority is set to 'Low'.
In order for this to work, the spec must have the same name as the blueprint.
specs. It takes the project and blueprint name as arguments. For specs that
are still under review (--in-review) it will set them to "Blocked" (and
definition status to Review). For approved specs it will set definition
status to Approved, and set Spec URL. In both cases it will set the target
milestone, approver name and specified priority (by default, 'Low').
Examples:
./spec2bp.py ../oslo-specs/specs/juno/graduate-oslo-i18n.rst juno-3
./spec2bp.py glance super-spec --milestone=juno-2 --priority=Medium
Set the graduate-oslo-i18n oslo blueprint in Launchpad to Approved, set
yourself as approver, set the spec URL, priority to Low and milestone to
juno-3.
Glance's super-spec.rst was approved and you want to add it to juno-2,
with Medium priority. This will do it all for you.
./spec2bp.py warp-speed.rst kilo-1 --priority=High --test
./spec2bp.py nova --specpath=specs/kilo/approved/my-awesome-spec.rst
--in-review --milestone=juno-2
Nova's my-awesome-spec.rst is still under review, but you would like to
add the my-awesome-spec blueprint to juno-2 (marked Blocked). Since it's
located in a non-standard path, we specify it using --specpath parameter.
./spec2bp.py nova my-awesome-spec --priority=High
my-awesome-spec is now approved. You want to flip all the approval bits,
but also change its priority to High. There is no need to pass --specpath
again, spec2bp will infer it from the blueprint URL field.
On Launchpad test servers, try setting the warp-speed blueprint fields as
above (with priority set to High and milestone set to kilo-1).
stable_freeze.py
----------------

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# Script to set fields in LP blueprint for an approved spec
# Script to set fields in LP blueprint for an approved or in-review spec
#
# Copyright 2014 Thierry Carrez <thierry@openstack.org>
# All Rights Reserved.
@ -18,7 +18,6 @@
# under the License.
import argparse
import os
import re
import requests
from launchpadlib.launchpad import Launchpad
@ -26,69 +25,120 @@ from launchpadlib.launchpad import Launchpad
# Parameters
parser = argparse.ArgumentParser(
description="Set blueprint fields for an approved spec file")
parser.add_argument('specfile', help='spec file (in a -specs git repo)')
parser.add_argument('milestone', help='which milestone to target the BP to')
parser.add_argument("--test", action='store_const', const='staging',
default='production', help='Use LP staging server to test')
parser.add_argument("--priority", default='Low',
parser.add_argument('project', help='project name')
parser.add_argument('blueprint', help='blueprint name')
parser.add_argument('--specpath', help='path to spec in specs repository. '
'Defaults to blueprint URL or specs/series/blueprint.rst')
parser.add_argument('--in-review', action='store_true',
help='spec is still under review, mark as Blocked')
parser.add_argument('--milestone', help='which milestone to target the BP to')
parser.add_argument('--priority',
choices=['Essential', 'High', 'Medium', 'Low'],
help='which priority to set (default is Low)')
parser.add_argument("--lp-project", default='',
help=('which Launchpad project to use '
'(default is based on spec repo name)'))
help='priority to set (default is keep, or set Low)')
parser.add_argument('--test', action='store_const', const='staging',
default='production', help='Use LP staging server to test')
args = parser.parse_args()
# Validate specfile
m = re.match(r".*/(\w+)-specs/(.+)/(.+).rst", os.path.abspath(args.specfile))
if m is None:
parser.error("%s has not a recognized spec pattern" % args.specfile)
projname = m.group(1)
specs_repo = '%s-specs' % projname
if projname == 'oslo':
projname = 'oslo-incubator'
if args.lp_project:
projname = args.lp_project
repoloc = "openstack/%s/plain/%s" % (specs_repo, m.group(2))
bpname = m.group(3)
# Check that spec was approved (merged in specs repository)
# Also use this to validate spec link URL
cgit = "http://git.openstack.org/cgit"
specurl = "%s/%s/%s.rst" % (cgit, repoloc, bpname)
try:
r = requests.get(specurl)
if r.status_code != 200 or not r.text:
parser.error("No such confirmed spec in %s-specs" % projname)
except requests.exceptions.RequestException as exc:
parser.exit(1, "Error trying to confirm spec is valid")
# Log into Launchpad
launchpad = Launchpad.login_with('openstack-releasing', args.test,
version='devel')
# Validate project
project = launchpad.projects[projname]
project = launchpad.projects[args.project]
if not project:
parser.error("%s project does not exist in Launchpad" % projname)
parser.error("%s project does not exist in Launchpad" % args.project)
# Validate milestone
milestone = project.getMilestone(name=args.milestone)
if not milestone:
parser.error("%s milestone does not exist in Launchpad" % args.milestone)
if not milestone.is_active:
parser.error("%s is no longer an active milestone" % args.milestone)
# Determine spec repository name
oslo = [p.name for p in launchpad.project_groups['oslo'].projects]
if args.project in oslo:
specrepo = "oslo-specs"
else:
specrepo = args.project + "-specs"
# Validate spec
bp = project.getSpecification(name=bpname)
bp = project.getSpecification(name=args.blueprint)
if not bp:
parser.exit(1, "Blueprint %s not found for project %s"
% (bpname, projname))
parser.error("Blueprint %s not found for project %s"
% (args.blueprint, args.project))
# If no milestone is set on blueprint, we must have a milestone argument
if not bp.milestone and not args.milestone:
parser.error("Blueprint has no milestone, so you must specify one to set "
"using the --milestone argument")
# Validate milestone if provided
if args.milestone:
milestone = project.getMilestone(name=args.milestone)
if not milestone:
parser.error("%s milestone does not exist in LP" % args.milestone)
if not milestone.is_active:
parser.error("%s is no longer an active milestone" % args.milestone)
else:
milestone = bp.milestone
# Determine spec URL
if args.specpath:
path = args.specpath
else:
if bp.specification_url:
m = re.match(r".*openstack.org.*/\w+-specs/(.+)/(.+).rst",
bp.specification_url)
if m is not None:
# Looks like a valid spec link, let's infer path from that
if m.group(1).startswith("plain/"):
path = "%s/%s.rst" % (m.group(1)[6:], m.group(2))
else:
path = "%s/%s.rst" % (m.group(1), m.group(2))
path = 'specs/%s/%s.rst' % (milestone.series_target.name, args.blueprint)
site = "http://git.openstack.org/cgit/openstack/%s" % specrepo
url = "%s/plain/%s" % (site, path)
# Set blueprints fields
if args.in_review:
# Check if the spec is not already approved
try:
r = requests.get(url)
if r.status_code == 200 and r.text:
parser.error("The spec you want to set --in-review seems to "
"have been approved already!")
except requests.exceptions.RequestException as exc:
pass
bp.definition_status = 'Review'
bp.implementation_status = 'Blocked'
else:
# Check that spec was approved (merged in specs repository)
# Also use this to validate spec link URL
try:
r = requests.get(url)
if r.status_code != 200 or not r.text:
parser.error("""
Can't find the spec corresponding to the blueprint in cgit. There are
multiple possible causes for that.
The spec may not be approved yet. If you just wanted to set fields on
unapproved BP, don't forget the --in-review parameter.
The spec may also point to some non-standard URL (not specs/series/bpname.rst).
In that case, you can use the --specrepo parameter to point to it.""")
except requests.exceptions.RequestException as exc:
parser.exit(1, "Error trying to confirm spec is valid")
# Set approver, definition status, spec URL, priority and milestone
bp.definition_status = 'Approved'
bp.specification_url = url
if bp.implementation_status == 'Blocked':
bp.implementation_status = 'Unknown'
# Set approver, definition status, spec URL, priority and milestone
bp.approver = launchpad.me
bp.definition_status = 'Approved'
bp.specification_url = specurl
bp.priority = args.priority
bp.milestone = milestone
bp.lp_save()
if bp.priority == 'Undefined':
bp.priority = args.priority or 'Low'
if bp.milestone != milestone:
bp.milestone = milestone
bp.lp_save()
bp.proposeGoal(goal=milestone.series_target)
else:
bp.lp_save()