Skip to content
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

JENKINS-29144: Enable proper environment access for build steps #4766

Merged
merged 14 commits into from
Jun 13, 2020

Conversation

Zastai
Copy link
Contributor

@Zastai Zastai commented Jun 1, 2020

See JENKINS-29144.

This extends SimpleBuildStep with an overload of perform() that takes an EnvVars.
Uses default implementation of both (as is done in other places) to deal with both old-implementer-called-by-new-client and new-implemented-called-by-old-client.

This change on its own does very little, by design.
The main fix for the ticket would come from a corresponding change in CoreStep, in the workflow-basic-steps-plugin, to make that pass the EnvVars from the step context.

In particular, I did NOT make a change to BuildStepCompatibilityLayer or any other users of SimpleBuildStep.
This is mainly because they would then need to be made more step-aware (specifically: no expansions should be done when the environment comes from a pipeline step).
If desired, I could add such changes to this PR, but leaving things as they are preserves the current behaviour of those steps just fine.

I added a small test case, to check whether a step implementing the new API receives the expected environment; but that isn't much of a test (in a FreeStyle build it would have had access to it via the Run object already). The real test is in the pipeline context.

Downstream PR jenkinsci/workflow-basic-steps-plugin#116

Proposed changelog entries

  • JENKINS-29144, Plugins can now more easily add support for using build steps in pipelines with access to the appropriate environment variables (such as from tools/environment blocks or steps like withEnv).
  • The fingerprint and archiveArtifacts pipeline steps will no longer apply any environment substitution.

Proposed upgrade guidelines

N/A

Submitter checklist

  • (If applicable) Jira issue is well described
  • Changelog entries and upgrade guidelines are appropriate for the audience affected by the change (users or developer, depending on the change). Examples
    • Fill-in the Proposed changelog entries section only if there are breaking changes or other changes which may require extra steps from users during the upgrade Shouldn't that be the Proposed upgrade guidelines section?
  • Appropriate autotests or explanation to why this change has no tests
  • For dependency updates: links to external changelogs and, if possible, full diffs

Desired reviewers

@jglick

Maintainer checklist

Before the changes are marked as ready-for-merge:

  • There are at least 2 approvals for the pull request and no outstanding requests for change
  • Conversations in the pull request are over OR it is explicit that a reviewer does not block the change
  • Changelog entries in the PR title and/or Proposed changelog entries are correct
  • Proper changelog labels are set so that the changelog can be generated automatically
  • If the change needs additional upgrade steps from users, upgrade-guide-needed label is set and there is a Proposed upgrade guidelines section in the PR title. (example)
  • If it would make sense to backport the change to LTS, a Jira issue must exist, be a Bug or Improvement, and be labeled as lts-candidate to be considered (see query).

Provides a default implementation for both overloads:
- if the old method is called, it forwards to the new method, passing
  the environment specified for the Run
  - this means that when a new implementer (who has overridden the new
    overload) gets called by an old client, they get the run
    environment only (fine for freestyle, only the initial environment
    in a pipeline)
- if the new method is called, it forwards to the old method, ignoring
  the passed environment
  - in this case, the old method MUST be overridden
  - this handles the case of an old implementer getting called by a
    new client
    - no change in functionality; implementer still gets the same
      environment to work with (via the Run)

BuildStepCompatibilityLayer has _not_ been changed yet to call the new
overload, mainly because it would just get the environment to pass the
same way the default implementation in SimpleBuildStep does (i.e.
passing the result of calling getEnvironment() on the Run).

Implementers of SimpleBuildStep (like ArtifactArchiver and
Fingerprinter) similarly have _not_ been changed. This is mainly
because they get Run's environment to apply substitution, and that
should not be done using an environment passed in from a pipeline
step. So if they switch to the new signature, their code should also
be adjusted to only expand environment variables when the Run is an
AbstractBuild.

With this change, a PR can be made to workflow-basic-steps-plugin,
to extend CoreStep to pass in the environment variables from the
step context. The combination of both PRs should then fix
JENKINS-29144 and allow the creation of Builders that are
automatically available as pipeline steps with proper honoring of
things like withEnv and global tools.
This currently just checks for a slave-level environment variable;
not sure if that is enough.
Copy link
Member

@jglick jglick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did NOT make a change to BuildStepCompatibilityLayer or any other users of SimpleBuildStep.
This is mainly because they would then need to be made more step-aware (specifically: no expansions should be done when the environment comes from a pipeline step).

