Skip to main content

Development

This guide covers how to set up a development environment for infrahub-sync, contribute to the project, and publish releases.

Prerequisites

  • Python 3.10–3.13 (3.12 recommended)
  • uv for dependency management
  • Git

Setting up your development environment

Clone the repository

git clone https://github.com/opsmill/infrahub-sync.git
cd infrahub-sync

Install uv

If you don't have uv installed, you can install it with:

curl -LsSf https://astral.sh/uv/install.sh | sh

Or see the uv installation guide for other options.

Install dependencies

uv sync --group dev

This installs all runtime and development dependencies defined in pyproject.toml.

Verify your setup

uv run infrahub-sync --help
uv run infrahub-sync list --directory examples/

Development workflow

Before committing any changes, run the following commands in order:

uv run invoke format    # Format code with ruff
uv run invoke lint # Lint code with ruff and pylint
uv run mypy infrahub_sync/ --ignore-missing-imports

Validate the CLI

After making changes, verify the CLI still works:

uv run infrahub-sync --help
uv run infrahub-sync list --directory examples/
uv run infrahub-sync generate --name from-netbox --directory examples/

Running tests

uv run pytest -q

Code standards

Python style

  • Python 3.10–3.13 compatible
  • Type hints on new or changed code
  • Ruff-formatted and lint-clean
  • Mypy-checked (do not increase existing error count)
  • Public functions and classes require documentation strings
  • Raise specific exceptions; avoid broad except Exception:

Line length

  • Maximum line length: 120 characters (configured in pyproject.toml)

Documentation

If you make user-facing changes (CLI flags, configuration options, new adapters), update the documentation.

Generate command-line documentation

uv run invoke docs.generate

Build documentation site

First-time setup (requires Node.js):

cd docs && npm install

Build the site:

uv run invoke docs.docusaurus

Lint markdown files

npx markdownlint-cli "docs/docs/**/*.{md,mdx}"
npx markdownlint-cli --fix "docs/docs/**/*.{md,mdx}"

Adding a new adapter

  1. Create infrahub_sync/adapters/<name>.py following existing adapter patterns
  2. Add connection configuration schema and an example under examples/
  3. Provide list and diff pathways before enabling sync
  4. Document required environment variables and expected error cases
  5. Create a documentation page in docs/docs/adapters/
  6. Add the adapter to the sidebar in docs/sidebars.ts

Invoke tasks

View all available tasks:

uv run invoke --list

Common tasks:

TaskDescription
linter.format-ruffFormat Python code with ruff
linter.lint-ruffLint Python code with ruff
linter.lint-pylintLint Python code with pylint
linter.lint-yamlLint YAML files with yamllint
docs.generateGenerate CLI documentation
docs.docusaurusBuild documentation website
formatAlias for ruff format
lintRun all linters

Publishing a release

This section documents how to publish new releases of infrahub-sync to PyPI.

Overview

The project uses an automated release system powered by GitHub Actions. There are three ways to publish a release:

  1. Automated release (recommended for regular releases)
  2. Manual GitHub release (for controlled releases)
  3. Manual workflow dispatch (for emergency or custom releases)

Prerequisites

Before publishing, ensure:

  • You have write access to the repository
  • The PYPI_TOKEN secret is configured in repository settings
  • The GH_INFRAHUB_BOT_TOKEN secret is configured (for automated releases)

This is the standard release flow. Releases are triggered automatically when PRs are merged to main or stable branches.

Step 1: Label your pull requests

Apply appropriate labels to PRs before merging. Labels determine the version bump:

LabelVersion BumpUse When
changes/major, type/breaking-changeMajor (1.0.0 → 2.0.0)Breaking API changes
changes/minor, type/feature, type/refactoringMinor (1.0.0 → 1.1.0)New features, refactoring
changes/patch, type/bug, type/housekeeping, type/documentationPatch (1.0.0 → 1.0.1)Bug fixes, docs, maintenance

Auto-labeling rules are configured in .github/release-drafter.yml but require a separate workflow trigger to activate. For now, apply labels manually:

