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

feat: Added python publishing #358

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/publish_wheel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
name: Publish Wheel Package
on:
workflow_dispatch:
workflow_run:
workflows: [goreleaser]
types:
- completed

jobs:
publish_whl:
name: Publish Wheel
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
with:
fetch-tags: true
- name: Run go release to create binaries
uses: actions/setup-go@v3
with:
go-version: 1.21
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
distribution: goreleaser
version: latest
args: build --clean
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install Twine
run: python -m pip install twine
- name: Pack WHEEL Package
working-directory: ./bdist/py
run: python3 setup.py
- name: Issue warning if TWINE_USERNAME and TWINE_PASSWORD are not set
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_AUTH_TOKEN }}
run: |
echo -n "::warning title=Missing authentication token::In order to publish an wheel package, you must set "
echo "the PYPI_USERNAME and PYPI_AUTH_TOKEN secrets"
if: ${{ (env.TWINE_USERNAME == '') || (env.TWINE_PASSWORD == '') }}
- name: Publish WHEEL package
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_AUTH_TOKEN }}
run: twine upload _wheel/*
working-directory: ./bdist/py
if: ${{ (env.TWINE_USERNAME == '') || (env.TWINE_PASSWORD == '') }}
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ You can add a `protolint` node to your `package.json` which may contain the cont

If you want to get an output that matches the TSC compiler, use reporter `tsc`.

### Within Python projects

You can use `protolint` as a linter within your python projects. Just add the desired version to
your `pyproject.toml` or `requirements.txt`.

The wheels downloaded will contain the compiled go binaries for `protolint` and `protoc-gen-protolint`. Your platform must
be compatible with the supported binary platforms.

You can add the linter configuration to the `tools.protolint` package in `pyproject.toml`.

## Usage

```sh
Expand Down
6 changes: 6 additions & 0 deletions _testdata/py_project/project_with_yaml/protolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
lint:
rules_option:
indent:
style: tab
newline: "\n"
7 changes: 7 additions & 0 deletions _testdata/py_project/project_with_yaml/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "protolint_user"
version = "0.1.0"

[tools.protolint.rules_option.indent]
style = "\t"
newline = "\r\n"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "protolint_user"
version = "0.1.0"

[tools.protolint.rules_option.indent]
style = "\t"
newline = "\r\n"
6 changes: 6 additions & 0 deletions _testdata/py_project/project_with_yaml_parent/protolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
lint:
rules_option:
indent:
style: tab
newline: "\n"
3 changes: 3 additions & 0 deletions _testdata/py_project/pyproject/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tools.protolint.rules_option.indent]
style = "\t"
newline = "\n"
6 changes: 6 additions & 0 deletions _testdata/py_project/pyproject_no_protolint/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
name = "protolint_user"
version = "0.1.0"

[tools]
y = "x"
3 changes: 3 additions & 0 deletions _testdata/py_project/pyproject_no_tools/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[project]
name = "protolint_user"
version = "0.1.0"
7 changes: 7 additions & 0 deletions _testdata/py_project/with_pyproject/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "protolint_user"
version = "0.1.0"

[tools.protolint.rules_option.indent]
style = "\t"
newline = "\n"
2 changes: 2 additions & 0 deletions bdist/py/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
_bdist/*
_wheel
183 changes: 183 additions & 0 deletions bdist/py/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#!/usr/bin/env python3

import contextlib
import hashlib
import logging
import os
import pathlib
import shutil
import subprocess
import sys
import zipfile


@contextlib.contextmanager
def as_cwd(path):
"""Changes working directory and returns to previous on exit."""
prev_cwd = pathlib.Path.cwd()
os.chdir(path)
try:
yield
finally:
os.chdir(prev_cwd)


def clear_dir(path):
if path.is_dir():
for e in path.glob("**/*"):
if e.is_dir():
logger.debug("Clearing entries from %s", e)
clear_dir(e)
else:
logger.debug("Removing file %s", e)
e.unlink()
logger.debug("Removing directory %s", path)
path.rmdir()


logger = logging.getLogger("BUILD")
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(sys.stdout))

