Instruction file imported from bossjones/zsh-dotfiles-prep (
.cursor/rules/gh_actions.mdc). Copyright stays with the author.
Homebrew Tap Formula Creation Guide
Standards and best practices for creating and maintaining Homebrew tap formulas.
actions:
-
type: suggest message: |
Homebrew Tap Formula Creation Guide
This guide provides standards and best practices for creating and maintaining Homebrew tap formulas.
Formula Structure
A Homebrew formula is a Ruby file that describes how to install a package. Here's the standard structure:
# typed: false # frozen_string_literal: true class PackageName < Formula desc "Short description of the package" homepage "https://example.com/package" url "https://example.com/package-1.0.0.tar.gz" sha256 "checksum_of_the_package" license "MIT" # or appropriate license # Optional: specify head for development version head "https://github.com/user/package.git", branch: "main" # Dependencies depends_on "dependency1" depends_on "dependency2" depends_on "dependency3" => :optional # Optional dependency # Platform-specific code on_macos do if Hardware::CPU.intel? # Intel-specific code end if Hardware::CPU.arm? # ARM-specific code end end on_linux do if Hardware::CPU.intel? # Linux Intel-specific code end if Hardware::CPU.arm? # Linux ARM-specific code end end def install # Installation steps system "./configure", "--prefix=#{prefix}" system "make", "install" # Install completions if available bash_completion.install "completions/package.bash" fish_completion.install "completions/package.fish" zsh_completion.install "completions/_package" end def caveats <<~EOS Additional information or warnings for users. For example, how to enable the package in their shell. EOS end test do # Test commands to verify installation system "#{bin}/package", "--version" end endVersioned Formulas
For versioned formulas (e.g.,
package@1.0.0), follow this naming convention:class PackageAT100 < Formula # Formula content endBest Practices
1. Formula Naming
- Use lowercase for formula names
- For versioned formulas, use
@followed by the version number - Convert version dots to underscores in the class name (e.g.,
1.0.0becomes100)
2. Dependencies
- Only include direct dependencies
- Use
:optionalfor optional dependencies - Use
:recommendedfor recommended but not required dependencies - Use
:buildfor build-time dependencies
depends_on "openssl@3" depends_on "readline" depends_on "python" => :optional depends_on "cmake" => :build3. Installation
- Use
#{prefix}for the installation prefix - Use
#{bin},#{lib}, etc. for standard directories - Install documentation to
#{doc} - Install completions to
bash_completion,fish_completion, andzsh_completion
4. Platform-Specific Code
- Use
on_macosandon_linuxblocks for platform-specific code - Check CPU architecture with
Hardware::CPU.intel?andHardware::CPU.arm? - Provide separate URLs and installation steps if necessary
5. Testing
- Include a
testblock to verify the installation - Test that the binary runs and produces expected output
- Keep tests simple and fast
6. Caveats
- Use the
caveatsmethod to provide additional information - Include instructions for enabling the package
- Mention any post-installation steps
Common Patterns
Installing from GitHub Releases
url "https://github.com/user/repo/archive/refs/tags/v#{version}.tar.gz"Installing Binary Releases
if OS.mac? && Hardware::CPU.intel? url "https://example.com/package-#{version}-mac-x86_64.tar.gz" # ... elsif OS.mac? && Hardware::CPU.arm? url "https://example.com/package-#{version}-mac-arm64.tar.gz" # ... elsif OS.linux? && Hardware::CPU.intel? url "https://example.com/package-#{version}-linux-x86_64.tar.gz" # ... endInstalling from Git
url "https://github.com/user/repo.git", tag: "v#{version}"Building from Source
def install system "./configure", "--disable-debug", "--disable-dependency-tracking", "--disable-silent-rules", "--prefix=#{prefix}" system "make", "install" endInstalling a Go Package
def install system "go", "build", *std_go_args(ldflags: "-s -w") endInstalling a Node.js Package
def install system "npm", "install", *Language::Node.std_npm_install_args(libexec) bin.install_symlink Dir["#{libexec}/bin/*"] endASDF Version Manager Formulas
For ASDF version manager formulas, follow these additional guidelines:
-
Disable automatic updates by creating an empty file:
touch libexec/"asdf_updates_disabled" -
Install the entire package to
libexecand create a script inbin:libexec.install Dir["*"] bin.write_exec_script libexec/"bin/asdf" -
Include appropriate caveats for shell initialization:
def caveats s = "To use asdf, add the following line to your #{shell_profile}:\n" s += if preferred == :fish " source #{opt_libexec}/asdf.fish\n\n" else " . #{opt_libexec}/asdf.sh\n\n" end s += "Restart your terminal for the settings to take effect." s end
examples:
-
input: |
Creating a formula for a simple CLI tool
I need to create a formula for a CLI tool that's distributed as a tarball. output: | Here's a formula for a simple CLI tool distributed as a tarball:
# typed: false # frozen_string_literal: true class SimpleCli < Formula desc "Command-line tool for doing simple things" homepage "https://example.com/simple-cli" url "https://example.com/downloads/simple-cli-1.2.3.tar.gz" sha256 "abcdef1234567890abcdef1234567890abcdef1234567890" license "MIT" depends_on "libfoo" depends_on "libbar" def install system "./configure", "--prefix=#{prefix}" system "make" system "make", "install" # Install completions bash_completion.install "completions/simple-cli.bash" fish_completion.install "completions/simple-cli.fish" zsh_completion.install "completions/_simple-cli" end test do assert_match "simple-cli version 1.2.3", shell_output("#{bin}/simple-cli --version") end endThis formula:
- Defines metadata (description, homepage, URL, checksum, license)
- Specifies dependencies
- Implements the installation process
- Installs shell completions
- Includes a test to verify the installation
-
input: |
Creating a versioned formula for a specific version of a tool
I need to create a formula for version 2.1.0 of a tool that already has a formula for the latest version. output: | Here's how to create a versioned formula for a specific version:
# typed: false # frozen_string_literal: true class ToolAT210 < Formula desc "Specific version (2.1.0) of the tool" homepage "https://example.com/tool" url "https://example.com/downloads/tool-2.1.0.tar.gz" sha256 "abcdef1234567890abcdef1234567890abcdef1234567890" license "Apache-2.0" keg_only :versioned_formula depends_on "dependency1" depends_on "dependency2" def install system "./configure", "--prefix=#{prefix}" system "make", "install" end test do assert_match "2.1.0", shell_output("#{bin}/tool --version") end endKey points for versioned formulas:
- Name the class with
ATfollowed by the version with dots replaced by underscores - Add
keg_only :versioned_formulato prevent conflicts with the main formula - Specify the exact version in the URL and tests
- Include appropriate dependencies for this specific version
- Name the class with
-
input: |
Creating a formula for a tool with platform-specific binaries
I need to create a formula for a tool that provides different binaries for different platforms. output: | Here's a formula for a tool with platform-specific binaries:
# typed: false # frozen_string_literal: true class PlatformTool < Formula desc "Tool with platform-specific binaries" homepage "https://example.com/platform-tool" version "3.0.1" license "MIT" on_macos do if Hardware::CPU.intel? url "https://example.com/downloads/platform-tool-3.0.1-macos-x86_64.tar.gz" sha256 "abcdef1234567890abcdef1234567890abcdef1234567890" def install bin.install "platform-tool" # Mac Intel-specific installation steps end end if Hardware::CPU.arm? url "https://example.com/downloads/platform-tool-3.0.1-macos-arm64.tar.gz" sha256 "123456abcdef123456abcdef123456abcdef123456abcdef" def install bin.install "platform-tool" # Mac ARM-specific installation steps end end end on_linux do if Hardware::CPU.intel? url "https://example.com/downloads/platform-tool-3.0.1-linux-x86_64.tar.gz" sha256 "fedcba0987654321fedcba0987654321fedcba0987654321" def install bin.install "platform-tool" # Linux Intel-specific installation steps end end if Hardware::CPU.arm? url "https://example.com/downloads/platform-tool-3.0.1-linux-arm64.tar.gz" sha256 "abcdef1234567890abcdef1234567890abcdef1234567890" def install bin.install "platform-tool" # Linux ARM-specific installation steps end end end test do assert_match "3.0.1", shell_output("#{bin}/platform-tool --version") end endThis formula:
- Uses platform-specific blocks with
on_macosandon_linux - Checks CPU architecture with
Hardware::CPU.intel?andHardware::CPU.arm? - Provides different URLs, checksums, and installation steps for each platform
- Includes a common test that works across all platforms
- Uses platform-specific blocks with
metadata: priority: high version: 1.0 tags: - homebrew - formula - package-management - ruby
Additional Resources
Official Documentation
Tools for Formula Development
brew create URL- Create a formula templatebrew audit --strict --online formula- Check formula for issuesbrew style formula- Check formula stylebrew test formula- Run formula tests
Common Formula Patterns
Go Packages
class GoPackage < Formula
desc "Go package description"
homepage "https://example.com/gopackage"
url "https://github.com/user/gopackage/archive/v1.0.0.tar.gz"
sha256 "checksum"
license "MIT"
head "https://github.com/user/gopackage.git", branch: "main"
depends_on "go" => :build
def install
system "go", "build", *std_go_args(ldflags: "-s -w -X main.version=#{version}")
end
test do
assert_match version.to_s, shell_output("#{bin}/gopackage version")
end
end
Ruby Gems
class RubyGem < Formula
desc "Ruby gem description"
homepage "https://example.com/rubygem"
url "https://github.com/user/rubygem/archive/v1.0.0.tar.gz"
sha256 "checksum"
license "MIT"
depends_on "ruby"
def install
ENV["GEM_HOME"] = libexec
system "gem", "build", "rubygem.gemspec"
system "gem", "install", "rubygem-#{version}.gem"
bin.install Dir["#{libexec}/bin/*"]
bin.env_script_all_files(libexec/"bin", GEM_HOME: ENV["GEM_HOME"])
end
test do
assert_match version.to_s, shell_output("#{bin}/rubygem --version")
end
end
Python Packages
class PythonPackage < Formula
include Language::Python::Virtualenv
desc "Python package description"
homepage "https://example.com/pythonpackage"
url "https://files.pythonhosted.org/packages/source/p/pythonpackage/pythonpackage-1.0.0.tar.gz"
sha256 "checksum"
license "MIT"
depends_on "python@3.10"
resource "dependency" do
url "https://files.pythonhosted.org/packages/source/d/dependency/dependency-2.0.0.tar.gz"
sha256 "dependency-checksum"
end
def install
virtualenv_install_with_resources
end
test do
assert_match version.to_s, shell_output("#{bin}/pythonpackage --version")
end
end
Node.js Packages
class NodePackage < Formula
desc "Node.js package description"
homepage "https://example.com/nodepackage"
url "https://registry.npmjs.org/nodepackage/-/nodepackage-1.0.0.tgz"
sha256 "checksum"
license "MIT"
depends_on "node"
def install
system "npm", "install", *Language::Node.std_npm_install_args(libexec)
bin.install_symlink Dir["#{libexec}/bin/*"]
end
test do
assert_match version.to_s, shell_output("#{bin}/nodepackage --version")
end
end
GitHub Actions Workflow Debugging Guide
Standards and best practices for debugging GitHub Actions workflows using the GitHub CLI.
actions:
-
type: suggest message: |
GitHub Actions Workflow Debugging Guide
This guide provides standards and best practices for debugging GitHub Actions workflows using the GitHub CLI.
GitHub CLI Workflow Debugging Commands
The GitHub CLI (
gh) provides several commands for debugging workflows:# List workflow runs to identify failed runs gh run list # View details of a specific workflow run gh run view <run-id> # View logs of a specific workflow run gh run view <run-id> --log # Download logs of a specific workflow run gh run view <run-id> --log-failed --log-file=logs.txt # Watch a running workflow gh run watch <run-id> # Re-run a failed workflow gh run rerun <run-id>Common Options
All
gh runcommands support these options:-R, --repo <[HOST/]OWNER/REPO> # Select another repositoryWorkflow Debugging Best Practices
1. Identifying Failed Workflows
List recent workflow runs to identify failures:
# List all recent workflow runs gh run list # List only failed workflow runs gh run list --status failed # List runs for a specific workflow gh run list --workflow "CI"2. Examining Workflow Run Details
View detailed information about a workflow run:
# View run details gh run view <run-id> # View run details in web browser gh run view <run-id> --web3. Analyzing Workflow Logs
Access and analyze workflow logs:
# View complete logs gh run view <run-id> --log # View only logs from failed steps gh run view <run-id> --log-failed # Download logs for offline analysis gh run view <run-id> --log --log-file=workflow-logs.txt4. Debugging with Workflow Re-runs
Re-run workflows for debugging:
# Re-run a failed workflow gh run rerun <run-id> # Re-run a workflow from a specific failed job gh run rerun <run-id> --failed5. Using Debug Logging
Enable debug logging in workflows:
# Add this to your workflow file env: ACTIONS_RUNNER_DEBUG: true ACTIONS_STEP_DEBUG: trueOr set repository secrets for debugging:
ACTIONS_RUNNER_DEBUG: true ACTIONS_STEP_DEBUG: true6. Using the Debug Action
Add a debugging step to your workflow:
steps: - name: Debug uses: actions/github-script@v6 with: script: | console.log(context) console.log(github)7. Using tmate for Interactive Debugging
Add tmate to your workflow for SSH access:
steps: - name: Setup tmate session uses: mxschmitt/action-tmate@v3 with: limit-access-to-actor: trueCommon Debugging Scenarios
Debugging Environment Variables
steps: - name: Debug environment variables run: env | sortDebugging Context Information
steps: - name: Debug context run: | echo "github.event_name: ${{ github.event_name }}" echo "github.ref: ${{ github.ref }}" echo "github.sha: ${{ github.sha }}" echo "github.workflow: ${{ github.workflow }}"Debugging File System
steps: - name: Debug file system run: | pwd ls -la find . -type f -name "*.yml" | sort
examples:
-
input: |
How do I check why my workflow failed?
output: | To check why your workflow failed, follow these steps:
- First, list your recent workflow runs to identify the failed run:
gh run list --status failed- Note the run ID of the failed workflow, then view the details:
gh run view <run-id>- To see the logs from the failed steps:
gh run view <run-id> --log-failed- For a more detailed analysis, you can view the complete logs:
gh run view <run-id> --log- If you prefer to analyze the logs in your browser:
gh run view <run-id> --webThis will open the GitHub Actions web interface where you can see a visual representation of the workflow run and detailed logs for each step.
If you need to download the logs for offline analysis:
gh run view <run-id> --log --log-file=debug-logs.txt -
input: |
How can I debug a workflow interactively?
output: | To debug a GitHub Actions workflow interactively, you can use tmate to get SSH access to the runner. Here's how:
- Add the tmate action to your workflow file:
name: Debugging with tmate on: workflow_dispatch: # Allows manual triggering jobs: debug: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 # Add any steps that come before the failure # Add this step to enable SSH access - name: Setup tmate session uses: mxschmitt/action-tmate@v3 with: limit-access-to-actor: true # Only allow the workflow triggerer to connect- Trigger the workflow manually:
gh workflow run "Debugging with tmate"- Watch the workflow run:
gh run watch-
When the tmate step executes, it will output SSH connection details in the logs. You can use these details to connect to the runner via SSH and debug interactively.
-
After you're done debugging, you can exit the SSH session, and the workflow will continue.
This approach is extremely useful for:
- Inspecting the environment
- Testing commands manually
- Checking file contents
- Verifying permissions
- Debugging complex issues that are hard to diagnose from logs alone
-
input: |
How do I enable debug logging in GitHub Actions?
output: | To enable debug logging in GitHub Actions, you have two options:
Option 1: Add debug environment variables to your workflow
Add these environment variables to your workflow file:
name: Workflow with Debug Logging on: push: branches: [main] env: ACTIONS_RUNNER_DEBUG: true ACTIONS_STEP_DEBUG: true jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build run: ./build.shOption 2: Set repository secrets
For more persistent debugging across all workflows:
- Go to your repository settings
- Navigate to Secrets > Actions
- Add these repository secrets:
- Name:
ACTIONS_RUNNER_DEBUG, Value:true - Name:
ACTIONS_STEP_DEBUG, Value:true
- Name:
These debug settings will:
- Enable detailed logs for the Actions runner
- Show step-by-step debug information
- Include more context in error messages
- Log internal processes that are normally hidden
After enabling debug logging, run your workflow:
gh workflow run "Workflow Name"Then view the detailed logs:
gh run view --logThe logs will now contain much more detailed information to help you diagnose issues.
metadata: priority: high version: 1.0 tags: - github-actions - debugging - workflows - ci-cd - github-cli
Additional Resources
Official Documentation
Debugging Tools
GitHub Actions Debug Action
# Add this to your workflow for debugging context
- name: Dump GitHub context
uses: actions/github-script@v6
with:
script: console.log(JSON.stringify(context, null, 2))
Workflow Dump Environment
# Add this to your workflow to dump all environment variables
- name: Dump environment
run: |
echo "=============================================="
echo "Dumping environment variables"
echo "=============================================="
env | sort
echo "=============================================="
Workflow Dump Context
# Add this to your workflow to dump GitHub context
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
Workflow Dump Job Context
# Add this to your workflow to dump job context
- name: Dump job context
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: echo "$JOB_CONTEXT"