Not sure I follow. All you need to do is patch BuildStepCompatibilityLayer to call the nondeprecated overload, passing in Run.getEnvironment(TaskListener)---same behavior as now, just being explicit and avoiding an internal use of a deprecated API. This will only be used for traditional job types. CoreStep would directly call the new overload using its contextual EnvVars.

Zastai and others added 2 commits June 2, 2020 22:37
Co-authored-by: Jesse Glick <jglick@cloudbees.com>
@jglick
Copy link
Member

jglick commented Jun 2, 2020

If we are patching SimpleBuildStep it would be wise to consider JENKINS-46175. Sorry I did not find it earlier.

…new SimpleBuildStep API

Both will only use the environment for substitution when the Run being passed is an AbstractBuild, following the guidelines that for pipelines, the user does their own substitution in Groovy.
This causes a small behaviour change; before, even in pipelines, substitution would be done by these steps using the (tiny) initial environment.
@Zastai
Copy link
Contributor Author

Zastai commented Jun 2, 2020

I did NOT make a change to BuildStepCompatibilityLayer or any other users of SimpleBuildStep.
This is mainly because they would then need to be made more step-aware (specifically: no expansions should be done when the environment comes from a pipeline step).

Not sure I follow. All you need to do is patch BuildStepCompatibilityLayer to call the nondeprecated overload, passing in Run.getEnvironment(TaskListener)---same behavior as now, just being explicit and avoiding an internal use of a deprecated API. This will only be used for traditional job types. CoreStep would directly call the new overload using its contextual EnvVars.

I was more talking about Fingerprinter & co. For BSCL it is indeed a small patch, but given it would just be doing exactly what the default does, I left it out.
And the fact that deprecation warnings seem to be disabled, and replacing the deprecated @NonNull with the suggested @Nonnull (from javax.annotation) is actively prohibited, made me disinclined to make changes based only on deprecation.

But I've now pushed a commit that adds that change.

@jglick
Copy link
Member

jglick commented Jun 2, 2020

For BSCL it is indeed a small patch, but given it would just be doing exactly what the default does, I left it out.

Was just suggesting this on the principle that when you deprecate a method you should attempt to also replace all implementations and call sites within the same source tree, so you do not leave deprecation warnings around.

@Zastai
Copy link
Contributor Author

Zastai commented Jun 2, 2020

If we are patching SimpleBuildStep it would be wise to consider JENKINS-46175. Sorry I did not find it earlier.

Hmm. What would be best - adding overloads without the FilePath?

Adding a needsWorkspace() and/or needsEnvVars() to the Descriptor is great, but that would be tricky to check from the interface, for example.

@jglick
Copy link
Member

jglick commented Jun 2, 2020

replacing the deprecated @NonNull with the suggested @Nonnull (from javax.annotation) is actively prohibited

javax.annotation is actively prohibited. @NonNull is correct and is not deprecated.

@jglick
Copy link
Member

jglick commented Jun 2, 2020

What would be best - adding overloads without the FilePath?

I was thinking to make the FilePath and Launcher args @Nullable, with the descriptor specifying whether the actual behavior is @Nonnull (the only current option, and the future default if you do not implement the new descriptor interface) or @CheckForNull (step may be called with or without a node context).

I guess this could be done at another time. I am just trying to think through whether we would need to change the Java binary signature of SimpleBuildStep itself again, but I think we would not.

@jglick jglick added developer Changes which impact plugin developers plugin-api-changes Changes the API of Jenkins available for use in plugins. labels Jun 2, 2020
@Zastai
Copy link
Contributor Author

Zastai commented Jun 2, 2020

replacing the deprecated @NonNull with the suggested @Nonnull (from javax.annotation) is actively prohibited
javax.annotation is actively prohibited. @NonNull is correct and is not deprecated.

That's so weird - in my plugin I got deprecation warnings for them.
Looks like this is the joy of multiple maven packages providing the same classes.
In my plugin I ran into edu.umd.cs.findbugs.annotations.NonNull from com.google.code.findbugs:annotation:3.0.0, which is deprecated.
In Jenkins master, edu.umd.cs.findbugs.annotations.NonNull comes from com.github.spotbugs:4.0.1, which is not deprecated. Yay.

@Zastai
Copy link
Contributor Author

Zastai commented Jun 2, 2020