file_dir: pathlib.Path = pathlib.Path(os.path.dirname(__file__))
bdist = file_dir / "_bdist"
wheel = file_dir / "_wheel"

if wheel.is_dir():
clear_dir(wheel)

bdist.mkdir(exist_ok=True, parents=True)
wheel.mkdir(exist_ok=True, parents=True)

logger.info("Building files from %s", file_dir)
repo_root = file_dir / ".." / ".."
license_file = repo_root / "LICENSE"
readme_md = repo_root / "README.md"

logger.info("Using repository root %s", repo_root)

dist = repo_root / "dist"

logger.info("Using previously files from %s", dist)

cp: subprocess.CompletedProcess = subprocess.run(["git", "describe", "--tag"], capture_output=True)
version_id = cp.stdout.decode("utf8").lstrip("v").rstrip("\n")
del cp

logger.info("Assuming version is %s", version_id)

ap_map: dict[str, str] = {
"darwin_amd64_v1": "macosx_x86_64",
"darwin_arm64": "darwin_aarch64",
"linux_amd64_v1": "manylinux1_x86_64",
"linux_arm64": "manylinux1_aarch64",
"linux_arm_7": "linux_armv7l",
"windows_amd64_v1": "win_amd64",
"windows_arm64": "win_arch64",
}

executables = {"protolint", "protoc-gen-protolint"}

PY_TAG = "py2.py3"
ABI_TAG = "none"

for arch_platform in ap_map.keys():
tag = f"{PY_TAG}-{ABI_TAG}-{ap_map[arch_platform]}"
logger.info("Packing files for %s using tag %s", arch_platform, tag)
suffix = ".exe" if "windows" in arch_platform else ""

pdir = bdist / arch_platform
clear_dir(pdir)
pdir.mkdir(exist_ok=True, parents=True)

p_executables = [dist / f"{exe}_{arch_platform}" / f"{exe}{suffix}" for exe in executables]

logger.debug("Creating wheel data folder")
dataFolder = pdir / f"protolint-{version_id}.data"
logger.debug("Creating wheel data folder")
distInfoFolder = pdir / f"protolint-{version_id}.dist-info"

dataFolder.mkdir(parents=True, exist_ok=True)
distInfoFolder.mkdir(parents=True, exist_ok=True)

with as_cwd(pdir):
logger.debug("Creating scripts folder")
scripts = dataFolder / "scripts"
scripts.mkdir(parents=True, exist_ok=True)
for p in p_executables:
logger.debug("Copying executable %s to scripts folder %s", p, scripts)
shutil.copy(p, scripts)

logger.debug("Copying LICENSE from %s", license_file)
shutil.copy(license_file, distInfoFolder)

with (distInfoFolder / "WHEEL").open("w+") as wl:
logger.debug("Writing WHEEL file")
wl.writelines([
"Wheel-Version: 1.0\n",
"Generator: https://github.com/yoheimuta/protolint/\n",
"Root-Is-PureLib: false\n",
f"Tag: {tag}\n"]
)

with (distInfoFolder / "METADATA").open("w+") as ml:
logger.debug("Writing METADATA file")
ml.writelines([
"Metadata-Version: 2.1\n",
"Name: protolint\n",
"Summary: A pluggable linter and fixer to enforce Protocol Buffer style and conventions.\n",
"Description-Content-Type: text/markdown\n",
"Author: yohei yoshimuta\n",
"Maintainer: yohei yoshimuta\n",
"Home-page: https://github.com/yoheimuta/protolint/\n",
"License-File: LICENSE\n",
"License: MIT\n",
"Classifier: Development Status :: 5 - Production/Stable\n",
"Classifier: Environment :: Console\n",
"Classifier: Intended Audience :: Developers\n",
"Classifier: License :: OSI Approved :: MIT License\n",
"Classifier: Natural Language :: English\n",
"Classifier: Operating System :: MacOS\n",
"Classifier: Operating System :: Microsoft :: Windows\n",
"Classifier: Operating System :: POSIX :: Linux\n",
"Classifier: Programming Language :: Go\n",
"Classifier: Topic :: Software Development :: Pre-processors\n",
"Classifier: Topic :: Utilities\n",
"Project-URL: Official Website, https://github.com/yoheimuta/protolint/\n",
"Project-URL: Source Code, https://github.com/yoheimuta/protolint.git\n",
"Project-URL: Issue Tracker, https://github.com/yoheimuta/protolint/issues\n",
f"Version: {version_id} \n",
f"Download-URL: https://github.com/yoheimuta/protolint/releases/tag/v{version_id}/\n",
])

