From a1b179eeb26036c5b78136d2860b021a410fc03a Mon Sep 17 00:00:00 2001 From: sergiopaniego Date: Tue, 27 Oct 2026 18:38:38 +0100 Subject: [PATCH 1/2] prepare_hf_deployment.sh: skip the visibility update when nothing changes `hf repo settings` fails with "At least one setting must be updated" when the Space already has the requested visibility, so redeploying a public Space with --public (or a private one with --private) uploaded fine but was reported as a failed deployment. Read the current visibility first and only call `hf repo settings` when it differs. Co-Authored-By: Claude Opus 5.5 --- scripts/prepare_hf_deployment.sh | 27 ++++++-- tests/scripts/test_prepare_hf_deployment.py | 74 +++++++++++++++++++++ 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/scripts/prepare_hf_deployment.sh b/scripts/prepare_hf_deployment.sh index db151f458..ecf81d53d 100755 --- a/scripts/prepare_hf_deployment.sh +++ b/scripts/prepare_hf_deployment.sh @@ -788,15 +788,28 @@ deploy_env() { "$stage_dir" \ . || return 1 - # Ensure visibility is applied even when repo already existed. - local privacy_flag="--private" - if [ "$PRIVATE" = false ]; then - privacy_flag="--no-private" - fi + # Ensure visibility is applied even when repo already existed. `hf repo settings` + # rejects an update that changes nothing, so only call it when visibility differs. + local space_info if [ ${#TOKEN_ARGS[@]} -gt 0 ]; then - hf repo settings --repo-type space "$privacy_flag" "${TOKEN_ARGS[@]}" "$space_repo" || return 1 + space_info=$(hf spaces info "$space_repo" --expand=private --format json "${TOKEN_ARGS[@]}") || return 1 else - hf repo settings --repo-type space "$privacy_flag" "$space_repo" || return 1 + space_info=$(hf spaces info "$space_repo" --expand=private --format json) || return 1 + fi + local current_private=false + if printf "%s" "$space_info" | grep -q '"private": *true'; then + current_private=true + fi + if [ "$current_private" != "$PRIVATE" ]; then + local privacy_flag="--private" + if [ "$PRIVATE" = false ]; then + privacy_flag="--no-private" + fi + if [ ${#TOKEN_ARGS[@]} -gt 0 ]; then + hf repo settings --repo-type space "$privacy_flag" "${TOKEN_ARGS[@]}" "$space_repo" || return 1 + else + hf repo settings --repo-type space "$privacy_flag" "$space_repo" || return 1 + fi fi DEPLOYED_SPACES+=("$space_repo") diff --git a/tests/scripts/test_prepare_hf_deployment.py b/tests/scripts/test_prepare_hf_deployment.py index 73316f79f..5f107beca 100644 --- a/tests/scripts/test_prepare_hf_deployment.py +++ b/tests/scripts/test_prepare_hf_deployment.py @@ -356,3 +356,77 @@ def test_prepare_hf_deployment_keeps_the_env_description_first(tmp_path: Path) - ) assert _description(meta).startswith("A chess reinforcement learning environment") assert "This Space is built from OpenEnv environment `chess_env`" in readme + + +@pytest.mark.parametrize( + ("current_private", "visibility_flag", "expected_settings_call"), + [ + ("false", "--public", None), + ("true", "--private", None), + ("true", "--public", "--no-private"), + ("false", "--private", "--private"), + ], +) +def test_prepare_hf_deployment_updates_visibility_only_when_it_differs( + tmp_path: Path, + current_private: str, + visibility_flag: str, + expected_settings_call: str | None, +) -> None: + """`hf repo settings` fails on an update that changes nothing, so a Space that + already has the requested visibility must not get one.""" + repo_root = Path(__file__).resolve().parents[2] + bin_dir = tmp_path / "bin" + bin_dir.mkdir() + calls = tmp_path / "hf-calls.log" + fake_hf = bin_dir / "hf" + fake_hf.write_text( + "#!/usr/bin/env bash\n" + f'echo "$*" >> "{calls}"\n' + 'if [ "$1 $2" = "spaces info" ]; then\n' + f' echo \'{{"id": "openenv/echo_env", "private": {current_private}}}\'\n' + "fi\n" + 'if [ "$1 $2" = "repo settings" ]; then\n' + f' [ "{current_private}" = true ] && [[ " $* " == *" --private "* ]] && exit 1\n' + f' [ "{current_private}" = false ] && [[ " $* " == *" --no-private "* ]] && exit 1\n' + "fi\n" + "exit 0\n" + ) + fake_hf.chmod(0o755) + + result = subprocess.run( + [ + "bash", + str(repo_root / "scripts" / "prepare_hf_deployment.sh"), + "--env", + "echo_env", + "--repo-id", + "openenv/echo_env", + visibility_flag, + "--skip-collection", + "--staging-dir", + str(tmp_path / "hf-staging"), + ], + cwd=repo_root, + env={ + **os.environ, + "OPENENV_VERSION": "main", + "PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}", + }, + check=False, + capture_output=True, + text=True, + ) + + assert result.returncode == 0, result.stdout + result.stderr + assert "Uploaded https://huggingface.co/spaces/openenv/echo_env" in result.stdout + settings_calls = [ + line + for line in calls.read_text().splitlines() + if line.startswith("repo settings") + ] + if expected_settings_call is None: + assert settings_calls == [] + else: + assert len(settings_calls) == 1 + assert f" {expected_settings_call} " in f" {settings_calls[0]} " From 79914526e4a8579cb32b34e96a3c3086b0ac5ae1 Mon Sep 17 00:00:00 2001 From: sergiopaniego Date: Tue, 27 Oct 2026 18:47:05 +0100 Subject: [PATCH 2/2] Make a Space public with --public, which hf repos settings honors `hf repos settings` ignores `--no-private`, so the script never made a Space public. --- scripts/prepare_hf_deployment.sh | 11 ++++++----- tests/scripts/test_prepare_hf_deployment.py | 13 +++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/scripts/prepare_hf_deployment.sh b/scripts/prepare_hf_deployment.sh index ecf81d53d..618e0397a 100755 --- a/scripts/prepare_hf_deployment.sh +++ b/scripts/prepare_hf_deployment.sh @@ -788,8 +788,9 @@ deploy_env() { "$stage_dir" \ . || return 1 - # Ensure visibility is applied even when repo already existed. `hf repo settings` - # rejects an update that changes nothing, so only call it when visibility differs. + # Ensure visibility is applied even when repo already existed. `hf repos settings` + # rejects an update that changes nothing (and ignores `--no-private`), so only call + # it when visibility differs, with `--public` to make a Space public. local space_info if [ ${#TOKEN_ARGS[@]} -gt 0 ]; then space_info=$(hf spaces info "$space_repo" --expand=private --format json "${TOKEN_ARGS[@]}") || return 1 @@ -803,12 +804,12 @@ deploy_env() { if [ "$current_private" != "$PRIVATE" ]; then local privacy_flag="--private" if [ "$PRIVATE" = false ]; then - privacy_flag="--no-private" + privacy_flag="--public" fi if [ ${#TOKEN_ARGS[@]} -gt 0 ]; then - hf repo settings --repo-type space "$privacy_flag" "${TOKEN_ARGS[@]}" "$space_repo" || return 1 + hf repos settings --repo-type space "$privacy_flag" "${TOKEN_ARGS[@]}" "$space_repo" || return 1 else - hf repo settings --repo-type space "$privacy_flag" "$space_repo" || return 1 + hf repos settings --repo-type space "$privacy_flag" "$space_repo" || return 1 fi fi diff --git a/tests/scripts/test_prepare_hf_deployment.py b/tests/scripts/test_prepare_hf_deployment.py index 5f107beca..452bf048c 100644 --- a/tests/scripts/test_prepare_hf_deployment.py +++ b/tests/scripts/test_prepare_hf_deployment.py @@ -363,7 +363,7 @@ def test_prepare_hf_deployment_keeps_the_env_description_first(tmp_path: Path) - [ ("false", "--public", None), ("true", "--private", None), - ("true", "--public", "--no-private"), + ("true", "--public", "--public"), ("false", "--private", "--private"), ], ) @@ -373,8 +373,8 @@ def test_prepare_hf_deployment_updates_visibility_only_when_it_differs( visibility_flag: str, expected_settings_call: str | None, ) -> None: - """`hf repo settings` fails on an update that changes nothing, so a Space that - already has the requested visibility must not get one.""" + """`hf repos settings` fails on an update that changes nothing and ignores + `--no-private`, so it only runs when visibility differs, with `--public`.""" repo_root = Path(__file__).resolve().parents[2] bin_dir = tmp_path / "bin" bin_dir.mkdir() @@ -386,9 +386,10 @@ def test_prepare_hf_deployment_updates_visibility_only_when_it_differs( 'if [ "$1 $2" = "spaces info" ]; then\n' f' echo \'{{"id": "openenv/echo_env", "private": {current_private}}}\'\n' "fi\n" - 'if [ "$1 $2" = "repo settings" ]; then\n' + 'if [ "$1 $2" = "repos settings" ]; then\n' f' [ "{current_private}" = true ] && [[ " $* " == *" --private "* ]] && exit 1\n' - f' [ "{current_private}" = false ] && [[ " $* " == *" --no-private "* ]] && exit 1\n' + f' [ "{current_private}" = false ] && [[ " $* " == *" --public "* ]] && exit 1\n' + ' [[ " $* " == *" --no-private "* ]] && exit 1\n' "fi\n" "exit 0\n" ) @@ -423,7 +424,7 @@ def test_prepare_hf_deployment_updates_visibility_only_when_it_differs( settings_calls = [ line for line in calls.read_text().splitlines() - if line.startswith("repo settings") + if line.startswith("repos settings") ] if expected_settings_call is None: assert settings_calls == []