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

Include Javascript files in JVM binary #4264

Merged
merged 48 commits into from Feb 28, 2017
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0c4c696
Merge remote-tracking branch 'upstream/master'
Sep 11, 2015
7ebbf32
Merge remote-tracking branch 'upstream/master'
Sep 23, 2015
dfe9c91
Merge remote-tracking branch 'upstream/master'
Sep 24, 2015
6c1e8e4
Merge remote-tracking branch 'upstream/master'
Sep 28, 2015
abb642d
Merge remote-tracking branch 'upstream/master'
Oct 6, 2015
07198d8
Merge remote-tracking branch 'upstream/master'
Oct 6, 2015
5dafffc
Merge remote-tracking branch 'upstream/master'
Oct 16, 2015
a89e0ac
Merge remote-tracking branch 'upstream/master'
Oct 19, 2015
caefbfc
Merge remote-tracking branch 'upstream/master'
Oct 26, 2015
08d4ffb
Merge remote-tracking branch 'upstream/master'
Oct 27, 2015
9b82e4e
Merge remote-tracking branch 'upstream/master'
Oct 28, 2015
aa2d6dc
Merge remote-tracking branch 'upstream/master'
Nov 4, 2015
26e98fe
Merge remote-tracking branch 'upstream/master'
Nov 4, 2015
710d9ef
Merge remote-tracking branch 'upstream/master'
Nov 10, 2015
624e0d5
Merge remote-tracking branch 'upstream/master'
Nov 12, 2015
e2ffa2b
Merge remote-tracking branch 'upstream/master'
Nov 19, 2015
058b63b
Merge remote-tracking branch 'upstream/master'
Nov 30, 2015
177418a
Merge remote-tracking branch 'upstream/master'
Dec 7, 2015
dcbe36b
Adding NodeBundle task
Dec 8, 2015
1d4211d
Tests
Dec 8, 2015
9b4cacf
Set theme jekyll-theme-minimal
UnrememberMe Jan 26, 2017
ddb0048
Merge commit 'refs/pull/2674/head' of https://github.com/pantsbuild/p…
Jan 26, 2017
cb22899
Merge branch 'master' of https://github.com/UnrememberMe/pants
Jan 26, 2017
52cf7d8
remove theme
Jan 27, 2017
e6d507e
add dereference param to archiver
Jan 27, 2017
c46e1c1
Make sure only support tar archival types since zip does not support …
Jan 27, 2017
d336c18
move filter out non-node modules up
Jan 27, 2017
8369bdb
incorporate minor review feedbacks
Jan 30, 2017
1da9f25
incorporate minor review feedbacks
Jan 30, 2017
939ce00
Add node_bundle to deployable_archives product. Add unit test.
Jan 31, 2017
b827c0d
add node_bundle test to unit test group.
Jan 31, 2017
ab2e269
actually add node_bundle to default unit test group.
Jan 31, 2017
7d44280
Merge remote-tracking branch 'upstream/master'
Feb 1, 2017
9e04853
add classmethod decorator for perpare method
Feb 1, 2017
157e85e
Add node_bundle target to encapsulate bundle goal. Update integration…
Feb 3, 2017
542f794
Add node_bundle target, unit tests and integration tests.
Feb 3, 2017
80a86f0
fix missing build dependencies
Feb 3, 2017
cefc5a2
Merge branch 'master' of https://github.com/pantsbuild/pants
Feb 3, 2017
5cb745a
Merge branch 'master' of https://github.com/pantsbuild/pants
Feb 6, 2017
3e745ac
WIP
Feb 11, 2017
17d71ae
Generated Javascript files are included correctly.
Feb 14, 2017
688bcba
Add integration test for node build task. Fix unit tests for node bu…
Feb 16, 2017
e4b72c2
Add unit tests for node_build task
Feb 16, 2017
93828b4
Fix resource location string
Feb 16, 2017
1a510a4
Incorporate review feedback
Feb 17, 2017
c011d57
Simplify node_build invalidated block.
Feb 22, 2017
0f26643
Merge remote-tracking branch 'upstream/master' into rjiang/node_as_re…
Feb 27, 2017
ff9b491
Support Yarnpkg as package manage for node_build task.
Feb 27, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,17 @@
jvm_binary(name = 'jsresources',
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

This has the same name as the directory, so no need to specify the name (it's the "default" target).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ack. Is it recommended not to name "default" target explicitly? I always names default targets so far.

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Yes, it's recommended not to explicitly add their names.

source = 'JsResourcesMain.java',
main = 'org.pantsbuild.testproject.jsresources.JsResourcesMain',
dependencies = [
'3rdparty:guava',
'contrib/node/examples/src/node/web-component-button:web-component-button-processed',
]
)

jvm_binary(name = 'jsresources-with-dependency-artifacts',
source = 'JsResourcesMain.java',
main = 'org.pantsbuild.testproject.jsresources.JsResourcesMain',
dependencies = [
'3rdparty:guava',
'contrib/node/examples/src/node/web-component-button:web-component-button-processed-with-dependency-artifacts',
]
)
@@ -0,0 +1,30 @@
package org.pantsbuild.testproject.jsresources;

import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import java.io.IOException;
import java.net.URL;

public class JsResourcesMain {
private static String RESOURCE_PATH = "web-component-button-processed/Button.js";
private static String ALT_RESOURCE_PATH = (
"web-component-button-processed-with-dependency-artifacts/Button.js");

public static void main(String[] args) throws IOException {
// Introduce a 3rdparty library so we can test if Manifest's Class-Path entry is
// set properly for both internal and external dependencies.
URL resourceUrl;
try {
resourceUrl = Resources.getResource(RESOURCE_PATH);
} catch (IllegalArgumentException e) {
resourceUrl = Resources.getResource(ALT_RESOURCE_PATH);
}
String content = Resources.toString(resourceUrl, Charsets.UTF_8);
// Ensure resource is loaded properly.
System.out.println(content);
}

private JsResourcesMain() {
// not called. placates checkstyle
}
}
3 changes: 2 additions & 1 deletion contrib/node/examples/src/node/web-build-tool/BUILD
Expand Up @@ -6,5 +6,6 @@ node_module(
'contrib/node/examples/3rdparty/node/null-loader',
'contrib/node/examples/3rdparty/node/style-loader',
'contrib/node/examples/3rdparty/node/webpack',
]
],
preserve_artifacts=False,
)
31 changes: 30 additions & 1 deletion contrib/node/examples/src/node/web-component-button/BUILD
Expand Up @@ -5,7 +5,7 @@ node_module(
'contrib/node/examples/3rdparty/node/mocha',
'contrib/node/examples/3rdparty/node/react',
'contrib/node/examples/src/node/web-build-tool',
]
],
)

