Skip to content
Closed
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
94 changes: 94 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Release

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

build-binaries:
needs: test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller

- name: Build binary
run: python scripts/build_binaries.py

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: binaries-${{ matrix.os }}
path: dist/casbin-cli-*

release:
needs: [test, build-binaries]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install wheel twine
npm install -g semantic-release @semantic-release/changelog @semantic-release/exec @semantic-release/github

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Move binaries to dist
run: |
mkdir -p dist
find artifacts -name "casbin-cli-*" -exec cp {} dist/ \;

- name: Semantic Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
#PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
run: npx semantic-release
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,5 @@ cython_debug/
marimo/_static/
marimo/_lsp/
__marimo__/

.idea
38 changes: 38 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"branches": ["master"],
"repositoryUrl": "https://github.com/casbin/casbin-python-cli",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
[
"@semantic-release/exec",
{
"prepareCmd": "python scripts/update_version.py ${nextRelease.version}"
}
],
[
"@semantic-release/github",
{
"assets": [
{
"path": "dist/*.whl",
"label": "Python Wheel"
},
{
"path": "dist/*.tar.gz",
"label": "Source Distribution"
},
{
"path": "dist/casbin-cli-*"
}
]
}
]
]
}
43 changes: 31 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,39 @@ python -m casbin_cli.client enforce -m "examples/rbac_model.conf" -p "examples/r
## Project Structure

```
casbin-python-cli/
├── casbin_cli/
│ ├── __init__.py
│ ├── client.py # Main entry point and CLI argument parsing
│ ├── command_executor.py # Dynamic method execution and JSON response
│ ├── enforcer_factory.py # Enforcer creation and input validation
│ ├── response.py # JSON response formatting
│ └── utils.py # Utility functions
├── examples/ # Example configuration files
├── requirements.txt # Python dependencies
├── setup.py # Package installation script
└── README.md
casbin-python-cli/
├── .github/
│ └── workflows/
│ └── release.yml # GitHub Actions CI/CD
├── scripts/
│ ├── update_version.py # Version management
│ └── build_binaries.py # Binary building
├── casbin_cli/
│ ├── __init__.py
│ ├── __version__.py # Version source
│ ├── client.py # Main CLI entry point
│ ├── command_executor.py # Command execution
│ ├── enforcer_factory.py # Enforcer creation
│ ├── response.py # Response formatting
│ └── utils.py # Utilities
├── examples/ # Example configurations
├── .releaserc.json # Semantic release config
├── package.json # Node.js dependencies
├── requirements.txt # Python dependencies
├── setup.py # Package setup
└── README.md
```

### Release Process

Releases are automated via GitHub Actions:

1. Push commits to `main` branch
2. Semantic release analyzes commit messages
3. Automatically generates version numbers and changelog
4. Builds cross-platform binaries
5. Publishes to PyPI and GitHub Releases

## Requirements

- Python 3.6+
Expand Down
1 change: 1 addition & 0 deletions casbin_cli/__version__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "1.0.0"
15 changes: 8 additions & 7 deletions casbin_cli/client.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import argparse
import sys
import json
from .command_executor import CommandExecutor
from .enforcer_factory import EnforcerFactory
from .utils import process_line_breaks
from casbin_cli.command_executor import CommandExecutor
from casbin_cli.enforcer_factory import EnforcerFactory
from casbin_cli.utils import process_line_breaks
from casbin_cli.__version__ import __version__

class Client:
@staticmethod
Expand All @@ -23,10 +24,10 @@ def run(args=None):
if command_name in ['-h', '--help']:
Client._print_help()
return ""
elif command_name in ['-v', '--version']:
print("casbin-python-cli 1.0.0")
print("pycasbin 1.17.0")
return ""
#elif command_name in ['-v', '--version']:
# print(f"casbin-python-cli {__version__}")
# print("pycasbin 1.17.0")
# return ""

# Handle line breaks
processed_args = [args[0]]
Expand Down
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "casbin-python-cli",
"version": "1.0.0",
"private": true,
"devDependencies": {
"@semantic-release/changelog": "^6.0.0",
"@semantic-release/exec": "^6.0.0",
"@semantic-release/github": "^8.0.0",
"semantic-release": "^19.0.0"
}
}
57 changes: 57 additions & 0 deletions scripts/build_binaries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright 2025 The casbin Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import sys
import subprocess
import platform

def build_binary():
"""Build standalone binary using PyInstaller"""

# Install PyInstaller if not present
subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"], check=True)

# Get platform info
system = platform.system().lower()
arch = platform.machine().lower()

# Build binary
binary_name = f"casbin-cli-{system}-{arch}"
if system == "windows":
binary_name += ".exe"

cmd = [
"pyinstaller",
"--onefile",
"--name", binary_name,
"--console",
"--paths", ".",
"--hidden-import", "casbin_cli",
"--hidden-import", "casbin",
"--hidden-import", "casbin_cli.client",
"--hidden-import", "casbin_cli.command_executor",
"--hidden-import", "casbin_cli.enforcer_factory",
"--hidden-import", "casbin_cli.response",
"--hidden-import", "casbin_cli.utils",
"--collect-all", "casbin",
"casbin_cli/client.py"
]

subprocess.run(cmd, check=True)

print(f"Binary built successfully: dist/{binary_name}")

if __name__ == "__main__":
build_binary()
45 changes: 45 additions & 0 deletions scripts/update_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright 2025 The casbin Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import re
import os

def update_version(new_version):
"""Update version in all relevant files"""

# Update setup.py
setup_py_path = "setup.py"
with open(setup_py_path, 'r') as f:
content = f.read()

# Update client.py version display
client_py_path = "casbin_cli/client.py"
with open(client_py_path, 'r') as f:
content = f.read()

# Create __version__.py
version_py_path = "casbin_cli/__version__.py"
with open(version_py_path, 'w') as f:
f.write(f'__version__ = "{new_version}"\n')

print(f"Updated version to {new_version}")

if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python update_version.py <version>")
sys.exit(1)

new_version = sys.argv[1]
update_version(new_version)
27 changes: 26 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
# Copyright 2025 The casbin Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from setuptools import setup, find_packages
import os

# Read version from __version__.py
version_file = os.path.join(os.path.dirname(__file__), 'casbin_cli', '__version__.py')
if os.path.exists(version_file):
exec(open(version_file).read())
version = __version__
else:
version = "1.0.0"

setup(
name="casbin-python-cli",
version="1.0.0",
version=version,
description="A command-line tool for PyCasbin",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
packages=find_packages(),
install_requires=[
"casbin>=1.17.0",
Expand Down
Loading