SkillLink 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- SkillLink/SkillLink.py +103 -0
- SkillLink/__init__.py +1 -0
- {skilllink-0.1.0.dist-info → skilllink-0.1.1.dist-info}/METADATA +10 -2
- skilllink-0.1.1.dist-info/RECORD +6 -0
- skilllink-0.1.1.dist-info/top_level.txt +1 -0
- skilllink-0.1.0.dist-info/RECORD +0 -4
- skilllink-0.1.0.dist-info/top_level.txt +0 -1
- {skilllink-0.1.0.dist-info → skilllink-0.1.1.dist-info}/WHEEL +0 -0
SkillLink/SkillLink.py
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import requests
|
4
|
+
import zipfile
|
5
|
+
import shutil
|
6
|
+
from io import BytesIO
|
7
|
+
|
8
|
+
logger = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
class SkillLink:
|
11
|
+
def __init__(self, githubRepo: str = None, repoFolder: str = None, localDir: str = None):
|
12
|
+
self.githubRepo = githubRepo
|
13
|
+
self.repoFolder = repoFolder
|
14
|
+
self.localDir = localDir
|
15
|
+
if not self.githubRepo:
|
16
|
+
raise ValueError("GitHub repo must be specified e.g., 'TristanMcBrideSr/SkillForge'")
|
17
|
+
if not self.repoFolder:
|
18
|
+
raise ValueError("Repo folder must be specified e.g., 'SkillForge'")
|
19
|
+
if not self.localDir:
|
20
|
+
raise ValueError("Local dir must be specified e.g., '/your/path/skills'")
|
21
|
+
|
22
|
+
def syncNewFiles(self, srcDir, dstDir, skillList=None, override=False):
|
23
|
+
onlyFiles = None
|
24
|
+
if skillList:
|
25
|
+
normalized = set()
|
26
|
+
for name in skillList:
|
27
|
+
name = name.lower()
|
28
|
+
normalized.add(name)
|
29
|
+
if not name.endswith(".py"):
|
30
|
+
normalized.add(f"{name}.py")
|
31
|
+
onlyFiles = normalized
|
32
|
+
|
33
|
+
for root, dirs, files in os.walk(srcDir):
|
34
|
+
relRoot = os.path.relpath(root, srcDir)
|
35
|
+
targetRoot = os.path.join(dstDir, relRoot) if relRoot != '.' else dstDir
|
36
|
+
os.makedirs(targetRoot, exist_ok=True)
|
37
|
+
for file in files:
|
38
|
+
if onlyFiles and file.lower() not in onlyFiles:
|
39
|
+
continue
|
40
|
+
srcFile = os.path.join(root, file)
|
41
|
+
dstFile = os.path.join(targetRoot, file)
|
42
|
+
if os.path.exists(dstFile):
|
43
|
+
if override:
|
44
|
+
shutil.copy2(srcFile, dstFile)
|
45
|
+
print(f"Overridden {dstFile} with {srcFile}")
|
46
|
+
logger.info(f"Overridden {dstFile} with {srcFile}")
|
47
|
+
else:
|
48
|
+
print(f"File {dstFile} already exists locally. Skipping (preserving local).")
|
49
|
+
logger.info(f"File {dstFile} already exists locally. Skipping (preserving local).")
|
50
|
+
continue
|
51
|
+
shutil.copy2(srcFile, dstFile)
|
52
|
+
print(f"Copied {srcFile} to {dstFile}")
|
53
|
+
logger.info(f"Copied {srcFile} to {dstFile}")
|
54
|
+
|
55
|
+
def startSync(self, **kwargs):
|
56
|
+
"""
|
57
|
+
Syncs skills from a GitHub repo zip to local directory.
|
58
|
+
|
59
|
+
Kwargs:
|
60
|
+
skillList: list of skills to sync (default: all)
|
61
|
+
override: bool, if True always overwrite local (default: False)
|
62
|
+
githubToken: str, GitHub token for private repos (default: None)
|
63
|
+
branch: str, Git branch (default: 'master')
|
64
|
+
"""
|
65
|
+
skillList = kwargs.get('skillList')
|
66
|
+
override = kwargs.get('override', False)
|
67
|
+
githubToken = kwargs.get('githubToken')
|
68
|
+
branch = kwargs.get('branch', 'master')
|
69
|
+
|
70
|
+
logger.info(f"Starting {self.repoFolder} connection...")
|
71
|
+
zipUrl = f"https://github.com/{self.githubRepo}/archive/refs/heads/{branch}.zip"
|
72
|
+
logger.info("Starting sync...")
|
73
|
+
logger.info(f"Downloading {zipUrl} ...")
|
74
|
+
headers = {"User-Agent": "Mozilla/5.0"}
|
75
|
+
if githubToken:
|
76
|
+
headers["Authorization"] = f"Bearer {githubToken}"
|
77
|
+
|
78
|
+
try:
|
79
|
+
r = requests.get(zipUrl, headers=headers)
|
80
|
+
r.raise_for_status()
|
81
|
+
tempDir = "tmpDownload"
|
82
|
+
z = zipfile.ZipFile(BytesIO(r.content))
|
83
|
+
z.extractall(tempDir)
|
84
|
+
|
85
|
+
extractedRoot = os.path.join(tempDir, os.listdir(tempDir)[0])
|
86
|
+
skillsSrc = os.path.join(extractedRoot, self.repoFolder)
|
87
|
+
|
88
|
+
if not os.path.exists(skillsSrc):
|
89
|
+
logger.error(f"Can't find {self.repoFolder} in the repo!")
|
90
|
+
shutil.rmtree(tempDir)
|
91
|
+
raise FileNotFoundError(f"Can't find {self.repoFolder} in the repo!")
|
92
|
+
|
93
|
+
os.makedirs(self.localDir, exist_ok=True)
|
94
|
+
|
95
|
+
self.syncNewFiles(skillsSrc, self.localDir, skillList, override)
|
96
|
+
|
97
|
+
shutil.rmtree(tempDir)
|
98
|
+
logger.info("Sync complete.")
|
99
|
+
return True
|
100
|
+
|
101
|
+
except Exception as e:
|
102
|
+
logger.error(f"Sync failed: {e}", exc_info=True)
|
103
|
+
return False
|
SkillLink/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
from .SkillLink import SkillLink
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: SkillLink
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.1
|
4
4
|
Summary: A Modern way to sync skills
|
5
5
|
Author: Tristan McBride Sr.
|
6
6
|
Author-email: "Tristan McBride Sr." <142635792+TristanMcBrideSr@users.noreply.github.com>
|
@@ -167,6 +167,12 @@ syncer.startSync(
|
|
167
167
|
|
168
168
|
---
|
169
169
|
|
170
|
+
## Code Examples
|
171
|
+
|
172
|
+
You can find code examples on my [GitHub repository](https://github.com/TristanMcBrideSr/TechBook).
|
173
|
+
|
174
|
+
---
|
175
|
+
|
170
176
|
## License
|
171
177
|
|
172
178
|
This project is licensed under the [Apache License, Version 2.0](LICENSE).
|
@@ -174,7 +180,9 @@ Copyright 2025 Tristan McBride Sr.
|
|
174
180
|
|
175
181
|
---
|
176
182
|
|
177
|
-
|
183
|
+
## Acknowledgements
|
184
|
+
|
185
|
+
Project by:
|
178
186
|
- Tristan McBride Sr.
|
179
187
|
- Sybil
|
180
188
|
|
@@ -0,0 +1,6 @@
|
|
1
|
+
SkillLink/SkillLink.py,sha256=boenTeVIr4sAfeHcLiNcZCAw1FHEw7Orp1-UN-esAaU,4341
|
2
|
+
SkillLink/__init__.py,sha256=b6TVK2Ve_Cct2wu3FLn_2X7cXzCqhI6kTIM81OorPMM,32
|
3
|
+
skilllink-0.1.1.dist-info/METADATA,sha256=CrnAat_Xw7T-rplELWnBZO6E-QWz8wJfuUuqURMW7N4,4710
|
4
|
+
skilllink-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
5
|
+
skilllink-0.1.1.dist-info/top_level.txt,sha256=8Nq2y-9CjjC46x0nlmsBxabHDFhJcw5FnprPDQz3RKY,10
|
6
|
+
skilllink-0.1.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
SkillLink
|
skilllink-0.1.0.dist-info/RECORD
DELETED
@@ -1,4 +0,0 @@
|
|
1
|
-
skilllink-0.1.0.dist-info/METADATA,sha256=kfBFsjjH2DhlPLSRmN0FjRKGWTrpymX_tmiNR2NJ-EY,4558
|
2
|
-
skilllink-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
3
|
-
skilllink-0.1.0.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
4
|
-
skilllink-0.1.0.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
|
File without changes
|