From 31b7968e7d699e18d57f16a858020ea759dd6850 Mon Sep 17 00:00:00 2001 From: apentori Date: Tue, 20 Oct 2026 12:03:27 +0200 Subject: [PATCH 1/2] nix: add nix flake Signed-off-by: apentori --- .github/workflows/nix.yml | 37 ++++++ README.md | 8 ++ flake.lock | 27 +++++ flake.nix | 240 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 312 insertions(+) create mode 100644 .github/workflows/nix.yml create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml new file mode 100644 index 0000000..33619fd --- /dev/null +++ b/.github/workflows/nix.yml @@ -0,0 +1,37 @@ +name: Nix + +on: + pull_request: + branches: [master] + push: + branches: [master] + +permissions: + contents: read + +concurrency: + group: nix-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + flake: + name: nix flake check + runs-on: ['self-hosted','ubuntu-22.04'] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + experimental-features = nix-command flakes + + # Builds the package, imports the SDK and verifies that the version and + # the packaged dependencies match `pyproject.toml`. + - name: Check the flake + run: nix flake check --print-build-logs + + - name: Build the package + run: nix build .#status-sdk diff --git a/README.md b/README.md index 8e78a63..c7cf72d 100644 --- a/README.md +++ b/README.md @@ -146,3 +146,11 @@ The compose file reads three variables from the environment. All of them have a ``` STATUS_GO_COMMIT=2bee8b6a38cdc8f92d74e2dbb8c4e77fbbeea149 PLATFORM=linux/amd64 docker compose -f status_sdk/docker-compose.yaml up -d ``` + +### Nix + +The SDK is packaged as a nix flake. It provides the library itself, a development shell and a set of checks. + +```bash +nix develop +``` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1c688af --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1792210252, + "narHash": "sha256-Mh0PUaIv4nFY+8wAGonaEh8iBzwsmdNlwjVHmFg1Biw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "09063bd1f2b36fd9fd793c36dfade7bd40a93dfc", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..bf37cba --- /dev/null +++ b/flake.nix @@ -0,0 +1,240 @@ +{ + description = "Status Python SDK"; + + inputs = { + # A channel URL: the exact revision is pinned in `flake.lock` and can be + # moved forward with `nix flake update`. + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = + { self, nixpkgs }: + let + inherit (nixpkgs) lib; + + systems = lib.filter (system: lib.elem system nixpkgs.lib.systems.flakeExposed) [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + ]; + + forAllSystems = lib.genAttrs systems; + pkgsFor = system: nixpkgs.legacyPackages.${system}; + + version = "1.4.0"; + + release = { + rev = "1.4.0"; + hash = "sha256-vNXGUaqJdzoCCVl/CnubBdR7TiVG94g+/FcFJAJSdUU="; + }; + + fetchRelease = + pkgs: + pkgs.fetchFromGitHub { + owner = "status-im"; + repo = "status-python-sdk"; + rev = release.rev; + hash = release.hash; + }; + + mkStatusSdk = + { + system, + pname ? "status-sdk", + }: + let + pkgs = pkgsFor system; + + # nixpkgs' `eth-utils` 6.0.0 imports `pydantic` at runtime but lists + # it as a test dependency only, so `import eth_utils` fails without + # it. `eth-abi` depends on `eth-utils` and the SDK depends on + # `eth-abi`, so the missing transitive dependency is fixed here + # instead of adding `pydantic` to the SDK's own dependencies. + python = pkgs.python3.override { + packageOverrides = pySelf: pySuper: { + eth-utils = pySuper.eth-utils.overridePythonAttrs (old: { + dependencies = old.dependencies ++ [ pySuper.pydantic ]; + }); + }; + }; + in + python.pkgs.buildPythonPackage { + inherit pname version; + + src = fetchRelease pkgs; + + format = "pyproject"; + build-system = [ python.pkgs.setuptools ]; + + dependencies = with python.pkgs; [ + requests + websocket-client + websockets + pandas + pillow + eth-abi + pyyaml + pycryptodome + ]; + + doCheck = false; + + pythonImportsCheck = [ "status_sdk" ]; + + # at runtime the SDK writes into its own installation directory + # (`assets/`, `data/`, `backups/`). That directory is read-only inside + meta = { + description = "Python SDK for the Status App backend (status-im/status-go)"; + homepage = "https://github.com/status-im/status-python-sdk"; + license = lib.licenses.mit; + platforms = lib.platforms.unix; + maintainers = [ + { + name = "Nick Ninov"; + email = "nikolay@status.im"; + } + { + name = "Alexis Pentori"; + email = "alexis@status.im"; + } + ]; + }; + }; + in + { + packages = forAllSystems ( + system: + let + base = mkStatusSdk { inherit system; }; + in + { + default = base; + status-sdk = base; + } + ); + + formatter = forAllSystems (system: (pkgsFor system).nixfmt); + + overlays.default = final: prev: { + python3Packages = prev.python3Packages // { + status-sdk = self.packages.${final.stdenv.hostPlatform.system}.status-sdk; + }; + }; + + devShells = forAllSystems ( + system: + let + pkgs = pkgsFor system; + python = pkgs.python3; + base = self.packages.${system}.status-sdk; + in + { + default = pkgs.mkShell { + name = "status-sdk-shell"; + packages = [ + (python.withPackages (ps: [ base ])) + + # Linting and testing. + pkgs.ruff + python.pkgs.mypy + python.pkgs.pytest + + # Tools the SDK shells out to: Docker for `launch_docker_container`, + # Nix and Git for `build_and_launch`. + pkgs.docker-client + pkgs.nix + pkgs.git + pkgs.curl + pkgs.jq + pkgs.gnumake + ]; + + shellHook = '' + # Prefer the working tree over the packaged copy, so that edits are + # picked up without reinstalling anything. + if [ -d "$PWD/status_sdk" ]; then + export PYTHONPATH="$PWD''${PYTHONPATH:+:$PYTHONPATH}" + fi + + echo "status-sdk development shell" + echo " the SDK is importable: python3 -c 'import status_sdk'" + echo " linting: ruff check . && ruff format --check ." + echo " the optional extras (agents, community-greet) are not packaged" + echo " in Nix - see the README for how to run the examples" + ''; + }; + } + ); + + checks = forAllSystems ( + system: + let + pkgs = pkgsFor system; + python = pkgs.python3; + base = self.packages.${system}.status-sdk; + in + { + default = base; + + import = + pkgs.runCommand "status-sdk-import-check" + { + nativeBuildInputs = [ (python.withPackages (ps: [ base ])) ]; + } + '' + python3 -c "import status_sdk; print('status-sdk', status_sdk.__version__)" + touch $out + ''; + + version = + pkgs.runCommand "status-sdk-version-check" + { + nativeBuildInputs = [ python ]; + released = fetchRelease pkgs; + flakeSrc = ./.; + flakeVersion = version; + } + '' + released="$released" flake_src="$flakeSrc" flake_version="$flakeVersion" python3 - <<'PY' + import os, re, tomllib, pathlib + + def version(path): + return tomllib.loads(pathlib.Path(path).read_text())["project"]["version"] + + released = version(os.path.join(os.environ["released"], "pyproject.toml")) + local = version(os.path.join(os.environ["flake_src"], "pyproject.toml")) + flake = os.environ["flake_version"] + + assert released == local, f"released sources are {released}, pyproject.toml is {local}" + assert released == flake, f"released sources are {released}, the flake is {flake}" + + changelog = pathlib.Path(os.path.join(os.environ["flake_src"], "CHANGELOG.md")).read_text() + entry = r"^## \[" + re.escape(local) + r"\] - \d{4}-\d{2}-\d{2}$" + assert re.search(entry, changelog, re.M), f"no changelog entry for {local}" + PY + touch $out + ''; + + dependencies = + pkgs.runCommand "status-sdk-dependencies-check" + { + nativeBuildInputs = [ python ]; + flakeSrc = ./.; + nixDependencies = builtins.concatStringsSep " " (map (dep: dep.pname) base.dependencies); + } + '' + nix_dependencies="$nixDependencies" flake_src="$flakeSrc" python3 - <<'PY' + import os, re, tomllib, pathlib + + pyproject = tomllib.loads(pathlib.Path(os.path.join(os.environ["flake_src"], "pyproject.toml")).read_text()) + declared = sorted(re.split(r"[^\w.-]", dep)[0] for dep in pyproject["project"]["dependencies"]) + packaged = sorted(os.environ["nix_dependencies"].split()) + + assert packaged == declared, f"packaged dependencies {packaged} do not match pyproject.toml {declared}" + PY + touch $out + ''; + } + ); + }; +} From 27d06fc825b25a1b542d3c569dd12390561d3f04 Mon Sep 17 00:00:00 2001 From: apentori Date: Tue, 20 Oct 2026 13:16:11 +0200 Subject: [PATCH 2/2] ci: use self-hosted runner Signed-off-by: apentori --- .github/workflows/publish.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index feb1f96..daa6701 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ on: jobs: build: name: Build distribution - runs-on: ubuntu-latest + runs-on: ['self-hosted','ubuntu-22.04'] steps: - name: Check out repository diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f4f9293..672f7c0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ on: jobs: version: name: Verify Version Bump - runs-on: ubuntu-latest + runs-on: ['self-hosted','ubuntu-22.04'] steps: - name: Check out repository