From 512cd0be3a88251c138414e8a3dd4941d898de13 Mon Sep 17 00:00:00 2001 From: Luke Waite Date: Fri, 20 Oct 2017 15:03:30 -0400 Subject: [PATCH] Run upgrades in the background, prepare release v1.1.0 --- CHANGELOG.md | 5 +- ...redworkflow => GitLab-1.1.0.alfredworkflow | Bin 168020 -> 171958 bytes README.md | 1 - src/gitlab.py | 49 ++++++---------- src/info.plist | 2 +- src/update.py | 53 ++++++++++++++++++ 6 files changed, 74 insertions(+), 36 deletions(-) rename GitLab-1.0.2.alfredworkflow => GitLab-1.1.0.alfredworkflow (95%) create mode 100644 src/update.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 5814522..59152c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +git stat + ## [v1.0.2] (2017-10-20) * Bump alfred-workflow to 1.28.1 to fix issues @@ -15,7 +17,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * Initial implementaiton of alfred-gitlab workflow -[Unreleased]: https://github.com/lukewaite/alfred-gitlab/compare/v1.0.2...HEAD +[Unreleased]: https://github.com/lukewaite/alfred-gitlab/compare/v1.1.0...HEAD +[v1.1.2]: https://github.com/lukewaite/alfred-gitlab/compare/v1.0.2...v1.1.0 [v1.0.2]: https://github.com/lukewaite/alfred-gitlab/compare/v1.0.1...v1.0.2 [v1.0.1]: https://github.com/lukewaite/alfred-gitlab/compare/v1.0.0...v1.0.1 [v1.0.0]: https://github.com/lukewaite/alfred-gitlab/compare/90b63639ac1d06f9a52c37afd3f9c1da37d6ebd2...v1.0.0 diff --git a/GitLab-1.0.2.alfredworkflow b/GitLab-1.1.0.alfredworkflow similarity index 95% rename from GitLab-1.0.2.alfredworkflow rename to GitLab-1.1.0.alfredworkflow index a31a3ac7524dd288647d03d8672690fce3c25314..6a8a112356de4c288580e7b04ad56b11fd933124 100644 GIT binary patch delta 5939 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 aAUTRP5y(JD1|S1G#s&b88GnQn!2bZny7Z0! delta 2104 zcmZXVc{J4h9>;(4wOmV#eK$=*%h-EnQplPmq^C);Mig=|7)IJmnVE>JgC8@Pid=Dx zd9sWMk;syzWPQ+6s>xDKMtP*3J9Y0p=iYPw_y`^T%PLuTxT40jq( zm zH0v`%oN-pTG~vBv;crK)1}(8iR6tsnbl!vLTN-iG;$~YyoaCK)RN&bYerhS-FNA-_ z5Zb%t(88PNxsLCL)=ElC1&SvrtbtoZ7{~WGw>eN1NIt4LqOF@O)3{7~y3e2>pwq8< z>27Z1#~SwLs>)DcJ3E{m*e2C+*miLEO-v=X=aI?~eO%vf@b)?5uT2`6cb6p#IH9c2 z90#G1g=?8hIYAE=?t_i9oFxjmjtsI}a_8@Sp9FGZCJQU9E*^BYV^ai4Q60lmP6{4k zr9QVLeH|3)mvHn-@0Be~KbEyC&kUdcTe|fr&m?pDKuw=D8#UcuS2>>bB{#%vu_<4$ zzTM(r-_>WUxK_FLPxBqUuGr^pMMdnR-*Lr0u5R~zBHRuV-ZoS(Y6qL)K55M)L^d1< zoju4dE!nqpCq9^7BSCrHT-}QQrMmYq)~h1h&UM%INvpx*yY>ZI15a$1cf(~jR|&E! zcB956`i2UbN}pHDNhN#u4%7P#m1_66s@*WGOhf3s({-pLSj=2gYV5iG@=1eY<%D3I zL(Sp+l_*GlAJ+-gw39qpypZ5MuWAXHy@~UvE*bK?MtUX=NwLkjW9rvrf61L>{t9b+ zY?qAN^(S`1Ka&ne_P%Z4D?2KtuE;;IUi4TM46of4-N59ZBXD1Hzg6-%!T9uEjyX>)a)2=*7(g9(PmHBNYx%~e?C zJ{uaSQm&LyvZ*Tciz_RJcy=p6e zq7I!jh38-96nMw!X<763mK2!>cCKBwrSKRE*Y*if3i- z(HdG9H*^n-qdEp!e?E zWKU;#$%$?Dp1xY~d(drEt`f;o*5(l*7RLnshpBqqO$xGg`6q?MBWH#3SX$6lj4 zg4Dev)+*FpP(fK^!8-4v+fQT}zlq=mcTA@5_^iLF>(-61nY%4}brQ~DwV;3BFJ1a=#XBb%Y4IU`+Q_QuuqMgE~4_n{>r;& zcdBdqsui(^v4V2j8E>_;8Li_oFpUq%{Zq+#T`uZZ$;RO_p>pCI8b@_>F*ZQZWWI1@ zZe$<~*P;z}M}DSd3vbLwmp9DsZW9TEESsO<2PSyp7RY( z_7KP5C!|#CBT}y!qaR(to*oN>U9;7L7D7WzD|!{y9(^-+0ZT;nuqo9NPFi3so@;;L z^%j4F;IJaH%?54FAnOfD9m~{b5_xjAqZV-vGp$<-GwbTb@!wK1m%WbN*a73 zcXJsIn|CbAe)9!ANZg-dc4wmly&0alv~{3mcXQ^)i<4=n?)>McKTI5%UZ!3co_+2Y zSMD4=v`p+qW*R5s!YcLB0k|KGpHh7 z6e8=rl2;|f?3E-L?le6q84)xfD8idjlZuvf{uyA5rn(_=>UGj;<$)WrexUFVb>NJw z=Wygzew^)HOie}*AIIntD%aoUPf3Zg6oHze$m78PVT%?G@g^|{$BZ8eii4;W2RTSy z&t1fhpdZS6MLze@O>_Dgqdgj)WCUfG&(H1}KCOK3WV+asOXW_5^V=xhurGAt_QtSL-j z0ev`z2~Yq>hqIaBI9Oo7Z<)YT>|qVW$mXcQo-AM|HqrqxO1d=RIu^jdCMBXcdkPk? zfCd~^0vyCLUqXzN0(JN{Q)Dmvu%Eq##Y@3)v8q)FK2Zuh!8>L6ZYekhEL32rGT;pi z*l=(e@CDDx;rnI42W$X1jt!LIc|{-zo3eo&pq%}o?3GuBl%Z(RM~_|uqC)=!^+>I> diff --git a/README.md b/README.md index 639993f..6acea57 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ By default, we will only show projects which you are a member of. ## TODOs * Optionally, allow you to search for non-membership repos -* Background the API Fetch updates, so you don't have to wait for them after initial fetch * Add alfred-workflow updater notifications * Clean up diff --git a/src/gitlab.py b/src/gitlab.py index 9be509e..fe076e0 100644 --- a/src/gitlab.py +++ b/src/gitlab.py @@ -1,38 +1,13 @@ # encoding: utf-8 import sys import argparse -from workflow import Workflow, ICON_WEB, ICON_WARNING, web, PasswordNotFound +from workflow import Workflow, ICON_WEB, ICON_WARNING, ICON_INFO, web, PasswordNotFound +from workflow.background import run_in_background, is_running -__version__ = '1.0.2' +__version__ = '1.1.0' log = None -def get_projects(api_key, url): - """ - Parse all pages of projects - :return: list - """ - return get_project_page(api_key, url, 1, []) - -def get_project_page(api_key, url, page, list): - log.info("Calling API page {page}".format(page=page)) - params = dict(private_token=api_key, per_page=100, page=page, membership='true') - r = web.get(url, params) - - # throw an error if request failed - # Workflow will catch this and show it to the user - r.raise_for_status() - - # Parse the JSON returned by GitLab and extract the projects - result = list + r.json() - - nextpage = r.headers.get('X-Next-Page') - if nextpage: - result = get_project_page(api_key, url, nextpage, result) - - return result - - def search_for_project(project): """Generate a string search key for a project""" elements = [] @@ -73,7 +48,7 @@ def main(wf): #################################################################### try: - api_key = wf.get_password('gitlab_api_key') + wf.get_password('gitlab_api_key') except PasswordNotFound: # API key has not yet been set wf.add_item('No API key set.', 'Please use glsetkey to set your GitLab API key.', @@ -100,13 +75,21 @@ def main(wf): query = args.query - def wrapper(): - return get_projects(api_key, api_url) + projects = wf.cached_data('projects', None, max_age=0) - projects = wf.cached_data('projects', wrapper, max_age=3600) + # Start update script if cached data is too old (or doesn't exist) + if not wf.cached_data_fresh('projects', max_age=600): + cmd = ['/usr/bin/python', wf.workflowfile('update.py')] + run_in_background('update', cmd) + + # Notify the user if the cache is being updated + if is_running('update'): + wf.add_item('Updating project list via GitLab', + valid=False, + icon=ICON_INFO) # If script was passed a query, use it to filter projects - if query: + if query and projects: projects = wf.filter(query, projects, key=search_for_project, min_score=20) if not projects: # we have no data to show, so show a warning and stop diff --git a/src/info.plist b/src/info.plist index 1183334..c0c5984 100644 --- a/src/info.plist +++ b/src/info.plist @@ -346,7 +346,7 @@ version - 1.0.2 + 1.1.0 webaddress https://lukewaite.ca diff --git a/src/update.py b/src/update.py new file mode 100644 index 0000000..ff91154 --- /dev/null +++ b/src/update.py @@ -0,0 +1,53 @@ +# encoding: utf-8 + +from workflow import web, Workflow, PasswordNotFound + +def get_projects(api_key, url): + """ + Parse all pages of projects + :return: list + """ + return get_project_page(api_key, url, 1, []) + +def get_project_page(api_key, url, page, list): + log.info("Calling API page {page}".format(page=page)) + params = dict(private_token=api_key, per_page=100, page=page, membership='true') + r = web.get(url, params) + + # throw an error if request failed + # Workflow will catch this and show it to the user + r.raise_for_status() + + # Parse the JSON returned by GitLab and extract the projects + result = list + r.json() + + nextpage = r.headers.get('X-Next-Page') + if nextpage: + result = get_project_page(api_key, url, nextpage, result) + + return result + +def main(wf): + try: + # Get API key from Keychain + api_key = wf.get_password('gitlab_api_key') + api_url = wf.settings.get('api_url', None) + + # Retrieve projects from cache if available and no more than 600 + # seconds old + def wrapper(): + return get_projects(api_key, api_url) + + projects = wf.cached_data('projects', wrapper, max_age=3600) + + # Record our progress in the log file + log.debug('{} gitlab projects cached'.format(len(projects))) + + except PasswordNotFound: # API key has not yet been set + # Nothing we can do about this, so just log it + wf.logger.error('No API key saved') + +if __name__ == u"__main__": + wf = Workflow() + log = wf.logger + wf.run(main) \ No newline at end of file