File size: 8,009 Bytes
6d6dbbc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9ebbe39
6d6dbbc
 
9ebbe39
 
6d6dbbc
 
 
 
 
 
 
 
 
 
9ebbe39
 
 
 
6d6dbbc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#!/usr/bin/env python3
"""Upload this codebase to Hugging Face Hub while excluding the .back folder.

Examples:
  /g/data/rr81/aev/bin/python upload.py
  /g/data/rr81/aev/bin/python upload.py --repo-id aryadomain/all_code_base
  /g/data/rr81/aev/bin/python upload.py --method large
  /g/data/rr81/aev/bin/python upload.py --repo-id my-user/all_code_base
"""

from __future__ import annotations

import argparse
import inspect
from pathlib import Path
from typing import List

from huggingface_hub import HfApi


def build_ignore_patterns(extra_ignore: List[str]) -> List[str]:
    # Always exclude local metadata and the requested backup folder.
    patterns = [
        ".git",
        ".git/**",
        ".back",
        ".back/**",
        "**/.back/**",
    ]
    patterns.extend(extra_ignore)
    return patterns


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Upload a folder to Hugging Face Hub, excluding .back."
    )
    parser.add_argument(
        "--source-dir",
        type=str,
        default=".",
        help="Local folder to upload (default: current directory).",
    )
    parser.add_argument(
        "--repo-name",
        type=str,
        default="all_code_base",
        help="Repo name used when --repo-id is not provided.",
    )
    parser.add_argument(
        "--repo-id",
        type=str,
        default="aryadomain/all_code_base",
        help="Full Hugging Face repo id like user_or_org/repo_name (default: aryadomain/all_code_base).",
    )
    parser.add_argument(
        "--namespace",
        type=str,
        default=None,
        help="Optional user/org namespace override when using --repo-name.",
    )
    parser.add_argument(
        "--repo-type",
        type=str,
        default="model",
        choices=["model", "dataset", "space"],
        help="Hub repo type.",
    )
    parser.add_argument(
        "--private",
        action="store_true",
        help="Create the repo as private.",
    )
    parser.add_argument(
        "--revision",
        type=str,
        default="main",
        help="Target branch/revision (default: main).",
    )
    parser.add_argument(
        "--commit-message",
        type=str,
        default="Upload codebase excluding .back",
        help="Commit message for upload.",
    )
    parser.add_argument(
        "--extra-ignore",
        nargs="*",
        default=[],
        help="Additional ignore patterns for upload_folder.",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Print resolved settings and exit without uploading.",
    )
    parser.add_argument(
        "--method",
        type=str,
        default="large",
        choices=["auto", "folder", "large"],
        help=(
            "Upload method: auto chooses large upload for big folders, "
            "folder forces upload_folder, large forces upload_large_folder compatibility path."
        ),
    )
    parser.add_argument(
        "--large-threshold-gb",
        type=float,
        default=10.0,
        help="In auto mode, switch to large upload when folder size exceeds this many GB.",
    )
    return parser.parse_args()


def resolve_repo_id(api: HfApi, args: argparse.Namespace) -> str:
    if args.repo_id:
        return args.repo_id

    if args.namespace:
        return f"{args.namespace}/{args.repo_name}"

    who = api.whoami()
    username = who.get("name")
    if not username:
        raise RuntimeError("Could not resolve username from Hugging Face login.")
    return f"{username}/{args.repo_name}"


def folder_size_bytes(folder: Path) -> int:
    total = 0
    for p in folder.rglob("*"):
        if p.is_file() and ".back" not in p.parts:
            try:
                total += p.stat().st_size
            except OSError:
                pass
    return total


def upload_folder_compat(
    api: HfApi,
    repo_id: str,
    repo_type: str,
    source_dir: Path,
    revision: str,
    commit_message: str,
    ignore_patterns: List[str],
):
    kwargs = {
        "repo_id": repo_id,
        "repo_type": repo_type,
        "folder_path": str(source_dir),
        "path_in_repo": ".",
        "revision": revision,
        "commit_message": commit_message,
        "ignore_patterns": ignore_patterns,
    }
    sig = inspect.signature(api.upload_folder)
    if "multi_commits" in sig.parameters:
        kwargs["multi_commits"] = True
    if "multi_commits_verbose" in sig.parameters:
        kwargs["multi_commits_verbose"] = True
    return api.upload_folder(**kwargs)


def upload_large_compat(
    api: HfApi,
    repo_id: str,
    repo_type: str,
    source_dir: Path,
    revision: str,
    ignore_patterns: List[str],
):
    if not hasattr(api, "upload_large_folder"):
        raise RuntimeError("Installed huggingface_hub does not provide upload_large_folder")

    sig = inspect.signature(api.upload_large_folder)
    kwargs = {
        "repo_id": repo_id,
        "repo_type": repo_type,
        "folder_path": str(source_dir),
    }
    if "revision" in sig.parameters:
        kwargs["revision"] = revision
    if "path_in_repo" in sig.parameters:
        kwargs["path_in_repo"] = "."
    if "ignore_patterns" in sig.parameters:
        kwargs["ignore_patterns"] = ignore_patterns

    return api.upload_large_folder(**kwargs)


def main() -> None:
    args = parse_args()
    source_dir = Path(args.source_dir).resolve()

    if not source_dir.exists() or not source_dir.is_dir():
        raise FileNotFoundError(f"Source directory not found or not a directory: {source_dir}")

    api = HfApi()
    repo_id = resolve_repo_id(api, args)
    ignore_patterns = build_ignore_patterns(args.extra_ignore)
    total_size_gb = None

    if args.method == "auto":
        total_size = folder_size_bytes(source_dir)
        total_size_gb = total_size / (1024 ** 3)
        use_large = total_size_gb >= args.large_threshold_gb
    else:
        use_large = args.method == "large"

    print("Source directory:", source_dir)
    print("Repo id:", repo_id)
    print("Repo type:", args.repo_type)
    print("Private:", args.private)
    print("Revision:", args.revision)
    print("Ignore patterns:", ignore_patterns)
    if total_size_gb is None:
        print("Folder size scan: skipped (set --method auto to enable size-based selection)")
    else:
        print(f"Folder size (excluding .back): {total_size_gb:.2f} GB")
    print("Upload method:", "large" if use_large else "folder")

    if args.dry_run:
        print("Dry run requested. Exiting before create/upload.")
        return

    api.create_repo(
        repo_id=repo_id,
        repo_type=args.repo_type,
        private=args.private,
        exist_ok=True,
    )

    if use_large:
        try:
            commit_info = upload_large_compat(
                api=api,
                repo_id=repo_id,
                repo_type=args.repo_type,
                source_dir=source_dir,
                revision=args.revision,
                ignore_patterns=ignore_patterns,
            )
        except Exception as e:
            print(f"Large upload path failed ({e}). Falling back to upload_folder with multi-commit mode.")
            commit_info = upload_folder_compat(
                api=api,
                repo_id=repo_id,
                repo_type=args.repo_type,
                source_dir=source_dir,
                revision=args.revision,
                commit_message=args.commit_message,
                ignore_patterns=ignore_patterns,
            )
    else:
        commit_info = upload_folder_compat(
            api=api,
            repo_id=repo_id,
            repo_type=args.repo_type,
            source_dir=source_dir,
            revision=args.revision,
            commit_message=args.commit_message,
            ignore_patterns=ignore_patterns,
        )

    print("Upload completed.")
    print("Commit:", commit_info)
    print("Repo URL: https://huggingface.co/" + repo_id)


if __name__ == "__main__":
    main()