about summary refs log tree commit diff
path: root/pkgs/by-name/so/sonarr/update.py
blob: 195a2d5d05ac0842efc34f3e4fa35571acf950b8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import json
import os
import pathlib
import requests
import shutil
import subprocess
import sys
import tempfile


def replace_in_file(file_path, replacements):
    file_contents = pathlib.Path(file_path).read_text()
    for old, new in replacements.items():
        if old == new:
            continue
        updated_file_contents = file_contents.replace(old, new)
        # A dumb way to check that we’ve actually replaced the string.
        if file_contents == updated_file_contents:
            print(f"no string to replace: {old}{new}", file=sys.stderr)
            sys.exit(1)
        file_contents = updated_file_contents
    with tempfile.NamedTemporaryFile(mode="w") as t:
        t.write(file_contents)
        t.flush()
        shutil.copyfile(t.name, file_path)


def nix_hash_to_sri(hash):
    return subprocess.run(
        [
            "nix",
            "--extra-experimental-features", "nix-command",
            "hash",
            "to-sri",
            "--type", "sha256",
            "--",
            hash,
        ],
        stdout=subprocess.PIPE,
        text=True,
        check=True,
    ).stdout.rstrip()


nixpkgs_path = "."
attr_path = os.getenv("UPDATE_NIX_ATTR_PATH", "sonarr")

package_attrs = json.loads(subprocess.run(
    [
        "nix",
        "--extra-experimental-features", "nix-command",
        "eval",
        "--json",
        "--file", nixpkgs_path,
        "--apply", """p: {
          dir = builtins.dirOf p.meta.position;
          version = p.version;
          sourceHash = p.src.outputHash;
          yarnHash = p.yarnOfflineCache.outputHash;
        }""",
        "--",
        attr_path,
    ],
    stdout=subprocess.PIPE,
    text=True,
    check=True,
).stdout)

old_version = package_attrs["version"]
new_version = old_version

# Note that we use Sonarr API instead of GitHub to fetch latest stable release.
# This corresponds to the Updates tab in the web UI. See also
# https://github.com/Sonarr/Sonarr/blob/070919a7e6a96ca7e26524996417c6f8d1b5fcaa/src/NzbDrone.Core/Update/UpdatePackageProvider.cs
version_update = requests.get(
    f"https://services.sonarr.tv/v1/update/main?version={old_version}",
).json()
if version_update["available"]:
    new_version = version_update["updatePackage"]["version"]

if new_version == old_version:
    sys.exit()

source_nix_hash, source_store_path = subprocess.run(
    [
        "nix-prefetch-url",
        "--name", "source",
        "--unpack",
        "--print-path",
        f"https://github.com/Sonarr/Sonarr/archive/v{new_version}.tar.gz",
    ],
    stdout=subprocess.PIPE,
    text=True,
    check=True,
).stdout.rstrip().split("\n")

old_source_hash = package_attrs["sourceHash"]
new_source_hash = nix_hash_to_sri(source_nix_hash)

old_yarn_hash = package_attrs["yarnHash"]
new_yarn_hash = nix_hash_to_sri(subprocess.run(
    [
        "prefetch-yarn-deps",
        # does not support "--" separator :(
        # Also --verbose writes to stdout, yikes.
        os.path.join(source_store_path, "yarn.lock"),
    ],
    stdout=subprocess.PIPE,
    text=True,
    check=True,
).stdout.rstrip())

package_dir = package_attrs["dir"]
package_file_name = "package.nix"
deps_file_name = "deps.nix"

# To update deps.nix, we copy the package to a temporary directory and run
# passthru.fetch-deps script there.
with tempfile.TemporaryDirectory() as work_dir:
    package_file = os.path.join(work_dir, package_file_name)
    deps_file = os.path.join(work_dir, deps_file_name)

    shutil.copytree(package_dir, work_dir, dirs_exist_ok=True)

    replace_in_file(package_file, {
        # NB unlike hashes, versions are likely to be used in code or comments.
        # Try to be more specific to avoid false positive matches.
        f"version = \"{old_version}\"": f"version = \"{new_version}\"",
        old_source_hash: new_source_hash,
        old_yarn_hash: new_yarn_hash,
    })

    # Generate nuget-to-nix dependency lock file.
    fetch_deps = os.path.join(work_dir, "fetch-deps")
    subprocess.run(
        [
            "nix",
            "--extra-experimental-features", "nix-command",
            "build",
            "--impure",
            "--nix-path", "",
            "--include", f"nixpkgs={nixpkgs_path}",
            "--include", f"package={package_file}",
            "--expr", "(import <nixpkgs> { }).callPackage <package> { }",
            "--out-link", fetch_deps,
            "passthru.fetch-deps",
        ],
        check=True,
    )
    subprocess.run(
        [
            fetch_deps,
            deps_file,
        ],
        stdout=subprocess.DEVNULL,
        check=True,
    )

    shutil.copy(deps_file, os.path.join(package_dir, deps_file_name))
    shutil.copy(package_file, os.path.join(package_dir, package_file_name))