making-with-code-cli 4.1.1__tar.gz → 5.0.0__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.
Files changed (42) hide show
  1. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0}/PKG-INFO +14 -14
  2. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0}/pyproject.toml +21 -16
  3. making_with_code_cli-5.0.0/src/making_with_code_cli/__init__.py +0 -0
  4. making_with_code_cli-5.0.0/src/making_with_code_cli/git_backend/base_backend.py +33 -0
  5. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/git_backend/mwc_backend.py +26 -1
  6. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/settings.py +8 -5
  7. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/setup/__init__.py +6 -3
  8. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/setup/tasks.py +64 -100
  9. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/check/check_module.py +2 -2
  10. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/update/__init__.py +1 -0
  11. making_with_code_cli-4.1.1/making_with_code_cli/git_backend/base_backend.py +0 -47
  12. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0}/README.md +0 -0
  13. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/cli.py +0 -0
  14. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/curriculum.py +0 -0
  15. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/decorators.py +0 -0
  16. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/errors.py +0 -0
  17. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/git_backend/__init__.py +0 -0
  18. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/git_wrapper.py +0 -0
  19. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/helpers.py +0 -0
  20. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/mwc_accounts_api.py +0 -0
  21. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/styles.py +0 -0
  22. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/submit.py +0 -0
  23. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/__init__.py +0 -0
  24. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/assess.py +0 -0
  25. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/check/__init__.py +0 -0
  26. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/gitea_api/api.py +0 -0
  27. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/gitea_api/exceptions.py +0 -0
  28. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/log.py +0 -0
  29. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/patch.py +0 -0
  30. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/section/__init__.py +0 -0
  31. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/section/create.py +0 -0
  32. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/section/edit.py +0 -0
  33. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/section/show.py +0 -0
  34. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/setup.py +0 -0
  35. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/status.py +0 -0
  36. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/student/__init__.py +0 -0
  37. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/student/create.py +0 -0
  38. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/student/update.py +0 -0
  39. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/student_repo_functions.py +0 -0
  40. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/student_repos.py +0 -0
  41. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/teach/update.py +0 -0
  42. {making_with_code_cli-4.1.1 → making_with_code_cli-5.0.0/src}/making_with_code_cli/version.py +0 -0
@@ -1,24 +1,25 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: making-with-code-cli
3
- Version: 4.1.1
3
+ Version: 5.0.0
4
4
  Summary: Courseware for Making With Code
5
- License: MIT
6
5
  Author: Chris Proctor
7
- Author-email: chris@chrisproctor.net
8
- Requires-Python: >=3.11,<4.0
9
- Classifier: License :: OSI Approved :: MIT License
6
+ Author-email: Chris Proctor <chris@chrisproctor.net>
7
+ License: MIT
10
8
  Classifier: Programming Language :: Python :: 3
11
9
  Classifier: Programming Language :: Python :: 3.11
12
10
  Classifier: Programming Language :: Python :: 3.12
13
11
  Classifier: Programming Language :: Python :: 3.13
14
- Requires-Dist: click (>=8.1.8,<9.0.0)
15
- Requires-Dist: dateparser (>=1.2.0,<2.0.0)
16
- Requires-Dist: gitpython (>=3.1.44,<4.0.0)
17
- Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
18
- Requires-Dist: requests (>=2.32.3,<3.0.0)
19
- Requires-Dist: tabulate (>=0.9.0,<0.10.0)
20
- Requires-Dist: toml (>=0.10.2,<0.11.0)
21
- Requires-Dist: tqdm (>=4.67.1,<5.0.0)
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Dist: pyyaml>=6.0.2,<7.0.0
14
+ Requires-Dist: click>=8.1.8,<9.0.0
15
+ Requires-Dist: requests>=2.32.3,<3.0.0
16
+ Requires-Dist: tabulate>=0.9.0,<0.10.0
17
+ Requires-Dist: gitpython>=3.1.44,<4.0.0
18
+ Requires-Dist: dateparser>=1.2.0,<2.0.0
19
+ Requires-Dist: tqdm>=4.67.1,<5.0.0
20
+ Requires-Dist: tomlkit>=0.14.0
21
+ Requires-Python: >=3.11, <4.0
22
+ Project-URL: documentation, https://docs.makingwithcode.org/making-with-code-cli
22
23
  Project-URL: issues, https://github.com/cproctor/making-with-code-courseware/issues
23
24
  Description-Content-Type: text/markdown
