this also reformats everything with alejandra and moves the pre-commit config to the framework by github:cachix/pre-commit-hooks.nix
3OAFCHK7EIRUD3ZLAGZ3MAXVYOGFAJVDOF63ZIGNU7I3RWIPMAOQC
TDB7ST53L77BX5VJ2WR3JK5CIXNSLARB25HP36SQXXDWAWUGPCSAC
5TV7PKKVOU5ZI54V25KW4GSHHF4SJUDOV6TODQ7FKKSJZGXY2IQAC
CUFEZOOL3ITGUC2NOLN3WFVWTETYM7L5QU3UNVXVOH6GQYI4R5FQC
YVDXUA2QK7TLURRMLOYX4DBTLHEMAAROTF3L2PIRCPDVJNOL7EDAC
IKVV4XDUTXIEYX6G3GRZEVXK5IWGKZOMG76BO25GS6K3JOUTXK4AC
GVZHFU756PCUMRRHHDNHBYW5QFCJ5W3WRB62Q3OIBIWIFEMTHTJQC
453U6GRFTTGLAAGWCKCNHTVUF2FRH7DVCMBSSZHLTMGTPTPAZHAAC
B6XTNCPKQCL7GHNQEHAJ7YUBSTODBSI4C7RF7LA3IPG7BELTFP2AC
PVZI36WHTCMRPPMQRHNXTP63OT557JZUJI5UAB3DN6FB3XZOALKAC
5KERTK24BRCTXLSFWTOMBAWWVN7C6A2M4KKE5FPARSLBONOUUJRAC
3NPXCY4ENVBVSJZB655T7KVWTGYLTHN6XVS5YYWUXXUQ2IUJRTAQC
3XLJNJNO3AXFFQ25H7VCI565BOS46XK2C6EZJQRQTZOWJCNHDIJAC
ZIKMEP6COCUDQUM7UMN57BWYK7FZEXSEWJ74KW2YCHVSEGJ5EBDAC
JSALME2CH2BEWU75UIGJQPFRIIGCXHJ7Z45HODUW5EKB2T7Q4BFQC
5HXGJ3EZ5G7GSZ6A2PBFOQBYXP7WYU4QOCKG2ND2TX47XADKURQQC
YLLCGI2YBTBMN2EO5D4CQ4EPHMHBWTYBUVEUSS5NAQYTJDN352WQC
KUTBHT3LPAECGVN2NRJHGKBQP2WEPYO6BCG3RCH4YFHPL3MWVWXAC
WJXAODNJYL54BUFSRF3G2AEBWEI6Q4YH5DXYLK3UYC6JRJYGG7TQC
5FLTMCXWFLP6MI36R73NDC5ZZGKRB66IXOWY7ZTESTLC7M357H2QC
QYSR4PDNVTQOISVLQMO5M7MYBEG37SV6Y7JGM6QPC3CA2BJAYPYQC
X3IANZNCU5AF7H3YGJ4EUOIP3TKSQNZKVEY7I36NZY7NLI7R6YXQC
H5AVZMA7X5KIKSR7LZPDCYTYU5E25XGKWESBYC6S5DR2BVI3QWJAC
DCOF764JDXAREHUOIUZ5LCO47QEOPHSS2KKAYQGV7PDHOWDG5WDAC
5HLAP6OIMUSEZK2JBMEG7LUHNZZIBWPJBPZBB4S6FUBHD5L43VAQC
HGOULRA3TXXOSJADMYYUAN74YKYJCD6TRTHDFKCTJPEKACLES22AC
XGCXHTW2NG3AV27H6BLO3EHD65Z2GTZISEGATGP2N2CB2TNCKLIAC
NHRF46PF2N726G4UW743BDBKZU6K5BEOQ44DCI6TDIZ3TAB4NNCQC
HZ4SV4CG73RCVZ3B4FX3VQA44JEW546NYJWOQF6VGEVRGDR5TAUAC
T7CTIGVFEE6SQAQTZ3PENHCH5RRFJ3EBWXGHVALXA7BSI4ZY2NUAC
DTNETCKQOYKWBQCHFC3EUTRFBHB4E3VZVCBRN6OFD6KWTBR3W5RQC
4A5J4ZQ3HM2ZK6OO7N3RZ2WX3YHNGA3X5WJIB4UQJPQGUG4O3AYQC
VXESMJDKIRYTNWV6PVDZUSECSP7ESMK5LZPEE72DFBUU5MH7GWLAC
BX2EXA45RHKBK4ZFZYUMHYDYKRVIUQJZDW6O643XUHKI5LVNDSJAC
DCJDPW7CQ5OR75ECIVESHYVC5JSXZ5CKV6E3EWLRFHHKAKE7JGKAC
ENRHXIAOXEVZGED76KL4CZJ5SPZDX5IRS3WICJ22WRKSCCU6IC2QC
COUSBBHL5XPZC5KWCYFFAE554D4VFFO55QOD726ODWIRZGWOUSUAC
IZ6I7IFHKZLD4EUJHSRGGRMVNKMOBNHCV7HPQUK2CUGP5M7EGEFAC
A4YNQVE34SZHRGN2HGBT2M5L7ROU4D5HFHTYUSVZHKTXD2PLM5VQC
MWV72ACT6D63U5I4UUNIO4WK7ZN3Q5WYRI72K3DCKCNWZ22QUR5AC
NJ5ENURMCBSNFQPNXXNUBMUXVJEBIGNH2T7EWGB63F3FV723BUWAC
G5GQ67PH3P4PUNWAR43D6W4PP35E3LBPIG5WE4HCUM5IODT4VDHAC
4PEXC6EE4FNTPUYT64XDSOD6X5IVDY3Y2QDKYTSHEBOYS4JATQTQC
CL7RP3ZICLJT7YUI3O54IB2GZY7QMKK7ZC6GZPDXFYQKOOM64CHQC
EAV6XCAV7SQXSOO3UX3UYKEPNCNQ26BCFJ64ALU646ZZELDJ2ALQC
ROMB2RDGX2LJ4U2XAWJ5S7YANTEY7ICEHIV3FJCPXTTHM5DOYFHQC
INKINKWN6MRRH4CGUVTWI5TQ7VSCHA6ODXMLHVVFCHYPM27AYNNQC
PS2VFJMZVN77RHZFVUVD2DA3MZNMXYPHT6XRHW2EUTZRGLAXF4YQC
7BNKOFCPOMGW4OEIPGZJF76J4CTD65TZ7CLJPKJOWOIJ65EDVWEQC
VMKJ4UOWNB2QFRPYKWNOOVY6PU7QH6A6EFEKDLWRU3VIQIEHI7QAC
ZH5LGJE4YAOHG5UP6P4X3NPIUROJWHEXR3PX56PATR5KNIMQL35QC
TXU55BEHKBJLAFQATXNK36M3CM2KNH3YCQCVF54NK7O2CO4LHM4QC
TIAN54QWQA6YCIYL6C7YXXELA3SKBSMP2Y3ET2YLRWHHOPDE3Y3AC
W6GZ3OOBTJG4IYCGZIB5L2KQ623JSGOTBUTLDVM5JB4LXFFMYCUQC
BPBU3VMEUPTXFVYKEPATHO6O5ZUN3I4MW6MRWHPIUXSWJ73WTX5AC
3GK6JF2XEWIG6EUSVTQ4ZFMH34RAHROOVZ2KOMF7AMR4HZKEGD5AC
2KXMUFYOTAZO5GFGRFG6GGFJ3BIFO42O7CXBQRP2XH5WWZUOT6UAC
S6TTLG2JJJNDFDBBGOYLA67OGTSLXY4E64FMB25EYDTJSTGITR7QC
MAIEPZVQNNMHNN5MJ4UVPR5D7M3EE4B6QEBTR7L3KQERPUKOD5IQC
QADXBPZPPIAICY2HTCAZSZYX33TG72CXI2XZOG3DCNMOIXAPWQMQC
2MH4TU2HSSXKDWLUQXEBJER4F52ALDFAKVUVNT5O4XSFZHPYV53QC
IAC34BX3IZTFOKNNPGVY7VYLLDOLTWBOXGXAO7B5SLGFDF7RAAQQC
ZQKZM3SWCUV4E4BM76UDKEKHBN4FFDJ52ZJUF5XMWR2D2BGRMABQC
TAHNZ4UZMQJ5SM5GGVXCSZNX2ZFA5MBPBEDZLBR3NPT6ECC5OEWQC
QRSG7RJSX5R255Y2HHTLYBXMBR374QS3PV2UJDWK2ZPMG7V66SSAC
GY6B4NDD7FQNTOOFR53SFU4RVX4CVFV6NG5S6ZQ2J37HFAZSFSBQC
UNZZPAQTA6ONYPQXUICTU6QFQCUM2TRBWVWO2L3PAZTZY6F5FPHQC
ADHAOXXNVKFWHEYV77O74MZRNCUSS2A2AIHNPQT62EWX2AY4XH4AC
O64A6IJJGMWJYUDR6VUFGYFPWWPN222BEMMLFCMLKDEFO5OVRE4QC
5KTOFVFDH2TUKNVY7VIL4GHFK7KPW5WDYMRU6EMI4276SF7CXBGQC
H6LIOHTE46GHT5OK3HFCWZ56A7PTNWH476VS3HHKT3K5RQ5QJRWQC
6NRXEHSO2O7ERNNCXKCIHIZSTBXC7RMRO4SKOBDUPFXMTNDZJ53AC
3RG3K64UNBVMFYBBHNYCI7Y6GI7NZAHCCT76GMBBGK7PO4744BLAC
SFZ7ZFEJSWMSBJVTVQJRH4SAX3LTYO3AGFVPD24HYTQDESKHMLUQC
LTSFJDOV7PWQTVB44ZAPDHR6HUOAG4WZJQPP7TBCXRBS57EZ5T3AC
K25EHKGA7AW7YAZLYLB27DXGX4QPHFQEXU4B4S7WLRPVIV3WUJ7QC
YETF5UHCE3GS27IXWNLS4I3PVVBCFYRJTLPUETD5WILFFBEPTPRAC
EWSZ2QIQQBG2EOVAU2IDFXQXE7D3PNFX3HE5LV2YPXEZONHM4MTQC
WBQMAQLSGL2ZMUGJAJHTHF2DWAYL3LQO56VM7PMGNHSLHUNGZPJQC
HYP7NYOZCLUZSNM3AA234JVSU3JNSQFN3G3DDPKR6IDEJA3MCRWQC
U6GSJX5ZG4O7R3XO4CA7G62TTDE22HRZ2LL7EQKLQSYCHCAA3CQQC
VBIKNV64COAFFZXT2MWPUFAZPSPFIIK2TOPGBXKIOU7XCVQ4LJ6AC
WHTEZBXRS7R3AM44LJZFR2YE6RP4IWE3AB5V3DYGO4YJU7PBTMQAC
QLLKRJFQMIZ6UDEN44E2Z6KZNBJN75FKMPDWIT4EKMIXZPKHEY2AC
IU7QFC7V4DK5WB5D4VDQN7ZFTZNBQ5U7E2TGZVR7MIXFJDW2CXIQC
IGP7FHYGXFXSDYGO5ABKFXBO2V2ZMYL6GSB2BWKB64T7GTGVXWKQC
C4JNLSVRIR3BVGJRE7JOE5DADBGZTNKKTHLHHIAFTS6KCY4W63RAC
EAPJCWPUTO4MRRM3TAMQC2AMLFVAOQPFDP4BQYT74ZLHGI5IISOAC
OEG6DPRQVUDPTETGTOR7WYOJTIB4SWUPXGYW4FEPWSYYNULOCTIQC
BKU3SBYQV4NMFT4HI3LPNSZ55M4OV2GVWZAQZUAIXXGLL7RPI7GAC
E56TRR6IFFHTTJWJ4UUGKDSDGTN4YBIDYJCVLDRLMNY3MS3TWXSQC
YBD5JJV7JVV6BVGGMNKQDV4U2QPAOKGNRUK2OVY7LFSDU5RWHCKAC
TEJLAHEG6JYVPWBHUHIO6NTSB7HO62RJ6G5J2B6UNPDVG7XK4XIAC
MW575RFTDFT5GPNR373ECHKFG6T66IKMSIMRGB3ZBKLAYATGVDVQC
HQAWYBVCFQ2PBNEKQFMBBJRIJAWU75TAKYMREN6TSXDXNN7ORXWAC
HBLAD3UT5OW4AVVJKVN55YQBJNWEACH5IOC5IFBKXJ33XQLUIHGQC
BYGEHAR6KNZXJ2QWH5ASEB7SVDXTWANYRGHBPAO4FHBTBQJOGMRAC
2NMPOND2YKPM2UAML57TJ6NZXPDUHWPMWY6WJMLQ3BXVA6SXS2CAC
47GPXWYAN5HMMVVIWFEAY63MID4U2UHIBH4HNMDYWNQ2J6E2WX6AC
BJKOD7ZFG2DJGJ6E4WZJD6ITNMRNNCALRVTNX26LMDSRK6VLNJGAC
IVJFJ675FQ2V7JVOYEFISWM47QMESRSZQYGXBMIGAU3NG5E4MQWAC
H4PTMRYDGOSGYLS5JK3IADXSWOXEWTZMUJ6OYSNODEWS7OMCBV3QC
UPKMMJFFBYJRZHQDUNLVXKVJH32VNY5ZGEH3GXI3KOG7NO5OJF3AC
M47OO2CYU6I4IGSY76VPSERKWG7AC6NGU343YAMGGTFPGYMWCXHAC
ENVJVMMFPGW7UZNC5A27IQ4VZJDKXSNZ4I24BGYTTJXZXU6GAF7AC
SC7MDSHCX3734KSWU4YFGJTKLJRSRM7AHC4MALZKQGDAHH42ILJQC
QOTSIIJGJAYIO5PLOSYF6NFUJSY4ITXDSJUE4IN6ZSJK6HOQTBJAC
6DZO6E3ALZ477IYSCGVKASDYM54CZEDON55NR2UTUGOCZR3BMTAAC
X575KR6QP76HLPGLLS6XINQGQEAQD76SNOKPJMTGKGR3JJW6A5EQC
6QF3TT2JA5DYRPTSEPZMZX352OY45GVZXJJ525EAUZEEOSLUKEDQC
FMMNSJMBROWJ6OC4MUBK6KSBJDQ7R26S33HZTQRAF5CF5VMFNE6QC
N7DHDONTAB6UEU4PHQR7BNSUQFAA4JBIVSTKDQ7V7OF3CLOEWXOQC
A32NGEIBDOOKELILD6YPAGDCEDVVSYBSNJNS4RDXB7SHBQNIVNPQC
BYCLZBRJTN2ZIINK42SBGJHKCPVXECL632SYTV6EEQROEUNZJFHAC
7UWOMG432IHX7BGGN7PPQUGSSUP6CCQNBXOO7TKNMKMK2R2WFOGAC
ZH6EAGFIDERASR6IRHMOHY3RFYDIXB6SXBQZHA36X6NOENM6W43QC
723QWZKMMKC44ZAQQYSSOPCB65TKSORXU2D4PIHWOUEKICJGUBVAC
BN7PSYS5IMGH4CXNCQZGOWBETO7KY4TKICB4ESZEZVXVGLOPVV7QC
{ pkgs ? import <nixpkgs> { } }:
black
flake8
importmagic
ipdb
ipython
isort
pytest
typer
];
in
pkgs.mkShell {
buildInputs = with pkgs; [
nixpkgs-fmt
pre-commit
shellcheck
shfmt
}
] ++ pypkgs;
shellHook = ''
'';
export PYTHONBREAKPOINT=ipdb.set_trace;
export PYTHONDONTWRITEBYTECODE=true;
statix
mdl
let
pkgs.python39
pkgs.nodePackages.pyright
# typer doesn't support click 8 which is what's packaged upstream
# https://github.com/NixOS/nixpkgs/issues/129479
# override it here 👍👍
python39Packages = pkgs.python39Packages.override {
overrides = self: super: {
version = "7.1.2";
src = super.fetchPypi {
inherit version;
sha256 = "d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a";
};
});
};
};
pypkgs = with python39Packages; [
inherit (old) pname;
click = super.click.overrideAttrs (old: rec {
{ pkgs ? import <nixpkgs> { }, lib ? pkgs.lib, ... }:
pkgs.python39Packages.buildPythonApplication {
pname = "path_finder";
version = "0.0.1";
}
checkInputs = with pkgs.python39Packages; [ pytestCheckHook ];
propagatedBuildInputs = with pkgs.python39Packages; [ typer ];
src = pkgs.lib.cleanSource ./.;
from typing import Optional
import typer
cli = typer.Typer()
@cli.command()
def main(name: str, find_all: bool = False, parent_name: Optional[str] = None):
parent_name = parent_name or ""
path_finder = PathFinder.from_env()
paths = path_finder.find(name, parent_name=parent_name)
if not find_all:
path = next(paths, None)
paths = [path] if path else []
for path in paths:
typer.echo(path)
if __name__ == "__main__":
cli()
if __package__:
from .lib import PathFinder
else:
from lib import PathFinder
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Generator, Optional
@dataclass
class PathFinder:
paths: list[Path]
@classmethod
def from_env(cls) -> "PathFinder":
path = os.getenv("PATH")
if not path:
raise RuntimeError("PATH environment variable is not set.")
return cls(list(map(Path, path.split(":"))))
def find_first(self, name: str, parent_name: str = "") -> Optional[Path]:
return next(self.find(name, parent_name=parent_name), None)
def find(self, name: str, parent_name: str = "") -> Generator[Path, None, None]:
for path in self.paths:
if not path.is_dir():
path = path.parent
if parent_name:
parents = (parent.joinpath(parent_name) for parent in path.parents)
path = next((parent for parent in parents if parent.exists()), path)
result_path = path.joinpath(name)
if result_path.exists():
yield result_path
path = path.resolve()
from setuptools import find_packages, setup
setup(
name="path_finder",
version="0.0.1",
py_modules=["path_finder.cli", "path_finder.lib"],
entry_points={"console_scripts": ["path_finder = path_finder.cli:cli"]},
pacakges=find_packages(),
)
from unittest.mock import patch
import pytest
from typer.testing import CliRunner
@pytest.fixture
def invoke_cli():
def invoke(*args):
return CliRunner(mix_stderr=False).invoke(cli, args)
yield invoke
def test_cli_help_parent_name_no_bad_default(invoke_cli):
"""
typer produces [default: ] if main's signature for parent_name isn't Optional
so yeah we 💯 want to enforce a nice help message by making main's signature
a little more annoying
"""
result = invoke_cli("--help")
for line in result.stdout.splitlines():
if "--parent-name TEXT" in line:
assert "default" not in line
break
else:
pytest.fail("expected --parent-name TEXT in stdout")
@pytest.mark.parametrize(
"arg_find_all,search_results,expected_output",
[
("--no-find-all", ["one", "two"], ["one"]),
("--find-all", ["one", "two"], ["one", "two"]),
("--find-all", ["two"], ["two"]),
("--no-find-all", ["two", "one"], ["two"]),
("--find-all", [], []),
("--no-find-all", [], []),
],
)
def test_cli_find_all(arg_find_all, search_results, expected_output, invoke_cli):
with patch.object(PathFinder, "find", return_value=iter(search_results)):
result = invoke_cli("whatever", arg_find_all)
assert expected_output == result.stdout.splitlines()
from path_finder.cli import cli
from path_finder.lib import PathFinder
from pathlib import Path
def test_find_first_does_not_exist(tmp_path: Path):
path_finder = PathFinder([tmp_path])
assert not tmp_path.joinpath("needle.txt").exists()
assert path_finder.find_first("needle") is None
assert path_finder.find_first("needle", parent_name="anything") is None
def test_find_first_parent_exists_needle_does_not(tmp_path: Path):
tmp_path.joinpath("parent").mkdir()
nest_one = tmp_path.joinpath("parent/nest_one")
nest_two = tmp_path.joinpath("parent/nest_two")
nest_one.mkdir()
nest_two.mkdir()
path_finder = PathFinder([nest_one])
assert path_finder.find_first("needle") is None
assert path_finder.find_first("needle", parent_name="nest_two") is None
def test_find_first_success_needle_in_parent_nested(tmp_path):
search_dir = tmp_path.joinpath("thing/nested/bin")
bundle_dir = search_dir.parent.parent.joinpath("bundle_dir")
search_dir.mkdir(parents=True)
bundle_dir.mkdir(parents=True)
bundle_dir.joinpath("Bundle.app").write_text("app here 😎")
actual = PathFinder(paths=[search_dir]).find_first(
"Bundle.app", parent_name="bundle_dir"
)
assert actual is not None and actual.exists()
def test_find_first_needle_in_path(tmp_path: Path):
path_dir = tmp_path.joinpath("thing/nested/bin")
path_dir.mkdir(parents=True)
(path_dir / "file.txt").write_text("hi there :)")
actual = PathFinder(paths=[path_dir]).find_first("file.txt")
assert actual is not None and actual.exists()
def test_find_first_depends_on_sort_order_of_paths(tmp_path: Path):
no_match = tmp_path.joinpath("nomatch")
no_match.mkdir()
match_one = tmp_path.joinpath("one/file.txt")
match_two = tmp_path.joinpath("two/file.txt")
match_one.parent.mkdir()
match_two.parent.mkdir()
match_one.write_text("hi from match_one :)")
match_two.write_text("hi from match_two :)")
assert (
PathFinder([no_match, match_one, match_two]).find_first("file.txt") == match_one
)
assert (
PathFinder([no_match, match_two, match_one]).find_first("file.txt") == match_two
)
def test_find_many(tmp_path: Path):
no_match = tmp_path.joinpath("nomatch")
no_match.mkdir()
match_one = tmp_path.joinpath("one/file.txt")
match_two = tmp_path.joinpath("two/file.txt")
match_one.parent.mkdir()
match_two.parent.mkdir()
match_one.write_text("hi from match_one :)")
match_two.write_text("hi from match_two :)")
path_finder = PathFinder([no_match.parent, match_one.parent, match_two])
assert list(path_finder.find("file.txt")) == [match_one, match_two]
def test_find_many_paths_contain_result(tmp_path: Path):
"""if given full path PathFinder.find should return it."""
no_match = tmp_path.joinpath("nomatch")
no_match.mkdir()
match_one = tmp_path.joinpath("one/file.txt")
match_two = tmp_path.joinpath("two/file.txt")
match_one.parent.mkdir()
match_two.parent.mkdir()
match_one.write_text("hi from match_one :)")
match_two.write_text("hi from match_two :)")
path_finder = PathFinder([no_match, match_one, match_two])
assert list(path_finder.find("file.txt")) == [match_one, match_two]
from path_finder.lib import PathFinder
repos:
hooks:
hooks:
hooks:
- id: mdl-markdown-linter
name: markdown-linter
description: ruby gem to lint markdown files
entry: mdl -g
language: system
types: [markdown]
minimum_pre_commit_version: 1.14.2
- id: python-format-black
name: python-black
description: format python with black
entry: black
language: system
types: [python]
- id: python-sort-imports
name: python-isort
description: format python imports with isort
entry: isort
language: system
types: [python]
- id: python-lint-flake8
name: python-flake8
description: python flake8
entry: flake8
language: system
types: [python]
# - id: python-pytest
# name: python-pytest
# description: python pytest
# entry: pytest
# language: system
# types: [python]
# pass_filenames: false
# always_run: true
# TODO getting these to work will be a pain
# TODO consider using something like
# TODO https://github.com/cachix/pre-commit-hooks.nix
#
# - id: python-type-check-pyright
# name: python-type-check-pyright
# description: python type check pyright
# entry: pyright
# args: [./]
# language: system
# types: [python]
# pass_filenames: false
# always_run: true
- id: nixpkgs-fmt
name: nixpkgs-fmt
description: Format nix code with nixpkgs-fmt.
entry: nixpkgs-fmt
language: system
files: \.nix$
always_run: true
minimum_pre_commit_version: 1.14.2
- id: statix
name: statix
description: static linter for nix code
entry: statix check .
language: system
files: \.nix$
always_run: true
minimum_pre_commit_version: 1.14.2
pass_filenames: false
- repo: local
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.8
hooks:
- id: mdformat
# NOTE: these require shellcheck and shfmt to be installed
# in the environment they should be installed by
# lorri -> direnv -> nix-shell
# automatically from ~/dotfiles/shell.nix
- id: shellcheck
- id: shfmt
- repo: https://github.com/syntaqx/git-hooks
rev: v0.0.17
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: destroyed-symlinks
- id: mixed-line-ending
rev: v4.0.1
- repo: https://github.com/pre-commit/pre-commit-hooks
mkKeyValue = key: value:
let
v =
if pkgs.lib.isBool value then
(if value then "True" else "False")
else toString value;
in
"${key} = ${v}";
mkKeyValue = key: value: let
v =
if pkgs.lib.isBool value
then
(
if value
then "True"
else "False"
)
else toString value;
in "${key} = ${v}";
stringify =
{ mkKey ? (k: "${k}")
, mkValue ? pkgs.lib.generators.mkValueStringDefault
,
}: attrs:
(pkgs.lib.strings.concatStringsSep
"\n"
(pkgs.lib.attrsets.mapAttrsToList
(key: value: "${mkKey key} ${mkValue value}")
attrs));
stringify = {
mkKey ? (k: "${k}"),
mkValue ? pkgs.lib.generators.mkValueStringDefault,
}: attrs: (pkgs.lib.strings.concatStringsSep
"\n"
(pkgs.lib.attrsets.mapAttrsToList
(key: value: "${mkKey key} ${mkValue value}")
attrs));
{ config, pkgs, ... }:
let
tmuxModal = with pkgs; tmuxPlugins.mkTmuxPlugin rec {
pluginName = "tmux-modal";
version = "unstable-2022-02-19";
src = fetchFromGitHub {
owner = "whame";
repo = "tmux-modal";
rev = "5ecffca7af0950e49f47a2681c9fb07ccfb9b407";
sha256 = "sha256-pcleS1lyJQ5qV3B3actjNHJJwly6zi50FXegFMe5Iis=";
};
rtpFilePath = "${pluginName}.tmux";
meta = {
homepage = "https://github.com/whame/tmux-modal";
description = "A modal mode for tmux.";
longDescription =
''
{
config,
pkgs,
...
}: let
tmuxModal = with pkgs;
tmuxPlugins.mkTmuxPlugin rec {
pluginName = "tmux-modal";
version = "unstable-2022-02-19";
src = fetchFromGitHub {
owner = "whame";
repo = "tmux-modal";
rev = "5ecffca7af0950e49f47a2681c9fb07ccfb9b407";
sha256 = "sha256-pcleS1lyJQ5qV3B3actjNHJJwly6zi50FXegFMe5Iis=";
};
rtpFilePath = "${pluginName}.tmux";
meta = {
homepage = "https://github.com/whame/tmux-modal";
description = "A modal mode for tmux.";
longDescription = ''
license = lib.licenses.mit;
platforms = lib.platforms.unix;
maintainers = with lib.maintainers; [ ];
license = lib.licenses.mit;
platforms = lib.platforms.unix;
maintainers = with lib.maintainers; [];
};
postInstall = ''
sed -i -e 's|KBD_CMD=M-m|KBD_CMD=C-x |g' $target/${rtpFilePath}
sed -i -e 's|KBD_CMD_EXIT=M-m|KBD_CMD_EXIT=C-x|g' $target/${rtpFilePath}
'';
postInstall = ''
sed -i -e 's|KBD_CMD=M-m|KBD_CMD=C-x |g' $target/${rtpFilePath}
sed -i -e 's|KBD_CMD_EXIT=M-m|KBD_CMD_EXIT=C-x|g' $target/${rtpFilePath}
'';
};
in
{
in {
modules-left = [ "clock" "custom/media" ];
modules-center = [ "sway/mode" "sway/workspaces" ];
modules-right = [ "idle_inhibitor" "pulseaudio" "network" "bluetooth" "battery" ];
modules-left = ["clock" "custom/media"];
modules-center = ["sway/mode" "sway/workspaces"];
modules-right = ["idle_inhibitor" "pulseaudio" "network" "bluetooth" "battery"];
exec = with pkgs; writeShellApplication {
name = "waybar_media_play_pause_toggler";
runtimeInputs = [ playerctl ];
text = ''
player_status=$(playerctl status 2>/dev/null)
exec = with pkgs;
writeShellApplication {
name = "waybar_media_play_pause_toggler";
runtimeInputs = [playerctl];
text = ''
player_status=$(playerctl status 2>/dev/null)
compose_mode = {
# a mode for entering other modes, or inserting commands based on
# sequential key presses
# e.g. Shift+space -> k -> s == bemenu_try_restart_systemd_user_services
f = "fullscreen toggle; mode default;";
k = "mode kill_mode";
r = "mode resize_mode";
t = ''exec swaymsg [app_id="scratch_terminal"] scratchpad show; mode default;'';
v = "mode volume_mode";
"Shift+space" = "mode disabled_mode";
} // leaveModeKeys;
kill_mode = {
"Shift+q" = "exec swaymsg exit";
q = "${execNwgBar}";
r = "exec systemctl reboot -i";
s = "exec bemenu_try_restart_systemd_user_services; mode default;";
w = "kill; mode default;";
} // leaveModeKeys;
resize_mode = {
"${cfg.up}" = "resize shrink width 15 px";
"${cfg.down}" = "resize grow height 15 px";
"${cfg.left}" = "resize shrink height 15 px";
"${cfg.right}" = "resize grow width 15 px";
"Shift+${cfg.up}" = "resize shrink width 45 px";
"Shift+${cfg.down}" = "resize grow height 45 px";
"Shift+${cfg.left}" = "resize shrink height 45 px";
"Shift+${cfg.right}" = "resize grow width 45 px";
} // leaveModeKeys;
volume_mode = {
"${cfg.up}" = "${setVolume} +1%";
"Shift+${cfg.up}" = "${setVolume} +10%";
"${cfg.down}" = "${setVolume} -1%";
"Shift+${cfg.down}" = "${setVolume} -10%";
} // leaveModeKeys;
workspace_mode = {
"0" = "workspace 0";
"1" = "workspace 1";
"2" = "workspace 2";
"3" = "workspace 3";
"4" = "workspace 4";
"5" = "workspace 5";
"6" = "workspace 6";
"7" = "workspace 7";
"8" = "workspace 8";
"9" = "workspace 9";
"${cfg.right}" = "workspace next";
"${cfg.left}" = "workspace prev";
} // leaveModeKeys;
compose_mode =
{
# a mode for entering other modes, or inserting commands based on
# sequential key presses
# e.g. Shift+space -> k -> s == bemenu_try_restart_systemd_user_services
f = "fullscreen toggle; mode default;";
k = "mode kill_mode";
r = "mode resize_mode";
t = ''exec swaymsg [app_id="scratch_terminal"] scratchpad show; mode default;'';
v = "mode volume_mode";
"Shift+space" = "mode disabled_mode";
}
// leaveModeKeys;
kill_mode =
{
"Shift+q" = "exec swaymsg exit";
q = "${execNwgBar}";
r = "exec systemctl reboot -i";
s = "exec bemenu_try_restart_systemd_user_services; mode default;";
w = "kill; mode default;";
}
// leaveModeKeys;
resize_mode =
{
"${cfg.up}" = "resize shrink width 15 px";
"${cfg.down}" = "resize grow height 15 px";
"${cfg.left}" = "resize shrink height 15 px";
"${cfg.right}" = "resize grow width 15 px";
"Shift+${cfg.up}" = "resize shrink width 45 px";
"Shift+${cfg.down}" = "resize grow height 45 px";
"Shift+${cfg.left}" = "resize shrink height 45 px";
"Shift+${cfg.right}" = "resize grow width 45 px";
}
// leaveModeKeys;
volume_mode =
{
"${cfg.up}" = "${setVolume} +1%";
"Shift+${cfg.up}" = "${setVolume} +10%";
"${cfg.down}" = "${setVolume} -1%";
"Shift+${cfg.down}" = "${setVolume} -10%";
}
// leaveModeKeys;
workspace_mode =
{
"0" = "workspace 0";
"1" = "workspace 1";
"2" = "workspace 2";
"3" = "workspace 3";
"4" = "workspace 4";
"5" = "workspace 5";
"6" = "workspace 6";
"7" = "workspace 7";
"8" = "workspace 8";
"9" = "workspace 9";
"${cfg.right}" = "workspace next";
"${cfg.left}" = "workspace prev";
}
// leaveModeKeys;
preBuild = old.preBuild or "" + ''
substituteInPlace setup.py --replace 'version = "3.0.0"' 'version = "2.8.0"'
'';
preConfigure = (old.preConfigure or "") + ''cat << EOF > requirements.txt
blessed==1.19.0
readchar==2.0.1
python-editor==1.0.4
EOF
preBuild =
old.preBuild
or ""
+ ''
substituteInPlace setup.py --replace 'version = "3.0.0"' 'version = "2.8.0"'
'';
preConfigure =
(old.preConfigure or "")
+ '' cat << EOF > requirements.txt
blessed==1.19.0
readchar==2.0.1
python-editor==1.0.4
EOF
"profile non-production-connect" = awscliConfig // {
sso_account_id = "186258024085";
sso_role_name = "non-production-backend-access";
};
"profile non-production-connect" =
awscliConfig
// {
sso_account_id = "186258024085";
sso_role_name = "non-production-backend-access";
};
ph-widget() {
ph info | grep -q 'Database Version' && ph show --field password "$(ph grep -i . | fzf)" | wl-copy --trim-newline
}
ph-widget() {
ph info | grep -q 'Database Version' && ph show --field password "$(ph grep -i . | fzf)" | wl-copy --trim-newline
}
sessionVariables = rec {
EDITOR = if isEmacsEnabled then "emacsclient -t" else "nvim";
GIT_EDITOR = EDITOR;
KEYTIMEOUT = "1";
LESS = "-SRXF";
} // (if isEmacsEnabled then {
VISUAL = "emacs";
} else { });
sessionVariables =
rec {
EDITOR =
if isEmacsEnabled
then "emacsclient -t"
else "nvim";
GIT_EDITOR = EDITOR;
KEYTIMEOUT = "1";
LESS = "-SRXF";
}
// (
if isEmacsEnabled
then {
VISUAL = "emacs";
}
else {}
);
home.packages = with pkgs; [
curl
direnv
fd
htop
jq
neofetch
pre-commit
procs
ripgrep
sshfs
wget
] ++ builtins.attrValues (import ./shell_extras.nix { inherit pkgs; });
home.packages = with pkgs;
[
curl
direnv
fd
htop
jq
neofetch
pre-commit
procs
ripgrep
sshfs
wget
]
++ builtins.attrValues (import ./shell_extras.nix {inherit pkgs;});
xdg.configFile."pgcli/config".text = ''
# Generated by home-manager from nixpkgs.pgcli in ~/dotfiles
# For a list of options see: https://www.pgcli.com/config
xdg.configFile."pgcli/config".text =
''
# Generated by home-manager from nixpkgs.pgcli in ~/dotfiles
# For a list of options see: https://www.pgcli.com/config
buildPythonApplication rec {
pname = "passhole";
version = "1.9.7";
src = fetchPypi {
inherit pname version;
sha256 = "sha256-0qOxGv6YdcK4AjbE+IbBxYNBe/NE/z4k6PuVGCPdjFk=";
};
propagatedBuildInputs = [
colorama
future
pykeepass_cache
pynput
pyotp
pkgs.gnome.zenity
];
}
buildPythonApplication rec {
pname = "passhole";
version = "1.9.7";
src = fetchPypi {
inherit pname version;
sha256 = "sha256-0qOxGv6YdcK4AjbE+IbBxYNBe/NE/z4k6PuVGCPdjFk=";
};
propagatedBuildInputs = [
colorama
future
pykeepass_cache
pynput
pyotp
pkgs.gnome.zenity
];
}
boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "vmd" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
boot.initrd.availableKernelModules = ["xhci_pci" "thunderbolt" "vmd" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc"];
boot.initrd.kernelModules = ["dm-snapshot"];
boot.kernelModules = ["kvm-intel"];
boot.extraModulePackages = [];
fileSystems."/" =
{
device = "/dev/disk/by-uuid/316096c1-57d0-4536-8397-3ad8d2710647";
fsType = "ext4";
};
fileSystems."/" = {
device = "/dev/disk/by-uuid/316096c1-57d0-4536-8397-3ad8d2710647";
fsType = "ext4";
};
boot.initrd.availableKernelModules = [ "virtio_pci" "virtio_scsi" "ahci" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
boot.initrd.availableKernelModules = ["virtio_pci" "virtio_scsi" "ahci" "sd_mod"];
boot.initrd.kernelModules = [];
boot.kernelModules = [];
boot.extraModulePackages = [];
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod"];
boot.initrd.kernelModules = ["dm-snapshot"];
boot.kernelModules = ["kvm-amd"];
boot.extraModulePackages = [];
after = [ "network-pre.target" "tailscale.service" ];
wants = [ "network-pre.target" "tailscale.service" ];
wantedBy = [ "multi-user.target" ];
after = ["network-pre.target" "tailscale.service"];
wants = ["network-pre.target" "tailscale.service"];
wantedBy = ["multi-user.target"];
postFixup = old.postFixup + ''
wrapProgram $out/bin/zoom-us --unset XDG_SESSION_TYPE
wrapProgram $out/bin/zoom --unset XDG_SESSION_TYPE
'';
postFixup =
old.postFixup
+ ''
wrapProgram $out/bin/zoom-us --unset XDG_SESSION_TYPE
wrapProgram $out/bin/zoom --unset XDG_SESSION_TYPE
'';
} else { } // {
# allows firefox to see userChrome.css etc
"toolkit.legacyUserProfileCustomizations.stylesheets" = true;
}
else
{}
// {
# allows firefox to see userChrome.css etc
"toolkit.legacyUserProfileCustomizations.stylesheets" = true;
"browser.startup.homepage" = "https://duckduckgo.com";
"browser.search.region" = "US";
"browser.search.isUS" = true;
"browser.bookmarks.showMobileBookmarks" = true;
"browser.toolbars.bookmarks.visibility" = "never";
};
"browser.startup.homepage" = "https://duckduckgo.com";
"browser.search.region" = "US";
"browser.search.isUS" = true;
"browser.bookmarks.showMobileBookmarks" = true;
"browser.toolbars.bookmarks.visibility" = "never";
};
python39Packages.buildPythonApplication rec {
pname = "lastpass-authenticator-export";
version = "unstable-2022-03-31";
src = fetchFromGitHub {
owner = "dmaasland";
repo = "lastpass-authenticator-export";
rev = "154422b60152f328435e71bed722be5788808486";
sha256 = "sha256-CmNBrEFuwWCdFijU5VUzg/zzLQ2ALaIjAWKtAZnehes=";
};
setup = ''
from setuptools import find_packages, setup
python39Packages.buildPythonApplication rec {
pname = "lastpass-authenticator-export";
version = "unstable-2022-03-31";
src = fetchFromGitHub {
owner = "dmaasland";
repo = "lastpass-authenticator-export";
rev = "154422b60152f328435e71bed722be5788808486";
sha256 = "sha256-CmNBrEFuwWCdFijU5VUzg/zzLQ2ALaIjAWKtAZnehes=";
};
setup = ''
from setuptools import find_packages, setup
setup(
name="${pname}",
version="${version}",
py_modules=["lastpass_authenticator_export"],
entry_points={"console_scripts": ["lastpass_authenticator_export = lastpass_authenticator_export:main"]},
pacakges=find_packages(),
)
'';
postUnpack = ''
echo '${setup}' > $sourceRoot/setup.py
mv $sourceRoot/lastpass-authenticator-export.py $sourceRoot/lastpass_authenticator_export.py
'';
setup(
name="${pname}",
version="${version}",
py_modules=["lastpass_authenticator_export"],
entry_points={"console_scripts": ["lastpass_authenticator_export = lastpass_authenticator_export:main"]},
pacakges=find_packages(),
)
'';
postUnpack = ''
echo '${setup}' > $sourceRoot/setup.py
mv $sourceRoot/lastpass-authenticator-export.py $sourceRoot/lastpass_authenticator_export.py
'';
{ email, realName, imapHost, smtpHost, gmail ? false }: {
flavor = if gmail then "gmail.com" else "plain";
address = email;
userName = email;
realName = realName;
{
email,
realName,
imapHost,
smtpHost,
gmail ? false,
}: {
inherit realName;
flavor =
if gmail
then "gmail.com"
else "plain";
address = email;
userName = email;
passwordCommand =
if pkgs.stdenv.hostPlatform.isLinux then
"${pkgs.gnome3.libsecret}/bin/secret-tool lookup email ${email}"
else
# TODO use config.home.username
"security find-generic-password -a christophercummings -s ${email} -w";
passwordCommand =
if pkgs.stdenv.hostPlatform.isLinux
then "${pkgs.gnome3.libsecret}/bin/secret-tool lookup email ${email}"
else
# TODO use config.home.username
"security find-generic-password -a christophercummings -s ${email} -w";
mbsync = {
enable = true;
create = "both";
expunge = "both";
remove = "both";
extraConfig.account = {
# NOTE microsoft office 365 imap servers may require this to be 1
# because they do not support concurrent imap commands
PipelineDepth = 50;
mbsync = {
enable = true;
create = "both";
expunge = "both";
remove = "both";
extraConfig.account = {
# NOTE microsoft office 365 imap servers may require this to be 1
# because they do not support concurrent imap commands
PipelineDepth = 50;
};
(template "email@email.domain" "Real Name" "imap.email.domain" "smtp.email.domain") // {
# you can pass extra params here if necessary. otherwise omit the `// { ... }`
primary = true;
}
(template "email@email.domain" "Real Name" "imap.email.domain" "smtp.email.domain")
// {
# you can pass extra params here if necessary. otherwise omit the `// { ... }`
primary = true;
}
outputs =
{ self
, nixpkgs
, emacs-overlay
, home-manager
, nixos-hardware
, nix-doom-emacs
, wayland-overlay
, ...
}@inputs:
outputs = {
self,
nixpkgs,
emacs-overlay,
home-manager,
nixos-hardware,
nix-doom-emacs,
wayland-overlay,
pre-commit-hooks,
...
} @ inputs: let
overlays = [emacs-overlay.overlay wayland-overlay.overlay];
pkgs = nixpkgs.legacyPackages.x86_64-linux;
helloDotfiles = pkgs.writeShellApplication {
name = "helloDotfiles";
text = ''
echo 👋👋 hello from github:averagechris/dotfiles
echo have a nice day 😎
'';
};
in {
overlays = {
emacs = emacs-overlay.overlay;
wayland = wayland-overlay.overlay;
};
let
overlays = [ emacs-overlay.overlay wayland-overlay.overlay ];
in
{
inherit overlays;
nixosConfigurations = {
thelio-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {inherit inputs overlays;};
modules = [
./nixpkgs/nixos/thelio
nixos-hardware.nixosModules.system76
./nixpkgs/nixos/common.nix
./nixpkgs/nixos/desktop_common.nix
./nixpkgs/nixos/graphical.nix
./nixpkgs/nixos/greetd.nix
./nixpkgs/nixos/networking.nix
./nixpkgs/nixos/docker.nix
./nixpkgs/nixos/sound.nix
./nixpkgs/nixos/tailscale.nix
./nixpkgs/nixos/users/chris.nix
./nixpkgs/nixos/users/chris-focus.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
}
];
};
xps-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {inherit inputs overlays;};
modules = [
./nixpkgs/nixos/xps
nixos-hardware.nixosModules.system76
./nixpkgs/nixos/common.nix
./nixpkgs/nixos/desktop_common.nix
./nixpkgs/nixos/docker.nix
./nixpkgs/nixos/graphical.nix
./nixpkgs/nixos/greetd.nix
./nixpkgs/nixos/networking.nix
./nixpkgs/nixos/sound.nix
./nixpkgs/nixos/tailscale.nix
./nixpkgs/nixos/users/chris-focus.nix
./nixpkgs/nixos/users/chris.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
}
];
};
tootsie = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {inherit inputs overlays;};
modules = [
./nixpkgs/nixos/tootsie
./nixpkgs/nixos/common.nix
./nixpkgs/nixos/tailscale.nix
./nixpkgs/nixos/users/chris-minimal.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
}
];
};
};
nixosConfigurations = {
thelio-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs overlays; };
modules = [
./nixpkgs/nixos/thelio
nixos-hardware.nixosModules.system76
./nixpkgs/nixos/common.nix
./nixpkgs/nixos/desktop_common.nix
./nixpkgs/nixos/graphical.nix
./nixpkgs/nixos/greetd.nix
./nixpkgs/nixos/networking.nix
./nixpkgs/nixos/docker.nix
./nixpkgs/nixos/sound.nix
./nixpkgs/nixos/tailscale.nix
./nixpkgs/nixos/users/chris.nix
./nixpkgs/nixos/users/chris-focus.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
}
];
};
packages.x86_64-linux.default = helloDotfiles;
xps-nixos = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs overlays; };
modules = [
./nixpkgs/nixos/xps
nixos-hardware.nixosModules.system76
./nixpkgs/nixos/common.nix
./nixpkgs/nixos/desktop_common.nix
./nixpkgs/nixos/docker.nix
./nixpkgs/nixos/graphical.nix
./nixpkgs/nixos/greetd.nix
./nixpkgs/nixos/networking.nix
./nixpkgs/nixos/sound.nix
./nixpkgs/nixos/tailscale.nix
./nixpkgs/nixos/users/chris-focus.nix
./nixpkgs/nixos/users/chris.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
}
];
checks.x86_64-linux = {
pre-commit = pre-commit-hooks.lib.x86_64-linux.run {
src = ./.;
hooks = {
alejandra.enable = true;
statix.enable = true;
shellcheck.enable = true;
markdown-formatter = {
enable = true;
name = "markdown-formatter";
types = ["markdown"];
language = "system";
pass_filenames = true;
entry = with pkgs.python310Packages; "${mdformat}/bin/mdformat";
};
markdown-linter = {
enable = true;
name = "markdown-linter";
types = ["markdown"];
language = "system";
pass_filenames = true;
entry = with pkgs; "${mdl}/bin/mdl -g";
};
tootsie = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { inherit inputs overlays; };
modules = [
./nixpkgs/nixos/tootsie
./nixpkgs/nixos/common.nix
./nixpkgs/nixos/tailscale.nix
./nixpkgs/nixos/users/chris-minimal.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
}
];
};
devShells = {
x86_64-linux.default = pkgs.mkShell {
inherit (self.checks.x86_64-linux.pre-commit) shellHook;
buildInputs = with pkgs; [
alejandra
cachix
helloDotfiles
mdl
statix
python310Packages.mdformat
];
"locked": {
"lastModified": 1644229661,
"narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_4": {
"pre-commit-hooks": {
"inputs": {
"flake-utils": "flake-utils_3",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1652714503,
"narHash": "sha256-qQKVEfDe5FqvGgkZtg5Pc491foeiDPIOeycHMqnPDps=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "521a524771a8e93caddaa0ac1d67d03766a8b0b3",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
/.direnv/