Changing the signature of the old API from NonNull to Nullable/CheckForNull feels like a breaking change.
I have no issue changing the filepath and launcher to nullable on the new API, with a nullcheck for the path that forwards to the old API.
Would that be needed for the envvars too? Or are those available everywhere in a declarative pipeline?

Copy link
Member

@jglick jglick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks right so far as it goes; now needs a matching PR in workflow-basic-steps-plugin.

core/src/main/java/jenkins/tasks/SimpleBuildStep.java Outdated Show resolved Hide resolved
test/src/test/java/jenkins/tasks/SimpleBuildStepTest.java Outdated Show resolved Hide resolved
test/src/test/java/jenkins/tasks/SimpleBuildStepTest.java Outdated Show resolved Hide resolved
test/src/test/java/jenkins/tasks/SimpleBuildStepTest.java Outdated Show resolved Hide resolved
@jglick
Copy link
Member

jglick commented Jun 2, 2020

in my plugin I got deprecation warnings for them

Because you are using an older jenkins.version in which we were using an older library.

@jglick
Copy link
Member

jglick commented Jun 2, 2020

Changing the signature of the old API from NonNull to Nullable/CheckForNull feels like a breaking change.

Changing from @NonNull to @Nullable is compatible for implementers I think, since we would not actually be passing null unless that same plugin opted in to the mode in which it might be passed. CoreStep / CoreWrapperStep would check at runtime if they were called outside a node context yet had a delegate which did not tolerate that, and throw MissingContextVariableException as needed.

@jglick
Copy link
Member

jglick commented Jun 2, 2020

Would that be needed for the envvars too? Or are those available everywhere in a declarative pipeline?

EnvVars should be available in any context, though outside a node context there are certainly a lot fewer of them.

@daniel-beck daniel-beck self-requested a review June 2, 2020 21:16
@Zastai
Copy link
Contributor Author

Zastai commented Jun 2, 2020

I've left out the nullability changes for now. Mainly because the two steps in core both need the workspace, so I would have to also add the descriptor portion. Because SimpleBuildStep is just an interface, there is no SimpleBuildStepDescriptor in the hierarchy to hang anything on. And I'm not sure that adding a SimpleBuildStepDescriptor interface would be ideal. Feels like a separate PR, albeit one I'd be willing to look at after this one.

Copy link
Member

@jglick jglick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(conditional on approved downstream PR)

@varyvol
Copy link

varyvol commented Jun 4, 2020

Approved but waiting for the downstream in workflow-basic-steps-api.

@Zastai
Copy link
Contributor Author

Zastai commented Jun 4, 2020

You should be able to download the Jenkins war file from the build artifacts if you need it.

I've opened INFRA-2628 to report the issue with that incrementals publisher script (at least when run on Windows).

As I understand it, to create the downstream PR, I need to set its pom.xml to refer to the incremental produced by this PR. I can get around that locally, but without the incremental deployed to the repository, any PR I make would fail to build, would it not?
I'll keep an eye on that INFRA ticket and force a rebuild as and when it's fixed.

@jglick jglick added the on-hold This pull request depends on another event/release, and it cannot be merged right now label Jun 4, 2020
Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Indeed a reference implementation in a plugin would be nice, but IMHO internal implementations already justify the change. E.g. Archiver's support for variable expansion can be considered a minor improvement on its own.

BTW, is there a risk that the introduced variable expansion leads to regressions when variable macros are present in the strings but not supposed to be resolved? Not a blocker even in this case IMHO

@Zastai
Copy link
Contributor Author

Zastai commented Jun 5, 2020

Looks good to me. Indeed a reference implementation in a plugin would be nice, but IMHO internal implementations already justify the change. E.g. Archiver's support for variable expansion can be considered a minor improvement on its own.

BTW, is there a risk that the introduced variable expansion leads to regressions when variable macros are present in the strings but not supposed to be resolved? Not a blocker even in this case IMHO

Actually, ArtifactArchiver and Fingerprinter have almost no functional change with this PR. Essentially it causes them to stop expanding the (very small set of) initial environment variables when used in pipelines. In freestyle jobs they retain the full expansion they already had.

But to close the actual ticket, the change to CoreStep in workflow-basic-steps-plugin is required. Because it will be CoreStep that actually passes the EnvVars in when the SimpleBuildStep is used in a pipeline.

Zastai added a commit to Zastai/workflow-basic-steps-plugin that referenced this pull request Jun 7, 2020
Builds on jenkinsci/jenkins#4766 (incremental version 2.240-rc30032.5a9c198e2a33).