24
25
 
@@ -50,4 +51,3 @@ Once installed, you can set up your computer using the `mwc setup` command. Run
50
51
 
51
52
  If you're interested in computing education and want to learn more about Making With Code,
52
53
  please contact [Dr. Chris Proctor](https://chrisproctor.net).
53
-
@@ -1,38 +1,43 @@
1
1
  [project]
2
2
  name = "making-with-code-cli"
3
- version = "4.1.1"
3
+ version = "5.0.0"
4
4
  description = "Courseware for Making With Code"
5
- authors = [
6
- {name = "Chris Proctor",email = "chris@chrisproctor.net"}
7
- ]
8
- license = {text = "MIT"}
9
- readme = "README.md"
10
- homepage = "https://github.com/cproctor/making-with-code-courseware"
5
+ authors = [{ name = "Chris Proctor", email = "chris@chrisproctor.net" }]
11
6
  requires-python = ">=3.11,<4.0"
7
+ readme = "README.md"
8
+ license = { text = "MIT" }
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Programming Language :: Python :: 3.11",
12
+ "Programming Language :: Python :: 3.12",
13
+ "Programming Language :: Python :: 3.13",
14
+ "Programming Language :: Python :: 3.14",
15
+ ]
12
16
  dependencies = [
13
17
  "pyyaml (>=6.0.2,<7.0.0)",
14
18
  "click (>=8.1.8,<9.0.0)",
15
19
  "requests (>=2.32.3,<3.0.0)",
16
- "toml (>=0.10.2,<0.11.0)",
17
20
  "tabulate (>=0.9.0,<0.10.0)",
18
21
  "gitpython (>=3.1.44,<4.0.0)",
19
22
  "dateparser (>=1.2.0,<2.0.0)",
20
23
  "tqdm (>=4.67.1,<5.0.0)",
24
+ "tomlkit>=0.14.0",
21
25
  ]
26
+ homepage = "https://github.com/cproctor/making-with-code-courseware"
22
27
 
23
28
  [project.urls]
29
+ documentation = "https://docs.makingwithcode.org/making-with-code-cli"
24
30
  issues = "https://github.com/cproctor/making-with-code-courseware/issues"
25
31
 
26
32
  [project.scripts]
27
33
  mwc = "making_with_code_cli.cli:cli"
28
34
 
29
35
  [build-system]
30
- requires = ["poetry-core>=2.0.0,<3.0.0"]
31
- build-backend = "poetry.core.masonry.api"
36
+ requires = ["uv_build>=0.9.0,<0.10.0"]
37
+ build-backend = "uv_build"
32
38
 
33
- [tool.poetry.group.docs]
34
- optional = true
35
-
36
- [tool.poetry.group.docs.dependencies]
37
- sphinx = "^8.2.3"
38
- sphinx-rtd-theme = "^3.0.2"
39
+ [dependency-groups]
40
+ docs = [
41
+ "sphinx>=8.2.3,<9",
42
+ "sphinx-rtd-theme>=3.0.2,<4",
43
+ ]
@@ -0,0 +1,33 @@
1
+ from pathlib import Path
2
+ from subprocess import run, CalledProcessError
3
+ import click
4
+ from making_with_code_cli.helpers import cd
5
+ from making_with_code_cli.setup.tasks import WORK_DIR_PERMISSIONS
6
+ from making_with_code_cli.styles import (
7
+ address,
8
+ info,
9
+ confirm,
10
+ error,
11
+ )
12
+
13
+ class GitBackend:
14
+ """Base class interface to backend git server.
15
+ All Making With Code deployments are backed by a git server, but the nature of the
16
+ server and the strategies for completing tasks vary by backend.
17
+ """
18
+
19
+ def __init__(self, settings):
20
+ self.settings = settings
21
+
22
+ def init_module(self, module, modpath):
23
+ raise NotImplemented()
24
+
25
+ def update(self, module, modpath, install=True):
26
+ raise NotImplemented()
27
+
28
+ def work_dir(self):
29
+ return Path(self.settings['work_dir'])
30
+
31
+ def relative_path(self, path):
32
+ return path.relative_to(self.work_dir())
33
+
@@ -38,8 +38,33 @@ class MWCBackend(GitBackend):
38
38
  with cd(modpath.parent):
39
39
  self.clone_repo(repo_name)
40
40
  if (modpath / self.COMMIT_TEMPLATE).exists():
