Gumroad Documentation

repository·main·Indexed 27 days ago

https://github.com/antiwork/gumroad

Source code and development guides for the Gumroad e-commerce platform. Built with Ruby on Rails and Node.js, the documentation covers local environment setup using Docker, dependency installation, testing with RSpec and Capybara, and administrative tasks such as generating accounting reports and managing Elasticsearch indices.

Tokens
25.6K
Snippets
75
Records
144
Agent score
94%

What's inside gumroad

  1. Understand Gumroad's Tax Collection Model

    main
    As of 2025, Gumroad operates as a Merchant of Record. This means Gumroad is responsible for collecting and remitting taxes on behalf of the creator for all jurisdictions where Gumroad meets specific tax collection requirements. Creators do not need to manage tax collection themselves.
  2. Remove a batch of jobs from a Sidekiq queue

    main

    If you have enqueued too many jobs, or jobs in the wrong queue, you can delete them by iterating through a Sidekiq::Queue. The following pattern deletes jobs in batches (e.g., 500 at a time) to avoid long-running loops and allows for a running total of deleted jobs.

    def delete_batch_of_jobs
      i = 0
      queue = Sidekiq::Queue.new('default')
      jobs = []
      queue.each do |job|
        if job.klass == "ElasticsearchIndexerWorker" && job.args[1]['class_name'] == 'Purchase::Indices::V999'
          i += 1
          jobs << job
          break if i == 500
        end
      end
      jobs.each(&:delete)
      i
    end
    
    def delete_jobs_with_running_total
      total = 0
      loop do
        deleted = delete_batch_of_jobs
        total += deleted
        puts "[#{Time.now}] Total deleted: #{total}"
        break if deleted == 0
      end
      total
    end
    
    delete_jobs_with_running_total
  3. Access container statistics via Hashi-UI

    main

    To inspect container status and statistics, use the Hashi-UI proxy. This is useful for checking if containers are having issues communicating with Vault by reviewing CPU/Disk status of steward servers.

    Follow these steps:

    1. Navigate to the nomad directory.
    2. Source the proxy functions.
    3. Enable the production proxy.
    4. Access the UI in your browser.
    ```bash
    $ cd nomad
    $ source nomad_proxy_functions.sh
    $ proxy_on production

    Then navigate to http://localhost:8080/nomad/production/allocations to see containers and their status.

  4. Use the Tailwind CSS Migration Prompt for AI assistants

    main

    When migrating CSS components to Tailwind CSS using an AI assistant, use the provided prompt to enforce strict styling guidelines. This ensures the output adheres to project standards regarding utility usage, responsive design, and conditional class management.

    Usage Instructions:

    1. Copy the prompt text provided in the documentation.
    2. Paste it into your AI assistant (e.g., ChatGPT, Claude).
    3. Append the source component code you wish to migrate immediately after the prompt.
    I'm migrating a CSS component to Tailwind CSS. Please help me convert the existing styles following these strict guidelines:
    
    ### ❌ Not Allowed
    1. **No `@apply` directives** - All Tailwind classes must be applied directly in the markup, not through CSS files
    2. **No new utility classes** - Only use Tailwind's built-in utilities; don't create custom ones
    3. **No arbitrary values without justification** - Avoid `[#hex]`, `[10px]`, etc. Use design system tokens. If you must use arbitrary values, explain why they're necessary
    4. **No inline styles** - All styling must be done through Tailwind classes
    
    ### ✅ Required Practices
    
    #### Mobile-First Responsive Design
    - Design mobile-first and use `sm:`, `md:`, `lg:` prefixes only where values actually change
    - Don't prefix every utility with a breakpoint if the value is the same
    
    #### Conditional Classes
    - Prefer using our `classNames` utility over `cx`, `twMerge`, or ternary operators
    
    #### Avoid `!important`
    - Avoid bang modifiers (`mt-2!`, `flex!`, etc.)
    - Instead, investigate the root cause of the specificity conflict and fix it properly
    
    #### Typography
    - Consider using the `prose` plugin for content-heavy areas with paragraphs, lists, and rich text
    
    ### Output Requirements
    1. Show the complete component with all Tailwind classes applied
    2. Explain any arbitrary values you had to use (with justification)
    3. Point out any specificity issues you encountered and how you resolved them
    4. Note any areas where the `prose` plugin might be beneficial
  5. Find purchases and process refunds

    main

    Locate specific transactions by email or ID and execute refunds.

    # Find purchase ID by creator and customer email
    User.find_by(email: 'creator@example.com').sales.successful.where(email: 'customer@example.com').pluck(:id, :created_at, :stripe_transaction_id, :total_transaction_cents)
    
    # Find purchase ID by customer email (last 25)
    Purchase.successful.where(email: 'customer@example.com').select(:id, :created_at, :stripe_transaction_id, :total_transaction_cents).last(25)
    
    # Process refund by purchase ID
    Purchase.find(purchase_id).refund!(refunding_user_id: GUMROAD_ADMIN_ID)
    
    # Process refund by external ID
    Purchase.find_by_external_id(purchase_external_id).refund!(refunding_user_id: GUMROAD_ADMIN_ID)
    
    # Find PayPal purchases from a charge ID
    Charge.find_by_external_id("abcdefghijklmno==").purchases
  6. Recipe for migrating a single spec file

    main

    Follow these steps to migrate a single spec file to Minitest:

    1. Analyze: Identify all create(...) or let(...) objects. Decide if they should be shared fixture rows or test-specific mutations.
    2. Prepare Fixtures: Add rows to test/fixtures/<table_name>.yml. Ensure columns with attribute ... default: and validates ... inclusion: are explicitly spelled out.
    3. Write Test: Create test/<path>/<name>_test.rb.
      • Convert describe/it to test "...".
      • Convert expect(x).to eq(y) to assert_equal y, x.
      • For policies, use assert_policy_permits or refute_policy_permits from test/support/policy_assertions.rb.
    4. Execute: Run the specific test using: RAILS_ENV=test bin/rails test test/<path>/<name>_test.rb.
    5. Cleanup: git rm the old spec file and run rubocop on the new files.
    6. Document: In the PR, list the files moved, compare example counts, and include the rails test output.
    RAILS_ENV=test bin/rails test test/<path>/<name>_test.rb
  7. Run long-running tasks using `web_server_generic`

    main

    For long-running tasks in production, use the web_server_generic instance. Note: Only one web_server_generic instance can run at a time. Ask in the #engineering Slack channel before redeploying it.

    1. Start the generic web server

    export DEPLOY_TAG=production-<revision>
    cd nomad/production && ./start_generic_web.sh

    Open the URL provided by the command and note the client IP address from the hostname.

    2. Access the Rails console

    SSH into the server and use the following command (replacing 10.1.x.x with your instance IP) to open a persistent Rails console via screen:

    INSTANCE_IP=10.1.x.x COMMAND="screen -adR" ./console.sh
    bundle exec rails c

    If your connection is interrupted, run the same INSTANCE_IP=10.1.x.x COMMAND="screen -adR" ./console.sh command again to reconnect.

    3. Stop the instance

    Once the task is complete, stop the instance using these commands:

    cd nomad
    source nomad_proxy_functions.sh
    proxy_off production; proxy_on production
    nomad_insecure_wrapper stop -purge web_server_generic
    proxy_off production
  8. Configure macOS for Spring/Objective-C

    main

    On macOS, Spring's fork-based workers may crash due to Apple's Objective-C runtime. To prevent this, add the following to your shell profile:

    export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

    export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
  9. Clean up old deployment releases to free disk space

    main

    If a server disk is full due to large deployment directories:

    1. SSH into the worker.
    2. Identify the current active deployment directory:
      grd
      cd ..
      ls -l
      (Note the directory that current links to).
    3. Navigate to the releases directory:
      cd releases
      ls -l
    4. Remove all past deployment directories using rm -rf [name], ensuring you do not remove the directory that current is currently pointing to.

    If the current directory itself is too large, you must deploy a code change to production to create a new current link, then remove the old directory.

  10. Mark users compliant (unsuspend users)

    main

    To unsuspend users and mark them as compliant, iterate through user_ids and call user.mark_compliant!(author_name: "...").

    user_ids = []
    
    users = User.find(user_ids)
    users.each do |user|
      begin
        user.mark_compliant!(author_name: "Iffy")
      rescue => e
        puts "Error processing user #{user.id}: #{e.message}"
      end
    end