From b6f1f4231a6b9dfe7e4400629c460cb3b34a8230 Mon Sep 17 00:00:00 2001 From: Jochen Sprickerhof Date: Tue, 16 Nov 2021 14:28:38 +0100 Subject: [PATCH] Drop code for the old wiki --- examples/config.yml | 7 -- fdroidserver/build.py | 63 +--------- fdroidserver/checkupdates.py | 39 ------ fdroidserver/common.py | 23 ---- fdroidserver/deploy.py | 25 ---- fdroidserver/exception.py | 7 -- fdroidserver/update.py | 234 ----------------------------------- jenkins-build-all | 13 +- setup.py | 1 - 9 files changed, 3 insertions(+), 409 deletions(-) diff --git a/examples/config.yml b/examples/config.yml index 8dbe5d44..c66d157f 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -280,13 +280,6 @@ # virustotal_apikey: {env: virustotal_apikey} -# The build logs can be posted to a mediawiki instance, like on f-droid.org. -# wiki_protocol: http -# wiki_server: server -# wiki_path: /wiki/ -# wiki_user: login -# wiki_password: 1234 - # Keep a log of all generated index files in a git repo to provide a # "binary transparency" log for anyone to check the history of the # binaries that are published. This is in the form of a "git remote", diff --git a/fdroidserver/build.py b/fdroidserver/build.py index 13c651ce..e1ae293b 100644 --- a/fdroidserver/build.py +++ b/fdroidserver/build.py @@ -23,7 +23,6 @@ import glob import subprocess import posixpath import re -import sys import tarfile import threading import traceback @@ -1074,12 +1073,6 @@ def main(): app['Builds'] = [build] break - if options.wiki: - import mwclient - site = mwclient.Site((config['wiki_protocol'], config['wiki_server']), - path=config['wiki_path']) - site.login(config['wiki_user'], config['wiki_password']) - # Build applications... failed_builds = [] build_succeeded = [] @@ -1112,8 +1105,6 @@ def main(): else: timer = None - wikilog = None - build_starttime = common.get_wiki_timestamp() tools_version_log = '' if not options.onserver: tools_version_log = common.get_android_tools_version_log() @@ -1205,7 +1196,6 @@ def main(): build_succeeded.append(app) build_succeeded_ids.append([app['id'], build.versionCode]) - wikilog = "Build succeeded" except VCSException as vcse: reason = str(vcse).split('\n', 1)[0] if options.verbose else str(vcse) @@ -1215,17 +1205,17 @@ def main(): logging.debug("Error encoutered, stopping by user request.") common.force_exit(1) add_failed_builds_entry(failed_builds, appid, build, vcse) - wikilog = str(vcse) common.deploy_build_log_with_rsync( appid, build.versionCode, "".join(traceback.format_exc()) ) except FDroidException as e: + tstamp = time.strftime("%Y-%m-%d %H:%M:%SZ", time.gmtime()) with open(os.path.join(log_dir, appid + '.log'), 'a+') as f: f.write('\n\n============================================================\n') f.write('versionCode: %s\nversionName: %s\ncommit: %s\n' % (build.versionCode, build.versionName, build.commit)) f.write('Build completed at ' - + common.get_wiki_timestamp() + '\n') + + tstamp + '\n') f.write('\n' + tools_version_log + '\n') f.write(str(e)) logging.error("Could not build app %s: %s" % (appid, e)) @@ -1233,7 +1223,6 @@ def main(): logging.debug("Error encoutered, stopping by user request.") common.force_exit(1) add_failed_builds_entry(failed_builds, appid, build, e) - wikilog = e.get_wikitext() common.deploy_build_log_with_rsync( appid, build.versionCode, "".join(traceback.format_exc()) ) @@ -1244,36 +1233,10 @@ def main(): logging.debug("Error encoutered, stopping by user request.") common.force_exit(1) add_failed_builds_entry(failed_builds, appid, build, e) - wikilog = str(e) common.deploy_build_log_with_rsync( appid, build.versionCode, "".join(traceback.format_exc()) ) - if options.wiki and wikilog: - try: - # Write a page with the last build log for this version code - lastbuildpage = appid + '/lastbuild_' + build.versionCode - newpage = site.Pages[lastbuildpage] - with open(os.path.join('tmp', 'fdroidserverid')) as fp: - fdroidserverid = fp.read().rstrip() - txt = "* build session started at " + common.get_wiki_timestamp(start_timestamp) + '\n' \ - + "* this build started at " + build_starttime + '\n' \ - + "* this build completed at " + common.get_wiki_timestamp() + '\n' \ - + common.get_git_describe_link() \ - + '* fdroidserverid: [https://gitlab.com/fdroid/fdroidserver/commit/' \ - + fdroidserverid + ' ' + fdroidserverid + ']\n\n' - if buildserverid: - txt += '* buildserverid: [https://gitlab.com/fdroid/fdroidserver/commit/' \ - + buildserverid + ' ' + buildserverid + ']\n\n' - txt += tools_version_log + '\n\n' - txt += '== Build Log ==\n\n' + wikilog - newpage.save(txt, summary='Build log') - # Redirect from /lastbuild to the most recent build log - newpage = site.Pages[appid + '/lastbuild'] - newpage.save('#REDIRECT [[' + lastbuildpage + ']]', summary='Update redirect') - except Exception as e: - logging.error("Error while attempting to publish build log: %s" % e) - if timer: timer.cancel() # kill the watchdog timer @@ -1298,27 +1261,13 @@ def main(): "{} builds failed", len(failed_builds)).format(len(failed_builds))) if options.wiki: - wiki_page_path = 'build_' + time.strftime('%s', start_timestamp) - newpage = site.Pages[wiki_page_path] - txt = '' - txt += "* command line: %s\n" % ' '.join(sys.argv) - txt += "* started at %s\n" % common.get_wiki_timestamp(start_timestamp) - txt += "* completed at %s\n" % common.get_wiki_timestamp() - if buildserverid: - txt += ('* buildserverid: [https://gitlab.com/fdroid/fdroidserver/commit/{id} {id}]\n' - .format(id=buildserverid)) - if fdroidserverid: - txt += ('* fdroidserverid: [https://gitlab.com/fdroid/fdroidserver/commit/{id} {id}]\n' - .format(id=fdroidserverid)) if os.cpu_count(): - txt += "* host processors: %d\n" % os.cpu_count() status_output['hostOsCpuCount'] = os.cpu_count() if os.path.isfile('/proc/meminfo') and os.access('/proc/meminfo', os.R_OK): with open('/proc/meminfo') as fp: for line in fp: m = re.search(r'MemTotal:\s*([0-9].*)', line) if m: - txt += "* host RAM: %s\n" % m.group(1) status_output['hostProcMeminfoMemTotal'] = m.group(1) break fdroid_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) @@ -1328,18 +1277,10 @@ def main(): for line in configfile: m = re.search(r'cpus\s*=\s*([0-9].*)', line) if m: - txt += "* guest processors: %s\n" % m.group(1) status_output['guestVagrantVmCpus'] = m.group(1) m = re.search(r'memory\s*=\s*([0-9].*)', line) if m: - txt += "* guest RAM: %s MB\n" % m.group(1) status_output['guestVagrantVmMemory'] = m.group(1) - txt += "* successful builds: %d\n" % len(build_succeeded) - txt += "* failed builds: %d\n" % len(failed_builds) - txt += "\n\n" - newpage.save(txt, summary='Run log') - newpage = site.Pages['build'] - newpage.save('#REDIRECT [[' + wiki_page_path + ']]', summary='Update redirect') if buildserverid: status_output['buildserver'] = {'commitId': buildserverid} diff --git a/fdroidserver/checkupdates.py b/fdroidserver/checkupdates.py index 9a686126..eb30693e 100644 --- a/fdroidserver/checkupdates.py +++ b/fdroidserver/checkupdates.py @@ -563,38 +563,6 @@ def status_update_json(processed, failed): common.write_status_json(output) -def update_wiki(gplaylog, locallog): - if config.get('wiki_server') and config.get('wiki_path'): - try: - import mwclient - site = mwclient.Site((config['wiki_protocol'], config['wiki_server']), - path=config['wiki_path']) - site.login(config['wiki_user'], config['wiki_password']) - - # Write a page with the last build log for this version code - wiki_page_path = 'checkupdates_' + time.strftime('%s', start_timestamp) - newpage = site.Pages[wiki_page_path] - txt = '' - txt += "* command line: " + ' '.join(sys.argv) + "\n" - txt += common.get_git_describe_link() - txt += "* started at " + common.get_wiki_timestamp(start_timestamp) + '\n' - txt += "* completed at " + common.get_wiki_timestamp() + '\n' - txt += "\n\n" - txt += common.get_android_tools_version_log() - txt += "\n\n" - if gplaylog: - txt += '== --gplay check ==\n\n' - txt += gplaylog - if locallog: - txt += '== local source check ==\n\n' - txt += locallog - newpage.save(txt, summary='Run log') - newpage = site.Pages['checkupdates'] - newpage.save('#REDIRECT [[' + wiki_page_path + ']]', summary='Update redirect') - except Exception as e: - logging.error(_('Error while attempting to publish log: %s') % e) - - config = None options = None start_timestamp = time.gmtime() @@ -635,10 +603,8 @@ def main(): apps = common.read_app_args(options.appid, allapps, False) - gplaylog = '' if options.gplay: for appid, app in apps.items(): - gplaylog += '* ' + appid + '\n' version, reason = check_gplay(app) if version is None: if reason == '404': @@ -660,10 +626,8 @@ def main(): else: logging.info("{0} has the same version {1} on the Play Store" .format(_getappname(app), version)) - update_wiki(gplaylog, None) return - locallog = '' processed = [] failed = dict() exit_code = 0 @@ -675,7 +639,6 @@ def main(): msg = _("Processing {appid}").format(appid=appid) logging.info(msg) - locallog += '* ' + msg + '\n' try: checkupdates_app(app) @@ -684,11 +647,9 @@ def main(): msg = _("...checkupdate failed for {appid} : {error}").format(appid=appid, error=e) logging.error(msg) logging.debug(traceback.format_exc()) - locallog += msg + '\n' failed[appid] = str(e) exit_code = 1 - update_wiki(None, locallog) status_update_json(processed, failed) sys.exit(exit_code) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 5961f687..5a92fba5 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -4100,13 +4100,6 @@ def get_examples_dir(): return examplesdir -def get_wiki_timestamp(timestamp=None): - """Return current time in the standard format for posting to the wiki.""" - if timestamp is None: - timestamp = time.gmtime() - return time.strftime("%Y-%m-%d %H:%M:%SZ", timestamp) - - def get_android_tools_versions(): """Get a list of the versions of all installed Android SDK/NDK components.""" global config @@ -4140,22 +4133,6 @@ def get_android_tools_version_log(): return log -def get_git_describe_link(): - """Get a link to the current fdroiddata commit, to post to the wiki.""" - try: - output = subprocess.check_output(['git', 'describe', '--always', '--dirty', '--abbrev=0'], - universal_newlines=True).strip() - except subprocess.CalledProcessError: - pass - if output: - commit = output.replace('-dirty', '') - return ('* fdroiddata: [https://gitlab.com/fdroid/fdroiddata/commit/{commit} {id}]\n' - .format(commit=commit, id=output)) - else: - logging.error(_("'{path}' failed to execute!").format(path='git describe')) - return '' - - def calculate_math_string(expr): ops = { ast.Add: operator.add, diff --git a/fdroidserver/deploy.py b/fdroidserver/deploy.py index e56030fe..1ce9e141 100644 --- a/fdroidserver/deploy.py +++ b/fdroidserver/deploy.py @@ -672,28 +672,6 @@ def push_binary_transparency(git_repo_path, git_remote): origin.push('master') -def update_wiki(): - try: - import mwclient - site = mwclient.Site((config['wiki_protocol'], config['wiki_server']), - path=config['wiki_path']) - site.login(config['wiki_user'], config['wiki_password']) - - # Write a page with the last build log for this version code - wiki_page_path = 'deploy_' + time.strftime('%s', start_timestamp) - newpage = site.Pages[wiki_page_path] - txt = '' - txt += "* command line: " + ' '.join(sys.argv) + "\n" - txt += "* started at " + common.get_wiki_timestamp(start_timestamp) + '\n' - txt += "* completed at " + common.get_wiki_timestamp() + '\n' - txt += "\n\n" - newpage.save(txt, summary='Run log') - newpage = site.Pages['deploy'] - newpage.save('#REDIRECT [[' + wiki_page_path + ']]', summary='Update redirect') - except Exception as e: - logging.error(_('Error while attempting to publish log: %s') % e) - - def main(): global config, options @@ -811,9 +789,6 @@ def main(): push_binary_transparency(BINARY_TRANSPARENCY_DIR, binary_transparency_remote) - if config.get('wiki_server') and config.get('wiki_path'): - update_wiki() - common.write_status_json(common.setup_status_output(start_timestamp)) sys.exit(0) diff --git a/fdroidserver/exception.py b/fdroidserver/exception.py index 15228797..7e6e32ab 100644 --- a/fdroidserver/exception.py +++ b/fdroidserver/exception.py @@ -8,13 +8,6 @@ class FDroidException(Exception): return self.detail return '[...]\n' + self.detail[-16000:] - def get_wikitext(self): - ret = repr(self.value) + "\n" - if self.detail: - ret += "=detail=\n" - ret += "
\n" + self.shortened_detail() + "
\n" - return ret - def __str__(self): if self.value is None: ret = __name__ diff --git a/fdroidserver/update.py b/fdroidserver/update.py index f08a830f..4c217180 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -199,236 +199,6 @@ def status_update_json(apps, apks): common.write_status_json(output, options.pretty) -def update_wiki(apps, apks): - """Update the wiki. - - Parameters - ---------- - apps - fully populated list of all applications - apks - all apks, except... - """ - logging.info("Updating wiki") - wikicat = 'Apps' - wikiredircat = 'App Redirects' - import mwclient - site = mwclient.Site((config['wiki_protocol'], config['wiki_server']), - path=config['wiki_path']) - site.login(config['wiki_user'], config['wiki_password']) - generated_pages = {} - generated_redirects = {} - - for appid in apps: - app = metadata.App(apps[appid]) - - wikidata = '' - if app.Disabled: - wikidata += '{{Disabled|' + app.Disabled + '}}\n' - if app.AntiFeatures: - for af in sorted(app.AntiFeatures): - wikidata += '{{AntiFeature|' + af + '}}\n' - if app.RequiresRoot: - requiresroot = 'Yes' - else: - requiresroot = 'No' - - apppagename = common.get_app_display_name(app) - - wikidata += '{{App|id=%s|name=%s|added=%s|lastupdated=%s|source=%s|tracker=%s|web=%s|changelog=%s|donate=%s|flattr=%s|liberapay=%s|bitcoin=%s|litecoin=%s|license=%s|root=%s|author=%s|email=%s}}\n' % ( - appid, - apppagename, - app.added.strftime('%Y-%m-%d') if app.added else '', - app.lastUpdated.strftime('%Y-%m-%d') if app.lastUpdated else '', - app.SourceCode, - app.IssueTracker, - app.WebSite, - app.Changelog, - app.Donate, - app.FlattrID, - app.LiberapayID, - app.Bitcoin, - app.Litecoin, - app.License, - requiresroot, - app.AuthorName, - app.AuthorEmail) - - if app.Provides: - wikidata += "This app provides: %s" % ', '.join(app.Summary.split(',')) - - wikidata += app.Summary - wikidata += " - [https://f-droid.org/repository/browse/?fdid=" + appid + " view in repository]\n\n" - - wikidata += "=Description=\n" - wikidata += app.Description + "\n" - - wikidata += "=Maintainer Notes=\n" - if app.MaintainerNotes: - wikidata += app.MaintainerNotes + "\n" - wikidata += "\nMetadata: [https://gitlab.com/fdroid/fdroiddata/blob/master/metadata/{0}.yml current] [https://gitlab.com/fdroid/fdroiddata/commits/master/metadata/{0}.yml history]\n".format(appid) - - # Get a list of all packages for this application... - apklist = [] - gotcurrentver = False - cantupdate = False - buildfails = False - for apk in apks: - if apk['packageName'] == appid: - if str(apk['versionCode']) == app.get('CurrentVersionCode'): - gotcurrentver = True - apklist.append(apk) - # Include ones we can't build, as a special case... - for build in app.get('Builds', []): - if build.disable: - if build.versionCode == app.get('CurrentVersionCode'): - cantupdate = True - # TODO: Nasty: vercode is a string in the build, and an int elsewhere - apklist.append({'versionCode': int(build.versionCode), - 'versionName': build.versionName, - 'buildproblem': "The build for this version was manually disabled. Reason: {0}".format(build.disable), - }) - else: - builtit = False - for apk in apklist: - if apk['versionCode'] == int(build.versionCode): - builtit = True - break - if not builtit: - buildfails = True - apklist.append({'versionCode': int(build.versionCode), - 'versionName': build.versionName, - 'buildproblem': "The build for this version appears to have failed. Check the [[{0}/lastbuild_{1}|build log]].".format(appid, build.versionCode), - }) - if app.get('CurrentVersionCode') == '0': - cantupdate = True - # Sort with most recent first... - apklist = sorted(apklist, key=lambda apk: apk['versionCode'], reverse=True) - - wikidata += "=Versions=\n" - if len(apklist) == 0: - wikidata += "We currently have no versions of this app available." - elif not gotcurrentver: - wikidata += "We don't have the current version of this app." - else: - wikidata += "We have the current version of this app." - wikidata += " (Check mode: " + app.UpdateCheckMode + ") " - wikidata += " (Auto-update mode: " + app.AutoUpdateMode + ")\n\n" - if len(app.NoSourceSince) > 0: - wikidata += "This application has partially or entirely been missing source code since version " + app.NoSourceSince + ".\n\n" - if len(app.CurrentVersion) > 0: - wikidata += "The current (recommended) version is " + app.CurrentVersion - wikidata += " (version code " + app.CurrentVersionCode + ").\n\n" - validapks = 0 - for apk in apklist: - wikidata += "==" + apk['versionName'] + "==\n" - - if 'buildproblem' in apk: - wikidata += "We can't build this version: " + apk['buildproblem'] + "\n\n" - else: - validapks += 1 - wikidata += "This version is built and signed by " - if 'srcname' in apk: - wikidata += "F-Droid, and guaranteed to correspond to the source tarball published with it.\n\n" - else: - wikidata += "the original developer.\n\n" - wikidata += "Version code: " + str(apk['versionCode']) + '\n' - - wikidata += '\n[[Category:' + wikicat + ']]\n' - if len(app.NoSourceSince) > 0: - wikidata += '\n[[Category:Apps missing source code]]\n' - if validapks == 0 and not app.Disabled: - wikidata += '\n[[Category:Apps with no packages]]\n' - if cantupdate and not app.Disabled: - wikidata += "\n[[Category:Apps we cannot update]]\n" - if buildfails and not app.Disabled: - wikidata += "\n[[Category:Apps with failing builds]]\n" - elif not gotcurrentver and not cantupdate and not app.Disabled and app.UpdateCheckMode != "Static": - wikidata += '\n[[Category:Apps to Update]]\n' - if app.Disabled: - wikidata += '\n[[Category:Apps that are disabled]]\n' - if app.UpdateCheckMode == 'None' and not app.Disabled: - wikidata += '\n[[Category:Apps with no update check]]\n' - for appcat in app.Categories: - wikidata += '\n[[Category:{0}]]\n'.format(appcat) - - # We can't have underscores in the page name, even if they're in - # the package ID, because MediaWiki messes with them... - pagename = appid.replace('_', ' ') - - # Drop a trailing newline, because mediawiki is going to drop it anyway - # and it we don't we'll think the page has changed when it hasn't... - if wikidata.endswith('\n'): - wikidata = wikidata[:-1] - - generated_pages[pagename] = wikidata - - # Make a redirect from the name to the ID too, unless there's - # already an existing page with the name and it isn't a redirect. - noclobber = False - for ch in '_{}:[]|': - apppagename = apppagename.replace(ch, ' ') - # Drop double spaces caused mostly by replacing ':' above - apppagename = apppagename.replace(' ', ' ') - for expagename in site.allpages(prefix=apppagename, - filterredir='nonredirects', - generator=False): - if expagename == apppagename: - noclobber = True - # Another reason not to make the redirect page is if the app name - # is the same as it's ID, because that will overwrite the real page - # with an redirect to itself! (Although it seems like an odd - # scenario this happens a lot, e.g. where there is metadata but no - # builds or binaries to extract a name from. - if apppagename == pagename: - noclobber = True - if not noclobber: - generated_redirects[apppagename] = "#REDIRECT [[" + pagename + "]]\n[[Category:" + wikiredircat + "]]" - - for tcat, genp in [(wikicat, generated_pages), - (wikiredircat, generated_redirects)]: - catpages = site.Pages['Category:' + tcat] - existingpages = [] - for page in catpages: - existingpages.append(page.name) - if page.name in genp: - pagetxt = page.text() - if pagetxt != genp[page.name]: - logging.debug("Updating modified page " + page.name) - page.save(genp[page.name], summary='Auto-updated') - else: - logging.debug("Page " + page.name + " is unchanged") - else: - logging.warning('Deleting page ' + page.name) - page.delete('No longer published') - for pagename, text in genp.items(): - logging.debug("Checking " + pagename) - if pagename not in existingpages: - logging.debug("Creating page " + pagename) - try: - newpage = site.Pages[pagename] - newpage.save(text, summary='Auto-created') - except Exception as e: - logging.error("...FAILED to create page '{0}': {1}".format(pagename, e)) - - # Purge server cache to ensure counts are up to date - site.Pages['Repository Maintenance'].purge() - - # Write a page with the last build log for this version code - wiki_page_path = 'update_' + time.strftime('%s', start_timestamp) - newpage = site.Pages[wiki_page_path] - txt = '' - txt += "* command line: " + ' '.join(sys.argv) + "\n" - txt += "* started at " + common.get_wiki_timestamp(start_timestamp) + '\n' - txt += "* completed at " + common.get_wiki_timestamp() + '\n' - txt += common.get_git_describe_link() - txt += "\n\n" - txt += common.get_android_tools_version_log() - newpage.save(txt, summary='Run log') - newpage = site.Pages['update'] - newpage.save('#REDIRECT [[' + wiki_page_path + ']]', summary='Update redirect') - - def delete_disabled_builds(apps, apkcache, repodirs): """Delete disabled build outputs. @@ -2503,10 +2273,6 @@ def main(): # Update known apks info... knownapks.writeifchanged() - # Update the wiki... - if options.wiki: - logging.warning(_('wiki support is deprecated and will be removed in the next release!')) - update_wiki(apps, apks + archapks) status_update_json(apps, apks + archapks) logging.info(_("Finished")) diff --git a/jenkins-build-all b/jenkins-build-all index ee6a9850..aa663fc5 100755 --- a/jenkins-build-all +++ b/jenkins-build-all @@ -83,17 +83,6 @@ fi echo "build_server_always: true" > config.yml echo "deploy_process_logs: true" >> config.yml -# if the local mediawiki is available, then use it -if nc -z -w1 localhost 32445; then - wikiflag="--wiki" - echo "wiki_protocol: http" >> config.yml - echo "wiki_server: localhost:32445" >> config.yml - echo "wiki_path: /mediawiki/" >> config.yml - echo "wiki_user: fdroid" >> config.yml - echo "wiki_password: update.TestCase" >> config.yml -else - sed -i '/^wiki_/d' config.yml -fi printf '\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\nbuild all with reproducible signatures\n' for f in metadata/*/signatures/*; do @@ -114,7 +103,7 @@ sed -Ei 's,^(\s+endtime\s*=\s*time\.time\(\))\s*.*,\1 + 6 * 60 * 60 # 6 hours,' $WORKSPACE/fdroidserver/build.py printf '\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\nbuild all\n' -$WORKSPACE/fdroid build --verbose --latest --no-tarball --all $wikiflag +$WORKSPACE/fdroid build --verbose --latest --no-tarball --all vagrant global-status if [ -d builder ]; then diff --git a/setup.py b/setup.py index dfb7ba7d..ae7ec2db 100755 --- a/setup.py +++ b/setup.py @@ -74,7 +74,6 @@ setup(name='fdroidserver', 'clint', 'defusedxml', 'GitPython', - 'mwclient', 'paramiko', 'Pillow', 'apache-libcloud >= 0.14.1',