diff --git a/.github/BOT_FEATURES.md b/.github/BOT_FEATURES.md new file mode 100644 index 00000000..b97915ce --- /dev/null +++ b/.github/BOT_FEATURES.md @@ -0,0 +1,78 @@ +# Bot Features for Issues and Discussions + +This document describes the minimal, conservative bot features that help users with issues and discussions. + +## Issue Helper Bot (`.github/workflows/issue-bot.yml`) + +### Features + +#### 1. Conservative Auto-labeling +- **Trigger**: When a new issue is opened +- **Action**: Only adds labels for **clear, explicit** cases +- **Labels**: + - `bug` - Only if title starts with "bug:" or "[bug]" OR has multiple bug indicators + - `enhancement` - Only if explicitly mentions "feature request" or title starts with "feature:" or "[feature]" + - `documentation` - Only if explicitly mentions "documentation issue" or title starts with "docs:" + - `security` - Only if explicitly mentions "security issue" or title starts with "security:" + - `needs-triage` - Only if issue body is very short (< 50 chars) and no other labels match +- **Philosophy**: Only labels when it's very clear - avoids false positives + +#### 2. Help on Request Only +- **Trigger**: When an issue contains `/help` in title or body +- **Action**: Provides helpful resources and links +- **Note**: Only responds when explicitly requested - no automatic responses + +## Discussion Helper Bot (`.github/workflows/discussion-bot.yml`) + +### Features + +#### 1. Mark as Answered (Manual) +- **Trigger**: When a maintainer comments `/answered` on a discussion +- **Action**: Marks the discussion as answered +- **Note**: Only works for repository members/owners/collaborators +- **Philosophy**: Completely manual - maintainer decides when to mark as answered + +## How It Works + +Both bots use GitHub Actions workflows that: +- Run only on specific, conservative triggers +- Use GitHub's API for minimal actions (labeling, marking answered) +- Use strict pattern matching - only for very clear cases +- **No automatic responses** - only responds when explicitly requested + +## Safety Features + +- **Minimal automation**: Only labels very clear cases, no automatic responses +- **No external services**: All bots run using GitHub Actions only +- **Manual control**: Most actions require explicit triggers +- **Conservative matching**: Only acts on very clear patterns +- **Easy to disable**: Can be disabled in repository settings + +## Customization + +To customize bot behavior: + +1. **Modify keywords**: Edit the regex patterns in the workflow files +2. **Change responses**: Update the response text in the scripts +3. **Add new features**: Extend the workflows with additional checks + +## Disabling Bots + +To temporarily disable a bot: +1. Go to repository Settings → Actions → Workflows +2. Find the bot workflow +3. Click "..." → Disable workflow + +Or comment out the workflow file in `.github/workflows/` + +## Best Practices + +1. **Review bot comments**: Ensure they're helpful and accurate +2. **Update responses**: Keep documentation links current +3. **Monitor behavior**: Check that labels are applied correctly +4. **Community feedback**: Adjust based on user feedback + +--- + +**Note**: These bots are designed to be helpful assistants, not replacements for human interaction. They provide initial guidance, but community members and maintainers provide the real support! + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 580a9c0f..f4a6cd37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,13 +8,12 @@ on: jobs: test: - name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }} - runs-on: ${{ matrix.os }} + name: Test Python ${{ matrix.python-version }} + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v4 @@ -28,9 +27,9 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt', '**/pyproject.toml') }} + key: ubuntu-pip-${{ hashFiles('**/requirements*.txt', '**/pyproject.toml') }} restore-keys: | - ${{ runner.os }}-pip- + ubuntu-pip- - name: Install dependencies run: | @@ -40,21 +39,12 @@ jobs: - name: Run tests run: | if [ -d "tests" ] && [ "$(find tests -name 'test_*.py' -o -name '*_test.py' | wc -l)" -gt 0 ]; then - pytest --cov=semantica --cov-report=xml --cov-report=html + pytest else echo "⚠️ No tests directory or test files found. Skipping test execution." echo "This is expected for initial setup. Add tests to the 'tests/' directory." exit 0 fi - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' && hashFiles('coverage.xml') != '' - with: - file: ./coverage.xml - flags: unittests - name: codecov-umbrella - fail_ci_if_error: false continue-on-error: true lint: @@ -94,26 +84,6 @@ jobs: flake8 tests/ fi - - name: Run mypy - run: mypy semantica/ - continue-on-error: true - - type-check: - name: Type Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e ".[dev]" - - name: Run mypy run: mypy semantica/ || echo "Type checking completed with errors (non-blocking)" continue-on-error: true diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml deleted file mode 100644 index 0dbf83c8..00000000 --- a/.github/workflows/dependabot.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Dependabot Auto-merge - -on: - pull_request: - types: [opened, synchronize, reopened] - -jobs: - dependabot: - name: Dependabot - runs-on: ubuntu-latest - if: github.actor == 'dependabot[bot]' - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e ".[dev]" - - - name: Run tests - run: | - if [ -d "tests" ] && [ "$(find tests -name 'test_*.py' -o -name '*_test.py' | wc -l)" -gt 0 ]; then - pytest - else - echo "⚠️ No tests found. Skipping test execution." - exit 0 - fi - - - name: Auto-merge Dependabot PRs - if: success() - uses: fastify/github-action-merge-dependabot@v3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.github/workflows/discussion-bot.yml b/.github/workflows/discussion-bot.yml new file mode 100644 index 00000000..ae8ea291 --- /dev/null +++ b/.github/workflows/discussion-bot.yml @@ -0,0 +1,43 @@ +name: Discussion Helper Bot + +on: + discussion_comment: + types: [created] + +jobs: + + mark-answered: + name: Mark Discussions as Answered + runs-on: ubuntu-latest + if: | + github.event.action == 'created' && + contains(github.event.comment.body, '/answered') && + (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'COLLABORATOR') + steps: + - name: Mark as answered + uses: actions/github-script@v7 + with: + script: | + const discussion = context.payload.discussion; + const commentId = context.payload.comment.id; + + try { + // Mark the comment as the answer + await github.rest.discussions.markAnswerComment({ + discussion_number: discussion.number, + comment_number: commentId, + owner: context.repo.owner, + repo: context.repo.repo + }); + + // Mark the discussion as answered + await github.rest.discussions.markAsAnswer({ + discussion_number: discussion.number, + comment_number: commentId, + owner: context.repo.owner, + repo: context.repo.repo + }); + } catch (error) { + console.log('Could not mark as answered (may require manual action):', error.message); + } + diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a228cb57..564e7e04 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,6 +12,8 @@ on: paths: - 'docs/**' - 'mkdocs.yml' + workflow_dispatch: + # Manual trigger for deploying documentation jobs: build: @@ -42,42 +44,12 @@ jobs: fi continue-on-error: true - - name: Deploy to GitHub Pages - if: github.ref == 'refs/heads/main' && github.event_name == 'push' + # Documentation deployment is manual for safety + # To deploy: Go to Actions tab -> Documentation workflow -> Run workflow + - name: Deploy to GitHub Pages (Manual) + if: github.event_name == 'workflow_dispatch' uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./site keep_files: false - - preview: - name: Preview Documentation - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements-docs.txt - pip install mkdocs-material mkdocstrings[python] - - - name: Build documentation - run: mkdocs build --strict - - - name: Comment PR with preview link - uses: actions/github-script@v7 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: '📚 Documentation preview will be available after merge.' - }) diff --git a/.github/workflows/issue-bot.yml b/.github/workflows/issue-bot.yml new file mode 100644 index 00000000..c2c1d6f0 --- /dev/null +++ b/.github/workflows/issue-bot.yml @@ -0,0 +1,100 @@ +name: Issue Helper Bot + +on: + issues: + types: [opened] + issue_comment: + types: [created] + +jobs: + + auto-label: + name: Auto-label Issues (Conservative) + runs-on: ubuntu-latest + if: github.event.action == 'opened' + steps: + - name: Label based on keywords (only clear matches) + uses: actions/github-script@v7 + with: + script: | + const issue = context.payload.issue; + const title = issue.title.toLowerCase(); + const body = (issue.body || '').toLowerCase(); + const text = title + ' ' + body; + + const labels = []; + + // Only label if there are clear, strong indicators + // Bug detection - must have multiple indicators + if ((text.match(/\b(bug|crash|broken|fails|doesn't work|not working)\b/) && + text.match(/\b(error|exception|traceback|problem)\b/)) || + title.startsWith('bug:') || title.startsWith('[bug]')) { + labels.push('bug'); + } + + // Feature request - must be explicit + else if (text.match(/\b(feature request|enhancement request|proposal)\b/) || + title.startsWith('feature:') || title.startsWith('[feature]') || + title.startsWith('enhancement:') || title.startsWith('[enhancement]')) { + labels.push('enhancement'); + } + + // Documentation - must be explicit + else if (text.match(/\b(documentation issue|doc fix|docs update)\b/) || + title.startsWith('docs:') || title.startsWith('[docs]')) { + labels.push('documentation'); + } + + // Security - must be explicit + else if (text.match(/\b(security issue|vulnerability|security bug)\b/) || + title.startsWith('security:') || title.startsWith('[security]')) { + labels.push('security'); + } + + // Only add needs-triage if no labels and issue seems incomplete + if (labels.length === 0 && (body.length < 50 || !body.includes('\n'))) { + labels.push('needs-triage'); + } + + // Add labels only if we found something + if (labels.length > 0) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + labels: labels + }); + } + + respond-to-help-request: + name: Respond Only to Explicit Help Requests + runs-on: ubuntu-latest + if: | + github.event.action == 'opened' && + (contains(github.event.issue.body, '/help') || contains(github.event.issue.title, '/help')) + steps: + - name: Provide help resources + uses: actions/github-script@v7 + with: + script: | + const issue = context.payload.issue; + + const response = `📚 **Help Resources** + +Here are some helpful resources: + +- [Getting Started](https://github.com/${{ github.repository }}/blob/main/docs/getting-started.md) +- [FAQ](https://github.com/${{ github.repository }}/blob/main/docs/faq.md) +- [Documentation](https://github.com/${{ github.repository }}/tree/main/docs) +- [GitHub Discussions](https://github.com/${{ github.repository }}/discussions) - Ask questions here +- [Discord](https://discord.gg/semantica) - Real-time chat + +💡 **Tip**: For questions, consider using [GitHub Discussions](https://github.com/${{ github.repository }}/discussions) instead of issues.`; + + await github.rest.issues.createComment({ + issue_number: issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: response + }); + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8559cd4a..8bd2a837 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,18 +47,9 @@ jobs: prerelease: false generate_release_notes: true - - name: Publish to PyPI - if: startsWith(github.ref, 'refs/tags/v') - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} - run: | - if [ -z "${TWINE_PASSWORD:-}" ]; then - echo "⚠️ PYPI_API_TOKEN secret not configured. Skipping PyPI upload." - echo "This is expected if PyPI publishing is not yet set up." - exit 0 - fi - echo "📦 Publishing to PyPI..." - twine upload --skip-existing dist/* - continue-on-error: true + # PyPI publishing is intentionally disabled for manual control + # To publish to PyPI manually: + # 1. Ensure PYPI_API_TOKEN secret is configured in repository settings + # 2. Run: twine upload dist/* + # This prevents accidental publishing of wrong versions diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 60fc6a1b..37d679a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,17 +35,6 @@ repos: hooks: - id: flake8 args: ['--max-line-length=88', '--extend-ignore=E203,W503'] - additional_dependencies: - - flake8-docstrings - - flake8-bugbear - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 - hooks: - - id: mypy - additional_dependencies: - - types-all - args: ['--ignore-missing-imports', '--no-strict-optional'] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 @@ -55,26 +44,14 @@ repos: - id: python-no-eval - id: python-no-log-warn - - repo: https://github.com/PyCQA/bandit - rev: 1.7.6 - hooks: - - id: bandit - args: ['-r', 'semantica/', '-f', 'json', '-o', 'bandit-report.json'] - exclude: ^tests/ - - repo: https://github.com/adrienverge/yamllint rev: v1.33.0 hooks: - id: yamllint args: ['-d', '{extends: default, rules: {line-length: {max: 120}}}'] - - repo: local - hooks: - - id: pytest - name: pytest - entry: pytest - language: system - pass_filenames: false - always_run: true - args: ['-v', '--tb=short'] + # Removed slow hooks for faster development: + # - mypy: Type checking (can be run manually or in CI) + # - bandit: Security scanning (can be run separately) + # - pytest: Testing (should be run manually, not on every commit)