def save_source_file(request, project_id, file_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) source_file = get_object_or_404(SourceFile, pk=file_id, project=project) try: if source_file.was_modified_since(int(request.POST['modified'])): send_keen_event('cloudpebble', 'cloudpebble_save_abort_unsafe', data={ 'data': { 'filename': source_file.file_name, 'kind': 'source' } }, project=project, request=request) raise Exception(_("Could not save: file has been modified since last save.")) source_file.save_file(request.POST['content'], folded_lines=request.POST['folded_lines']) except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_save_file', data={ 'data': { 'filename': source_file.file_name, 'kind': 'source' } }, project=project, request=request) return json_response({"modified": time.mktime(source_file.last_modified.utctimetuple())})
def do_import_github(project_id, github_user, github_project, github_branch, delete_project=False): try: url = "https://github.com/%s/%s/archive/%s.zip" % (github_user, github_project, github_branch) if file_exists(url): u = urllib2.urlopen(url) return do_import_archive(project_id, u.read()) else: raise Exception("The branch '%s' does not exist." % github_branch) except Exception as e: try: project = Project.objects.get(pk=project_id) user = project.owner except: project = None user = None if delete_project and project is not None: try: project.delete() except: pass send_keen_event('cloudpebble', 'cloudpebble_github_import_failed', user=user, data={ 'data': { 'reason': e.message, 'github_user': github_user, 'github_project': github_project, 'github_branch': github_branch } }) raise
def proxy_keen(request, project_id): from utils.keen_helper import send_keen_event project = get_object_or_404(Project, pk=project_id) acceptable_events = { 'app_install_succeeded', 'websocket_connection_failed', 'app_install_failed', 'app_log_view', 'app_logged_crash', 'sdk_screenshot_success', 'sdk_screenshot_failed' } event = request.POST['event'] if event not in acceptable_events: return json_failure("nope.") data = {} if 'data' in request.POST: data['data'] = json.loads(request.POST['data']) if 'device' in request.POST: data['device'] = json.loads(request.POST['device']) if len(data.items()) == 0: data = None send_keen_event(['cloudpebble', 'sdk'], event, project=project, request=request, data=data) return json_response({})
def update_resource(request, project_id, resource_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) resource = get_object_or_404(ResourceFile, pk=resource_id, project=project) resource_ids = json.loads(request.POST['resource_ids']) try: with transaction.commit_on_success(): # Lazy approach: delete all the resource_ids and recreate them. # We could do better. resources = [] ResourceIdentifier.objects.filter(resource_file=resource).delete() for r in resource_ids: regex = r['regex'] if 'regex' in r else None tracking = int(r['tracking']) if 'tracking' in r else None resources.append(ResourceIdentifier.objects.create(resource_file=resource, resource_id=r['id'], character_regex=regex, tracking=tracking)) if 'file' in request.FILES: resource.save_file(request.FILES['file'], request.FILES['file'].size) except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_save_file', data={ 'data': { 'filename': resource.file_name, 'kind': 'source' } }, project=project, request=request) return json_response({"file": { "id": resource.id, "kind": resource.kind, "file_name": resource.file_name, "resource_ids": [{'id': x.resource_id, 'regex': x.character_regex} for x in resources], "identifiers": [x.resource_id for x in resources] }})
def save_source_file(request, project_id, file_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) source_file = get_object_or_404(SourceFile, pk=file_id, project=project) try: expected_modification_time = datetime.datetime.fromtimestamp(int(request.POST['modified'])) if source_file.last_modified.replace(tzinfo=None, microsecond=0) > expected_modification_time: send_keen_event('cloudpebble', 'cloudpebble_save_abort_unsafe', data={ 'data': { 'filename': source_file.file_name, 'kind': 'source' } }, project=project, request=request) raise Exception("Could not save: file has been modified since last save.") source_file.save_file(request.POST['content']) except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_save_file', data={ 'data': { 'filename': source_file.file_name, 'kind': 'source' } }, project=project, request=request) return json_response({"modified": time.mktime(source_file.last_modified.utctimetuple())})
def view_project(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) if project.app_uuid is None: project.app_uuid = generate_half_uuid() if project.app_company_name is None: project.app_company_name = request.user.username if project.app_short_name is None: project.app_short_name = project.name if project.app_long_name is None: project.app_long_name = project.app_short_name if project.app_version_label is None: project.app_version_label = '1.0' send_keen_event('cloudpebble', 'cloudpebble_open_project', request=request, project=project) app_keys = sorted(json.loads(project.app_keys).iteritems(), key=lambda x: x[1]) try: token = request.user.social_auth.get(provider='pebble').extra_data['access_token'] except: token = '' return render(request, 'ide/project.html', { 'project': project, 'app_keys': app_keys, 'font_css': settings.TYPOGRAPHY_CSS, 'libpebble_proxy': json.dumps(settings.LIBPEBBLE_PROXY), 'token': token, 'phone_shorturl': settings.PHONE_SHORTURL, })
def save_project_settings(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) try: with transaction.commit_on_success(): project.name = request.POST['name'] project.app_uuid = request.POST['app_uuid'] project.app_company_name = request.POST['app_company_name'] project.app_short_name = request.POST['app_short_name'] project.app_long_name = request.POST['app_long_name'] project.app_version_code = int(request.POST['app_version_code']) project.app_version_label = request.POST['app_version_label'] project.app_is_watchface = bool(int(request.POST['app_is_watchface'])) project.app_capabilities = request.POST['app_capabilities'] project.app_keys = request.POST['app_keys'] project.app_jshint = bool(int(request.POST['app_jshint'])) menu_icon = request.POST['menu_icon'] if menu_icon != '': menu_icon = int(menu_icon) old_icon = project.menu_icon if old_icon is not None: old_icon.is_menu_icon = False old_icon.save() icon_resource = project.resources.filter(id=menu_icon)[0] icon_resource.is_menu_icon = True icon_resource.save() project.save() except IntegrityError as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_save_project_settings', project=project, request=request) return json_response({})
def load_source_file(request, project_id, file_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) source_file = get_object_or_404(SourceFile, pk=file_id, project=project) try: content = source_file.get_contents() try: folded_lines = json.loads(source_file.folded_lines) except ValueError: folded_lines = [] send_keen_event('cloudpebble', 'cloudpebble_open_file', data={ 'data': { 'filename': source_file.file_name, 'kind': 'source' } }, project=project, request=request) except Exception as e: return json_failure(str(e)) else: return json_response({ "success": True, "source": content, "modified": time.mktime(source_file.last_modified.utctimetuple()), "folded_lines": folded_lines })
def resource_info(request, project_id, resource_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) resource = get_object_or_404(ResourceFile, pk=resource_id) resources = resource.get_identifiers() send_keen_event('cloudpebble', 'cloudpebble_open_file', data={ 'data': { 'filename': resource.file_name, 'kind': 'resource', 'resource-kind': resource.kind } }, project=project, request=request) return json_response({ 'resource': { 'resource_ids': [{ 'id': x.resource_id, 'regex': x.character_regex, 'tracking': x.tracking } for x in resources], 'id': resource.id, 'file_name': resource.file_name, 'kind': resource.kind } })
def resource_info(request, project_id, resource_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) resource = get_object_or_404(ResourceFile, pk=resource_id) resources = resource.get_identifiers() send_keen_event('cloudpebble', 'cloudpebble_open_file', data={ 'data': { 'filename': resource.file_name, 'kind': 'resource', 'resource-kind': resource.kind } }, project=project, request=request) return json_response({ 'resource': { "target_platforms": json.loads(resource.target_platforms) if resource.target_platforms else None, 'resource_ids': [{ 'id': x.resource_id, 'regex': x.character_regex, 'tracking': x.tracking, 'compatibility': x.compatibility } for x in resources], 'id': resource.id, 'file_name': resource.file_name, 'kind': resource.kind, "variants": [x.get_tags() for x in resource.variants.all()], "extra": {y.resource_id: {'regex': y.character_regex, 'tracking': y.tracking, 'compatibility': y.compatibility} for y in resource.identifiers.all()} } })
def index(request): user = request.user my_projects = Project.objects.filter(owner=user).order_by('-last_modified') if not user.settings.accepted_terms: # Screw it. # user_settings = user.settings # user_settings.accepted_terms = True # user_settings.save() return render(request, 'ide/new-owner.html', {'my_projects': my_projects}) elif settings.SOCIAL_AUTH_PEBBLE_REQUIRED and user.social_auth.filter( provider='pebble').count() == 0: return render(request, 'registration/merge_account.html') else: send_keen_event('cloudpebble', 'cloudpebble_project_list', request=request) return render( request, 'ide/index.html', { 'my_projects': my_projects, 'sdk_templates': TemplateProject.objects.filter( template_kind=TemplateProject.KIND_TEMPLATE), 'example_templates': TemplateProject.objects.filter( template_kind=TemplateProject.KIND_EXAMPLE), 'demo_templates': TemplateProject.objects.filter( template_kind=TemplateProject.KIND_SDK_DEMO), 'default_template_id': settings.DEFAULT_TEMPLATE })
def create_resource(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) kind = request.POST['kind'] resource_ids = json.loads(request.POST['resource_ids']) file_name = request.FILES['file'].name resources = [] try: with transaction.commit_on_success(): rf = ResourceFile.objects.create(project=project, file_name=file_name, kind=kind) for r in resource_ids: regex = r['regex'] if 'regex' in r else None tracking = int(r['tracking']) if 'tracking' in r else None resources.append(ResourceIdentifier.objects.create(resource_file=rf, resource_id=r['id'], character_regex=regex, tracking=tracking)) rf.save_file(request.FILES['file'], request.FILES['file'].size) except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_create_file', data={ 'data': { 'filename': file_name, 'kind': 'resource', 'resource-kind': kind } }, project=project, request=request) return json_response({"file": { "id": rf.id, "kind": rf.kind, "file_name": rf.file_name, "resource_ids": [{'id': x.resource_id, 'regex': x.character_regex} for x in resources], "identifiers": [x.resource_id for x in resources] }})
def settings_page(request): user_settings = request.user.settings try: github = request.user.github except UserGithub.DoesNotExist: github = None if request.method == 'POST': form = SettingsForm(request.POST, instance=user_settings) if form.is_valid(): form.save() send_keen_event('cloudpebble', 'cloudpebble_change_user_settings', request=request) return render(request, 'ide/settings.html', { 'form': form, 'saved': True, 'github': github }) else: form = SettingsForm(instance=user_settings) send_keen_event('cloudpebble', 'cloudpebble_view_user_settings', request=request) return render(request, 'ide/settings.html', { 'form': form, 'saved': False, 'github': github })
def create_archive(project_id): project = Project.objects.get(pk=project_id) prefix = re.sub(r'[^\w]+', '_', project.name).strip('_').lower() with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as temp: filename = temp.name with zipfile.ZipFile(filename, 'w', compression=zipfile.ZIP_DEFLATED) as z: add_project_to_archive(z, project) # Generate a URL u = uuid.uuid4().hex send_keen_event('cloudpebble', 'cloudpebble_export_project', project=project) if not settings.AWS_ENABLED: outfile = '%s%s/%s.zip' % (settings.EXPORT_DIRECTORY, u, prefix) os.makedirs(os.path.dirname(outfile), 0755) shutil.copy(filename, outfile) os.chmod(outfile, 0644) return '%s%s/%s.zip' % (settings.EXPORT_ROOT, u, prefix) else: outfile = '%s/%s.zip' % (u, prefix) s3.upload_file('export', outfile, filename, public=True, content_type='application/zip') return '%s%s' % (settings.EXPORT_ROOT, outfile)
def create_project(request): name = request.POST['name'] template_id = request.POST.get('template', None) project_type = request.POST.get('type', 'native') try: with transaction.commit_on_success(): project = Project.objects.create( name=name, owner=request.user, sdk_version=2, app_company_name=request.user.username, app_short_name=name, app_long_name=name, app_version_code=1, app_version_label='1.0', app_is_watchface=False, app_capabilities='', project_type=project_type ) if template_id is not None and int(template_id) != 0: template = TemplateProject.objects.get(pk=int(template_id)) template.copy_into_project(project) elif project_type == 'simplyjs': f = SourceFile.objects.create(project=project, file_name="app.js") f.save_file(open('{}/src/html/demo.js'.format(settings.SIMPLYJS_ROOT)).read()) except IntegrityError as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_create_project', project=project, request=request) return json_response({"id": project.id})
def delete_variant(request, project_id, resource_id, variant): project = get_object_or_404(Project, pk=project_id, owner=request.user) resource = get_object_or_404(ResourceFile, pk=resource_id, project=project) if variant == '0': variant = '' variant_to_delete = resource.variants.get(tags=variant) if resource.variants.count() == 1: return json_failure("You cannot delete the last remaining variant of a resource.") try: variant_to_delete.delete() except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_delete_variant', data={ 'data': { 'filename': resource.file_name, 'kind': 'resource', 'resource-kind': resource.kind, 'variant': variant } }, project=project, request=request) return json_response({'resource': { 'variants': [x.get_tags() for x in resource.variants.all()] }})
def import_gist(user_id, gist_id): user = User.objects.get(pk=user_id) g = github.Github() try: gist = g.get_gist(gist_id) except github.UnknownObjectException: send_keen_event('cloudpebble', 'cloudpebble_gist_not_found', user=user, data={'data': {'gist_id': gist_id}}) raise Exception("Couldn't find gist to import.") files = gist.files default_name = gist.description or 'Sample project' is_native = True if 'appinfo.json' in files: settings = json.loads(files['appinfo.json'].content) if len(files) == 2 and 'simply.js' in files: is_native = False else: settings = {} if len(files) == 1 and 'simply.js' in files: is_native = False project_settings = { 'name': settings.get('longName', default_name), 'owner': user, 'sdk_version': 2, 'app_uuid': generate_half_uuid(), 'app_short_name': settings.get('shortName', default_name), 'app_long_name': settings.get('longName', default_name), 'app_company_name': settings.get('companyName', user.username), 'app_version_code': 1, 'app_version_label': settings.get('versionLabel', '1.0'), 'app_is_watchface': settings.get('watchapp', {}).get('watchface', False), 'app_capabilities': ','.join(settings.get('capabilities', [])), 'app_keys': dict_to_pretty_json(settings.get('appKeys', {})), 'project_type': 'native' if is_native else 'simplyjs' } with transaction.commit_on_success(): project = Project.objects.create(**project_settings) if is_native: for filename in gist.files: if filename.endswith('.c') or filename.endswith('.h') or filename == 'pebble-js-app.js': # Because gists can't have subdirectories. if filename == 'pebble-js-app.js': cp_filename = 'js/pebble-js-app.js' else: cp_filename = filename source_file = SourceFile.objects.create(project=project, file_name=cp_filename) source_file.save_file(gist.files[filename].content) else: source_file = SourceFile.objects.create(project=project, file_name='app.js') source_file.save_file(gist.files['simply.js'].content) send_keen_event('cloudpebble', 'cloudpebble_gist_import', project=project, data={'data': {'gist_id': gist_id}}) return project.id
def transition_accept(request): user_settings = request.user.settings user_settings.accepted_terms = True user_settings.save() send_keen_event('cloudpebble', 'cloudpebble_ownership_transition_accepted', request=request) return json_response({})
def remove_github_auth(request): try: user_github = request.user.github user_github.delete() except UserGithub.DoesNotExist: pass send_keen_event('cloudpebble', 'cloudpebble_github_revoked', request=request) return HttpResponseRedirect('/ide/settings')
def create_resource(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) kind = request.POST['kind'] resource_ids = json.loads(request.POST['resource_ids']) file_name = request.FILES['file'].name resources = [] try: with transaction.commit_on_success(): rf = ResourceFile.objects.create(project=project, file_name=file_name, kind=kind) for r in resource_ids: regex = r['regex'] if 'regex' in r else None tracking = int(r['tracking']) if 'tracking' in r else None resources.append( ResourceIdentifier.objects.create(resource_file=rf, resource_id=r['id'], character_regex=regex, tracking=tracking)) rf.save_file(request.FILES['file'], request.FILES['file'].size) except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_create_file', data={ 'data': { 'filename': file_name, 'kind': 'resource', 'resource-kind': kind } }, project=project, request=request) return json_response({ "file": { "id": rf.id, "kind": rf.kind, "file_name": rf.file_name, "resource_ids": [{ 'id': x.resource_id, 'regex': x.character_regex } for x in resources], "identifiers": [x.resource_id for x in resources], "extra": { y.resource_id: { 'regex': y.character_regex, 'tracking': y.tracking } for y in rf.identifiers.all() } } })
def create_project(request): name = request.POST['name'] template_id = request.POST.get('template', None) if template_id is not None: template_id = int(template_id) project_type = request.POST.get('type', 'native') template_name = None sdk_version = request.POST.get('sdk', 2) try: with transaction.commit_on_success(): project = Project.objects.create( name=name, owner=request.user, app_company_name=request.user.username, app_short_name=name, app_long_name=name, app_version_label='1.0', app_is_watchface=False, app_capabilities='', project_type=project_type, sdk_version=sdk_version, ) if template_id is not None and template_id != 0: template = TemplateProject.objects.get(pk=template_id) template_name = template.name template.copy_into_project(project) elif project_type == 'simplyjs': f = SourceFile.objects.create(project=project, file_name="app.js") f.save_file( open('{}/src/html/demo.js'.format( settings.SIMPLYJS_ROOT)).read()) elif project_type == 'pebblejs': f = SourceFile.objects.create(project=project, file_name="app.js") f.save_file( open('{}/src/js/app.js'.format( settings.PEBBLEJS_ROOT)).read()) except IntegrityError as e: return json_failure(str(e)) else: send_keen_event( 'cloudpebble', 'cloudpebble_create_project', {'data': { 'template': { 'id': template_id, 'name': template_name } }}, project=project, request=request) return json_response({"id": project.id})
def import_gist(request, gist_id): send_keen_event('cloudpebble', 'cloudpebble_gist_landing', request=request, data={'data': { 'gist_id': gist_id }}) return render(request, 'ide/gist-import.html', { 'gist_id': gist_id, 'blurb': request.GET.get('blurb', None) })
def delete_project(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) if not bool(request.POST.get('confirm', False)): return json_failure("Not confirmed") try: project.delete() except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_delete_project', project=project, request=request) return json_response({})
def start_github_auth(request): nonce = uuid.uuid4().hex try: user_github = request.user.github except UserGithub.DoesNotExist: user_github = UserGithub.objects.create(user=request.user) user_github.nonce = nonce user_github.save() send_keen_event('cloudpebble', 'cloudpebble_github_started', request=request) return HttpResponseRedirect('https://github.com/login/oauth/authorize?client_id=%s&scope=repo&state=%s' % (settings.GITHUB_CLIENT_ID, nonce))
def get_shortlink(request): from utils.keen_helper import send_keen_event url = request.POST['url'] try: r = urllib2.Request('http://api.small.cat/entries', json.dumps({'value': url, 'duration': 60}), headers={'Content-Type': 'application/json'}) response = json.loads(urllib2.urlopen(r).read()) except urllib2.URLError as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_generate_shortlink', data={ 'data': {'short_url': response['url']} }, request=request) return json_response({'url': response['url']})
def update_resource(request, project_id, resource_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) resource = get_object_or_404(ResourceFile, pk=resource_id, project=project) resource_ids = json.loads(request.POST['resource_ids']) try: with transaction.commit_on_success(): # Lazy approach: delete all the resource_ids and recreate them. # We could do better. resources = [] ResourceIdentifier.objects.filter(resource_file=resource).delete() for r in resource_ids: regex = r['regex'] if 'regex' in r else None tracking = int(r['tracking']) if 'tracking' in r else None resources.append( ResourceIdentifier.objects.create(resource_file=resource, resource_id=r['id'], character_regex=regex, tracking=tracking)) if 'file' in request.FILES: resource.save_file(request.FILES['file'], request.FILES['file'].size) except Exception as e: return json_failure(str(e)) else: send_keen_event( 'cloudpebble', 'cloudpebble_save_file', data={'data': { 'filename': resource.file_name, 'kind': 'source' }}, project=project, request=request) return json_response({ "file": { "id": resource.id, "kind": resource.kind, "file_name": resource.file_name, "resource_ids": [{ 'id': x.resource_id, 'regex': x.character_regex } for x in resources], "identifiers": [x.resource_id for x in resources] } })
def start_github_auth(request): nonce = uuid.uuid4().hex try: user_github = request.user.github except UserGithub.DoesNotExist: user_github = UserGithub.objects.create(user=request.user) user_github.nonce = nonce user_github.save() send_keen_event('cloudpebble', 'cloudpebble_github_started', request=request) return HttpResponseRedirect( 'https://github.com/login/oauth/authorize?client_id=%s&scope=repo&state=%s' % (settings.GITHUB_CLIENT_ID, nonce))
def delete_source_file(request, project_id, file_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) source_file = get_object_or_404(SourceFile, pk=file_id, project=project) try: source_file.delete() except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_delete_file', data={ 'data': { 'filename': source_file.file_name, 'kind': 'source' } }, project=project, request=request) return json_response({})
def build_log(request, project_id, build_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) build = get_object_or_404(BuildResult, project=project, pk=build_id) try: log = build.read_build_log() except Exception as e: return json_failure(str(e)) send_keen_event('cloudpebble', 'cloudpebble_view_build_log', data={ 'data': { 'build_state': build.state } }, project=project, request=request) return json_response({"log": log})
def create_source_file(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) try: f = SourceFile.objects.create(project=project, file_name=request.POST['name']) f.save_file(request.POST.get('content', '')) except IntegrityError as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_create_file', data={ 'data': { 'filename': request.POST['name'], 'kind': 'source' } }, project=project, request=request) return json_response({"file": {"id": f.id, "name": f.file_name}})
def create_source_file(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) try: f = SourceFile.objects.create(project=project, file_name=request.POST['name']) f.save_file('') except IntegrityError as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_create_file', data={ 'data': { 'filename': request.POST['name'], 'kind': 'source' } }, project=project, request=request) return json_response({"file": {"id": f.id, "name": f.file_name}})
def build_log(request, project_id, build_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) build = get_object_or_404(BuildResult, project=project, pk=build_id) try: log = build.read_build_log() except Exception as e: return json_failure(str(e)) send_keen_event('cloudpebble', 'cloudpebble_view_build_log', data={'data': { 'build_state': build.state }}, project=project, request=request) return json_response({"log": log})
def view_project(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) if project.app_uuid is None: project.app_uuid = generate_half_uuid() if project.app_company_name is None: project.app_company_name = request.user.username if project.app_short_name is None: project.app_short_name = project.name if project.app_long_name is None: project.app_long_name = project.app_short_name if project.app_version_code is None: project.app_version_code = 1 if project.app_version_label is None: project.app_version_label = '1.0' send_keen_event('cloudpebble', 'cloudpebble_open_project', request=request, project=project) app_keys = json.loads(project.app_keys).iteritems() return render(request, 'ide/project.html', {'project': project, 'app_keys': app_keys})
def proxy_keen(request, project_id): from utils.keen_helper import send_keen_event project = get_object_or_404(Project, pk=project_id) acceptable_events = { 'app_install_succeeded', 'websocket_connection_failed', 'app_install_failed', 'app_log_view', 'app_log_clear', 'app_log_download', 'app_logged_crash', 'sdk_screenshot_success', 'sdk_screenshot_failed', 'cloudpebble_created_ui_layout', 'cloudpebble_ib_displayed', 'cloudpebble_ib_created_layer', 'cloudpebble_android_beta_modal', 'cloudpebble_android_beta_download', 'qemu_launched', 'cloudpebble_timeline_displayed', 'sdk_pin_inserted', 'sdk_pin_deleted', } event = request.POST['event'] if event not in acceptable_events: return json_failure("nope.") data = {} if 'data' in request.POST: data['data'] = json.loads(request.POST['data']) if 'device' in request.POST: data['device'] = json.loads(request.POST['device']) collections = ['cloudpebble', 'sdk'] if 'collections' in request.POST: collections = list(set(collections) & set(json.loads(request.POST['collections']))) if len(data.items()) == 0: data = None send_keen_event(collections, event, project=project, request=request, data=data) return json_response({})
def proxy_keen(request, project_id): from utils.keen_helper import send_keen_event project = get_object_or_404(Project, pk=project_id) acceptable_events = { 'app_install_succeeded', 'websocket_connection_failed', 'app_install_failed', 'app_log_view', 'app_logged_crash', 'sdk_screenshot_success', 'sdk_screenshot_failed', 'cloudpebble_created_ui_layout', 'cloudpebble_ib_displayed', 'cloudpebble_ib_created_layer', 'cloudpebble_android_beta_modal', 'cloudpebble_android_beta_download', 'qemu_launched', } event = request.POST['event'] if event not in acceptable_events: return json_failure("nope.") data = {} if 'data' in request.POST: data['data'] = json.loads(request.POST['data']) if 'device' in request.POST: data['device'] = json.loads(request.POST['device']) collections = ['cloudpebble', 'sdk'] if 'collections' in request.POST: collections = list( set(collections) & set(json.loads(request.POST['collections']))) if len(data.items()) == 0: data = None send_keen_event(collections, event, project=project, request=request, data=data) return json_response({})
def create_resource(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) kind = request.POST['kind'] resource_ids = json.loads(request.POST['resource_ids']) posted_file = request.FILES.get('file', None) file_name = request.POST['file_name'] new_tags = json.loads(request.POST['new_tags']) resources = [] try: with transaction.atomic(): rf = ResourceFile.objects.create(project=project, file_name=file_name, kind=kind) for r in resource_ids: regex = r['regex'] if 'regex' in r else None tracking = int(r['tracking']) if 'tracking' in r else None resources.append(ResourceIdentifier.objects.create(resource_file=rf, resource_id=r['id'], character_regex=regex, tracking=tracking)) if posted_file is not None: variant = ResourceVariant.objects.create(resource_file=rf, tags=",".join(str(int(t)) for t in new_tags)) variant.save_file(posted_file, posted_file.size) target_platforms = json.loads(request.POST.get('target_platforms', None)) rf.target_platforms = None if target_platforms is None else json.dumps(target_platforms) rf.save() except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_create_file', data={ 'data': { 'filename': file_name, 'kind': 'resource', 'resource-kind': kind } }, project=project, request=request) return json_response({"file": { "id": rf.id, "kind": rf.kind, "file_name": rf.file_name, "target_platforms": json.loads(rf.target_platforms) if rf.target_platforms else None, "resource_ids": [{'id': x.resource_id, 'regex': x.character_regex} for x in resources], "identifiers": [x.resource_id for x in resources], "variants": [x.get_tags() for x in rf.variants.all()], "extra": {y.resource_id: {'regex': y.character_regex, 'tracking': y.tracking} for y in rf.identifiers.all()} }})
def complete_github_auth(request): if 'error' in request.GET: return HttpResponseRedirect('/ide/settings') nonce = request.GET['state'] code = request.GET['code'] user_github = request.user.github if user_github.nonce is None or nonce != user_github.nonce: return HttpResponseBadRequest('nonce mismatch.') # This probably shouldn't be in a view. Oh well. params = urllib.urlencode({ 'client_id': settings.GITHUB_CLIENT_ID, 'client_secret': settings.GITHUB_CLIENT_SECRET, 'code': code }) r = urllib2.Request('https://github.com/login/oauth/access_token', params, headers={'Accept': 'application/json'}) result = json.loads(urllib2.urlopen(r).read()) user_github = request.user.github user_github.token = result['access_token'] user_github.nonce = None # Try and figure out their username. auth_string = base64.encodestring( '%s:%s' % (settings.GITHUB_CLIENT_ID, settings.GITHUB_CLIENT_SECRET)).replace( '\n', '') r = urllib2.Request('https://api.github.com/applications/%s/tokens/%s' % (settings.GITHUB_CLIENT_ID, user_github.token)) r.add_header("Authorization", "Basic %s" % auth_string) result = json.loads(urllib2.urlopen(r).read()) user_github.username = result['user']['login'] user_github.avatar = result['user']['avatar_url'] user_github.save() send_keen_event('cloudpebble', 'cloudpebble_github_linked', request=request, data={'data': { 'username': user_github.username }}) return HttpResponseRedirect('/ide/settings')
def export_user_projects(user_id): user = User.objects.get(pk=user_id) projects = Project.objects.filter(owner=user) with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as temp: filename = temp.name with zipfile.ZipFile(filename, 'w', compression=zipfile.ZIP_DEFLATED) as z: for project in projects: add_project_to_archive(z, project, prefix='cloudpebble-export/') # Generate a URL u = uuid.uuid4().hex outfile = '%s%s/%s.zip' % (settings.EXPORT_DIRECTORY, u, 'cloudpebble-export') os.makedirs(os.path.dirname(outfile), 0755) shutil.copy(filename, outfile) os.chmod(outfile, 0644) send_keen_event('cloudpebble', 'cloudpebble_export_all_projects', user=user) return '%s%s/%s.zip' % (settings.EXPORT_ROOT, u, 'cloudpebble-export')
def export_user_projects(user_id): user = User.objects.get(pk=user_id) projects = Project.objects.filter(owner=user) with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as temp: filename = temp.name with zipfile.ZipFile(filename, "w", compression=zipfile.ZIP_DEFLATED) as z: for project in projects: add_project_to_archive(z, project, prefix="cloudpebble-export/") # Generate a URL u = uuid.uuid4().hex outfile = "%s%s/%s.zip" % (settings.EXPORT_DIRECTORY, u, "cloudpebble-export") os.makedirs(os.path.dirname(outfile), 0755) shutil.copy(filename, outfile) os.chmod(outfile, 0644) send_keen_event("cloudpebble", "cloudpebble_export_all_projects", user=user) return "%s%s/%s.zip" % (settings.EXPORT_ROOT, u, "cloudpebble-export")
def create_resource(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) kind = request.POST['kind'] resource_ids = json.loads(request.POST['resource_ids']) default_file = request.FILES.get('file', None) colour_file = request.FILES.get('file_colour', None) file_name = default_file.name if default_file is not None else colour_file.name resources = [] try: with transaction.commit_on_success(): rf = ResourceFile.objects.create(project=project, file_name=file_name, kind=kind) for r in resource_ids: regex = r['regex'] if 'regex' in r else None tracking = int(r['tracking']) if 'tracking' in r else None resources.append(ResourceIdentifier.objects.create(resource_file=rf, resource_id=r['id'], character_regex=regex, tracking=tracking)) if default_file is not None: default_variant = ResourceVariant.objects.create(resource_file=rf, variant=ResourceVariant.VARIANT_DEFAULT) default_variant.save_file(default_file, default_file.size) if colour_file is not None: colour_variant = ResourceVariant.objects.create(resource_file=rf, variant=ResourceVariant.VARIANT_COLOUR) colour_variant.save_file(colour_file, colour_file.size) except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_create_file', data={ 'data': { 'filename': file_name, 'kind': 'resource', 'resource-kind': kind } }, project=project, request=request) return json_response({"file": { "id": rf.id, "kind": rf.kind, "file_name": rf.file_name, "resource_ids": [{'id': x.resource_id, 'regex': x.character_regex} for x in resources], "identifiers": [x.resource_id for x in resources], "variants": [x.variant for x in rf.variants.all()], "extra": {y.resource_id: {'regex': y.character_regex, 'tracking': y.tracking} for y in rf.identifiers.all()} }})
def save_project_settings(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) try: with transaction.commit_on_success(): sdk_version = int(request.POST['sdk_version']) project.name = request.POST['name'] project.sdk_version = sdk_version if sdk_version == 1: project.version_def_name = request.POST['version_def_name'] elif sdk_version > 1: project.app_uuid = request.POST['app_uuid'] project.app_company_name = request.POST['app_company_name'] project.app_short_name = request.POST['app_short_name'] project.app_long_name = request.POST['app_long_name'] project.app_version_code = int( request.POST['app_version_code']) project.app_version_label = request.POST['app_version_label'] project.app_is_watchface = bool( int(request.POST['app_is_watchface'])) project.app_capabilities = request.POST['app_capabilities'] project.app_keys = request.POST['app_keys'] project.app_jshint = bool(int(request.POST['app_jshint'])) menu_icon = request.POST['menu_icon'] if menu_icon != '': menu_icon = int(menu_icon) old_icon = project.menu_icon if old_icon is not None: old_icon.is_menu_icon = False old_icon.save() icon_resource = project.resources.filter(id=menu_icon)[0] icon_resource.is_menu_icon = True icon_resource.save() project.save() except IntegrityError as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_save_project_settings', project=project, request=request) return json_response({})
def resource_info(request, project_id, resource_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) resource = get_object_or_404(ResourceFile, pk=resource_id) resources = resource.get_identifiers() send_keen_event('cloudpebble', 'cloudpebble_open_file', data={ 'data': { 'filename': resource.file_name, 'kind': 'resource', 'resource-kind': resource.kind } }, project=project, request=request) return json_response({ 'resource': { 'resource_ids': [{ 'id': x.resource_id, 'regex': x.character_regex, 'tracking': x.tracking, 'compatibility': x.compatibility } for x in resources], 'id': resource.id, 'file_name': resource.file_name, 'kind': resource.kind, "variants": [x.variant for x in resource.variants.all()], "extra": { y.resource_id: { 'regex': y.character_regex, 'tracking': y.tracking, 'compatibility': y.compatibility } for y in resource.identifiers.all() } } })
def settings_page(request): user_settings = request.user.settings try: github = request.user.github except UserGithub.DoesNotExist: github = None if request.method == 'POST': form = SettingsForm(request.POST, instance=user_settings) if form.is_valid(): form.save() send_keen_event('cloudpebble', 'cloudpebble_change_user_settings', request=request) return render(request, 'ide/settings.html', {'form': form, 'saved': True, 'github': github}) else: form = SettingsForm(instance=user_settings) send_keen_event('cloudpebble', 'cloudpebble_view_user_settings', request=request) return render(request, 'ide/settings.html', {'form': form, 'saved': False, 'github': github})
def update_resource(request, project_id, resource_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) resource = get_object_or_404(ResourceFile, pk=resource_id, project=project) resource_ids = json.loads(request.POST['resource_ids']) try: with transaction.atomic(): # Lazy approach: delete all the resource_ids and recreate them. # We could do better. resources = [] ResourceIdentifier.objects.filter(resource_file=resource).delete() for r in resource_ids: regex = r['regex'] if 'regex' in r else None tracking = int(r['tracking']) if 'tracking' in r else None compat = r['compatibility'] if 'compatibility' in r else None resources.append(ResourceIdentifier.objects.create(resource_file=resource, resource_id=r['id'], character_regex=regex, tracking=tracking, compatibility=compat)) if 'file' in request.FILES: default_variant = resource.variants.get_or_create(variant=ResourceVariant.VARIANT_DEFAULT)[0] default_variant.save_file(request.FILES['file'], request.FILES['file'].size) if 'file_colour' in request.FILES: colour_variant = resource.variants.get_or_create(variant=ResourceVariant.VARIANT_COLOUR)[0] colour_variant.save_file(request.FILES['file_colour'], request.FILES['file_colour'].size) except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_save_file', data={ 'data': { 'filename': resource.file_name, 'kind': 'source' } }, project=project, request=request) return json_response({"file": { "id": resource.id, "kind": resource.kind, "file_name": resource.file_name, "resource_ids": [{'id': x.resource_id, 'regex': x.character_regex, 'compatibility': x.compatibility} for x in resources], "identifiers": [x.resource_id for x in resources], "variants": [x.variant for x in resource.variants.all()], "extra": {y.resource_id: {'regex': y.character_regex, 'tracking': y.tracking, 'compatibility': y.compatibility} for y in resource.identifiers.all()} }})
def load_source_file(request, project_id, file_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) source_file = get_object_or_404(SourceFile, pk=file_id, project=project) try: content = source_file.get_contents() send_keen_event('cloudpebble', 'cloudpebble_open_file', data={ 'data': { 'filename': source_file.file_name, 'kind': 'source' } }, project=project, request=request) except Exception as e: return json_failure(str(e)) else: return json_response({ "success": True, "source": content, "modified": time.mktime(source_file.last_modified.utctimetuple()) })
def create_resource(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) kind = request.POST['kind'] resource_ids = json.loads(request.POST['resource_ids']) posted_file = request.FILES.get('file', None) file_name = request.POST['file_name'] new_tags = json.loads(request.POST['new_tags']) resources = [] try: with transaction.atomic(): rf = ResourceFile.objects.create(project=project, file_name=file_name, kind=kind) for r in resource_ids: resource_options = decode_resource_id_options(r) resources.append(ResourceIdentifier.objects.create(resource_file=rf, **resource_options)) if posted_file is not None: variant = ResourceVariant.objects.create(resource_file=rf, tags=",".join(str(int(t)) for t in new_tags)) variant.save_file(posted_file, posted_file.size) rf.save() except Exception as e: return json_failure(str(e)) else: send_keen_event('cloudpebble', 'cloudpebble_create_file', data={ 'data': { 'filename': file_name, 'kind': 'resource', 'resource-kind': kind } }, project=project, request=request) return json_response({"file": { "id": rf.id, "kind": rf.kind, "file_name": rf.file_name, "resource_ids": [x.get_options_dict(with_id=True) for x in resources], "identifiers": [x.resource_id for x in resources], "variants": [x.get_tags() for x in rf.variants.all()], "extra": {y.resource_id: y.get_options_dict(with_id=False) for y in rf.identifiers.all()} }})
def create_project_repo(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) repo = request.POST['repo'] description = request.POST['description'] try: repo = ide.git.create_repo(request.user, repo, description) except Exception as e: return json_failure(str(e)) else: project.github_repo = repo.full_name project.github_branch = "master" project.github_last_sync = None project.github_last_commit = None project.save() send_keen_event('cloudpebble', 'cloudpebble_created_github_repo', project=project, request=request, data={ 'data': { 'repo': project.github_repo } }) return json_response({"repo": repo.html_url})
def view_project(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) if project.app_uuid is None: project.app_uuid = generate_half_uuid() if project.app_company_name is None: project.app_company_name = request.user.username if project.app_short_name is None: project.app_short_name = project.name if project.app_long_name is None: project.app_long_name = project.app_short_name if project.app_version_code is None: project.app_version_code = 1 if project.app_version_label is None: project.app_version_label = '1.0' send_keen_event('cloudpebble', 'cloudpebble_open_project', request=request, project=project) app_keys = json.loads(project.app_keys).iteritems() return render(request, 'ide/project.html', { 'project': project, 'app_keys': app_keys })
def create_project_repo(request, project_id): project = get_object_or_404(Project, pk=project_id, owner=request.user) repo = request.POST['repo'] description = request.POST['description'] try: repo = ide.git.create_repo(request.user, repo, description) except Exception as e: return json_failure(str(e)) else: project.github_repo = repo.full_name project.github_branch = "master" project.github_last_sync = None project.github_last_commit = None project.save() send_keen_event('cloudpebble', 'cloudpebble_created_github_repo', project=project, request=request, data={'data': { 'repo': project.github_repo }}) return json_response({"repo": repo.html_url})
def run_compile(build_result): build_result = BuildResult.objects.get(pk=build_result) project = build_result.project source_files = SourceFile.objects.filter(project=project) resources = ResourceFile.objects.filter(project=project) # Assemble the project somewhere base_dir = tempfile.mkdtemp(dir=os.path.join(settings.CHROOT_ROOT, 'tmp') if settings.CHROOT_ROOT else None) try: # Resources resource_root = 'resources' os.makedirs(os.path.join(base_dir, resource_root, 'images')) os.makedirs(os.path.join(base_dir, resource_root, 'fonts')) os.makedirs(os.path.join(base_dir, resource_root, 'data')) if project.project_type == 'native': # Source code create_source_files(project, base_dir) manifest_dict = generate_manifest_dict(project, resources) open(os.path.join(base_dir, 'appinfo.json'), 'w').write(json.dumps(manifest_dict)) for f in resources: target_dir = os.path.abspath( os.path.join(base_dir, resource_root, ResourceFile.DIR_MAP[f.kind])) abs_target = os.path.abspath( os.path.join(target_dir, f.file_name)) f.copy_all_variants_to_dir(target_dir) # Reconstitute the SDK open(os.path.join(base_dir, 'wscript'), 'w').write(generate_wscript_file(project)) open(os.path.join(base_dir, 'pebble-jshintrc'), 'w').write(generate_jshint_file(project)) elif project.project_type == 'simplyjs': shutil.rmtree(base_dir) shutil.copytree(settings.SIMPLYJS_ROOT, base_dir) manifest_dict = generate_simplyjs_manifest_dict(project) js = '\n\n'.join(x.get_contents() for x in source_files if x.file_name.endswith('.js')) escaped_js = json.dumps(js) build_result.save_simplyjs(js) open(os.path.join(base_dir, 'appinfo.json'), 'w').write(json.dumps(manifest_dict)) open(os.path.join(base_dir, 'src', 'js', 'zzz_userscript.js'), 'w').write(""" (function() { simply.mainScriptSource = %s; })(); """ % escaped_js) elif project.project_type == 'pebblejs': shutil.rmtree(base_dir) shutil.copytree(settings.PEBBLEJS_ROOT, base_dir) manifest_dict = generate_pebblejs_manifest_dict(project, resources) create_source_files(project, base_dir) for f in resources: if f.kind != 'png': continue target_dir = os.path.abspath( os.path.join(base_dir, resource_root, ResourceFile.DIR_MAP[f.kind])) abs_target = os.path.abspath( os.path.join(target_dir, f.file_name)) if not abs_target.startswith(target_dir): raise Exception("Suspicious filename: %s" % f.file_name) f.copy_to_path(ResourceVariant.VARIANT_DEFAULT, abs_target) open(os.path.join(base_dir, 'appinfo.json'), 'w').write(json.dumps(manifest_dict)) # Build the thing cwd = os.getcwd() success = False output = 'Failed to get output' build_start_time = now() try: os.chdir(base_dir) if project.sdk_version == '2': tool = settings.SDK2_PEBBLE_TOOL elif project.sdk_version == '3': tool = settings.SDK3_PEBBLE_TOOL else: raise Exception("invalid sdk version.") output = subprocess.check_output([tool, "build"], stderr=subprocess.STDOUT, preexec_fn=_set_resource_limits) except subprocess.CalledProcessError as e: output = e.output print output success = False except Exception as e: success = False output = str(e) else: success = True temp_file = os.path.join(base_dir, 'build', '%s.pbw' % os.path.basename(base_dir)) if not os.path.exists(temp_file): success = False print "Success was a lie." finally: build_end_time = now() os.chdir(cwd) if success: # Try reading file sizes out of it first. try: s = os.stat(temp_file) build_result.total_size = s.st_size # Now peek into the zip to see the component parts with zipfile.ZipFile(temp_file, 'r') as z: store_size_info(build_result, 'aplite', z) store_size_info(build_result, 'basalt', z) except Exception as e: print "Couldn't extract filesizes: %s" % e # Try pulling out debug information. if project.sdk_version == '2': save_debug_info( base_dir, build_result, BuildResult.DEBUG_APP, 'aplite', os.path.join(base_dir, 'build', 'pebble-app.elf')) save_debug_info( base_dir, build_result, BuildResult.DEBUG_WORKER, 'aplite', os.path.join(base_dir, 'build', 'pebble-worker.elf')) else: save_debug_info( base_dir, build_result, BuildResult.DEBUG_APP, 'aplite', os.path.join(base_dir, 'build', 'aplite/pebble-app.elf')) save_debug_info( base_dir, build_result, BuildResult.DEBUG_WORKER, 'aplite', os.path.join(base_dir, 'build', 'aplite/pebble-worker.elf')) save_debug_info( base_dir, build_result, BuildResult.DEBUG_APP, 'basalt', os.path.join(base_dir, 'build', 'basalt/pebble-app.elf')) save_debug_info( base_dir, build_result, BuildResult.DEBUG_WORKER, 'basalt', os.path.join(base_dir, 'build', 'basalt/pebble-worker.elf')) build_result.save_pbw(temp_file) build_result.save_build_log(output) build_result.state = BuildResult.STATE_SUCCEEDED if success else BuildResult.STATE_FAILED build_result.finished = now() build_result.save() data = { 'data': { 'cloudpebble': { 'build_id': build_result.id, 'job_run_time': (build_result.finished - build_result.started).total_seconds(), }, 'build_time': (build_end_time - build_start_time).total_seconds(), } } event_name = 'app_build_succeeded' if success else 'app_build_failed' send_keen_event(['cloudpebble', 'sdk'], event_name, data, project=project) except Exception as e: print "Build failed due to internal error: %s" % e traceback.print_exc() build_result.state = BuildResult.STATE_FAILED build_result.finished = now() try: build_result.save_build_log("Something broke:\n%s" % e) except: pass build_result.save() finally: # shutil.rmtree(base_dir) print base_dir
def github_push(user, commit_message, repo_name, project): g = Github(user.github.token, client_id=settings.GITHUB_CLIENT_ID, client_secret=settings.GITHUB_CLIENT_SECRET) repo = g.get_repo(repo_name) try: branch = repo.get_branch(project.github_branch or repo.master_branch) except GithubException: raise Exception("Unable to get branch.") commit = repo.get_git_commit(branch.commit.sha) tree = repo.get_git_tree(commit.tree.sha, recursive=True) paths = [x.path for x in tree.tree] next_tree = { x.path: InputGitTreeElement(path=x.path, mode=x.mode, type=x.type, sha=x.sha) for x in tree.tree } try: root = find_project_root(paths) except: root = '' src_root = root + 'src/' project_sources = project.source_files.all() has_changed = False for source in project_sources: repo_path = src_root + source.file_name if repo_path not in next_tree: has_changed = True next_tree[repo_path] = InputGitTreeElement( path=repo_path, mode='100644', type='blob', content=source.get_contents()) print "New file: %s" % repo_path else: sha = next_tree[repo_path]._InputGitTreeElement__sha our_content = source.get_contents() expected_sha = git_sha(our_content) if expected_sha != sha: print "Updated file: %s" % repo_path next_tree[repo_path]._InputGitTreeElement__sha = NotSet next_tree[ repo_path]._InputGitTreeElement__content = our_content has_changed = True expected_source_files = [src_root + x.file_name for x in project_sources] for path in next_tree.keys(): if not path.startswith(src_root): continue if path not in expected_source_files: del next_tree[path] print "Deleted file: %s" % path has_changed = True # Now try handling resource files. resources = project.resources.all() resource_root = root + 'resources/' for res in resources: repo_path = resource_root + res.path if repo_path in next_tree: content = res.get_contents() if git_sha( content) != next_tree[repo_path]._InputGitTreeElement__sha: print "Changed resource: %s" % repo_path has_changed = True blob = repo.create_git_blob(base64.b64encode(content), 'base64') print "Created blob %s" % blob.sha next_tree[repo_path]._InputGitTreeElement__sha = blob.sha else: print "New resource: %s" % repo_path blob = repo.create_git_blob(base64.b64encode(res.get_contents()), 'base64') print "Created blob %s" % blob.sha next_tree[repo_path] = InputGitTreeElement(path=repo_path, mode='100644', type='blob', sha=blob.sha) remote_manifest_path = root + 'appinfo.json' remote_wscript_path = root + 'wscript' remote_manifest_sha = next_tree[ remote_manifest_path]._InputGitTreeElement__sha if remote_manifest_path in next_tree else None if remote_manifest_sha is not None: their_manifest_dict = json.loads(git_blob(repo, remote_manifest_sha)) their_res_dict = their_manifest_dict['resources'] else: their_manifest_dict = {} their_res_dict = {'media': []} our_manifest_dict = generate_manifest_dict(project, resources) our_res_dict = our_manifest_dict['resources'] if our_res_dict != their_res_dict: print "Resources mismatch." has_changed = True # Try removing things that we've deleted, if any to_remove = set(x['file'] for x in their_res_dict['media']) - set( x['file'] for x in our_res_dict['media']) for path in to_remove: repo_path = resource_root + path if repo_path in next_tree: print "Deleted resource: %s" % repo_path del next_tree[repo_path] # This one is separate because there's more than just the resource map changing. if their_manifest_dict != our_manifest_dict: if remote_manifest_path in next_tree: next_tree[remote_manifest_path]._InputGitTreeElement__sha = NotSet next_tree[ remote_manifest_path]._InputGitTreeElement__content = generate_manifest( project, resources) else: next_tree[remote_manifest_path] = InputGitTreeElement( path=remote_manifest_path, mode='100644', type='blob', content=generate_manifest(project, resources)) if project.project_type == 'native' and remote_wscript_path not in next_tree: next_tree[remote_wscript_path] = InputGitTreeElement( path=remote_wscript_path, mode='100644', type='blob', content=generate_wscript_file(project, True)) has_changed = True # Commit the new tree. if has_changed: print "Has changed; committing" # GitHub seems to choke if we pass the raw directory nodes off to it, # so we delete those. for x in next_tree.keys(): if next_tree[x]._InputGitTreeElement__mode == '040000': del next_tree[x] print "removing subtree node %s" % x print[x._InputGitTreeElement__mode for x in next_tree.values()] git_tree = repo.create_git_tree(next_tree.values()) print "Created tree %s" % git_tree.sha git_commit = repo.create_git_commit(commit_message, git_tree, [commit]) print "Created commit %s" % git_commit.sha git_ref = repo.get_git_ref( 'heads/%s' % (project.github_branch or repo.master_branch)) git_ref.edit(git_commit.sha) print "Updated ref %s" % git_ref.ref project.github_last_commit = git_commit.sha project.github_last_sync = now() project.save() return True send_keen_event('cloudpebble', 'cloudpebble_github_push', user=user, data={'data': { 'repo': project.github_repo }}) return False