41
+ run(f"git config commit.template {self.COMMIT_TEMPLATE}", shell=True,
42
+ check=True, cwd=modpath)
43
+ run("uv venv", shell=True, check=True, cwd=modpath)
44
+ self.init_direnv(modpath)
45
+
46
+ def update(self, module, modpath, install=True):
47
+ if (modpath / ".git").is_dir():
41
48
  with cd(modpath):
42
- run(f"git config commit.template {self.COMMIT_TEMPLATE}", shell=True, check=True)
49
+ relpath = self.relative_path(modpath)
50
+ try:
51
+ click.echo(address(f"Checking {relpath} for updates.", preformatted=True))
52
+ gitresult = run("git pull", shell=True, check=True, capture_output=True,
53
+ text=True)
54
+ click.echo(info(gitresult.stdout))
55
+ if install and Path("pyproject.toml").exists():
56
+ result = run("uv sync", shell=True, check=True,
57
+ capture_output=True, text=True)
58
+ self.init_direnv(modpath)
59
+ click.echo(info(result.stdout, preformatted=True))
60
+ except CalledProcessError as e:
61
+ click.echo(error(f"There was a problem updating {relpath}. Ask a teacher."))
62
+ raise e
63
+
64
+ def init_direnv(self, modpath):
65
+ if not (modpath / ".envrc").exists():
66
+ (modpath / ".envrc").write_text("source .venv/bin/activate")
67
+ run("direnv allow", shell=True, check=True, cwd=modpath)
43
68
 
44
69
  def user_has_repo(self, repo_name, username=None):
45
70
  """Checks to see whether a user already has the named repo.
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- import yaml
2
+ import tomllib
3
3
  import os
4
4
 
5
5
 
@@ -14,7 +14,8 @@ def get_settings_path(settings_path=None):
14
14
  elif "MWC_CONFIG" in os.environ:
15
15
  return Path(os.environ["MWC_CONFIG"])
16
16
  else:
17
- return Path.home() / ".mwc"
17
+ xdg_config_home = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
18
+ return xdg_config_home / "settings.toml"
18
19
 
19
20
  def read_settings(settings_path=None):
20
21
  """Reads the settings file and returns a dict.
@@ -22,7 +23,8 @@ def read_settings(settings_path=None):
22
23
  """
23
24
  sp = get_settings_path(settings_path)
24
25
  if sp.exists():
25
- return yaml.safe_load(sp.read_text())
26
+ with open(sp, "rb") as f:
27
+ return tomllib.load(f)
26
28
  else:
27
29
  return {}
28
30
 
@@ -38,6 +40,7 @@ def iter_settings(settings, prefix=None):
38
40
  else:
39
41
  yield '.'.join(keypath), value
40
42
 
43
+ # TODO
41
44
  def check_settings(settings):
42
45
  """Checks that all settings match SETTINGS_FORMAT"""
43
46
  errors = []
@@ -45,5 +48,5 @@ def check_settings(settings):
45
48
  def write_settings(settings, settings_path=None):
46
49
  """Writes the settings to the settings file."""
47
50
  sp = get_settings_path(settings_path)
48
- sp.write_text(yaml.dump(settings))
49
-
51
+ with open(sp, "w") as f:
52
+ tomlkit.dump(settings, f)
@@ -29,8 +29,10 @@ from making_with_code_cli.setup.tasks import (
29
29
  choose_work_dir,
30
30
  choose_course,
31
31
  choose_editor,
32
+ WriteMWCShellConfig,
33
+ SourceMWCShellConfig,
32
34
  InstallXCode,
33
- InstallPoetry,
35
+ InstallDirenv,
34
36
  InstallGit,
35
37
  InstallTree,
36
38
  InstallVSCode,
@@ -99,14 +101,15 @@ def setup(ctx, config, debug, git_name, git_email, mwc_accounts_url):
99
101
  write_settings(settings, config)
100
102
 
101
103
  task_classes = [
104
+ WriteMWCShellConfig,
105
+ SourceMWCShellConfig,
102
106
  InstallXCode,
103
- InstallPoetry,
107
+ InstallDirenv,
104
108
  InstallGit,
105
109
  InstallTree,
106
110
  InstallVSCode,
107
111
  InstallImageMagick,
108
112
  InstallHttpie,
109
- #InstallScipy,
110
113
  GitConfiguration,
111
114
  ]
112
115
  errors = []
@@ -129,24 +129,6 @@ def choose_work_dir(default=None, teacher=False):
129
129
  work_dir.mkdir(mode=WORK_DIR_PERMISSIONS, parents=True)
130
130
  return work_dir
131
131
 
132
- def choose_mwc_site_url(default=None):
133
- """Asks the user for the Making With Code site URL
134
- """
135
- while True:
136
- url = click.prompt(
137
- question("What's the URL of your Making With Code website?"),
138
- default=default,
139
- )
140
- if url.endswith('/'):
141
- url = url[:-1]
142
- try:
143
- curriculum = get_curriculum({'mwc_site_url': url})
144
- return url
145
- except CurriculumSiteNotAvailable as err:
146
- click.echo(error(str(err)))
147
- except requests.exceptions.MissingSchema as e:
148
- click.echo(error(str(e)))
149
-
150
132
  def choose_course(options, default=None):
151
133
  """Asks the user which course they are part of"""
152
134
  if len(options) == 0:
@@ -192,19 +174,28 @@ def get_shell_name():
192
174
  return shellpath.stdout.split('/')[-1].strip()
193
175
 
194
176
  def get_mwc_rc_path():
195
- return (Path.home() / ".mwc_rc").resolve()
177
+ xdg_config_home = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
178
+ return xdg_config_home / "mwc" / "shell_config.sh"
196
179
 
197
180
  def platform_rc_file():
198
181
  shell = get_shell_name()
182
+ if shell == "zsh":
183
+ dothome = Path(os.environ.get("ZDOTDIR", Path.home()))
184
+ else:
185
+ dothome = Path.home()
199
186
  candidates = [
200
- ("bash", Path.home() / ".bash_profile"),
201
- ("bash", Path.home() / ".bash_rc"),
202
- ("bash", Path.home() / ".bashrc"),
203
- ("zsh", Path.home() / ".zprofile"),
204
- ("zsh", Path.home() / ".zshrc"),
187
+ ("bash", dothome / ".bashrc"),
188
+ ("bash", dothome / ".bash_profile"),
189
+ ("bash", dothome / ".bash_login"),
190
+ ("bash", dothome / ".profile"),
191
+ ("zsh", dothome / ".zshenv"),
192
+ ("zsh", dothome / ".zshrc"),
193
+ ("zsh", dothome / ".zprofile"),
194
+ ("zsh", dothome / ".zlogin"),
205
195
  ]
206
196
  for sh, rc in candidates:
207
197
  if shell == sh and rc.exists():
198
+ click.echo(debug(f"Shell identified as {sh}. Using {rc} as rc file."))
208
199
  return rc
209
200
  raise IOError("Can't find an rc file.")
210
201
 
@@ -244,11 +235,43 @@ class SetupTask:
244
235
  def executable_on_path(self, name):
245
236
  return bool(run(f"which {name}", shell=True, capture_output=True).stdout)
246
237
 
247
- # DEPRECATED
248
- class MWCShellConfig(SetupTask):
249
- """Writes a line in the rc shell config file sourcing ~/.mwc_rc.
238
+ class WriteMWCShellConfig(SetupTask):
239
+ description = "Write the MWC shell config file to ~/.config/mwc/shell_config.sh or XDG_CONFIG_HOME"
240
+
241
+ def is_complete(self):
242
+ p = get_mwc_rc_path()
243
+ return p.exists() and p.read_text() == self.generate_shell_config()
244
+
245
+ def run_task(self):
246
+ mwc_rc_path = get_mwc_rc_path()
247
+ config_text = self.generate_shell_config()
248
+ self.debug_log(f"Writing to {mwc_rc_path}:\n\n{config_text}\n\n")
249
+ mwc_rc_path.write_text(config_text)
250
+
251
+ def generate_shell_config(self):
252
+ "Generates the shell configuration file contents"
253
+ f = ""
254
+ shell = get_shell_name()
255
+ f += "# Making With Code RC File\n\n"
256
+ f += "## Hook direnv into shell\n"
257
+ f += f'eval "$(direnv hook {shell})"\n\n'
258
+
259
+ if Platform.detect() == Platform.MAC:
260
+ if self.settings['editor'] == "subl":
261
+ f += "## Add subl to $PATH\n"
262
+ subldir = "/Applications/Sublime Text.app/Contents/SharedSupport/bin"
263
+ f += f'export PATH="{subldir}:$PATH"\n\n'
264
+ shell = get_shell_name()
265
+ if shell == "zsh":
266
+ f += "## Initialize zsh autocomplete\n"
267
+ f += "fpath+=~/.zfunc\n"
268
+ f += "autoload -Uz compinit && compinit\n\n"
269
+ return f
270
+
271
+ class SourceMWCShellConfig(SetupTask):
272
+ """Writes a line in the shell rc config file sourcing ~/.config/mwc/shell_config.sh.
250
273
  """
