Skip to content

Commit c8ff045

Browse files
committed
Initial version of rt-config
1 parent 0a69109 commit c8ff045

File tree

4 files changed

+264
-0
lines changed

4 files changed

+264
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
/sbin/rt-passwd
5858
/sbin/standalone_httpd
5959
/sbin/rt-munge-attachments
60+
/sbin/rt-config
6061
/var/
6162
/autom4te.cache/
6263
/configure

Makefile.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ SYSTEM_BINARIES = rt-attributes-viewer \
166166
rt-test-dependencies \
167167
rt-validator \
168168
rt-validate-aliases \
169+
rt-config \
169170
standalone_httpd
170171

171172

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ AC_CONFIG_FILES([
500500
sbin/rt-importer
501501
sbin/rt-passwd
502502
sbin/rt-munge-attachments
503+
sbin/rt-config
503504
bin/rt-crontool
504505
bin/rt-run-scheduled-processes
505506
bin/rt-mailgate

sbin/rt-config.in

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
#!@PERL@
2+
# BEGIN BPS TAGGED BLOCK {{{
3+
#
4+
# COPYRIGHT:
5+
#
6+
# This software is Copyright (c) 1996-2026 Best Practical Solutions, LLC
7+
# <sales@bestpractical.com>
8+
#
9+
# (Except where explicitly superseded by other copyright notices)
10+
#
11+
#
12+
# LICENSE:
13+
#
14+
# This work is made available to you under the terms of Version 2 of
15+
# the GNU General Public License. A copy of that license should have
16+
# been provided with this software, but in any event can be snarfed
17+
# from www.gnu.org.
18+
#
19+
# This work is distributed in the hope that it will be useful, but
20+
# WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22+
# General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27+
# 02110-1301 or visit their web page on the internet at
28+
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
29+
#
30+
#
31+
# CONTRIBUTION SUBMISSION POLICY:
32+
#
33+
# (The following paragraph is not intended to limit the rights granted
34+
# to you to modify and distribute this software under the terms of
35+
# the GNU General Public License and is only of importance to you if
36+
# you choose to contribute your changes and enhancements to the
37+
# community by submitting them to Best Practical Solutions, LLC.)
38+
#
39+
# By intentionally submitting any modifications, corrections or
40+
# derivatives to this work, or any other work intended for use with
41+
# Request Tracker, to Best Practical Solutions, LLC, you confirm that
42+
# you are the copyright holder for those contributions and you grant
43+
# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
44+
# royalty-free, perpetual, license to use, copy, create derivative
45+
# works based on those contributions, and sublicense and distribute
46+
# those contributions and any derivatives thereof.
47+
#
48+
# END BPS TAGGED BLOCK }}}
49+
use strict;
50+
use warnings;
51+
52+
# fix lib paths, some may be relative
53+
BEGIN {
54+
require File::Spec;
55+
my @libs = ( "@RT_LIB_PATH@", "@LOCAL_LIB_PATH@" );
56+
my $bin_path;
57+
58+
for my $lib (@libs) {
59+
unless ( File::Spec->file_name_is_absolute($lib) ) {
60+
unless ($bin_path) {
61+
if ( File::Spec->file_name_is_absolute(__FILE__) ) {
62+
$bin_path = ( File::Spec->splitpath(__FILE__) )[1];
63+
}
64+
else {
65+
require FindBin;
66+
no warnings "once";
67+
$bin_path = $FindBin::Bin;
68+
}
69+
}
70+
$lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
71+
}
72+
unshift @INC, $lib;
73+
}
74+
75+
}
76+
77+
use RT::Interface::CLI qw/Init loc/;
78+
use 5.26.3;
79+
use Encode;
80+
81+
my %opt = ();
82+
Init( \%opt, 'value=s', 'format=s' );
83+
84+
my $action = shift @ARGV or RT::Interface::CLI->ShowHelp( Message => loc('Missing action.') );
85+
my $config_name = shift @ARGV or RT::Interface::CLI->ShowHelp( Message => loc('Missing configuration name.') );
86+
87+
if ( $action eq 'show' ) {
88+
my $value = RT->Config->Get($config_name);
89+
if ( ref $value ) {
90+
my $format = lc( $opt{format} || 'perl' );
91+
if ( $format eq 'json' ) {
92+
eval { print encode( 'UTF-8', json($value) ) }; # json output has newline already
93+
if ($@) {
94+
say STDERR encode( 'UTF-8',
95+
loc( "[_1] can not be formatted as json, switching to perl", $config_name ) );
96+
}
97+
else {
98+
exit 0;
99+
}
100+
}
101+
102+
no warnings 'once';
103+
require Data::Dumper;
104+
local $Data::Dumper::Terse = 1;
105+
local $Data::Dumper::Indent = 2;
106+
local $Data::Dumper::Sortkeys = 1;
107+
local $Data::Dumper::Deparse = 1;
108+
print Data::Dumper::Dumper($value); # Dumper output has newline already.
109+
}
110+
elsif ( defined $value ) {
111+
say encode( 'UTF-8', $value );
112+
}
113+
else {
114+
say encode( 'UTF-8', loc('Not defined') );
115+
}
116+
}
117+
elsif ( $action eq 'edit' ) {
118+
no warnings 'once';
119+
if ( my $meta = $RT::Config::META{$config_name} ) {
120+
if ( $meta->{Immutable} || $meta->{Obfuscate} ) {
121+
say STDERR encode( 'UTF-8', loc( "[_1] can be modified only in config file.", $config_name ) );
122+
exit 1;
123+
}
124+
else {
125+
my $value = $opt{value};
126+
if ( defined $value ) {
127+
$value = decode( 'UTF-8', $value );
128+
}
129+
else {
130+
my $old_value = RT->Config->Get($config_name);
131+
chomp( $value = edit( ref $old_value ? json($old_value) : $old_value ) );
132+
}
133+
134+
if ( $meta->{Type} ne 'SCALAR' ) {
135+
require JSON;
136+
$value = JSON::from_json($value);
137+
}
138+
139+
my $old_value = RT->Config->Get($config_name);
140+
if ( ( ref $old_value ? RT::Configuration->_SerializeContent($old_value) : $old_value ) eq
141+
( ref $value ? RT::Configuration->_SerializeContent($value) : $value ) )
142+
{
143+
say encode( 'UTF-8', loc('Nothing changed.') );
144+
exit 0;
145+
}
146+
147+
my $config = RT::Configuration->new( RT->SystemUser );
148+
$config->LoadByCols( Name => $config_name, Disabled => 0 );
149+
my ( $ret, $msg );
150+
if ( $config->Id ) {
151+
( $ret, $msg ) = $config->SetContent($value);
152+
}
153+
else {
154+
( $ret, $msg ) = $config->Create(
155+
Name => $config_name,
156+
Content => $value,
157+
);
158+
}
159+
160+
if ($ret) {
161+
say encode( 'UTF-8', $msg );
162+
}
163+
else {
164+
say STDERR encode( 'UTF-8', $msg );
165+
exit 1;
166+
}
167+
}
168+
}
169+
else {
170+
say STDERR encode( 'UTF-8', loc('No metadata found.') );
171+
exit 1;
172+
}
173+
}
174+
elsif ( $action eq 'reset' ) {
175+
my $config = RT::Configuration->new( RT->SystemUser );
176+
$config->LoadByCols( Name => $config_name, Disabled => 0 );
177+
if ( $config->Id ) {
178+
my ( $ret, $msg ) = $config->Delete;
179+
if ($ret) {
180+
say encode( 'UTF-8', $msg );
181+
}
182+
else {
183+
say STDERR encode( 'UTF-8', $msg );
184+
exit 1;
185+
}
186+
}
187+
else {
188+
say STDERR encode( 'UTF-8', loc( '[_1] is not set in database.', $config_name ) );
189+
exit 1;
190+
}
191+
}
192+
else {
193+
RT::Interface::CLI->ShowHelp( Message => loc( 'Invalid action [_1].', $action ) );
194+
}
195+
196+
sub json {
197+
my $value = shift;
198+
require JSON;
199+
state $JSON = JSON->new->pretty->canonical;
200+
return $JSON->encode($value);
201+
}
202+
203+
sub edit {
204+
my ($text) = @_;
205+
my $editor = $ENV{EDITOR} || $ENV{VISUAL} || "vi";
206+
207+
local $/ = undef;
208+
209+
my $handle = File::Temp->new;
210+
binmode $handle, ':encoding(UTF-8)';
211+
print $handle $text;
212+
close($handle);
213+
214+
system( $editor, $handle->filename ) && die "Couldn't run $editor.\n";
215+
216+
open( $handle, '<:encoding(UTF-8)', $handle->filename ) or die "$handle: $!\n";
217+
$text = <$handle>;
218+
close($handle);
219+
220+
return $text;
221+
}
222+
223+
__END__
224+
225+
=head1 NAME
226+
227+
rt-config - Manage RT configurations
228+
229+
=head1 SYNOPSIS
230+
231+
rt-config show DefaultQueue
232+
rt-config show PriorityAsString
233+
rt-config show PriorityAsString --format perl
234+
rt-config show PriorityAsString --format json
235+
236+
# Update configs in Database
237+
rt-config edit DefaultQueue # Invoke default editor to set the new value
238+
rt-config edit PriorityAsString --value '{"Default": {"High": 100, "Low": 0, "Medium": 50}}'
239+
240+
# Revert config changes to the file version
241+
rt-config reset DefaultQueue
242+
rt-config reset PriorityAsString
243+
244+
=head1 OPTIONS
245+
246+
=over
247+
248+
=item B<--value>
249+
250+
For C<edit> only, you can specify the new value using this option.
251+
Reference values must be encoded as C<json>.
252+
253+
=item B<--format>
254+
255+
For C<show> only. This is used to format reference values. Available options
256+
are C<perl> and C<json>. By default, it prefers C<perl> and also falls back
257+
to C<perl> when C<json> is not applicable.
258+
259+
=back
260+
261+
=cut

0 commit comments

Comments
 (0)