aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/nix/build-support/update-mirror.nu
blob: 974e9d0f55d5f500ec874ac39d579d6ca53be361 (plain)
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
161
162
163
#!/usr/bin/env nu

# This script downloads external dependencies from build.zig.zon.json that
# are not already mirrored at deps.files.ghostty.org, saves them to a local
# directory, and updates the build.zig.zon files (the root one as well as
# every pkg/*/build.zig.zon) to point to the new mirror URLs.
#
# HTTP files are downloaded unmodified. Git dependencies use Zig's normalized
# cache archive so their content hashes match the originals.
#
# After running this script, the files in the output directory can be uploaded
# to blob storage, and the build.zig.zon files will already be updated with
# the new URLs.
def download-git-package [url: string, expected_hash: string, destination: string] {
  let cache_dir = (mktemp --directory)
  let result = (do { ^zig fetch --global-cache-dir $cache_dir $url } | complete)

  if $result.exit_code != 0 {
    rm --recursive $cache_dir
    error make {
      msg: $"zig fetch failed for ($url): ($result.stderr | str trim)"
    }
  }

  let actual_hash = ($result.stdout | str trim)
  if $actual_hash != $expected_hash {
    rm --recursive $cache_dir
    error make {
      msg: $"zig fetch hash mismatch for ($url): expected ($expected_hash), got ($actual_hash)"
    }
  }

  let archive = ($cache_dir | path join "p" $"($actual_hash).tar.gz")
  if not ($archive | path exists) {
    rm --recursive $cache_dir
    error make {
      msg: $"zig fetch did not produce a cache archive for ($url)"
    }
  }

  cp $archive $destination
  rm --recursive $cache_dir
}

def main [
  --output: string = "tmp-mirror", # Output directory for the mirrored files
  --prefix: string = "https://deps.files.ghostty.org/", # Final URL prefix to ignore
  --dry-run, # Print what would be downloaded without downloading
] {
  let script_dir = ($env.CURRENT_FILE | path dirname)
  let root_dir = ($script_dir | path join ".." ".." | path expand)
  let input_file = ($root_dir | path join "build.zig.zon.json")
  let output_dir = $output

  # All build.zig.zon files that may reference external URLs: the root
  # one plus every package under pkg/. build.zig.zon.json is generated
  # from the full dependency tree so it covers all of these.
  let zon_files = (
    [($root_dir | path join "build.zig.zon")]
    | append (glob ($root_dir | path join "pkg" "*" "build.zig.zon"))
    | sort
  )

  # Ensure the output directory exists
  mkdir $output_dir

  # Read and parse the JSON file
  let deps = open $input_file

  # Track URL replacements for build.zig.zon
  mut url_replacements = []

  # Process each dependency
  for entry in ($deps | transpose key value) {
    let key = $entry.key
    let name = $entry.value.name
    let url = $entry.value.url

    let is_git_url = ($url | str starts-with "git+http")
    let is_http_url = ($url | str starts-with "http")

    # Skip URLs that aren't HTTP downloads or HTTP-backed Git repositories.
    if not ($is_http_url or $is_git_url) {
      continue
    }

    # Skip URLs already hosted at the prefix
    if ($url | str starts-with $prefix) {
      continue
    }

    # Git dependencies are mirrored using an archive of the normalized package
    # tree produced by Zig. Fetching it over HTTP produces the same package hash.
    let extension = if $is_git_url {
      ".tar.gz"
    } else {
      $url | parse -r '(\.[a-z0-9]+(?:\.[a-z0-9]+)?)$' | get -o capture0.0 | default ""
    }

    # Try to extract commit hash (40 hex chars) from URL
    let commit_hash = ($url | parse -r '([a-f0-9]{40})' | get -o capture0.0 | default "")

    # Try to extract date pattern (YYYY-MM-DD or YYYYMMDD with optional suffixes)
    let date_pattern = ($url | parse -r '((?:release-)?20\d{2}(?:-?\d{2}){2}(?:[-]\d+)*(?:[-][a-z0-9]+)?)' | get -o capture0.0 | default "")

    # Build filename based on what we found
    let filename = if (not ($commit_hash | is-empty)) {
      $"($name)-($commit_hash)($extension)"
    } else if (not ($date_pattern | is-empty)) {
      $"($name)-($date_pattern)($extension)"
    } else {
      $"($key)($extension)"
    }
    let new_url = $"($prefix)($filename)"
    print $"($url) -> ($filename)"
    
    # Track the replacement
    $url_replacements = ($url_replacements | append {old: $url, new: $new_url})
    
    # Download the file
    if not $dry_run {
      let destination = ($output_dir | path join $filename)
      if $is_git_url {
        download-git-package $url $key $destination
      } else {
        http get $url | save -f $destination
      }
    }
  }

  if $dry_run {
    print "Dry run complete - no files were downloaded\n"
  } else {
    print "All dependencies downloaded successfully\n"
  }

  # Apply the URL replacements to every build.zig.zon that references them.
  for zon_file in $zon_files {
    mut zon_content = (open --raw $zon_file)
    mut count = 0
    for replacement in $url_replacements {
      if ($zon_content | str contains $replacement.old) {
        $zon_content = ($zon_content | str replace --all $replacement.old $replacement.new)
        $count = $count + 1
      }
    }

    if $count == 0 {
      continue
    }

    let relative_path = ($zon_file | path relative-to $root_dir)
    if $dry_run {
      print $"Would update ($count) URLs in ($relative_path)"
    } else {
      # Backup the old file
      let backup_file = $"($zon_file).bak"
      cp $zon_file $backup_file
      $zon_content | save -f $zon_file
      print $"Updated ($count) URLs in ($relative_path), backup at ($relative_path).bak"
    }
  }
}