11 def replace_in_file(file_path
, replacements
):
12 file_contents
= pathlib
.Path(file_path
).read_text()
13 for old
, new
in replacements
.items():
16 updated_file_contents
= file_contents
.replace(old
, new
)
17 # A dumb way to check that we’ve actually replaced the string.
18 if file_contents
== updated_file_contents
:
19 print(f
"no string to replace: {old} → {new}", file=sys
.stderr
)
21 file_contents
= updated_file_contents
22 with tempfile
.NamedTemporaryFile(mode
="w") as t
:
23 t
.write(file_contents
)
25 shutil
.copyfile(t
.name
, file_path
)
28 def nix_hash_to_sri(hash):
29 return subprocess
.run(
32 "--extra-experimental-features", "nix-command",
39 stdout
=subprocess
.PIPE
,
46 attr_path
= os
.getenv("UPDATE_NIX_ATTR_PATH", "sonarr")
48 package_attrs
= json
.loads(subprocess
.run(
51 "--extra-experimental-features", "nix-command",
54 "--file", nixpkgs_path
,
56 dir = builtins.dirOf p.meta.position;
58 sourceHash = p.src.outputHash;
59 yarnHash = p.yarnOfflineCache.outputHash;
64 stdout
=subprocess
.PIPE
,
69 old_version
= package_attrs
["version"]
70 new_version
= old_version
72 # Note that we use Sonarr API instead of GitHub to fetch latest stable release.
73 # This corresponds to the Updates tab in the web UI. See also
74 # https://github.com/Sonarr/Sonarr/blob/070919a7e6a96ca7e26524996417c6f8d1b5fcaa/src/NzbDrone.Core/Update/UpdatePackageProvider.cs
75 version_update
= requests
.get(
76 f
"https://services.sonarr.tv/v1/update/main?version={old_version}",
78 if version_update
["available"]:
79 new_version
= version_update
["updatePackage"]["version"]
81 if new_version
== old_version
:
84 source_nix_hash
, source_store_path
= subprocess
.run(
90 f
"https://github.com/Sonarr/Sonarr/archive/v{new_version}.tar.gz",
92 stdout
=subprocess
.PIPE
,
95 ).stdout
.rstrip().split("\n")
97 old_source_hash
= package_attrs
["sourceHash"]
98 new_source_hash
= nix_hash_to_sri(source_nix_hash
)
100 old_yarn_hash
= package_attrs
["yarnHash"]
101 new_yarn_hash
= nix_hash_to_sri(subprocess
.run(
103 "prefetch-yarn-deps",
104 # does not support "--" separator :(
105 # Also --verbose writes to stdout, yikes.
106 os
.path
.join(source_store_path
, "yarn.lock"),
108 stdout
=subprocess
.PIPE
,
113 package_dir
= package_attrs
["dir"]
114 package_file_name
= "package.nix"
115 deps_file_name
= "deps.nix"
117 # To update deps.nix, we copy the package to a temporary directory and run
118 # passthru.fetch-deps script there.
119 with tempfile
.TemporaryDirectory() as work_dir
:
120 package_file
= os
.path
.join(work_dir
, package_file_name
)
121 deps_file
= os
.path
.join(work_dir
, deps_file_name
)
123 shutil
.copytree(package_dir
, work_dir
, dirs_exist_ok
=True)
125 replace_in_file(package_file
, {
126 # NB unlike hashes, versions are likely to be used in code or comments.
127 # Try to be more specific to avoid false positive matches.
128 f
"version = \"{old_version}\"": f
"version = \"{new_version}\"",
129 old_source_hash
: new_source_hash
,
130 old_yarn_hash
: new_yarn_hash
,
133 # Generate nuget-to-nix dependency lock file.
134 fetch_deps
= os
.path
.join(work_dir
, "fetch-deps")
138 "--extra-experimental-features", "nix-command",
142 "--include", f
"nixpkgs={nixpkgs_path}",
143 "--include", f
"package={package_file}",
144 "--expr", "(import <nixpkgs> { }).callPackage <package> { }",
145 "--out-link", fetch_deps
,
146 "passthru.fetch-deps",
155 stdout
=subprocess
.DEVNULL
,
159 shutil
.copy(deps_file
, os
.path
.join(package_dir
, deps_file_name
))
160 shutil
.copy(package_file
, os
.path
.join(package_dir
, package_file_name
))