Gumroad Documentation
repository·main·Indexed 27 days ago
https://github.com/antiwork/gumroadSource 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.
What's inside gumroad
- 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.
Remove a batch of jobs from a Sidekiq queue
mainIf 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_totalAccess container statistics via Hashi-UI
mainTo 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:
- Navigate to the
nomaddirectory. - Source the proxy functions.
- Enable the production proxy.
- Access the UI in your browser.
```bash $ cd nomad $ source nomad_proxy_functions.sh $ proxy_on productionThen navigate to
http://localhost:8080/nomad/production/allocationsto see containers and their status.- Navigate to the
Use the Tailwind CSS Migration Prompt for AI assistants
mainWhen 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:
- Copy the prompt text provided in the documentation.
- Paste it into your AI assistant (e.g., ChatGPT, Claude).
- 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 beneficialFind purchases and process refunds
mainLocate 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==").purchasesRecipe for migrating a single spec file
mainFollow these steps to migrate a single spec file to Minitest:
- Analyze: Identify all
create(...)orlet(...)objects. Decide if they should be shared fixture rows or test-specific mutations. - Prepare Fixtures: Add rows to
test/fixtures/<table_name>.yml. Ensure columns withattribute ... default:andvalidates ... inclusion:are explicitly spelled out. - Write Test: Create
test/<path>/<name>_test.rb.- Convert
describe/ittotest "...". - Convert
expect(x).to eq(y)toassert_equal y, x. - For policies, use
assert_policy_permitsorrefute_policy_permitsfromtest/support/policy_assertions.rb.
- Convert
- Execute: Run the specific test using:
RAILS_ENV=test bin/rails test test/<path>/<name>_test.rb. - Cleanup:
git rmthe old spec file and runrubocopon the new files. - Document: In the PR, list the files moved, compare example counts, and include the
rails testoutput.
RAILS_ENV=test bin/rails test test/<path>/<name>_test.rb- Analyze: Identify all
Run long-running tasks using `web_server_generic`
mainFor long-running tasks in production, use the
web_server_genericinstance. Note: Only oneweb_server_genericinstance can run at a time. Ask in the#engineeringSlack channel before redeploying it.1. Start the generic web server
export DEPLOY_TAG=production-<revision> cd nomad/production && ./start_generic_web.shOpen 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.xwith your instance IP) to open a persistent Rails console viascreen:INSTANCE_IP=10.1.x.x COMMAND="screen -adR" ./console.sh bundle exec rails cIf your connection is interrupted, run the same
INSTANCE_IP=10.1.x.x COMMAND="screen -adR" ./console.shcommand 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 productionInstall and configure Redis on macOS
mainInstall Redis via Homebrew, start the service using
brew servicesso it persists across logins, and verify the installation by pinging the server withredis-cli.brew install redis brew services start redis # Test if Redis server is running redis-cli pingConfigure macOS for Spring/Objective-C
mainOn 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=YESexport OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YESClean up old deployment releases to free disk space
mainIf a server disk is full due to large deployment directories:
- SSH into the worker.
- Identify the current active deployment directory:
(Note the directory thatgrd cd .. ls -lcurrentlinks to). - Navigate to the releases directory:
cd releases ls -l - Remove all past deployment directories using
rm -rf [name], ensuring you do not remove the directory thatcurrentis currently pointing to.
If the
currentdirectory itself is too large, you must deploy a code change to production to create a newcurrentlink, then remove the old directory.Mark users compliant (unsuspend users)
mainTo unsuspend users and mark them as compliant, iterate through
user_idsand calluser.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 endAccess logs for the production environment
mainTo view the logs for the production environment, navigate to the
nomad/productiondirectory and execute thelogs.shscript.cd nomad/production && ./logs.sh