Gemsmith Documentation

repository·main·Indexed 19 days ago

https://github.com/bkuhlmann/gemsmith

A CLI tool for professional Ruby gem development that extends basic scaffolding with advanced features. Gemsmith provides capabilities for generating CLI skeletons, automated local installation, secure publishing with private registry support, and tools to edit or view installed gems. It includes specialized builders for project structures, integration with Zeitwerk, and automated packaging and pushing workflows including YubiKey OTP support.

Tokens
4.4K
Snippets
22
Records
23
Agent score
67%

What's inside Gemsmith

  1. Configure private gem publishing via gemspec metadata

    main

    To publish to a private server instead of the public RubyGems, add the allowed_push_host key to your gem's metadata. This prevents accidental public publishing and tells Gemsmith which credentials to use from your $HOME/.gem/credentials file.

    Note: The keys and values in the metadata must be strings.

    Gem::Specification.new do |spec|
      spec.metadata = {"allowed_push_host" => "https://private.example.com"}
    end
  2. Install Gemsmith

    main

    You can install Gemsmith using RubyGems. There are two installation methods: one with high security (recommended if you have the public certificate) and one without.

    Install with Security

    If you have the public certificate installed, use the --trust-policy HighSecurity flag. If not, you must first add the certificate.

    Install without Security

    Standard installation via RubyGems.

    # Install with security
    # 💡 Skip this line if you already have the public certificate installed.
    gem cert --add <(curl --compressed --location https://alchemists.io/gems.pem)
    gem install gemsmith --trust-policy HighSecurity
    
    # Install without security
    gem install gemsmith
  3. Build a gem with CLI support

    main

    The build command is the core of Gemsmith. While it supports many Rubysmith flags, the unique --cli flag allows you to build a gem that includes a Command Line Interface.

    Gemsmith uses a mix of Object-Oriented and Functional programming, leveraging OptionParser, Containable, and Infusible. A gem built with --cli includes working specs and provides two namespaces: configuration and CLI for extending functionality.

    By default, the generated CLI supports:

    • -v, --version: Show version
    • -h, --help: Show help
    • config: Manage configuration
    gemsmith build --name demo --cli
  4. Install, Publish, Edit, and View gems

    main

    Gemsmith provides commands to manage gems (both those built with Gemsmith and existing gems with a *.gemspec file):

    • Install: Install a gem locally for testing. Use --install <name> for explicit installation or just --install for the current context.
    • Publish: Publish a gem to RubyGems (or a private server). Requires GPG keys for Git tags and RubyGems MFA (e.g., YubiKey) enabled by default.
    • Edit: Opens an existing local gem in your default editor (or $EDITOR). If multiple versions exist, you will be prompted to choose.
    • View: Opens the gem's documentation in your default browser.
    # Install
    gemsmith --install demo
    
    # Publish
    gemsmith --publish demo
    
    # Edit
    gemsmith --edit demo
    
    # View
    gemsmith --view demo
  5. Structure of the generated Gemsmith CLI entrypoint

    main

    When Gemsmith generates a new project, the executable file located at exe/<project_name> is a Ruby script that serves as the CLI entrypoint. It performs two primary actions:

    1. Requires the main project library using the generated <%= settings.project_path %>.
    2. Instantiates and executes the CLI shell via <%= settings.project_namespaced_class %>::CLI::Shell.new.call.

    This structure ensures that all commands and logic defined within the project's namespaced CLI module are available to the user via the command line.

    #! /usr/bin/env ruby
    
    require "<%= settings.project_path %>"
    
    <%= settings.project_namespaced_class %>::CLI::Shell.new.call
  6. Configure Gemsmith

    main

    Gemsmith can be configured via a global YAML file or XDG environment variables.

    Global Config Path: $HOME/.config/gemsmith/configuration.yml

    Key Configuration Sections:

    • build: Controls build defaults (e.g., cli: false).
    • project: Contains URI information. Providing these keys ensures they are automatically added to your generated gemspec and project documentation.
    build:
      cli: false
    
    project:
      uri:
        # Add sub-key values here.
  7. Automate gem publishing with GitHub Actions

    main

    You can automate the publishing process using GitHub Actions. The following workflow checks if a new Git tag exists (indicating a new version) and runs gemsmith --publish if it does. It also handles setting up the necessary .gem/credentials file using GitHub Secrets.

    name: Gemsmith
    
    on:
      push:
        branches: main
    
    jobs:
      build:
        runs-on: ubuntu-latest
        container:
          image: ruby:latest
        permissions:
          contents: write
          packages: write
    
        steps:
          - name: Checkout
            uses: actions/checkout@v4
            with:
              fetch-depth: '0'
              ref: ${{github.head_ref}}
          - name: Setup
            run: |
              git config user.email "engineering@example.com"
              git config user.name "Gemsmith Publisher"
              mkdir -p $HOME/.gem
              printf "%s\n" "https://rubygems.pkg.github.com/example: Bearer ${{secrets.GITHUB_TOKEN}}" > $HOME/.gem/credentials
              chmod 0600 $HOME/.gem/credentials
          - name: Install
            run: gem install gemsmith
          - name: Publish
            run: |
              if git describe --tags --abbrev=0 > /dev/null 2>&1; then
                gemsmith --publish
              else
                printf "First gem version must be manually created. Skipping.\n"
              fi
  8. Configure credentials for private gem servers

    main

    Gemsmith uses the standard RubyGems credentials file located at $HOME/.gem/credentials. To support private servers, add a line where the key is the URL of your private server.

    Example configuration for both public RubyGems and a private server:

    :rubygems_api_key: 2a0b460650e67d9b85a60e183defa376
    https://private.example.com: Basic dXNlcjpwYXNzd29yZA==
  9. Build a gem package with Gemsmith::Tools::Packager

    main

    The Gemsmith::Tools::Packager class is used to build a gem package for distribution. It wraps the standard RubyGems build command. When a specification is passed to the call method, it executes the build process and then attempts to copy the resulting .gem file to its parent directory (the directory containing the gem file) to ensure the package is placed alongside the specification.

    If the build fails due to a Gem::Exception, the method returns a Failure monad containing the error message. On success, it returns a Success monad containing the specification object.

    # Example usage of Packager
    # Assumes 'specification' is an object responding to .name and .package_path
    
    packager = Gemsmith::Tools::Packager.new
    result = packager.call(specification)
    
    if result.success?
      puts "Successfully built: #{result.value!.name}"
    else
      puts "Build failed: #{result.failure}"
    end
  10. Push a gem package to a remote server with Pusher

    main

    The Gemsmith::Tools::Pusher class is used to push a gem package to a remote gem server. It wraps the standard RubyGems push command.

    When pushing, the tool attempts to automatically include a One-Time Password (OTP) if a YubiKey is detected via the ykman (YubiKey Manager) CLI tool. If ykman is not found or the OTP cannot be retrieved, it falls back to a standard push without the --otp flag.

    Behavior:

    • Success: Returns Success(specification) where specification is the gem specification object.
    • Failure: Returns Failure(error_message) if a Gem::Exception occurs during the push process.
    # Example usage of Pusher
    pusher = Gemsmith::Tools::Pusher.new
    result = pusher.call(gem_specification)
    
    if result.success?
      puts "Gem pushed successfully: #{result.value!}"
    else
      puts "Push failed: #{result.failure}"
    end
  11. Build project skeleton CLI templates

    main

    The Gemsmith::Builders::CLI class is responsible for generating the file structure and source code for a CLI-based Ruby project. It renders several key components including the executable, core library files, configuration files, shell logic, and test specifications.

    To trigger the CLI template generation, the build_cli setting must be enabled in your configuration.

    # The CLI builder is activated via the settings object
    settings.build_cli = true
  12. Access the Gemsmith Zeitwerk loader

    main

    The Gemsmith.loader method provides access to the Zeitwerk::Loader instance used by the library. This is useful if you need to inspect the loaded constants or the registry within the Gemsmith namespace. The loader uses custom inflections for CLI, CircleCI, and RSpec.

    # Access the loader instance
    loader = Gemsmith.loader