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

Added automatic addition of a remote when creating a git repo. #33

Merged
merged 4 commits into from
Nov 17, 2015
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
158 changes: 158 additions & 0 deletions conda_smithy/ci_register.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/env python
from __future__ import print_function
import os
import requests


# https://circleci.com/docs/api#add-environment-variable

# curl -X POST --header "Content-Type: application/json" -d '{"name":"foo", "value":"bar"}'
# https://circleci.com/api/v1/project/:username/:project/envvar?circle-token=:token

try:
# Create a token at https://circleci.com/account/api. Put it in circle.token
with open(os.path.expanduser('~/.conda-smithy/circle.token'), 'r') as fh:
circle_token = fh.read().strip()
except IOError:
print('No circle token. Put one in ~/.conda-smithy/circle.token')

try:
with open(os.path.expanduser('~/.conda-smithy/appveyor.token'), 'r') as fh:
appveyor_token = fh.read().strip()
except IOError:
print('No appveyor token. Put one in ~/.conda-smithy/appveyor.token')



def add_token_to_circle(user, project):
url_template = ('https://circleci.com/api/v1/project/{user}/{project}/envvar?'
'circle-token={token}')
url = url_template.format(token=circle_token, user=user, project=project)
data = {'name': 'BINSTAR_TOKEN', 'value': os.environ['BINSTAR_TOKEN']}
response = requests.post(url, data)
if response.status_code != 201:
raise ValueError(response)


def add_project_to_circle(user, project):
headers = {'Content-Type': 'application/json',
'Accept': 'application/json'}
url_template = ('https://circleci.com/api/v1/{component}?'
'circle-token={token}')
url = url_template.format(component='projects', token=circle_token)
data = {'username': user, 'repo': project}
response = requests.get(url, headers=headers)

if response.status_code != 201:
response.raise_for_status()

repos = response.json()
matching = [repo for repo in repos if repo['username'] == data['username'] and repo['reponame'] == data['repo']]

if matching and matching[0].get('followed', False):
print(' * {}/{} already enabled on CircleCI'.format(user, project))
else:
url = url_template.format(component='project/{}/{}/follow'.format(user, project).lower(), token=circle_token)
response = requests.post(url, headers={})
# It is a strange response code, but is doing what was asked...
if response.status_code != 400:
response.raise_for_status()
print(' * Added to circle-ci')


def add_project_to_appveyor(user, project):
headers = {'Authorization': 'Bearer {}'.format(appveyor_token),
}
url = 'https://ci.appveyor.com/api/projects'

response = requests.get(url, headers=headers)
if response.status_code != 201:
response.raise_for_status()
repos = [repo['repositoryName'].lower() for repo in response.json()]

if '{}/{}'.format(user, project).lower() in repos:
print(' * {}/{} already enabled on appveyor'.format(user, project))
else:
data = {'repositoryProvider': 'gitHub', 'repositoryName': '{}/{}'.format(user, project)}
response = requests.post(url, headers=headers, data=data)
if response.status_code != 201:
response.raise_for_status()
print(' * {}/{} has been enabled on appveyor'.format(user, project))


def add_project_to_travis(user, project):
headers = {
'User-Agent': 'conda-smithy',
'Accept': 'application/vnd.travis-ci.2+json',
}
endpoint = 'https://api.travis-ci.org'
url = '{}/auth/github'.format(endpoint)
with open(os.path.expanduser('~/.conda-smithy/github.token'), 'r') as fh:
github_token = fh.read().strip()
data = {"github_token": github_token}
response = requests.post(url, data=data, headers=headers)
if response.status_code != 201:
response.raise_for_status()

token = response.json()['access_token']
headers['Authorization'] = 'token {}'.format(token)

url = '{}/hooks'.format(endpoint)
response = requests.get(url, headers=headers)
content = response.json()
found = [hooked for hooked in content['hooks']
if hooked['owner_name'] == user and hooked['name'] == project]

if not found:
print(" * Travis doesn't know about the repo, synching (takes a few seconds). Re-run to get travisi-ci enabled.")
url = '{}/users/sync'.format(endpoint)
response = requests.post(url, headers=headers)
elif found[0]['active'] is True:
print(' * {}/{} already enabled on travis-ci'.format(user, project))
else:
repo_id = found[0]['id']
url = '{}/hooks'.format(endpoint)
response = requests.put(url, headers=headers, json={'hook': {'id': repo_id, 'active': True}})
if response.json().get('result'):
print(' * Registered on travis-ci')
else:
raise RuntimeError('Unable to register on travis-ci, response from hooks was negative')
url = '{}/users/sync'.format(endpoint)
response = requests.post(url, headers=headers)


def travis_token_update_conda_forge_config(feedstock_directory, user, project):
import vendored
import vendored.travis_encrypt as travis
import ruamel.yaml
item = 'BINSTAR_TOKEN="{}"'.format(os.environ['BINSTAR_TOKEN'])
slug = "{}/{}".format(user, project)

