Skip to content

Commit a009067

Browse files
Add ./pants generate-pants-ini for first time users to generate pants.ini with sensible defaults (#7448)
### Problem Our first-time [setup instructions](https://www.pantsbuild.org/install.html#recommended-installation) are harder than need be. Currently, before being able to do anything with Pants, you must: 1) curl `pants` 1) `chmod +x pants` 1) `touch pants.ini` 1) Copy and paste `[GLOBAL]` into the file. 1) Run `./pants -V` or decide you want to pin a different value. 1) Copy and paste that value into a new line `pants_version`. Instead, we could simplify the instructions to: 1) curl `pants` 1) `chmod +x pants` 1) `touch pants.ini` 1) `./pants generate-pants-ini` Once we add instructions on `pants_runtime_python_version`, this would get even more complex. Simplifying the onboarding process should make it easier for us to retain first time users. There are a lot of new concepts they already have to learn, e.g. BUILD files, targets, and goals. The less overwhelming we can make this process, the better. Refer to https://pantsbuild.slack.com/archives/CBNMV1LRH/p1552506655047900 for the original discussion around this idea and pantsbuild/setup#45 for the original implementation until we realized this should be a goal. ### Solution Add new `./pants generate-pants-ini` to setup the initial value with defaults as follows: * `pants_version` == version used during the run * `pants_runtime_python_version` == version used during the run We do not provide options to override these defaults, because this is meant for first time users who would not yet be familiar with options or discovering them via `--help`. Further, they can change the defaults by rewriting `pants.ini`. We also do not allow this command to be ran on pre-existing `pants.ini`, because Python's configparser would strip all comments and mess up the indentation. Instead, users should simply modify their file. ### Result If there is no content in `pants.ini`, we will print this and update `pants.ini` accordingly: ``` Adding sensible defaults to /Users/eric/DocsLocal/code/projects/pants/pants.ini: * Pinning `pants_version` to `1.15.0rc0`. * Pinning `pants_runtime_python_version` to `3.6`. You may modify these values directly in the file at any time. The ./pants script will detect any changes the next time you run it. You are now ready to use Pants! ``` If there is already content, we will fail the task and explain how to update, including the sensible defaults as a suggested edit. ### TODO: allow missing `pants.ini` Currently, Pants fails to execute if there is no `pants.ini` in the buildroot. This does not seem intentional, as an empty `pants.ini` will fix the issue. As a followup, we should allow a missing `pants.ini` and then use this command to generate the file. It will allow us to avoid having to instruct `touch pants.ini`. ### TODO: Updating docs We cannot update the docs to reflect this new option until 1.15.0 is released. The setup repo defaults to the most stable version, and this goal will not be available in 1.14.0 so would confuse people.
1 parent 03d0c5f commit a009067

File tree

4 files changed

+94
-0
lines changed

4 files changed

+94
-0
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# coding=utf-8
2+
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
3+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
4+
5+
from __future__ import absolute_import, division, print_function, unicode_literals
6+
7+
import os
8+
import sys
9+
from textwrap import dedent
10+
11+
from pants.base.build_environment import get_default_pants_config_file
12+
from pants.base.exceptions import TaskError
13+
from pants.task.console_task import ConsoleTask
14+
from pants.version import VERSION as pants_version
15+
16+
17+
class GeneratePantsIni(ConsoleTask):
18+
"""Generate pants.ini with sensible defaults."""
19+
20+
def console_output(self, _targets):
21+
pants_ini_path = get_default_pants_config_file()
22+
python_version = ".".join(str(v) for v in sys.version_info[:2])
23+
pants_ini_content = dedent("""\
24+
[GLOBAL]
25+
pants_version: {}
26+
pants_runtime_python_version: {}
27+
""".format(pants_version, python_version)
28+
)
29+
30+
if os.stat(pants_ini_path).st_size != 0:
31+
raise TaskError("{} is not empty. To update config values, please directly modify pants.ini. "
32+
"For example, you may want to add these entries:\n\n{}".format(pants_ini_path, pants_ini_content))
33+
34+
yield dedent("""\
35+
Adding sensible defaults to {}:
36+
* Pinning `pants_version` to `{}`.
37+
* Pinning `pants_runtime_python_version` to `{}`.
38+
""".format(pants_ini_path, pants_version, python_version)
39+
)
40+
41+
with open(pants_ini_path, "w") as f:
42+
f.write(pants_ini_content)
43+
44+
yield ("You may modify these values directly in the file at any time. "
45+
"The ./pants script will detect any changes the next time you run it.")
46+
yield "\nYou are now ready to use Pants!"

src/python/pants/core_tasks/register.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pants.core_tasks.clean import Clean
99
from pants.core_tasks.deferred_sources_mapper import DeferredSourcesMapper
1010
from pants.core_tasks.explain_options_task import ExplainOptionsTask
11+
from pants.core_tasks.generate_pants_ini import GeneratePantsIni
1112
from pants.core_tasks.list_goals import ListGoals
1213
from pants.core_tasks.login import Login
1314
from pants.core_tasks.noop import NoopCompile, NoopTest
@@ -76,6 +77,9 @@ def register_goals():
7677
task(name='options', action=ExplainOptionsTask).install()
7778
task(name='targets', action=TargetsHelp).install()
7879

80+
# Set up pants.ini.
81+
task(name="generate-pants-ini", action=GeneratePantsIni).install()
82+
7983
# Stub for other goals to schedule 'compile'. See noop_exec_task.py for why this is useful.
8084
task(name='compile', action=NoopCompile).install('compile')
8185

tests/python/pants_test/core_tasks/BUILD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ python_tests(
3535
],
3636
)
3737

38+
python_tests(
39+
name = 'generate_pants_ini',
40+
sources = ['test_generate_pants_ini.py'],
41+
dependencies = [
42+
'src/python/pants:version',
43+
'src/python/pants/base:exceptions',
44+
'src/python/pants/core_tasks',
45+
'tests/python/pants_test:task_test_base',
46+
'3rdparty/python:configparser',
47+
],
48+
)
49+
3850
python_tests(
3951
name = 'prep_command_integration',
4052
sources = ['test_prep_command_integration.py'],
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# coding=utf-8
2+
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
3+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
4+
5+
from __future__ import absolute_import, division, print_function, unicode_literals
6+
7+
import configparser
8+
9+
from pants.base.exceptions import TaskError
10+
from pants.core_tasks.generate_pants_ini import GeneratePantsIni
11+
from pants.version import VERSION
12+
from pants_test.task_test_base import ConsoleTaskTestBase
13+
14+
15+
class GeneratePantsIniTest(ConsoleTaskTestBase):
16+
17+
@classmethod
18+
def task_type(cls):
19+
return GeneratePantsIni
20+
21+
def test_pants_ini_generated_when_empty(self):
22+
temp_pants_ini_path = self.create_file("pants.ini")
23+
self.execute_task()
24+
config = configparser.ConfigParser()
25+
config.read(temp_pants_ini_path)
26+
self.assertEqual(config["GLOBAL"]["pants_version"], VERSION)
27+
self.assertIn(config["GLOBAL"]["pants_runtime_python_version"], {"2.7", "3.6", "3.7"})
28+
29+
def test_fails_when_pants_ini_is_not_empty(self):
30+
temp_pants_ini_path = self.create_file("pants.ini", contents="[GLOBAL]")
31+
with self.assertRaisesWithMessageContaining(TaskError, temp_pants_ini_path):
32+
self.execute_task()

0 commit comments

Comments
 (0)