node_test(
Expand All @@ -29,3 +29,32 @@ node_bundle(
name='web-component-button-bundle',
node_module=':web-component-button'
)

# The following target invokes webpack build.
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

...because of the build_script argument? More comment would be helpful probably.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added some comments to clarify different scenarios.

node_module(
name='web-component-button-processed',
sources=globs('package.json', 'webpack.config.js', 'src/*', 'test/*'),
dependencies=[
'contrib/node/examples/3rdparty/node/mocha',
'contrib/node/examples/3rdparty/node/react',
'contrib/node/examples/src/node/web-build-tool',
],
build_script='build',
)

node_bundle(
name='web-component-button-processed-bundle',
node_module=':web-component-button-processed'
)

node_module(
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Maybe a comment indicating why both this and the above target exist?

Could this target depend on the other one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a line of comments. This target could be depending on :web-component-button-processed, but it will generate a different output structure.

current BUILD rule =>

JAR / web-component-button-processed-with-dependency-artifacts / Button.js
JAR / web-dependency-test/...

change BUILD rule to

node_module( 
  name = 'module_name',
  dependencies=[':web-component-button-processed', 'web-dependency-test'])

will generate =>

JAR / web-component-button-processed / Button.js
JAR / web-dependency-test/ ...
JAR / module_name / node_modules / web-component-button-processed / %source_files%
JAR / module_name / node_modules / % other_node_modules % / ...

Basically, each module's artifacts will be included at JAR root separately, disregarding the hierarchy.

name='web-component-button-processed-with-dependency-artifacts',
sources=globs('package.json', 'webpack.config.js', 'src/*', 'test/*'),
dependencies=[
'contrib/node/examples/3rdparty/node/mocha',
'contrib/node/examples/3rdparty/node/react',
'contrib/node/examples/src/node/web-build-tool',
'contrib/node/examples/src/node/web-dependency-test',
],
build_script='build',
)
6 changes: 6 additions & 0 deletions contrib/node/examples/src/node/web-dependency-test/BUILD
@@ -0,0 +1,6 @@
node_module(
sources=globs('package.json', 'src/*'),
dependencies=[
'contrib/node/examples/3rdparty/node/mocha',
]
)
@@ -0,0 +1,3 @@
{
"name": "web-dependency-test"
}
@@ -0,0 +1 @@
console.writeln("Hello World.")
2 changes: 2 additions & 0 deletions contrib/node/src/python/pants/contrib/node/register.py
Expand Up @@ -16,6 +16,7 @@
from pants.contrib.node.targets.node_preinstalled_module import NodePreinstalledModule
from pants.contrib.node.targets.node_remote_module import NodeRemoteModule
from pants.contrib.node.targets.node_test import NodeTest as NodeTestTarget
from pants.contrib.node.tasks.node_build import NodeBuild
from pants.contrib.node.tasks.node_bundle import NodeBundle as NodeBundleTask
from pants.contrib.node.tasks.node_repl import NodeRepl
from pants.contrib.node.tasks.node_resolve import NodeResolve
Expand All @@ -39,6 +40,7 @@ def register_goals():
task(name='node', action=NodeRepl).install('repl')
task(name='node', action=NodeResolve).install('resolve')
task(name='node', action=NodeRun).install('run')
task(name='node', action=NodeBuild).install('compile', first=True)
task(name='node', action=NodeTestTask).install('test')
task(name='node', action=NodeBundleTask).install('bundle')

Expand Down
2 changes: 1 addition & 1 deletion contrib/node/src/python/pants/contrib/node/targets/BUILD
Expand Up @@ -14,7 +14,7 @@ python_library(
name='node_bundle',
sources=['node_bundle.py'],
dependencies=[
':node_module',
':node_package',
'src/python/pants/base:payload',
'src/python/pants/fs',
],
Expand Down
Expand Up @@ -10,10 +10,10 @@
from pants.base.payload_field import PrimitiveField
from pants.fs import archive as archive_lib

from pants.contrib.node.targets.node_module import NodeModule
from pants.contrib.node.targets.node_package import NodePackage


class NodeBundle(NodeModule):
class NodeBundle(NodePackage):
"""A bundle of node modules."""

def __init__(self, node_module=None, archive='tgz', address=None, payload=None, **kwargs):
Expand Down
21 changes: 17 additions & 4 deletions contrib/node/src/python/pants/contrib/node/targets/node_module.py
Expand Up @@ -6,18 +6,28 @@
unicode_literals, with_statement)

from pants.base.payload import Payload
from pants.base.payload_field import PrimitiveField

from pants.contrib.node.targets.node_package import NodePackage


class NodeModule(NodePackage):
"""A Node module."""

def __init__(self, sources=None, address=None, payload=None, **kwargs):
def __init__(
self, sources=None, build_script=None, output_dir='dist',
preserve_artifacts=True, address=None, payload=None, **kwargs):
"""
:param sources: Javascript and other source code files that make up this module; paths are
relative to the BUILD file's directory.
:type sources: `globs`, `rglobs` or a list of strings

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

These make a lot more sense now, thanks.

One last bit of confusion is the dev_dependencies argument. Is that marking a whole target as a dev_dependency? I guess that would be because the node_tests target doesn't actually own any sources of its own, and thus needs to get them from some other target that has the dev dependency?

I guess that's not really a thing we can fix unless we're able to encourage people to split their target into "target" and "test target", as most other languages do.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a bit confusion because the usage scenario is a bit confusion. When dev_depedency is set to True, all we really do is not to include this module in class path (or bundlable js product) so that it does not show up in the final bundle. Its dependent node_module targets are treated separately based on their target definition.

The reason is sometimes developers writes a build time tool in JS. They might include this build tool as a dependency. An example is contrib/node/examples/src/node/web-component-button. It includes contrib/node/examples/src/node/web-build-tool as a dependency. web-build-tool simply invokes webpack with some customized configuration included. The example is simplified. A more realistic example is a node_module includes a JS tool for asset uploading or template hydration.
I don't think we support this kind of usage in other languages as this is usage pattern is only common for Node developers.

:param build_script: build script name as defined in package.json.
:param output_dir: relative path to assets generated by build script. The path will be
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

A longer example would be good here: this all gets rendered to http://www.pantsbuild.org/build_dictionary.html , so it's worthwhile!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fleshed out a little bit more. Not sure if necessary to add some examples codes. Let me know if I should consider to do that.

preserved in the created JAR if the target is used as a JVM target dependency.
:param preserve_artifacts: boolean value. Default is True. If a node_module is used as parts
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Hm... "artifacts" is a pretty overloaded name. It sounds like what this does is "preserve_dev_dependencies" maybe? Or I'm not understanding.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

renamed to "dev_dependency" and set default to False.

of devDependencies and thus should not be included in the final artifact, set this value to
False.
"""
# TODO(John Sirois): Support devDependencies, etc. The devDependencies case is not
# clear-cut since pants controlled builds would provide devDependencies as needed to perform
Expand All @@ -26,8 +36,11 @@ def __init__(self, sources=None, address=None, payload=None, **kwargs):
# of pre-existing package.json files as node_module targets will require this.
payload = payload or Payload()
payload.add_fields({
'sources': self.create_sources_field(sources=sources,
sources_rel_path=address.spec_path,
key_arg='sources'),
'sources': self.create_sources_field(
sources=sources, sources_rel_path=address.spec_path, key_arg='sources'),
'build_script': PrimitiveField(build_script),
'output_dir': PrimitiveField(output_dir),
'preserve_artifacts': PrimitiveField(preserve_artifacts),
})

super(NodeModule, self).__init__(address=address, payload=payload, **kwargs)
12 changes: 12 additions & 0 deletions contrib/node/src/python/pants/contrib/node/tasks/BUILD
Expand Up @@ -16,6 +16,18 @@ python_library(
sources=['node_paths.py']
)

python_library(
name='node_build',
sources=['node_build.py'],
dependencies=[
':node_paths',
':node_task',
'src/python/pants/base:workunit',
'src/python/pants/task',
'src/python/pants/util:contextutil',
]
)

python_library(
name='node_bundle',
sources=['node_bundle.py'],
Expand Down
89 changes: 89 additions & 0 deletions contrib/node/src/python/pants/contrib/node/tasks/node_build.py
@@ -0,0 +1,89 @@
# coding=utf-8
# Copyright 2015 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)

import os
from collections import defaultdict

from pants.backend.jvm.tasks.classpath_products import ClasspathProducts
from pants.base.exceptions import TaskError
from pants.base.workunit import WorkUnitLabel
from pants.goal.products import MultipleRootedProducts
from pants.util.contextutil import pushd
from pants.util.dirutil import absolute_symlink

from pants.contrib.node.tasks.node_paths import NodePaths
from pants.contrib.node.tasks.node_task import NodeTask


class NodeBuild(NodeTask):
"""Create an archive bundle of NodeModule targets."""

@classmethod
def product_types(cls):
# runtime_classpath is used for JVM target to include node build results as resources.
return ['bundleable_js', 'runtime_classpath']

@classmethod
def prepare(cls, options, round_manager):
super(NodeBuild, cls).prepare(options, round_manager)
round_manager.require_data(NodePaths)

@property
def create_target_dirs(self):
return True

def __init__(self, *args, **kwargs):
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

empty, can remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

super(NodeBuild, self).__init__(*args, **kwargs)

def execute(self):
node_paths = self.context.products.get_data(NodePaths)
runtime_classpath_product = self.context.products.get_data(
'runtime_classpath', init_func=ClasspathProducts.init_func(self.get_options().pants_workdir))
bundleable_js_product = self.context.products.get_data(
'bundleable_js', init_func=lambda: defaultdict(MultipleRootedProducts))

targets = self.context.targets(predicate=self.is_node_module)
with self.invalidated(targets) as invalidation_check:
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

I think that you want to set invalidate_dependents=True here, which will cause dependencies to invalidate their dependents when they change (yes: it should probably be the default).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

for vt in invalidation_check.all_vts:
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

When using an invalidated block, I strongly recommend making the block as small as possible, and breaking out the "thing to do only if a target is invalid" and "things to do regardless" bits as methods.

The goal is to make it easy to read the block itself, since errors there lead to cache bugs.

target = vt.target
target_address = target.address.reference()
node_installed_path = node_paths.node_path(target)

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

The invalidated block is still quite deeply nested, and thus difficult to read/review. Please fix the factoring in here (mentioned before) before we merge this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated. Please take another look.

with pushd(node_installed_path):
if target.payload.build_script:
if not vt.valid:
self.context.log.info('Running node build {} for {} at {}\n'.format(
target.payload.build_script, target_address, node_installed_path))
result, npm_build_command = self.execute_npm(
['run-script', target.payload.build_script],
workunit_name=target_address,
workunit_labels=[WorkUnitLabel.COMPILER])
if result != 0:
raise TaskError(
'Failed to run build for {}:\n\t{} failed with exit code {}'.format(
target_address, npm_build_command, result))

if target.payload.preserve_artifacts:
output_dir = os.path.join(node_installed_path, target.payload.output_dir)
if os.path.exists(output_dir):
bundleable_js_product[target].add_abs_paths(output_dir, [output_dir])
else:
raise TaskError(
'Target {} has build script {} specified, but did not generate any output '
'at {}.\n'.format(target_address, npm_build_command, output_dir))
else:
if target.payload.preserve_artifacts:
bundleable_js_product[target].add_abs_paths(node_installed_path, [node_installed_path])
output_dir = node_installed_path

if target.payload.preserve_artifacts:
if not vt.valid:
# Resources included in a JAR file will be under %target_name%
absolute_symlink(output_dir, os.path.join(vt.results_dir, target.address.target_name))
self.context.log.debug('adding {} for target {} to runtime classpath'.format(
vt.results_dir, target_address))
runtime_classpath_product.add_for_target(target, [('default', vt.results_dir)])
37 changes: 20 additions & 17 deletions contrib/node/src/python/pants/contrib/node/tasks/node_bundle.py
Expand Up @@ -11,7 +11,6 @@
from pants.fs import archive
from pants.util import dirutil

from pants.contrib.node.tasks.node_paths import NodePaths
from pants.contrib.node.tasks.node_task import NodeTask


Expand All @@ -25,31 +24,35 @@ def product_types(cls):
@classmethod
def prepare(cls, options, round_manager):
super(NodeBundle, cls).prepare(options, round_manager)
round_manager.require_data(NodePaths)
round_manager.require_data('bundleable_js')

def __init__(self, *args, **kwargs):
super(NodeBundle, self).__init__(*args, **kwargs)
self._outdir = self.get_options().pants_distdir

def execute(self):
node_paths = self.context.products.get_data(NodePaths)
bundleable_js = self.context.products.get_data('bundleable_js')
bundle_archive_product = self.context.products.get('deployable_archives')
dirutil.safe_mkdir(self._outdir) # Make sure dist dir is present.

for target in self.context.target_roots:
if self.is_node_bundle(target):
archiver = archive.archiver(target.payload.archive)
# build_dir is a symlink. Since dereference option for tar is set to False, we need to
# dereference manually to archive the linked build dir.
build_dir = os.path.realpath(node_paths.node_path(target.node_module))
self.context.log.debug('archiving %s' % build_dir)
archivepath = archiver.create(
build_dir,
self._outdir,
target.package_name,
None,
dereference=False
)
bundle_archive_product.add(
target, os.path.dirname(archivepath)).append(os.path.basename(archivepath))
self.context.log.info('created {}'.format(os.path.relpath(archivepath, get_buildroot())))

for _, abs_paths in bundleable_js[target.node_module].abs_paths():
for abs_path in abs_paths:
# build_dir is a symlink. Since dereference option for tar is set to False, we need to
# dereference manually to archive the linked build dir.
build_dir = os.path.realpath(abs_path)
self.context.log.debug('archiving {}'.format(build_dir))
archivepath = archiver.create(
build_dir,
self._outdir,
target.package_name,
None,
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

would be good to pass this arg by-name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

dereference=False
)
bundle_archive_product.add(
target, os.path.dirname(archivepath)).append(os.path.basename(archivepath))
self.context.log.info(
'created {}'.format(os.path.relpath(archivepath, get_buildroot())))
15 changes: 14 additions & 1 deletion contrib/node/tests/python/pants_test/contrib/node/tasks/BUILD
@@ -1,6 +1,19 @@
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

python_tests(
name='node_build',
sources=['test_node_build.py'],
dependencies=[
'contrib/node/src/python/pants/contrib/node/targets:node_module',
'contrib/node/src/python/pants/contrib/node/tasks:node_build',
'src/python/pants/build_graph',
'src/python/pants/util:contextutil',
'src/python/pants/util:dirutil',
'tests/python/pants_test/tasks:task_test_base',
],
)

python_tests(
name='node_bundle',
sources=['test_node_bundle.py'],
Expand All @@ -21,7 +34,7 @@ python_tests(
'src/python/pants/fs',
'src/python/pants/util:contextutil',
],
timeout=120,
timeout=300,
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

When one timeout goes up, it's good to tighten a different one. Maybe you can bring each of node_build and node_bundle down to timeout=30?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

tags={'integration'},
)

Expand Down