From ed4e214d481f67f796014aa80731b6d273618b6c Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Sat, 2 Nov 2019 15:54:20 -0700 Subject: add common scripts, remove old scripts, update readme --- Com/Simatime/roun | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 50 +++++++----- bild | 2 + chip/make | 138 --------------------------------- chip/push | 48 ------------ chip/roun | 227 ------------------------------------------------------ chip/run | 3 - ghci | 2 + push | 5 ++ rise | 5 ++ 10 files changed, 270 insertions(+), 437 deletions(-) create mode 100755 Com/Simatime/roun create mode 100755 bild delete mode 100755 chip/make delete mode 100755 chip/push delete mode 100755 chip/roun delete mode 100755 chip/run create mode 100755 ghci create mode 100755 push create mode 100755 rise diff --git a/Com/Simatime/roun b/Com/Simatime/roun new file mode 100755 index 0000000..addbaf3 --- /dev/null +++ b/Com/Simatime/roun @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 + +import argparse + +DESC = """roun is a program that takes numbers and turns them into human-readable +names, and vice versa. + +A 'roun' is the human-readable name. For example: + + roun = hidor-kahih + ip = 68.107.97.20 + hex = 0x446b6114 + int = 1147887892 + +The names are inspired by proquints . + +Currently the algorithm is the same, but I would like to modify the algorithm at +some point to be less odd than what proquints currently is. When I get time. +""" + + +def _char_list_to_dict(char_list): + return {c: k for (c, k) in zip(char_list, range(len(char_list)))} + + +UINT_TO_CONSONANT = "bdfghjklmnprstvz" +UINT_TO_VOWEL = "aiou" + +CONSONANT_TO_UINT = _char_list_to_dict(UINT_TO_CONSONANT) +VOWEL_TO_UINT = _char_list_to_dict(UINT_TO_VOWEL) + +MASK_LAST4 = 0xF +MASK_LAST2 = 0x3 + +CHARS_PER_CHUNK = 5 + + +def _uint16_to_roun(uint16_val): + val = uint16_val + res = ["?"] * CHARS_PER_CHUNK + for i in range(CHARS_PER_CHUNK): + if i & 1: + res[-i - 1] = UINT_TO_VOWEL[val & MASK_LAST2] + val >>= 2 + else: + res[-i - 1] = UINT_TO_CONSONANT[val & MASK_LAST4] + val >>= 4 + return "".join(res) + + +def uint_to_roun(uint_val, separator="-"): + """Convert 32-bit integer value into corresponding roun string identifier. + + >>> uint_to_roun(0x7F000001, '-') + lusab-babad + + :param uint_val: 32-bit integer value to encode + :param separator: string to separate character rounets + :return: roun string identifier + """ + if uint_val < 0 or uint_val > 0xFFFFFFFF: + raise ValueError("uint_val should be in range 0-0xFFFFFFFF") + return _uint16_to_roun(uint_val >> 16) + separator + _uint16_to_roun(uint_val) + + +def roun_to_uint(roun): + """Convert roun string identifier into corresponding 32-bit integer value. + + >>> hex(roun_to_uint('lusab-babad')) + '0x7F000001' + + :param roun: roun string identifier to decode + :return: 32-bit integer representation of the roun encoded value + """ + nchar = len(roun) + if nchar < 10 or nchar > 11: + raise ValueError("roun should be in form of two rounets + optional separator") + + res = 0 + for i, c in enumerate(roun): + mag = CONSONANT_TO_UINT.get(c) + if mag is not None: + res <<= 4 + res += mag + else: + mag = VOWEL_TO_UINT.get(c) + if mag is not None: + res <<= 2 + res += mag + elif i != 5: + raise ValueError("bad roun format") + return res + + +def ip2uint_str(ipv4_str): + """Convert IPv4 string to 32-bit integer value""" + parts = ipv4_str.split(".") + if len(parts) != 4: + raise ValueError( + "Expected IPv4 address in form A.B.C.D, got {}".format(ipv4_str) + ) + ip = [0] * 4 + for i, part in enumerate(parts): + try: + int_part = int(part) + except ValueError: + raise ValueError("Part {} of IPv4 address is not an integer".format(i)) + if int_part < 0 or int_part > 255: + raise ValueError("Part {} of IPv4 address is not in range 0-255".format(i)) + ip[i] = int_part + return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3] + + +def uint_to_ip_str(uint_val): + "Covert 32-bit integer value to IPv4 string" + return "{}.{}.{}.{}".format( + (uint_val >> 24) & 0xFF, + (uint_val >> 16) & 0xFF, + (uint_val >> 8) & 0xFF, + uint_val & 0xFF, + ) + + +def uint_to_roun_str(uint_str, separator="-"): + return uint_to_roun(int(uint_str), separator) + + +def roun_to_uint_str(roun): + return str(roun_to_uint(roun)) + + +def hex2roun_str(hex_str, separator="-"): + return uint_to_roun(int(hex_str, 16), separator) + + +def roun2hex_str(roun): + return hex(roun_to_uint(roun)) + + +def convert(str_val, target=None): + """Convert between roun, integer, hex or IPv4 string representations. + Tries to guess the representation from input. + :param str_val: input representation (string) + :return: output representation (string) + """ + if target is not None and target not in {"uint", "hex", "ip"}: + raise ValueError("Convert target should be one of: uint, hex, ip") + + if target == "uint": + return roun_to_uint_str(str_val) + + if target == "hex": + return roun2hex_str(str_val) + + if target == "ip": + return uint_to_ip_str(roun_to_uint(str_val)) + + # try to guess the representation + try: + return roun_to_uint_str(str_val) + except ValueError: + pass + + try: + return uint_to_roun_str(str_val) + except ValueError: + pass + + try: + return hex2roun_str(str_val) + except ValueError: + pass + + try: + return uint_to_roun_str(ip2uint_str(str_val)) + except ValueError: + pass + + raise ValueError("Unrecognized input format: {}".format(str_val)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=DESC, formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument( + "-n", + "--uint", + action="store_true", + help="convert from roun to 32-bit integer", + required=False, + ) + parser.add_argument( + "-x", + "--hex", + action="store_true", + help="convert from roun to hexadecimal", + required=False, + ) + parser.add_argument( + "-i", + "--ip", + action="store_true", + help="convert from roun to IPv4", + required=False, + ) + parser.add_argument( + "val", + nargs="?", + type=str, + default=None, + help="value to convert (if not specified, " + "IP address of the current host is printed)", + ) + + args = parser.parse_args() + + target = None + if args.uint: + target = "uint" + elif args.hex: + target = "hex" + elif args.ip: + target = "ip" + + res = convert(args.val, target) + print("{}".format(res)) diff --git a/README.md b/README.md index df6db66..3e97c8c 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,22 @@ # Source Layout -The source tree maps to the DNS namespace that we own. The purpose of this -mapping is to keep things organized hierarchically in how they are deployed on -the Internet. The main 'common' space is `com.simatime`, everything else should -be related to the application. +The source tree maps to the DNS namespace that we own. The purpose of +this mapping is to keep things organized hierarchically in how they are +deployed on the Internet. The main 'common' space is `Com.Simatime`, +other namespaces should be related to the application. -Development aspects should be localized to their sub-namespaces as much as -possible. Only after sufficient iteration such that interfaces are solidified -and functionality is well-established should some code be promoted up the -namespace hierarchy. +Development aspects should be localized to their sub-namespaces as much +as possible. Only after sufficient iteration such that interfaces are +solidified and functionality is well-established should some code be +promoted up the namespace hierarchy. Boundaries and interfaces between namespaces should be small and -well-defined. Likewise, the functionality and purpose of a particular namespace -should be small and well-defined. Following the unix principle of "do one thing -and do it well" is advised. +well-defined. Likewise, the functionality and purpose of a particular +namespace should be small and well-defined. Following the unix principle +of "do one thing and do it well" is advised. + +For building the code, we use `nix` and basically copy the namespace +hierarchy into the main build file `./default.nix`. # Development @@ -21,31 +24,36 @@ To build code, do: nix build -f default.nix -To get a repl: +To get in the environment for a thing, use `nix run`. For example, if +you want `ghci` with packages for `ibb`, do this: - nix run -f default.nix + nix run -f default.nix Com.InfluencedByBooks.ghc && ghci And to deploy: nix copy --to ssh://root@simatime.com ./result ssh root@simatime.com $(realpath ./result)/bin/switch-to-configuration switch +These three common tasks are captured in the `./bild`, `./ghci`, and +`./push` scripts. + # Goals of the developer workflow: - have minimal ceremony - default to asynchrony, but allow for synchronous work when necessary - automate the boring stuff -- standardize environments, tooling, and versions to minimize friction while - collaborating +- standardize environments, tooling, and versions to minimize friction + while collaborating - support the longevity and self-sustainability of the project -Ideally, each contributor should be able to go off grid for a day or a week or -more, continue working offline, submit their work when finished, and have no or -minimal conflicts. This also refers to the resilience of the production systems. +Ideally, each contributor should be able to go off grid for a day or a +week or more, continue working offline, submit their work when finished, +and have no or minimal conflicts. This also refers to the resilience of +the production systems. -We should never need "out of office" email auto-replies, or urgent contact. No -pager duty, no daily stand-ups. Yes, this policy will affect what code we write, -not just how we write it; that is by design. +We should never need "out of office" email auto-replies, or urgent +contact. No pager duty, no daily stand-ups. Yes, this policy will affect +what code we write, not just how we write it; that is by design. ## Org diff --git a/bild b/bild new file mode 100755 index 0000000..36d8917 --- /dev/null +++ b/bild @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +nix build -f default.nix "$@" diff --git a/chip/make b/chip/make deleted file mode 100755 index da5fe97..0000000 --- a/chip/make +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env runhaskell - -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# OPTIONS_GHC -Wall #-} - --- | chip/make --- --- this is the main build script. it just calls out to `ghc --make` basically. --- - -module Main where - -import Control.Concurrent -import Control.Exception -import Control.Monad -import Data.ByteString.Char8 (pack) -import qualified Data.Char as Char -import Data.List -import System.Directory -import System.Environment -import System.FilePath -import System.INotify -import System.Process - -data Notify = Notify - { notify :: IO () - , wait :: IO () - } - --- | skip channel to model concurrency semantics. this ensures that we don't --- execute more than one command at a time. -initNotify :: IO Notify -initNotify = do - mvar <- newMVar () - pure $ Notify - (void $ tryPutMVar mvar ()) - (takeMVar mvar) - -main :: IO () -main = do - (app, act) <- parseArgs <$> getArgs - case act of - Rise -> do - inotify <- initINotify - Notify {..} <- initNotify - dirs <- nub . concat <$> mapM getDirs ["aero", "apex", "lore"] - forM_ dirs $ \arg -> - void $ addWatch inotify [Modify] (pack arg) (const notify) - forever $ wait >> bild app >> say "waiting..." - Make -> bild app - Tidy -> do - callCommand "rm -rf bild/*" - say "made: tidy" - -bild :: App -> IO () -bild app = do - say $ "make: " ++ name app - apex app `catch` nop - aero app `catch` nop - say $ "made: " ++ name app - -say :: String -> IO () -say = putStrLn - -nop :: SomeException -> IO () -nop _ = pure () - - -data Action = Make | Rise | Tidy - -parseArgs :: [String] -> (App, Action) -parseArgs [] = errorWithoutStackTrace "usage: chip/make [make|rise|tidy] " -parseArgs (act:name:_) = - ( App (lowercase name) (capitalize name) - , case lowercase act of - "rise" -> Rise - "tidy" -> Tidy - _ -> Make - ) -parseArgs (name:_) = case name of - "tidy" -> (App (lowercase "") (capitalize ""), Tidy) - _ -> (App (lowercase name) (capitalize name), Make) - -capitalize, lowercase :: String -> String -capitalize (α:ω) = Char.toUpper α : map Char.toLower ω -capitalize [] = [] -lowercase (α:ω) = Char.toLower α : map Char.toLower ω -lowercase [] = [] - -data App = App - { name :: String - , entrypoint :: String - } - --- | common build options. -ghcopts :: String -ghcopts = "-odir bild/o -hidir bild/hi -Wall" - --- | compile with ghc. -apex :: App -> IO () -apex App {..} = callCommand $ intercalate " " - [ "ghc" - , ghcopts - , "-iapex -ilore" - , "-main-is", entrypoint - , "--make", "apex/" ++ entrypoint ++ ".hs" - , "-o bild/" ++ name - ] - --- | compile with ghcjs. -aero :: App -> IO () -aero App {..} = callCommand $ intercalate " " - [ "ghcjs" - , ghcopts - , "-iaero -ilore" - , "-main-is", entrypoint - , "--make", "aero/" ++ entrypoint ++ ".hs" - , "-o bild/" ++ name - ] - -getDirs :: FilePath -> IO [FilePath] -getDirs path = do - isDir <- doesDirectoryExist path - if isDir - then do - dirs <- listDirectory path - if null dirs - then pure [path] - else concat <$> do - mapM getDirs $ (path ) <$> dirs - else pure [prune path] - -prune :: String -> String -prune = reverse . dropWhile (/= '/') . reverse - -secs :: Int -> Int -secs = (* 1000000) diff --git a/chip/push b/chip/push deleted file mode 100755 index f7114ed..0000000 --- a/chip/push +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import subprocess -import tempfile -import importlib.util -import sys - -def shell(*args): - "Run a shell command and capture the output." - try: - proc = subprocess.run(args, check=True, capture_output=True) - except subprocess.CalledProcessError as e: - print(f'! fail {e.returncode}') - print(f'! {" ".join(e.cmd)}') - print(f'! {e.stderr.strip().decode("utf8")}') - sys.exit(1) - return proc.stdout.strip().decode('utf8') - -cli = argparse.ArgumentParser(description='deploy a thing') -cli.add_argument('depo', type=str, - help='the depo roun to deploy') - -args = cli.parse_args() - -cwd = os.getcwd() -bild_dir = f"{cwd}/bild" - -src = f"{cwd}/depo/{args.depo}" -out = f"{bild_dir}/{args.depo}" - - -# bild -shell("nix-build", "-A", f"depo.{args.depo}", "--out-link", out) -print("+ bilt") - -# push -shell("nix", "copy", "--to", f"ssh://root@{args.depo}", f"{out}") -print("+ sent") - -# switch -shell("ssh", f"root@{args.depo}", "sudo", - f"{os.readlink(out)}/bin/switch-to-configuration", - "switch") -print("+ switched") - -print(f"+ pushed {args.depo}") diff --git a/chip/roun b/chip/roun deleted file mode 100755 index addbaf3..0000000 --- a/chip/roun +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env python3 - -import argparse - -DESC = """roun is a program that takes numbers and turns them into human-readable -names, and vice versa. - -A 'roun' is the human-readable name. For example: - - roun = hidor-kahih - ip = 68.107.97.20 - hex = 0x446b6114 - int = 1147887892 - -The names are inspired by proquints . - -Currently the algorithm is the same, but I would like to modify the algorithm at -some point to be less odd than what proquints currently is. When I get time. -""" - - -def _char_list_to_dict(char_list): - return {c: k for (c, k) in zip(char_list, range(len(char_list)))} - - -UINT_TO_CONSONANT = "bdfghjklmnprstvz" -UINT_TO_VOWEL = "aiou" - -CONSONANT_TO_UINT = _char_list_to_dict(UINT_TO_CONSONANT) -VOWEL_TO_UINT = _char_list_to_dict(UINT_TO_VOWEL) - -MASK_LAST4 = 0xF -MASK_LAST2 = 0x3 - -CHARS_PER_CHUNK = 5 - - -def _uint16_to_roun(uint16_val): - val = uint16_val - res = ["?"] * CHARS_PER_CHUNK - for i in range(CHARS_PER_CHUNK): - if i & 1: - res[-i - 1] = UINT_TO_VOWEL[val & MASK_LAST2] - val >>= 2 - else: - res[-i - 1] = UINT_TO_CONSONANT[val & MASK_LAST4] - val >>= 4 - return "".join(res) - - -def uint_to_roun(uint_val, separator="-"): - """Convert 32-bit integer value into corresponding roun string identifier. - - >>> uint_to_roun(0x7F000001, '-') - lusab-babad - - :param uint_val: 32-bit integer value to encode - :param separator: string to separate character rounets - :return: roun string identifier - """ - if uint_val < 0 or uint_val > 0xFFFFFFFF: - raise ValueError("uint_val should be in range 0-0xFFFFFFFF") - return _uint16_to_roun(uint_val >> 16) + separator + _uint16_to_roun(uint_val) - - -def roun_to_uint(roun): - """Convert roun string identifier into corresponding 32-bit integer value. - - >>> hex(roun_to_uint('lusab-babad')) - '0x7F000001' - - :param roun: roun string identifier to decode - :return: 32-bit integer representation of the roun encoded value - """ - nchar = len(roun) - if nchar < 10 or nchar > 11: - raise ValueError("roun should be in form of two rounets + optional separator") - - res = 0 - for i, c in enumerate(roun): - mag = CONSONANT_TO_UINT.get(c) - if mag is not None: - res <<= 4 - res += mag - else: - mag = VOWEL_TO_UINT.get(c) - if mag is not None: - res <<= 2 - res += mag - elif i != 5: - raise ValueError("bad roun format") - return res - - -def ip2uint_str(ipv4_str): - """Convert IPv4 string to 32-bit integer value""" - parts = ipv4_str.split(".") - if len(parts) != 4: - raise ValueError( - "Expected IPv4 address in form A.B.C.D, got {}".format(ipv4_str) - ) - ip = [0] * 4 - for i, part in enumerate(parts): - try: - int_part = int(part) - except ValueError: - raise ValueError("Part {} of IPv4 address is not an integer".format(i)) - if int_part < 0 or int_part > 255: - raise ValueError("Part {} of IPv4 address is not in range 0-255".format(i)) - ip[i] = int_part - return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3] - - -def uint_to_ip_str(uint_val): - "Covert 32-bit integer value to IPv4 string" - return "{}.{}.{}.{}".format( - (uint_val >> 24) & 0xFF, - (uint_val >> 16) & 0xFF, - (uint_val >> 8) & 0xFF, - uint_val & 0xFF, - ) - - -def uint_to_roun_str(uint_str, separator="-"): - return uint_to_roun(int(uint_str), separator) - - -def roun_to_uint_str(roun): - return str(roun_to_uint(roun)) - - -def hex2roun_str(hex_str, separator="-"): - return uint_to_roun(int(hex_str, 16), separator) - - -def roun2hex_str(roun): - return hex(roun_to_uint(roun)) - - -def convert(str_val, target=None): - """Convert between roun, integer, hex or IPv4 string representations. - Tries to guess the representation from input. - :param str_val: input representation (string) - :return: output representation (string) - """ - if target is not None and target not in {"uint", "hex", "ip"}: - raise ValueError("Convert target should be one of: uint, hex, ip") - - if target == "uint": - return roun_to_uint_str(str_val) - - if target == "hex": - return roun2hex_str(str_val) - - if target == "ip": - return uint_to_ip_str(roun_to_uint(str_val)) - - # try to guess the representation - try: - return roun_to_uint_str(str_val) - except ValueError: - pass - - try: - return uint_to_roun_str(str_val) - except ValueError: - pass - - try: - return hex2roun_str(str_val) - except ValueError: - pass - - try: - return uint_to_roun_str(ip2uint_str(str_val)) - except ValueError: - pass - - raise ValueError("Unrecognized input format: {}".format(str_val)) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description=DESC, formatter_class=argparse.RawDescriptionHelpFormatter - ) - parser.add_argument( - "-n", - "--uint", - action="store_true", - help="convert from roun to 32-bit integer", - required=False, - ) - parser.add_argument( - "-x", - "--hex", - action="store_true", - help="convert from roun to hexadecimal", - required=False, - ) - parser.add_argument( - "-i", - "--ip", - action="store_true", - help="convert from roun to IPv4", - required=False, - ) - parser.add_argument( - "val", - nargs="?", - type=str, - default=None, - help="value to convert (if not specified, " - "IP address of the current host is printed)", - ) - - args = parser.parse_args() - - target = None - if args.uint: - target = "uint" - elif args.hex: - target = "hex" - elif args.ip: - target = "ip" - - res = convert(args.val, target) - print("{}".format(res)) diff --git a/chip/run b/chip/run deleted file mode 100755 index cf4a768..0000000 --- a/chip/run +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -cd bild && exec "./$1" diff --git a/ghci b/ghci new file mode 100755 index 0000000..be1cd94 --- /dev/null +++ b/ghci @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +nix run -f default.nix "$@.ghc" -c ghci diff --git a/push b/push new file mode 100755 index 0000000..81ad75f --- /dev/null +++ b/push @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +where="$1" +what="$2" +nix copy --to ssh://root@$where $what +ssh root@$where $(realpath $what)/bin/switch-to-configuration switch diff --git a/rise b/rise new file mode 100755 index 0000000..07776e0 --- /dev/null +++ b/rise @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +nix build -f default.nix "$@".app +export STATIC_DIR=$(realpath ./result/static) +echo $STATIC_DIR +./result/bin/ibb -- cgit v1.2.3