course-setup 3.0.5__tar.gz → 3.0.7__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.
- {course_setup-3.0.5 → course_setup-3.0.7}/PKG-INFO +12 -7
- {course_setup-3.0.5 → course_setup-3.0.7}/README.md +11 -6
- {course_setup-3.0.5 → course_setup-3.0.7}/pyproject.toml +1 -1
- course_setup-3.0.7/src/setup_course_github/.DS_Store +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/archive_course.py +16 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/retire_course.py +32 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/unretire_course.py +7 -1
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/.claude/settings.local.json +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/__init__.py +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/config.py +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/generic/.gitignore +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/generic/.ipynb_checkpoints/Course notebook-checkpoint.ipynb +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/generic/Course notebook.ipynb +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/generic/README.md +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/init_config.py +0 -0
- {course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/setup_course.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: course-setup
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.7
|
|
4
4
|
Summary: CLI tools for setting up and retiring GitHub-backed course repositories
|
|
5
5
|
Author: Reuven Lerner
|
|
6
6
|
Author-email: Reuven Lerner <reuven@lerner.co.il>
|
|
@@ -177,6 +177,10 @@ retire-course ./Acme-2026-03 ./Beta-2026-03 ./Gamma-2026-02
|
|
|
177
177
|
|
|
178
178
|
If any directory fails, the rest are still processed and errors are reported at the end.
|
|
179
179
|
|
|
180
|
+
> **Tip:** run `retire-course` from the *parent* of the course directory, not
|
|
181
|
+
> from inside it. If you're already inside, the command detects it and tells
|
|
182
|
+
> you to `cd ..` rather than failing with a confusing error.
|
|
183
|
+
|
|
180
184
|
### `archive-course` — Create a zip archive of a course
|
|
181
185
|
|
|
182
186
|
```bash
|
|
@@ -188,12 +192,13 @@ archive-course ./Acme-python-intro-2026-03
|
|
|
188
192
|
| `--output`, `-o` | Custom output zip path (defaults to `{dirname}.zip`) |
|
|
189
193
|
| `--no-html` | Skip HTML export of Jupyter notebooks |
|
|
190
194
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
This archives **all files** in the course directory (`.py`, `.csv`, `.toml`,
|
|
196
|
+
notebooks, etc.), excluding `.git`, `.venv`, `__pycache__`, and
|
|
197
|
+
`.ipynb_checkpoints` directories. For Jupyter notebooks, each `.ipynb` is also
|
|
198
|
+
exported to HTML via `nbconvert` and the `.html` is included alongside the
|
|
199
|
+
original; use `--no-html` to skip the HTML export. After creating the archive,
|
|
200
|
+
a summary is printed listing the archive path, file count, size, notebooks,
|
|
201
|
+
and all other included files.
|
|
197
202
|
|
|
198
203
|
### `unretire-course` — Restore a retired course
|
|
199
204
|
|
|
@@ -157,6 +157,10 @@ retire-course ./Acme-2026-03 ./Beta-2026-03 ./Gamma-2026-02
|
|
|
157
157
|
|
|
158
158
|
If any directory fails, the rest are still processed and errors are reported at the end.
|
|
159
159
|
|
|
160
|
+
> **Tip:** run `retire-course` from the *parent* of the course directory, not
|
|
161
|
+
> from inside it. If you're already inside, the command detects it and tells
|
|
162
|
+
> you to `cd ..` rather than failing with a confusing error.
|
|
163
|
+
|
|
160
164
|
### `archive-course` — Create a zip archive of a course
|
|
161
165
|
|
|
162
166
|
```bash
|
|
@@ -168,12 +172,13 @@ archive-course ./Acme-python-intro-2026-03
|
|
|
168
172
|
| `--output`, `-o` | Custom output zip path (defaults to `{dirname}.zip`) |
|
|
169
173
|
| `--no-html` | Skip HTML export of Jupyter notebooks |
|
|
170
174
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
`
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
This archives **all files** in the course directory (`.py`, `.csv`, `.toml`,
|
|
176
|
+
notebooks, etc.), excluding `.git`, `.venv`, `__pycache__`, and
|
|
177
|
+
`.ipynb_checkpoints` directories. For Jupyter notebooks, each `.ipynb` is also
|
|
178
|
+
exported to HTML via `nbconvert` and the `.html` is included alongside the
|
|
179
|
+
original; use `--no-html` to skip the HTML export. After creating the archive,
|
|
180
|
+
a summary is printed listing the archive path, file count, size, notebooks,
|
|
181
|
+
and all other included files.
|
|
177
182
|
|
|
178
183
|
### `unretire-course` — Restore a retired course
|
|
179
184
|
|
|
Binary file
|
|
@@ -88,6 +88,17 @@ def archive_course(
|
|
|
88
88
|
else f"{zip_size / (1024 * 1024):.1f} MB"
|
|
89
89
|
)
|
|
90
90
|
|
|
91
|
+
# Collect non-notebook, non-HTML files for the summary
|
|
92
|
+
notebook_names = {nb.name for nb in notebooks}
|
|
93
|
+
html_names = {nb.with_suffix(".html").name for nb in notebooks}
|
|
94
|
+
with zipfile.ZipFile(zip_path, "r") as zf:
|
|
95
|
+
other_files = sorted(
|
|
96
|
+
Path(name).name
|
|
97
|
+
for name in zf.namelist()
|
|
98
|
+
if Path(name).name not in notebook_names
|
|
99
|
+
and Path(name).name not in html_names
|
|
100
|
+
)
|
|
101
|
+
|
|
91
102
|
# Print summary
|
|
92
103
|
print(f"Archive created: {zip_path}")
|
|
93
104
|
print(f"Files: {file_count}")
|
|
@@ -104,6 +115,11 @@ def archive_course(
|
|
|
104
115
|
if export_html and html_exported > 0:
|
|
105
116
|
print(f"HTML exports: {html_exported}")
|
|
106
117
|
|
|
118
|
+
if other_files:
|
|
119
|
+
print("Other files:")
|
|
120
|
+
for name in other_files:
|
|
121
|
+
print(f" {name}")
|
|
122
|
+
|
|
107
123
|
return zip_path
|
|
108
124
|
|
|
109
125
|
|
|
@@ -14,6 +14,36 @@ from setup_course_github import __author__, __email__, __version__, get_github
|
|
|
14
14
|
from setup_course_github.config import load_config
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
class InsideCourseDirectoryError(RuntimeError):
|
|
18
|
+
"""Raised when a command is run from inside the course directory it targets."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _check_not_inside_course(dirname: str) -> None:
|
|
22
|
+
"""Raise InsideCourseDirectoryError if the user is inside *dirname*.
|
|
23
|
+
|
|
24
|
+
Two cases are detected:
|
|
25
|
+
1. *dirname* resolves to the current working directory (e.g. ``.`` or
|
|
26
|
+
the absolute path of cwd).
|
|
27
|
+
2. *dirname* does not exist relative to cwd, but its basename matches
|
|
28
|
+
cwd's basename — the user has cd'd into the directory and then
|
|
29
|
+
passed its bare name on the command line.
|
|
30
|
+
"""
|
|
31
|
+
cwd = Path.cwd().resolve()
|
|
32
|
+
target = Path(dirname)
|
|
33
|
+
|
|
34
|
+
inside = False
|
|
35
|
+
if target.exists() and target.resolve() == cwd:
|
|
36
|
+
inside = True
|
|
37
|
+
elif not target.exists() and target.name == cwd.name:
|
|
38
|
+
inside = True
|
|
39
|
+
|
|
40
|
+
if inside:
|
|
41
|
+
raise InsideCourseDirectoryError(
|
|
42
|
+
f"You appear to be inside '{dirname}'. "
|
|
43
|
+
"Move up one directory (cd ..) and run the command again."
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
17
47
|
def get_remote_url(dirname: str) -> str:
|
|
18
48
|
"""Return the git remote.origin.url for the repo in dirname."""
|
|
19
49
|
result = subprocess.run(
|
|
@@ -126,6 +156,8 @@ def _build_retirement_summary(
|
|
|
126
156
|
|
|
127
157
|
def retire_course(dirname: str, keep_public: bool = False) -> None:
|
|
128
158
|
"""Move the local directory to the archive, optionally making the repo private."""
|
|
159
|
+
_check_not_inside_course(dirname)
|
|
160
|
+
|
|
129
161
|
config = load_config()
|
|
130
162
|
|
|
131
163
|
remote_url = get_remote_url(dirname)
|
|
@@ -6,11 +6,17 @@ import sys
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
from setup_course_github import __author__, __email__, __version__, get_github
|
|
9
|
-
from setup_course_github.retire_course import
|
|
9
|
+
from setup_course_github.retire_course import (
|
|
10
|
+
_check_not_inside_course,
|
|
11
|
+
get_remote_url,
|
|
12
|
+
parse_repo_name,
|
|
13
|
+
)
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
def unretire_course(dirname: str) -> None:
|
|
13
17
|
"""Make the repo public again and move the directory to cwd."""
|
|
18
|
+
_check_not_inside_course(dirname)
|
|
19
|
+
|
|
14
20
|
remote_url = get_remote_url(dirname)
|
|
15
21
|
repo_name = parse_repo_name(remote_url)
|
|
16
22
|
|
{course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/.claude/settings.local.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{course_setup-3.0.5 → course_setup-3.0.7}/src/setup_course_github/generic/Course notebook.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|