Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/upstream/main' into releas…
Browse files Browse the repository at this point in the history
…e/2.14.1/prepare
  • Loading branch information
jsirois committed Jan 16, 2023
2 parents 15e52b5 + 07da1d6 commit ed08f75
Show file tree
Hide file tree
Showing 17 changed files with 1,270 additions and 103 deletions.
73 changes: 0 additions & 73 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -608,78 +608,6 @@ jobs:
name: Deploy to S3
run: ./build-support/bin/deploy_to_s3.py
timeout-minutes: 90
build_wheels_macos11_x86_64:
env:
PANTS_REMOTE_CACHE_READ: 'false'
PANTS_REMOTE_CACHE_WRITE: 'false'
if: ((github.repository_owner == 'pantsbuild') && (github.event_name == 'push'
|| needs.classify_changes.outputs.release == 'true')) && (needs.classify_changes.outputs.docs_only
!= 'true')
name: Build wheels (macOS11-x86_64)
needs:
- classify_changes
runs-on:
- macos-11
steps:
- name: Check out code
uses: actions/checkout@v3
with:
fetch-depth: 10
- name: Expose Pythons
uses: pantsbuild/actions/expose-pythons@627a8ce25d972afa03da1641be9261bbbe0e3ffe
- name: Cache Rust toolchain
uses: actions/cache@v3
with:
key: macOS11-x86_64-rustup-${{ hashFiles('rust-toolchain') }}-v2
path: '~/.rustup/toolchains/1.66.1-*
~/.rustup/update-hashes
~/.rustup/settings.toml
'
- name: Cache Cargo
uses: benjyw/rust-cache@461b9f8eee66b575bce78977bf649b8b7a8d53f1
with:
cache-bin: 'false'
shared-key: engine
workspaces: src/rust/engine
- if: github.event_name != 'pull_request'
name: Setup toolchain auth
run: 'echo TOOLCHAIN_AUTH_TOKEN="${{ secrets.TOOLCHAIN_AUTH_TOKEN }}" >> $GITHUB_ENV
'
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: 1.17.1
- env:
ARCHFLAGS: -arch x86_64
name: Build wheels
run: '[[ "${GITHUB_EVENT_NAME}" == "pull_request" ]] && export MODE=debug
USE_PY39=true ./build-support/bin/release.sh build-local-pex
./build-support/bin/release.sh build-wheels
USE_PY38=true ./build-support/bin/release.sh build-wheels
USE_PY39=true ./build-support/bin/release.sh build-wheels'
- continue-on-error: true
if: always()
name: Upload pants.log
uses: actions/upload-artifact@v3
with:
name: pants-log-wheels-macOS11-x86_64
path: .pants.d/pants.log
- env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
if: github.event_name == 'push'
name: Deploy to S3
run: ./build-support/bin/deploy_to_s3.py
timeout-minutes: 90
check_labels:
if: github.repository_owner == 'pantsbuild'
name: Ensure PR has a category label
Expand Down Expand Up @@ -800,7 +728,6 @@ jobs:
- build_wheels_linux_x86_64
- build_wheels_macos10_15_x86_64
- build_wheels_macos11_arm64
- build_wheels_macos11_x86_64
- check_labels
- classify_changes
- lint_python
Expand Down
10 changes: 9 additions & 1 deletion build-support/bin/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ python_sources(

python_tests(
name="py_tests",
overrides={"reversion_test.py": {"timeout": 90, "dependencies": ["3rdparty/python#pex"]}},
overrides={
"reversion_test.py": {"timeout": 90, "dependencies": ["3rdparty/python#pex"]},
"generate_json_schema_test.py": {
"dependencies": [
"build-support/bin/json_schema_testdata:json_schema_samples",
"3rdparty/python#types-setuptools",
]
},
},
)

pex_binary(
Expand Down
58 changes: 58 additions & 0 deletions build-support/bin/check_json_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
"""Check a generated a JSON schema file before uploading it to the JSON schema store
(https://www.schemastore.org/json/).
Live run:
$ ./pants run build-support/bin/generate_json_schema.py -- --all-help-file=all-help.json
$ ./pants run build-support/bin/check_json_schema.py -- --schema="pantsbuild-$(./pants version).json"
"""
import argparse
import getpass
import json


def main() -> None:
args = get_args()
with open(args.schema) as fh:
raw_data = fh.read()

with open(args.schema) as fh:
json_data = json.load(fh)

# there should be some options
assert json_data["properties"]["GLOBAL"]["properties"]["pants_version"]

# certain options' default values may be a result of variable expansion
username = getpass.getuser()
if username in raw_data:
raise ValueError(f"{username} is in the schema file.")

for section_name, section_data in json_data["properties"].items():
for option_name, option_data in section_data["properties"].items():
# every property description should contain some text before the URL
if option_data["description"].startswith("\nhttp"):
raise ValueError(f"{option_name} has an incomplete description.")

# every option's description should contain a URL
if "https://www.pantsbuild.org/v" not in option_data["description"]:
raise ValueError(f"{option_name} should have a URL in description.")


def create_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Checks JSON schema file.")
parser.add_argument(
"--schema",
help="Input schema file with the contents produced by the schema generation script.",
required=True,
)
return parser


def get_args():
return create_parser().parse_args()


if __name__ == "__main__":
main()
1 change: 0 additions & 1 deletion build-support/bin/generate_github_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,6 @@ def build_wheels_jobs() -> Jobs:
return {
**build_wheels_job(Platform.LINUX_X86_64, ALL_PYTHON_VERSIONS),
**build_wheels_job(Platform.LINUX_ARM64, ALL_PYTHON_VERSIONS),
**build_wheels_job(Platform.MACOS11_X86_64, ALL_PYTHON_VERSIONS),
**build_wheels_job(Platform.MACOS10_15_X86_64, ALL_PYTHON_VERSIONS),
**build_wheels_job(Platform.MACOS11_ARM64, [PYTHON39_VERSION]),
}
Expand Down
165 changes: 165 additions & 0 deletions build-support/bin/generate_json_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
"""Generates a JSON schema file to be uploaded to the JSON schema store
(https://www.schemastore.org/json/). The schema file is used by IDEs (PyCharm, VSCode, etc) to
provide intellisense when editing Pants configuration files in TOML format. It can also be used to
validate your pants.toml configuration file programmatically.
Live run:
$ ./pants help-all > all-help.json
$ ./pants run build-support/bin/generate_json_schema.py -- --all-help-file=all-help.json
"""
import argparse
import itertools
import json
import re
from typing import Any, Dict, Iterable

from packaging.version import Version

from pants.version import VERSION

GENERATED_JSON_SCHEMA_FILENAME = f"pantsbuild-{VERSION}.json"
DOCS_URL = "https://www.pantsbuild.org"
VERSION_MAJOR_MINOR = f"{Version(VERSION).major}.{Version(VERSION).minor}"

PYTHON_TO_JSON_TYPE_MAPPING = {
"str": "string",
"bool": "boolean",
"list": "array",
"int": "number",
"float": "number",
"dict": "object",
}

# Certain default values will be expanded using local runtime environment which is undesirable.
# This list may need to be extended as more options with environment specific default values are added.
ENV_SPECIFIC_OPTION_DEFAULTS = {
"pants_config_files": ["<buildroot>/pants.toml"],
"pants_subprocessdir": "<buildroot>/.pids",
"pants_distdir": "<buildroot>/dist",
"pants_workdir": "<buildroot>/.pants.d",
"local_store_dir": "$XDG_CACHE_HOME/lmdb_store",
"named_caches_dir": "$XDG_CACHE_HOME/named_caches",
}


def simplify_option_description(description: str) -> str:
"""Take only a first sentence out of a multi-sentence description without a final full stop.
There is an assumption that there are no newlines.
"""
return re.split(r"(?<=[^A-Z].[.?]) +(?=[A-Z])", description)[0].rstrip(".")


def get_description(option: dict, section: str) -> str:
"""Get a shortened description with a URL to the online docs of the given option."""
option_help: str = option["help"].lstrip("\n").split("\n")[0]
option_name: str = option["config_key"]
simplified_option_help = simplify_option_description(option_help)
url = f"{DOCS_URL}/v{VERSION_MAJOR_MINOR}/docs/reference-{section.lower()}#{option_name}"
return f"{simplified_option_help}\n{url}"


def get_default(option: dict) -> Any:
"""Get default value for an option.
Ensure options that depend on any machine specific environment are properly handled. E.g.
`"default": "<buildroot>/.pants.d"` will be expanded to the `"default": "/home/your-user.name/code/pants/.pants.d"`
which is not what we want to have in a public schema.
"""
return ENV_SPECIFIC_OPTION_DEFAULTS.get(option["config_key"], option["default"])


def build_scope_properties(ruleset: dict, options: Iterable[dict], scope: str) -> dict:
"""Build properties object for a single scope.
There are custom types (e.g. `file_option` or `LogLevel`) for which one cannot safely infer a
type, so no type is added to the ruleset. If there are choices, there is no need to provide
"type" for the schema (assuming all choices share the same type). If an option value can be
loaded from a file, a union of `string` and option's `typ` is used. Otherwise, a provided `typ`
field is used.
"""
for option in options:
properties = ruleset[scope]["properties"]

properties[option["config_key"]] = {
"description": get_description(option, scope),
"default": get_default(option),
}
if option["choices"]:
# TODO(alte): find a safe way to sort choices
properties[option["config_key"]]["enum"] = option["choices"]
else:
typ = PYTHON_TO_JSON_TYPE_MAPPING.get(option["typ"])
# TODO(alte): do we want to maintain a mapping between special options?
# `process_total_child_memory_usage` ("typ": "memory_size") -> "int"
# `engine_visualize_to` ("typ": "dir_option") -> "str"
if typ:
# options may allow providing value inline or loading from a filepath string
if option.get("fromfile"):
properties[option["config_key"]]["oneOf"] = [{"type": typ}, {"type": "string"}]
else:
properties[option["config_key"]]["type"] = typ

# TODO(alte): see if one safely codify in the schema the fact that we support `.add` and `.remove`
# semantics on arrays; e.g. `extra_requirements.add` can either be an array or an object
# {add|remove: array}
return ruleset


def main() -> None:
args = get_args()
with open(args.all_help_file) as fh:
all_help = json.load(fh)["scope_to_help_info"]

# set GLOBAL scope that is declared under an empty string
all_help["GLOBAL"] = all_help[""]
del all_help[""]

# build ruleset for all scopes (where "scope" is a [section]
# in the pants.toml configuration file such as "pytest" or "mypy")
ruleset = {}
for scope, options in all_help.items():
ruleset[scope] = {
"description": all_help[scope]["description"],
"type": "object",
"properties": {},
}
ruleset = build_scope_properties(
ruleset=ruleset,
options=itertools.chain(options["basic"], options["advanced"]),
scope=scope,
)

schema: Dict[str, Any] = dict()
schema["$schema"] = "http://json-schema.org/draft-04/schema#"
schema["description"] = "Pants configuration file schema: https://www.pantsbuild.org/"
schema["properties"] = ruleset
# custom plugins may have own configuration sections
schema["additionalProperties"] = True
schema["type"] = "object"

with open(GENERATED_JSON_SCHEMA_FILENAME, "w") as fh:
fh.write(json.dumps(schema, indent=2, sort_keys=True))


def create_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Generates JSON schema file to be used in IDEs for Pants configuration files in TOML format."
)
parser.add_argument(
"--all-help-file",
help="Input file with the contents produced by the `./pants help-all` command.",
required=True,
)
return parser


def get_args():
return create_parser().parse_args()


if __name__ == "__main__":
main()
Loading

0 comments on commit ed08f75

Please sign in to comment.