Centralize Tensamin packages modules and release automation
This commit is contained in:
parent
423ee32a37
commit
123205c97d
28 changed files with 32292 additions and 10 deletions
137
packages/client.nix
Normal file
137
packages/client.nix
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
{ pkgs, rust, rustPlatform, inputs, hashes, mtpSource, mtpVendor }:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
pnpm = pkgs.pnpm;
|
||||
sdk-deps = pkgs.fetchPnpmDeps {
|
||||
pname = "mtp-sdk";
|
||||
version = "0.4.0";
|
||||
src = inputs.mtp;
|
||||
inherit pnpm;
|
||||
fetcherVersion = 4;
|
||||
hash = hashes.sdk;
|
||||
};
|
||||
wasmVersion = (lib.findFirst (p: p.name == "wasm-bindgen") (throw "Missing wasm-bindgen")
|
||||
(builtins.fromTOML (builtins.readFile (inputs.mtp + "/Cargo.lock"))).package).version;
|
||||
wasmBindgen = pkgs.${"wasm-bindgen-cli_" + builtins.replaceStrings [ "." ] [ "_" ] wasmVersion};
|
||||
mtp-sdk = pkgs.stdenv.mkDerivation {
|
||||
pname = "mtp-sdk";
|
||||
version = "0.4.0";
|
||||
src = mtpSource;
|
||||
pnpmDeps = sdk-deps;
|
||||
cargoDeps = mtpVendor;
|
||||
nativeBuildInputs = [ rust pkgs.nodejs pnpm pkgs.pnpmConfigHook rustPlatform.cargoSetupHook wasmBindgen pkgs.lld ];
|
||||
MTP_TYPE_MAPS = "${inputs.mtp-type-maps}/type-maps.yaml";
|
||||
RUSTFLAGS = "--cfg=web_sys_unstable_apis";
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
cargo build --locked --offline --release -p mtp-wasm --target wasm32-unknown-unknown
|
||||
mkdir -p sdk/bindings/wasm/pkg
|
||||
wasm-bindgen target/wasm32-unknown-unknown/release/mtp_wasm.wasm \
|
||||
--target web --out-dir sdk/bindings/wasm/pkg --out-name mtp_wasm
|
||||
pnpm --dir sdk run build:ts
|
||||
# The Nix SDK already contains WASM compiled with the authoritative maps.
|
||||
# Keep the plugin's YAML generation and aliases, but reuse that build.
|
||||
substituteInPlace sdk/dist/vite/index.js \
|
||||
--replace-fail 'await runWasmPack(state);' \
|
||||
'await fs.cp(path.join(packageRoot, "bindings/wasm/pkg"), state.outDir, { recursive: true });'
|
||||
runHook postBuild
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p "$out"
|
||||
cp -r sdk/dist sdk/bindings sdk/type-map sdk/package.json "$out/"
|
||||
mkdir -p "$out/node_modules"
|
||||
cp -rL sdk/node_modules/yaml "$out/node_modules/"
|
||||
'';
|
||||
passthru.source = inputs.mtp;
|
||||
};
|
||||
client-source = pkgs.runCommand "client-source" { nativeBuildInputs = [ pkgs.python3 ]; } ''
|
||||
cp -r ${inputs.client} "$out"
|
||||
chmod -R u+w "$out"
|
||||
rm -rf "$out/mtp-type-maps"
|
||||
cp -r ${inputs.mtp-type-maps} "$out/mtp-type-maps"
|
||||
mkdir -p "$out/.mtp-sdk"
|
||||
cp ${inputs.mtp}/sdk/package.json "$out/.mtp-sdk/package.json"
|
||||
python - "$out" <<'PY'
|
||||
import json, sys, os, re
|
||||
from pathlib import Path
|
||||
root = Path(sys.argv[1])
|
||||
workspace = root / 'pnpm-workspace.yaml'
|
||||
workspace.write_text(re.sub(r'^ mtp:.*\n', "", workspace.read_text(), flags=re.M))
|
||||
for path in root.rglob('package.json'):
|
||||
if '.mtp-sdk' in path.parts:
|
||||
continue
|
||||
data = json.loads(path.read_text())
|
||||
for section in ['dependencies', 'devDependencies']:
|
||||
if 'mtp' in data.get(section, {}):
|
||||
data[section]['mtp'] = 'link:' + os.path.relpath(root / '.mtp-sdk', path.parent)
|
||||
path.write_text(json.dumps(data, indent=2) + '\n')
|
||||
PY
|
||||
${lib.optionalString (builtins.pathExists ./locks/client.yaml) ''cp ${./locks/client.yaml} "$out/pnpm-lock.yaml"''}
|
||||
'';
|
||||
client-deps = pkgs.fetchPnpmDeps {
|
||||
pname = "tensamin";
|
||||
version = "0.0.11";
|
||||
src = client-source;
|
||||
inherit pnpm;
|
||||
fetcherVersion = 4;
|
||||
hash = hashes.client;
|
||||
};
|
||||
common = {
|
||||
version = "0.0.11";
|
||||
src = client-source;
|
||||
pnpmDeps = client-deps;
|
||||
nativeBuildInputs = [ pkgs.nodejs pnpm pkgs.pnpmConfigHook pkgs.makeWrapper ];
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
|
||||
postPatch = ''
|
||||
rm -rf .mtp-sdk
|
||||
cp -r ${mtp-sdk} .mtp-sdk
|
||||
chmod -R u+w .mtp-sdk
|
||||
'';
|
||||
passthru = { source = inputs.client; inherit mtp-sdk; };
|
||||
meta.platforms = [ "x86_64-linux" "aarch64-linux" ];
|
||||
};
|
||||
client-web = pkgs.stdenv.mkDerivation (common // {
|
||||
pname = "tensamin-web";
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
pnpm run build:web
|
||||
runHook postBuild
|
||||
'';
|
||||
installPhase = ''mkdir -p "$out"; cp -r apps/web/dist/. "$out/"'';
|
||||
});
|
||||
client = pkgs.stdenv.mkDerivation (common // {
|
||||
pname = "tensamin";
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
TENSAMIN_WEB_BASE=./ pnpm run build:web
|
||||
pnpm --dir apps/electron run build
|
||||
runHook postBuild
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p "$out/lib/tensamin" "$out/bin"
|
||||
cp -r apps/electron/dist "$out/lib/tensamin/"
|
||||
cp apps/electron/package.json "$out/lib/tensamin/"
|
||||
mkdir -p "$out/lib/web/dist"
|
||||
cp -r apps/web/dist/. "$out/lib/web/dist/"
|
||||
makeWrapper ${pkgs.electron}/bin/electron "$out/bin/tensamin" \
|
||||
--add-flags "$out/lib/tensamin" \
|
||||
--prefix PATH : ${lib.makeBinPath [ pkgs.pulseaudio ]} \
|
||||
--set ELECTRON_OZONE_PLATFORM_HINT auto
|
||||
if [ -d apps/electron/build/icons ]; then
|
||||
mkdir -p "$out/lib/tensamin/build"
|
||||
cp -r apps/electron/build/icons "$out/lib/tensamin/build/icons"
|
||||
install -Dm644 apps/electron/build/icons/icon.png "$out/share/icons/hicolor/512x512/apps/tensamin.png"
|
||||
fi
|
||||
mkdir -p "$out/share/applications"
|
||||
cp ${pkgs.makeDesktopItem {
|
||||
name = "tensamin";
|
||||
desktopName = "Tensamin";
|
||||
exec = "tensamin %U";
|
||||
icon = "tensamin";
|
||||
categories = [ "Network" ];
|
||||
mimeTypes = [ "x-scheme-handler/tensamin" ];
|
||||
}}/share/applications/* "$out/share/applications/"
|
||||
'';
|
||||
meta = common.meta // { mainProgram = "tensamin"; };
|
||||
});
|
||||
in { inherit client client-web mtp-sdk client-source client-deps sdk-deps; }
|
||||
196
packages/default.nix
Normal file
196
packages/default.nix
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
{ inputs, pkgs, system, hashes ? import ./hashes.nix }:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
rust = pkgs.rust-bin.stable.latest.default.override {
|
||||
targets = [ "wasm32-unknown-unknown" "${pkgs.stdenv.hostPlatform.parsed.cpu.name}-unknown-linux-musl" ];
|
||||
extensions = [ "rust-src" "rustfmt" "clippy" ];
|
||||
};
|
||||
rustPlatform = pkgs.makeRustPlatform { cargo = rust; rustc = rust; };
|
||||
projects = [ "iota" "omikron" "omega" "mtp" ];
|
||||
prepared = lib.genAttrs projects (name: pkgs.runCommand "${name}-source" {
|
||||
nativeBuildInputs = [ pkgs.python3 ] ++ lib.optional (name == "iota") rust;
|
||||
} ''
|
||||
python ${../lib/prepare-source.py} ${name} ${inputs.${name}} ${inputs.mtp} \
|
||||
${inputs.mtp-type-maps} ${inputs.iota} "$out"
|
||||
${lib.optionalString (name == "iota") ''
|
||||
rustfmt --edition 2024 "$out/iota-paths/src/lib.rs" "$out/iota-installer/src/lib.rs" "$out/iota-daemon/src/main.rs"
|
||||
''}
|
||||
'');
|
||||
sources = lib.genAttrs projects (name:
|
||||
let lock = ./locks + "/${name}.lock";
|
||||
in if builtins.pathExists lock then pkgs.runCommand "${name}-locked-source" {} ''
|
||||
cp -r ${prepared.${name}} "$out"
|
||||
chmod -R u+w "$out"
|
||||
cp ${lock} "$out/Cargo.lock"
|
||||
'' else prepared.${name});
|
||||
vendors = lib.genAttrs projects (name: rustPlatform.fetchCargoVendor {
|
||||
pname = "${name}-vendor";
|
||||
version = "0.1.0";
|
||||
src = sources.${name};
|
||||
hash = hashes.${name};
|
||||
});
|
||||
mkRust = name: flags: rustPlatform.buildRustPackage {
|
||||
pname = name;
|
||||
version = "0.1.0";
|
||||
src = sources.${name};
|
||||
cargoDeps = vendors.${name};
|
||||
cargoBuildFlags = flags;
|
||||
nativeBuildInputs = [ pkgs.cmake pkgs.perl pkgs.pkg-config ];
|
||||
buildInputs = [ pkgs.openssl pkgs.sqlite ] ++ lib.optional (name == "omega") pkgs.libmysqlclient;
|
||||
dontUseCmakeConfigure = true;
|
||||
MTP_TYPE_MAPS = "${inputs.mtp-type-maps}/type-maps.yaml";
|
||||
doCheck = false;
|
||||
postInstall = lib.optionalString (name == "iota") ''
|
||||
mkdir -p "$out/share/iota"
|
||||
cp -r static/web "$out/share/iota/web"
|
||||
'';
|
||||
passthru = {
|
||||
source = inputs.${name};
|
||||
transformedSource = sources.${name};
|
||||
inherit (inputs) mtp mtp-type-maps;
|
||||
};
|
||||
meta = { platforms = [ "x86_64-linux" "aarch64-linux" ]; mainProgram = name; };
|
||||
};
|
||||
iota = mkRust "iota" [ "-p" "iota" "-p" "iota-daemon" "-p" "iota-updater" "-p" "iota-installer" ];
|
||||
staticPkgs = pkgs.pkgsStatic;
|
||||
iotaPortable = rustPlatform.buildRustPackage {
|
||||
pname = "iota-portable";
|
||||
inherit (iota) version src cargoDeps cargoBuildFlags postInstall;
|
||||
nativeBuildInputs = [ pkgs.cmake pkgs.perl pkgs.pkg-config pkgs.binutils pkgs.removeReferencesTo staticPkgs.stdenv.cc ];
|
||||
buildInputs = [ staticPkgs.openssl staticPkgs.sqlite staticPkgs.zlib ];
|
||||
OPENSSL_STATIC = "1";
|
||||
SQLITE3_STATIC = "1";
|
||||
env.PKG_CONFIG_ALLOW_CROSS = "1";
|
||||
# Keep build scripts on the native host and cross-link only the payload.
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
export CARGO_TARGET_${pkgs.stdenv.buildPlatform.rust.cargoEnvVarTarget}_LINKER=${pkgs.stdenv.cc}/bin/cc
|
||||
export CARGO_TARGET_${staticPkgs.stdenv.hostPlatform.rust.cargoEnvVarTarget}_LINKER=${staticPkgs.stdenv.cc}/bin/${staticPkgs.stdenv.cc.targetPrefix}cc
|
||||
export CC_${pkgs.stdenv.buildPlatform.rust.cargoEnvVarTarget}=${pkgs.stdenv.cc}/bin/cc
|
||||
export CC_${staticPkgs.stdenv.hostPlatform.rust.cargoEnvVarTarget}=${staticPkgs.stdenv.cc}/bin/${staticPkgs.stdenv.cc.targetPrefix}cc
|
||||
export CC_${builtins.replaceStrings [ "-" ] [ "_" ] staticPkgs.stdenv.hostPlatform.rust.rustcTarget}=${staticPkgs.stdenv.cc}/bin/${staticPkgs.stdenv.cc.targetPrefix}cc
|
||||
export CXX_${builtins.replaceStrings [ "-" ] [ "_" ] staticPkgs.stdenv.hostPlatform.rust.rustcTarget}=${staticPkgs.stdenv.cc}/bin/${staticPkgs.stdenv.cc.targetPrefix}c++
|
||||
unset NIX_CFLAGS_COMPILE NIX_LDFLAGS
|
||||
export CARGO_TARGET_${staticPkgs.stdenv.hostPlatform.rust.cargoEnvVarTarget}_RUSTFLAGS="-L native=${lib.getLib staticPkgs.zlib}/lib --remap-path-prefix=/nix/store=/usr/src"
|
||||
cargo build --locked --offline --release -j "$NIX_BUILD_CORES" \
|
||||
--target ${staticPkgs.stdenv.hostPlatform.rust.rustcTarget} ${lib.escapeShellArgs iota.cargoBuildFlags}
|
||||
runHook postBuild
|
||||
'';
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p "$out/bin"
|
||||
for binary in iota iota-daemon iota-updater iota-release iota-bundle; do
|
||||
cp "target/${staticPkgs.stdenv.hostPlatform.rust.rustcTarget}/release/$binary" "$out/bin/"
|
||||
done
|
||||
runHook postInstall
|
||||
'';
|
||||
dontUseCmakeConfigure = true;
|
||||
MTP_TYPE_MAPS = "${inputs.mtp-type-maps}/type-maps.yaml";
|
||||
doCheck = false;
|
||||
# Reject accidental dynamic linkage before these binaries reach a release.
|
||||
postFixup = ''
|
||||
for binary in "$out"/bin/*; do
|
||||
# Prebuilt Rust std retains compiler source paths in panic messages.
|
||||
remove-references-to -t ${rust} "$binary"
|
||||
if readelf -l "$binary" | grep -q INTERP || readelf -d "$binary" | grep -q NEEDED; then
|
||||
echo "Non-portable ELF: $binary" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
'';
|
||||
allowedReferences = [ "out" ];
|
||||
meta = iota.meta;
|
||||
};
|
||||
clientPackages = import ./client.nix {
|
||||
inherit pkgs rust rustPlatform inputs hashes;
|
||||
mtpSource = sources.mtp;
|
||||
mtpVendor = vendors.mtp;
|
||||
};
|
||||
packages = {
|
||||
inherit iota;
|
||||
iota-portable = iotaPortable;
|
||||
iota-bundle = pkgs.runCommand "iota-bundle" {} ''
|
||||
mkdir -p "$out/bin" "$out/share/iota"
|
||||
cp -r ${iota}/bin/. "$out/bin/"
|
||||
cp -r ${inputs.iota}/systemd "$out/share/iota/"
|
||||
cp -r ${sources.iota}/scripts "$out/share/iota/"
|
||||
cp ${inputs.iota}/iota-updater/artifacts.tsv "$out/share/iota/"
|
||||
cp -r ${iota}/share/iota/web "$out/share/iota/static-web"
|
||||
cp ${sources.iota}/iota-installer/bundle-files.txt "$out/share/iota/"
|
||||
'';
|
||||
iota-daemon = iota.overrideAttrs {
|
||||
pname = "iota-daemon";
|
||||
cargoBuildFlags = [ "-p" "iota-daemon" ];
|
||||
meta.mainProgram = "iota-daemon";
|
||||
};
|
||||
iota-ui = iota.overrideAttrs {
|
||||
pname = "iota-ui";
|
||||
cargoBuildFlags = [ "-p" "iota" ];
|
||||
postInstall = iota.postInstall + ''mv "$out/bin/iota" "$out/bin/iota-ui"'';
|
||||
meta.mainProgram = "iota-ui";
|
||||
};
|
||||
omikron = mkRust "omikron" [];
|
||||
omega = mkRust "omega" [];
|
||||
iota-container = pkgs.dockerTools.buildLayeredImage {
|
||||
name = "tensamin/iota";
|
||||
tag = "${inputs.iota.shortRev or "local"}";
|
||||
contents = [ iotaPortable pkgs.cacert pkgs.dockerTools.binSh ];
|
||||
fakeRootCommands = ''
|
||||
mkdir -p var/lib/iota/config var/lib/iota/runtime tmp
|
||||
chmod 1777 tmp
|
||||
chown -R 1000:1000 var/lib/iota
|
||||
'';
|
||||
enableFakechroot = true;
|
||||
config = {
|
||||
Entrypoint = [ "${iotaPortable}/bin/iota-daemon" ];
|
||||
User = "1000:1000";
|
||||
WorkingDir = "/var/lib/iota";
|
||||
Volumes = { "/var/lib/iota" = {}; };
|
||||
ExposedPorts = { "1984/tcp" = {}; "1984/udp" = {}; };
|
||||
Env = [
|
||||
"HOME=/var/lib/iota"
|
||||
"IOTA_DATA_ROOT=/var/lib/iota"
|
||||
"IOTA_DEPLOYMENT_MODE=user_local"
|
||||
"IOTA_ASSET_DIR=${iotaPortable}/share/iota/web"
|
||||
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||
];
|
||||
};
|
||||
};
|
||||
omikron-container = pkgs.dockerTools.buildLayeredImage {
|
||||
name = "tensamin/omikron";
|
||||
tag = "${inputs.omikron.shortRev or "local"}";
|
||||
contents = [ packages.omikron pkgs.cacert pkgs.dockerTools.binSh ];
|
||||
config = {
|
||||
Entrypoint = [ "${packages.omikron}/bin/omikron" ];
|
||||
WorkingDir = "/var/lib/omikron";
|
||||
Env = [ "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ];
|
||||
};
|
||||
};
|
||||
omega-container = pkgs.dockerTools.buildLayeredImage {
|
||||
name = "tensamin/omega";
|
||||
tag = "${inputs.omega.shortRev or "local"}";
|
||||
contents = [ packages.omega pkgs.cacert pkgs.dockerTools.binSh ];
|
||||
config = {
|
||||
Entrypoint = [ "${packages.omega}/bin/omega" ];
|
||||
WorkingDir = "/var/lib/omega";
|
||||
Env = [ "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ];
|
||||
};
|
||||
};
|
||||
inherit (clientPackages) client client-web mtp-sdk;
|
||||
default = packages.client;
|
||||
update-all = pkgs.writeShellApplication {
|
||||
name = "update-all";
|
||||
runtimeInputs = [ pkgs.nix pkgs.git pkgs.python3 rust pkgs.nodejs pkgs.pnpm pkgs.coreutils ];
|
||||
text = ''exec python ${../scripts/update-all.py} "$@"'';
|
||||
};
|
||||
} // lib.mapAttrs' (name: value: lib.nameValuePair "${name}-source" value) prepared
|
||||
// lib.mapAttrs' (name: value: lib.nameValuePair "${name}-vendor" value) vendors
|
||||
// { inherit (clientPackages) client-source client-deps sdk-deps; };
|
||||
in {
|
||||
inherit packages;
|
||||
devShells = lib.genAttrs [ "iota" "omikron" "omega" "mtp" "client" ] (name: pkgs.mkShell {
|
||||
packages = [ rust pkgs.git pkgs.cmake pkgs.perl pkgs.pkg-config pkgs.nodejs pkgs.pnpm pkgs.wasm-pack ];
|
||||
buildInputs = [ pkgs.openssl pkgs.sqlite ] ++ lib.optional (name == "omega") pkgs.libmysqlclient;
|
||||
MTP_TYPE_MAPS = "${inputs.mtp-type-maps}/type-maps.yaml";
|
||||
}) // { default = pkgs.mkShell { packages = [ packages.update-all ]; }; };
|
||||
}
|
||||
8
packages/hashes.nix
Normal file
8
packages/hashes.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
iota = "sha256-LYr2OqOSUX3AXwoJr15xtswpHRVz50evrrT7ep5FZog=";
|
||||
omikron = "sha256-HFSZ6fEzMzHWFrjPBY08XaLC+OoNNIURflvO/CVaKZM=";
|
||||
omega = "sha256-rvz6UoZlKPHvTsURLTBqSjuPvdwlKpmKBQ1+27fzC8I=";
|
||||
mtp = "sha256-zfucKSWEIx3l1L9lTj1lidD/qE/HwueLBVJoND51MMU=";
|
||||
client = "sha256-w6onBznxx/7bsIjodQz98tcsudcDNEtTPJgf2bmrXSU=";
|
||||
sdk = "sha256-24mJCREdij/ha95AdmxOIsE/cHKkiBNoQCPANau1GcU=";
|
||||
}
|
||||
10385
packages/locks/client.yaml
Normal file
10385
packages/locks/client.yaml
Normal file
File diff suppressed because it is too large
Load diff
6163
packages/locks/iota.lock
Normal file
6163
packages/locks/iota.lock
Normal file
File diff suppressed because it is too large
Load diff
5677
packages/locks/mtp.lock
Normal file
5677
packages/locks/mtp.lock
Normal file
File diff suppressed because it is too large
Load diff
3359
packages/locks/omega.lock
Normal file
3359
packages/locks/omega.lock
Normal file
File diff suppressed because it is too large
Load diff
4180
packages/locks/omikron.lock
Normal file
4180
packages/locks/omikron.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue