making-with-code-cli 4.1.2__tar.gz → 5.0.1__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.2 → making_with_code_cli-5.0.1}/PKG-INFO +14 -15
  2. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1}/pyproject.toml +20 -16
  3. making_with_code_cli-5.0.1/src/making_with_code_cli/__init__.py +0 -0
  4. making_with_code_cli-5.0.1/src/making_with_code_cli/git_backend/base_backend.py +33 -0
  5. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/git_backend/mwc_backend.py +26 -1
  6. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/settings.py +8 -5
  7. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/setup/__init__.py +8 -5
  8. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/setup/tasks.py +67 -102
  9. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/check/check_module.py +2 -2
  10. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/update/__init__.py +1 -0
  11. making_with_code_cli-4.1.2/making_with_code_cli/git_backend/base_backend.py +0 -47
  12. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1}/README.md +0 -0
  13. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/cli.py +0 -0
  14. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/curriculum.py +0 -0
  15. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/decorators.py +0 -0
  16. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/errors.py +0 -0
  17. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/git_backend/__init__.py +0 -0
  18. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/git_wrapper.py +0 -0
  19. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/helpers.py +0 -0
  20. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/mwc_accounts_api.py +0 -0
  21. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/styles.py +0 -0
  22. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/submit.py +0 -0
  23. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/__init__.py +0 -0
  24. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/assess.py +0 -0
  25. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/check/__init__.py +0 -0
  26. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/gitea_api/api.py +0 -0
  27. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/gitea_api/exceptions.py +0 -0
  28. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/log.py +0 -0
  29. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/patch.py +0 -0
  30. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/section/__init__.py +0 -0
  31. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/section/create.py +0 -0
  32. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/section/edit.py +0 -0
  33. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/section/show.py +0 -0
  34. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/setup.py +0 -0
  35. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/status.py +0 -0
  36. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/student/__init__.py +0 -0
  37. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/student/create.py +0 -0
  38. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/student/update.py +0 -0
  39. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/student_repo_functions.py +0 -0
  40. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/student_repos.py +0 -0
  41. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/teach/update.py +0 -0
  42. {making_with_code_cli-4.1.2 → making_with_code_cli-5.0.1/src}/making_with_code_cli/version.py +0 -0
@@ -1,25 +1,25 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: making-with-code-cli
3
- Version: 4.1.2
3
+ Version: 5.0.1
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)
22
- Project-URL: Documentation, https://docs.makingwithcode.org/making-with-code-cli
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
23
23
  Project-URL: issues, https://github.com/cproctor/making-with-code-courseware/issues
24
24
  Description-Content-Type: text/markdown
25
25
 
@@ -51,4 +51,3 @@ Once installed, you can set up your computer using the `mwc setup` command. Run
51
51
 
52
52
  If you're interested in computing education and want to learn more about Making With Code,
53
53
  please contact [Dr. Chris Proctor](https://chrisproctor.net).
54
-
@@ -1,24 +1,29 @@
1
1
  [project]
2
2
  name = "making-with-code-cli"
3
- version = "4.1.2"
3
+ version = "5.0.1"
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]
24
29
  documentation = "https://docs.makingwithcode.org/making-with-code-cli"
@@ -28,12 +33,11 @@ issues = "https://github.com/cproctor/making-with-code-courseware/issues"
28
33
  mwc = "making_with_code_cli.cli:cli"
29
34
 
30
35
  [build-system]
31
- requires = ["poetry-core>=2.0.0,<3.0.0"]
32
- build-backend = "poetry.core.masonry.api"
36
+ requires = ["uv_build>=0.9.0,<0.10.0"]
37
+ build-backend = "uv_build"
33
38
 
34
- [tool.poetry.group.docs]
35
- optional = true
36
-
37
- [tool.poetry.group.docs.dependencies]
38
- sphinx = "^8.2.3"
39
- 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 tomlkit
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 tomlkit.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)
@@ -1,7 +1,7 @@
1
1
  import click
2
2
  from subprocess import run
3
+ from pprint import pformat
3
4
  import traceback
4
- import yaml
5
5
  from making_with_code_cli.mwc_accounts_api import MWCAccountsAPI
6
6
  from making_with_code_cli.git_backend import get_backend
7
7
  from making_with_code_cli.update import update
