Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/fs-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,9 @@ publish = true # FeatureScripts: finalize it at once, and tag it (t

They run in name order. Each runs as `fs release` or `fs parts SET --release` would, then its file is deleted and what the
release recorded (`fs-studios.json`, a part set's locations and exports) is committed. Every version a release makes
ends its description with the file's name (`[releases/frame-hubs.toml]`), so a file whose release Onshape already has,
whole or partway, stops `fs run-releases` rather than releasing twice: check what it made, finish it by hand, and
delete the file. `fs releases` lists and checks the pending files without calling Onshape.
ends its description with the file's name and the commit that added it (`[releases/frame-hubs.toml 1a2b3c4d5e6f]`), so a
file whose release Onshape already has, whole or partway, stops `fs run-releases` rather than releasing twice: check
what it made, finish it by hand, and delete the file. A file added again later, under the same name, is a new release. `fs releases` lists and checks the pending files without calling Onshape.

### The Onshape workflow

Expand Down
6 changes: 6 additions & 0 deletions fs_cli/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ def commit_with_contents(root: pathlib.Path, file: pathlib.Path, contents: str)
return None


def added_in(root: pathlib.Path, file: pathlib.Path) -> str | None:
"""The commit that last added a file, or None while it's uncommitted."""
output = _git(root, "log", "--diff-filter=A", "-1", "--format=%H", "--", str(file))
return output.strip() if output and output.strip() else None


def has_tag(root: pathlib.Path, name: str) -> bool:
return _git(root, "rev-parse", "--verify", "--quiet", f"refs/tags/{name}") is not None

Expand Down
7 changes: 6 additions & 1 deletion fs_cli/release_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import tomllib

from fs_cli.config import Config
from fs_cli.git import added_in
from fs_cli.versions import VersionType

RELEASES_FOLDER = "releases"
Expand All @@ -39,6 +40,8 @@ class ReleaseFile:
part_set: str | None = None
beta: bool = False
publish: bool = True
# The commit that added it, so a file of the same name asking for a later release isn't taken for this one
added: str | None = None

@property
def target(self) -> str:
Expand All @@ -47,7 +50,8 @@ def target(self) -> str:
@property
def marker(self) -> str:
"""Ends the description of what it releases, so a release Onshape already has is found again."""
return f"[{RELEASES_FOLDER}/{self.path.name}]"
added = f" {self.added[:12]}" if self.added else ""
return f"[{RELEASES_FOLDER}/{self.path.name}{added}]"

@property
def described(self) -> str:
Expand Down Expand Up @@ -92,4 +96,5 @@ def load(config: Config, path: pathlib.Path) -> ReleaseFile:
data.get("part_set"),
bool(data.get("beta", False)),
bool(data.get("publish", True)),
added_in(config.root, path),
)
17 changes: 13 additions & 4 deletions tests/fs_cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2343,22 +2343,31 @@ def test_run_releases_makes_each_release_once(repo, onshape, capsys):
'script = "robotFrame"\nbump = "minor"\ndescription = "First"\npublish = false\n'
)
commit_all(repo, "Ask for a release")
asked = git_output(repo, "rev-parse", "HEAD").strip()[:12]

assert run(onshape, "run-releases", "--no-push-tags") == 0
released = onshape.versions_by_document["back"][-1]
assert (released.name, released.description) == ("Robot frame - v0.1.0", "First [releases/frame.toml]")
assert (released.name, released.description) == ("Robot frame - v0.1.0", f"First [releases/frame.toml {asked}]")
assert element_id in onshape.studios("back", released.id)
assert not (repo / "releases" / "frame.toml").exists()
assert git_output(repo, "log", "-1", "--format=%s") == "Release robotFrame (releases/frame.toml)\n"
assert git_output(repo, "status", "--porcelain") == ""

# Asked for again, it's found made rather than made twice
(repo / "releases" / "frame.toml").write_text('script = "robotFrame"\nbump = "minor"\n')
commit_all(repo, "Ask again")
# Run again before its file was deleted, as after a run that stopped partway, it's found made rather than made twice
git(repo, "reset", "-q", "--hard", "HEAD~1")
capsys.readouterr()
assert run(onshape, "run-releases", "--no-push-tags") == 2
assert "already made Robot frame - v0.1.0" in capsys.readouterr().err

# A file of the same name asking for a later release makes it
git(repo, "rm", "-q", "releases/frame.toml")
commit_all(repo, "Released by hand")
(repo / "releases").mkdir(exist_ok=True)
(repo / "releases" / "frame.toml").write_text('script = "robotFrame"\nbump = "minor"\n')
commit_all(repo, "Ask again")
assert run(onshape, "run-releases", "--no-push-tags") == 0
assert onshape.versions_by_document["back"][-1].name == "Robot frame - v0.2.0"


def test_branches_prunes_only_its_workspaces_of_branches_gone_from_origin(repo, onshape, tmp_path_factory, capsys):
from fs_cli.branches import MADE_BY_FS
Expand Down
Loading