diff --git a/.cpa/flake8.cfg b/.cpa/flake8.cfg index b03d221..426aebc 100644 --- a/.cpa/flake8.cfg +++ b/.cpa/flake8.cfg @@ -41,9 +41,12 @@ exclude = build, dist, htmlcov.*, - -application-import-names = flake8 # List of application-specific import names. -import-order-style = google # Import statement format style. -max-complexity = 18 # The maximum McCabe complexity allowed. -max-line-length = 120 # The maximum allowed line length. +# List of application-specific import names. +application-import-names = flake8 +# Import statement format style. +import-order-style = google +# The maximum McCabe complexity allowed. +max-complexity = 18 +# The maximum allowed line length. +max-line-length = 120 # per-file-ignores = # Per-file-ignores setting can be used to ignore specific errors in specific files. diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..1937ccd --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,45 @@ +name: CI + +#################################### +# Start the job on all push and PR # +#################################### +on: + pull_request: + branches: [master, main] + types: [synchronize, opened, reopened, ready_for_review] + # push: + # branches: [master, main] + +jobs: + precommits: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + python -m pip install pre-commit + pre-commit install + + - name: Install shfmt + run: | + SHFMT_VERSION="v3.7.0" + SHFMT_BIN="shfmt_${SHFMT_VERSION}_linux_amd64" + wget -O shfmt "https://github.com/mvdan/sh/releases/download/${SHFMT_VERSION}/${SHFMT_BIN}" + chmod +x shfmt + sudo mv shfmt /usr/local/bin/ + + - name: Run pre-commits + run: | + pre-commit run --all-files + # pre-commit run --from-ref origin/main --to-ref HEAD + # run: | + # BASE_COMMIT_ID=$(git rev-parse origin/main) + # pre-commit run --from-ref ${{ env.BASE_COMMIT_ID }} --to-ref HEAD diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..e658db9 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,57 @@ +name: Release + +on: + push: + tags: + - "v*" + # pull_request: + # branches: [master, main] + # types: [synchronize, opened, reopened, ready_for_review] + +jobs: + create-release: + name: Create Release + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Create release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release create ${{ github.ref_name }} --generate-notes --title "Version ${{ github.ref_name }}" + + upload-release: + name: Build and Release + permissions: + contents: write + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Install Rust Toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Print Runner OS + run: echo "Runner OS is ${{ runner.os }}" + + - name: Build Release + run: cargo build --release + + - name: Archive Release Binary (Windows) + if: runner.os == 'Windows' + run: Compress-Archive -Path ./target/release/cpa.exe -DestinationPath cpa-Windows.zip + shell: pwsh + + - name: Archive Release Binary (MacOS, Linux) + if: runner.os != 'Windows' + run: zip -j cpa-${{ runner.os }}.zip ./target/release/cpa + + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release upload ${{ github.ref_name }} "cpa-${{ runner.os }}.zip" --clobber diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fb2a4f..1002cf1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,10 +13,10 @@ repos: - id: check-merge-conflict # Searches for merge conflict markers within files. - id: check-symlinks # Detects broken symlinks. - id: check-added-large-files # Blocks commits that add large files. Default limit is 500kB. + args: ["--maxkb=1300"] # Can be configured with args, e.g., '--maxkb=1000' to change the limit. # Files in 'your_dir/' can be excluded. # exclude: 'your_dir/.*' - # args: ['--maxkb=5000'] - id: end-of-file-fixer # Ensures files end with a single newline or are empty. - id: trailing-whitespace # Removes any trailing whitespace at the end of lines. @@ -59,7 +59,7 @@ repos: - id: isort # Sorts Python imports into sections and by alphabetical order args: - --settings-path - - .cpa/pyproject.toml + - pyproject.toml types: - python @@ -69,7 +69,7 @@ repos: - id: black # Formats Python code to conform to the Black code style args: - --config - - .cpa/pyproject.toml + - pyproject.toml types: - python @@ -116,9 +116,9 @@ repos: - markdown exclude: templates/.pre-commit-config.yaml - ## + ############################################################################# # Rust - ## + ############################################################################# - repo: https://github.com/doublify/pre-commit-rust rev: v1.0 hooks: diff --git a/README.md b/README.md index c304a13..3a9eeee 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,26 @@ # CPA: Create-Python-App -## Overview - -`cpa` is a cli tool designed to streamline the setup of new Python projects. It automates the creation of Python projects with commonly used configs and boilerplate. - -## Goals - -- **Speed up Project Creation**: Reduce the time spent on repetitive setup tasks -- **Best Practices**: Encourage best practices for code quality, formatting, and style by including configs for tools like `black`, `isort`, and `flake8`. -- **Automation**: Automate tasks such as generating `.gitignore` files, setting up pre-commit hooks, and configuring code linters and formatters. +![CPA Logo](cpa.png) -## Features +## Overview -- Provides pre-commit hook setup with hooks for checking merge conflicts, large files, and code styling. +`cpa` is a cli tool for ultra fast setup of new Python projects. It automates the creation of config files for style & lint checks, gitignore, a basic Dockerfile and dependency management configuration. An opinionated set of pre-commit hooks are included for enforcing best practices and reducing setup time. ## Installation -Download binary from Github +### MacOS, Linux -```bash +Download latest binary and install via provided `install.sh` script or get it from [Releases](https://github.com/ysawa0/create-python-app/releases) ``` +sh install.sh +``` + +### Windows + +Download latest binary from [Releases](https://github.com/ysawa0/create-python-app/releases) page -Building from source +### Building from source ```bash # cd to project @@ -34,19 +32,26 @@ cargo install --path . To create a new project: ```bash -cpa create --name +cpa create --name myproject ``` Optional params: -- `--preset`: Specifies a Python version for the project. Defaults to "python" which is mapped internally to "python3.10". +- `--preset`: Specifies a Python version for the project. Defaults to "python3.10" Example: ```bash -cpa create --name my_project --preset python3.10 +cpa create --name myproject --preset python3.10 ``` +## Goals + +- **Speed up Project Creation**: Reduce the time spent on repetitive setup tasks +- **Best Practices**: Encourage best practices for code quality, formatting, and style by including configs for tools like `black`, `isort`, and `flake8`. +- **Automation**: Automate tasks such as generating `.gitignore` files, setting up pre-commit hooks, and configuring code linters and formatters. +- Golang, Rust support planned + ## Contributions and Feedback Users are welcome to contribute to the project by submitting pull requests or opening issues for bugs and feature requests. Feedback is also greatly appreciated to help improve the tool. diff --git a/cpa.png b/cpa.png new file mode 100644 index 0000000..34bd624 Binary files /dev/null and b/cpa.png differ diff --git a/example/.cpa/flake8.cfg b/example/.cpa/flake8.cfg new file mode 100644 index 0000000..eb83fbf --- /dev/null +++ b/example/.cpa/flake8.cfg @@ -0,0 +1,49 @@ +[flake8] +ignore = +# C901, # function is too complex. Ignored because max-complexity is set. + D100, # Missing docstring in public module. + D101, # Missing docstring in public class. + D102, # Missing docstring in public method. + D103, # Missing docstring in public function. + D104, # Missing docstring in public package. + D105, # Missing docstring in magic method. + D107, # Missing docstring in __init__. + D205, # 1 blank line required between summary line and description. + D400, # First line should end with a period. + E203, # whitespace before ':'. Conflicts with how Black formats slicing. + E231, # missing whitespace after ',', ';', or ':'. Conflicts with Black. + E266, # too many leading '#' for block comment. + E402, # module level import not at top of file. + E501, # line too long (82 > 79 characters). Ignored because max-line-length is set. + F841, # local variable is assigned to but never used. + I100, # Import statements are in the wrong order. + I201, # Missing newline between import groups. + I202, # Additional newline in a group of imports. + W503 # line break before binary operator. This is no longer PEP 8 compliant. + +exclude = + .cache, + .coverage.*, + .env, # Environment directory used by some tools + .git, # Version control directory + .github, # GitHub metadata directory + .gradle, + .hg, # Mercurial directory + .mypy_cache, # MyPy cache directory + .pytest_cache, # PyTest cache directory + .svn, # Subversion directory + .tox, + .venv, # Common virtual environment directory + .vscode, # VS Code configuration directory + *__pycache__, # Python cache directory + *.egg-info, + *.pyc, + build, + dist, + htmlcov.*, + +application-import-names = flake8 # List of application-specific import names. +import-order-style = google # Import statement format style. +max-complexity = 18 # The maximum McCabe complexity allowed. +max-line-length = 120 # The maximum allowed line length. +# per-file-ignores = # Per-file-ignores setting can be used to ignore specific errors in specific files. diff --git a/example/.cpa/prettier.json b/example/.cpa/prettier.json new file mode 100644 index 0000000..64f80e2 --- /dev/null +++ b/example/.cpa/prettier.json @@ -0,0 +1,7 @@ +{ + "bracketSpacing": true, + "singleQuote": false, + "useTabs": false, + "tabWidth": 2, + "trailingComma": "all" +} diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..ea1aea1 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,189 @@ +########################################################################################## +# Python +# From: https://github.com/github/gitignore/blob/main/Python.gitignore +########################################################################################## +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +########################################################################################## +# Rust +# From: https://github.com/github/gitignore/blob/main/Rust.gitignore +########################################################################################## +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + + +########################################################################################## +# Misc +########################################################################################## +tmp* diff --git a/example/.pre-commit-config.yaml b/example/.pre-commit-config.yaml new file mode 100644 index 0000000..2984965 --- /dev/null +++ b/example/.pre-commit-config.yaml @@ -0,0 +1,112 @@ +default_language_version: + python: python3 + +repos: + ############################################################################# + # Misc + ############################################################################# + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-merge-conflict # Searches for merge conflict markers within files. + - id: check-added-large-files # Blocks commits that add large files. Default limit is 500kB. + # Can be configured with args, e.g., '--maxkb=1000' to change the limit. + # exclude: 'your_dir/.*' + # args: ['--maxkb=5000'] + - id: check-case-conflict # Identifies potential case-insensitive file name conflicts. + - id: check-ast # Validates the syntax of Python files. + - id: check-symlinks # Detects broken symlinks. + - id: trailing-whitespace # Removes any trailing whitespace at the end of lines. + - id: end-of-file-fixer # Ensures files end with a single newline or are empty. + + ############################################################################# + # JSON, TOML + ############################################################################# + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-json # Validates JSON files to ensure they are properly formatted and syntactically correct. + types: [json] + - id: check-toml # Checks TOML files for errors and format issues to ensure valid syntax. + types: [toml] + + ############################################################################# + # Shell + ############################################################################# + - repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 3.0.0 + hooks: + - id: shfmt # Formats shell scripts to a standard convention using shfmt. + - id: shellcheck # Lints shell scripts to identify syntax and usage errors, with a specified severity of 'warning'. + args: + - --severity=warning + + ############################################################################# + # Python + ############################################################################# + - repo: https://github.com/PyCQA/autoflake + rev: v2.2.1 + hooks: + - id: autoflake # Removes unused imports and unused variables from Python code. + args: + - --in-place + - --remove-all-unused-imports + + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort # Sorts Python imports into sections and by alphabetical order. + args: + - --settings-path + - pyproject.toml + types: + - python + + - repo: https://github.com/psf/black + rev: 23.10.1 + hooks: + - id: black # Formats Python code to conform to the Black code style. + args: + - --config + - pyproject.toml + types: + - python + + - repo: https://github.com/pycqa/flake8 + rev: 6.1.0 + hooks: + - id: flake8 # Lints Python code for errors and code style issues based on PEP8. + args: + - --config=.cpa/flake8.cfg + types: + - python + + # - repo: https://github.com/astral-sh/ruff-pre-commit + # rev: v0.0.270 + # hooks: + # - id: ruff + + # - repo: https://github.com/python-poetry/poetry + # rev: '1.4.0' + # hooks: + # - id: poetry-check # Makes sure the poetry configuration does not get committed in a broken state. + # - id: poetry-lock # Ensures the poetry.lock file is up-to-date with the pyproject.toml changes. + + ############################################################################# + # CSS, Markdown, JavaScript, TypeScript, YAML style formatter + ############################################################################# + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.0.3 + hooks: + - id: prettier # An opinionated code formatter supporting multiple languages. + name: prettier + entry: prettier + args: [--config, .cpa/prettier.json, --write] + types_or: + - javascript + - ts + - tsx + - scss + - css + - yaml + - markdown diff --git a/example/Dockerfile b/example/Dockerfile new file mode 100644 index 0000000..1b4b414 --- /dev/null +++ b/example/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.10-slim + +# Set the working directory in the container to /app +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY . . + +# Install any needed packages specified in requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +CMD ["python", "./main.py"] diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..5c2a614 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,25 @@ +.PHONY: setuppc +setuppc: + @echo "Setting up pre-commit and hooks..." + python3 -m pip install pre-commit + pre-commit install + +ifeq ($(shell uname),Darwin) + @echo "Setting up shfmt (macOS)..." + brew install shfmt + + @echo "Setting up shellcheck (macOS)..." + brew install shellcheck +else + @echo "Setting up shfmt (Linux)..." + wget -qO shfmt "https://github.com/mvdan/sh/releases/download/v3.7.0/shfmt_v3.7.0_$(shell uname -m)" + chmod +x shfmt + sudo mv shfmt /usr/local/bin/shfmt + + @echo "Setting up shellcheck (Linux)..." + sudo apt-get install shellcheck || sudo yum install shellcheck || sudo dnf install shellcheck +endif + +.PHONY: reqtxt +reqtxt: + poetry export -f requirements.txt --output requirements.txt --without-hashes diff --git a/example/main.py b/example/main.py new file mode 100644 index 0000000..ad35e5a --- /dev/null +++ b/example/main.py @@ -0,0 +1 @@ +print("Hello World") diff --git a/example/pyproject.toml b/example/pyproject.toml new file mode 100644 index 0000000..65d53b1 --- /dev/null +++ b/example/pyproject.toml @@ -0,0 +1,62 @@ +[tool.poetry] +name = "example" +version = "0.0.1" +description = "" +authors = [ + "My Name " +] +license = "" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.black] +line-length = 120 +skip-string-normalization = false +target-version = ['py310'] +include = '\.pyi?$' +exclude = ''' +/( + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' +color = true + +[tool.isort] +balanced_wrapping = true +include_trailing_comma = true +known_first_party = "example" +known_third_party = [ + "boto3", # Common for AWS + "django", # Common web framework, if used + "flask", # Common web framework, if used + "jinja2", # Common templating engine + "matplotlib", # Common for plotting + "numpy", # Common for numerical operations + "pandas", # Common for data manipulation + "pendulum", # Common for date time + "pytest", # Common for testing + "requests", # Common for HTTP requests + "sqlalchemy", # Common ORM for databases +] +multi_line_output = 3 +profile = "black" +line_length = 120 + +[tool.poetry.dependencies] +python = "^3.10" + +[tool.poetry.group.dev.dependencies] +pre-commit = "^3.5.0" +pytest = "^7.3.1" +pytest-cov = "^4.1.0" diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..8b5dd49 --- /dev/null +++ b/install.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +USER_REPO="ysawa0/create-python-app" +BINARY_NAME="cpa" +TARGET_DIR="$HOME/bin" + +# Determine OS type and corresponding download asset name +OS_TYPE="" +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + OS_TYPE="Linux" +elif [[ "$OSTYPE" == "darwin"* ]]; then + OS_TYPE="macOS" +else + echo "Unsupported operating system." + exit 1 +fi + +# Prepare the binary and zip names +ZIP_NAME="$BINARY_NAME-$OS_TYPE.zip" +ZIP_PATH="$TARGET_DIR/$ZIP_NAME" + +# Fetch the latest release data from GitHub API +DOWNLOAD_URL=$(curl -s https://api.github.com/repos/$USER_REPO/releases/latest | + grep "browser_download_url.*$OS_TYPE.zip" | + cut -d '"' -f 4) + +# Check if the download URL was found +if [ -z "$DOWNLOAD_URL" ]; then + echo "No release found for $OS_TYPE" + exit 1 +fi + +# Create the target directory if it doesn't exist +mkdir -p "$TARGET_DIR" + +# Download the zip file +curl -L -o "$ZIP_PATH" "$DOWNLOAD_URL" || { + echo "Failed to download the release" + exit 1 +} + +# Unzip the binary to the target directory +unzip -o "$ZIP_PATH" -d "$TARGET_DIR" || { + echo "Failed to unzip the release" + exit 1 +} + +# Assume the binary is named $BINARY_NAME inside the zip +BINARY_PATH="$TARGET_DIR/$BINARY_NAME" + +# Give execute permissions to the binary +chmod +x "$BINARY_PATH" || { + echo "Failed to set execute permissions on the binary" + exit 1 +} + +# Remove the downloaded zip +rm "$ZIP_PATH" + +# Optionally, append the target directory to PATH if it's not already there +if [[ ":$PATH:" != *":$TARGET_DIR:"* ]]; then + echo "export PATH=\$PATH:$TARGET_DIR" >>~/.bashrc + echo "$TARGET_DIR added to PATH" +fi + +echo "cpa installed to $BINARY_PATH" diff --git a/pypropoject.toml b/pyproject.toml similarity index 100% rename from pypropoject.toml rename to pyproject.toml diff --git a/src/main.rs b/src/main.rs index 70cfaa5..4639547 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,13 +14,13 @@ enum Cli { struct CreateArgs { #[clap(long, required = true)] name: String, - #[clap(long, required = false, default_value = "python")] + #[clap(long, required = false, default_value = "python3.10")] preset: String, } #[derive(Parser)] struct UpdateArgs { - #[clap(long, required = false, default_value = "python")] + #[clap(long, required = false, default_value = "python3.10")] preset: String, } diff --git a/src/python.rs b/src/python.rs index 5d6d767..4edd092 100644 --- a/src/python.rs +++ b/src/python.rs @@ -6,14 +6,27 @@ use std::fs::{self, File}; use std::io::Write; use std::process; +#[derive(Template)] +#[template(path = ".gitignore", escape = "none")] +struct GitIgnore {} + +#[derive(Template)] +#[template(path = "Makefile", escape = "none")] +struct Makefile {} + +#[derive(Template)] +#[template(path = "Dockerfile", escape = "none")] +struct Dockerfile {} + +#[derive(Template)] +#[template(path = "main.py", escape = "none")] +struct MainPy {} + #[derive(Template)] #[template(path = ".pre-commit-config.yaml", escape = "none")] struct PreCommitConfig { python: bool, } -#[derive(Template)] -#[template(path = ".gitignore", escape = "none")] -struct GitIgnore {} #[derive(Template)] #[template(path = "pyproject.toml", escape = "none")] @@ -37,38 +50,82 @@ pub fn derive_preset(mut preset: String, name: String) { } let _ = fs::create_dir_all(format!("./{}/.cpa", name)); - // Render gitignore - let gi = GitIgnore {}; - let out_gi = gi.render().expect("Failed to render"); - let mut file_gi = - File::create(format!("./{}/.gitignore", name)).expect("Could not create file"); - file_gi - .write_all(out_gi.as_bytes()) - .expect("Could not write to file"); + // Render .gitignore + File::create(format!("./{}/.gitignore", name)) + .and_then(|mut file| { + file.write_all( + GitIgnore {} + .render() + .expect("Failed to render .gitignore") + .as_bytes(), + ) + }) + .expect("Failed to create or write to .gitignore"); + + // Render Makefile + File::create(format!("./{}/Makefile", name)) + .and_then(|mut file| { + file.write_all( + Makefile {} + .render() + .expect("Failed to render Makefile") + .as_bytes(), + ) + }) + .expect("Failed to create or write to Makefile"); + + // Render Dockerfile + File::create(format!("./{}/Dockerfile", name)) + .and_then(|mut file| { + file.write_all( + Dockerfile {} + .render() + .expect("Failed to render Dockerfile") + .as_bytes(), + ) + }) + .expect("Failed to create or write to Dockerfile"); + + // Render main.py + File::create(format!("./{}/main.py", name)) + .and_then(|mut file| file.write_all(MainPy {}.render().expect("Render fail").as_bytes())) + .expect("Failed to render or write to main.py"); // Render pre-commit conf - let pc = PreCommitConfig { python: true }; - let out_pc = pc.render().expect("Failed to render"); - let mut file_pc = - File::create(format!("./{}/.pre-commit-config.yaml", name)).expect("Could not create file"); - file_pc - .write_all(out_pc.as_bytes()) - .expect("Could not write to file"); + File::create(format!("./{}/.pre-commit-config.yaml", name)) + .and_then(|mut file| { + file.write_all( + PreCommitConfig { python: true } + .render() + .expect("Failed to render .pre-commit-config.yaml") + .as_bytes(), + ) + }) + .expect("Failed to create or write to .pre-commit-config.yaml"); - // Render conf for each pre-commit hook - let f8 = Flake8 {}; - let out_f8 = f8.render().expect("Failed to render"); - let mut f_f8 = - File::create(format!("./{}/.cpa/flake8.cfg", name)).expect("Could not create file"); - f_f8.write_all(out_f8.as_bytes()) - .expect("Could not write to file"); + // Render Flake8 conf + File::create(format!("./{}/.cpa/flake8.cfg", name)) + .and_then(|mut file| { + file.write_all( + Flake8 {} + .render() + .expect("Failed to render flake8.cfg") + .as_bytes(), + ) + }) + .expect("Failed to create or write to flake8.cfg"); - let p = Prettier {}; - let out_p = p.render().expect("Failed to render"); - let mut f_p = - File::create(format!("./{}/.cpa/prettier.json", name)).expect("Could not create file"); - f_p.write_all(out_p.as_bytes()) - .expect("Could not write to file"); + // Render Prettier conf + File::create(format!("./{}/.cpa/prettier.json", name)) + .and_then(|mut file| { + file.write_all( + Prettier {} + .render() + .expect("Failed to render prettier.json") + .as_bytes(), + ) + }) + .expect("Failed to create or write to prettier.json"); // Render Poetry conf let re = Regex::new(r"python(3\.\d+|4\.\d+)").unwrap(); @@ -87,7 +144,7 @@ pub fn derive_preset(mut preset: String, name: String) { }; let out_pyproj: String = pyproj.render().expect("Failed to render"); let mut f_pyproj = - File::create(format!("./{}/pypropoject.toml", name)).expect("Could not create file"); + File::create(format!("./{}/pyproject.toml", name)).expect("Could not create file"); f_pyproj .write_all(out_pyproj.as_bytes()) .expect("Could not write to file"); diff --git a/templates/.pre-commit-config.yaml b/templates/.pre-commit-config.yaml index 2bca8eb..90fe6a5 100644 --- a/templates/.pre-commit-config.yaml +++ b/templates/.pre-commit-config.yaml @@ -59,7 +59,7 @@ repos: - id: isort # Sorts Python imports into sections and by alphabetical order. args: - --settings-path - - .cpa/pyproject.toml + - pyproject.toml types: - python @@ -69,7 +69,7 @@ repos: - id: black # Formats Python code to conform to the Black code style. args: - --config - - .cpa/pyproject.toml + - pyproject.toml types: - python diff --git a/templates/Dockerfile b/templates/Dockerfile new file mode 100644 index 0000000..1b4b414 --- /dev/null +++ b/templates/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.10-slim + +# Set the working directory in the container to /app +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY . . + +# Install any needed packages specified in requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +CMD ["python", "./main.py"] diff --git a/templates/Makefile b/templates/Makefile new file mode 100644 index 0000000..5c2a614 --- /dev/null +++ b/templates/Makefile @@ -0,0 +1,25 @@ +.PHONY: setuppc +setuppc: + @echo "Setting up pre-commit and hooks..." + python3 -m pip install pre-commit + pre-commit install + +ifeq ($(shell uname),Darwin) + @echo "Setting up shfmt (macOS)..." + brew install shfmt + + @echo "Setting up shellcheck (macOS)..." + brew install shellcheck +else + @echo "Setting up shfmt (Linux)..." + wget -qO shfmt "https://github.com/mvdan/sh/releases/download/v3.7.0/shfmt_v3.7.0_$(shell uname -m)" + chmod +x shfmt + sudo mv shfmt /usr/local/bin/shfmt + + @echo "Setting up shellcheck (Linux)..." + sudo apt-get install shellcheck || sudo yum install shellcheck || sudo dnf install shellcheck +endif + +.PHONY: reqtxt +reqtxt: + poetry export -f requirements.txt --output requirements.txt --without-hashes diff --git a/templates/main.py b/templates/main.py new file mode 100644 index 0000000..ad35e5a --- /dev/null +++ b/templates/main.py @@ -0,0 +1 @@ +print("Hello World")