Skip to content

Commit

Permalink
Added build script
Browse files Browse the repository at this point in the history
  • Loading branch information
RhetTbull committed Jul 15, 2022
1 parent 628e0c0 commit 84c977a
Show file tree
Hide file tree
Showing 7 changed files with 394 additions and 10 deletions.
120 changes: 120 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# MacNotesApp

Work with Apple MacOS Notes.app from the command line. Also includes python interface for scripting Notes.app from your own python code.

## Installation

If you just want to use the command line tool, the easiest option is to install via [pipx](https://pypa.github.io/pipx/).

If you use `pipx`, you will not need to create a python virtual environment as `pipx` takes care of this. The easiest way to do this on a Mac is to use [homebrew](https://brew.sh/):

* Open `Terminal` (search for `Terminal` in Spotlight or look in `Applications/Utilities`)
* Install `homebrew` according to instructions at [https://brew.sh/](https://brew.sh/)
* Type the following into Terminal: `brew install pipx`
* Then type this: `pipx install macnotesapp`
* `pipx` will install the `macnotesapp` command line interface (CLI) as an executable named `notes`
* Now you should be able to run `notes` by typing: `notes`

Once you've installed macnotesapp with pipx, to upgrade to the latest version:

pipx upgrade macnotesapp

## Command Line Usage

<!-- [[[cog
import cog
from macnotesapp.cli import cli_main
from click.testing import CliRunner
runner = CliRunner()
result = runner.invoke(cli_main, ["--help"])
help = result.output.replace("Usage: cli-main", "Usage: notes")
cog.out(
"```\n{}\n```".format(help)
)
]]] -->
```
Usage: notes [OPTIONS] COMMAND [ARGS]...
notes: work with Apple Notes on the command line.
Options:
-v, --version Show the version and exit.
-h, --help Show this message and exit.
Commands:
accounts Print information about Notes accounts.
add Add new note.
config Configure default settings for account, editor, etc.
help Print help; for help on commands: help <command>.
list List notes, optionally filtering by account or text.
```
<!-- [[[end]]] -->

Use `notes help COMMAND` to get help on a specific command. For example, `notes help add`:

<!-- [[[cog
import cog
from macnotesapp.cli import cli_main
from click.testing import CliRunner
runner = CliRunner()
result = runner.invoke(cli_main, ["help", "add", "--no-markup"])
help = result.output.replace("Usage: cli-main", "Usage: notes")
cog.out(
"```\n{}\n```".format(help)
)
]]] -->
```
Usage: notes add [OPTIONS] NOTE
Add new note.
There are multiple ways to add a new note:
Add a new note from standard input (STDIN):
notes add
cat file.txt | notes add
notes add < file.txt
Add a new note by passing string on command line:
notes add NOTE
Add a new note by opening default editor (defined in $EDITOR or via `notes
config`):
notes add --edit
notes add -e
If NOTE is a single line, adds new note with name NOTE and no body. If NOTE is
more than one line, adds new note where name is first line of NOTE and body is
remainder.
Body of note must be plain text unless --html/-h or --markdown/-m
flag is set in which case body should be HTML or Markdown, respectively. If
--edit/-e flag is set, note will be opened in default editor before
being added. If --show/-s flag is set, note will be shown in Notes.app
after being added.
Account and top level folder may be specified with --account/-a and
--folder/-f, respectively. If not provided, default account and folder
are used.
Options:
-s, --show Show note in Notes after adding.
-F, --file FILENAME
-h, --html Use HTML for body of note.
-m, --markdown Use Markdown for body of note.
-p, --plaintext Use plaintext for body of note (default unless changed
in `notes config`).
-e, --edit Edit note text before adding in default editor.
-a, --account ACCOUNT Add note to account ACCOUNT.
-f, --folder FOLDER Add note to folder FOLDER.
--help Show this message and exit.
```
<!-- [[[end]]] -->
27 changes: 27 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/sh

# script to help build macnotesapp release
# this is unique to my own dev setup

echo "Cleaning old build and dist directories"
rm -rf dist
rm -rf build

echo "Updating README.md"
cog -r README.md

# build the package
echo "Building package"
python3 -m build

# build CLI executable
echo "Building CLI executable"
pyinstaller macnotesapp.spec

# package executable as DMG
echo "Zipping executable"
test -f dist/notes.zip && rm dist/notes.zip
zip dist/notes.zip dist/notes && rm dist/notes

# Done!
echo "Done"
18 changes: 18 additions & 0 deletions cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
""" stand alone command line script for use with pyinstaller
To build this into an executable:
- install pyinstaller:
python3 -m pip install pyinstaller
- then use make_cli_exe.sh to run pyinstaller
Resulting executable will be in "dist/notes"
Note: This is *not* the cli that "python3 -m pip install macnotesapp" or "python setup.py install" would install;
it's merely a wrapper around __main__.py to allow pyinstaller to work
"""

from macnotesapp.cli import cli_main

if __name__ == "__main__":
cli_main()
55 changes: 55 additions & 0 deletions macnotesapp.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- mode: python ; coding: utf-8 -*-
# spec file for pyinstaller
# run `pyinstaller macnotesapp.spec`


import os
import importlib

pathex = os.getcwd()

from PyInstaller.utils.hooks import collect_data_files

# include necessary data files
datas = collect_data_files("macnotesapp")
datas.extend(
[
("macnotesapp/macnotesapp.applescript", "macnotesapp"),
]
)

block_cipher = None

a = Analysis(
["cli.py"],
pathex=[pathex],
binaries=[],
datas=datas,
hiddenimports=["pkg_resources.py2_warn"],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name="notes",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
)
46 changes: 39 additions & 7 deletions macnotesapp/cli/cli_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from rich.console import Console
from rich.markdown import Markdown

from .click_rich_echo import rich_echo_via_pager
from .click_rich_echo import rich_echo_via_pager, set_rich_console

HELP_WIDTH = 110
HIGHLIGHT_COLOR = "yellow"
Expand All @@ -35,11 +35,19 @@ class RichHelpCommand(click.Command):
"""Custom click.Command that overrides get_help() to print rich help text"""

def get_help(self, ctx):
"""Get help with rich markup"""
help_text = super().get_help(ctx)
formatter = click.HelpFormatter(width=HELP_WIDTH)
formatter.write(rich_text(help_text, width=HELP_WIDTH))
return formatter.getvalue()

def get_help_no_markup(self, ctx):
"""Get help without any rich markup"""
help_text = super().get_help(ctx)
formatter = click.HelpFormatter(width=HELP_WIDTH)
formatter.write(rich_text(help_text, width=HELP_WIDTH, markup=False))
return formatter.getvalue()


@click.command()
@click.option(
Expand All @@ -48,10 +56,13 @@ def get_help(self, ctx):
help="Width of help text",
hidden=True,
)
@click.option(
"--no-markup", is_flag=True, help="Print output with no markup", hidden=True
)
@click.argument("topic", default=None, required=False, nargs=1)
@click.argument("subtopic", default=None, required=False, nargs=1)
@click.pass_context
def help(ctx, topic, subtopic, width, **kw):
def help(ctx, topic, subtopic, width, no_markup, **kw):
"""Print help; for help on commands: help <command>."""
if topic is None:
click.echo(ctx.parent.get_help())
Expand Down Expand Up @@ -82,18 +93,32 @@ def wrap_text(

if subtopic:
cmd = ctx.obj.group.commands[topic]
help_text = get_subtopic_help(cmd, ctx, subtopic)
rich_echo_via_pager(
get_subtopic_help(cmd, ctx, subtopic),
help_text,
# theme=get_theme(),
width=HELP_WIDTH,
color=not no_markup,
)
return

if topic in ctx.obj.group.commands:
ctx.info_name = topic
# click.echo_via_pager(ctx.obj.group.commands[topic].get_help(ctx))
try:
help_text = (
ctx.obj.group.commands[topic].get_help_no_markup(ctx)
if no_markup
else ctx.obj.group.commands[topic].get_help(ctx)
)
except AttributeError:
# Not a RichHelpCommand
help_text = ctx.obj.group.commands[topic].get_help(ctx)

rich_echo_via_pager(
ctx.obj.group.commands[topic].get_help(ctx), width=HELP_WIDTH
help_text,
width=HELP_WIDTH,
color=not no_markup,
)
return

Expand Down Expand Up @@ -202,9 +227,16 @@ def format_help_text(text: str, formatter: click.HelpFormatter):
formatter.write_text(text)


def rich_text(text, width=78, markdown=False):
"""Return rich formatted text"""
console = Console(force_terminal=True, width=width)
def rich_text(text: str, width: int = 78, markdown: bool = False, markup: bool = True):
"""Return rich formatted text
Args:
text: text to format
width: default terminal width
markdown: if True, uses markdown syntax for formatting text
markup: if False, does not use rich markup
"""
console = Console(force_terminal=markup, width=width)
with console.capture() as capture:
console.print(Markdown(text) if markdown else text, end="")
return capture.get()
Expand Down
Loading

0 comments on commit 84c977a

Please sign in to comment.