forge_yaml = os.path.join(feedstock_directory, 'conda-forge.yml')
if os.path.exists(forge_yaml):
with open(forge_yaml, 'r') as fh:
code = ruamel.yaml.load(fh, ruamel.yaml.RoundTripLoader)
else:
code = {}

# Code could come in as an empty list.
if not code:
code = {}

code.setdefault('travis', {}).setdefault('secure', {})['BINSTAR_TOKEN'] = travis.encrypt(slug, item)
with open(forge_yaml, 'w') as fh:
fh.write(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper))


if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("user")
parser.add_argument("project")
args = parser.parse_args(['pelson', 'udunits-delme-feedstock'])

# add_project_to_circle(args.user, args.project)
# add_project_to_appveyor(args.user, args.project)
add_project_to_travis(args.user, args.project)
# travis_token_update_conda_forge_config('../udunits-delme-feedstock', args.user, args.project)
print('Done')
53 changes: 45 additions & 8 deletions conda_smithy/conda_smithy.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from conda_build.metadata import MetaData

import conda_smithy.configure_circle_ci as configure_circle_ci
import conda_smithy.ci_register as ci_register
import conda_smithy.configure_feedstock as configure_feedstock


Expand Down Expand Up @@ -78,6 +78,9 @@ def __init__(self, parser):
group = subcommand_parser.add_mutually_exclusive_group()
group.add_argument("--user")
group.add_argument("--organization", default="conda-forge")
subcommand_parser.add_argument("--remote-name", default="upstream",
help="The name of the remote to add to the local repo (default: upstream). "
"An empty string will disable adding of a remote.")

def __call__(self, args):
try:
Expand All @@ -88,6 +91,8 @@ def __call__(self, args):
meta = configure_feedstock.meta_of_feedstock(args.feedstock_directory)

from github import Github
from github.GithubException import GithubException
from git import Repo
gh = Github(token)
if args.user is not None:
pass
Expand All @@ -96,10 +101,29 @@ def __call__(self, args):
else:
# Use the organization provided.
user_or_org = gh.get_organization(args.organization)
repo = user_or_org.create_repo(os.path.basename(os.path.abspath(args.feedstock_directory)),
has_wiki=False,
description='A conda-smithy repository for {}.'.format(meta.name()))
print('Created {} on github'.format(repo.full_name))

repo_name = os.path.basename(os.path.abspath(args.feedstock_directory))
try:
gh_repo = user_or_org.create_repo(repo_name, has_wiki=False,
description='A conda-smithy repository for {}.'.format(meta.name()))
print('Created {} on github'.format(gh_repo.full_name))
except GithubException as gh_except:
if gh_except.data.get('errors', [{}])[0].get('message', '') != u'name already exists on this account':
raise
gh_repo = user_or_org.get_repo(repo_name)
print('Github repository already exists.')

# Now add this new repo as a remote on the local clone.
repo = Repo(args.feedstock_directory)
remote_name = args.remote_name.strip()
if remote_name:
if remote_name in [remote.name for remote in repo.remotes]:
existing_remote = repo.remotes[remote_name]
if existing_remote.url != gh_repo.ssh_url:
print("Remote {} already exists, and doesn't point to {} "
"(it points to {}).".format(remote_name, gh_repo.ssh_url, existing_remote.url))
else:
repo.create_remote(remote_name, gh_repo.ssh_url)


class RegisterFeedstockCI(Subcommand):
Expand Down Expand Up @@ -129,9 +153,11 @@ def __call__(self, args):
repo = os.path.basename(os.path.abspath(args.feedstock_directory))

print('CI Summary for {}/{} (may take some time):'.format(owner, repo))
configure_circle_ci.add_project_to_circle(owner, repo)
configure_circle_ci.add_project_to_appveyor(owner, repo)
configure_circle_ci.add_project_to_travis(owner, repo)
ci_register.add_project_to_travis(owner, repo)
ci_register.travis_token_update_conda_forge_config(args.feedstock_directory, owner, repo)
ci_regiser.add_project_to_circle(owner, repo)
ci_register.add_token_to_circle(owner, repo)
ci_register.add_project_to_appveyor(owner, repo)


def main():
Expand Down Expand Up @@ -165,5 +191,16 @@ def main():
args.subcommand_func(args)


class Rerender(Subcommand):
subcommand = 'rerender'
def __init__(self, parser):
# conda-smithy render /path/to/udunits-recipe
subcommand_parser = Subcommand.__init__(self, parser)
subcommand_parser.add_argument("--feedstock_directory", default=os.getcwd())

def __call__(self, args):
configure_feedstock.main(args.feedstock_directory)


if __name__ == '__main__':
main()
124 changes: 0 additions & 124 deletions conda_smithy/configure_circle_ci.py

This file was deleted.

Empty file.
Loading