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

Defer evaluation of a step's DisplayName until its condition is evaluated. #2313

Merged
merged 7 commits into from
Feb 7, 2023

Conversation

jww3
Copy link
Contributor

@jww3 jww3 commented Dec 13, 2022

Proposed fix for #2309

This fix relocates the logic for expanding a step's display name.

In the previous implementation, the display name was evaluated as part of step execution. It was therefore possible for display names to remain unevaluated for steps that ended up being skipped.

With this change, we now evaluate the display name right before we evaluate the step's condition.

In this way, the display name gets updated even if the condition check results in the step being skipped.

Workflow used to Test

name: Issue 2309 Test
on:
  workflow_dispatch:
jobs:   
  skippedstep:
    env:
      NPM_PACKAGE_ORG: "random-name"
    strategy:
      matrix:
        variation: ["aaa","bbb"]
    runs-on: self-hosted
    steps:
      - name: Step 1 -- Actual = ${{ github.actor }}; Expected = jww3 
        run: echo "this is expanded"
        if: false
      - name: Step 2 -- Actual = ${{ env.NPM_PACKAGE_ORG }}; Expected = random-name
        run: echo "I'll be expanded"
      - name: Step 3 (fix) -- Actual = ${{ env.NPM_PACKAGE_ORG }}; Expected = random-name
        if: false
        run: echo "I'll not be expanded"
      - name: Step 4 -- Actual = ${{ matrix.variation }}; Expected = aaa or bbb
        run: echo "am expanded as well"
        if: false

source: https://github.com/bbq-beets/jww3-2022a/blob/main/.github/workflows/verify-fix-2309.yml

Before

image

After

image

@jww3 jww3 requested a review from a team as a code owner December 13, 2022 18:14
@AvaStancu
Copy link
Contributor

AvaStancu commented Dec 15, 2022

Congrats on one of your very first PRs as part of the runtime team 👏 Very clear issue descriptions also 👏 👏 👏
Would it be possible to extract your implementation to a separate method and add some unit tests for it?

@jww3
Copy link
Contributor Author

jww3 commented Dec 16, 2022

Congrats on one of your very first PRs as part of the runtime team 👏 Very clear issue descriptions also 👏 👏 👏 Would it be possible to extract your implementation to a separate method and add some unit tests for it?

Thanks! I spent a couple hours trying to devise a unit test for this given our unit testing framework and had to give up. Thomas and I both looked at it. As best as I can tell, there's no great way in our unit testing framework to exercise this code path without extensive mocking -- that is, mocking to the point where you're not really proving anything meaningful.

jww3 added a commit that referenced this pull request Dec 16, 2022
if (_didFullyEvaluateDisplayName || !string.IsNullOrEmpty(Action.DisplayName))
{
return false;
return true;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a subtle change in behavior, but fortunately there weren't many existing callers (mainly just unit tests) and even fewer existing callers that even bothered checking the return value

Copy link
Contributor

Choose a reason for hiding this comment

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

The return value is IsDisplayNameSet, and the out var says IsUpdated[JustNow].

Why do we need both? 🤔 Is it to avoid setting it again in StepsRunner.TryUpdateDisplayName()?

Copy link
Contributor Author

@jww3 jww3 Jan 23, 2023

Choose a reason for hiding this comment

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

Consider code that invokes EvaluateDisplayName multiple times and updates the TimelineRecord based on whether the call "succeeded" or not.

Example:

if (EvaluateDisplayName(this.ExecutionContext.ExpressionValues, this.ExecutionContext)) 
{
   this.ExecutionContext.UpdateTimelineRecordDisplayName(this.DisplayName);
}
.
.
.
if (EvaluateDisplayName(this.ExecutionContext.ExpressionValues, this.ExecutionContext)) 
{
   this.ExecutionContext.UpdateTimelineRecordDisplayName(this.DisplayName);
}

Which TimelineRecord update is accurate and which one is (at best) redundant?

src/Runner.Worker/StepsRunner.cs Outdated Show resolved Hide resolved
Name = "action",
Id = actionId,
DisplayName = explicitDisplayName,
DisplayNameToken = new BasicExpressionToken(null, null, null, "matrix.node"),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

in this unit test, we're setting both DisplayName and DisplayNameToken.

AvaStancu
AvaStancu previously approved these changes Jan 5, 2023
// can make reasonable guarantees that they won't throw an exception.
try
{
if (this.Stage == ActionRunStage.Main && EvaluateDisplayName(this.ExecutionContext.ExpressionValues, this.ExecutionContext, out updated))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add a comment to clarify why we only attempt this on Stage == ActionRunStage.Main

Copy link
Collaborator

Choose a reason for hiding this comment

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

Its been a while but I believe a job consists of the following stages

  • Job started
    • We try to evaluate all display names here
  • PreJobSteps
    • No contexts have changed from the above, so no need to retry if we couldn't fully expand
  • Main Steps
    • Context may now have changed, lets update the display names before we start the steps
  • Post Job Steps
    • Name should have already been fully expanded, no need to try again.
  • Job complete
    • Nothing to do, job is done don't evaluate display names

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you!

fhammerl
fhammerl previously approved these changes Feb 6, 2023
Copy link
Contributor

@fhammerl fhammerl left a comment

Choose a reason for hiding this comment

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

LGTM

@jww3
Copy link
Contributor Author

jww3 commented Feb 6, 2023

Fix verification with latest changes:

image

Copy link
Contributor

@fhammerl fhammerl left a comment

Choose a reason for hiding this comment

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

LGTM

@jww3 jww3 changed the title Defer evaluation of a step's DislayName until its condition is evaluated. Defer evaluation of a step's DisplayName until its condition is evaluated. Feb 7, 2023
@jww3 jww3 merged commit 9a228e5 into main Feb 7, 2023
@jww3 jww3 deleted the users/jww3/jit-displayname-evaluation branch February 7, 2023 10:42
ashb pushed a commit to ashb/runner that referenced this pull request Feb 7, 2023
…ated. (actions#2313)

* Defer evaluation of a step's DisplayName until its condition is evaluated.
* Formalize TryUpdateDisplayName and EvaluateDisplayName as members of interface `IStep` (actions#2374)
nikola-jokic pushed a commit to nikola-jokic/runner that referenced this pull request May 12, 2023
…ated. (actions#2313)

* Defer evaluation of a step's DisplayName until its condition is evaluated.
* Formalize TryUpdateDisplayName and EvaluateDisplayName as members of interface `IStep` (actions#2374)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants