Penelope the Cat

CLI Tools

NeoCities Command Line Interface Tools

As I’ve mentioned before, I currently manage the site by hand. It didn’t take much time fiddling with the NeoCities web file manager before I decided I needed something a little snappier that would quickly allow me to upload files from CLI. This is a collection of ongoing notes.

You’ll need an API Key, which can be generated. The easiest way to do this is probably using CURL. You can assign this key to the Environmental Variable NEOCITIES_API_KEY (System > Advanced System Settings > Advanced > Environmental Variable) .

> curl "https://USER:PASS@neocities.org/api/key"

neocitizen: Python client library for Neocities API

There’s a new library in town, poyo46’s neocitizen. I found the documentation on this a bit non-intuitive and ended up digging through the code to try to understand how it works.

  F:\NeoCities>neocitizen upload --help
  Usage: neocitizen upload [OPTIONS]

    Upload local data to your Neocities site.

  Options:
    -d, --dir PATH        Local directory path.
    --dir-on-server TEXT  Destination directory.
    -f, --file TEXT       (local file path):(path on server)
    --help                Show this message and exit.
To upload a specific directory:

I ran into trouble trying to upload individual files, and consulted the code to see what arguments were expected to make sure I wasn't messing them up.

@pytest.mark.parametrize(
    "args, expected_code",
    [
        (["upload"], 0),
        (["upload", "--dir=foo"], 0),
        (["upload", "--dir=foo", "--dir-on-server=bar"], 0),
        (["upload", "--file=foo.txt"], 0),
        (["upload", "--file=foo.txt:dir/foo.txt", "--file=bar.txt:dir/bar.txt"], 0),
        (["upload", "--file=foo.txt:dir1/foo.txt:dir2/foo.txt"], 1),
    ],
)

Based on the above code, it seems this should work, right?

F:\NeoCities>neocitizen upload --file=iguess.webp
Traceback (most recent call last):
  File "c:\python36\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\python36\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Python36\Scripts\neocitizen.exe\__main__.py", line 7, in 
  File "c:\python36\lib\site-packages\click\core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "c:\python36\lib\site-packages\click\core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "c:\python36\lib\site-packages\click\core.py", line 1659, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "c:\python36\lib\site-packages\click\core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "c:\python36\lib\site-packages\click\core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "c:\python36\lib\site-packages\neocitizen\cli.py", line 55, in upload
    raise BadParameter
TypeError: __init__() missing 1 required positional argument: 'message'

How about this?

F:\NeoCities>neocitizen upload --file=iguess.webp:iguess.webp
Traceback (most recent call last):
  File "c:\python36\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\python36\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Python36\Scripts\neocitizen.exe\__main__.py", line 7, in 
  File "c:\python36\lib\site-packages\click\core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "c:\python36\lib\site-packages\click\core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "c:\python36\lib\site-packages\click\core.py", line 1659, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "c:\python36\lib\site-packages\click\core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "c:\python36\lib\site-packages\click\core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "c:\python36\lib\site-packages\neocitizen\cli.py", line 56, in upload
    api.upload_files(file_map={paths[0]: paths[1]})
  File "c:\python36\lib\site-packages\neocitizen\api.py", line 266, in upload_fi
les
    extension = get_extension(local_path)
  File "c:\python36\lib\site-packages\neocitizen\utils.py", line 31, in get_exte
nsion
    i = path.name.rfind(".")
AttributeError: 'str' object has no attribute 'name'

Assuming I haven't done something horribly wrong.... IDK maybe I did do something horribly wrong. The library is packed into an exe so I would have to figure out how to unpack it to make any changes.

Worth it?

I GUESS

It's a quick way to upload a directory from the CLI.

Neocities Ruby Gem (a.k.a. THAT OLD CHESTNUT)

I have long wrestled with my ancient nemesis, Ruby CLI, on Windows. Previously:

  F:\NeoCities>gem install neocities
  ERROR:  Could not find a valid gem 'neocities' (>= 0), here is why:
    Unable to download data from https://rubygems.org/ - SSL_connect retur
  ned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (
  https://api.rubygems.org/specs.4.8.gz)

I’ve tried many things to remove that certificate error, which even prevents me from updating gem itself. This time I did a fresh install of the latest version of Ruby. I found I was able to install some gems, but when I tried neocities…

>gem install neocities
Temporarily enhancing PATH to include DevKit...
Building native extensions. This could take a while...
ERROR:  Error installing neocities:
ERROR: Failed to build gem native extension.

current directory: C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/neocities-0.0.15/ext
C:/Ruby23-x64/bin/ruby.exe mkrf_conf.rb
mkrf_conf.rb:4:in '
': uninitialized constant Gem::Command (NameError) rake failed, exit code 1 Gem files will remain installed in C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/neocit ies-0.0.15 for inspection. Results logged to C:/Ruby23-x64/lib/ruby/gems/2.3.0/extensions/x64-mingw32/2.3.0 /neocities-0.0.15/gem_make.out

I tried updating Ruby Gems to no effect. I knew I couldn’t be the only person having this issue, and fortunately I wasn’t the only person posting about it either. Mere days ago kingpanther13 provided a solution for issue #35, uninitialized constant Gem::Command (NameError) when building extension:

For anyone else having this issue, I spent hours trying to get the neocities CLI to work using Ruby 3.1 and kept getting this same error no matter what I tried, using #34 , using the instructions given by Jamesgecko, everything. I finally decided to do a fresh install of Ruby for version 2.7.5 with the devkit, and just simply using "gem install neocities" worked like a charm on the first try.

So I uninstalled Ruby 3.1.1 and installed Ruby 2.7.5… and it worked! My only lingering issue was using cmd as an Administrator from within Atom. I found setting Atom’s program properties to administrator allowed this.

Example usage for uploading the local file images/iguess.webp to the images directory:

> neocities upload -d images images/iguess.webp

One thing I like about the NeoCities CLI is it won’t upload the file if a matching version exists on the server. One thing I don't like is it's all or nothing--you can either upload an individual file or you can push the entire site to root, but you can't upload a specific directory like you can with neocitizen.

I spent a significant amount of time troubleshooting this gem. Was it worth it?

I GUESS

If I want to take advantage of push I have to keep my local files much cleaner than they are now.

Shadowm00n's Neocities Management Script

Shadowm00n's Neocities Management Script is a Perl-based client. Haven't investigated it yet.