This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
||||||
|
language: "en-US"
|
||||||
|
early_access: false
|
||||||
|
reviews:
|
||||||
|
profile: "chill"
|
||||||
|
request_changes_workflow: false
|
||||||
|
high_level_summary: false
|
||||||
|
poem: false
|
||||||
|
review_status: true
|
||||||
|
review_details: false
|
||||||
|
auto_review:
|
||||||
|
enabled: true
|
||||||
|
drafts: false
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
result
|
||||||
|
.direnv
|
||||||
|
/.pre-commit-config.yaml
|
||||||
|
*.AppImage
|
||||||
|
*.exe
|
||||||
|
/target
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
extends: default
|
||||||
|
rules:
|
||||||
|
line-length:
|
||||||
|
max: 160
|
||||||
|
truthy:
|
||||||
|
check-keys: false
|
||||||
|
document-start:
|
||||||
|
present: false
|
||||||
|
comments-indentation: disable
|
||||||
Generated
+561
@@ -0,0 +1,561 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"once_cell_polyfill",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.104"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "duct"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e66e9c0c03d094e1a0ba1be130b849034aa80c3a2ab8ee94316bc809f3fa684"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"os_pipe",
|
||||||
|
"shared_child",
|
||||||
|
"shared_thread",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.189"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mutually_exclusive_features"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e94e1e6445d314f972ff7395df2de295fe51b71821694f0b0e1e79c4f12c8577"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.31.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.50.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_pipe"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "runner"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"duct",
|
||||||
|
"mutually_exclusive_features",
|
||||||
|
"nix",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"walkdir",
|
||||||
|
"xdgdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared_child"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"sigchld",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared_thread"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52b86057fcb5423f5018e331ac04623e32d6b5ce85e33300f92c79a1973928b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sigchld"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"os_pipe",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "3.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||||
|
dependencies = [
|
||||||
|
"nu-ansi-term",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.53.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xdgdir"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c93fba109a63f08e168ab02529ddabd99e4ebb78b98ed6bce82e3da52522d235"
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "3"
|
||||||
|
members = ["crates/runner"]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
edition = "2024"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[workspace.metadata.crane]
|
||||||
|
name = "affinity-nix"
|
||||||
|
|
||||||
|
[workspace.lints.clippy]
|
||||||
|
pedantic = { level = "deny" }
|
||||||
|
nursery = { level = "deny" }
|
||||||
+674
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
# affinity-nix
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Affinity v3 & v2 packaged with Nix!
|
||||||
|
|
||||||
|
Based on https://github.com/lf-/affinity-crimes and https://affinity.liz.pet/, and uses [ElementalWarrior's wine](https://gitlab.winehq.org/ElementalWarrior/wine).
|
||||||
|
|
||||||
|
We also install https://github.com/noahc3/AffinityPluginLoader for a far more pleasant experience.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Affinity on Wine is far from perfect and you will likely experience bugs and issues. Please have patience and [report any issues you experience](https://github.com/mrshmllow/affinity-nix/issues/new/choose).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Thank you to my GitHub sponsors!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Preamble
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> We provide a binary cache for this repo. Please [add cache.forall.systems as a substituter](https://cache.forall.systems/) to avoid compiling yourself.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This repo does not attempt to redistribute affinity archives. Any instance of caching Canva IP should be reported as a bug.
|
||||||
|
|
||||||
|
User preferences are located in `$XDG_DATA_HOME/affinity/` or `$XDG_DATA_HOME/affinity-v3/` falling back to `$HOME/.local/share/affinity/` or `$HOME/.local/share/affinity-v3/`.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
A wine prefix containing all the necessary dependencies and the affinity installation is built in nix and mounted at runtime.
|
||||||
|
Overlayfs is used to keep your user preferences intact. [fuse-overlayfs](https://github.com/containers/fuse-overlayfs) will be fallen back on if your kernel rejects unprivileged user namespaces, common on hardened systems. This can reduce performance.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR;
|
||||||
|
A([Winetricks])-.->B[Nix Store];
|
||||||
|
C([Affinity])-.->B;
|
||||||
|
D([Wine])-.->B;
|
||||||
|
|
||||||
|
B e1@-->E[Overlayfs];
|
||||||
|
F[User Data] e2@<-->E;
|
||||||
|
|
||||||
|
E<-- Mounted @ Runtime -->G[Affinity Application];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Instructions
|
||||||
|
|
||||||
|
### Running Ad-hoc
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ nix run github:mrshmllow/affinity-nix#affinity-v3
|
||||||
|
|
||||||
|
-- v2 versions:
|
||||||
|
|
||||||
|
$ nix run github:mrshmllow/affinity-nix#affinity-photo
|
||||||
|
$ nix run github:mrshmllow/affinity-nix#affinity-designer
|
||||||
|
$ nix run github:mrshmllow/affinity-nix#affinity-publisher
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installing the applications on your system (Optional)
|
||||||
|
|
||||||
|
#### Install with nix-profile
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ nix profile install github:mrshmllow/affinity-nix#affinity-v3
|
||||||
|
|
||||||
|
-- v2 versions:
|
||||||
|
|
||||||
|
$ nix profile install github:mrshmllow/affinity-nix#affinity-photo
|
||||||
|
$ nix profile install github:mrshmllow/affinity-nix#affinity-designer
|
||||||
|
$ nix profile install github:mrshmllow/affinity-nix#affinity-publisher
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Install on NixOS / Home Manager with Flakes
|
||||||
|
|
||||||
|
Installing via an overlay is recommended, as the package is `unfree`
|
||||||
|
making it difficult to use directly.
|
||||||
|
|
||||||
|
Install with NixOS:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# flake.nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
affinity-nix.url = "github:mrshmllow/affinity-nix";
|
||||||
|
# ...
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = inputs @ {
|
||||||
|
affinity-nix,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
nixosConfigurations.my-system = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs = {inherit inputs;};
|
||||||
|
modules = [
|
||||||
|
# ...
|
||||||
|
({ pkgs, ... }: {
|
||||||
|
nixpkgs.overlays = [ affinity-nix.overlays.default ];
|
||||||
|
|
||||||
|
environment.systemPackages = [ pkgs.affinity-v3 ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Install with Home Manager:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# flake.nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
affinity-nix.url = "github:mrshmllow/affinity-nix";
|
||||||
|
# ...
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = inputs @ {
|
||||||
|
affinity-nix,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
homeConfigurations.my-user = home-manager.lib.homeManagerConfiguration {
|
||||||
|
pkgs = nixpkgs.legacyPackages."x86_64-linux";
|
||||||
|
extraSpecialArgs = {inherit inputs;};
|
||||||
|
modules = [
|
||||||
|
# ...
|
||||||
|
({ pkgs, ... }: {
|
||||||
|
nixpkgs.overlays = [ affinity-nix.overlays.default ];
|
||||||
|
|
||||||
|
home.packages = [ pkgs.affinity-v3 ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Install on NixOS without Flakes
|
||||||
|
|
||||||
|
Install without flakes:
|
||||||
|
|
||||||
|
```nixos
|
||||||
|
# configuration.nix
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
affinity = import (fetchTarball "https://github.com/mrshmllow/affinity-nix/archive/refs/heads/main.tar.gz");
|
||||||
|
in
|
||||||
|
{
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
affinity.overlays.default
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.my-user.packages = with pkgs; [
|
||||||
|
affinity-photo
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Troubleshooting, winetricks, wineboot, and more
|
||||||
|
|
||||||
|
Each package (`v3|photo|designer|publisher`) has the following usage:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ affinity-v3 --help
|
||||||
|
Usage: affinity-v3 [OPTIONS] [AFFINITY_ARGUMENTS]... [COMMAND]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
wine Run wine within sandbox
|
||||||
|
winetricks Run winetricks within sandbox
|
||||||
|
wineboot Run wineboot within sandbox
|
||||||
|
wineserver Run wineserver within sandbox
|
||||||
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
[AFFINITY_ARGUMENTS]... Arguments for affinity application
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--verbose Make Wine far more verbose
|
||||||
|
-h, --help Print help
|
||||||
|
-V, --version Print version
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Armed with these you should be able to follow https://affinity.liz.pet/v2/misc-troubleshooting/ for troubleshooting steps.
|
||||||
|
|
||||||
|
For example, accessing `wine`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ affinity-v3 wine
|
||||||
|
Usage: wine PROGRAM [ARGUMENTS...] Run the specified program
|
||||||
|
wine --help Display this help and exit
|
||||||
|
wine --version Output version information and exit
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Or `winecfg`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ affinity-v3 wine winecfg
|
||||||
|
```
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "runner"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.102"
|
||||||
|
clap = { version = "4.6.0", features = ["derive"] }
|
||||||
|
duct = "1.1.1"
|
||||||
|
mutually_exclusive_features = "0.1.0"
|
||||||
|
nix = { version = "0.31.2", features = ["sched", "mount"] }
|
||||||
|
tracing = "0.1.44"
|
||||||
|
tracing-subscriber = "0.3.23"
|
||||||
|
walkdir = "2.5.0"
|
||||||
|
xdgdir = "0.8.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["v3"]
|
||||||
|
|
||||||
|
v3 = []
|
||||||
|
photo = []
|
||||||
|
designer = []
|
||||||
|
publisher = []
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::LazyLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use duct::cmd;
|
||||||
|
use tracing::{info, instrument};
|
||||||
|
|
||||||
|
use crate::make_env;
|
||||||
|
|
||||||
|
pub static REGISTRY_PATCHES: LazyLock<PathBuf> =
|
||||||
|
LazyLock::new(|| PathBuf::from(env!("REGISTRY_PATCHES")));
|
||||||
|
pub static ON_LINUX: LazyLock<PathBuf> = LazyLock::new(|| PathBuf::from(env!("ON_LINUX")));
|
||||||
|
pub const RSYNC: &str = env!("RSYNC");
|
||||||
|
|
||||||
|
const LATEST_REVISION: u32 = 10;
|
||||||
|
|
||||||
|
#[instrument(skip_all, ret)]
|
||||||
|
pub fn read_revision(wine_prefix: &Path) -> Result<Option<u32>> {
|
||||||
|
let revision_file = wine_prefix.join(".revision");
|
||||||
|
|
||||||
|
if !revision_file.is_file() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let revision: u32 = fs::read_to_string(revision_file)
|
||||||
|
.context("reading revision file")?
|
||||||
|
.split_whitespace()
|
||||||
|
.collect::<String>()
|
||||||
|
.parse()
|
||||||
|
.context("parsing revision to u32")?;
|
||||||
|
|
||||||
|
return Ok(Some(revision));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub fn sync_v2_settings(wine_prefix: &Path, user: &str) -> Result<()> {
|
||||||
|
for app in ["Photo", "Designer", "Publisher"] {
|
||||||
|
let app_settings_dst = wine_prefix.join(format!(
|
||||||
|
"drive_c/users/{user}/AppData/Roaming/Affinity/{app}/2.0/"
|
||||||
|
));
|
||||||
|
let app_settings_src = ON_LINUX.join(format!("Auxiliary/Settings/{app}/2.0/"));
|
||||||
|
|
||||||
|
fs::create_dir_all(&app_settings_dst).context(format!(
|
||||||
|
"creating settings directory for {app} w/ user {user}: {}",
|
||||||
|
app_settings_dst.display()
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let rsync = cmd!(
|
||||||
|
RSYNC,
|
||||||
|
"-v",
|
||||||
|
"--ignore-existing",
|
||||||
|
"--chmod=D755,F644",
|
||||||
|
"--recursive",
|
||||||
|
&app_settings_src,
|
||||||
|
&app_settings_dst
|
||||||
|
)
|
||||||
|
.stderr_to_stdout()
|
||||||
|
.read()
|
||||||
|
.context(format!(
|
||||||
|
"syncing {app} settings with rsync: dst {}, src {}",
|
||||||
|
app_settings_dst.display(),
|
||||||
|
app_settings_src.display()
|
||||||
|
))?;
|
||||||
|
|
||||||
|
for line in rsync.lines().filter(|x| !x.is_empty()) {
|
||||||
|
info!(app = ?app, "{line}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_revision(wine_prefix: &Path) -> Result<()> {
|
||||||
|
let revision_file = wine_prefix.join(".revision");
|
||||||
|
|
||||||
|
fs::write(&revision_file, LATEST_REVISION.to_string()).context("writing revision to file")?;
|
||||||
|
|
||||||
|
info!("wrote {LATEST_REVISION:?} to {revision_file:?}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub fn perform_migrations(wine_prefix: &Path) -> Result<()> {
|
||||||
|
let revision = crate::check::read_revision(wine_prefix)?.unwrap_or(0);
|
||||||
|
|
||||||
|
if revision < 10 {
|
||||||
|
let migration = make_env(
|
||||||
|
&cmd!(
|
||||||
|
crate::WINE,
|
||||||
|
"regedit",
|
||||||
|
"/S",
|
||||||
|
REGISTRY_PATCHES.join("one.reg")
|
||||||
|
),
|
||||||
|
wine_prefix,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.stderr_to_stdout()
|
||||||
|
.read()
|
||||||
|
.context("applying one.reg")?;
|
||||||
|
|
||||||
|
for line in migration.lines() {
|
||||||
|
info!("vkd3d migration: {line}");
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("finished vkd3d migration.");
|
||||||
|
}
|
||||||
|
|
||||||
|
write_revision(wine_prefix).context("writing revision")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,619 @@
|
|||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
fs::{self},
|
||||||
|
io::{self, BufRead, BufReader},
|
||||||
|
os::unix::fs::PermissionsExt,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::LazyLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
use duct::cmd;
|
||||||
|
use mutually_exclusive_features::exactly_one_of;
|
||||||
|
use nix::{
|
||||||
|
libc::{self, S_IWUSR},
|
||||||
|
mount::{MsFlags, mount, umount},
|
||||||
|
sched::{CloneFlags, unshare},
|
||||||
|
sys::wait::{WaitStatus, waitpid},
|
||||||
|
unistd::{ForkResult, fork},
|
||||||
|
};
|
||||||
|
use tracing::{Level, error, info, instrument};
|
||||||
|
use tracing_subscriber::FmtSubscriber;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
use xdgdir::BaseDir;
|
||||||
|
|
||||||
|
use crate::check::{perform_migrations, sync_v2_settings};
|
||||||
|
|
||||||
|
mod check;
|
||||||
|
mod migrate;
|
||||||
|
|
||||||
|
exactly_one_of!("v3", "photo", "designer", "publisher");
|
||||||
|
|
||||||
|
const MOUNT_FAIL_STATUS: i32 = 111;
|
||||||
|
|
||||||
|
pub(crate) const WINE: &str = env!("WINE");
|
||||||
|
pub(crate) const WINESERVER: &str = env!("WINESERVER");
|
||||||
|
pub(crate) const WINETRICKS: &str = env!("WINETRICKS");
|
||||||
|
pub(crate) const FUSE_OVERLAYFS: &str = env!("FUSE_OVERLAYFS");
|
||||||
|
pub(crate) const GNUTAR: &str = env!("GNUTAR");
|
||||||
|
pub(crate) const ZENITY: &str = env!("ZENITY");
|
||||||
|
|
||||||
|
pub(crate) static LOWER_DIR: LazyLock<PathBuf> =
|
||||||
|
LazyLock::new(|| PathBuf::from(env::var("LOWER_DIR").expect("LOWER_DIR env var to exist")));
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Arguments {
|
||||||
|
/// Make Wine far more verbose.
|
||||||
|
#[arg(long)]
|
||||||
|
verbose: bool,
|
||||||
|
|
||||||
|
/// Specified tool to use. If none matches, defaults to the affinity application.
|
||||||
|
#[command(subcommand)]
|
||||||
|
subcommand: Option<Program>,
|
||||||
|
|
||||||
|
/// Arguments for affinity application
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
affinity: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum Program {
|
||||||
|
/// Run wine within sandbox
|
||||||
|
Wine {
|
||||||
|
/// arguments to wine
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
arguments: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Run winetricks within sandbox
|
||||||
|
Winetricks {
|
||||||
|
/// arguments to winetricks
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
arguments: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Run wineboot within sandbox
|
||||||
|
Wineboot {
|
||||||
|
/// arguments to wineboot
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
arguments: Vec<String>,
|
||||||
|
},
|
||||||
|
/// Run wineserver within sandbox
|
||||||
|
Wineserver {
|
||||||
|
/// arguments to wineserver
|
||||||
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||||
|
arguments: Vec<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum ProgramToExecute {
|
||||||
|
Affinity { arguments: Vec<String> },
|
||||||
|
Other(Program),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Paths {
|
||||||
|
upper: PathBuf,
|
||||||
|
work: PathBuf,
|
||||||
|
wine_prefix: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Paths {
|
||||||
|
fn ensure_created(&self) -> anyhow::Result<()> {
|
||||||
|
let work_dir = self.work.join("work");
|
||||||
|
|
||||||
|
if work_dir.is_dir() {
|
||||||
|
let mut perms = fs::metadata(&work_dir)?.permissions();
|
||||||
|
perms.set_mode(0o700);
|
||||||
|
fs::set_permissions(&work_dir, perms).context("set workdir permissions")?;
|
||||||
|
|
||||||
|
fs::remove_dir_all(&work_dir).context("delete workdir")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::create_dir_all(&self.upper).context("creating upper")?;
|
||||||
|
fs::create_dir_all(&self.work).context("creating work")?;
|
||||||
|
fs::create_dir_all(&self.wine_prefix).context("creating wine_prefix")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_tracing() {
|
||||||
|
let subscriber = FmtSubscriber::builder()
|
||||||
|
.with_max_level(Level::TRACE)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_env(expression: &duct::Expression, wine_prefix: &Path, verbose: bool) -> duct::Expression {
|
||||||
|
let expression = expression.env("WINEPREFIX", wine_prefix.display().to_string());
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
expression.env("WINEDEBUG", "-all,fixme-all".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
fn warmup_prefix_directories(destination: &PathBuf) -> io::Result<()> {
|
||||||
|
for entry in WalkDir::new(&*LOWER_DIR)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(std::result::Result::ok)
|
||||||
|
{
|
||||||
|
if !entry.file_type().is_dir() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let relative_path = entry.path().strip_prefix(&*LOWER_DIR).unwrap();
|
||||||
|
let target_path = destination.join(relative_path);
|
||||||
|
|
||||||
|
if let Err(err) = fs::create_dir_all(&target_path) {
|
||||||
|
error!(target_path = ?target_path, err = ?err, "failed to create");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
|
fn warmup_prefix_registry(destination: &PathBuf, user: &String) -> io::Result<()> {
|
||||||
|
let important_files = vec!["system.reg", "user.reg", "userdef.reg", ".update-timestamp"];
|
||||||
|
|
||||||
|
for file in important_files {
|
||||||
|
let src_path = LOWER_DIR.join(file);
|
||||||
|
let dst_path = destination.join(file);
|
||||||
|
|
||||||
|
if !src_path.try_exists()? || dst_path.try_exists()? {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = fs::read_to_string(&src_path)?;
|
||||||
|
let modified_content = content.replace("nixbld", user);
|
||||||
|
|
||||||
|
fs::write(&dst_path, modified_content)?;
|
||||||
|
|
||||||
|
let mut perms = fs::metadata(&dst_path)?.permissions();
|
||||||
|
let current_mode = perms.mode();
|
||||||
|
|
||||||
|
// set write bit
|
||||||
|
perms.set_mode(current_mode | S_IWUSR);
|
||||||
|
fs::set_permissions(&dst_path, perms)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
fn wineboot_update(wine_prefix: &Path, verbose: bool) -> anyhow::Result<()> {
|
||||||
|
let handle = make_env(
|
||||||
|
&cmd!(WINE, "wineboot", "--update")
|
||||||
|
.stderr_to_stdout()
|
||||||
|
.unchecked(),
|
||||||
|
wine_prefix,
|
||||||
|
verbose,
|
||||||
|
)
|
||||||
|
.reader()?;
|
||||||
|
|
||||||
|
let lines = BufReader::new(&handle).lines();
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
let line = line?;
|
||||||
|
info!("{line}");
|
||||||
|
}
|
||||||
|
|
||||||
|
match handle.try_wait()? {
|
||||||
|
Some(output) => {
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"wineboot --update failed: {:?}",
|
||||||
|
output.status
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!("wineboot child is still running in some way.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
fn execute(paths: &Paths, program: &ProgramToExecute, verbose: bool) -> anyhow::Result<()> {
|
||||||
|
let user = std::env::var("USER")?;
|
||||||
|
|
||||||
|
info!(user = ?user);
|
||||||
|
|
||||||
|
if let Err(e) = warmup_prefix_directories(&paths.upper) {
|
||||||
|
error!(error = %e, "warmup prefix directories failed (non-fatal)");
|
||||||
|
}
|
||||||
|
if let Err(e) = warmup_prefix_registry(&paths.upper, &user) {
|
||||||
|
error!(error = %e, "warmup prefix registry failed (non-fatal)");
|
||||||
|
}
|
||||||
|
wineboot_update(&paths.wine_prefix, verbose)?;
|
||||||
|
|
||||||
|
info!("finished warming & wineboot");
|
||||||
|
|
||||||
|
perform_migrations(&paths.wine_prefix).context("performing migrations")?;
|
||||||
|
|
||||||
|
if cfg!(not(feature = "v3")) {
|
||||||
|
sync_v2_settings(&paths.wine_prefix, &user).context("sync v2 settings")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("finished pre-check");
|
||||||
|
|
||||||
|
let application_handle = match program {
|
||||||
|
ProgramToExecute::Affinity { arguments } => {
|
||||||
|
let exe_suffix = if cfg!(feature = "v3") {
|
||||||
|
"Affinity/AffinityHook.exe"
|
||||||
|
} else if cfg!(feature = "photo") {
|
||||||
|
"Photo 2/Photo.exe"
|
||||||
|
} else if cfg!(feature = "publisher") {
|
||||||
|
"Publisher 2/Publisher.exe"
|
||||||
|
} else if cfg!(feature = "designer") {
|
||||||
|
"Designer 2/Designer.exe"
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let exe_path = format!(
|
||||||
|
"{}/drive_c/Program Files/Affinity/{}",
|
||||||
|
paths.wine_prefix.display(),
|
||||||
|
exe_suffix
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("using exe {exe_path:?}");
|
||||||
|
|
||||||
|
cmd(
|
||||||
|
WINE,
|
||||||
|
std::iter::once(exe_path).chain(arguments.iter().cloned()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ProgramToExecute::Other(Program::Wine { arguments }) => cmd(WINE, arguments),
|
||||||
|
ProgramToExecute::Other(Program::Winetricks { arguments }) => cmd(WINETRICKS, arguments),
|
||||||
|
ProgramToExecute::Other(Program::Wineboot { arguments }) => cmd(
|
||||||
|
WINE,
|
||||||
|
std::iter::once("wineboot".to_string()).chain(arguments.iter().cloned()),
|
||||||
|
),
|
||||||
|
ProgramToExecute::Other(Program::Wineserver { arguments }) => cmd(WINESERVER, arguments),
|
||||||
|
};
|
||||||
|
|
||||||
|
let application_handle = make_env(
|
||||||
|
&application_handle.stderr_to_stdout().unchecked(),
|
||||||
|
&paths.wine_prefix,
|
||||||
|
verbose,
|
||||||
|
)
|
||||||
|
.reader()?;
|
||||||
|
|
||||||
|
let lines = BufReader::new(&application_handle).lines();
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
let line = line?;
|
||||||
|
println!("{line}");
|
||||||
|
}
|
||||||
|
|
||||||
|
match application_handle.try_wait()? {
|
||||||
|
Some(output) => {
|
||||||
|
if output.status.success() {
|
||||||
|
info!(status = ?output.status, "application process ended cleanly");
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"application process failed: {:?}",
|
||||||
|
output.status
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!("application process is still running in some way.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let wineserver_wait = make_env(
|
||||||
|
&cmd!(WINESERVER, "-w").stderr_to_stdout().unchecked(),
|
||||||
|
&paths.wine_prefix,
|
||||||
|
verbose,
|
||||||
|
)
|
||||||
|
.reader()?;
|
||||||
|
|
||||||
|
let lines = BufReader::new(&wineserver_wait).lines();
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
let line = line?;
|
||||||
|
info!("{line}");
|
||||||
|
}
|
||||||
|
|
||||||
|
match wineserver_wait.try_wait()? {
|
||||||
|
Some(output) => {
|
||||||
|
if output.status.success() {
|
||||||
|
info!(status = ?output.status, "wineserver -w exited cleanly");
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("wineserver -w failed: {:?}", output.status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!("wineserver child is still running in some way.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all, ret)]
|
||||||
|
fn make_mount_options(paths: &Paths) -> String {
|
||||||
|
format!(
|
||||||
|
"lowerdir={},upperdir={},workdir={}",
|
||||||
|
LOWER_DIR.display(),
|
||||||
|
paths.upper.display(),
|
||||||
|
paths.work.display()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(paths))]
|
||||||
|
fn cleanup_fuse(paths: &Paths) -> anyhow::Result<()> {
|
||||||
|
let fusermount3 = cmd!(
|
||||||
|
"/usr/bin/env",
|
||||||
|
"fusermount3",
|
||||||
|
"-u",
|
||||||
|
"-z",
|
||||||
|
paths.wine_prefix.display().to_string()
|
||||||
|
)
|
||||||
|
.stdout_to_stderr()
|
||||||
|
.stderr_capture()
|
||||||
|
.run();
|
||||||
|
|
||||||
|
let success = match &fusermount3 {
|
||||||
|
Ok(output) => {
|
||||||
|
if output.status.success() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
error!(status = %output.status, stderr = ?output.stderr, "`fusermount3` failed");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(error = ?err, "`fusermount3` failed");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !success {
|
||||||
|
let fusermount = cmd!(
|
||||||
|
"/usr/bin/env",
|
||||||
|
"fusermount",
|
||||||
|
"-u",
|
||||||
|
"-z",
|
||||||
|
paths.wine_prefix.display().to_string()
|
||||||
|
)
|
||||||
|
.stdout_to_stderr()
|
||||||
|
.stderr_capture()
|
||||||
|
.run()?;
|
||||||
|
|
||||||
|
if !fusermount.status.success() {
|
||||||
|
error!(status = %fusermount.status, stderr = ?String::from_utf8_lossy(&fusermount.stderr), "`fusermount2` failed");
|
||||||
|
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"both `fusermount3` & `fusermount` failed! fusermount2: {:?}",
|
||||||
|
String::from_utf8_lossy(&fusermount.stderr)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("unmounted fuse cleanly");
|
||||||
|
|
||||||
|
let _ = fs::remove_dir(&paths.wine_prefix)
|
||||||
|
.inspect_err(|err| error!(error = ?err, "failed to remove wine_prefix"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
fn mount_run_unprivileged(
|
||||||
|
paths: &Paths,
|
||||||
|
program: &ProgramToExecute,
|
||||||
|
verbose: bool,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let handle = cmd!(
|
||||||
|
FUSE_OVERLAYFS,
|
||||||
|
"-o",
|
||||||
|
make_mount_options(paths),
|
||||||
|
&paths.wine_prefix
|
||||||
|
)
|
||||||
|
.stderr_to_stdout()
|
||||||
|
.unchecked()
|
||||||
|
.reader()?;
|
||||||
|
|
||||||
|
let lines = BufReader::new(&handle).lines();
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
let line = line?;
|
||||||
|
info!("{line}");
|
||||||
|
}
|
||||||
|
|
||||||
|
match handle.try_wait()? {
|
||||||
|
Some(output) => {
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"fuse-overlayfs failed to mount: {:?}",
|
||||||
|
output.status
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!("fuse-overlayfs child is still running in some way.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = execute(paths, program, verbose) {
|
||||||
|
error!(error = ?err, "Running with fuse failed.");
|
||||||
|
let _ = cleanup_fuse(paths);
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"failed to run application with `fuse-overlayfs`: {err:?}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_fuse(paths)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(paths))]
|
||||||
|
fn cleanup_privileged(paths: &Paths) {
|
||||||
|
let _ = umount(&paths.wine_prefix)
|
||||||
|
.inspect_err(|err| error!(error = ?err, "failed to unmount wine_prefix"));
|
||||||
|
let _ = fs::remove_dir(&paths.wine_prefix)
|
||||||
|
.inspect_err(|err| error!(error = ?err, "failed to remove wine_prefix"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
fn mount_execute_privileged(paths: &Paths, program: &ProgramToExecute, verbose: bool) {
|
||||||
|
match mount(
|
||||||
|
Some("overlay"),
|
||||||
|
&paths.wine_prefix,
|
||||||
|
Some("overlay"),
|
||||||
|
MsFlags::empty(),
|
||||||
|
Some(make_mount_options(paths).as_str()),
|
||||||
|
) {
|
||||||
|
Ok(()) => {
|
||||||
|
if let Err(err) = execute(paths, program, verbose) {
|
||||||
|
error!(error = ?err, "Running privileged failed.");
|
||||||
|
cleanup_privileged(paths);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(errorno = ?err, "Mount failed.");
|
||||||
|
std::process::exit(MOUNT_FAIL_STATUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_privileged(paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
fn mount_run_privileged(
|
||||||
|
paths: &Paths,
|
||||||
|
ids: (u32, u32),
|
||||||
|
program: &ProgramToExecute,
|
||||||
|
verbose: bool,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
if let Err(err) = fs::write("/proc/self/setgroups", b"deny\n") {
|
||||||
|
error!(error = ?err, "failed to write setgroups");
|
||||||
|
mount_run_unprivileged(paths, program, verbose)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let uid_map = format!("0 {} 1\n", ids.0);
|
||||||
|
if let Err(err) = fs::write("/proc/self/uid_map", uid_map) {
|
||||||
|
error!(error = ?err, "failed to write uid_map");
|
||||||
|
mount_run_unprivileged(paths, program, verbose)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let gid_map = format!("0 {} 1\n", ids.1);
|
||||||
|
if let Err(err) = fs::write("/proc/self/gid_map", gid_map) {
|
||||||
|
error!(error = ?err, "failed to write gid_map");
|
||||||
|
mount_run_unprivileged(paths, program, verbose)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
match unsafe { fork() } {
|
||||||
|
Ok(ForkResult::Parent { child }) => match waitpid(child, None) {
|
||||||
|
Ok(WaitStatus::Exited(_, status)) if status == MOUNT_FAIL_STATUS => {
|
||||||
|
info!("Falling back on unprivileged due to failed mount");
|
||||||
|
mount_run_unprivileged(paths, program, verbose)?;
|
||||||
|
}
|
||||||
|
Ok(WaitStatus::Exited(_, status)) => {
|
||||||
|
std::process::exit(status);
|
||||||
|
}
|
||||||
|
Ok(WaitStatus::Signaled(_, signal, _)) => {
|
||||||
|
info!(signal= ?signal, "child was killed by signal");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(errorno = ?err, "failed to wait for child");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Ok(ForkResult::Child) => {
|
||||||
|
unsafe {
|
||||||
|
// make sure this namespace dies if the parent ends
|
||||||
|
nix::libc::prctl(nix::libc::PR_SET_PDEATHSIG, nix::libc::SIGKILL);
|
||||||
|
}
|
||||||
|
mount_execute_privileged(paths, program, verbose);
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(errorno = ?err, "Fork failed.");
|
||||||
|
info!("Falling back on unprivileged");
|
||||||
|
mount_run_unprivileged(paths, program, verbose)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
init_tracing();
|
||||||
|
|
||||||
|
let args = Arguments::parse();
|
||||||
|
|
||||||
|
let base = BaseDir::new(if cfg!(feature = "v3") {
|
||||||
|
"affinity-v3"
|
||||||
|
} else {
|
||||||
|
"affinity"
|
||||||
|
})
|
||||||
|
.expect("obtaining xdg basedir failed");
|
||||||
|
|
||||||
|
let paths = Paths {
|
||||||
|
upper: base.data,
|
||||||
|
work: base.state,
|
||||||
|
wine_prefix: base
|
||||||
|
.runtime
|
||||||
|
.unwrap_or_else(std::env::temp_dir)
|
||||||
|
.join(format!("affinity-nix-prefix-{}", std::process::id())),
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(paths = ?paths, lower = ?LOWER_DIR.display());
|
||||||
|
|
||||||
|
if !LOWER_DIR.is_dir() {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"{} does not exist or is not a directory! If $LOWER_DIR does not exist, you are likely using the wrong package, refer to affinity-nix's readme for correct usage.",
|
||||||
|
LOWER_DIR.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
paths
|
||||||
|
.ensure_created()
|
||||||
|
.context("setting up tmp directories")?;
|
||||||
|
migrate::migrate(&paths).context("migrating to new overlayfs runner")?;
|
||||||
|
|
||||||
|
let uid = unsafe { libc::getuid() };
|
||||||
|
let gid = unsafe { libc::getgid() };
|
||||||
|
|
||||||
|
let unshare =
|
||||||
|
unshare(CloneFlags::CLONE_NEWUSER | CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWPID);
|
||||||
|
|
||||||
|
let program = if let Some(subcommand) = args.subcommand {
|
||||||
|
ProgramToExecute::Other(subcommand)
|
||||||
|
} else {
|
||||||
|
ProgramToExecute::Affinity {
|
||||||
|
arguments: args.affinity,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(program = ?program);
|
||||||
|
|
||||||
|
match unshare {
|
||||||
|
Ok(()) => mount_run_privileged(&paths, (uid, gid), &program, args.verbose),
|
||||||
|
Err(err) => {
|
||||||
|
error!(errorno = ?err, "Unshare failed.");
|
||||||
|
mount_run_unprivileged(&paths, &program, args.verbose)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
use std::{
|
||||||
|
env, fs,
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use duct::cmd;
|
||||||
|
use tracing::{error, info, instrument};
|
||||||
|
|
||||||
|
use crate::{GNUTAR, Paths, ZENITY};
|
||||||
|
|
||||||
|
const MESSAGE: &str = "There have been upgrades to affinity-nix!
|
||||||
|
|
||||||
|
Affinity and it's dependencies are no longer installed directly, reducing startup time and manual updating.
|
||||||
|
|
||||||
|
To migrate, we need to delete the old installation. Your registry files and user data won't be touched.";
|
||||||
|
|
||||||
|
fn make_message(backup_location: &Path) -> String {
|
||||||
|
format!(
|
||||||
|
"{MESSAGE} A backup will be created in at {}. Is that OK?",
|
||||||
|
backup_location.display()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub fn migrate(paths: &Paths) -> anyhow::Result<()> {
|
||||||
|
let Some(revision) = crate::check::read_revision(&paths.upper)? else {
|
||||||
|
info!("revision does not exist, skipping backup");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
if revision >= 9 {
|
||||||
|
info!("revision more than or equal to 9, skipping backup");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let backup_path = Path::new(&env::var("HOME").context("finding $HOME var")?).join(format!(
|
||||||
|
"affinity-nix-backup-{}.tar.zst",
|
||||||
|
std::process::id()
|
||||||
|
));
|
||||||
|
|
||||||
|
let question = cmd!(
|
||||||
|
ZENITY,
|
||||||
|
"--question",
|
||||||
|
"--width",
|
||||||
|
"480",
|
||||||
|
format!("--text={}", make_message(&backup_path))
|
||||||
|
)
|
||||||
|
.unchecked()
|
||||||
|
.run()?;
|
||||||
|
|
||||||
|
if !question.status.success() {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"User refused to migrate, we cannot continue or there may be data loss."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let tar_handle = cmd!(GNUTAR, "--zstd", "-cvpf", backup_path, &paths.upper)
|
||||||
|
.stdout_to_stderr()
|
||||||
|
.stderr_capture()
|
||||||
|
.reader()?;
|
||||||
|
|
||||||
|
let lines = BufReader::new(&tar_handle).lines();
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
let line = line?;
|
||||||
|
info!("{line}");
|
||||||
|
}
|
||||||
|
|
||||||
|
match tar_handle.try_wait()? {
|
||||||
|
Some(output) => {
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"backing up with tar failed: {:?}",
|
||||||
|
output.status
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(status = %output.status, "tar ended cleanly");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!("tar child is still running in some way.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let drive_c = paths.upper.join("drive_c");
|
||||||
|
|
||||||
|
if !drive_c.is_dir() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in drive_c.read_dir().context("reading drive_c")? {
|
||||||
|
let entry = entry.context("reading drive_c entry")?;
|
||||||
|
|
||||||
|
if entry.file_name() == "users" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
info!(path = ?path, "removing entry");
|
||||||
|
|
||||||
|
if entry
|
||||||
|
.file_type()
|
||||||
|
.context("reading entry filetype")?
|
||||||
|
.is_dir()
|
||||||
|
{
|
||||||
|
fs::remove_dir_all(&path)
|
||||||
|
.with_context(|| format!("removing directory {}", path.display()))?;
|
||||||
|
} else {
|
||||||
|
fs::remove_file(&path).with_context(|| format!("removing file {}", path.display()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
let
|
||||||
|
lockFile = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||||
|
flake-compat-node = lockFile.nodes.${lockFile.nodes.root.inputs.flake-compat};
|
||||||
|
flake-compat = builtins.fetchTarball {
|
||||||
|
inherit (flake-compat-node.locked) url;
|
||||||
|
sha256 = flake-compat-node.locked.narHash;
|
||||||
|
};
|
||||||
|
|
||||||
|
flake = import flake-compat {
|
||||||
|
src = ./.;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
flake.defaultNix
|
||||||
Generated
+247
@@ -0,0 +1,247 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"corefonts": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1431183738,
|
||||||
|
"narHash": "sha256-610Km9GCtYWxi2SH5jH/XYPXc5f58q5lB05/5XxO/qM=",
|
||||||
|
"owner": "pushcx",
|
||||||
|
"repo": "corefonts",
|
||||||
|
"rev": "8f8627b0004b5f1b952c09cf6390e4ae46475e59",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pushcx",
|
||||||
|
"repo": "corefonts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"crane": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1787326676,
|
||||||
|
"narHash": "sha256-lWhBbBvC05/xwivKBBiM2YNizpmgqCgyOIzomvRuwxs=",
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"rev": "692f7e9ef2ece8125b466f66f2af532b3edaed0d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1751685974,
|
||||||
|
"narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=",
|
||||||
|
"rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1",
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://git.lix.systems/api/v1/repos/lix-project/flake-compat/archive/549f2762aebeff29a2e5ece7a7dc0f955281a1d1.tar.gz?rev=549f2762aebeff29a2e5ece7a7dc0f955281a1d1"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat_2": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1767039857,
|
||||||
|
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1785627969,
|
||||||
|
"narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat_2",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1787138109,
|
||||||
|
"narHash": "sha256-O2tBn84NNuHrnqNVxx/XqsXwfYvS1YwBh+7CBnbCYsk=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "928612b364dc241a05bf6567ad44d499efb6b9aa",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1782918843,
|
||||||
|
"narHash": "sha256-ETYnV9U7Sr+A45dohzZdfCZKOss4qrTkO+wgNZNvEc0=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e8273b29fe1390ec8d4603f2477357555291432e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1785031560,
|
||||||
|
"narHash": "sha256-OmshNvn2vupOFpYinLUu+1Dnpu4n7Q5N3ggGVNHpkUI=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "0e79af5e3d4dcfcd676ab5ba3f95d2e3352e078c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-wine": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1735834308,
|
||||||
|
"narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6df24922a1400241dae323af55f30e4318a6ca65",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6df24922a1400241dae323af55f30e4318a6ca65",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1787135253,
|
||||||
|
"narHash": "sha256-RD2kNWCG+Bjo6h+JVjWVNntZs2GtRoeY2xHjts/FNkA=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ffb3c9b700e759be2ef13237c9d8f953b32a1e46",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770107345,
|
||||||
|
"narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "4533d9293756b63904b7238acb84ac8fe4c8c2c4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"on-linux": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1787365621,
|
||||||
|
"narHash": "sha256-aRS0sZ6p7YJagjFaZFrS1zuvN3PVKNBJOze1Ek/bOi8=",
|
||||||
|
"owner": "seapear",
|
||||||
|
"repo": "AffinityOnLinux",
|
||||||
|
"rev": "0fe210754999a2ace3039c11fbbd11b45b5ee399",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "seapear",
|
||||||
|
"repo": "AffinityOnLinux",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugin-loader-src": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1776972538,
|
||||||
|
"narHash": "sha256-2EvrOYQCB2Z2YXhcPnjR67KoOCvwTVW3TfLcmU3//ak=",
|
||||||
|
"owner": "noahc3",
|
||||||
|
"repo": "AffinityPluginLoader",
|
||||||
|
"rev": "1d7956d5b791bd6a213e8b28c1e25e1f4bcc6166",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "noahc3",
|
||||||
|
"repo": "AffinityPluginLoader",
|
||||||
|
"rev": "1d7956d5b791bd6a213e8b28c1e25e1f4bcc6166",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"corefonts": "corefonts",
|
||||||
|
"crane": "crane",
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"git-hooks": "git-hooks",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"nixpkgs-wine": "nixpkgs-wine",
|
||||||
|
"on-linux": "on-linux",
|
||||||
|
"plugin-loader-src": "plugin-loader-src",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1786901030,
|
||||||
|
"narHash": "sha256-WSFCsDSE5ffgD2MqzkM2CYjeFiKhRF/dJUN8uedb6YE=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "27b3b12a8e6375f28ebe122f07d230ca5459bbfa",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
description = "An attempt at packaging affinity photo for nix";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
# a known working revision of nixpkgs for wine. somewhat tracked by #63
|
||||||
|
nixpkgs-wine.url = "github:nixos/nixpkgs/6df24922a1400241dae323af55f30e4318a6ca65";
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
|
on-linux = {
|
||||||
|
url = "github:seapear/AffinityOnLinux";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
plugin-loader-src = {
|
||||||
|
url = "github:noahc3/AffinityPluginLoader/1d7956d5b791bd6a213e8b28c1e25e1f4bcc6166";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||||
|
git-hooks.url = "github:cachix/git-hooks.nix";
|
||||||
|
|
||||||
|
flake-compat.url = "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz";
|
||||||
|
|
||||||
|
corefonts = {
|
||||||
|
url = "github:pushcx/corefonts";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
crane.url = "github:ipetkov/crane";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
inputs@{
|
||||||
|
flake-parts,
|
||||||
|
git-hooks,
|
||||||
|
treefmt-nix,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
|
imports = [
|
||||||
|
git-hooks.flakeModule
|
||||||
|
treefmt-nix.flakeModule
|
||||||
|
./nix/hooks.nix
|
||||||
|
./nix/fmt.nix
|
||||||
|
./nix/shells.nix
|
||||||
|
./packages
|
||||||
|
./tests/default.nix
|
||||||
|
./overlay.nix
|
||||||
|
];
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
];
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
_module.args = {
|
||||||
|
craneLib = inputs.crane.mkLib pkgs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
perSystem = _: {
|
||||||
|
treefmt = {
|
||||||
|
projectRootFile = "flake.nix";
|
||||||
|
programs = {
|
||||||
|
nixfmt.enable = true;
|
||||||
|
yamlfmt.enable = true;
|
||||||
|
shfmt.enable = true;
|
||||||
|
rustfmt.enable = true;
|
||||||
|
taplo.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
pre-commit = {
|
||||||
|
settings = {
|
||||||
|
hooks = {
|
||||||
|
statix.enable = true;
|
||||||
|
zizmor.enable = true;
|
||||||
|
typos.enable = true;
|
||||||
|
deadnix = {
|
||||||
|
enable = true;
|
||||||
|
settings.edit = true;
|
||||||
|
};
|
||||||
|
fmt = {
|
||||||
|
enable = true;
|
||||||
|
name = "nix fmt";
|
||||||
|
entry = "${lib.getExe config.formatter} --no-cache";
|
||||||
|
};
|
||||||
|
ty = {
|
||||||
|
enable = true;
|
||||||
|
name = "ty check";
|
||||||
|
files = "\\.py$";
|
||||||
|
entry = lib.getExe (
|
||||||
|
pkgs.writeShellScriptBin "ty-check" ''
|
||||||
|
cd tests/
|
||||||
|
${lib.getExe pkgs.uv} run ty check
|
||||||
|
''
|
||||||
|
);
|
||||||
|
};
|
||||||
|
actionlint = {
|
||||||
|
enable = true;
|
||||||
|
files = "^\\.github/workflows/.*\\.(yml|yaml)$";
|
||||||
|
};
|
||||||
|
yamllint = {
|
||||||
|
enable = true;
|
||||||
|
settings.configPath = ".yamllint.yaml";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
craneLib,
|
||||||
|
pkgs,
|
||||||
|
self',
|
||||||
|
wine-packages,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.pre-commit;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Adapted from
|
||||||
|
# https://github.com/cachix/git-hooks.nix/blob/dcf5072734cb576d2b0c59b2ac44f5050b5eac82/flake-module.nix#L66-L78
|
||||||
|
devShells.default = craneLib.devShell {
|
||||||
|
packages = lib.flatten [
|
||||||
|
cfg.settings.enabledPackages
|
||||||
|
cfg.settings.package
|
||||||
|
|
||||||
|
pkgs.uv
|
||||||
|
pkgs.rust-analyzer
|
||||||
|
];
|
||||||
|
|
||||||
|
env = {
|
||||||
|
WINE = lib.getExe self'.packages.wine;
|
||||||
|
WINESERVER = lib.getExe wine-packages.wineserver;
|
||||||
|
WINETRICKS = lib.getExe wine-packages.winetricks;
|
||||||
|
FUSE_OVERLAYFS = lib.getExe pkgs.fuse-overlayfs;
|
||||||
|
GNUTAR = lib.getExe pkgs.gnutar;
|
||||||
|
ZENITY = lib.getExe pkgs.zenity;
|
||||||
|
RSYNC = lib.getExe pkgs.rsync;
|
||||||
|
REGISTRY_PATCHES = (pkgs.callPackage ../packages/registry-patches.nix { }).combined;
|
||||||
|
ON_LINUX = inputs.on-linux.outPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
shellHook = builtins.concatStringsSep "\n" [
|
||||||
|
cfg.installationScript
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
{ withSystem, inputs, ... }:
|
||||||
|
{
|
||||||
|
flake.overlays.default =
|
||||||
|
_final: prev:
|
||||||
|
withSystem prev.stdenv.hostPlatform.system (_: {
|
||||||
|
affinity-v3 = prev.callPackage ./packages/affinity-v3/package.nix {
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
affinity-photo = prev.callPackage ./packages/affinity/package.nix {
|
||||||
|
name = "Photo";
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
affinity-designer = prev.callPackage ./packages/affinity/package.nix {
|
||||||
|
name = "Designer";
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
affinity-publisher = prev.callPackage ./packages/affinity/package.nix {
|
||||||
|
name = "Publisher";
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
self',
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
packages = {
|
||||||
|
affinity-v3 = pkgs.callPackage ./package.nix {
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
default = self'.packages.affinity-v3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
makeDesktopItem,
|
||||||
|
lib,
|
||||||
|
affinity-v3 ? null,
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
affinity-v3 = makeDesktopItem rec {
|
||||||
|
desktopName = "Affinity";
|
||||||
|
name = "affinity-v3";
|
||||||
|
exec = "${lib.getExe affinity-v3} %U";
|
||||||
|
icon = name;
|
||||||
|
type = "Application";
|
||||||
|
categories = [ "Graphics" ];
|
||||||
|
keywords = [
|
||||||
|
"Graphics"
|
||||||
|
"2DGraphics"
|
||||||
|
"RasterGraphics"
|
||||||
|
"VectorGraphics"
|
||||||
|
"image"
|
||||||
|
"editor"
|
||||||
|
"vector"
|
||||||
|
"drawing"
|
||||||
|
];
|
||||||
|
mimeTypes = [
|
||||||
|
"application/afphoto"
|
||||||
|
"application/afdesign"
|
||||||
|
];
|
||||||
|
startupWMClass = "affinity.exe";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
fetchurl,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
}:
|
||||||
|
rec {
|
||||||
|
icon = fetchurl {
|
||||||
|
url = "https://web.archive.org/web/20260310003857/https://static.canva.com/domain-assets/affinity/static/images/apple-touch-180x180-1.png";
|
||||||
|
sha256 = "sha256-KLT9loFiD2y8uNXNzc27DZ4A73yEs6Ntr4h3WULG5HM=";
|
||||||
|
};
|
||||||
|
|
||||||
|
iconPackage = pkgs.runCommand "affinity-v3-icons" { } ''
|
||||||
|
mkdir -p $out/share/icons/hicolor/256x256/apps
|
||||||
|
|
||||||
|
${lib.getExe pkgs.imagemagick} \
|
||||||
|
${icon} \
|
||||||
|
-resize 256x256 \
|
||||||
|
$out/share/icons/hicolor/256x256/apps/affinity-v3.png
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
symlinkJoin,
|
||||||
|
callPackage,
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
wine-packages = callPackage ../wine/packages.nix {
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
apl-combined = callPackage ../apl/apl-combined.nix {
|
||||||
|
src = inputs.plugin-loader-src;
|
||||||
|
};
|
||||||
|
|
||||||
|
prefixBase = callPackage ../prefixWithAffinity.nix {
|
||||||
|
inherit inputs wine-packages apl-combined;
|
||||||
|
};
|
||||||
|
|
||||||
|
runner = callPackage ../runner/package.nix {
|
||||||
|
inherit prefixBase inputs wine-packages;
|
||||||
|
registry-patches = (callPackage ../registry-patches.nix { }).combined;
|
||||||
|
name = "v3";
|
||||||
|
};
|
||||||
|
|
||||||
|
desktop = callPackage ./desktopItems.nix {
|
||||||
|
affinity-v3 = runner.package;
|
||||||
|
};
|
||||||
|
|
||||||
|
icons = callPackage ./icons.nix { };
|
||||||
|
icon-package = icons.iconPackage;
|
||||||
|
in
|
||||||
|
symlinkJoin {
|
||||||
|
name = "Affinity v3";
|
||||||
|
pname = "affinity-v3";
|
||||||
|
paths = [
|
||||||
|
runner.package
|
||||||
|
desktop.affinity-v3
|
||||||
|
icon-package
|
||||||
|
];
|
||||||
|
meta = {
|
||||||
|
mainProgram = "affinity-v3";
|
||||||
|
|
||||||
|
description = "Affinity v3";
|
||||||
|
license = lib.licenses.unfree;
|
||||||
|
homepage = "https://affinity.serif.com/";
|
||||||
|
platforms = [ "x86_64-linux" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
makeV2Package =
|
||||||
|
name:
|
||||||
|
(pkgs.callPackage ./package.nix {
|
||||||
|
inherit inputs name;
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = {
|
||||||
|
affinity-photo = makeV2Package "Photo";
|
||||||
|
affinity-designer = makeV2Package "Designer";
|
||||||
|
affinity-publisher = makeV2Package "Publisher";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
makeDesktopItem,
|
||||||
|
lib,
|
||||||
|
photo ? null,
|
||||||
|
publisher ? null,
|
||||||
|
designer ? null,
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
photo = makeDesktopItem rec {
|
||||||
|
desktopName = "Affinity Photo 2";
|
||||||
|
name = "affinity-photo";
|
||||||
|
exec = "${lib.getExe photo} %U";
|
||||||
|
icon = name;
|
||||||
|
type = "Application";
|
||||||
|
categories = [ "Graphics" ];
|
||||||
|
keywords = [
|
||||||
|
"Graphics"
|
||||||
|
"2DGraphics"
|
||||||
|
"RasterGraphics"
|
||||||
|
"image"
|
||||||
|
"editor"
|
||||||
|
"vector"
|
||||||
|
"drawing"
|
||||||
|
];
|
||||||
|
mimeTypes = [ "application/afphoto" ];
|
||||||
|
startupWMClass = "photo.exe";
|
||||||
|
};
|
||||||
|
designer = makeDesktopItem rec {
|
||||||
|
desktopName = "Affinity Designer 2";
|
||||||
|
name = "affinity-designer";
|
||||||
|
exec = "${lib.getExe designer} %U";
|
||||||
|
icon = name;
|
||||||
|
type = "Application";
|
||||||
|
categories = [ "Graphics" ];
|
||||||
|
keywords = [
|
||||||
|
"Graphics"
|
||||||
|
"2DGraphics"
|
||||||
|
"VectorGraphics"
|
||||||
|
"image"
|
||||||
|
"editor"
|
||||||
|
"vector"
|
||||||
|
"drawing"
|
||||||
|
];
|
||||||
|
mimeTypes = [ "application/afdesign" ];
|
||||||
|
startupWMClass = "designer.exe";
|
||||||
|
};
|
||||||
|
publisher = makeDesktopItem rec {
|
||||||
|
desktopName = "Affinity Publisher 2";
|
||||||
|
name = "affinity-publisher";
|
||||||
|
exec = "${lib.getExe publisher} %U";
|
||||||
|
icon = name;
|
||||||
|
type = "Application";
|
||||||
|
categories = [ "Graphics" ];
|
||||||
|
keywords = [
|
||||||
|
"Graphics"
|
||||||
|
"2DGraphics"
|
||||||
|
"RasterGraphics"
|
||||||
|
"VectorGraphics"
|
||||||
|
"image"
|
||||||
|
"editor"
|
||||||
|
"vector"
|
||||||
|
"drawing"
|
||||||
|
];
|
||||||
|
mimeTypes = [
|
||||||
|
"application/afdesign"
|
||||||
|
"application/afphoto"
|
||||||
|
# Note that there is no mime type for a `.afpub` file
|
||||||
|
];
|
||||||
|
startupWMClass = "publisher.exe";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
fetchurl,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
}:
|
||||||
|
rec {
|
||||||
|
icons = {
|
||||||
|
photo = fetchurl {
|
||||||
|
url = "https://cdn.serif.com/store/img/logos/affinity-photo-2-020520191502.svg";
|
||||||
|
sha256 = "04bnf12znp9dgkwpk46c01381cw298gq14ga7j7dwccyl3m556d7";
|
||||||
|
};
|
||||||
|
designer = fetchurl {
|
||||||
|
url = "https://cdn.serif.com/store/img/logos/affinity-designer-2-020520191502.svg";
|
||||||
|
sha256 = "025j4aq8py2a5r34hx39nlhdmps06z6506h7wxchyxbbz6xz3d11";
|
||||||
|
};
|
||||||
|
publisher = fetchurl {
|
||||||
|
url = "https://cdn.serif.com/store/img/logos/affinity-publisher-2-020520191502.svg";
|
||||||
|
sha256 = "16ghh3mc2vjhvphbyy9zgan066ccp3xj1ilb6z5lmvsb94dp37aj";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mkIconPackageFor =
|
||||||
|
name:
|
||||||
|
pkgs.runCommand "${name}-icons" { } ''
|
||||||
|
mkdir -p $out/share/icons/hicolor/{256x256,scalable}/apps
|
||||||
|
|
||||||
|
cp ${icons.${lib.toLower name}} \
|
||||||
|
$out/share/icons/hicolor/scalable/apps/affinity-${lib.toLower name}.svg
|
||||||
|
|
||||||
|
${lib.getExe pkgs.inkscape} \
|
||||||
|
-w 256 -h 256 \
|
||||||
|
${icons.${lib.toLower name}} \
|
||||||
|
-o - > $out/share/icons/hicolor/256x256/apps/affinity-${lib.toLower name}.png
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
symlinkJoin,
|
||||||
|
callPackage,
|
||||||
|
lib,
|
||||||
|
name,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
wine-packages = callPackage ../wine/packages.nix {
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
apl-combined = callPackage ../apl/apl-combined.nix {
|
||||||
|
src = inputs.plugin-loader-src;
|
||||||
|
};
|
||||||
|
|
||||||
|
prefixBase = callPackage ../prefixWithAffinity.nix {
|
||||||
|
inherit inputs wine-packages apl-combined;
|
||||||
|
v3 = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
runner = callPackage ../runner/package.nix {
|
||||||
|
inherit
|
||||||
|
prefixBase
|
||||||
|
inputs
|
||||||
|
wine-packages
|
||||||
|
name
|
||||||
|
;
|
||||||
|
registry-patches = (callPackage ../registry-patches.nix { }).combined;
|
||||||
|
};
|
||||||
|
|
||||||
|
desktop = callPackage ./desktopItems.nix {
|
||||||
|
${lib.toLower name} = runner.package;
|
||||||
|
};
|
||||||
|
|
||||||
|
icons = callPackage ./icons.nix { };
|
||||||
|
icon-package = icons.mkIconPackageFor name;
|
||||||
|
|
||||||
|
# last version of affinity v2 released
|
||||||
|
lastV2Version = "2.6.5";
|
||||||
|
in
|
||||||
|
symlinkJoin {
|
||||||
|
name = "affinity-${lib.toLower name}-${lastV2Version}";
|
||||||
|
pname = "affinity-${lib.toLower name}";
|
||||||
|
paths = [
|
||||||
|
runner.package
|
||||||
|
desktop.${lib.toLower name}
|
||||||
|
icon-package
|
||||||
|
];
|
||||||
|
meta = {
|
||||||
|
mainProgram = "affinity-${lib.toLower name}";
|
||||||
|
|
||||||
|
description = "Affinity ${name} ${lastV2Version}";
|
||||||
|
license = lib.licenses.unfree;
|
||||||
|
homepage = "https://affinity.serif.com/";
|
||||||
|
platforms = [ "x86_64-linux" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
callPackage,
|
||||||
|
symlinkJoin,
|
||||||
|
src,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
version = "unstable";
|
||||||
|
|
||||||
|
apl = callPackage ./apl.nix {
|
||||||
|
inherit version src;
|
||||||
|
};
|
||||||
|
|
||||||
|
bootstrap = callPackage ./bootstrap.nix {
|
||||||
|
inherit version src;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
symlinkJoin {
|
||||||
|
pname = "apl-combined";
|
||||||
|
inherit version;
|
||||||
|
paths = [
|
||||||
|
apl
|
||||||
|
bootstrap
|
||||||
|
];
|
||||||
|
|
||||||
|
postBuild = ''
|
||||||
|
mv $out/lib/AffinityPluginLoader/* $out
|
||||||
|
rm -rf $out/lib
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
buildDotnetModule,
|
||||||
|
src,
|
||||||
|
version,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
buildDotnetModule rec {
|
||||||
|
pname = "AffinityPluginLoader";
|
||||||
|
|
||||||
|
inherit version src;
|
||||||
|
|
||||||
|
projectFile = "AffinityPluginLoader.sln";
|
||||||
|
|
||||||
|
patches = [ ./no-login.patch ];
|
||||||
|
|
||||||
|
nugetDeps = ./deps.json;
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
pushd $out/lib/${pname}
|
||||||
|
rm *.pdb
|
||||||
|
rm *.config
|
||||||
|
|
||||||
|
mkdir -p ./apl/plugins
|
||||||
|
mv ./WineFix.dll ./apl/plugins
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
version,
|
||||||
|
src,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
pkgs.pkgsCross.mingwW64.stdenv.mkDerivation {
|
||||||
|
pname = "affinity-bootstrap";
|
||||||
|
|
||||||
|
inherit version;
|
||||||
|
|
||||||
|
src = "${src}/AffinityBootstrap";
|
||||||
|
|
||||||
|
nativeBuildInputs = [ pkgs.wine64 ];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
mkdir -p build
|
||||||
|
|
||||||
|
cat << 'EOF' > mscoree.def
|
||||||
|
LIBRARY mscoree.dll
|
||||||
|
EXPORTS
|
||||||
|
CLRCreateInstance
|
||||||
|
EOF
|
||||||
|
|
||||||
|
x86_64-w64-mingw32-dlltool -d mscoree.def -l build/libmscoree.a
|
||||||
|
|
||||||
|
$CC -shared -o build/AffinityBootstrap.dll bootstrap.c \
|
||||||
|
-I${pkgs.wine64}/include/wine/windows \
|
||||||
|
-Lbuild \
|
||||||
|
-lole32 -loleaut32 -luuid -lmscoree
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/lib/AffinityPluginLoader
|
||||||
|
cp build/AffinityBootstrap.dll $out/lib/AffinityPluginLoader
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
packages.apl-combined = pkgs.callPackage ./apl-combined.nix {
|
||||||
|
src = inputs.plugin-loader-src;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"pname": "Lib.Harmony",
|
||||||
|
"version": "2.4.2",
|
||||||
|
"hash": "sha256-1kWS5TCQRkVZ/OSGEsnKfI3HMROEE3a3qjRV9G/F1Xk="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETFramework.ReferenceAssemblies",
|
||||||
|
"version": "1.0.3",
|
||||||
|
"hash": "sha256-FBoJP5DHZF0QHM0xLm9yd4HJZVQOuSpSKA+VQRpphEE="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETFramework.ReferenceAssemblies.net48",
|
||||||
|
"version": "1.0.3",
|
||||||
|
"hash": "sha256-in40hTjn65E1FpaRFon0nj1PY/i6tRdDK74Vm4sRBKI="
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,589 @@
|
|||||||
|
From 45ff4e8c5615af1e8f0a41e92580506788a10b24 Mon Sep 17 00:00:00 2001
|
||||||
|
From: marshmallow <git@althaea.zone>
|
||||||
|
Date: Mon, 13 Apr 2026 20:11:35 +1000
|
||||||
|
Subject: [PATCH] Revert "Restore Canva sign-in: Textbox to capture
|
||||||
|
authorization token (#44)"
|
||||||
|
|
||||||
|
This reverts commit 915e1cc5096475f433b8447fc7c6b7cb3b52b7d9.
|
||||||
|
---
|
||||||
|
WineFix/Patches/CanvaSignInPatch.cs | 435 -----------------------
|
||||||
|
WineFix/Patches/MainWindowLoadedPatch.cs | 55 +++
|
||||||
|
WineFix/WineFix.csproj | 4 -
|
||||||
|
WineFix/WineFixPlugin.cs | 14 -
|
||||||
|
4 files changed, 55 insertions(+), 453 deletions(-)
|
||||||
|
delete mode 100644 WineFix/Patches/CanvaSignInPatch.cs
|
||||||
|
|
||||||
|
diff --git a/WineFix/Patches/CanvaSignInPatch.cs b/WineFix/Patches/CanvaSignInPatch.cs
|
||||||
|
deleted file mode 100644
|
||||||
|
index 7c05fcb..0000000
|
||||||
|
--- a/WineFix/Patches/CanvaSignInPatch.cs
|
||||||
|
+++ /dev/null
|
||||||
|
@@ -1,435 +0,0 @@
|
||||||
|
-using System;
|
||||||
|
-using System.Globalization;
|
||||||
|
-using System.Linq;
|
||||||
|
-using System.Reflection;
|
||||||
|
-using System.Windows;
|
||||||
|
-using System.Windows.Controls;
|
||||||
|
-using System.Windows.Data;
|
||||||
|
-using System.Windows.Media;
|
||||||
|
-using System.Windows.Media.Imaging;
|
||||||
|
-using HarmonyLib;
|
||||||
|
-using AffinityPluginLoader.Core;
|
||||||
|
-
|
||||||
|
-namespace WineFix.Patches
|
||||||
|
-{
|
||||||
|
- /// <summary>
|
||||||
|
- /// Adds a paste-URL textbox to the Canva sign-in dialog so users can manually
|
||||||
|
- /// paste the authentication redirect URL from their browser, bypassing the need
|
||||||
|
- /// for the affinity:// protocol handler which doesn't work under Wine.
|
||||||
|
- /// </summary>
|
||||||
|
- public static class CanvaSignInPatch
|
||||||
|
- {
|
||||||
|
- private static FieldInfo _authHookField;
|
||||||
|
- private static object _cloudServicesService;
|
||||||
|
- private static Border _overlayPanel;
|
||||||
|
-
|
||||||
|
- public static void ApplyPatches(Harmony harmony)
|
||||||
|
- {
|
||||||
|
- Logger.Info("Applying CanvaSignIn patch...");
|
||||||
|
-
|
||||||
|
- var serifAffinity = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
- .FirstOrDefault(a => a.GetName().Name == "Serif.Affinity");
|
||||||
|
- var serifInterop = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
- .FirstOrDefault(a => a.GetName().Name == "Serif.Interop.Persona");
|
||||||
|
-
|
||||||
|
- if (serifAffinity == null || serifInterop == null)
|
||||||
|
- {
|
||||||
|
- Logger.Error("Required assemblies not found");
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- // Resolve the AuthHookReceived backing field on CloudServicesService
|
||||||
|
- var cssType = serifInterop.GetType("Serif.Interop.Persona.Services.CloudServicesService");
|
||||||
|
- _authHookField = cssType?.GetField("<backing_store>AuthHookReceived",
|
||||||
|
- BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
-
|
||||||
|
- if (_authHookField == null)
|
||||||
|
- {
|
||||||
|
- // Try the mangled name from decompilation
|
||||||
|
- _authHookField = cssType?.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
|
||||||
|
- .FirstOrDefault(f => f.Name.Contains("AuthHookReceived") && f.FieldType == typeof(Action<Uri>));
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (_authHookField == null)
|
||||||
|
- {
|
||||||
|
- Logger.Error("Could not find AuthHookReceived backing field");
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- // Patch ProductKeyDialog_Loaded to inject our UI
|
||||||
|
- var dialogType = serifAffinity.GetType("Serif.Affinity.UI.ProductKeyDialog");
|
||||||
|
- var loadedMethod = dialogType?.GetMethod("ProductKeyDialog_Loaded",
|
||||||
|
- BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
-
|
||||||
|
- if (loadedMethod == null)
|
||||||
|
- {
|
||||||
|
- Logger.Error("ProductKeyDialog_Loaded not found");
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- harmony.Patch(loadedMethod,
|
||||||
|
- postfix: new HarmonyMethod(typeof(CanvaSignInPatch), nameof(OnDialogLoaded)));
|
||||||
|
- Logger.Info("Patched ProductKeyDialog_Loaded");
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- public static void OnDialogLoaded(object __instance)
|
||||||
|
- {
|
||||||
|
- try
|
||||||
|
- {
|
||||||
|
- var window = __instance as Window;
|
||||||
|
- if (window == null) return;
|
||||||
|
-
|
||||||
|
- // Get CloudServicesService via Application.Current.GetService<T>()
|
||||||
|
- var app = Application.Current;
|
||||||
|
- var getServiceMethod = app.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
- .Where(m => m.Name == "GetService" && m.IsGenericMethod)
|
||||||
|
- .FirstOrDefault();
|
||||||
|
-
|
||||||
|
- if (getServiceMethod != null)
|
||||||
|
- {
|
||||||
|
- var cssType = _authHookField.DeclaringType;
|
||||||
|
- var bound = getServiceMethod.MakeGenericMethod(cssType);
|
||||||
|
- _cloudServicesService = bound.Invoke(app, null);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- // Listen for DataContext property changes to detect state transitions
|
||||||
|
- var model = ((FrameworkElement)window).DataContext;
|
||||||
|
- if (model is System.ComponentModel.INotifyPropertyChanged npc)
|
||||||
|
- {
|
||||||
|
- npc.PropertyChanged += (s, e) =>
|
||||||
|
- {
|
||||||
|
- if (e.PropertyName == "State")
|
||||||
|
- InjectPasteUI(window);
|
||||||
|
- };
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- // Also try immediately in case we're already in SigningIn
|
||||||
|
- InjectPasteUI(window);
|
||||||
|
- }
|
||||||
|
- catch (Exception ex)
|
||||||
|
- {
|
||||||
|
- Logger.Error($"OnDialogLoaded failed: {ex.Message}");
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- private static void InjectPasteUI(Window window)
|
||||||
|
- {
|
||||||
|
- try
|
||||||
|
- {
|
||||||
|
- var model = ((FrameworkElement)window).DataContext;
|
||||||
|
- var stateProp = model.GetType().GetProperty("State");
|
||||||
|
- if (stateProp == null) return;
|
||||||
|
-
|
||||||
|
- var state = stateProp.GetValue(model);
|
||||||
|
- if (state.ToString() == "SigningIn")
|
||||||
|
- {
|
||||||
|
- window.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Loaded,
|
||||||
|
- new Action(() => InjectPasteUIImpl(window)));
|
||||||
|
- }
|
||||||
|
- else
|
||||||
|
- {
|
||||||
|
- HideOverlay();
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- catch (Exception ex)
|
||||||
|
- {
|
||||||
|
- Logger.Error($"InjectPasteUI failed: {ex.Message}");
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- private static void InjectPasteUIImpl(Window window)
|
||||||
|
- {
|
||||||
|
- try
|
||||||
|
- {
|
||||||
|
- if (_overlayPanel != null)
|
||||||
|
- {
|
||||||
|
- _overlayPanel.Visibility = Visibility.Visible;
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- var existingContent = window.Content as UIElement;
|
||||||
|
- if (existingContent == null) return;
|
||||||
|
-
|
||||||
|
- // Wrap existing window content in a new Grid so we can overlay on top
|
||||||
|
- var wrapperGrid = new Grid();
|
||||||
|
- window.Content = wrapperGrid;
|
||||||
|
- wrapperGrid.Children.Add(existingContent);
|
||||||
|
-
|
||||||
|
- // Build overlay content
|
||||||
|
- var panel = new StackPanel
|
||||||
|
- {
|
||||||
|
- VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
- Margin = new Thickness(30)
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- var heading = new TextBlock
|
||||||
|
- {
|
||||||
|
- Text = "Running under Wine?",
|
||||||
|
- FontSize = 14,
|
||||||
|
- FontWeight = FontWeights.SemiBold,
|
||||||
|
- Foreground = new SolidColorBrush(Color.FromRgb(0x33, 0x33, 0x33)),
|
||||||
|
- Margin = new Thickness(0, 0, 0, 8)
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- var instructions = new TextBlock
|
||||||
|
- {
|
||||||
|
- Text = "After signing in, your browser will show a " +
|
||||||
|
- "\"Launching Affinity\" page. Copy the full URL " +
|
||||||
|
- "from the address bar and paste it below:",
|
||||||
|
- Foreground = new SolidColorBrush(Color.FromRgb(0x65, 0x65, 0x65)),
|
||||||
|
- FontSize = 12,
|
||||||
|
- TextWrapping = TextWrapping.Wrap,
|
||||||
|
- Margin = new Thickness(0, 0, 0, 10)
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- var screenshot = LoadEmbeddedImage("WineFix.Resources.signin.png");
|
||||||
|
- Image screenshotImage = null;
|
||||||
|
- if (screenshot != null)
|
||||||
|
- {
|
||||||
|
- screenshotImage = new Image
|
||||||
|
- {
|
||||||
|
- Source = screenshot,
|
||||||
|
- Stretch = Stretch.Uniform,
|
||||||
|
- HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||||
|
- Margin = new Thickness(0, 0, 0, 12)
|
||||||
|
- };
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- var textBox = new TextBox
|
||||||
|
- {
|
||||||
|
- Height = 28,
|
||||||
|
- FontSize = 12,
|
||||||
|
- VerticalContentAlignment = VerticalAlignment.Center,
|
||||||
|
- HorizontalAlignment = HorizontalAlignment.Stretch
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- var placeholder = new TextBlock
|
||||||
|
- {
|
||||||
|
- Text = "https://page.service.serif.com/canva-auth-redirect/...",
|
||||||
|
- Foreground = new SolidColorBrush(Color.FromRgb(0xAA, 0xAA, 0xAA)),
|
||||||
|
- FontSize = 12,
|
||||||
|
- IsHitTestVisible = false,
|
||||||
|
- VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
- Margin = new Thickness(4, 0, 0, 0)
|
||||||
|
- };
|
||||||
|
- var textBoxContainer = new Grid { Height = 28, HorizontalAlignment = HorizontalAlignment.Stretch };
|
||||||
|
- textBoxContainer.Children.Add(textBox);
|
||||||
|
- textBoxContainer.Children.Add(placeholder);
|
||||||
|
- textBox.TextChanged += (s, e) => placeholder.Visibility =
|
||||||
|
- string.IsNullOrEmpty(textBox.Text) ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
- textBox.GotFocus += (s, e) => placeholder.Visibility = Visibility.Collapsed;
|
||||||
|
- textBox.LostFocus += (s, e) => placeholder.Visibility =
|
||||||
|
- string.IsNullOrEmpty(textBox.Text) ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
-
|
||||||
|
- var button = new Button
|
||||||
|
- {
|
||||||
|
- Content = "Submit",
|
||||||
|
- Margin = new Thickness(0, 8, 0, 0),
|
||||||
|
- Padding = new Thickness(0, 8, 0, 8),
|
||||||
|
- HorizontalAlignment = HorizontalAlignment.Stretch
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- var errorText = new TextBlock
|
||||||
|
- {
|
||||||
|
- Foreground = Brushes.Red,
|
||||||
|
- FontSize = 11,
|
||||||
|
- TextWrapping = TextWrapping.Wrap,
|
||||||
|
- Visibility = Visibility.Collapsed,
|
||||||
|
- Margin = new Thickness(0, 4, 0, 0)
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- button.Click += (s, e) => OnSubmitUrl(textBox.Text, errorText);
|
||||||
|
-
|
||||||
|
- panel.Children.Add(heading);
|
||||||
|
- panel.Children.Add(instructions);
|
||||||
|
- if (screenshotImage != null)
|
||||||
|
- panel.Children.Add(screenshotImage);
|
||||||
|
- panel.Children.Add(textBoxContainer);
|
||||||
|
- panel.Children.Add(button);
|
||||||
|
- panel.Children.Add(errorText);
|
||||||
|
-
|
||||||
|
- // Overlay: right-aligned, 50% width, full height, white background
|
||||||
|
- _overlayPanel = new Border
|
||||||
|
- {
|
||||||
|
- Background = new SolidColorBrush(Color.FromArgb(0xF0, 0xFF, 0xFF, 0xFF)),
|
||||||
|
- HorizontalAlignment = HorizontalAlignment.Right,
|
||||||
|
- VerticalAlignment = VerticalAlignment.Stretch,
|
||||||
|
- Child = panel
|
||||||
|
- };
|
||||||
|
- _overlayPanel.SetBinding(FrameworkElement.WidthProperty,
|
||||||
|
- new Binding("ActualWidth")
|
||||||
|
- {
|
||||||
|
- Source = window,
|
||||||
|
- Converter = new HalfWidthConverter()
|
||||||
|
- });
|
||||||
|
-
|
||||||
|
- wrapperGrid.Children.Add(_overlayPanel);
|
||||||
|
-
|
||||||
|
- // Nudge the "Back" button left so it's not covered by the overlay
|
||||||
|
- var contentControl = FindChild<ContentControl>(existingContent,
|
||||||
|
- c => c.ContentTemplateSelector != null);
|
||||||
|
- if (contentControl != null)
|
||||||
|
- {
|
||||||
|
- var backButton = FindChild<Button>(contentControl, b =>
|
||||||
|
- Grid.GetRow(b) == 4 && Grid.GetColumnSpan(b) == 3
|
||||||
|
- && b.HorizontalAlignment == HorizontalAlignment.Center);
|
||||||
|
- if (backButton != null)
|
||||||
|
- backButton.HorizontalAlignment = HorizontalAlignment.Left;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- Logger.Info("Injected paste-URL overlay into SigningIn dialog");
|
||||||
|
- }
|
||||||
|
- catch (Exception ex)
|
||||||
|
- {
|
||||||
|
- Logger.Error($"InjectPasteUIImpl failed: {ex.Message}");
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- private static void HideOverlay()
|
||||||
|
- {
|
||||||
|
- if (_overlayPanel != null)
|
||||||
|
- _overlayPanel.Visibility = Visibility.Collapsed;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- private static void OnSubmitUrl(string input, TextBlock errorText)
|
||||||
|
- {
|
||||||
|
- try
|
||||||
|
- {
|
||||||
|
- errorText.Visibility = Visibility.Collapsed;
|
||||||
|
-
|
||||||
|
- if (string.IsNullOrWhiteSpace(input))
|
||||||
|
- {
|
||||||
|
- ShowError(errorText, "Please paste a URL.");
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- var affinityUri = ExtractAffinityUri(input.Trim());
|
||||||
|
- if (affinityUri == null)
|
||||||
|
- {
|
||||||
|
- ShowError(errorText, "Could not find an authentication URL. Please paste the full URL from your browser's address bar after signing in.");
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (_cloudServicesService == null || _authHookField == null)
|
||||||
|
- {
|
||||||
|
- ShowError(errorText, "Internal error: cloud services not available.");
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- var handler = _authHookField.GetValue(_cloudServicesService) as Action<Uri>;
|
||||||
|
- if (handler == null)
|
||||||
|
- {
|
||||||
|
- ShowError(errorText, "Internal error: auth hook not registered.");
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- Logger.Info($"Invoking AuthHookReceived with: {affinityUri}");
|
||||||
|
- handler.Invoke(affinityUri);
|
||||||
|
- }
|
||||||
|
- catch (Exception ex)
|
||||||
|
- {
|
||||||
|
- Logger.Error($"OnSubmitUrl failed: {ex}");
|
||||||
|
- ShowError(errorText, $"Error: {ex.Message}");
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /// <summary>
|
||||||
|
- /// Extracts the affinity:// URI from either:
|
||||||
|
- /// - A direct affinity://canva/authorize?... URL
|
||||||
|
- /// - A redirect page URL: https://page.service.serif.com/canva-auth-redirect/?url=affinity%3A%2F%2F...
|
||||||
|
- /// </summary>
|
||||||
|
- private static Uri ExtractAffinityUri(string input)
|
||||||
|
- {
|
||||||
|
- if (input.StartsWith("affinity://", StringComparison.OrdinalIgnoreCase))
|
||||||
|
- return new Uri(input);
|
||||||
|
-
|
||||||
|
- try
|
||||||
|
- {
|
||||||
|
- var uri = new Uri(input);
|
||||||
|
- var query = uri.Query;
|
||||||
|
- if (string.IsNullOrEmpty(query)) return null;
|
||||||
|
-
|
||||||
|
- var decoded = ParseQueryParam(query, "url");
|
||||||
|
- if (decoded != null && decoded.StartsWith("affinity://", StringComparison.OrdinalIgnoreCase))
|
||||||
|
- return new Uri(decoded);
|
||||||
|
- }
|
||||||
|
- catch { }
|
||||||
|
-
|
||||||
|
- return null;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- private static string ParseQueryParam(string query, string name)
|
||||||
|
- {
|
||||||
|
- if (query.StartsWith("?")) query = query.Substring(1);
|
||||||
|
-
|
||||||
|
- foreach (var part in query.Split('&'))
|
||||||
|
- {
|
||||||
|
- var eq = part.IndexOf('=');
|
||||||
|
- if (eq < 0) continue;
|
||||||
|
- var key = Uri.UnescapeDataString(part.Substring(0, eq));
|
||||||
|
- if (string.Equals(key, name, StringComparison.OrdinalIgnoreCase))
|
||||||
|
- return Uri.UnescapeDataString(part.Substring(eq + 1));
|
||||||
|
- }
|
||||||
|
- return null;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- private static void ShowError(TextBlock tb, string msg)
|
||||||
|
- {
|
||||||
|
- tb.Text = msg;
|
||||||
|
- tb.Visibility = Visibility.Visible;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- private static BitmapImage LoadEmbeddedImage(string resourceName)
|
||||||
|
- {
|
||||||
|
- try
|
||||||
|
- {
|
||||||
|
- var assembly = typeof(CanvaSignInPatch).Assembly;
|
||||||
|
- using (var stream = assembly.GetManifestResourceStream(resourceName))
|
||||||
|
- {
|
||||||
|
- if (stream == null) return null;
|
||||||
|
- var bitmap = new BitmapImage();
|
||||||
|
- bitmap.BeginInit();
|
||||||
|
- bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||||
|
- bitmap.StreamSource = stream;
|
||||||
|
- bitmap.EndInit();
|
||||||
|
- bitmap.Freeze();
|
||||||
|
- return bitmap;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- catch (Exception ex)
|
||||||
|
- {
|
||||||
|
- Logger.Error($"Failed to load embedded image: {ex.Message}");
|
||||||
|
- return null;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- private static T FindChild<T>(DependencyObject parent, Func<T, bool> predicate = null)
|
||||||
|
- where T : DependencyObject
|
||||||
|
- {
|
||||||
|
- var count = VisualTreeHelper.GetChildrenCount(parent);
|
||||||
|
- for (int i = 0; i < count; i++)
|
||||||
|
- {
|
||||||
|
- var child = VisualTreeHelper.GetChild(parent, i);
|
||||||
|
- if (child is T t && (predicate == null || predicate(t)))
|
||||||
|
- return t;
|
||||||
|
- var result = FindChild(child, predicate);
|
||||||
|
- if (result != null) return result;
|
||||||
|
- }
|
||||||
|
- return null;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- internal class HalfWidthConverter : IValueConverter
|
||||||
|
- {
|
||||||
|
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
- {
|
||||||
|
- if (value is double d) return d * 0.5;
|
||||||
|
- return value;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
- {
|
||||||
|
- throw new NotImplementedException();
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-}
|
||||||
|
diff --git a/WineFix/Patches/MainWindowLoadedPatch.cs b/WineFix/Patches/MainWindowLoadedPatch.cs
|
||||||
|
index 5c658b5..8e32a33 100644
|
||||||
|
--- a/WineFix/Patches/MainWindowLoadedPatch.cs
|
||||||
|
+++ b/WineFix/Patches/MainWindowLoadedPatch.cs
|
||||||
|
@@ -52,10 +52,12 @@ namespace WineFix.Patches
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transpiler that replaces the call to HasPreviousPackageInstalled() with loading 'false'
|
||||||
|
+ // and also removes the call to base.OnMainWindowLoaded()
|
||||||
|
public static IEnumerable<CodeInstruction> OnMainWindowLoaded_Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||||
|
{
|
||||||
|
var codes = new List<CodeInstruction>(instructions);
|
||||||
|
bool patchedPackageCheck = false;
|
||||||
|
+ bool patchedBaseCall = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < codes.Count; i++)
|
||||||
|
{
|
||||||
|
@@ -97,6 +99,54 @@ namespace WineFix.Patches
|
||||||
|
patchedPackageCheck = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ // Look for: call instance void Serif.Interop.Persona.Application::OnMainWindowLoaded(...)
|
||||||
|
+ // (Index 22 in the IL - base class is Serif.Interop.Persona.Application, not System.Windows.Application!)
|
||||||
|
+ if (!patchedBaseCall &&
|
||||||
|
+ instruction.opcode == OpCodes.Call &&
|
||||||
|
+ instruction.operand is MethodInfo baseMethod &&
|
||||||
|
+ baseMethod.Name == "OnMainWindowLoaded" &&
|
||||||
|
+ baseMethod.DeclaringType != null &&
|
||||||
|
+ (baseMethod.DeclaringType.FullName == "Serif.Interop.Persona.Application" ||
|
||||||
|
+ baseMethod.DeclaringType.FullName == "System.Windows.Application"))
|
||||||
|
+ {
|
||||||
|
+ Logger.Debug($"Found base.OnMainWindowLoaded call at instruction {i}");
|
||||||
|
+
|
||||||
|
+ // The IL sequence is (indices 20-22):
|
||||||
|
+ // ldarg.0 ; load 'this'
|
||||||
|
+ // ldarg.1 ; load 'mainWindow'
|
||||||
|
+ // call base.OnMainWindowLoaded
|
||||||
|
+ // Replace all three with NOPs, preserving labels
|
||||||
|
+ if (i >= 2 &&
|
||||||
|
+ codes[i - 2].opcode == OpCodes.Ldarg_0 &&
|
||||||
|
+ codes[i - 1].opcode == OpCodes.Ldarg_1)
|
||||||
|
+ {
|
||||||
|
+ // Create NOPs but preserve labels
|
||||||
|
+ var nop1 = new CodeInstruction(OpCodes.Nop);
|
||||||
|
+ nop1.labels.AddRange(codes[i - 2].labels);
|
||||||
|
+ codes[i - 2] = nop1;
|
||||||
|
+
|
||||||
|
+ var nop2 = new CodeInstruction(OpCodes.Nop);
|
||||||
|
+ nop2.labels.AddRange(codes[i - 1].labels);
|
||||||
|
+ codes[i - 1] = nop2;
|
||||||
|
+
|
||||||
|
+ var nop3 = new CodeInstruction(OpCodes.Nop);
|
||||||
|
+ nop3.labels.AddRange(codes[i].labels);
|
||||||
|
+ codes[i] = nop3;
|
||||||
|
+
|
||||||
|
+ Logger.Debug($"Removed base.OnMainWindowLoaded call (indices {i-2} to {i})");
|
||||||
|
+ patchedBaseCall = true;
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ Logger.Warning($"WARNING: Found base.OnMainWindowLoaded but preceding instructions don't match expected pattern");
|
||||||
|
+ var nop = new CodeInstruction(OpCodes.Nop);
|
||||||
|
+ nop.labels.AddRange(codes[i].labels);
|
||||||
|
+ codes[i] = nop;
|
||||||
|
+ patchedBaseCall = true;
|
||||||
|
+ }
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!patchedPackageCheck)
|
||||||
|
@@ -104,6 +154,11 @@ namespace WineFix.Patches
|
||||||
|
Logger.Warning($"WARNING: Could not find HasPreviousPackageInstalled call to patch");
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (!patchedBaseCall)
|
||||||
|
+ {
|
||||||
|
+ Logger.Warning($"WARNING: Could not find base.OnMainWindowLoaded call to patch");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return codes.AsEnumerable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/WineFix/WineFix.csproj b/WineFix/WineFix.csproj
|
||||||
|
index b1d3c82..c62939d 100644
|
||||||
|
--- a/WineFix/WineFix.csproj
|
||||||
|
+++ b/WineFix/WineFix.csproj
|
||||||
|
@@ -29,10 +29,6 @@
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
- <Reference Include="System.Xaml" />
|
||||||
|
- </ItemGroup>
|
||||||
|
- <ItemGroup>
|
||||||
|
- <EmbeddedResource Include="Resources\signin.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\AffinityPluginLoader\AffinityPluginLoader.csproj">
|
||||||
|
diff --git a/WineFix/WineFixPlugin.cs b/WineFix/WineFixPlugin.cs
|
||||||
|
index 4ed9f42..a6ccd09 100644
|
||||||
|
--- a/WineFix/WineFixPlugin.cs
|
||||||
|
+++ b/WineFix/WineFixPlugin.cs
|
||||||
|
@@ -25,10 +25,6 @@ namespace WineFix
|
||||||
|
{
|
||||||
|
return new PluginSettingsDefinition(PluginId)
|
||||||
|
.AddSection("Patches")
|
||||||
|
- .AddBool(SettingCanvaSignInHelper, "Canva sign-in helper",
|
||||||
|
- defaultValue: true,
|
||||||
|
- restartRequired: true,
|
||||||
|
- description: "Patch the Canva sign-in dialog to include a helper textbox input and instructions to complete Canva sign-in without needing a protocol URL handler.")
|
||||||
|
.AddBool(BezierRenderingFixKey, "Bezier curves: Fix tool preview path rendering",
|
||||||
|
defaultValue: true,
|
||||||
|
restartRequired: true,
|
||||||
|
@@ -109,16 +105,6 @@ namespace WineFix
|
||||||
|
{
|
||||||
|
Logger.Info("Skipping ColorPicker Wayland fix (setting: " + magnifierFix + ")");
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- if (context.Settings.GetEffectiveValue<bool>(SettingCanvaSignInHelper))
|
||||||
|
- {
|
||||||
|
- context.Patch("Canva sign-in paste URL fix",
|
||||||
|
- h => Patches.CanvaSignInPatch.ApplyPatches(h));
|
||||||
|
- }
|
||||||
|
- else
|
||||||
|
- {
|
||||||
|
- Logger.Info("Skipping Canva sign-in helper (disabled in settings)");
|
||||||
|
- }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
{
|
||||||
|
linkFarm,
|
||||||
|
fetchurl,
|
||||||
|
writeText,
|
||||||
|
runCommand,
|
||||||
|
callPackage,
|
||||||
|
xvfb-run,
|
||||||
|
lib,
|
||||||
|
inputs,
|
||||||
|
wine-packages,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
verbs = [
|
||||||
|
"tahoma"
|
||||||
|
"vcrun2022"
|
||||||
|
#"dotnet20"
|
||||||
|
"dotnet48"
|
||||||
|
"corefonts"
|
||||||
|
"win11"
|
||||||
|
];
|
||||||
|
|
||||||
|
inherit (wine-packages)
|
||||||
|
wine
|
||||||
|
winetricks
|
||||||
|
wineserver
|
||||||
|
;
|
||||||
|
|
||||||
|
dependencies = callPackage ./dependencies.nix { };
|
||||||
|
winetricksCache = linkFarm "winetricks-cache" [
|
||||||
|
{
|
||||||
|
name = "vcrun2022/vc_redist.x64.exe";
|
||||||
|
path = fetchurl {
|
||||||
|
urls = [
|
||||||
|
"https://download.visualstudio.microsoft.com/download/pr/7ebf5fdb-36dc-4145-b0a0-90d3d5990a61/CC0FF0EB1DC3F5188AE6300FAEF32BF5BEEBA4BDD6E8E445A9184072096B713B/VC_redist.x64.exe"
|
||||||
|
];
|
||||||
|
hash = "sha256-zA/w6x3D9RiK5jAPrvMr9b7rpL3W6ORFqRhAcglrcTs=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "vcrun2022/vc_redist.x86.exe";
|
||||||
|
path = fetchurl {
|
||||||
|
urls = [
|
||||||
|
"https://download.visualstudio.microsoft.com/download/pr/7ebf5fdb-36dc-4145-b0a0-90d3d5990a61/0C09F2611660441084CE0DF425C51C11E147E6447963C3690F97E0B25C55ED64/VC_redist.x86.exe"
|
||||||
|
];
|
||||||
|
hash = "sha256-DAnyYRZgRBCEzg30JcUcEeFH5kR5Y8NpD5fgslxV7WQ=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "dotnet48/ndp48-x86-x64-allos-enu.exe";
|
||||||
|
path = fetchurl {
|
||||||
|
urls = [
|
||||||
|
"https://download.visualstudio.microsoft.com/download/pr/7afca223-55d2-470a-8edc-6a1739ae3252/abd170b4b0ec15ad0222a809b761a036/ndp48-x86-x64-allos-enu.exe"
|
||||||
|
];
|
||||||
|
hash = "sha256-lYidbePyBwwHeQrWzyAA0z2aG9/Go4FyWrgqscMU/VM=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "dotnet40/dotNetFx40_Full_x86_x64.exe";
|
||||||
|
path = fetchurl {
|
||||||
|
urls = [
|
||||||
|
"https://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe"
|
||||||
|
];
|
||||||
|
hash = "sha256-ZeBkJY8uQYgWswT2Rv+eh68QHkyVUqsGS7dNKBw4ZZ8=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "tahoma/IELPKTH.CAB";
|
||||||
|
path = fetchurl {
|
||||||
|
url = "https://downloads.sourceforge.net/corefonts/OldFiles/IELPKTH.CAB";
|
||||||
|
hash = "sha256-wb4/uPAEJXC+duxtqgOpkULIg2fBvIECQLhYJ8cVlho=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "dotnet20/NetFx64.exe";
|
||||||
|
path = fetchurl {
|
||||||
|
urls = [
|
||||||
|
"https://download.microsoft.com/download/9/8/6/98610406-c2b7-45a4-bdc3-9db1b1c5f7e2/NetFx20SP1_x64.exe"
|
||||||
|
];
|
||||||
|
hash = "sha256-FzHlPeX0i6rgljZ3JXZg3xMpVJ6BxItNfbf38/Iymqs=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
layer_1 = runCommand "base-prefix-1" { } ''
|
||||||
|
set -x -e
|
||||||
|
mkdir -p $out
|
||||||
|
export WINEPREFIX="$out"
|
||||||
|
export WINETRICKS_UPDATE_CHECK=0
|
||||||
|
export WINETRICKS_LATEST_VERSION_CHECK=disabled
|
||||||
|
|
||||||
|
mkdir -p /tmp/cache
|
||||||
|
export XDG_CACHE_HOME="/tmp/cache"
|
||||||
|
|
||||||
|
${lib.getExe wine} wineboot --update
|
||||||
|
|
||||||
|
# by diffing a registry dump we found that you can disable the file association
|
||||||
|
# through a registry key.
|
||||||
|
${lib.getExe wine} regedit /S "${(writeText "file-association-disable.reg" ''
|
||||||
|
Windows Registry Editor Version 5.00
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Wine\DllOverrides]
|
||||||
|
"winemenubuilder.exe"=""
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Wine\FileOpenAssociations]
|
||||||
|
"Enable"="N"
|
||||||
|
'').outPath}"
|
||||||
|
|
||||||
|
${lib.getExe winetricks} renderer=vulkan
|
||||||
|
|
||||||
|
install -D -t "$WINEPREFIX/drive_c/windows/system32/WinMetadata/" ${dependencies}/*.winmd
|
||||||
|
|
||||||
|
${lib.getExe wineserver} -w
|
||||||
|
'';
|
||||||
|
|
||||||
|
layer_2 =
|
||||||
|
runCommand "base-prefix-2"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [
|
||||||
|
xvfb-run
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
set -x -e
|
||||||
|
|
||||||
|
mkdir -p $out
|
||||||
|
cp -a ${layer_1}/. $out
|
||||||
|
chmod -R +w $out
|
||||||
|
export WINEPREFIX="$out"
|
||||||
|
|
||||||
|
mkdir -p /tmp/cache
|
||||||
|
export XDG_CACHE_HOME="/tmp/cache"
|
||||||
|
|
||||||
|
${lib.getExe wine} winecfg -v win7
|
||||||
|
xvfb-run ${lib.getExe wine} "${dependencies}/MicrosoftEdgeWebView2RuntimeInstallerX64.exe" /silent /install
|
||||||
|
${lib.getExe wine} winecfg -v win11
|
||||||
|
|
||||||
|
${lib.getExe wine} regedit /S "${(writeText "webview2-regedit-changes.reg" ''
|
||||||
|
Windows Registry Editor Version 5.00
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\edgeupdate]
|
||||||
|
"Start"=dword:00000004
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\edgeupdatem]
|
||||||
|
"Start"=dword:00000004
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Wine\AppDefaults]
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Wine\AppDefaults\msedgewebview2.exe]
|
||||||
|
"Version"="win7"
|
||||||
|
'').outPath}"
|
||||||
|
|
||||||
|
# The Edge Update service gets to start before we can deactivate it, so it must be stopped manually
|
||||||
|
${lib.getExe wine} taskkill /f /im MicrosoftEdgeUpdate.exe
|
||||||
|
|
||||||
|
${lib.getExe wineserver} -w
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
runCommand "base-prefix-3"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [
|
||||||
|
xvfb-run
|
||||||
|
];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
set -x -e
|
||||||
|
|
||||||
|
${lib.strings.toShellVars {
|
||||||
|
inherit verbs;
|
||||||
|
}}
|
||||||
|
|
||||||
|
mkdir -p /tmp/cache/winetricks/corefonts
|
||||||
|
|
||||||
|
mkdir -p $out
|
||||||
|
cp -a ${layer_2}/. $out
|
||||||
|
chmod -R +w $out
|
||||||
|
export WINEPREFIX="$out"
|
||||||
|
|
||||||
|
export XDG_CACHE_HOME="/tmp/cache"
|
||||||
|
export WINETRICKS_UPDATE_CHECK=0
|
||||||
|
export WINETRICKS_LATEST_VERSION_CHECK=disabled
|
||||||
|
|
||||||
|
export WINEDEBUG=-all
|
||||||
|
export WINEESYNC=0
|
||||||
|
export WINEFSYNC=0
|
||||||
|
|
||||||
|
cp -R ${winetricksCache}/* /tmp/cache/winetricks
|
||||||
|
cp -R ${inputs.corefonts}/*.exe /tmp/cache/winetricks/corefonts
|
||||||
|
|
||||||
|
xvfb-run ${lib.getExe winetricks} -q -f "''${verbs[@]}"
|
||||||
|
|
||||||
|
${lib.getExe wineserver} -w
|
||||||
|
''
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./wine
|
||||||
|
./affinity
|
||||||
|
./affinity-v3
|
||||||
|
./apl
|
||||||
|
./runner
|
||||||
|
];
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
wine-packages,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
# this function builds the prefix right up until the affinity sources
|
||||||
|
# are used. this separation exists for build caching, as the affinity sources
|
||||||
|
# should never be exposed to a cache
|
||||||
|
packages.base-prefix-pre-affinity = pkgs.callPackage ./basePrefix.nix {
|
||||||
|
inherit inputs wine-packages;
|
||||||
|
};
|
||||||
|
|
||||||
|
_module.args = {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
fetchurl,
|
||||||
|
lib,
|
||||||
|
runCommand,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
paths = {
|
||||||
|
"Windows.Services.winmd" = fetchurl {
|
||||||
|
url = "https://archive.org/download/windows.services-system.winmd/Windows.Services.winmd";
|
||||||
|
hash = "sha256-l9TRXyyacQFHoVsq6s8zGvJ+SLl4pTNv6yesNksAzdQ=";
|
||||||
|
};
|
||||||
|
"Windows.System.winmd" = fetchurl {
|
||||||
|
url = "https://archive.org/download/windows.services-system.winmd/Windows.System.winmd";
|
||||||
|
hash = "sha256-EOh5DJXzF+gdqW1Jc4lS1/J2x3hC/SxRkHMzzS4h0TM=";
|
||||||
|
};
|
||||||
|
"MicrosoftEdgeWebView2RuntimeInstallerX64.exe" = fetchurl {
|
||||||
|
url = "https://archive.org/download/microsoft-edge-web-view-2-runtime-installer-v109.0.1518.78/MicrosoftEdgeWebView2RuntimeInstallerX64.exe";
|
||||||
|
hash = "sha256-8sxJhj4iFALUZk2fqUSkfyJUPaLcs2NDjD5Zh4m5/Vs=";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
runCommand "dependencies" { } ''
|
||||||
|
mkdir -p $out
|
||||||
|
|
||||||
|
${lib.concatStringsSep "\n" (
|
||||||
|
lib.mapAttrsToList (name: content: ''
|
||||||
|
ln -s ${content} $out/${name}
|
||||||
|
'') paths
|
||||||
|
)}
|
||||||
|
''
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
callPackage,
|
||||||
|
fetchzip,
|
||||||
|
runCommand,
|
||||||
|
lndir,
|
||||||
|
zstd,
|
||||||
|
lib,
|
||||||
|
|
||||||
|
inputs,
|
||||||
|
v3 ? true,
|
||||||
|
wine-packages,
|
||||||
|
apl-combined,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
layer_3 = callPackage ./basePrefix.nix {
|
||||||
|
inherit inputs wine-packages;
|
||||||
|
};
|
||||||
|
|
||||||
|
installers = callPackage ./sources.nix { };
|
||||||
|
registry-patches = callPackage ./registry-patches.nix { };
|
||||||
|
|
||||||
|
vkd3d = fetchzip {
|
||||||
|
url = "https://github.com/HansKristian-Work/vkd3d-proton/releases/download/v3.0.1/vkd3d-proton-3.0.1.tar.zst";
|
||||||
|
nativeBuildInputs = [ zstd ];
|
||||||
|
hash = "sha256-xHJFtVDD/ZHVHF2Fn7TEEX0fMUWJvujNyNt2Xyw9F7o=";
|
||||||
|
};
|
||||||
|
|
||||||
|
inherit (wine-packages) wine wineserver;
|
||||||
|
in
|
||||||
|
runCommand "base-prefix-4" { } ''
|
||||||
|
set -x -e
|
||||||
|
|
||||||
|
mkdir -p $out
|
||||||
|
cp -a ${layer_3}/. $out
|
||||||
|
chmod -R +w $out
|
||||||
|
export WINEPREFIX="$out"
|
||||||
|
|
||||||
|
cp ${vkd3d}/x64/d3d12.dll "$WINEPREFIX/drive_c/windows/system32"
|
||||||
|
cp ${vkd3d}/x64/d3d12core.dll "$WINEPREFIX/drive_c/windows/system32"
|
||||||
|
|
||||||
|
${lib.getExe wine} regedit /S "${registry-patches.one-vkd3d}"
|
||||||
|
|
||||||
|
${lib.optionalString v3 ''
|
||||||
|
${lib.getExe lndir} ${installers.v3} "$WINEPREFIX/drive_c/Program Files/"
|
||||||
|
|
||||||
|
pushd "$WINEPREFIX/drive_c/Program Files/Affinity/Affinity"
|
||||||
|
cp -r "${apl-combined}/." .
|
||||||
|
popd
|
||||||
|
''}
|
||||||
|
|
||||||
|
${lib.optionalString (!v3) ''
|
||||||
|
${lib.getExe lndir} ${installers.photo} "$WINEPREFIX/drive_c/Program Files/"
|
||||||
|
${lib.getExe lndir} ${installers.designer} "$WINEPREFIX/drive_c/Program Files/"
|
||||||
|
${lib.getExe lndir} ${installers.publisher} "$WINEPREFIX/drive_c/Program Files/"
|
||||||
|
''}
|
||||||
|
|
||||||
|
${lib.getExe wineserver} -w
|
||||||
|
|
||||||
|
rm -rf $WINEPREFIX/drive_c/users/nixbld
|
||||||
|
''
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
}:
|
||||||
|
rec {
|
||||||
|
# vkd3d was added in revision 10
|
||||||
|
one-vkd3d =
|
||||||
|
(pkgs.writeText "vkd3d-regedit-changes.reg" ''
|
||||||
|
Windows Registry Editor Version 5.00
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Wine\DllOverrides]
|
||||||
|
"d3d12"="native"
|
||||||
|
"d3d12core"="native"
|
||||||
|
'').outPath;
|
||||||
|
|
||||||
|
combined = pkgs.linkFarm "registry-patches-combined" [
|
||||||
|
{
|
||||||
|
name = "one.reg";
|
||||||
|
path = one-vkd3d;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
wine-packages,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
runner = pkgs.callPackage ./package.nix {
|
||||||
|
inherit inputs wine-packages;
|
||||||
|
|
||||||
|
registry-patches = (pkgs.callPackage ../registry-patches.nix { }).combined;
|
||||||
|
prefixBase = "/non-functional-runner";
|
||||||
|
name = "v3";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
checks = {
|
||||||
|
runner = runner.package;
|
||||||
|
|
||||||
|
runner-clippy = runner.package-clippy;
|
||||||
|
};
|
||||||
|
|
||||||
|
packages = {
|
||||||
|
runner = runner.package;
|
||||||
|
runner-artifacts = runner.package-artifacts;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
|
||||||
|
inputs,
|
||||||
|
registry-patches,
|
||||||
|
wine-packages,
|
||||||
|
prefixBase,
|
||||||
|
name,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
craneLib = inputs.crane.mkLib pkgs;
|
||||||
|
src = craneLib.cleanCargoSource ../..;
|
||||||
|
commonArgs = {
|
||||||
|
inherit src;
|
||||||
|
strictDeps = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
|
||||||
|
|
||||||
|
fileSetForCrate =
|
||||||
|
crate:
|
||||||
|
lib.fileset.toSource {
|
||||||
|
root = ../..;
|
||||||
|
fileset = lib.fileset.unions [
|
||||||
|
../../Cargo.toml
|
||||||
|
../../Cargo.lock
|
||||||
|
(craneLib.fileset.commonCargoSources crate)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
env = {
|
||||||
|
WINE = lib.getExe wine-packages.wine;
|
||||||
|
WINESERVER = lib.getExe wine-packages.wineserver;
|
||||||
|
WINETRICKS = lib.getExe wine-packages.winetricks;
|
||||||
|
FUSE_OVERLAYFS = lib.getExe pkgs.fuse-overlayfs;
|
||||||
|
GNUTAR = lib.getExe pkgs.gnutar;
|
||||||
|
ZENITY = lib.getExe pkgs.zenity;
|
||||||
|
RSYNC = lib.getExe pkgs.rsync;
|
||||||
|
REGISTRY_PATCHES = registry-patches;
|
||||||
|
ON_LINUX = inputs.on-linux.outPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
executableName = "affinity-${lib.toLower name}";
|
||||||
|
|
||||||
|
executable = craneLib.buildPackage (
|
||||||
|
commonArgs
|
||||||
|
// {
|
||||||
|
inherit cargoArtifacts env;
|
||||||
|
|
||||||
|
pname = executableName;
|
||||||
|
|
||||||
|
cargoExtraArgs = "-p runner --no-default-features --features ${lib.toLower name}";
|
||||||
|
src = fileSetForCrate ../../crates/runner;
|
||||||
|
|
||||||
|
meta.mainProgram = executableName;
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
mv $out/bin/runner $out/bin/affinity-${lib.toLower name}
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
package =
|
||||||
|
pkgs.runCommand executableName
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp ${lib.getExe executable} $out/bin/${executableName}
|
||||||
|
|
||||||
|
wrapProgram $out/bin/${executableName} \
|
||||||
|
--set "LOWER_DIR" "${prefixBase}" \
|
||||||
|
'';
|
||||||
|
|
||||||
|
package-clippy = craneLib.cargoClippy (
|
||||||
|
commonArgs
|
||||||
|
// {
|
||||||
|
inherit cargoArtifacts env;
|
||||||
|
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
package-artifacts = cargoArtifacts;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
|
||||||
|
mkExtract =
|
||||||
|
source: segment:
|
||||||
|
pkgs.runCommand "affinity-extracted-sources"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs._7zz
|
||||||
|
pkgs.msitools
|
||||||
|
];
|
||||||
|
|
||||||
|
meta.license = lib.licenses.unfree;
|
||||||
|
}
|
||||||
|
''
|
||||||
|
7zz e ${source} -tpe "${segment}"
|
||||||
|
msiextract -C $out ./*
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
v3 = mkExtract (pkgs.fetchurl {
|
||||||
|
url = "https://downloads.affinity.studio/Affinity%20x64.exe";
|
||||||
|
hash = "sha256-cnPdRZlZrl5Pp6Vr+GkO7br98GrcMDnnJPXcAboxkC4=";
|
||||||
|
}) ".rsrc/2057/BIN/135";
|
||||||
|
|
||||||
|
photo = mkExtract (pkgs.fetchurl {
|
||||||
|
name = "affinity-photo-msi-2.6.5.exe";
|
||||||
|
url = "https://archive.org/download/affinity_20251030/affinity-photo-msi-2.6.5.exe";
|
||||||
|
hash = "sha256-waCX33rWVzZ8AYZWmv2EOqS6ikT6p7s9qRJ+8rDo6Wk=";
|
||||||
|
}) ".rsrc/2057/BIN/135";
|
||||||
|
|
||||||
|
designer = mkExtract (pkgs.fetchurl {
|
||||||
|
name = "affinity-designer-msi-2.6.5.exe";
|
||||||
|
url = "https://archive.org/download/affinity_20251030/affinity-designer-msi-2.6.5.exe";
|
||||||
|
hash = "sha256-b5K1FtsCWCjyn/NC4trB0g9Wv+AfLoDWgaZK2ZGXW0w=";
|
||||||
|
}) ".rsrc/2057/BIN/135";
|
||||||
|
|
||||||
|
publisher = mkExtract (pkgs.fetchurl {
|
||||||
|
name = "affinity-publisher-msi-2.6.5.exe";
|
||||||
|
url = "https://archive.org/download/affinity_20251030/affinity-publisher-msi-2.6.5.exe";
|
||||||
|
hash = "sha256-76ikkAnbAghaSU5YLYLxXiHfAuqVfoEcQK16rHd6+7A=";
|
||||||
|
}) ".rsrc/2057/BIN/135";
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
wine-packages = pkgs.callPackage ./packages.nix {
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.wine = wine-packages.wine;
|
||||||
|
|
||||||
|
_module.args = {
|
||||||
|
inherit (wine-packages) wineUnwrapped;
|
||||||
|
inherit wine-packages;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
stdenv,
|
||||||
|
callPackage,
|
||||||
|
pkgs,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
wineUnstable = inputs.nixpkgs.legacyPackages.${stdenv.hostPlatform.system}.wineWow64Packages.full;
|
||||||
|
|
||||||
|
symlink = callPackage ./symlink.nix { };
|
||||||
|
|
||||||
|
wineUnwrapped = symlink {
|
||||||
|
wine = wineUnstable;
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapWithPrefix = callPackage ./wrapWithPrefix.nix {
|
||||||
|
inherit wineUnwrapped;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit wineUnwrapped;
|
||||||
|
|
||||||
|
wine = wrapWithPrefix wineUnwrapped "wine";
|
||||||
|
winetricks = wrapWithPrefix pkgs.winetricks "winetricks";
|
||||||
|
wineserver = wrapWithPrefix wineUnwrapped "wineserver";
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{ runCommand }:
|
||||||
|
{ wine }:
|
||||||
|
runCommand "wine-symlink" { inherit wine; } ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
for f in $wine/bin/*; do
|
||||||
|
cp -L "$f" $out/bin/
|
||||||
|
done
|
||||||
|
|
||||||
|
ln -s "$wine/bin/wine" $out/bin/wine64
|
||||||
|
for dir in include lib share; do
|
||||||
|
ln -s "$wine/$dir" $out
|
||||||
|
done
|
||||||
|
|
||||||
|
patchShebangs $out
|
||||||
|
''
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
stdenv,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
wineUnwrapped,
|
||||||
|
}:
|
||||||
|
pkg: pname:
|
||||||
|
stdenv.mkDerivation rec {
|
||||||
|
name = "affinity-${pname}";
|
||||||
|
src = ./.;
|
||||||
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
|
installPhase = ''
|
||||||
|
makeWrapper ${lib.getExe' pkg pname} $out/bin/${name} \
|
||||||
|
--set LD_LIBRARY_PATH "${wineUnwrapped}/lib:$LD_LIBRARY_PATH" \
|
||||||
|
--set WINESERVER "${lib.getExe' wineUnwrapped "wineserver"}" \
|
||||||
|
--set WINELOADER "${lib.getExe' wineUnwrapped "wine"}" \
|
||||||
|
--set WINEDLLPATH "${wineUnwrapped}/lib/wine" \
|
||||||
|
--set WINE "${lib.getExe' wineUnwrapped "wine"}"
|
||||||
|
'';
|
||||||
|
meta.mainProgram = name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
perSystem =
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
self',
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
stripTyping =
|
||||||
|
value:
|
||||||
|
let
|
||||||
|
split = builtins.split "(from typing import TYPE_CHECKING|# typing-end)" value;
|
||||||
|
in
|
||||||
|
(builtins.elemAt split 0) + (builtins.elemAt split 4);
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
virtualisation.memorySize = 4096;
|
||||||
|
|
||||||
|
imports = [ "${pkgs.path}/nixos/tests/common/x11.nix" ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
checks = {
|
||||||
|
test-affinity-v3 = pkgs.testers.runNixOSTest {
|
||||||
|
name = "test-affinity-v3";
|
||||||
|
enableOCR = true;
|
||||||
|
inherit defaults;
|
||||||
|
|
||||||
|
nodes.machine.environment.systemPackages = [ self'.packages.affinity-v3 ];
|
||||||
|
|
||||||
|
testScript = stripTyping (builtins.readFile ./v3.py);
|
||||||
|
};
|
||||||
|
|
||||||
|
test-affinity-v2 = pkgs.testers.runNixOSTest {
|
||||||
|
name = "test-affinity-v2";
|
||||||
|
enableOCR = true;
|
||||||
|
inherit defaults;
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
photo.environment.systemPackages = [ self'.packages.affinity-photo ];
|
||||||
|
publisher.environment.systemPackages = [ self'.packages.affinity-publisher ];
|
||||||
|
designer.environment.systemPackages = [ self'.packages.affinity-designer ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
${stripTyping (builtins.readFile ./v2.py)}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
[project]
|
||||||
|
name = "affinity-nix-vm-tests"
|
||||||
|
version = "0.0.0"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = [
|
||||||
|
"colorama>=0.4.6",
|
||||||
|
"ipython>=9.8.0",
|
||||||
|
"junit-xml>=1.9",
|
||||||
|
"nixos-test-driver",
|
||||||
|
"ptpython>=3.0.32",
|
||||||
|
"remote-pdb>=2.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
nixos-test-driver = { git = "https://github.com/NixOS/nixpkgs", subdirectory = "nixos/lib/test-driver/src", branch = "nixos-25.11" }
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = ["ty>=0.0.4"]
|
||||||
Generated
+302
@@ -0,0 +1,302 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "affinity-nix-vm-tests"
|
||||||
|
version = "0.0.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama" },
|
||||||
|
{ name = "ipython" },
|
||||||
|
{ name = "junit-xml" },
|
||||||
|
{ name = "nixos-test-driver" },
|
||||||
|
{ name = "ptpython" },
|
||||||
|
{ name = "remote-pdb" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dev-dependencies]
|
||||||
|
dev = [
|
||||||
|
{ name = "ty" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "colorama", specifier = ">=0.4.6" },
|
||||||
|
{ name = "ipython", specifier = ">=9.8.0" },
|
||||||
|
{ name = "junit-xml", specifier = ">=1.9" },
|
||||||
|
{ name = "nixos-test-driver", git = "https://github.com/NixOS/nixpkgs?subdirectory=nixos%2Flib%2Ftest-driver%2Fsrc&branch=nixos-25.11" },
|
||||||
|
{ name = "ptpython", specifier = ">=3.0.32" },
|
||||||
|
{ name = "remote-pdb", specifier = ">=2.1.0" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata.requires-dev]
|
||||||
|
dev = [{ name = "ty", specifier = ">=0.0.4" }]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "appdirs"
|
||||||
|
version = "1.4.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asttokens"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "decorator"
|
||||||
|
version = "5.2.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "executing"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipython"
|
||||||
|
version = "9.12.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "decorator" },
|
||||||
|
{ name = "ipython-pygments-lexers" },
|
||||||
|
{ name = "jedi" },
|
||||||
|
{ name = "matplotlib-inline" },
|
||||||
|
{ name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
||||||
|
{ name = "prompt-toolkit" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
{ name = "stack-data" },
|
||||||
|
{ name = "traitlets" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/59/22/906c8108974c673ebef6356c506cebb6870d48cedea3c41e949e2dd556bb/ipython-9.12.0-py3-none-any.whl", hash = "sha256:0f2701e8ee86e117e37f50563205d36feaa259d2e08d4a6bc6b6d74b18ce128d", size = 625661, upload-time = "2026-03-27T09:42:42.831Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipython-pygments-lexers"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jedi"
|
||||||
|
version = "0.19.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "parso" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "junit-xml"
|
||||||
|
version = "1.9"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "six" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/98/af/bc988c914dd1ea2bc7540ecc6a0265c2b6faccc6d9cdb82f20e2094a8229/junit-xml-1.9.tar.gz", hash = "sha256:de16a051990d4e25a3982b2dd9e89d671067548718866416faec14d9de56db9f", size = 7349, upload-time = "2023-01-24T18:42:00.836Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/93/2d896b5fd3d79b4cadd8882c06650e66d003f465c9d12c488d92853dff78/junit_xml-1.9-py2.py3-none-any.whl", hash = "sha256:ec5ca1a55aefdd76d28fcc0b135251d156c7106fa979686a4b48d62b761b4732", size = 7130, upload-time = "2020-02-22T20:41:37.661Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matplotlib-inline"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "traitlets" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nixos-test-driver"
|
||||||
|
version = "0.0.0"
|
||||||
|
source = { git = "https://github.com/NixOS/nixpkgs?subdirectory=nixos%2Flib%2Ftest-driver%2Fsrc&branch=nixos-25.11#54170c54449ea4d6725efd30d719c5e505f1c10e" }
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parso"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pexpect"
|
||||||
|
version = "4.9.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "ptyprocess" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prompt-toolkit"
|
||||||
|
version = "3.0.52"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "wcwidth" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ptpython"
|
||||||
|
version = "3.0.32"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "appdirs" },
|
||||||
|
{ name = "jedi" },
|
||||||
|
{ name = "prompt-toolkit" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b6/8c/7e904ceeb512b4530c7ca1d918d3565d694a1fa7df337cdfc36a16347d68/ptpython-3.0.32.tar.gz", hash = "sha256:11651778236de95c582b42737294e50a66ba4a21fa01c0090ea70815af478fe0", size = 74080, upload-time = "2025-11-20T21:20:48.27Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4c/ac/0e35e5d7afd47ab0e2c71293ed2ad18df91a2a4a008c0ff59c2f22def377/ptpython-3.0.32-py3-none-any.whl", hash = "sha256:16435d323e5fc0a685d5f4dc5bb4494fb68ac68736689cd1247e1eda9369b616", size = 68099, upload-time = "2025-11-20T21:20:46.634Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ptyprocess"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pure-eval"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pygments"
|
||||||
|
version = "2.20.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remote-pdb"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e4/b5/4944cac06fd9fc4a2e168313ec220aa25ed96ce83947b63eea5b4045b22d/remote-pdb-2.1.0.tar.gz", hash = "sha256:2d70c6f41e0eabf0165e8f1be58f82aa7a605aaeab8f2aefeb9ce246431091c1", size = 22295, upload-time = "2020-07-24T13:31:32.985Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/c5/d208c66344bb785d800adb61aef512290d3473052b9e7697890f0547aff2/remote_pdb-2.1.0-py2.py3-none-any.whl", hash = "sha256:94f73a92ac1248cf16189211011f97096bdada8a7baac8c79372663bbb57b5d0", size = 6304, upload-time = "2020-07-24T13:31:31.535Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stack-data"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "asttokens" },
|
||||||
|
{ name = "executing" },
|
||||||
|
{ name = "pure-eval" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "traitlets"
|
||||||
|
version = "5.14.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ty"
|
||||||
|
version = "0.0.29"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/47/d5/853561de49fae38c519e905b2d8da9c531219608f1fccc47a0fc2c896980/ty-0.0.29.tar.gz", hash = "sha256:e7936cca2f691eeda631876c92809688dbbab68687c3473f526cd83b6a9228d8", size = 5469221, upload-time = "2026-04-05T15:01:21.328Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/03/b7/911f9962115acfa24e3b2ec9d4992dd994c38e8769e1b1d7680bb4d28a51/ty-0.0.29-py3-none-linux_armv6l.whl", hash = "sha256:b8a40955f7660d3eaceb0d964affc81b790c0765e7052921a5f861ff8a471c30", size = 10568206, upload-time = "2026-04-05T15:01:19.165Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fe/c3/fcae2167d4c77a97269f92f11d1b43b03617f81de1283d5d05b43432110c/ty-0.0.29-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6b6849adae15b00bbe2d3c5b078967dcb62eba37d38936b8eeb4c81a82d2e3b8", size = 10442530, upload-time = "2026-04-05T15:01:28.471Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/97/33/5a6bfa240cfcb9c36046ae2459fa9ea23238d20130d8656ff5ac4d6c012a/ty-0.0.29-py3-none-macosx_11_0_arm64.whl", hash = "sha256:dcdd9b17209788152f7b7ea815eda07989152325052fe690013537cc7904ce49", size = 9915735, upload-time = "2026-04-05T15:01:10.365Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/1e/318f45fae232118e81a6306c30f50de42c509c412128d5bd231eab699ffb/ty-0.0.29-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d8ed4789bae78ffaf94462c0d25589a734cab0366b86f2bbcb1bb90e1a7a169", size = 10419748, upload-time = "2026-04-05T15:01:32.375Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/a8/5687872e2ab5a0f7dd4fd8456eac31e9381ad4dc74961f6f29965ad4dd91/ty-0.0.29-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91ec374b8565e0ad0900011c24641ebbef2da51adbd4fb69ff3280c8a7eceb02", size = 10394738, upload-time = "2026-04-05T15:01:06.473Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/de/68/015d118097eeb95e6a44c4abce4c0a28b7b9dfb3085b7f0ee48e4f099633/ty-0.0.29-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:298a8d5faa2502d3810bbbb47a030b9455495b9921594206043c785dd61548cf", size = 10910613, upload-time = "2026-04-05T15:01:17.17Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1c/01/47ce3c6c53e0670eadbe80756b167bf80ed6681d1ba57cfde2e8065a13d1/ty-0.0.29-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c8fba1a3524c6109d1e020d92301c79d41bf442fa8d335b9fa366239339cb70", size = 11475750, upload-time = "2026-04-05T15:01:30.461Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/cf/e361845b1081c9264ad5b7c963231bab03f2666865a9f2a115c4233f2137/ty-0.0.29-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c48adf88a70d264128c39ee922ed14a947817fced1e93c08c1a89c9244edcde", size = 11190055, upload-time = "2026-04-05T15:01:12.369Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/12/0fb0857e9a62cb11586e9a712103877bbf717f5fb570d16634408cfdefee/ty-0.0.29-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ce0a7a0e96bc7b42518cd3a1a6a6298ef64ff40ca4614355c1aa807059b5c6f", size = 11020539, upload-time = "2026-04-05T15:01:37.022Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/36/5a26753802083f80cd125db6c4348ad42b3c982ec36e718e0bf4c18f75e5/ty-0.0.29-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6ac86a05b4a3731d45365ab97780acc7b8146fa62fccb3cbe94fe6546c67a97", size = 10396399, upload-time = "2026-04-05T15:01:26.167Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/e6/b4e75b5752239ab3ab400f19faef4dbef81d05aab5d3419fda0c062a3765/ty-0.0.29-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6bbbf53141af0f3150bf288d716263f1a3550054e4b3551ca866d38192ba9891", size = 10421461, upload-time = "2026-04-05T15:01:08.367Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/21/1084b5b609f9abed62070ec0b31c283a403832a6310c8bbc208bd45ee1e6/ty-0.0.29-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1c9e06b770c1d0ff5efc51e34312390db31d53fcf3088163f413030b42b74f84", size = 10599187, upload-time = "2026-04-05T15:01:23.52Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/a1/ce19a2ca717bbcc1ee11378aba52ef70b6ce5b87245162a729d9fdc2360f/ty-0.0.29-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0307fe37e3f000ef1a4ae230bbaf511508a78d24a5e51b40902a21b09d5e6037", size = 11121198, upload-time = "2026-04-05T15:01:15.22Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/6b/f1430b279af704321566ce7ec2725d3d8258c2f815ebd93e474c64cd4543/ty-0.0.29-py3-none-win32.whl", hash = "sha256:7a2a898217960a825f8bc0087e1fdbaf379606175e98f9807187221d53a4a8ed", size = 9995331, upload-time = "2026-04-05T15:01:01.32Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/ef/3ef01c17785ff9a69378465c7d0faccd48a07b163554db0995e5d65a5a23/ty-0.0.29-py3-none-win_amd64.whl", hash = "sha256:fc1294200226b91615acbf34e0a9ad81caf98c081e9c6a912a31b0a7b603bc3f", size = 11023644, upload-time = "2026-04-05T15:01:04.432Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/55/87280a994d6a2d2647c65e12abbc997ed49835794366153c04c4d9304d76/ty-0.0.29-py3-none-win_arm64.whl", hash = "sha256:f9794bbd1bb3ce13f78c191d0c89ae4c63f52c12b6daa0c6fe220b90d019d12c", size = 10428165, upload-time = "2026-04-05T15:01:34.665Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wcwidth"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" },
|
||||||
|
]
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from test_driver.machine import Machine
|
||||||
|
|
||||||
|
photo: Machine = None # ty: ignore[invalid-assignment]
|
||||||
|
publisher: Machine = None # ty: ignore[invalid-assignment]
|
||||||
|
designer: Machine = None # ty: ignore[invalid-assignment]
|
||||||
|
|
||||||
|
# typing-end
|
||||||
|
|
||||||
|
photo.wait_for_x()
|
||||||
|
publisher.wait_for_x()
|
||||||
|
designer.wait_for_x()
|
||||||
|
|
||||||
|
photo.succeed("affinity-photo >&2 &")
|
||||||
|
designer.succeed("affinity-designer >&2 &")
|
||||||
|
publisher.succeed("affinity-publisher >&2 &")
|
||||||
|
|
||||||
|
photo.wait_for_window(r"^Affinity Photo 2$")
|
||||||
|
photo.screenshot("screenshot-photo")
|
||||||
|
|
||||||
|
designer.wait_for_window(r"^Affinity Designer 2$")
|
||||||
|
designer.screenshot("screenshot-designer")
|
||||||
|
|
||||||
|
publisher.wait_for_window(r"^Affinity Publisher 2$")
|
||||||
|
publisher.screenshot("screenshot-publisher")
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from test_driver.machine import Machine
|
||||||
|
|
||||||
|
machine: Machine = None # ty: ignore[invalid-assignment]
|
||||||
|
|
||||||
|
# typing-end
|
||||||
|
|
||||||
|
machine.start()
|
||||||
|
machine.wait_for_x()
|
||||||
|
|
||||||
|
machine.succeed("affinity-v3 >&2 &")
|
||||||
|
machine.wait_for_window(r"^Affinity$")
|
||||||
|
machine.screenshot("screenshot")
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[files]
|
||||||
|
extend-exclude = ["packages/apl/no-login.patch"]
|
||||||
|
|
||||||
|
[type.nix.extend-words]
|
||||||
|
# this is a 7zz argument...
|
||||||
|
tpe = "tpe"
|
||||||
|
|
||||||
|
[type.md.extend-words]
|
||||||
|
Canva = "Canva"
|
||||||
Reference in New Issue
Block a user