CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-gidgethub

An asynchronous GitHub API library designed as a sans-I/O library for GitHub API access

Pending
Overview
Eval results
Files

actions.mddocs/

GitHub Actions

Utilities for GitHub Actions workflows including environment variable management, path manipulation, and logging command output. These functions help create GitHub Actions that integrate with the workflow environment.

Capabilities

Workspace Access

Access the GitHub Actions workspace directory.

import pathlib

def workspace() -> pathlib.Path:
    """
    Return the action workspace as a pathlib.Path object.
    
    Returns:
    - Path object pointing to the workspace directory
    
    Note: Uses the GITHUB_WORKSPACE environment variable.
    This function is cached with @functools.lru_cache(maxsize=1).
    """

Event Data Access

Access the webhook event that triggered the workflow.

def event() -> Any:
    """
    Return the webhook event data for the running action.
    
    Returns:
    - Parsed JSON data from the event that triggered the workflow
    
    Note: Uses the GITHUB_EVENT_PATH environment variable.
    This function is cached with @functools.lru_cache(maxsize=1).
    """

Workflow Commands

Issue logging commands that are processed by the GitHub Actions runner.

def command(cmd: str, data: str = "", **parameters: str) -> None:
    """
    Issue a logging command that will be processed by the Actions runner.
    
    Parameters:
    - cmd: Command name (e.g., "error", "warning", "notice", "debug")
    - data: Command data/message
    - **parameters: Command parameters (e.g., file, line, col, title)
    
    Examples:
    - command("error", "Something went wrong", file="app.py", line="42")
    - command("warning", "Deprecated function used")
    - command("notice", "Build completed successfully")
    """

Environment Management

Manage environment variables for the current and future actions.

def setenv(name: str, value: str) -> None:
    """
    Create or update an environment variable.
    The change applies to this action and future actions in the job.
    
    Parameters:
    - name: Environment variable name
    - value: Environment variable value (can be multiline)
    
    Note: Uses the GITHUB_ENV environment file for persistence.
    """

def addpath(path: Union[str, "os.PathLike[str]"]) -> None:
    """
    Prepend a directory to the PATH environment variable.
    This affects this action and all subsequent actions in the current job.
    
    Parameters:
    - path: Directory path to add to PATH
    
    Note: Uses the GITHUB_PATH environment file for persistence.
    """

Usage Examples

Basic Action Script

#!/usr/bin/env python3
import gidgethub.actions
import subprocess
import sys

