Compare commits

..

No commits in common. "4ba599b2fe69d24d9581e761fdc211ab8fc23d60" and "a0a64a098f3146c51914afd12ab7c0ce52f5f1d5" have entirely different histories.

8 changed files with 75 additions and 225 deletions

View file

@ -2,7 +2,6 @@ on: [push]
jobs: jobs:
alpine: alpine:
runs-on: ubuntu-latest
container: container:
image: ${{vars.DOCKER}}alpine:3.19 image: ${{vars.DOCKER}}alpine:3.19
steps: steps:
@ -17,7 +16,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
debian: debian:
runs-on: ubuntu-latest
needs: alpine needs: alpine
if: failure() || success() if: failure() || success()
container: container:
@ -34,7 +32,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
ubuntu: ubuntu:
runs-on: ubuntu-latest
needs: debian needs: debian
if: failure() || success() if: failure() || success()
container: container:
@ -51,7 +48,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
arch: arch:
runs-on: ubuntu-latest
needs: ubuntu needs: ubuntu
if: failure() || success() if: failure() || success()
container: container:
@ -68,7 +64,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
opensuse: opensuse:
runs-on: ubuntu-latest
needs: arch needs: arch
if: failure() || success() if: failure() || success()
container: container:
@ -85,7 +80,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
fedora: fedora:
runs-on: ubuntu-latest
needs: opensuse needs: opensuse
if: failure() || success() if: failure() || success()
container: container:
@ -102,7 +96,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
alma: alma:
runs-on: ubuntu-latest
needs: fedora needs: fedora
if: failure() || success() if: failure() || success()
container: container:
@ -119,7 +112,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
rocky: rocky:
runs-on: ubuntu-latest
needs: alma needs: alma
if: failure() || success() if: failure() || success()
container: container:

View file

@ -2,7 +2,6 @@ on: [push]
jobs: jobs:
alpine: alpine:
runs-on: ubuntu-latest
container: container:
image: ${{vars.DOCKER}}alpine:3.20 image: ${{vars.DOCKER}}alpine:3.20
steps: steps:
@ -17,7 +16,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
debian: debian:
runs-on: ubuntu-latest
needs: alpine needs: alpine
if: failure() || success() if: failure() || success()
container: container:
@ -37,7 +35,6 @@ jobs:
- run: spcd-synchronize - run: spcd-synchronize
ubuntu: ubuntu:
runs-on: ubuntu-latest
needs: debian needs: debian
if: failure() || success() if: failure() || success()
container: container:
@ -54,7 +51,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
arch: arch:
runs-on: ubuntu-latest
needs: ubuntu needs: ubuntu
if: failure() || success() if: failure() || success()
container: container:
@ -71,7 +67,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
opensuse: opensuse:
runs-on: ubuntu-latest
needs: arch needs: arch
if: failure() || success() if: failure() || success()
container: container:
@ -88,7 +83,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
fedora: fedora:
runs-on: ubuntu-latest
needs: opensuse needs: opensuse
if: failure() || success() if: failure() || success()
container: container:
@ -105,7 +99,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
alma: alma:
runs-on: ubuntu-latest
needs: fedora needs: fedora
if: failure() || success() if: failure() || success()
container: container:
@ -122,7 +115,6 @@ jobs:
- run: spcd-browse-workspace - run: spcd-browse-workspace
rocky: rocky:
runs-on: ubuntu-latest
needs: alma needs: alma
if: failure() || success() if: failure() || success()
container: container:

View file

@ -18,7 +18,7 @@ dependencies = ["rwx"]
description = "Shell to Python Continuous Deployment" description = "Shell to Python Continuous Deployment"
dynamic = ["version"] dynamic = ["version"]
keywords = [] keywords = []
license-files = ["license.md"] license-files = { paths = ["license.md"] }
name = "spcd" name = "spcd"
readme = "readme.md" readme = "readme.md"
requires-python = ">= 3.11" requires-python = ">= 3.11"

View file