PR Title PatternRecommended Label
Contains fixtype/bug
Contains enhance, improve, featuretype/feature
Contains choreci/skip-changelog
Contains deprecattype/deprecated

Step 2: Merge to main

Merge your labeled PR to the main branch. The automation will:

  1. Calculate the next version based on PR labels
  2. Update pyproject.toml with the new version (and regenerate uv.lock)
  3. Commit changes as chore(release): v{VERSION} [skip ci]
  4. Create/update a draft GitHub Release with auto-generated release notes

Step 3: Publish the GitHub release

  1. Navigate to the repository's Releases page
  2. Find the draft release created by Release Drafter
  3. Review the auto-generated release notes
  4. Edit if needed (add context, highlights, migration notes)
  5. Click Publish release

Publishing the release triggers the PyPI upload automatically.

Method 2: Manual GitHub release

Use this method when you want full control over the release timing and notes.

Step 1: Update the version

Update the version in pyproject.toml:

# Edit pyproject.toml and update the version field
uv lock

Commit and push the changes:

git add pyproject.toml uv.lock
git commit -m "chore(release): vX.Y.Z"
git push origin main

Step 2: Create a GitHub release

  1. Go to ReleasesDraft a new release
  2. Click Choose a tag and create a new tag matching your version (for example, 1.6.0)
  3. Set the target to main branch
  4. Add a release title (for example, 1.6.0)
  5. Write release notes describing the changes
  6. Click Publish release

This triggers the trigger-release.yml workflow, which publishes to PyPI.

Method 3: Manual workflow dispatch

Use this for emergency releases or when you need to bypass the standard flow.

Using the GitHub UI

  1. Go to ActionsPublish Infrahub Sync Package
  2. Click Run workflow
  3. Configure the inputs:
    • version: The version string (for example, 1.6.0) - optional, for labeling
    • publish: Set to true to publish to PyPI (default: false)
    • runs-on: OS for the runner (default: ubuntu-22.04)
  4. Click Run workflow

Using the GitHub CLI

gh workflow run workflow-publish.yml \
--field version="1.6.0" \
--field publish=true

Important: When using workflow dispatch, ensure pyproject.toml already has the correct version, as this method builds from the current code state.

Release notes

Release notes are auto-generated based on merged PRs and their labels:

CategoryLabels
Breaking Changeschanges/major
Minor Changeschanges/minor, type/feature, type/refactoring
Patch & Bug Fixestype/bug, changes/patch
Documentation Changetype/documentation

PRs with these labels are excluded from release notes:

  • ci/skip-changelog
  • type/duplicate

Verifying a release

After publishing:

  1. Check PyPI: Visit pypi.org/project/infrahub-sync to confirm the new version is available
  2. Check GitHub Actions: Ensure the publish workflow completed successfully
  3. Test Installation:
pip install infrahub-sync==<new-version>
infrahub-sync --version

Troubleshooting

Release workflow skipped

The automated release is skipped when:

  • The commit author is opsmill-bot with a chore prefix (prevents recursive releases)
  • No version bump is detected (no labeled PRs since last release)
  • Changes are only in the docs/ directory

The PyPI upload failed

Common causes:

  • PYPI_TOKEN secret is missing or invalid
  • Version already exists on PyPI (versions cannot be overwritten)
  • Network issues during upload

To retry, use the manual workflow dispatch method.

Version not bumped correctly

Ensure PRs have appropriate labels before merging. If labels are missing, the version drafter may not calculate a new version.

Workflow files reference

WorkflowTypePurpose
trigger-push-stable.ymlPush to main/stableCalculates version, bumps pyproject.toml, triggers release draft
workflow-release-drafter.ymlReusable (workflow_call)Creates/updates GitHub Release draft; invoked by trigger-release.yml
trigger-release.ymlGitHub Release publishedOrchestrates release: invokes release drafter and publish workflows
workflow-publish.ymlReusable (workflow_dispatch)Builds and publishes package to PyPI; invoked by trigger-release.yml