251
- description = "Link main shell config file to ~/.mwc_rc"
274
+ description = "Source MWC config file in the main shell config file"
252
275
 
253
276
  def is_complete(self):
254
277
  return f"source {get_mwc_rc_path()}" in platform_rc_file().read_text()
@@ -262,6 +285,7 @@ class MWCShellConfig(SetupTask):
262
285
  fh.write(f"\n# MAKING WITH CODE\n")
263
286
  fh.write(f'export PATH="$PATH:{mwcpath}"\n')
264
287
  fh.write(f"source {get_mwc_rc_path()}\n")
288
+ click.echo(info(f"Updated your shell login file ({rc_file}). You may need to close and reopen Terminal."))
265
289
 
266
290
  class InstallXCode(SetupTask):
267
291
  description = "Install XCode"
@@ -278,78 +302,6 @@ class InstallXCode(SetupTask):
278
302
  click.echo(address(msg))
279
303
  run("xcode-select --install", shell=True, check=True)
280
304
 
281
- class InstallPoetry(SetupTask):
282
- description = "Install poetry"
283
-
284
- def is_complete(self):
285
- if self.executable_on_path("poetry"):
286
- self.check_for_poetry2()
287
- return True
288
-
289
- def run_task(self):
290
- click.echo(address("Installing poetry..."))
291
- run("pipx install poetry", shell=True, check=True)
292
- run("poetry self add poetry-plugin-shell", shell=True, check=True)
293
- self.check_for_poetry2()
294
- click.echo(address("Installing poetry tab completions..."))
295
- shell = get_shell_name()
296
- try:
297
- if shell == "bash":
298
- run("poetry completions bash > ~/.bash_completion", shell=True, check=True)
299
- if shell == "zsh":
300
- tab_completion_dir = Path("~/.zfunc")
301
- tab_completion_dir.mkdir(exist_ok=True)
302
- run("poetry completions zsh > ~/.zfunc/_poetry", shell=True, check=True)
303
- except PermissionError:
304
- click.echo(warn(f"Couldn't install tab completions for poetry. This is not a big deal."))
305
-
306
- def check_for_poetry2(self, upgrade=True):
307
- result = run("poetry --version", shell=True, check=True, capture_output=True, text=True)
308
- match = re.search(r"(\d+)\.\d+\.\d+", result.stdout)
309
- if match:
310
- poetry_major_version = int(match.group(1))
311
- if not poetry_major_version == 2:
312
- if upgrade:
313
- click.echo(info("Upgrading Poetry"))
314
- run("pipx upgrade poetry", shell=True, check=True)
315
- run("poetry self add poetry-plugin-shell", shell=True, check=True)
316
- self.check_for_poetry2(upgrade=False)
317
- else:
318
- raise MWCInstallationError("Poetry 2 is required. Please upgrade.")
319
- else:
320
- raise MWCInstallationError("Could not determine Poetry version; Poetry 2 is required.")
321
-
322
- class WriteShellConfig(SetupTask):
323
- description = "Write the MWC shell configuration file ~/.mwc_rc"
324
-
325
- def is_complete(self):
326
- p = get_mwc_rc_path()
327
- return p.exists() and p.read_text() == self.generate_shell_config()
328
-
329
- def run_task(self):
330
- click.echo(address("Writing the MWC shell configuration file..."))
331
- mwc_rc_path = get_mwc_rc_path()
332
- config_text = self.generate_shell_config()
333
- self.debug_log(f"Writing to {mwc_rc_path}:\n\n{config_text}\n\n")
334
- mwc_rc_path.write_text(config_text)
335
- run(f"source {get_mwc_rc_path()}", shell=True, check=True)
336
-
337
- def generate_shell_config(self):
338
- "Generates the shell configuration file contents"
339
- f = ""
340
- shell = get_shell_name()
341
- f += "# Making With Code RC File\n\n"
342
- if Platform.detect() == Platform.MAC:
343
- if self.settings['editor'] == "subl":
344
- f += "## Add subl to $PATH\n"
345
- subldir = "/Applications/Sublime Text.app/Contents/SharedSupport/bin"
346
- f += f'export PATH="{subldir}:$PATH"\n\n'
347
- shell = get_shell_name()
348
- if shell == "zsh":
349
- f += "fpath+=~/.zfunc\n"
350
- f += "autoload -Uz compinit && compinit\n"
351
- return f
352
-
353
305
  class InstallPackage(SetupTask):
