File size: 2,721 Bytes
21dd449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/**
 * Heavily inspired by https://github.com/huggingface/huggingface_hub/blob/fcfd14361bd03f23f82efced1aa65a7cbfa4b922/src/huggingface_hub/file_download.py#L517
 */

import * as fs from "node:fs/promises";
import * as path from "node:path";
import * as os from "node:os";

function expandUser(path: string): string {
	if (path.startsWith("~")) {
		return path.replace("~", os.homedir());
	}
	return path;
}

/**
 * Create a symbolic link named dst pointing to src.
 *
 * By default, it will try to create a symlink using a relative path. Relative paths have 2 advantages:
 * - If the cache_folder is moved (example: back-up on a shared drive), relative paths within the cache folder will
 *  not break.
 * - Relative paths seems to be better handled on Windows. Issue was reported 3 times in less than a week when
 *   changing from relative to absolute paths. See https://github.com/huggingface/huggingface_hub/issues/1398,
 *   https://github.com/huggingface/diffusers/issues/2729 and https://github.com/huggingface/transformers/pull/22228.
 *   NOTE: The issue with absolute paths doesn't happen on admin mode.
 * When creating a symlink from the cache to a local folder, it is possible that a relative path cannot be created.
 * This happens when paths are not on the same volume. In that case, we use absolute paths.
 *
 * The result layout looks something like
 *     └── [ 128]  snapshots
 *         β”œβ”€β”€ [ 128]  2439f60ef33a0d46d85da5001d52aeda5b00ce9f
 *         β”‚   β”œβ”€β”€ [  52]  README.md -> ../../../blobs/d7edf6bd2a681fb0175f7735299831ee1b22b812
 *         β”‚   └── [  76]  pytorch_model.bin -> ../../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd
 *
 * If symlinks cannot be created on this platform (most likely to be Windows), the workaround is to avoid symlinks by
 * having the actual file in `dst`. If it is a new file (`new_blob=True`), we move it to `dst`. If it is not a new file
 * (`new_blob=False`), we don't know if the blob file is already referenced elsewhere. To avoid breaking existing
 * cache, the file is duplicated on the disk.
 */
export async function createSymlink(params: {
	/**
	 * The path to the symlink.
	 */
	finalPath: string;
	/**
	 * The path the symlink should point to.
	 */
	sourcePath: string;
}): Promise<void> {
	const abs_src = path.resolve(expandUser(params.sourcePath));
	const abs_dst = path.resolve(expandUser(params.finalPath));

	try {
		await fs.rm(abs_dst);
	} catch {
		// ignore
	}

	try {
		await fs.symlink(path.relative(path.dirname(abs_dst), abs_src), abs_dst);
	} catch {
		console.info(`Symlink not supported. Copying file from ${abs_src} to ${abs_dst}`);
		await fs.copyFile(abs_src, abs_dst);
	}
}