@ -54,10 +54,9 @@ Picture it…
* [X] installing Git to clone * [X] installing Git to clone
* [X] this project * [X] this project
* [X] its parent framework * [X] its parent framework
* [X] installing Python * [X] installing both Python
* [X] system environment * [X] system environment
* [X] virtual environment * [X] virtual environment
* [ ] managed version
* [X] generating a Python module to switch context * [X] generating a Python module to switch context
#### Python #### Python
@ -96,16 +95,30 @@ Handle project workflows in a unified way:
* [ ] SourceHut * [ ] SourceHut
* whatever the Operating System container * whatever the Operating System container
| System | Latest | Previous | * [X] Alma
|:---------|:---------------------------|:---------------------------| * [X] 9
| Alma | * [X] 9 | * [X] 8 | * [X] 8
| Alpine | * [X] 3.20 | * [X] 3.19 | * [X] Alpine
| Arch | * [X] 20240818 (.0.255804) | * [X] 20240101 (.0.204074) | * [X] 3.20
| Debian | * [X] Bookworm (12) | * [ ] Bullseye (11) | * [X] 3.19
| Fedora | * [X] 40 | * [X] 39 | * [X] Arch
| OpenSUSE | * [ ] 15.6 | * [ ] 15.5 | * [X] 20240818 (.0.255804)
| Rocky | * [X] 9 | * [X] 8 | * [X] 20240101 (.0.204074)
| Ubuntu | * [X] Noble (24.04) | * [ ] Jammy (22.04) | * [X] Debian
* [X] Bookworm (12)
* [ ] Bullseye (11)
* [X] Fedora
* [X] 40
* [X] 39
* [ ] OpenSUSE
* [ ] 15.6
* [ ] 15.5
* [X] Rocky
* [X] 9
* [X] 8
* [X] Ubuntu
* [X] Noble (24.04)
* [ ] Jammy (22.04)
### Environment variables ### Environment variables
@ -116,7 +129,6 @@ Handle project workflows in a unified way:
| SPCD_GIT_RWX | RWX Git repository | rwx | | SPCD_GIT_RWX | RWX Git repository | rwx |
| SPCD_GIT_SHUNIT | ShUnit Git repository | shunit2 | | SPCD_GIT_SHUNIT | ShUnit Git repository | shunit2 |
| SPCD_GIT_SPCD | SPCD Git repository | spcd | | SPCD_GIT_SPCD | SPCD Git repository | spcd |
| SPCD_REF_ARCHIVE | Archive deployment ref | old |
| SPCD_REF_FEATURE | Feature deployment ref | f | | SPCD_REF_FEATURE | Feature deployment ref | f |
| SPCD_REF_RELEASE | Release deployment ref | main | | SPCD_REF_RELEASE | Release deployment ref | main |
| SPCD_REF_STAGING | Staging deployment ref | dev | | SPCD_REF_STAGING | Staging deployment ref | dev |
@ -138,40 +150,44 @@ Handle project workflows in a unified way:
#### Latest #### Latest
| os | https | up ca | python | ffmpeg | gource | graphviz | plantuml | shellcheck | shfmt | shunit | | os | https | up ca | python | graphviz | plantuml | shellcheck | shunit | shfmt | gource | ffmpeg |
|:----------------|---|---|------------:|-------:|-----:|------:|-----------:|------:|-----:|------:| |:----------------|---|---|------------:|------:|-----------:|------:|------:|-----:|-----:|-------:|
| Arch 20240818 | ☑ | ☑ | 3.12 | 7.0.2 | 0.54 | 12.0 | 1.2023.13 | 0.10 | 3.8 | 2.1.8 | | Arch 20240818 | ☑ | ☑ | 3.12 | 12.0 | 1.2023.13 | 0.10 | 2.1.8 | 3.8 | 0.54 | 7.0.2 |
| Alpine 3.20 | ☑ | ☐ | 3.12 | 6.1.1 | 0.54 | 9.0 | 1.2024.4 | 0.10 | 3.8 | 2.1.8 | | Alpine 3.20 | ☑ | ☐ | 3.12 | 9.0 | 1.2024.4 | 0.10 | 2.1.8 | 3.8 | 0.54 | 6.1.1 |
| Fedora 40 | ☑ | ☑ | 3.12 → 3.13 | 6.1.2 | 0.55 | 9.0 | 1.2024.6 | 0.9 | 3.7 | 2.1.6 | | Fedora 40 | ☑ | ☑ | 3.12 → 3.13 | 9.0 | 1.2024.6 | 0.9 | 2.1.6 | 3.7 | 0.55 | 6.1.2 |
| Debian Bookworm | ☐ | ☐ | 3.11 | 5.1.6 | 0.54 | 2.42 | 1.2020.2 | 0.9 | 3.6 | 2.1.8 | | Debian Bookworm | ☐ | ☐ | 3.11 | 2.42 | 1.2020.2 | 0.9 | 2.1.8 | 3.6 | 0.54 | 5.1.6 |
| OpenSUSE 15.6 | ☐ | ☑ | 3.6 → 3.12 | 4.4.4 | 0.54 | 2.48 | 1.2020.9 | 0.8 | 3.5 | 2.1.6 | | OpenSUSE 15.6 | ☐ | ☑ | 3.6 → 3.12 | 2.48 | 1.2020.9 | 0.8 | 2.1.6 | 3.5 | 0.54 | 4.4.4 |
| Ubuntu Noble | ☐ | ☐ | 3.12 | 6.1.1 | 0.54 | u2.42 | u1.2020.2 | u0.9 | u3.8 | 2.1.8 | | Ubuntu Noble | ☐ | ☐ | 3.12 | u2.42 | u1.2020.2 | u0.9 | 2.1.8 | u3.8 | 0.54 | 6.1.1 |
| Alma / Rocky 9 | ☑ | ☑ | 3.9 → 3.12 | e5.1.4 | | 2.44 | e1.2024.6 | e0.8 | | | | Alma / Rocky 9 | ☑ | ☑ | 3.9 → 3.12 | 2.44 | e1.2024.6 | e0.8 | | | | e5.1.4 |
#### Previous #### Previous
| os | https | up ca | python | ffmpeg | gource | graphviz | plantuml | shellcheck | shfmt | shunit | | os | https | up ca | python | graphviz | plantuml | shellcheck | shunit | shfmt | gource | ffmpeg |
|:----------------|---|---|------------:|-------:|-----:|------:|-----------:|------:|-----:|------:| |:----------------|---|---|------------:|------:|-----------:|------:|------:|-----:|-----:|-------:|
| Alpine 3.19 | ☑ | ☐ | 3.11 | 6.1.1 | 0.54 | 9.0 | 1.2023.12 | 0.9 | 3.7 | 2.1.8 | | Alpine 3.19 | ☑ | ☐ | 3.11 | 9.0 | 1.2023.12 | 0.9 | 2.1.8 | 3.7 | 0.54 | 6.1.1 |
| Fedora 39 | ☑ | ☑ | 3.12 → 3.13 | 6.1.1 | 0.55 | 8.1 | 1.2024.6 | 0.9 | 3.5 | 2.1.6 | | Fedora 39 | ☑ | ☑ | 3.12 → 3.13 | 8.1 | 1.2024.6 | 0.9 | 2.1.6 | 3.5 | 0.55 | 6.1.1 |
| OpenSUSE 15.5 | ☐ | ☑ | 3.6 → 3.11 | 4.4.4 | 0.54 | 2.48 | 1.2020.9 | 0.8 | 3.5 | 2.1.6 | | OpenSUSE 15.5 | ☐ | ☑ | 3.6 → 3.11 | 2.48 | 1.2020.9 | 0.8 | 2.1.6 | 3.5 | 0.54 | 4.4.4 |
| Alma / Rocky 8 | ☑ | ☑ | 3.6 → 3.12 | | | 2.40 | e1.2024.6 | e0.6 | | | | Alma / Rocky 8 | ☑ | ☑ | 3.6 → 3.12 | 2.40 | e1.2024.6 | e0.6 | | | | |
#### Older Python #### Older Python
| os | https | up ca | python | ffmpeg | gource | graphviz | plantuml | shellcheck | shfmt | shunit | | os | https | up ca | python | graphviz | plantuml | shellcheck | shunit | shfmt | gource | ffmpeg |
|:----------------|---|---|------------:|-------:|-----:|------:|-----------:|------:|-----:|------:| |:----------------|---|---|------------:|------:|-----------:|------:|------:|-----:|-----:|-------:|
| Ubuntu Jammy | ☐ | ☐ | 3.10 | 4.4.2 | 0.51 | u2.42 | u1.2020.2 | u0.8 | u3.4 | 2.1.6 | | Ubuntu Jammy | ☐ | ☐ | 3.10 | u2.42 | u1.2020.2 | u0.8 | 2.1.6 | u3.4 | 0.51 | 4.4.2 |
| Debian Bullseye | ☐ | ☐ | 3.9 | 4.3.7 | 0.51 | 2.42 | 1.2020.2 | 0.7 | | 2.1.6 | | Debian Bullseye | ☐ | ☐ | 3.9 | 2.42 | 1.2020.2 | 0.7 | 2.1.6 | | 0.51 | 4.3.7 |
--- ---
## Who ## Who
### Author ### By
* [Marc Beninca](https://marc.beninca.link) * [Marc Beninca](https://marc.beninca.link)
### For
* People feeling the need to aim for consistency in the CI / CD universe
--- ---
## Where ## Where
@ -274,7 +290,5 @@ Handle project workflows in a unified way:
* rpm fusion * rpm fusion
* tex * tex
* translate to french * translate to french
* try to support * try to support nix
* guix
* nix
* uv * uv

View file

@ -10,13 +10,12 @@ from rwx import fs
from rwx.log import stream as log from rwx.log import stream as log
from rwx.ps import run from rwx.ps import run
from spcd import act, commands from spcd import cmd
from spcd.ci import project, projects from spcd.ci import project, projects
from spcd.shell import env from spcd.shell import env
from spcd.util import browse, cat, split, step from spcd.util import browse, cat, split, step
COMMANDS_PREFIX = "spcd-" COMMANDS_PREFIX = "spcd-"
ENTRY_POINT = "__main__.py"
def clone_project_branch() -> None: def clone_project_branch() -> None:
@ -45,45 +44,6 @@ def clone_project_branch() -> None:
) )
def install_actions() -> None:
"""Make actions usable in workflows."""
step("Install actions")
name = "action.yaml"
root = project.root / "act"
vpy = Path(env.SPCD_PYTHON_VENV_BINARIES) / "python"
for action in ("action", "synchronize"):
log.info(action)
directory = root / action
fs.make_directory(directory)
match action:
case "action":
inputs = """\
arg_1:
required: true
arg_2:
required: true
arg_3:
required: true
arg_4:
default: '"placeholder"'
"""
case "synchronize":
inputs = """\
source:
default: out
required: false
"""
yaml = f"""\
runs:
using: composite
steps:
- run: {vpy} -m spcd {action}
inputs:
{inputs}"""
fs.write(directory / name, yaml)
cat(directory / name)
def install_commands(path: Path) -> None: def install_commands(path: Path) -> None:
"""Make commands callable in the operating system. """Make commands callable in the operating system.
@ -92,12 +52,12 @@ def install_commands(path: Path) -> None:
""" """
step("Install commands") step("Install commands")
user = Path("/usr/local/bin") user = Path("/usr/local/bin")
for command in ( for command in [
"browse-workspace", "browse-workspace",
"build-project", "build-project",
"check-project", "check-project",
"synchronize", "synchronize",
): ]:
log.info(command) log.info(command)
(user / f"{COMMANDS_PREFIX}{command}").symlink_to(path) (user / f"{COMMANDS_PREFIX}{command}").symlink_to(path)
@ -148,20 +108,15 @@ def main(main_file: Path) -> None:
if env.SPCD_PYTHON_VENV_BINARIES not in paths: if env.SPCD_PYTHON_VENV_BINARIES not in paths:
environ["PATH"] = pathsep.join([env.SPCD_PYTHON_VENV_BINARIES, *paths]) environ["PATH"] = pathsep.join([env.SPCD_PYTHON_VENV_BINARIES, *paths])
path, *arguments = sys.argv path, *arguments = sys.argv
if (name := Path(path).name) == ENTRY_POINT: name = Path(path).name
if arguments: if name == "__main__.py":
name, *arguments = arguments
f = getattr(act, name)
f(*arguments)
else:
list_environment_variables() list_environment_variables()
clone_project_branch() clone_project_branch()
set_ssh() set_ssh()
install_actions()
install_commands(main_file) install_commands(main_file)
install_python_packages() install_python_packages()
else: else:
f = getattr(commands, name.replace("-", "_")) f = getattr(cmd, name.replace("-", "_"))
f(*arguments) f(*arguments)

