SkillLink 0.1.0__tar.gz → 0.1.2__tar.gz
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-0.1.0 → skilllink-0.1.2}/PKG-INFO +10 -2
- {skilllink-0.1.0 → skilllink-0.1.2}/README.md +9 -1
- skilllink-0.1.2/SkillLink/SkillLink.py +100 -0
- skilllink-0.1.2/SkillLink/__init__.py +1 -0
- {skilllink-0.1.0 → skilllink-0.1.2}/SkillLink.egg-info/PKG-INFO +10 -2
- {skilllink-0.1.0 → skilllink-0.1.2}/SkillLink.egg-info/SOURCES.txt +2 -0
- skilllink-0.1.2/SkillLink.egg-info/top_level.txt +1 -0
- {skilllink-0.1.0 → skilllink-0.1.2}/pyproject.toml +1 -1
- {skilllink-0.1.0 → skilllink-0.1.2}/setup.py +1 -1
- skilllink-0.1.0/SkillLink.egg-info/top_level.txt +0 -1
- {skilllink-0.1.0 → skilllink-0.1.2}/SkillLink.egg-info/dependency_links.txt +0 -0
- {skilllink-0.1.0 → skilllink-0.1.2}/SkillLink.egg-info/requires.txt +0 -0
- {skilllink-0.1.0 → skilllink-0.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: SkillLink
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2
|
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
|
|
@@ -149,6 +149,12 @@ syncer.startSync(
|
|
149
149
|
|
150
150
|
---
|
151
151
|
|
152
|
+
## Code Examples
|
153
|
+
|
154
|
+
You can find code examples on my [GitHub repository](https://github.com/TristanMcBrideSr/TechBook).
|
155
|
+
|
156
|
+
---
|
157
|
+
|
152
158
|
## License
|
153
159
|
|
154
160
|
This project is licensed under the [Apache License, Version 2.0](LICENSE).
|
@@ -156,7 +162,9 @@ Copyright 2025 Tristan McBride Sr.
|
|
156
162
|
|
157
163
|
---
|
158
164
|
|
159
|
-
|
165
|
+
## Acknowledgements
|
166
|
+
|
167
|
+
Project by:
|
160
168
|
- Tristan McBride Sr.
|
161
169
|
- Sybil
|
162
170
|
|
@@ -0,0 +1,100 @@
|
|
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
|
+
logger.info(f"Overridden {dstFile} with {srcFile}")
|
46
|
+
else:
|
47
|
+
logger.info(f"File {dstFile} already exists locally. Skipping (preserving local).")
|
48
|
+
continue
|
49
|
+
shutil.copy2(srcFile, dstFile)
|
50
|
+
logger.info(f"Copied {srcFile} to {dstFile}")
|
51
|
+
|
52
|
+
def startSync(self, **kwargs):
|
53
|
+
"""
|
54
|
+
Syncs skills from a GitHub repo zip to local directory.
|
55
|
+
|
56
|
+
Kwargs:
|
57
|
+
skillList: list of skills to sync (default: all)
|
58
|
+
override: bool, if True always overwrite local (default: False)
|
59
|
+
githubToken: str, GitHub token for private repos (default: None)
|
60
|
+
branch: str, Git branch (default: 'master')
|
61
|
+
"""
|
62
|
+
skillList = kwargs.get('skillList')
|
63
|
+
override = kwargs.get('override', False)
|
64
|
+
githubToken = kwargs.get('githubToken')
|
65
|
+
branch = kwargs.get('branch', 'master')
|
66
|
+
|
67
|
+
logger.info(f"Starting {self.repoFolder} connection...")
|
68
|
+
zipUrl = f"https://github.com/{self.githubRepo}/archive/refs/heads/{branch}.zip"
|
69
|
+
logger.info("Starting sync...")
|
70
|
+
logger.info(f"Downloading {zipUrl} ...")
|
71
|
+
headers = {"User-Agent": "Mozilla/5.0"}
|
72
|
+
if githubToken:
|
73
|
+
headers["Authorization"] = f"Bearer {githubToken}"
|
74
|
+
|
75
|
+
try:
|
76
|
+
r = requests.get(zipUrl, headers=headers)
|
77
|
+
r.raise_for_status()
|
78
|
+
tempDir = "tmpDownload"
|
79
|
+
z = zipfile.ZipFile(BytesIO(r.content))
|
80
|
+
z.extractall(tempDir)
|
81
|
+
|
82
|
+
extractedRoot = os.path.join(tempDir, os.listdir(tempDir)[0])
|
83
|
+
skillsSrc = os.path.join(extractedRoot, self.repoFolder)
|
84
|
+
|
85
|
+
if not os.path.exists(skillsSrc):
|
86
|
+
logger.error(f"Can't find {self.repoFolder} in the repo!")
|
87
|
+
shutil.rmtree(tempDir)
|
88
|
+
raise FileNotFoundError(f"Can't find {self.repoFolder} in the repo!")
|
89
|
+
|
90
|
+
os.makedirs(self.localDir, exist_ok=True)
|
91
|
+
|
92
|
+
self.syncNewFiles(skillsSrc, self.localDir, skillList, override)
|
93
|
+
|
94
|
+
shutil.rmtree(tempDir)
|
95
|
+
logger.info("Sync complete.")
|
96
|
+
return True
|
97
|
+
|
98
|
+
except Exception as e:
|
99
|
+
logger.error(f"Sync failed: {e}", exc_info=True)
|
100
|
+
return False
|
@@ -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.2
|
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 @@
|
|
1
|
+
SkillLink
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "SkillLink"
|
7
|
-
version = "0.1.
|
7
|
+
version = "0.1.2"
|
8
8
|
description = "A Modern way to sync skills"
|
9
9
|
authors = [
|
10
10
|
{ name="Tristan McBride Sr.", email = "142635792+TristanMcBrideSr@users.noreply.github.com" },
|
@@ -1 +0,0 @@
|
|
1
|
-
|
File without changes
|
File without changes
|
File without changes
|