@@ -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,
@@ -95,18 +97,19 @@ def setup(ctx, config, debug, git_name, git_email, mwc_accounts_url):
95
97
  click.echo(debug_fmt("MWC Accounts Server status:"))
96
98
  click.echo(debug_fmt(str(status)))
97
99
  click.echo(info("MWC settings:"))
98
- click.echo(info(yaml.dump(settings), preformatted=True))
100
+ click.echo(info(settings, preformatted=True))
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
 
@@ -232,10 +223,10 @@ class SetupTask:
232
223
  pass
233
224
 
234
225
  def report_not_needed(self):
235
- click.echo(info(f"{self.description} is already complete."))
226
+ click.echo(info(f"{self.description} is already complete.", preformatted=True))
236
227
 
237
228
  def report_complete(self):
238
- click.echo(success(f"{self.description} is complete!"))
229
+ click.echo(success(f"{self.description} is complete.", preformatted=True))
239
230
 
240
231
  def debug_log(self, message):
241
232
  if self.debug:
@@ -244,11 +235,44 @@ 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.parent.mkdir(parents=True, exist_ok=True)
250
+ mwc_rc_path.write_text(config_text)
251
+
252
+ def generate_shell_config(self):
253
+ "Generates the shell configuration file contents"
254
+ f = ""
255
+ shell = get_shell_name()
256
+ f += "# Making With Code RC File\n\n"
257
+ f += "## Hook direnv into shell\n"
258
+ f += f'eval "$(direnv hook {shell})"\n\n'
259
+
260
+ if Platform.detect() == Platform.MAC:
261
+ if self.settings['editor'] == "subl":
262
+ f += "## Add subl to $PATH\n"
263
+ subldir = "/Applications/Sublime Text.app/Contents/SharedSupport/bin"
264
+ f += f'export PATH="{subldir}:$PATH"\n\n'
265
+ shell = get_shell_name()
266
+ if shell == "zsh":
267
+ f += "## Initialize zsh autocomplete\n"
268
+ f += "fpath+=~/.zfunc\n"
269
+ f += "autoload -Uz compinit && compinit\n\n"
270
+ return f
271
+
272
+ class SourceMWCShellConfig(SetupTask):
273
+ """Writes a line in the shell rc config file sourcing ~/.config/mwc/shell_config.sh.
250
274
  """
251
- description = "Link main shell config file to ~/.mwc_rc"
275
+ description = "Source MWC config file in the main shell config file"
252
276
 
253
277
  def is_complete(self):
254
278
  return f"source {get_mwc_rc_path()}" in platform_rc_file().read_text()
@@ -262,6 +286,7 @@ class MWCShellConfig(SetupTask):
262
286
  fh.write(f"\n# MAKING WITH CODE\n")
263
287
  fh.write(f'export PATH="$PATH:{mwcpath}"\n')
264
288
  fh.write(f"source {get_mwc_rc_path()}\n")
289
+ click.echo(info(f"Updated your shell login file ({rc_file}). You may need to close and reopen Terminal."))
265
290
 
266
291
  class InstallXCode(SetupTask):
267
292
  description = "Install XCode"
@@ -278,78 +303,6 @@ class InstallXCode(SetupTask):
278
303
  click.echo(address(msg))
279
304
  run("xcode-select --install", shell=True, check=True)
280
305
 
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
306
  class InstallPackage(SetupTask):
354
307
  """A subclass of SetupTask for packages to be installed by the Platform package manager.
355
308
  """
@@ -382,6 +335,18 @@ class InstallPackage(SetupTask):
382
335
  self.debug_log(f"Running: {package_manager}{package_name}")
383
336
  run(f"{package_manager}{package_name}", shell=True, check=True)
384
337
 
338
+ class InstallPackageFromInstaller(InstallPackage):
339
+ """Install from a remotely-fetched installer specified as install_command.
340
+ """
341
+ def run_task(self):
342
+ click.echo(address(f"Installing {self.executable_name}..."))
343
+ self.debug_log(f"Running: {self.install_command}")
344
+ run(self.install_command, shell=True, check=True)
345
+
346
+ class InstallDirenv(InstallPackageFromInstaller):
347
+ executable_name = "direnv"
348
+ install_command = "curl -sfL https://direnv.net/install.sh | bash"
349
+
385
350
  class InstallGit(InstallPackage):
386
351
  platform = Platform.MAC | Platform.UBUNTU
387
352
  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
-