As per the definition of this tag "The “OpenStack TC Approved Release” is used as the superset of projects used by the OpenStack Foundation when creating commercial trademark programs." (read full definition in [1]) this tag was used to know if project under OpenStack is mature so that it can be considered as possible candidate for trademark programs. This situation was in early stage of OpenStack hen we used to have incubated vs integrated project status but now everything in project.yaml file is consider as mature, following release, active etc. Bylaw requirements are (section 4.1, 4.13[2]) "The Technical Committee shall designate a subset of the OpenStack Project an “OpenStack Technical Committee Approved Release” from time to time. The Board of Directors may determine "Trademark Designated OpenStack Software" from time to time, which will be a subset of the "OpenStack Technical Committee Approved Release" as provided in Section 4.1(b)(ii) and (iii)" (read full text in bylaw link[2]) We do not need any changes in bylaw as all projects listed in projects.yaml file are applicable as "OpenStack Technical Committee Approved Release" and if any of the current trademarked software is going to be deleted from the projects.yaml then it need to be done with Coordination Procedures listed in bylaw. This commit proposes to remove this tag and add section in place of this tag defintion to consider all the projects listed in projects.yaml as "OpenStack Technical Committee Approved Release". [1] https://governance.openstack.org/tc/reference/tags/tc_approved-release.html#rationale [2] https://www.openstack.org/legal/bylaws-of-the-openstack-foundation Change-Id: I0955c20a74eea8cac5f920bba60be1a334d50754
221 lines
8.5 KiB
Python
221 lines
8.5 KiB
Python
# 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
|
|
#
|
|
# http://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.
|
|
|
|
"""
|
|
Generate badges for the projects
|
|
"""
|
|
|
|
import os
|
|
|
|
from itertools import chain
|
|
from itertools import zip_longest
|
|
from PIL import ImageFont
|
|
from sphinx.util import logging
|
|
|
|
import projects
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
NUM_COL = 4
|
|
PADDING = 8
|
|
BADGE_SPACING = 4
|
|
BASE_TAGS_URL = 'https://governance.openstack.org/tc/reference/tags/'
|
|
COLOR_SCHEME = {
|
|
"brightgreen": "#4c1",
|
|
"green": "#97CA00",
|
|
"yellow": "#dfb317",
|
|
"yellowgreen": "#a4a61d",
|
|
"orange": "#fe7d37",
|
|
"red": "#e05d44",
|
|
"blue": "#007ec6",
|
|
"grey": "#555",
|
|
"lightgrey": "#9f9f9f",
|
|
}
|
|
|
|
OPENSTACK_SVG = """<svg id="Layer_1" x="0" y="0" height="20" width="20" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 209.67 180.35"><defs><style>.cls-1{opacity:0.98;}.cls-2{fill:#ed1944;}</style></defs><title>OpenStack_Logo_Mark</title><g class="cls-1"><path class="cls-2" d="M461.82,215.24h-150a17.17,17.17,0,0,0-17.12,17.12v40.35h41.61v-6.59a9.26,9.26,0,0,1,9.26-9.26h82.53a9.26,9.26,0,0,1,9.26,9.26v6.59H479V232.36A17.18,17.18,0,0,0,461.82,215.24Z" transform="translate(-294.67 -215.24)"/><path class="cls-2" d="M437.33,344.72a9.27,9.27,0,0,1-9.26,9.26H345.54a9.27,9.27,0,0,1-9.26-9.26v-6.59H294.67v40.34a17.17,17.17,0,0,0,17.12,17.13h150A17.18,17.18,0,0,0,479,378.47V338.13H437.33Z" transform="translate(-294.67 -215.24)"/><rect class="cls-2" y="69.37" width="41.62" height="41.62"/><rect class="cls-2" x="142.66" y="69.37" width="41.62" height="41.62"/></g><path class="cls-2" d="M504.33,386.39a9.2,9.2,0,1,0-9.2,9.21A9.21,9.21,0,0,0,504.33,386.39Zm-9.2,6.94a6.94,6.94,0,1,1,6.94-6.94A6.94,6.94,0,0,1,495.13,393.33Z" transform="translate(-294.67 -215.24)"/><path class="cls-2" d="M498.58,384.72v-.05a2.88,2.88,0,0,0-.76-2.09,3.38,3.38,0,0,0-2.45-.86H492v9h1.86v-3H495l1.66,3h2.14l-1.92-3.35A2.72,2.72,0,0,0,498.58,384.72Zm-1.88.06a1.3,1.3,0,0,1-1.47,1.35h-1.38v-2.72h1.34c1,0,1.51.45,1.51,1.35Z" transform="translate(-294.67 -215.24)"/></svg>"""
|
|
|
|
SVG_ROOT = """<?xml version="1.0" standalone="no"?>
|
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
<svg contentScriptType="text/ecmascript" zoomAndPan="magnify"
|
|
contentStyleType="text/css" height="{height}" width="{width}"
|
|
preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg"
|
|
version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
<title id="os:gov:badges:title">
|
|
This is a container for a set of OpenStack badges indicating the status and
|
|
features of this project and its repository
|
|
</title>
|
|
{svg}
|
|
</svg>
|
|
"""
|
|
FLAT_BADGE_TEMPLATE = """<svg id="{left_text}:{right_text}" width="{width}"
|
|
height="20" x="{svg_x}" y="{svg_y}">
|
|
<title>{left_text}:{right_text}</title>
|
|
<a target="_blank" xlink:href="{link}">
|
|
<linearGradient id="smooth:{left_text}:{right_text}" x2="0" y2="100%">
|
|
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
<stop offset="1" stop-opacity=".1"/>
|
|
</linearGradient>
|
|
|
|
<mask id="round:{left_text}:{right_text}">
|
|
<rect width="{width}" height="20" rx="3" fill="#fff"/>
|
|
</mask>
|
|
|
|
<g mask="url('#round:{left_text}:{right_text}')">
|
|
<rect width="{left_width}" height="20" fill="#555"/>
|
|
<rect x="{left_width}" width="{right_width}" height="20" fill="{color}"/>
|
|
<rect width="{width}" height="20"
|
|
fill="url('#smooth:{left_text}:{right_text}')"/>
|
|
</g>
|
|
|
|
<g fill="#fff" text-anchor="middle"
|
|
font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
|
<text x="{left_x}" y="15" fill="#010101"
|
|
fill-opacity=".3">{left_text}</text>
|
|
<text x="{left_x}" y="14">{left_text}</text>
|
|
<text x="{right_x}" y="15" fill="#010101"
|
|
fill-opacity=".3">{right_text}</text>
|
|
<text x="{right_x}" y="14">{right_text}</text>
|
|
</g>
|
|
</a>
|
|
</svg>
|
|
"""
|
|
|
|
|
|
def _badge(left_text, right_text, link=None, colorscheme='brightgreen'):
|
|
|
|
font = ImageFont.truetype('DejaVuSans.ttf', 11)
|
|
left_width = font.getsize(left_text)[0] + PADDING
|
|
right_width = font.getsize(right_text)[0] + PADDING
|
|
width = left_width + right_width
|
|
|
|
data = {
|
|
'link': link or '',
|
|
'svg_x': 0,
|
|
'svg_y': 0,
|
|
'color': COLOR_SCHEME[colorscheme],
|
|
'width': width,
|
|
'left_width': left_width,
|
|
'left_text': left_text,
|
|
'left_x': left_width / 2,
|
|
'right_width': right_width,
|
|
'right_text': right_text,
|
|
'right_x': left_width + right_width / 2 - 1,
|
|
}
|
|
|
|
return data
|
|
|
|
|
|
def _get_base_badges():
|
|
return [
|
|
_badge('openstack', 'community project',
|
|
'https://governance.openstack.org/tc/reference/projects/'),
|
|
_badge('cii best practices', 'passing',
|
|
'https://bestpractices.coreinfrastructure.org/projects/246')
|
|
]
|
|
|
|
|
|
def _get_tag_badges(tags):
|
|
badges = []
|
|
|
|
for tag in tags:
|
|
# NOTE(flaper87): will submit other patches to make these
|
|
# tags consistent with the rest.
|
|
if tag in ['starter-kit:compute']:
|
|
group, tname = 'tc', tag
|
|
else:
|
|
group, tname = tag.split(':')
|
|
|
|
link = BASE_TAGS_URL + '%s.html' % tag.replace(':', '_')
|
|
badges.append(_badge(group, tname, link, colorscheme='blue'))
|
|
|
|
return sorted(badges, key=lambda b: b['left_text']+b['right_text'])
|
|
|
|
|
|
def _organize_badges(base_badges, tag_badges):
|
|
|
|
# Arrange badges in NUM_COL columns, filling the rest with width=0 badges
|
|
ziped = list(zip_longest(*(iter(base_badges + tag_badges),) * NUM_COL,
|
|
fillvalue={'width': 0}))
|
|
|
|
result = []
|
|
# Calculate x,y for each badge, leaving BADGE_SPACING between them
|
|
for line, group in enumerate(ziped):
|
|
# Start a new line at x=25, after the openstack logo
|
|
result.append([])
|
|
x = 25
|
|
for col, badge in enumerate(group):
|
|
# Skip width=0 badges
|
|
if badge['width'] == 0:
|
|
break
|
|
|
|
# Column width is the width of the largest badge in column
|
|
col_width = max(ziped, key=lambda s: s[col]['width'])[col]['width']
|
|
|
|
badge['height'] = 20
|
|
badge['svg_y'] = (20 + BADGE_SPACING) * line
|
|
badge['svg_x'] = x
|
|
x += col_width + BADGE_SPACING
|
|
result[line].append(badge)
|
|
return result
|
|
|
|
|
|
def _to_svg(badges):
|
|
yield OPENSTACK_SVG
|
|
for badge in badges:
|
|
yield FLAT_BADGE_TEMPLATE.format(**badge)
|
|
|
|
|
|
def _generate_teams_badges(app, exception=None):
|
|
LOG.info('Generating team badges')
|
|
all_teams = projects.get_project_data()
|
|
files = []
|
|
|
|
badges_dir = os.path.join(app.outdir, 'badges')
|
|
if not os.path.exists(badges_dir):
|
|
os.mkdir(badges_dir)
|
|
|
|
filename = os.path.join(badges_dir, 'project-unofficial.svg')
|
|
svg_data = _badge('project', 'unofficial', colorscheme='red')
|
|
svg = FLAT_BADGE_TEMPLATE.format(**svg_data)
|
|
with open(filename, 'w', encoding='utf-8') as f:
|
|
f.write(SVG_ROOT.format(height=20, width=106, svg=svg))
|
|
files.append(filename)
|
|
|
|
for team, info in all_teams.items():
|
|
LOG.info('generating team badge for %s' % team)
|
|
|
|
for name, deliverable in info['deliverables'].items():
|
|
tags = info.get('tags', []) + deliverable.get('tags', [])
|
|
badges = _organize_badges(_get_base_badges(),
|
|
_get_tag_badges(tags))
|
|
svg = '\n'.join(_to_svg(chain(*badges)))
|
|
root_width = max([bdg_row[-1]['width'] + bdg_row[-1]['svg_x']
|
|
for bdg_row in badges])
|
|
root_height = badges[-1][0]['svg_y'] + badges[-1][0]['height']
|
|
|
|
for repo in deliverable.get('repos', []):
|
|
repo_name = repo.split('/')[1]
|
|
filename = os.path.join(badges_dir,
|
|
'%s.svg' % projects.slugify(repo_name))
|
|
with open(filename, 'w') as f:
|
|
f.write(SVG_ROOT.format(height=root_height,
|
|
width=root_width, svg=svg))
|
|
files.append(filename)
|
|
|
|
return files
|
|
|
|
|
|
def setup(app):
|
|
LOG.info('loading badges extension')
|
|
app.connect('build-finished', _generate_teams_badges)
|