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

GAME_CI_BUILD_SUCCESS and GAME_CI_STEP_SUMMARY #576

Conversation

tobyspark
Copy link
Contributor

@tobyspark tobyspark commented Sep 21, 2023

Changes

Seemingly a breaking change in v2 → v3 is the action reporting error depending on the parsing of Unity build results in the log. Not all Unity builds invoke a player build and so this output cannot be expected. Examples of people hitting this issue can be readily found in the issues tab. The discussion in #563 resulted in the user faking these results in order to be able to use the v3 action. Better would be an explicit success message Game CI could check for in these circumstances.

Further, there is an issue that the Unity build method cannot output step summaries, as the required file is on the host machine not docker container where Unity is running. As there is already code parsing the log output, adding a Game CI Step Summary token alongside the success token would be a conceptual fit and technically straightforward.

This PR adds the following

  • If the existing parsing for Unity build results passes, or if a new check for the presence of GAME_CI_BUILD_SUCCESS passes, then
  • All log lines starting with GAME_CI_STEP_SUMMARY are set as the step summary and the action completes
  • If neither parsing tests pass, an error is thrown that notifies the user that neither the build results were found nor GAME_CI_BUILD_SUCCESS

This expands relevant functionality (if you’re using a custom build method there’s a good chance you might have reason to want to summarise what happened in a successful build) and gives actionable information that wasn’t present before in the fail case.

Checklist

  • Read the contribution guide and accept the
    code of conduct
  • Docs (If new inputs or outputs have been added or changes to behavior that should be documented. Please make
    a PR in the documentation repo)
  • Readme (updated or not needed)
  • Tests (added, updated or not needed)

Related issues

@github-actions
Copy link

Cat Gif

}

// Check for presence of game-ci message(s)
const gameciMatch = result.stdout.match(/^GAME_CI_BUILD_SUCCESS$/ms);

Choose a reason for hiding this comment

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

I really like the idea of having a very simple message to check for. We should also document this to make sure, people using this for non-build stuff are aware.

Additionally, I'm wondering if there could be use cases, where the rather explicit GAME_CI_BUILD_SUCCESS should have a higher priority than Build results (You want to have the job succeed even if the Build results contain an error).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See comment #576 (comment)

