Archive:Build Service Future
This article about the Open Build Service is obsolete! You can find up to date information on https://github.com/openSUSE/open-build-service/ |
The Future is Open
This page collects the ideas, wishes and learned lessons of the Open Build Service developers as well as the community. This is not to be confused with the openSUSE:Build_Service_Roadmap, which covers shorter term (and more realistic) goals. Please understand that these are only ideas, neither promises nor planned features!
Ideas that are considered worth pursuing may be formulated into concepts and moved to the openSUSE:Build_Service_Concepts wiki page. They may then become part of the release goals of a future Open Build Service version. But for now, consider this page a braindump.
Architecture
Merge Api and Webui?
In other words, moving from a 3-tier architecture to 2 tiers.
- Less development traction
- Less caching issues and less sending XML around
- You know when to purge what caches.
- See bnc#559158
- Smaller (easier!!) code-base
- Enforce consistent API style (e.g. REST completely)
- make test suite for it
- use rails format handling
- /project/home:darix/runit(.html) -> html
- /project/home:darix/runit.json
- /project/home:darix/runit.xml
- Move to Rails-3 (or even Django :-)
- Have optional features per project (e.g. merging redmine into the webui)
- all additional features like bugtracking, wiki should be opt-in
- it should be possible reference external wikis/bugtracker properly on per project/package base.
- Small bugtracker/wiki/blog so projects can present themself
- Have optional features per project (e.g. merging redmine into the webui)
Mimetypes
Set proper mimetypes for files provided through the (future) REST API. Some current (bad) examples:
- https://api.opensuse.org/build/_workerstatus returns a binary instead of text/xml
- bnc#470611
Workflows and Collaboration
- submitrequest is to heavy / supports to many use cases
- Rather have add and update requests
- only one action per request to avoid current controller/view check-what-it-is madness
- Workflow engine http://ruote.rubyforge.org/
- Bugs/Tickets/Features closer to projects/packages?
- Probably like Redmine, i.e. a per project/package ticket system with the option to delegate to a central / another bugzilla instance
- Per project/package wiki (redmine, again)
- User karma and statistics like packaging efforts, wiki edits, request editing, tickets / bugs handling
- to motivate (feed the meritocracy), make user actions visible (facebook addictions)
- +1 for submissions:
- +1 for changelog entry
- +2 for sucessful build
- +1 for commit log entry, ...
- -1 for non-building submission
- needs metrics
1:n 1:n user ----> roles -----> permissions workflow --> roles \-> permissions
- properly defined use cases for all actions
- cucumber/rspec maybe?
- properly defined use cases for all actions
- handle workflows with ruote? http://www.engineyard.com/blog/2011/ruote-and-flow/
- package blacklisting!
- search-if-package-is-already-in-the-buildservice-and-hint-the-user-at-it-instead-of-letting-him-build-it-for-himself (tm)
Auditing
- Backend and frontend should log into the same database who did what and when to be able to reproduce changes
Have everything backed by a model
This is actually a no-brainer, here's what happen if you don't do it:
def involved_requests(opts = {}) opts = {:cache => true}.merge opts cachekey = "#{login}_involved_requests" Rails.cache.delete cachekey unless opts[:cache] requests = Rails.cache.fetch(cachekey, :expires_in => 10.minutes) do # FIXME: we assume that the user is involved in all his subprojects (home:#{login}:...) # that should not be needed ... verify ... iprojects = involved_projects.each.map {|x| x.name}.reject {|x| /^home:#{login}:/.match(x) }.sort requests = Array.new request_ids = Array.new myrequests = Hash.new unless iprojects.empty? # find active requests where person is maintained in target project predicate = iprojects.map {|item| "action/target/@project='#{item}'"}.join(" or ") predicate = "#{predicate} or starts-with(action/target/@project, 'home:#{login}:')" predicate = "(state/@name='new' or state/@name='review') and (#{predicate})" collection = Collection.find :what => :request, :predicate => predicate collection.each do |req| myrequests[Integer(req.value :id)] = req end # find requests created by person and still active collection = Collection.find :what => :request, :predicate => "(state/@name='new' or state/@name='review') and state/@who='#{login}'" collection.each do |req| myrequests[Integer(req.value :id)] = req end # find requests where person is reviewer collection = Collection.find :what => :request, :predicate => "state/@name='review' and review[@by_user='#{login}' and @state='new']" collection.each do |req| myrequests[Integer(req.value :id)] = req end keys = myrequests.keys().sort {|x,y| y <=> x} keys.each do |id| unless request_ids.include? id requests << myrequests[id] request_ids << id end end end # check for all open review tasks collection = BsRequest.find_open_review_requests(login) collection.each do |req| myrequests[Integer(req.value :id)] = req end keys = myrequests.keys().sort {|x,y| y <=> x} keys.each do |id| unless request_ids.include? id requests << myrequests[id] request_ids << id end end requests end return requests end
This piece of madness actually tries to catch all requests that a specific user is involved (and coolo said it's not even complete / buggy). Actually, this should be only asking the model for all requests of a specific id....
Users and Groups
Recently, we gained support for by_project and by_package reviews, which is actually a crude hack because we don't have a real groups concept. A new OBS iteration should create a group for each new project and package or take an existing group as owner. On top of that, every user should have a group which contains only himself. This way, the code only has to do group checks and management becomes a breeze. BTW, this is done by several major Web Development Frameworks and is proven to work.
- Every user gets a group with only him
- Every project/package/whatever is owned by a group
- By default, a new group is created for projects/packages/whatever OR an already existing group is used upon creation
Simple design, no hacks needed, no freaking corner-cases.
The Web Frontend
- log to /var/log/obs-frontend instead of $RAILS_ROOT/log
- server-side syntax highlighting / ditch JavaScript-based syntax highlighting
- results should be generated only upon source change / first access
- results should be cached accordingly
Modell / Data Storage
- ActiveXML is a bit awkward
- Use ActiveModel
- Do we need a wrapper around the XML stuff?
- Would something like JSON be better maybe?
- If we go for JSON, CouchDB might be a good idea for the store?
- Git-based storage?
- One Git repository per package?
- pre-commit-hooks for authentication
- post-commit-hooks to generate global statistics (karma, contribution history)
- One Git repository per package?
resources :projects do resources :packages do resources :history resources :roles resources :flags resources :attributes end resources :roles resources :flags end
- Full text search done in the database: https://github.com/Casecommons/pg_search
Controllers
- Considered too fat, real logic should be only in the models, whereas controllers only aggregate data for presentation
Views
- Considered to fat, views should contain no code at all (i.e. enforce MVC pattern)
- Move to a different template engine than ERB / eRuby / erubis
- Interesting candidates may be Django templates (Python) or Mustache, Liquid, Haml
- Move to a different template engine than ERB / eRuby / erubis
- Pagination for projects, packages, monitor and status pages?
Helpers
- They are currently much to fat, i.e. they should only transform data and shall be small
- Actually, they should be used only as a last resort.
- We have helper methods that span multiple pages and generate lots of HTML.
- Helpers shall never generate HTML !!! (XSS madness here)
Routing
Discussed at openSUSE:Build_Service_Future_Routing
Searches
Discussed at openSUSE:Build_Service_Future_Searches
Authorization
- Have OAuth
- Have OpenID
- Use authorization frameworks like https://github.com/stffn/declarative_authorization
Backend Communication
- Use a message queue system like ZeroMQ / ActiveMQ?
- This might save us having to configure each endpoint instead of just broadcasting the message and all parts can hook on it.
- http://wiki.secondlife.com/wiki/Message_Queue_Evaluation_Notes#RabbitMQ
- http://www.igvita.com/2010/11/17/routing-with-ruby-zeromq-devices/
- all messages need to reference data with a revision
- our storage has to support versioned data
Internationalization / Localization
- Incorporate right from the start (gettext preferred, http://www.yotabanana.com/hiki/ruby-gettext.html)
- We have to find out, which template engine supports this (apart from ERB)
- JavaScript poses issues on i18n/l10n
- Avoid generating text thru JavaScript, i.e. only DOM-manipulation
- AJAX-queries should then fetch translated content generated on the server side
- Language can be determined from what the User Agent sends (never Geolocation) and it should be a configurable option in the user settings
Communication with other tools
- XMLRPC instead of XML files over HTTP (see py2pack as an example)
- Way easier, removes lots of unnecessary parsing / checking code.
- More natural, clients (like osc) can directly call (remote) functions in their native language
- How to do it in Ruby: http://www.ibm.com/developerworks/opensource/library/os-ws-rubyrails/index.html
The Backend
- Move to Perl6 :-D
Code Conventions
- Proper indenting rules (i.e. no tabs checked in)
- Be explicit rather than implicit or automagical (as much as possible)
ChangeLogs
A nice idea would be to have a git pre-commit hook which checks the commit message. If the message contains a special tag, like "#change", it may add an entry to a (yet to create) ChangeLog file and add this to the current commit.
Ruby-specific
- Always use foo() function call syntax
- Use lower_case_function_names
- Add an explicit return statement at the end of each function (even return nil?)
- Improves security: functions don't leak the last statement!
- Allows to set breackpoint on last statement (debugging)
Rails Forms
- Only use form_for and style forms with CSS
- Every form into its own partial ! (future prof, reusable)
Perl-specific
- Use comments to explain awesome code constructs!
See also
The Open Build Service roadmap and concepts pages are closely related to this topic.