Skip to content

Commit

Permalink
Factor workunit failure into final exit code.
Browse files Browse the repository at this point in the history
Previously if a workunit fails, the run will end with Pants printing `FAILURE`, but the exit code is 0.

This change factors the result of workunit into the final exit code as well.

Before:
```bash
$ ./pants --pythonpath=tests/python --backend-packages=pants_test.goal.data do-some-work

23:41:53 00:00 [main]
               (To run a reporting server: ./pants server)
23:41:53 00:00   [setup]
23:41:53 00:00     [parse]
               Executing tasks in goals: bootstrap -> do-some-work
23:41:54 00:01   [bootstrap]
23:41:54 00:01     [substitute-aliased-targets]
23:41:54 00:01     [jar-dependency-management]
23:41:54 00:01     [bootstrap-jvm-tools]
23:41:54 00:01     [provide-tools-jar]
23:41:54 00:01   [do-some-work]
23:41:54 00:01     [do-some-work]
23:41:54 00:01       [non.existent.main.class]
                     ==== stderr ====
                     java.lang.ClassNotFoundException: non.existent.main.class
                     	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
                     	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
                     	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
                     	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
                     	at java.lang.Class.forName0(Native Method)
                     	at java.lang.Class.forName(Class.java:264)
                     	at com.martiansoftware.nailgun.NGSession.run(NGSession.java:242)

                     ==== stdout ====

23:41:54 00:01   [complete]
               FAILURE

$ echo $?
0
```

After:
```bash
$ ./pants --pythonpath=tests/python --backend-packages=pants_test.goal.data do-some-work

23:41:53 00:00 [main]
               (To run a reporting server: ./pants server)
23:41:53 00:00   [setup]
23:41:53 00:00     [parse]
               Executing tasks in goals: bootstrap -> do-some-work
23:41:54 00:01   [bootstrap]
23:41:54 00:01     [substitute-aliased-targets]
23:41:54 00:01     [jar-dependency-management]
23:41:54 00:01     [bootstrap-jvm-tools]
23:41:54 00:01     [provide-tools-jar]
23:41:54 00:01   [do-some-work]
23:41:54 00:01     [do-some-work]
23:41:54 00:01       [non.existent.main.class]
                     ==== stderr ====
                     java.lang.ClassNotFoundException: non.existent.main.class
                     	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
                     	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
                     	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
                     	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
                     	at java.lang.Class.forName0(Native Method)
                     	at java.lang.Class.forName(Class.java:264)
                     	at com.martiansoftware.nailgun.NGSession.run(NGSession.java:242)

                     ==== stdout ====

23:41:54 00:01   [complete]
               FAILURE

$ echo $?
1
```

Testing Done:
https://travis-ci.org/pantsbuild/pants/builds/161491405

Bugs closed: 3882

Reviewed at https://rbcommons.com/s/twitter/r/4244/
  • Loading branch information
wisechengyi committed Sep 23, 2016
1 parent 60da6a7 commit b30acb6
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 5 deletions.
8 changes: 5 additions & 3 deletions src/python/pants/bin/local_pants_runner.py
Expand Up @@ -92,12 +92,14 @@ def _run(self):
self._daemon_build_graph,
self._exiter).setup()

result = goal_runner.run()
goal_runner_result = goal_runner.run()

if repro:
# TODO: Have Repro capture the 'after' state (as a diff) as well?
repro.log_location_of_repro_file()
finally:
run_tracker.end()
run_tracker_result = run_tracker.end()

self._exiter.exit(result)
# Take the exit code with higher abs value in case of negative values.
final_exit_code = goal_runner_result if abs(goal_runner_result) > abs(run_tracker_result) else run_tracker_result
self._exiter.exit(final_exit_code)
8 changes: 6 additions & 2 deletions src/python/pants/goal/run_tracker.py
Expand Up @@ -86,8 +86,8 @@ def __init__(self, *args, **kwargs):
# run_id is safe for use in paths.
millis = int((run_timestamp * 1000) % 1000)
run_id = 'pants_run_{}_{}_{}'.format(
time.strftime('%Y_%m_%d_%H_%M_%S', time.localtime(run_timestamp)), millis,
uuid.uuid4().hex)
time.strftime('%Y_%m_%d_%H_%M_%S', time.localtime(run_timestamp)), millis,
uuid.uuid4().hex)

info_dir = os.path.join(self.get_options().pants_workdir, self.options_scope)
self.run_info_dir = os.path.join(info_dir, run_id)
Expand Down Expand Up @@ -317,6 +317,8 @@ def end(self):
"""This pants run is over, so stop tracking it.
Note: If end() has been called once, subsequent calls are no-ops.
:return: 0 for success, 1 for failure.
"""
if self._background_worker_pool:
if self._aborted:
Expand Down Expand Up @@ -349,6 +351,8 @@ def end(self):
self.report.close()
self.store_stats()

return 1 if outcome in [WorkUnit.FAILURE, WorkUnit.ABORTED] else 0

def end_workunit(self, workunit):
self.report.end_workunit(workunit)
path, duration, self_time, is_tool = workunit.end()
Expand Down
Empty file.
28 changes: 28 additions & 0 deletions tests/python/pants_test/goal/data/register.py
@@ -0,0 +1,28 @@
# coding=utf-8
# Copyright 2016 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
unicode_literals, with_statement)

from pants.backend.jvm.tasks.nailgun_task import NailgunTask
from pants.base.workunit import WorkUnit
from pants.goal.task_registrar import TaskRegistrar as task
from pants.task.task import Task


def register_goals():
task(name='run-dummy-workunit', action=TestWorkUnitTask).install()


class TestWorkUnitTask(NailgunTask):
@classmethod
def register_options(cls, register):
register('--success', default=False, type=bool)

def execute(self):
result = WorkUnit.SUCCESS if self.get_options().success else WorkUnit.FAILURE

# This creates workunit and marks it as failure.
with self.context.new_workunit('dummy') as workunit:
workunit.set_outcome(result)
22 changes: 22 additions & 0 deletions tests/python/pants_test/goal/test_run_tracker_integration.py
Expand Up @@ -6,6 +6,7 @@
unicode_literals, with_statement)

import json
import os

from pants.util.contextutil import temporary_file_path
from pants_test.pants_run_integration_test import PantsRunIntegrationTest
Expand All @@ -27,3 +28,24 @@ def test_stats_local_json_file(self):
self.assertIn('run_info', stats_json)
self.assertIn('self_timings', stats_json)
self.assertIn('cumulative_timings', stats_json)

def test_workunit_failure(self):
pants_run = self.run_pants([
'--pythonpath={}'.format(os.path.join(os.getcwd(), 'tests', 'python')),
'--backend-packages={}'.format('pants_test.goal.data'),
'run-dummy-workunit',
'--no-success'
])
# Make sure the task actually happens and of no exception.
self.assertIn('[run-dummy-workunit]', pants_run.stdout_data)
self.assertNotIn('Exception', pants_run.stderr_data)
self.assert_failure(pants_run)

def test_workunit_success(self):
pants_run = self.run_pants([
'--pythonpath={}'.format(os.path.join(os.getcwd(), 'tests', 'python')),
'--backend-packages={}'.format('pants_test.goal.data'),
'run-dummy-workunit',
'--success'
])
self.assert_success(pants_run)

0 comments on commit b30acb6

Please sign in to comment.