View file

@ -1,62 +0,0 @@
"""Actions available for workflows."""
from __future__ import annotations
import os
from ast import literal_eval
from pathlib import Path
from rwx import ps
from rwx.log import stream as log
from spcd.ci import project, projects
from spcd.shell import env
PREFIX = "INPUT_"
def action() -> None:
"""Display action inputs."""
for variable, value in parse_inputs().items():
log.info("%s = %s", variable, value)
def parse_inputs() -> dict[str, object]:
"""Parse inputs as a dictionary.
:return: name & value pairs
:rtype: dict[str, object]
"""
inputs = {}
for variable, value in sorted(projects.environment.items()):
if variable.startswith(PREFIX):
name = variable.removeprefix(PREFIX).lower()
inputs[name] = literal_eval(value)
return inputs
def synchronize(source: str | None = None, target: str | None = None) -> None:
"""Synchronize output towards a target.
:param source: where to deploy from
:type source: str | None
:param target: where to deploy to
:type target: str | None
"""
if not target:
user = "cd"
host = env.SPCD_PROJECT_PATH
root = (
Path(os.sep) / user / projects.group / project.name / project.branch
)
target = f"{user}@{host}:{root}"
if not source:
source = "out"
ps.run(
"rsync",
"--archive",
"--delete-before",
"--verbose",
f"{source}/",
f"{target}/",
)