def main():
    # Get workspace directory
    workspace = gidgethub.actions.workspace()
    print(f"Working in: {workspace}")
    
    # Get event data
    event_data = gidgethub.actions.event()
    event_name = event_data.get('action', 'unknown')
    
    gidgethub.actions.command("notice", f"Processing {event_name} event")
    
    # Example: Run tests
    try:
        result = subprocess.run(
            ["python", "-m", "pytest"], 
            cwd=workspace,
            capture_output=True,
            text=True
        )
        
        if result.returncode == 0:
            gidgethub.actions.command("notice", "Tests passed successfully")
        else:
            gidgethub.actions.command("error", f"Tests failed: {result.stderr}")
            sys.exit(1)
            
    except Exception as e:
        gidgethub.actions.command("error", f"Failed to run tests: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

Environment Variable Management

import gidgethub.actions
import os

def setup_build_environment():
    # Set build configuration
    gidgethub.actions.setenv("BUILD_TYPE", "release")
    gidgethub.actions.setenv("OPTIMIZATION_LEVEL", "3")
    
    # Set multiline environment variable
    config_json = """{
    "api_url": "https://api.example.com",
    "timeout": 30,
    "retries": 3
}"""
    gidgethub.actions.setenv("BUILD_CONFIG", config_json)
    
    # Add custom tools to PATH
    tools_dir = gidgethub.actions.workspace() / "tools" / "bin"
    gidgethub.actions.addpath(tools_dir)
    
    # Verify environment
    print(f"BUILD_TYPE: {os.environ.get('BUILD_TYPE')}")
    print(f"PATH includes tools: {str(tools_dir) in os.environ.get('PATH', '')}")

setup_build_environment()

Error Reporting with File Locations

import gidgethub.actions
import ast
import sys

def lint_python_file(file_path):
    """Lint a Python file and report errors with locations."""
    try:
        with open(file_path, 'r') as f:
            source = f.read()
        
        # Parse the file
        ast.parse(source)
        gidgethub.actions.command("notice", f"✓ {file_path} is valid Python")
        
    except SyntaxError as e:
        # Report syntax error with file location
        gidgethub.actions.command(
            "error",
            f"Syntax error: {e.msg}",
            file=str(file_path),
            line=str(e.lineno),
            col=str(e.offset) if e.offset else "1"
        )
        return False
    
    except Exception as e:
        gidgethub.actions.command("error", f"Failed to process {file_path}: {e}")
        return False
    
    return True

def main():
    workspace = gidgethub.actions.workspace()
    python_files = list(workspace.glob("**/*.py"))
    
    gidgethub.actions.command("notice", f"Linting {len(python_files)} Python files")
    
    all_valid = True
    for py_file in python_files:
        if not lint_python_file(py_file):
            all_valid = False
    
    if not all_valid:
        gidgethub.actions.command("error", "Linting failed")
        sys.exit(1)
    else:
        gidgethub.actions.command("notice", "All files passed linting")

if __name__ == "__main__":
    main()

Processing Webhook Events

import gidgethub.actions

def process_pull_request_event():
    event = gidgethub.actions.event()
    
    if event.get('action') == 'opened':
        pr = event['pull_request']
        
        gidgethub.actions.command(
            "notice", 
            f"New PR opened: {pr['title']}",
            title="Pull Request Opened"
        )
        
        # Check if PR affects specific files
        changed_files = []  # You'd get this from the GitHub API
        if any(f.endswith('.py') for f in changed_files):
            gidgethub.actions.setenv("RUN_PYTHON_TESTS", "true")
        
        if any(f.endswith(('.js', '.ts')) for f in changed_files):
            gidgethub.actions.setenv("RUN_JS_TESTS", "true")
    
    elif event.get('action') == 'closed':
        pr = event['pull_request']
        if pr['merged']:
            gidgethub.actions.command("notice", f"PR merged: {pr['title']}")
        else:
            gidgethub.actions.command("notice", f"PR closed: {pr['title']}")

process_pull_request_event()

Custom Action with Inputs and Outputs

import gidgethub.actions
import os
import json

def parse_inputs():
    """Parse action inputs from environment variables."""
    return {
        'target': os.environ.get('INPUT_TARGET', 'main'),
        'dry_run': os.environ.get('INPUT_DRY_RUN', 'false').lower() == 'true',
        'config_file': os.environ.get('INPUT_CONFIG_FILE', 'config.json')
    }

def set_outputs(**outputs):
    """Set action outputs."""
    for key, value in outputs.items():
        # GitHub Actions uses special echo commands for outputs
        print(f"::set-output name={key}::{value}")

def main():
    inputs = parse_inputs()
    
    gidgethub.actions.command(
        "notice", 
        f"Running with target: {inputs['target']}, dry_run: {inputs['dry_run']}"
    )
    
    workspace = gidgethub.actions.workspace()
    config_path = workspace / inputs['config_file']
    
    if not config_path.exists():
        gidgethub.actions.command(
            "error", 
            f"Config file not found: {config_path}",
            file=str(config_path)
        )
        return 1
    
    # Process configuration
    try:
        with open(config_path) as f:
            config = json.load(f)
        
        # Set outputs for other actions to use
        set_outputs(
            config_valid="true",
            target_branch=inputs['target'],
            config_version=config.get('version', 'unknown')
        )
        
        gidgethub.actions.command("notice", "Action completed successfully")
        return 0
        
    except Exception as e:
        gidgethub.actions.command("error", f"Failed to process config: {e}")
        return 1

if __name__ == "__main__":
    exit_code = main()
    exit(exit_code)

Integration with gidgethub API Client

import asyncio
import aiohttp
import gidgethub.actions
from gidgethub.aiohttp import GitHubAPI

async def comment_on_pr():
    """Add a comment to the PR that triggered this action."""
    event = gidgethub.actions.event()
    
    if event.get('action') != 'opened':
        gidgethub.actions.command("notice", "Not a PR opened event, skipping")
        return
    
    # Get GitHub token from environment
    github_token = os.environ.get('GITHUB_TOKEN')
    if not github_token:
        gidgethub.actions.command("error", "GITHUB_TOKEN not set")
        return
    
    repository = event['repository']['full_name']
    pr_number = event['pull_request']['number']
    
    async with aiohttp.ClientSession() as session:
        gh = GitHubAPI(session, "pr-commenter/1.0", oauth_token=github_token)
        
        try:
            # Add comment to PR
            await gh.post(
                f"/repos/{repository}/issues/{pr_number}/comments",
                data={
                    "body": "🎉 Thanks for opening this PR! Our automated checks are running."
                }
            )
            
            gidgethub.actions.command("notice", f"Added comment to PR #{pr_number}")
            
        except Exception as e:
            gidgethub.actions.command("error", f"Failed to add comment: {e}")

# Run the async function
asyncio.run(comment_on_pr())

GitHub Actions Environment Variables

Common environment variables available in GitHub Actions:

  • GITHUB_WORKSPACE: The workspace directory path
  • GITHUB_EVENT_PATH: Path to the webhook event JSON file
  • GITHUB_TOKEN: GitHub token for API access
  • GITHUB_REPOSITORY: Repository name (owner/name)
  • GITHUB_REF: Git ref that triggered the workflow
  • GITHUB_SHA: Commit SHA that triggered the workflow
  • GITHUB_ACTOR: Username of the user that triggered the workflow
  • GITHUB_WORKFLOW: Name of the workflow
  • GITHUB_RUN_ID: Unique identifier for the workflow run
  • GITHUB_RUN_NUMBER: Sequential number for the workflow run

Workflow Command Reference

Common workflow commands you can use with the command() function:

  • error: Create an error message
  • warning: Create a warning message
  • notice: Create a notice message
  • debug: Create a debug message
  • group: Start a collapsible group
  • endgroup: End a collapsible group
  • save-state: Save state for post-action
  • set-output: Set action output (deprecated, use environment files)

Types

import pathlib
import os
from typing import Any, Union

# Path types for workspace and file operations
PathLike = Union[str, "os.PathLike[str]"]
WorkspacePath = pathlib.Path

# Event data type (parsed JSON)
EventData = Any  # Dictionary containing webhook event data

Install with Tessl CLI

npx tessl i tessl/pypi-gidgethub

docs

actions.md

api-client.md

apps.md

exceptions.md

http-implementations.md

index.md

routing.md

sansio.md

tile.json