Skip to content

Commit 16a1758

Browse files
committed
Add PKG_SUCCESS_ON_NOP environment variable to modify NOP exit status
1 parent 652bdb1 commit 16a1758

File tree

3 files changed

+47
-17
lines changed

3 files changed

+47
-17
lines changed

src/client.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#
2222

2323
#
24-
# Copyright 2024 OmniOS Community Edition (OmniOSce) Association.
24+
# Copyright 2025 OmniOS Community Edition (OmniOSce) Association.
2525
# Copyright 2024 Oxide Computer Company
2626
# Copyright (c) 2007, 2024, Oracle and/or its affiliates.
2727
#
@@ -41,8 +41,8 @@
4141
# Environment variables
4242
#
4343
# PKG_IMAGE - root path of target image
44-
# PKG_IMAGE_TYPE [entire, partial, user] - type of image
45-
# XXX or is this in the Image configuration?
44+
# PKG_SUCCESS_ON_NOP - when an operation completes with nothing to do, exit with
45+
# the success code (0) instead of the NOP one (4).
4646

4747
try:
4848
import pkg.site_paths
@@ -200,6 +200,8 @@
200200
tmpdirs = []
201201
tmpfiles = []
202202

203+
EXIT_NOP_VAL = EXIT_OK if os.environ.get("PKG_SUCCESS_ON_NOP") else EXIT_NOP
204+
203205

204206
@atexit.register
205207
def cleanup():
@@ -620,7 +622,8 @@ def print_cmds(cmd_list, cmd_dic):
620622
--help or -?
621623
622624
Environment:
623-
PKG_IMAGE"""
625+
PKG_IMAGE
626+
PKG_SUCCESS_ON_NOP"""
624627
)
625628
)
626629
else:
@@ -898,6 +901,8 @@ def gen(meta=False):
898901
if errors:
899902
_generate_error_messages(out_json["status"], errors)
900903

904+
if out_json["status"] == EXIT_NOP:
905+
return EXIT_NOP_VAL
901906
return out_json["status"]
902907

903908