View file

@ -1,3 +1,5 @@
#! /usr/bin/env sh
# ╭───────────────╮ # ╭───────────────╮
# │ __ = internal │ # │ __ = internal │
# ╰───────────────╯ # ╰───────────────╯
@ -384,7 +386,6 @@ spcd_e_default() {
[ -n "${SPCD_GIT_SHUNIT}" ] || SPCD_GIT_SHUNIT="shunit2" [ -n "${SPCD_GIT_SHUNIT}" ] || SPCD_GIT_SHUNIT="shunit2"
[ -n "${SPCD_REF_ARCHIVE}" ] || SPCD_REF_ARCHIVE="old"
[ -n "${SPCD_REF_FEATURE}" ] || SPCD_REF_FEATURE="f" [ -n "${SPCD_REF_FEATURE}" ] || SPCD_REF_FEATURE="f"
[ -n "${SPCD_REF_RELEASE}" ] || SPCD_REF_RELEASE="main" [ -n "${SPCD_REF_RELEASE}" ] || SPCD_REF_RELEASE="main"
[ -n "${SPCD_REF_STAGING}" ] || SPCD_REF_STAGING="dev" [ -n "${SPCD_REF_STAGING}" ] || SPCD_REF_STAGING="dev"
@ -512,7 +513,6 @@ ${SPCD_PROJECT_ROOT}$(basename "${GITHUB_SERVER_URL}")"
fi fi
# check project variables # check project variables
case "${SPCD_PROJECT_BRANCH}" in case "${SPCD_PROJECT_BRANCH}" in
"${SPCD_REF_ARCHIVE}" | \
"${SPCD_REF_RELEASE}" | \ "${SPCD_REF_RELEASE}" | \
"${SPCD_REF_STAGING}" | \ "${SPCD_REF_STAGING}" | \
"${SPCD_REF_FEATURE}") ;; "${SPCD_REF_FEATURE}") ;;
@ -796,15 +796,6 @@ spcd_f_pkg() {
"${SPCD_OS_ALMA}" | "${SPCD_OS_ROCKY}") ;; "${SPCD_OS_ALMA}" | "${SPCD_OS_ROCKY}") ;;
*) spcd_f_pm_pkg_install "gource" ;; *) spcd_f_pm_pkg_install "gource" ;;
esac esac
# gpg
spcd_step "GnuPG"
case "${SPCD_OS_ID}" in
"${SPCD_OS_DEBIAN}")
spcd_f_pm_pkg_install "gpg"
;;
# TODO other operating systems
*) ;;
esac
# graphviz # graphviz
spcd_step "GraphViz" spcd_step "GraphViz"
spcd_f_pm_pkg_install "graphviz" spcd_f_pm_pkg_install "graphviz"
@ -826,9 +817,6 @@ spcd_f_pkg() {
# plantuml # plantuml
spcd_step "PlantUML" spcd_step "PlantUML"
spcd_f_pm_pkg_install "plantuml" spcd_f_pm_pkg_install "plantuml"
# qrencode
spcd_step "QRencode"
spcd_f_pm_pkg_install "qrencode"
# rsync # rsync
spcd_step "Rsync" spcd_step "Rsync"
spcd_f_pm_pkg_install "rsync" spcd_f_pm_pkg_install "rsync"
@ -852,20 +840,6 @@ spcd_f_pkg() {
;; ;;
*) spcd_f_pm_pkg_install "shfmt" ;; *) spcd_f_pm_pkg_install "shfmt" ;;
esac esac
# tex
spcd_step "TeX"
case "${SPCD_OS_ID}" in
"${SPCD_OS_DEBIAN}")
spcd_f_pm_pkg_install "texlive-xetex"
spcd_f_pm_pkg_install "texlive-lang-french"
spcd_f_pm_pkg_install "texlive-plain-generic"
spcd_f_pm_pkg_install "texlive-bibtex-extra"
spcd_f_pm_pkg_install "texlive-fonts-recommended"
spcd_f_pm_pkg_install "biber"
;;
# TODO other operating systems
*) ;;
esac
spcd_step_out spcd_step_out
} }
@ -1405,7 +1379,6 @@ index-url = ${SPCD_URL_PYTHON}/simple
spcd_step "Activate" spcd_step "Activate"
export PATH="${SPCD_PYTHON_VENV_BINARIES}:${PATH}" export PATH="${SPCD_PYTHON_VENV_BINARIES}:${PATH}"
export VIRTUAL_ENV="${SPCD_PYTHON_VENV}" export VIRTUAL_ENV="${SPCD_PYTHON_VENV}"
pip install "PyYAML"
spcd_step_out spcd_step_out
} }