const errorMatch = buildResults.match(/^Errors:\s*(\d+)$/m);
if (errorMatch && Number.parseInt(errorMatch[1], 10) !== 0) {
throw new Error(`There was an error building the project. Please read the logs for details.`);
}
} else {
throw new Error(`There was an error building the project. Please read the logs for details.`);
await summary.addHeading('Build Results').addQuote(unityMatch[0]).write();

Choose a reason for hiding this comment

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

Should this be logged before the exception? Otherwise you wouldn't see how many errors/exceptions there are, if you have any.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it should.

@tobyspark
Copy link
Contributor Author

tobyspark commented Oct 9, 2023

Having looked at this a bit more (prompted by the documentation part of #574), I wonder: why have this post-run parsing at all? To my understanding what’s happening is this

  • The default C# build method runs. In this, the Unity build command is actioned and its results are parsed into
    • a log message detailing amongst other things error count
    • an exit code that is a straight mapping of Unity's build command success status.
  • At some point, a decision was made that any errors from the build should also fail the build.
  • The implementation to handle the above was made after the Unity part completes, by parsing the logs from javascript.
  • At some point, people start to wonder why their builds are failing despite seeing “success” (i.e. exit code 0). This may be people using the default build method used to the pre-check behaviour (whose builds produce errors that can be tolerated), or people not using the default build method.

What I didn’t realise when fixing this behaviour for my custom build method, which pre-dated knowing about GameCI and was written from scratch, was that the default build method is GameCI’s, not Unity boiler plate. Or at least, even if it is Unity boiler plate, the example referenced in the documentation is hosted on GameCI and so under our control.

So why not adapt the GameCI C# build method? This has the following advantages

  • Keeps process exit code the canonical success metric
  • Keeps the build result producing code and success determining code in one place

There may of course be a reason why this wasn’t done, please holler if so.

What would remain is having some kind of flag in the workflow spec for the default build method that specifies whether errors should fail an otherwise successful build.

To do so, I see the neatly factored ExitWithResult method. The code sketch would be something like

            switch (result)
            {
                case BuildResult.Succeeded:
                    Console.WriteLine("Build succeeded!”);
                    if (BuildSummary.totalErrors > 0 && FailOnError)
                    {
                        EditorApplication.Exit(1);
                        break;
                    }
                    EditorApplication.Exit(0);
                    break;

@tobyspark
Copy link
Contributor Author

tobyspark commented Oct 9, 2023

One clarification – GAME_CI_STEP_SUMMARY is still wanted! So the javascript parsing would still be there, just not futzing with the exit code.

@tobyspark
Copy link
Contributor Author

tobyspark commented Oct 9, 2023

One further thought – though, doing EditorApplication.Exit(0) in the success case is slightly odd given it’s run with -quit, where it’s expected that the process quits when the method completes.

@JohannesDeml
Copy link

What would remain is having some kind of flag in the workflow spec for the default build method that specifies whether errors should fail an otherwise successful build.

I think moving this behavior from the build parameters to the c# script would be a very nice move to make this logic more understandable. As an end user I do look through the script (and also adapted it already), but haven't up to that point cared too much at what happens in the typescript scripts. This change would help for more transparency, while making the api less bloated.

One further thought – though, doing EditorApplication.Exit(0) in the success case is slightly odd given it’s run with -quit, where it’s expected that the process quits when the method completes.

I would prefer to keep it that way, since it is not breaking anything and in the meantime allows the default script to also work with manualExit set to true.

@tobyspark
Copy link
Contributor Author

Thanks. The plan, then, would be this?

  • Add a ‘with’ parameter to the action spec for ‘fail on error’
  • Modify unity-builder/dist/default-build-script/Assets/Editor/UnityBuilderAction along the lines of the code sketch
  • Remove GAME_CI_SUCCESS and the now-redundant success handling code in src/model/exec-with-error-check.ts

I’ll do that with an OK.

@webbertakken
Copy link
Member

Thanks. The plan, then, would be this?

  • Add a ‘with’ parameter to the action spec for ‘fail on error’
  • Modify unity-builder/dist/default-build-script/Assets/Editor/UnityBuilderAction along the lines of the code sketch
  • Remove GAME_CI_SUCCESS and the now-redundant success handling code in src/model/exec-with-error-check.ts

I’ll do that with an OK.

@davidmfinol @AndrewKahr @frostebite @GabLeRoux @cloudymax any thoughts or comments?

@GabLeRoux
Copy link
Member

GabLeRoux commented Oct 10, 2023

I don't have much free time this week, but I will look into this as there are quite a lot of related issues, I saw a few related help threads on discord as well:

I've updated description to point to a few issues that this should help closing and will look into details later this week 👍
Thanks for taking care of this <3

We definitely need to improve on error reporting. Something I had in mind would be to expose the errors found in the logs as warning instead of failing the build directly.

@webbertakken
Copy link
Member

If you can take care of this in a quality way we'll try to support this and get it in after you've made the changes. We do need this to be tested for the different scenarios.

@wilg
Copy link

wilg commented Nov 3, 2023

I'd really like to be able to opt out of the behavior. In my case, Unity outputs an error that I cannot work around, but the build is otherwise successful. A configuration option would be simple enough, I think. Trusting Unity's exit code should be sufficient for many cases.

@AndrewKahr
Copy link
Member

Just wanted to tag in some context on why this got added. What we noticed was on WIndows, the error codes didn't seem to be propagating out correctly and thus failed builds would still cause the workflows to appear as succeeded. So this parsing was added to check for errors so that it would fail the workflow correctly. The assumption we operated on was if there was an error, the build was probably bad as I had seen builds get made but had an Error and while it opened, there would be stuff wrong with it. But perhaps that was too broad an assumption. So what I see as the options would be to either ensure that the exit code actually works across all 3 platforms or the plan stated above. Though, with regards to the plan stated above, if you set it to not fail on error, how does it know to throw an error if the build actually fails to stop the pipeline?

But definitely open to getting a solution to this. We're gearing up for v4 of the actions so this is probably an ideal time to get something big like this into the release.

@tobyspark
Copy link
Contributor Author

tobyspark commented Nov 3, 2023

@AndrewKahr To be clear, where you say “error codes didn’t seem to be propagating out correctly”, do you mean 1. a success process exit code happened when Unity’s build method had returned a report containing errors, or 2. a failure process exit code happened but that somehow wasn’t propagating and the e.g. Javascript action code thought it was a success exit code?

If 1., then the error-parsing is not being removed, just the behaviour being straightened-out technically and to not make assumptions about custom build methods. If 2., then that’s new to me and... hmm. There’s still the base need that motivated this in the first place, but the right solution may take a bit more thought.

All – I was away, but am back now and will get to this.

@AndrewKahr
Copy link
Member

To be honest, I'm not sure exactly what was happening. All I know for certain is Unity would fail to build something, which on Linux would produce a nonzero exit code and then the pipeline would fail out, but on windows it would continue as if it succeeded tho I'm not sure if it failed to produce an error code or if it failed to propagate it out. So you might need to do some debugging. If you use Unity-Builder@main it will use the new images and build scripts which might have different behavior as well.

@wilg
Copy link

wilg commented Nov 3, 2023

It would be 🔥 if the build errors were surfaced to the GitHub Action via the annotations feature: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions

Right now you just get this:
image

Seems like if they're available to the script doing this work either in C# or Shell it would be relatively straightforward to emit them so you don't have to dig through the enormous log file usually.

@tobyspark
Copy link
Contributor Author

It would be 🔥 if the build errors were surfaced to the GitHub Action via the annotations feature: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions

That kind of thinking is what’s behind GAME_CI_STEP_SUMMARY (and parsing the logs for them in js is required, unless there’s something I’ve missed; see original post).

  • Add a ‘with’ parameter to the action spec for ‘fail on error’ (default: false)
  • Modify unity-builder/dist/default-build-script/Assets/Editor/UnityBuilderAction along the lines of the code sketch
  • Retain GAME_CI_STEP_SUMMARY and use it in the UnityBuilderAction to report errors
  • Remove GAME_CI_SUCCESS and the now-redundant success handling code in src/model/exec-with-error-check.ts

@AndrewKahr
Copy link
Member

Rather than parse, might be worth having it be part of the default build script in Unity so it can tap into the Unity log system and know exactly what is an error/warning. Found this thread that might help: https://discussions.unity.com/t/detect-compilation-errors-in-editor-script/74474/4

I've previously thought about how to parse for errors and there's just so many ways it can throw an error that isn't immediately obvious and creating a parser for all of them would be practically impossible. Certain errors are definitely easier to see as it might say error: or exception, but other times a plugin can throw an error but not explicitly have the word error in it (ie file not found or unable to do xyz). And sometimes it will print messages that sound like errors but actually aren't. So I would highly advise not going down the parsing route due to the infinite possibilities for what is/isn't an error and leverage Unity as much as possible if this is a desired feature.

@tobyspark
Copy link
Contributor Author

I think you missed my point about the parsing. The problem I hit is once discovered how to get annotations out of Unity code and into the GitHub Action itself. The problem is that Unity is running inside a docker container, so the method for scripts to communicate annotations won’t work (writing to host file system at a location provided by env var). Hence parsing the log output for GAME_CI_STEP_SUMMARY. Not parsing the logs for errors themselves.

Your goal is good, this is a technical detour on how to get there.

Disclaimer – I’ve only used my own custom build method, so all I currently know about the Game CI build method is what I found by looking for error code handling. So there’s some discovery I’ll do.

@AndrewKahr
Copy link
Member

AndrewKahr commented Nov 4, 2023

Ah apologies I misunderstood the goal.

From what I'm reading, it doesn't look like annotations need the env files do they? For example this is their code for error:

- name: Create annotation for build error
  run: echo "::error file=app.js,line=1::Missing semicolon"

If as long as we print out in that format via either debug.log or calling echo from c# code, is that enough to get it to annotate or am I missing something?

If we just need to be able to access the GitHub env and output files, from what I understand those are just files and the environment variable is a path to it. If in the docker.ts file where we mount relevant files/folders, what if we just mount the env and output files so the container can access it? And add those env variables to the list passed into the container so we know where to look since the file names are dynamic. Then you in theory can do everything in the container right?

@tobyspark
Copy link
Contributor Author

I expected that kind of “transparent container” experience when I first used Game CI, e.g. I was surprised environment variables set in the workflow step were not available within Unity. But on suggesting that, was firmly rebuffed on design grounds. That’s not my argument to make, what I’m drawing on is here. But I have now sympathy to the idea to just saying no, rather than special casing an increasing list of things, every time someone gets confused.

There’s a decision here. What I’ve got already implemented for annotations is a small tweak to the existing code here. But that whole log parsing step could be eliminated by configuring the docker container (assuming the paths are mappable, and maintaining it all when GitHub change things).

Caveat – assuming we don’t need to parse the logs for errors as per Andrew. i.e. that the Unity build results object and exit codes are sufficient

@tobyspark
Copy link
Contributor Author

tobyspark commented Nov 6, 2023

Procedural note – this is where splitting a PR would make sense, so one piece of functionality isn’t delayed by another (-failOnError and GAME_CI_STEP_SUMMARY). But they are connected by needing-or-not the log parsing.

@AndrewKahr
Copy link
Member

I had a look at the thread you mentioned and the key idea there was the environment variables automatically passing through. What I am talking about is adding the GITHUB_ENV and GITHUB_OUTPUT variables to the image environment factory so they get passed through to the container and add those paths as paths to get mounted in the docker.ts file. Then the container should be able to access it.

I also did an experiment yesterday and was able to create annotations with just Debug.Log and the console listener code provided in the forum link I sent earlier, I didn't even need to pass in the GitHub variables to the container.

@webbertakken
Copy link
Member

webbertakken commented Nov 6, 2023

Probably useful to also make this work out of the box for people who use their own custom script. We either need a clear snippet to add to people's script or take the parsing out of the container. In the case of the latter it would be better to run it from JS and not bash, as we're trying to get rid of as much low level scripts as possible.

Personally I think we shouldn't add this to the custom build script but in the JS logic, unless parsing proves to much more cumbersome than doing it directly during the build.

Passing the information from the build script to outside the container might be a fair option as well.

@AndrewKahr
Copy link
Member

I think it makes the most sense for the annotations to be done via build script in Unity than via the js action. The build script option is only 20 lines in a script the user needs to drop into their project (or automatically get it if they use the built in default build script). If we do it via js, we'd need to manage an endless amount of parsing formats to catch variations of warnings/errors and will probably be a long term pain as we aren't depending on an API which should remain stable long term. We could technically figure out a way to pass the info out of the container to the js, but I think that is a lot of unnecessary complexity and means we need to create and maintain our own API to pass stuff out rather than just letting people implement against APIs for their own CI systems.

@webbertakken
Copy link
Member

Fair enough, sounds good.

@AndrewKahr
Copy link
Member

Just wanted to give a heads up, I've merged some fixes for license activation issues in addition to annotations for errors/warnings so make sure to merge main into your branch. That should be a nice baseline to work off of for the annotations your are looking to do.

@AndrewKahr AndrewKahr mentioned this pull request Nov 15, 2023
4 tasks
@AndrewKahr
Copy link
Member

I was thinking a bit more about the exit code on Windows and did a deep dive into the issue and I think I've solved it so exit codes appear to be flowing out now. Thus this PR probably isn't needed anymore as the logic for build error is back to checking for a 0 error code which aligns with unity-builderv2. We do really appreciate the work that was put into this PR though, it ultimately has led to some really nice QOL improvements so it really is a net positive overall. After we get the changes released and it's clear the issue is in fact solved, we will close this PR but in the meantime, we can keep it open. If there's something not covered by the other PR that this one improves on though, definitely happy to merge those other improvements.

@tobyspark
Copy link
Contributor Author

That all sounds great. Relying once more on exit codes being canonical is both how it should be and solves my need. And selfishly, I’m happy you did that Windows deep dive, as it isn’t my speciality; I was concerned about custom build methods. (And hadn’t picked this back up yet as I’ve been on the cusp of getting back to my CI task for work, but emphasis cusp there)

Being able to output GitHub annotations without any Game CI special casing is also... how it should be. Though some documentation will be required to say while the docs give these options, only do it this way. Given at least I read their docs and tried a method that doesn’t work.

@AndrewKahr
Copy link
Member

Closing this now as it looks like the fixes for v4 should've solved the build issues

@AndrewKahr AndrewKahr closed this Nov 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
6 participants