# HG changeset patch # User drewp@bigasterisk.com # Date 1641716501 28800 # Node ID 6f38aa08408d71dc357de1354b943b0655aa9ce5 # Parent a4778c56cc03acf9802f69c3934c4ef946a5fa3c starting over: make a web page that draws a streamed graph from collector, with plans for services to scrape the data that collector will subscribe to diff -r a4778c56cc03 -r 6f38aa08408d .hgignore --- a/.hgignore Sun Dec 12 22:32:25 2021 -0800 +++ b/.hgignore Sun Jan 09 00:21:41 2022 -0800 @@ -1,4 +1,5 @@ dot-ssh __pycache__ config.yaml -config.json \ No newline at end of file +config.json +node_modules diff -r a4778c56cc03 -r 6f38aa08408d Dockerfile --- a/Dockerfile Sun Dec 12 22:32:25 2021 -0800 +++ b/Dockerfile Sun Jan 09 00:21:41 2022 -0800 @@ -5,7 +5,10 @@ RUN apt-get install -y python3 libpython3-dev python3-openssl python3-cffi-backend RUN python3 -m pip install mercurial -RUN python3 -m pip install -U pip +RUN npm install -g npm@8.3.0 +RUN npm install -g pnpm +RUN pnpm set registry "https://bigasterisk.com/js/" + COPY requirements.txt ./ RUN python3 -m pip install --index-url https://projects.bigasterisk.com/ --extra-index-url https://pypi.org/simple -r requirements.txt RUN python3 -m pip install -U 'https://github.com/drewp/cyclone/archive/python3.zip?v3' @@ -13,9 +16,13 @@ RUN python3 -m pip install 'https://foss.heptapod.net/mercurial/hg-git/-/archive/branch/default/hg-git-branch-default.zip' RUN groupadd --gid 1000 drewp && useradd --uid 501 --gid 1000 drewp +COPY package.json . +RUN pnpm install + COPY root-hgrc /root/.hgrc -COPY config.yaml hg_status.py index.html ./ COPY dot-ssh/* /root/.ssh/ +COPY config.yaml package.json pnpm-lock.yaml tsconfig.json vite.config.ts repo*.py patch_cyclone_sse.py index.html ./ +COPY view/ ./view #USER drewp -CMD ["python3", "hg_status.py", "-v"] +CMD ["python3", "repo_local_status.py", "-v"] diff -r a4778c56cc03 -r 6f38aa08408d deploy.yaml --- a/deploy.yaml Sun Dec 12 22:32:25 2021 -0800 +++ b/deploy.yaml Sun Jan 09 00:21:41 2022 -0800 @@ -13,24 +13,33 @@ app: reposync spec: containers: - - name: reposync + - name: view image: bang5:5000/reposync_image - imagePullPolicy: "Always" - securityContext: {capabilities: {add: [SYS_PTRACE]}} + command: + - pnpx + - vite + - --mode=dev ports: - - containerPort: 10001 - volumeMounts: - - {name: my, mountPath: /my} + - containerPort: 3000 + # - name: reposync + # image: bang5:5000/reposync_image + # imagePullPolicy: "Always" + # securityContext: {capabilities: {add: [SYS_PTRACE]}} + # ports: + # - containerPort: 10001 + # volumeMounts: + # - {name: my, mountPath: /my} + volumes: - { name: my, persistentVolumeClaim: { claimName: my } } affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - - matchExpressions: - - key: "kubernetes.io/hostname" - operator: In - values: ["bang"] + - matchExpressions: + - key: "kubernetes.io/hostname" + operator: In + values: ["bang"] --- apiVersion: v1 kind: Service @@ -38,8 +47,6 @@ name: reposync spec: ports: - - {port: 80, targetPort: 10001, name: http} + - { port: 80, targetPort: 3000, name: http } selector: app: reposync - - diff -r a4778c56cc03 -r 6f38aa08408d hg_status.py --- a/hg_status.py Sun Dec 12 22:32:25 2021 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,217 +0,0 @@ -import datetime -import json -import time -import traceback -from dataclasses import dataclass, field -from pathlib import Path -from typing import Dict, Optional, Tuple - -import cyclone.httpserver -import cyclone.sse -import cyclone.web -import docopt -import treq -import tzlocal -from cycloneerr import PrettyErrorHandler -from dateutil.parser import parse -from dateutil.tz import tzlocal -from prometheus_client.exposition import generate_latest -from prometheus_client.registry import REGISTRY -from ruamel.yaml import YAML -from standardservice.logsetup import log, verboseLogging -from twisted.internet import reactor -from twisted.internet.defer import inlineCallbacks, returnValue -from twisted.internet.utils import _UnexpectedErrorOutput, getProcessOutput - -githubOwner = 'drewp' - - -@inlineCallbacks -def runHg(cwd, args): - if args[0] not in ['push']: - args.extend(['-T', 'json']) - j = yield getProcessOutput('/usr/local/bin/hg', args, path=cwd) - returnValue(json.loads(j) if j else None) - - -@dataclass -class Repo: - path: Path - github: bool - _cache: Dict[str, Tuple[float, object]] = field(default_factory=dict) - - def _isStale(self, group) -> Optional[object]: - now = time.time() - if group not in self._cache: - return True - if now > self._cache[group][0] + 86400: - return True - print('fresh') - return False - - def _save(self, group, obj): - now = time.time() - self._cache[group] = (now, obj) - - def _get(self, group): - print('get') - return self._cache[group][1] - - @inlineCallbacks - def getStatus(self): - if self._isStale('status'): - try: - statusResp = yield runHg(self.path, ['status']) - except Exception as e: - status = {'error': repr(e)} - else: - unknowns = len([row for row in statusResp if row['status'] == '?']) - status = {'unknown': unknowns, 'changed': len(statusResp) - unknowns} - self._save('status', status) - returnValue(self._get('status')) - - @inlineCallbacks - def getLatestHgCommit(self): - if self._isStale('log'): - rows = yield runHg(self.path, ['log', '--limit', '1']) - commit = rows[0] - sec = commit['date'][0] - t = datetime.datetime.fromtimestamp(sec, tzlocal()) - self._save('log', {'email': commit['user'], 't': t.isoformat(), 'message': commit['desc']}) - returnValue(self._get('log')) - - @inlineCallbacks - def getLatestGithubCommit(self): - if self._isStale('github'): - resp = yield treq.get(f'https://api.github.com/repos/{githubOwner}/{self.path.name}/commits?per_page=1', - timeout=5, - headers={ - 'User-agent': 'reposync by github.com/drewp', - 'Accept': 'application/vnd.github.v3+json' - }) - ret = yield treq.json_content(resp) - commit = ret[0]['commit'] - t = parse(commit['committer']['date']).astimezone(tzlocal()).isoformat() - self._save('github', {'email': commit['committer']['email'], 't': t, 'message': commit['message']}) - returnValue(self._get('github')) - - @inlineCallbacks - def clearGithubMaster(self): - '''bang(pts/13):/tmp/reset% git init -Initialized empty Git repository in /tmp/reset/.git/ -then github set current to a new branch called 'clearing' with https://developer.github.com/v3/repos/#update-a-repository -bang(pts/13):/tmp/reset% git remote add origin git@github.com:drewp/href.git -bang(pts/13):/tmp/reset% git push origin :master -To github.com:drewp/href.git - - [deleted] master -maybe --set-upstream origin -bang(pts/13):/tmp/reset% git remote set-branches origin master -? -then push -then github setdefault to master -then github delete clearing -''' - - @inlineCallbacks - def pushToGithub(self): - if not self.github: - raise ValueError - yield runHg(self.path, ['bookmark', '--rev', 'default', 'master']) - out = yield runHg(self.path, ['push', f'git+ssh://git@github.com/{githubOwner}/{self.path.name}.git']) - print(f'out fompushh {out}') - - -class GithubSync(PrettyErrorHandler, cyclone.web.RequestHandler): - - @inlineCallbacks - def post(self): - try: - path = self.get_argument('repo') - repo = [r for r in self.settings.repos if str(r.path) == path][0] - yield repo.pushToGithub() - except Exception: - traceback.print_exc() - raise - - -class Statuses(cyclone.sse.SSEHandler): - - def update(self, key, data): - self.sendEvent(json.dumps({'key': key, 'update': data}).encode('utf8')) - - def bind(self): - self.toProcess = self.settings.repos[:] - reactor.callLater(0, self.runOne) - - @inlineCallbacks - def runOne(self): - if not self.toProcess: - print('done') - return - repo = self.toProcess.pop(0) - - try: - update = {'path': str(repo.path), 'github': repo.github, 'status': (yield repo.getStatus()), 'hgLatest': (yield repo.getLatestHgCommit())} - if repo.github: - update['githubLatest'] = (yield repo.getLatestGithubCommit()) - self.update(str(repo.path), update) - except Exception: - log.warn(f'not reporting on {repo}') - traceback.print_exc() - reactor.callLater(0, self.runOne) - - -class Metrics(cyclone.web.RequestHandler): - - def get(self): - self.add_header('content-type', 'text/plain') - self.write(generate_latest(REGISTRY)) - - -def main(): - args = docopt.docopt(''' -Usage: - hg_status.py [options] - -Options: - -v, --verbose more logging -''') - verboseLogging(args['--verbose']) - - # import sys - # sys.path.append('/usr/lib/python3/dist-packages') - # import OpenSSL - - yaml = YAML(typ='safe') - config = yaml.load(open('config.yaml')) - repos = [Repo(Path(row['dir']), row['github']) for row in config['hg_repos']] - - class Application(cyclone.web.Application): - - def __init__(self): - handlers = [ - (r"/()", cyclone.web.StaticFileHandler, { - 'path': '.', - 'default_filename': 'index.html' - }), - (r'/build/(bundle\.js)', cyclone.web.StaticFileHandler, { - 'path': './build/' - }), - (r'/status/events', Statuses), - (r'/githubSync', GithubSync), - (r'/metrics', Metrics), - ] - cyclone.web.Application.__init__( - self, - handlers, - repos=repos, - debug=args['--verbose'], - template_path='.', - ) - - reactor.listenTCP(10001, Application()) - reactor.run() - - -if __name__ == '__main__': - main() diff -r a4778c56cc03 -r 6f38aa08408d index.html --- a/index.html Sun Dec 12 22:32:25 2021 -0800 +++ b/index.html Sun Jan 09 00:21:41 2022 -0800 @@ -1,93 +1,24 @@ - + - - + + reposync + + -

