-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Scriptlet interpreter and prein, postin (using lua), and verifyscript support added #95
base: master
Are you sure you want to change the base?
Conversation
5b5cdca
to
414daf4
Compare
rpm.go
Outdated
} | ||
|
||
func (r *RPM) writeSingleScriptlet(h *index, scriptlet, name string, scriptletTag, scriptletProgramTag int) error { | ||
if scriptlet != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer using an early return to avoid this indentation. i.e.
if scriptlet == "" {
return nil
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
rpm.go
Outdated
func (r *RPM) writeSingleScriptlet(h *index, scriptlet, name string, scriptletTag, scriptletProgramTag int) error { | ||
if scriptlet != "" { | ||
scriptletProgram := r.scriptletProgram | ||
// All this manual parsing instead of a simple regex to provide detailed error messages like rpmbuild: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But.. We are not rpmbuild. One of our aims is simplicity.
I really appreciate the effort you put in here, so it's not easy for me to say so, but please remove all of this verification code.
rpmbuild is a very over-engineered and complex beast, riddled with backwards-compatibility-necessitated junk. It has a ton of implicit assumptions and checks. (And a lot of other problems too).
Our implementation is simple. We don't build anything. Originally we only supported rpms that are a "bag of files". Later we consented to adding scriptles. But verifying them goes a step too far.
If the user supplies a bad script, I guess it will fail for them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, makes sense! What about going one step further and removing the -p/path/here
syntax entirely? The rpmpack API could be extended to either have an additional AddPostinInterpreter
(or AddPostinScriptletProgram
?) for every scriptlet. Or just one SetScriptletProgramFor(what, path string)
where what
is postin
, preun
etc. Either way, in tar2rpm
just a single flag for all scriptlet types in the form of
-interpreter_for "preun:/bin/foo"
could be added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes!
The -p
thing is messy.
I think SetScriptletProgramFor
makes sense. I'm not sure what you propose by the single flag - how will a user specify separate interpreters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
414daf4
to
d21e2ab
Compare
Relevant
|
rpm.go
Outdated
files: make(map[string]RPMFile), | ||
customTags: make(map[int]IndexEntry), | ||
customSigs: make(map[int]IndexEntry), | ||
scriptletInterpreter: DefaultScriptletInterpreter, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scriptletInterpreter
can be removed, it always references DefaultScriptletInterpreter
now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Now used again, so SetScriptletInterpreterFor()
is not overwritten by a later SetDefaultScriptletInterpreter()
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much for the contribution! And for your patience.
I think I spotted a surprising behavior when setting the overall interpreter (there is a comment there), but this points out the fact that there are no tests. Can you add unit tests for the basic behavior? (Adding an interpreter, setting content, etc)?
Bonus points (imaginary of-course) if you add an actual test that uses an rpm based linux, in the "example_bazel" folder. This is a bit harder, but take a look at the examples and see if something works for you. It's harder because you have to install bazelisk and docker to make it work, so I'll understand if you skip it.
/gcbrun
rpm.go
Outdated
interpreter string | ||
} | ||
|
||
type ScriptletType struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be exported? Or should it be scriptletType
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
rpm.go
Outdated
scriptlets map[string]Scriptlet | ||
} | ||
|
||
type Scriptlet struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not directly used by end users, so it should be unexported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
rpm.go
Outdated
} | ||
|
||
type Scriptlet struct { | ||
// User provided. If `interpreter` is empty, `DefaultScriptletInterpreter` is used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"User provided" confused me more then it helped. 90% of the values in the rpm struct can be said to be user provided one way or another. Remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
rpm.go
Outdated
// AddPretrans adds a pretrans scriptlet | ||
func (r *RPM) writeScriptlets(h *index) { | ||
for name, s := range r.scriptlets { | ||
if s.content != "" || s.interpreter != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer the inverse logic to reduce indentation (aka guard clause)
if s.content == "" && s.interpreter == "" {
continue
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
rpm.go
Outdated
if s.interpreter == "" { | ||
interpreter := r.scriptletInterpreter | ||
if tagInfo.lua { | ||
interpreter = "<lua>" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is <lua>
really how it's written in RPM? Crazy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's also lua macros in .spec files which are evaluated at rpm build time!
rpm.go
Outdated
if s.content != "" { | ||
h.Add(tagInfo.tag, EntryString(s.content)) | ||
} | ||
if s.interpreter == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the "positive" logic would easier to follow here. Also avoid the extra indentation by using an else if.
"if the interpreter is not empty, use it. Else if it's lua, then use lua. Else use the default"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, well, moved around. But removed the indentation.
rpm.go
Outdated
// all scriptlets, defaults to `/bin/sh`. Will reset anything set by `SetScriptletInterpreterFor()`. | ||
// An emtpy string resets to the default. pretrans and posttrans usually use <lua> and are skipped. | ||
// The scriptlets of an rpm can be checked via `rpm -qp --scripts RPMFILE`. | ||
func (r *RPM) SetScriptletInterpreter(interpreter string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm somewhat troubled by this new logic. Imagine this: Someone adds a preinstall script, and uses SetScriptletInterpreter
to use bash instead of sh.
AFAICT, this used to work as expected - only a preinstall script is added, and it uses bash. I think that with your new logic, this will set "interpreter" for all of the scripts, and will call them emptily for all of them.
Shouldn't this change the scriptletInterpreter value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, now using DefaultScriptletInterpreter
for that again. Also, now an explicit SetScriptletInterpreterFor()
has a higher precedence than a later SetDefaultScriptletInterpreter()
, and added Default
to the name.
rpm.go
Outdated
if !found { | ||
return errors.New(fmt.Sprintf("invalid scriptlet name %q", name)) | ||
} | ||
content := "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the magic of zero values in go to avoid someof this:
item := r.scriptlets[name]
item.interpreter = interpreter
r.scriptlets[name] = item
Should just work, correctly, no matter if the value existed before. Here, I tested it out for you:
https://go.dev/play/p/ehTutXq-MwD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Not sure I like this magic :)
_, found := scriptletsTypes[name] | ||
if !found { | ||
panic(fmt.Sprintf("internal error: invalid scriptlet name %q", name)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about zero value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
/gcbrun |
The default interpreter '/bin/sh' can be changed via `SetDefaultScriptletInterpreter()`, or on a per-scriptlet basis via `SetScriptletInterpreterFor()` We use the more familiar name `interpreter`, RPM calls it `scriptlet program`.
These usually do not fork interpreters, so do not override them when calling `SetDefaultScriptletInterpreter()`. Note that explicity setting /bin/sh also *seems* to work, and so does setting <lua> for other scriptlet. Use at your own risk! See <https://rpm-software-management.github.io/rpm/manual/lua.html>
Overriding the interpreter (defaults to /bin/sh) for all or a specific scriptlet is possible via `-interpreter` or `-interpreter_for NAME:INTERPRETER` where NAME is the type of scriptlet, one of: prein postin preun postun pretrans posttrans verifyscript, e.g. `prein:/bin/ksh` pretrans and posttrans, which should be lua code, are now handled. Their interpreter '<lua>' is not overwritten, unless -interpreter_for is used. Also added verifyscript support. When calling this during `rpm -V pkgname` only the stderr output will be printed. The scriptlets of an rpm can be checked via `rpm -qp --scripts RPMFILE`.
d21e2ab
to
a2cd292
Compare
Added, now
I already have bazelisk and docker, the bazel-go-docker workflow isn't quite clear to me. Can you provide a basic test which builds an rpm with scriptlets and makes side effects of installing, calling |
Overriding the interpreter (defaults to /bin/sh) for all or a specific
scriptlet is possible via
-interpreter
or-interpreter_for NAME:INTERPRETER
where NAME is the type of scriptlet, one of:
prein postin preun postun pretrans posttrans verifyscript, e.g.
prein:/bin/ksh
pretrans and posttrans, which should be lua code, are now handled. Their
interpreter '<lua>' is not overwritten, unless -interpreter_for is used.
Also added verifyscript support. When calling this during
rpm -V pkgname
only the stderr output will be printed.
The scriptlets of an rpm can be checked via
rpm -qp --scripts RPMFILE
.These usually do not fork interpreters, so do not override it when calling
SetScriptletInterpreter()
. Note that explicity setting /bin/sh also seemsto work, and so does setting for other scriptlet. Use at your own risk!
See https://rpm-software-management.github.io/rpm/manual/lua.html
The default interpreter '/bin/sh' can be changed via
SetScriptletInterpreter()
,or on a per-scriptlet basis via
SetScriptletInterpreterFor()
We use the more familiar name
interpreter
, RPM calls itscriptlet program
.