354
306
  """A subclass of SetupTask for packages to be installed by the Platform package manager.
355
307
  """
@@ -382,6 +334,18 @@ class InstallPackage(SetupTask):
382
334
  self.debug_log(f"Running: {package_manager}{package_name}")
383
335
  run(f"{package_manager}{package_name}", shell=True, check=True)
384
336
 
337
+ class InstallPackageFromInstaller(InstallPackage):
338
+ """Install from a remotely-fetched installer specified as install_command.
339
+ """
340
+ def run_task(self):
341
+ click.echo(address(f"Installing {self.executable_name}..."))
342
+ self.debug_log(f"Running: {self.install_command}")
343
+ run(self.install_command, shell=True, check=True)
344
+
345
+ class InstallDirenv(InstallPackageFromInstaller):
346
+ executable_name = "direnv"
347
+ install_command = "curl -sfL https://direnv.net/install.sh | bash"
348
+
385
349
  class InstallGit(InstallPackage):
386
350
  platform = Platform.MAC | Platform.UBUNTU
387
351
  executable_name = brew_name = apt_name = "git"
@@ -7,7 +7,7 @@
7
7
  from pathlib import Path
8
8
  from subprocess import run
9
9
  import requests
10
- import toml
10
+ import tomllib
11
11
  from git import Repo, InvalidGitRepositoryError, GitCommandError
12
12
 
13
13
  DEFAULT_BRANCH_NAME = "main"
@@ -82,7 +82,7 @@ class TestMWCModule:
82
82
  if not md_file.exists():
83
83
  self.errors.append(f"pyproject.toml missing")
84
84
  return
85
- md = toml.load(md_file)
85
+ md = tomllib.load(md_file)
86
86
  pyversion = md['project'].get('requires-python')
87
87
  if not pyversion == PYTHON_VERSION:
88
88
  self.errors.append(f"python version is {pyversion}, expected {PYTHON_VERSION}")
@@ -1,6 +1,7 @@
1
1
  import click
2
2
  import traceback
3
3
  from pathlib import Path
4
+ from subprocess import run
4
5
  from making_with_code_cli.settings import read_settings
5
6
  from making_with_code_cli.curriculum import get_curriculum
6
7
  from making_with_code_cli.git_backend import get_backend
@@ -1,47 +0,0 @@
1
- from pathlib import Path
2
- from subprocess import run, CalledProcessError
3
- import click
4
- from making_with_code_cli.helpers import cd
5
- from making_with_code_cli.setup.tasks import WORK_DIR_PERMISSIONS
6
- from making_with_code_cli.styles import (
7
- address,
8
- info,
9
- confirm,
10
- error,
11
- )
12
-
13
- class GitBackend:
14
- """Base class interface to backend git server.
15
- All Making With Code deployments are backed by a git server, but the nature of the
16
- server and the strategies for completing tasks vary by backend.
17
- """
18
-
19
- def __init__(self, settings):
20
- self.settings = settings
21
-
22
- def init_module(self, module, modpath):
23
- raise NotImplemented()
24
-
25
- def update(self, module, modpath, install=True):
26
- if (modpath / ".git").is_dir():
27
- with cd(modpath):
28
- relpath = self.relative_path(modpath)
29
- try:
30
- click.echo(address(f"Checking {relpath} for updates.", preformatted=True))
31
- gitresult = run("git pull", shell=True, check=True, capture_output=True,
32
- text=True)
33
- click.echo(info(gitresult.stdout))
34
- if install and Path("pyproject.toml").exists():
35
- result = run("poetry install", shell=True, check=True,
36
- capture_output=True, text=True)
37
- click.echo(info(result.stdout, preformatted=True))
38
- except CalledProcessError as e:
39
- click.echo(error(f"There was a problem updating {relpath}. Ask a teacher."))
40
- raise e
41
-
42
- def work_dir(self):
43
- return Path(self.settings['work_dir'])
44
-
45
- def relative_path(self, path):
46
- return path.relative_to(self.work_dir())
47
-