repo statuses

- - - - - - +
+      notes: 
+
       repo1 clean                     synced to github   github up to date  [sync now]
             dirty [msg____] [commit]
 
@@ -110,5 +41,33 @@
       Here, keys are repo paths.
     
     
+ + diff -r a4778c56cc03 -r 6f38aa08408d package.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/package.json Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,19 @@ +{ + "name": "reposync", + "version": "1.0.0", + "description": "Mirrors a directory of darcs repos onto github.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lit": "^2.1.1", + "streamed-graph": "^0.0.4", + "vite": "^2.7.10" + }, + "devDependencies": { + "@types/node": "^17.0.8" + } +} diff -r a4778c56cc03 -r 6f38aa08408d patch_cyclone_sse.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patch_cyclone_sse.py Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,21 @@ +def patchCycloneSse(): + import cyclone.sse + from cyclone import escape + + def sendEvent(self, message, event=None, eid=None, retry=None): + if isinstance(message, dict): + message = escape.json_encode(message) + if isinstance(message, str): + message = message.encode("utf-8") + assert isinstance(message, bytes) + + if eid: + self.transport.write(b"id: %s\n" % eid) + if event: + self.transport.write(b"event: %s\n" % event) + if retry: + self.transport.write(b"retry: %s\n" % retry) + + self.transport.write(b"data: %s\n\n" % message) + + cyclone.sse.SSEHandler.sendEvent = sendEvent diff -r a4778c56cc03 -r 6f38aa08408d pnpm-lock.yaml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pnpm-lock.yaml Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,486 @@ +lockfileVersion: 5.3 + +specifiers: + '@types/node': ^17.0.8 + lit: ^2.1.1 + streamed-graph: ^0.0.4 + vite: ^2.7.10 + +dependencies: + lit: 2.1.1 + streamed-graph: 0.0.4 + +devDependencies: + '@types/node': 17.0.8 + vite: 2.7.10 + +packages: + + /@digitalbazaar/http-client/1.2.0: + resolution: {integrity: sha512-W9KQQ5pUJcaR0I4c2HPJC0a7kRbZApIorZgPnEDwMBgj16iQzutGLrCXYaZOmxqVLVNqqlQ4aUJh+HBQZy4W6Q==} + engines: {node: '>=10.0.0'} + dependencies: + esm: 3.2.25 + ky: 0.25.1 + ky-universal: 0.8.2_ky@0.25.1 + transitivePeerDependencies: + - domexception + - web-streams-polyfill + dev: false + + /@lit/reactive-element/1.1.1: + resolution: {integrity: sha512-B2JdRMwCGv+VpIRj3CYVQBx3muPDeE8y+HPgWqzrAHsO5/40BpwDFZeplIV790BaTqDVUDvZOKMSbuFM9zWC0w==} + dev: false + + /@rdfjs/types/1.0.1: + resolution: {integrity: sha512-YxVkH0XrCNG3MWeZxfg596GFe+oorTVusmNxRP6ZHTsGczZ8AGvG3UchRNkg3Fy4MyysI7vBAA5YZbESL+VmHQ==} + dependencies: + '@types/node': 17.0.8 + dev: false + + /@types/jsonld/1.5.6: + resolution: {integrity: sha512-OUcfMjRie5IOrJulUQwVNvV57SOdKcTfBj3pjXNxzXqeOIrY2aGDNGW/Tlp83EQPkz4tCE6YWVrGuc/ZeaAQGg==} + dev: false + + /@types/n3/1.10.4: + resolution: {integrity: sha512-FfRTwcbXcScVHuAjIASveRWL6Fi6fPALl1Ge8tMESYLqU7R42LJvtdBpUi+f9YK0oQPqIN+zFFgMDFJfLMx0bg==} + dependencies: + '@types/node': 17.0.8 + rdf-js: 4.0.2 + dev: false + + /@types/node/17.0.8: + resolution: {integrity: sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==} + + /@types/trusted-types/2.0.2: + resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} + dev: false + + /abort-controller/3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: false + + /canonicalize/1.0.8: + resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} + dev: false + + /data-uri-to-buffer/3.0.1: + resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} + engines: {node: '>= 6'} + dev: false + + /esbuild-android-arm64/0.13.15: + resolution: {integrity: sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==} + cpu: [arm64] + os: [android] + dev: true + optional: true + + /esbuild-darwin-64/0.13.15: + resolution: {integrity: sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==} + cpu: [x64] + os: [darwin] + dev: true + optional: true + + /esbuild-darwin-arm64/0.13.15: + resolution: {integrity: sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==} + cpu: [arm64] + os: [darwin] + dev: true + optional: true + + /esbuild-freebsd-64/0.13.15: + resolution: {integrity: sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==} + cpu: [x64] + os: [freebsd] + dev: true + optional: true + + /esbuild-freebsd-arm64/0.13.15: + resolution: {integrity: sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==} + cpu: [arm64] + os: [freebsd] + dev: true + optional: true + + /esbuild-linux-32/0.13.15: + resolution: {integrity: sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==} + cpu: [ia32] + os: [linux] + dev: true + optional: true + + /esbuild-linux-64/0.13.15: + resolution: {integrity: sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==} + cpu: [x64] + os: [linux] + dev: true + optional: true + + /esbuild-linux-arm/0.13.15: + resolution: {integrity: sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==} + cpu: [arm] + os: [linux] + dev: true + optional: true + + /esbuild-linux-arm64/0.13.15: + resolution: {integrity: sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==} + cpu: [arm64] + os: [linux] + dev: true + optional: true + + /esbuild-linux-mips64le/0.13.15: + resolution: {integrity: sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==} + cpu: [mips64el] + os: [linux] + dev: true + optional: true + + /esbuild-linux-ppc64le/0.13.15: + resolution: {integrity: sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==} + cpu: [ppc64] + os: [linux] + dev: true + optional: true + + /esbuild-netbsd-64/0.13.15: + resolution: {integrity: sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==} + cpu: [x64] + os: [netbsd] + dev: true + optional: true + + /esbuild-openbsd-64/0.13.15: + resolution: {integrity: sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==} + cpu: [x64] + os: [openbsd] + dev: true + optional: true + + /esbuild-sunos-64/0.13.15: + resolution: {integrity: sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==} + cpu: [x64] + os: [sunos] + dev: true + optional: true + + /esbuild-windows-32/0.13.15: + resolution: {integrity: sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==} + cpu: [ia32] + os: [win32] + dev: true + optional: true + + /esbuild-windows-64/0.13.15: + resolution: {integrity: sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==} + cpu: [x64] + os: [win32] + dev: true + optional: true + + /esbuild-windows-arm64/0.13.15: + resolution: {integrity: sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==} + cpu: [arm64] + os: [win32] + dev: true + optional: true + + /esbuild/0.13.15: + resolution: {integrity: sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==} + hasBin: true + requiresBuild: true + optionalDependencies: + esbuild-android-arm64: 0.13.15 + esbuild-darwin-64: 0.13.15 + esbuild-darwin-arm64: 0.13.15 + esbuild-freebsd-64: 0.13.15 + esbuild-freebsd-arm64: 0.13.15 + esbuild-linux-32: 0.13.15 + esbuild-linux-64: 0.13.15 + esbuild-linux-arm: 0.13.15 + esbuild-linux-arm64: 0.13.15 + esbuild-linux-mips64le: 0.13.15 + esbuild-linux-ppc64le: 0.13.15 + esbuild-netbsd-64: 0.13.15 + esbuild-openbsd-64: 0.13.15 + esbuild-sunos-64: 0.13.15 + esbuild-windows-32: 0.13.15 + esbuild-windows-64: 0.13.15 + esbuild-windows-arm64: 0.13.15 + dev: true + + /esm/3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + dev: false + + /event-target-shim/5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + dev: false + + /fetch-blob/2.1.2: + resolution: {integrity: sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==} + engines: {node: ^10.17.0 || >=12.3.0} + peerDependencies: + domexception: '*' + peerDependenciesMeta: + domexception: + optional: true + dev: false + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + dev: true + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /immutable/4.0.0: + resolution: {integrity: sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==} + dev: false + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /is-core-module/2.8.1: + resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==} + dependencies: + has: 1.0.3 + dev: true + + /jsonld/5.2.0: + resolution: {integrity: sha512-JymgT6Xzk5CHEmHuEyvoTNviEPxv6ihLWSPu1gFdtjSAyM6cFqNrv02yS/SIur3BBIkCf0HjizRc24d8/FfQKw==} + engines: {node: '>=12'} + dependencies: + '@digitalbazaar/http-client': 1.2.0 + canonicalize: 1.0.8 + lru-cache: 6.0.0 + rdf-canonize: 3.0.0 + transitivePeerDependencies: + - domexception + - web-streams-polyfill + dev: false + + /ky-universal/0.8.2_ky@0.25.1: + resolution: {integrity: sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ==} + engines: {node: '>=10.17'} + peerDependencies: + ky: '>=0.17.0' + web-streams-polyfill: '>=2.0.0' + peerDependenciesMeta: + web-streams-polyfill: + optional: true + dependencies: + abort-controller: 3.0.0 + ky: 0.25.1 + node-fetch: 3.0.0-beta.9 + transitivePeerDependencies: + - domexception + dev: false + + /ky/0.25.1: + resolution: {integrity: sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA==} + engines: {node: '>=10'} + dev: false + + /lit-element/3.1.1: + resolution: {integrity: sha512-14ClnMAU8EXnzC+M2/KDd3SFmNUn1QUw1+GxWkEMwGV3iaH8ObunMlO5svzvaWlkSV0WlxJCi40NGnDVJ2XZKQ==} + dependencies: + '@lit/reactive-element': 1.1.1 + lit-html: 2.1.1 + dev: false + + /lit-html/2.1.1: + resolution: {integrity: sha512-E4BImK6lopAYanJpvcGaAG8kQFF1ccIulPu2BRNZI7acFB6i4ujjjsnaPVFT1j/4lD9r8GKih0Y8d7/LH8SeyQ==} + dependencies: + '@types/trusted-types': 2.0.2 + dev: false + + /lit/2.1.1: + resolution: {integrity: sha512-yqDqf36IhXwOxIQSFqCMgpfvDCRdxLCLZl7m/+tO5C9W/OBHUj17qZpiMBT35v97QMVKcKEi1KZ3hZRyTwBNsQ==} + dependencies: + '@lit/reactive-element': 1.1.1 + lit-element: 3.1.1 + lit-html: 2.1.1 + dev: false + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: false + + /n3/1.12.2: + resolution: {integrity: sha512-vY1HBEraMPWQFLEK6sn67DeGMqTuwXnlEYpZ8gTVukKQSz2f44d+t+ZcmwEt8c99FlVAbpmMb/435Q8t0OC+7w==} + engines: {node: '>=8.0'} + dependencies: + queue-microtask: 1.2.3 + readable-stream: 3.6.0 + dev: false + + /nanoid/3.1.30: + resolution: {integrity: sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /node-fetch/3.0.0-beta.9: + resolution: {integrity: sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==} + engines: {node: ^10.17 || >=12.3} + dependencies: + data-uri-to-buffer: 3.0.1 + fetch-blob: 2.1.2 + transitivePeerDependencies: + - domexception + dev: false + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /picocolors/1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /postcss/8.4.5: + resolution: {integrity: sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.1.30 + picocolors: 1.0.0 + source-map-js: 1.0.1 + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: false + + /rdf-canonize/3.0.0: + resolution: {integrity: sha512-LXRkhab1QaPJnhUIt1gtXXKswQCZ9zpflsSZFczG7mCLAkMvVjdqCGk9VXCUss0aOUeEyV2jtFxGcdX8DSkj9w==} + engines: {node: '>=12'} + dependencies: + setimmediate: 1.0.5 + dev: false + + /rdf-js/4.0.2: + resolution: {integrity: sha512-ApvlFa/WsQh8LpPK/6hctQwG06Z9ztQQGWVtrcrf9L6+sejHNXLPOqL+w7q3hF+iL0C4sv3AX1PUtGkLNzyZ0Q==} + dependencies: + '@rdfjs/types': 1.0.1 + dev: false + + /readable-stream/3.6.0: + resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false + + /resolve/1.21.0: + resolution: {integrity: sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==} + hasBin: true + dependencies: + is-core-module: 2.8.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /rollup/2.63.0: + resolution: {integrity: sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /setimmediate/1.0.5: + resolution: {integrity: sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=} + dev: false + + /source-map-js/1.0.1: + resolution: {integrity: sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==} + engines: {node: '>=0.10.0'} + dev: true + + /streamed-graph/0.0.4: + resolution: {integrity: sha512-uatILnTW0GKmSf21MoiRelrrw1D8rFYajIXmWBSW90jINMFlzdYA03gWFeB4IFKFs/RTeqbgOr1OPQOQwJ6Lgw==} + dependencies: + '@types/jsonld': 1.5.6 + '@types/n3': 1.10.4 + immutable: 4.0.0 + jsonld: 5.2.0 + lit: 2.1.1 + n3: 1.12.2 + rdf-js: 4.0.2 + transitivePeerDependencies: + - domexception + - web-streams-polyfill + dev: false + + /string_decoder/1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /util-deprecate/1.0.2: + resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + dev: false + + /vite/2.7.10: + resolution: {integrity: sha512-KEY96ntXUid1/xJihJbgmLZx7QSC2D4Tui0FdS0Old5OokYzFclcofhtxtjDdGOk/fFpPbHv9yw88+rB93Tb8w==} + engines: {node: '>=12.2.0'} + hasBin: true + peerDependencies: + less: '*' + sass: '*' + stylus: '*' + peerDependenciesMeta: + less: + optional: true + sass: + optional: true + stylus: + optional: true + dependencies: + esbuild: 0.13.15 + postcss: 8.4.5 + resolve: 1.21.0 + rollup: 2.63.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: false diff -r a4778c56cc03 -r 6f38aa08408d pydeps --- a/pydeps Sun Dec 12 22:32:25 2021 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -PyGithub==1.14.2 diff -r a4778c56cc03 -r 6f38aa08408d repo_github_status.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/repo_github_status.py Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,3 @@ +""" +repos from config.yaml that are at github -> rdf data +""" \ No newline at end of file diff -r a4778c56cc03 -r 6f38aa08408d repo_local_status.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/repo_local_status.py Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,223 @@ +""" +configured hg dirs and settings -> rdf graph +""" +import datetime +import json +import time +import traceback +from dataclasses import dataclass, field +from pathlib import Path +from typing import Dict, Optional, Tuple + +import cyclone.httpserver +import cyclone.sse +import cyclone.web +import docopt +import treq +import tzlocal +from cycloneerr import PrettyErrorHandler +from dateutil.parser import parse +from dateutil.tz import tzlocal +from prometheus_client.exposition import generate_latest +from prometheus_client.registry import REGISTRY +from ruamel.yaml import YAML +from standardservice.logsetup import log, verboseLogging +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks, returnValue +from twisted.internet.utils import _UnexpectedErrorOutput, getProcessOutput + +from patch_cyclone_sse import patchCycloneSse +patchCycloneSse() + +githubOwner = 'drewp' + + +@inlineCallbacks +def runHg(cwd, args): + if args[0] not in ['push']: + args.extend(['-T', 'json']) + j = yield getProcessOutput('/usr/local/bin/hg', args, path=cwd) + returnValue(json.loads(j) if j else None) + + +@dataclass +class Repo: + path: Path + github: bool + _cache: Dict[str, Tuple[float, object]] = field(default_factory=dict) + + def _isStale(self, group) -> Optional[object]: + now = time.time() + if group not in self._cache: + return True + if now > self._cache[group][0] + 86400: + return True + print('fresh') + return False + + def _save(self, group, obj): + now = time.time() + self._cache[group] = (now, obj) + + def _get(self, group): + print('get') + return self._cache[group][1] + + @inlineCallbacks + def getStatus(self): + if self._isStale('status'): + try: + statusResp = yield runHg(self.path, ['status']) + except Exception as e: + status = {'error': repr(e)} + else: + unknowns = len([row for row in statusResp if row['status'] == '?']) + status = {'unknown': unknowns, 'changed': len(statusResp) - unknowns} + self._save('status', status) + returnValue(self._get('status')) + + @inlineCallbacks + def getLatestHgCommit(self): + if self._isStale('log'): + rows = yield runHg(self.path, ['log', '--limit', '1']) + commit = rows[0] + sec = commit['date'][0] + t = datetime.datetime.fromtimestamp(sec, tzlocal()) + self._save('log', {'email': commit['user'], 't': t.isoformat(), 'message': commit['desc']}) + returnValue(self._get('log')) + + @inlineCallbacks + def getLatestGithubCommit(self): + if self._isStale('github'): + resp = yield treq.get(f'https://api.github.com/repos/{githubOwner}/{self.path.name}/commits?per_page=1', + timeout=5, + headers={ + 'User-agent': 'reposync by github.com/drewp', + 'Accept': 'application/vnd.github.v3+json' + }) + ret = yield treq.json_content(resp) + commit = ret[0]['commit'] + t = parse(commit['committer']['date']).astimezone(tzlocal()).isoformat() + self._save('github', {'email': commit['committer']['email'], 't': t, 'message': commit['message']}) + returnValue(self._get('github')) + + @inlineCallbacks + def clearGithubMaster(self): + '''bang(pts/13):/tmp/reset% git init +Initialized empty Git repository in /tmp/reset/.git/ +then github set current to a new branch called 'clearing' with https://developer.github.com/v3/repos/#update-a-repository +bang(pts/13):/tmp/reset% git remote add origin git@github.com:drewp/href.git +bang(pts/13):/tmp/reset% git push origin :master +To github.com:drewp/href.git + - [deleted] master +maybe --set-upstream origin +bang(pts/13):/tmp/reset% git remote set-branches origin master +? +then push +then github setdefault to master +then github delete clearing +''' + + @inlineCallbacks + def pushToGithub(self): + if not self.github: + raise ValueError + yield runHg(self.path, ['bookmark', '--rev', 'default', 'master']) + out = yield runHg(self.path, ['push', f'git+ssh://git@github.com/{githubOwner}/{self.path.name}.git']) + print(f'out fompushh {out}') + + +class GithubSync(PrettyErrorHandler, cyclone.web.RequestHandler): + + @inlineCallbacks + def post(self): + try: + path = self.get_argument('repo') + repo = [r for r in self.settings.repos if str(r.path) == path][0] + yield repo.pushToGithub() + except Exception: + traceback.print_exc() + raise + + +class Statuses(cyclone.sse.SSEHandler): + + def update(self, key, data): + self.sendEvent(json.dumps({'key': key, 'update': data}).encode('utf8')) + + def bind(self): + self.toProcess = self.settings.repos[:] + reactor.callLater(0, self.runOne) + + @inlineCallbacks + def runOne(self): + if not self.toProcess: + print('done') + return + repo = self.toProcess.pop(0) + + try: + update = {'path': str(repo.path), 'github': repo.github, 'status': (yield repo.getStatus()), 'hgLatest': (yield repo.getLatestHgCommit())} + if repo.github: + update['githubLatest'] = (yield repo.getLatestGithubCommit()) + self.update(str(repo.path), update) + except Exception: + log.warn(f'not reporting on {repo}') + traceback.print_exc() + reactor.callLater(0, self.runOne) + + +class Metrics(cyclone.web.RequestHandler): + + def get(self): + self.add_header('content-type', 'text/plain') + self.write(generate_latest(REGISTRY)) + + +def main(): + args = docopt.docopt(''' +Usage: + hg_status.py [options] + +Options: + -v, --verbose more logging +''') + verboseLogging(args['--verbose']) + + # import sys + # sys.path.append('/usr/lib/python3/dist-packages') + # import OpenSSL + + yaml = YAML(typ='safe') + config = yaml.load(open('config.yaml')) + repos = [Repo(Path(row['dir']), row['github']) for row in config['hg_repos']] + + class Application(cyclone.web.Application): + + def __init__(self): + handlers = [ + (r"/()", cyclone.web.StaticFileHandler, { + 'path': '.', + 'default_filename': 'index.html' + }), + (r'/build/(bundle\.js)', cyclone.web.StaticFileHandler, { + 'path': './build/' + }), + (r'/status/events', Statuses), + (r'/githubSync', GithubSync), + (r'/metrics', Metrics), + ] + cyclone.web.Application.__init__( + self, + handlers, + repos=repos, + debug=args['--verbose'], + template_path='.', + ) + + reactor.listenTCP(10001, Application()) + reactor.run() + + +if __name__ == '__main__': + main() diff -r a4778c56cc03 -r 6f38aa08408d repo_sync.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/repo_sync.py Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,3 @@ +""" +service to sync repos from local to github +""" diff -r a4778c56cc03 -r 6f38aa08408d requirements.txt --- a/requirements.txt Sun Dec 12 22:32:25 2021 -0800 +++ b/requirements.txt Sun Jan 09 00:21:41 2022 -0800 @@ -4,11 +4,12 @@ prometheus_client==0.12.0 pyopenssl python-dateutil==2.8.2 -requests==2.26.0 -ruamel.yaml==0.17.17 +requests==2.27.1 +ruamel.yaml==0.17.20 treq==21.5.0 twisted[tls] tzlocal==4.1 +# PyGithub==1.14.2 cycloneerr==0.4.0 standardservice==0.6.0 diff -r a4778c56cc03 -r 6f38aa08408d root-hgrc --- a/root-hgrc Sun Dec 12 22:32:25 2021 -0800 +++ b/root-hgrc Sun Jan 09 00:21:41 2022 -0800 @@ -3,4 +3,4 @@ [extensions] hgext.bookmarks = -hggit = /usr/local/lib/python3.8/dist-packages/hggit \ No newline at end of file +hggit = /usr/local/lib/python3.9/dist-packages/hggit \ No newline at end of file diff -r a4778c56cc03 -r 6f38aa08408d skaffold.yaml --- a/skaffold.yaml Sun Dec 12 22:32:25 2021 -0800 +++ b/skaffold.yaml Sun Jan 09 00:21:41 2022 -0800 @@ -9,9 +9,10 @@ timezone: "Local" artifacts: - image: bang5:5000/reposync_image - sync: # files that could be patched sans python restart + sync: # files that could be patched and vite would pick them up infer: - index.html + - "view/*.ts" deploy: kubectl: manifests: diff -r a4778c56cc03 -r 6f38aa08408d tsconfig.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tsconfig.json Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,71 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "es2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff -r a4778c56cc03 -r 6f38aa08408d view/index.ts --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/view/index.ts Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,37 @@ +import { LitElement, html, css } from "lit"; +import { customElement, property } from "lit/decorators.js"; +export { StreamedGraph } from "streamed-graph"; + +@customElement("reposync-page") +export class ReposyncPage extends LitElement { + static styles = [ + css` + :host { + display: block; + } + + th { + text-align: left; + white-space: nowrap; + } + td.github { + white-space: nowrap; + opacity: 0.5; + } + td.github.on { + opacity: 1; + } + span.check { + font-size: 200%; + } + `, + ]; + + + render() { + return html` +

repo statuses yay

+ + `; + } +} diff -r a4778c56cc03 -r 6f38aa08408d vite.config.ts --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vite.config.ts Sun Jan 09 00:21:41 2022 -0800 @@ -0,0 +1,11 @@ +import { defineConfig } from "vite"; + +export default defineConfig({ + base: "https://bigasterisk.com/reposync/", + server: { + host: "0.0.0.0", + hmr: { + port: 443, + }, + }, +});