View file

@ -1,8 +1,6 @@
"""Commands available for workflows.""" """Commands available for workflows."""
from __future__ import annotations import os
from os import sep
from pathlib import Path from pathlib import Path
from rwx import ps from rwx import ps
@ -19,8 +17,8 @@ def spcd_browse_workspace() -> None:
def spcd_build_project() -> None: def spcd_build_project() -> None:
"""Perform the actual building process.""" """Perform the actual building process."""
for extension in ("py", "sh"): for extension in ["py", "sh"]:
path = project.root / f"render.{extension}" path = project.root / f"build.{extension}"
if path.exists(): if path.exists():
ps.run(str(path)) ps.run(str(path))
break break
@ -28,25 +26,11 @@ def spcd_build_project() -> None:
def spcd_check_project() -> None: def spcd_check_project() -> None:
"""Check the project for anything wrong.""" """Check the project for anything wrong."""
ps.run( ps.run("ruff", "check")
"ruff",
"check",
("--ignore", "D203,D213"),
"--isolated",
("--select", "ALL"),
)
ps.run(
"ruff",
"format",
"--diff",
"--isolated",
("--line-length", "80"),
)
def spcd_synchronize( def spcd_synchronize(
source: str | None = None, source: str | None = None, target: str | None = None
target: str | None = None,
) -> None: ) -> None:
"""Synchronize output towards a target. """Synchronize output towards a target.
@ -58,7 +42,9 @@ def spcd_synchronize(
if not target: if not target:
user = "cd" user = "cd"
host = env.SPCD_PROJECT_PATH host = env.SPCD_PROJECT_PATH
root = Path(sep) / user / projects.group / project.name / project.branch root = (
Path(os.sep) / user / projects.group / project.name / project.branch
)
target = f"{user}@{host}:{root}" target = f"{user}@{host}:{root}"
if not source: if not source:
source = "out" source = "out"
@ -67,6 +53,6 @@ def spcd_synchronize(
"--archive", "--archive",
"--delete-before", "--delete-before",
"--verbose", "--verbose",
f"{source}{sep}", f"{source}/",
f"{target}{sep}", f"{target}/",
) )