with readme_md.open("r") as readme:
ml.writelines(readme.readlines())

wheel_content = list(distInfoFolder.glob("**/*")) + list(dataFolder.glob("**/*"))
elements_to_relative_paths = {entry: str(entry).lstrip(str(pdir)).lstrip("/").lstrip("\\") for entry in wheel_content if entry.is_file()}
with (distInfoFolder / "RECORD").open("w+") as rl:
logger.debug("Writing RECORD file")
for entry in elements_to_relative_paths.keys():
relPath = elements_to_relative_paths[entry]
sha256 = hashlib.sha256(entry.read_bytes())
fs = entry.stat().st_size
rl.write(f"{relPath},sha256={sha256.hexdigest()},{str(fs)}\n")

rl.write(distInfoFolder.name + "/RECORD,,\n")
wheel_content.append(distInfoFolder / "RECORD")

whl_file = wheel / f"protolint-{version_id}-{tag}.whl"
if whl_file.is_file():
logger.debug("Removing existing wheel file")
whl_file.unlink()

with zipfile.ZipFile(whl_file, "w", compression=zipfile.ZIP_DEFLATED) as whl:
logger.info("Creating python wheel %s", whl_file)
for content in wheel_content:
whl.write(
content,
content.relative_to(pdir),
zipfile.ZIP_DEFLATED,
)

logger.info("Done")
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/yoheimuta/protolint

require (
github.com/BurntSushi/toml v1.3.2
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab
github.com/gertd/go-pluralize v0.2.0
github.com/golang/protobuf v1.5.2
github.com/hashicorp/go-hclog v1.2.0
Expand All @@ -12,7 +14,6 @@ require (
)

require (
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 h1:W9o46d2kbNL06lq7UNDPV0zYLzkrde/bjIqO02eoll0=
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo=
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab h1:5JxePczlyGAtj6R1MUEFZ/UFud6FfsOejq7xLC2ZIb0=
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand Down Expand Up @@ -87,12 +87,9 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yoheimuta/go-protoparser/v4 v4.7.0 h1:80LGfVM25sCoNDD08hv9O0ShQMjoTrIE76j5ON+gq3U=
Expand Down Expand Up @@ -177,7 +174,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/config/customizableSeverityOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "github.com/yoheimuta/protolint/linter/rule"
// CustomizableSeverityOption represents an option where the
// severity of a rule can be configured via yaml.
type CustomizableSeverityOption struct {
severity *rule.Severity `yaml:"severity"`
severity *rule.Severity `yaml:"severity" toml:"severity"`
}

// Severity returns the configured severity. If no severity
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/config/directories.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// Directories represents the target directories.
type Directories struct {
Exclude []string `yaml:"exclude"`
Exclude []string `yaml:"exclude" json:"exclude" toml:"exclude"`
}

func (d Directories) shouldSkipRule(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package config
// EnumFieldNamesZeroValueEndWithOption represents the option for the ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH rule.
type EnumFieldNamesZeroValueEndWithOption struct {
CustomizableSeverityOption
Suffix string `yaml:"suffix"`
Suffix string `yaml:"suffix" json:"suffix" toml:"suffix"`
}
2 changes: 1 addition & 1 deletion internal/linter/config/enumFieldsHaveCommentOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package config
// EnumFieldsHaveCommentOption represents the option for the ENUM_FIELDS_HAVE_COMMENT rule.
type EnumFieldsHaveCommentOption struct {
CustomizableSeverityOption
ShouldFollowGolangStyle bool `yaml:"should_follow_golang_style"`
ShouldFollowGolangStyle bool `yaml:"should_follow_golang_style" json:"should_follow_golang_style" toml:"should_follow_golang_style"`
}
Loading