@@ -2499,7 +2504,7 @@ def __api_op(
24992504
if _op == PKG_OP_FIX and _noexecute and _quiet_plan:
25002505
return _verify_exit_code(_api_inst)
25012506
if _api_inst.planned_nothingtodo():
2502-
return EXIT_NOP
2507+
return EXIT_NOP_VAL
25032508
if _noexecute or _stage == API_STAGE_PLAN:
25042509
return EXIT_OK
25052510
else:
@@ -2818,6 +2823,8 @@ def __handle_client_json_api_output(out_json, op, api_inst):
28182823
display_repo_failures(out_json["data"]["repo_status"])
28192824

28202825
__display_plan_messages(api_inst, frozenset([OP_STAGE_PREP, OP_STAGE_EXEC]))
2826+
if out_json["status"] == EXIT_NOP:
2827+
return EXIT_NOP_VAL
28212828
return out_json["status"]
28222829

28232830

@@ -3525,7 +3532,7 @@ def autoremove(
35253532

35263533
if not pargs:
35273534
msg(_("No removable packages for this image."))
3528-
return EXIT_NOP
3535+
return EXIT_NOP_VAL
35293536

35303537
out_json = client_api._uninstall(
35313538
PKG_OP_UNINSTALL,
@@ -3602,6 +3609,8 @@ def verify(
36023609

36033610
# Since the verify output has been handled by display_plan_cb, only
36043611
# status code needs to be returned.
3612+
if out_json["status"] == EXIT_NOP:
3613+
return EXIT_NOP_VAL
36053614
return out_json["status"]
36063615

36073616

@@ -3714,6 +3723,8 @@ def fix(
37143723
if "errors" in out_json:
37153724
_generate_error_messages(out_json["status"], out_json["errors"], cmd=op)
37163725

3726+
if out_json["status"] == EXIT_NOP:
3727+
return EXIT_NOP_VAL
37173728
return out_json["status"]
37183729

37193730

@@ -3937,7 +3948,7 @@ def set_mediator(
39373948
return EXIT_OOPS
39383949
else:
39393950
msg(_("No changes required."))
3940-
return EXIT_NOP
3951+
return EXIT_NOP_VAL
39413952

39423953
if api_inst.get_dehydrated_publishers():
39433954
msg(
@@ -4038,7 +4049,7 @@ def unset_mediator(
40384049
return EXIT_OOPS
40394050
else:
40404051
msg(_("No changes required."))
4041-
return EXIT_NOP
4052+
return EXIT_NOP_VAL
40424053

40434054
if not quiet:
40444055
__display_plan(api_inst, verbose, noexecute)
@@ -4172,7 +4183,7 @@ def unfreeze(api_inst, args):
41724183
try:
41734184
pkgs = api_inst.freeze_pkgs(pargs, unfreeze=True, dry_run=dry_run)
41744185
if not pkgs:
4175-
return EXIT_NOP
4186+
return EXIT_NOP_VAL
41764187
for s in pkgs:
41774188
logger.info(_("{0} was unfrozen.").format(s))
41784189
return EXIT_OK
@@ -5594,6 +5605,8 @@ def publisher_set(
55945605
add_info={"repo_uri": repo_uri},
55955606
)
55965607

5608+
if out_json["status"] == EXIT_NOP:
5609+
return EXIT_NOP_VAL
55975610
return out_json["status"]
55985611

55995612

@@ -5608,6 +5621,8 @@ def publisher_unset(api_inst, pargs):
56085621
out_json["status"], out_json["errors"], cmd="unset-publisher"
56095622
)
56105623

5624+
if out_json["status"] == EXIT_NOP:
5625+
return EXIT_NOP_VAL
56115626
return out_json["status"]
56125627

56135628

@@ -7202,7 +7217,7 @@ def update_format(api_inst, pargs):
72027217
return EXIT_OK
72037218

72047219
logger.info(_("Image format already current."))
7205-
return EXIT_NOP
7220+
return EXIT_NOP_VAL
72067221

72077222

72087223
def print_version(pargs):

src/man/pkg.1

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.\" Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
2-
.\" Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
32
.\" Copyright 2024 Oxide Computer Company
4-
.Dd December 10, 2024
3+
.\" Copyright 2025 OmniOS Community Edition (OmniOSce) Association.
4+
.Dd June 25, 2025
55
.Dt PKG 1
66
.Os
77
.Sh NAME
@@ -4331,6 +4331,12 @@ before a connection is aborted.
43314331
A value of 0 means do not abort the operation.
43324332
.Pp
43334333
Default value: 5
4334+
.It Sy PKG_CLIENT_MAX_TIMEOUT
4335+
Maximum number of transport attempts per host before the client aborts the
4336+
operation.
4337+
A value of 0 means do not abort the operation.
4338+
.Pp
4339+
Default value: 4
43344340
.It Sy PKG_CONCURRENCY
43354341
The number of child images to update in parallel.
43364342
Ignored if the
@@ -4353,12 +4359,16 @@ If
43534359
is 0 or a negative number, all child images are updated in parallel.
43544360
.Pp
43554361
Default value: 1
4356-
.It Sy PKG_CLIENT_MAX_TIMEOUT
4357-
Maximum number of transport attempts per host before the client aborts the
4358-
operation.
4359-
A value of 0 means do not abort the operation.
4362+
.It Sy PKG_SUCCESS_ON_NOP
4363+
When set to a non-zero value, cause
4364+
.Nm
4365+
operations that result in there being no changes to apply to exit
4366+
successfully, with exit status 0
4367+
.Pq Command succeeded ,
4368+
rather than with exit status 4
4369+
.Pq \&No changes were made - nothing to do .
43604370
.Pp
4361-
Default value: 4
4371+
Default value: 0
43624372
.It Sy http_proxy , Sy https_proxy
43634373
HTTP or HTTPS proxy server.
43644374
.El

src/tests/pkg5testenv.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ def setup_environment(path_to_proto, debug=False, system_test=False):
129129
if k.startswith("PKG_") or k.lower().endswith("_proxy"):
130130
del os.environ[k]
131131

132+
# This environment variable changes the exit status of operations that
133+
# result in no changes being required. Unset it so tests get the expected
134+
# behaviour.
135+
os.environ.pop("PKG_SUCCESS_ON_NOP", None)
136+
132137
#
133138
# Tell package manager where its application data files live.
134139
#

0 commit comments

Comments
 (0)