Note: I'm getting test failures from CatchErrorStepTest.optionalMessage(), but that doesn't involve CoreStep, so is probably caused by something else in current Jenkins master.
@jglick jglick removed the on-hold This pull request depends on another event/release, and it cannot be merged right now label Jun 8, 2020
@oleg-nenashev oleg-nenashev added the ready-for-merge The PR is ready to go, and it will be merged soon if there is no negative feedback label Jun 9, 2020
@oleg-nenashev
Copy link
Member

Essentially it causes them to stop expanding the (very small set of) initial environment variables when used in pipelines. In freestyle jobs they retain the full expansion they already had.

It would be great to document it in the changelog Would be great to get confirmation from @dwnusbaum

But to close the actual ticket, the change to CoreStep in workflow-basic-steps-plugin is required.

Do we also need to release workflow-basic-steps-plugin before this change lands in Weekly?

@Zastai
Copy link
Contributor Author

Zastai commented Jun 9, 2020

Changelog entry added.
I don't think we can release the plugin without having the weekly of core (the plugin currently references an incremental).

@oleg-nenashev
Copy link
Member

I don't think we can release the plugin without having the weekly of core (the plugin currently references an incremental).

If a change is done in a compatible way, we can. If I understand the required fix correctly, default void perform(@NonNull Run<?, ?> run, @NonNull FilePath workspace, @NonNull EnvVars env, @NonNull Launcher launcher, @NonNull TaskListener listener) throws InterruptedException, IOException { could be added as a second method there without and @Override annotation, and in such case the plugin will be able to be released before the core is actually out, and it will support both old a new versions.

@Zastai
Copy link
Contributor Author

Zastai commented Jun 9, 2020

Well no, I don't think so - CoreStep needs to call, not implement, that method. It wraps a non-Step (i.e. a Builder or Publisher) to make it usable in a pipeline.
Plugins implementing SimpleBuildStep don't need changes, and can indeed incrementally adjust their code if they want access to environment variables.

@jglick
Copy link
Member

jglick commented Jun 9, 2020

It would be great to document it in the changelog. Would be great to get confirmation

The behavioral change to fingerprint / archiveArtifacts seems appropriate to me. Any variable expansion from Pipeline syntax would have been unintentional and unwanted.

I don't think we can release the plugin without having the weekly of core (the plugin currently references an incremental).

Currently it actually is possible to release the plugin with an incremental core dep (pending a new Enforcer rule), but anyway I think there is no rush—the plugin release is only needed when someone first writes and publishes a SimpleBuildStep which implements only the new overload and actually uses environment variables in Pipeline mode.

@oleg-nenashev
Copy link
Member

Based on the feedback, we may merge it in 24 hours if there is no negative feedback. Please see the merge process documentation for more information about the merge process

@daniel-beck
Copy link
Member

jenkinsci/workflow-basic-steps-plugin#116 seems to be the downstream PR. This should have been in the PR comment. Per PR template:

<!-- For new API and extension points: Link to the reference implementation in open-source (or example in Javadoc) -->

@timja timja added the squash-merge-me Unclean or useless commit history, should be merged only with squash-merge label Jun 13, 2020
@timja timja merged commit 302159c into jenkinsci:master Jun 13, 2020
@Zastai Zastai deleted the JENKINS-29144 branch June 13, 2020 12:51
@Zastai
Copy link
Contributor Author

Zastai commented Jun 13, 2020

jenkinsci/workflow-basic-steps-plugin#116 seems to be the downstream PR. This should have been in the PR comment. Per PR template:

You're right - at the time of submission that did not exist yet, but I did indeed fail to go back and add it.

// If this is called, this must be an implementer of the previous API, in which case we call that, discarding
// the environment we were given.
// But for that to work, that API method must have been implemented.
if (Util.isOverridden(SimpleBuildStep.class, this.getClass(),
Copy link
Member

@KostyaSha KostyaSha Jul 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jglick @oleg-nenashev this requires perform method not to be final and breaks backward compatibility with uber-archive/phabricator-jenkins-plugin#344

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Zastai certainly not intentional. JENKINS-62723

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect #1804 was incorrect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
developer Changes which impact plugin developers plugin-api-changes Changes the API of Jenkins available for use in plugins. ready-for-merge The PR is ready to go, and it will be merged soon if there is no negative feedback squash-merge-me Unclean or useless commit history, should be merged only with squash-merge
Projects
None yet
9 participants