Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# GitHub token (required)
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# LLM Provider: "openai" or "anthropic"
LLM_PROVIDER=openai

# LLM API Key (required for AI-powered release notes)
LLM_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# LLM Model (optional, defaults to provider's default)
# LLM_MODEL=gpt-4o-mini

# Version override (optional, auto-detected if omitted)
# VERSION=1.0.0

# Version prefix (default: "v")
# VERSION_PREFIX=v

# Dry run mode (default: false)
# DRY_RUN=true
43 changes: 43 additions & 0 deletions .github/workflows/release-self.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,51 @@ jobs:
fetch-depth: 0

- uses: ./
id: diffr
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
llm-provider: openai
llm-api-key: ${{ secrets.OPENAI_API_KEY }}
dry-run: 'true'

- name: Validate outputs
run: |
echo "=== diffr dry-run output validation ==="

# Version must be valid semver
VERSION="${{ steps.diffr.outputs.version }}"
if [ -z "$VERSION" ]; then
echo "WARN: No version output (expected on first run with no tags)"
else
echo "Version: $VERSION"
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then
echo "FAIL: Version '$VERSION' is not valid semver"
exit 1
fi
fi

# Tag must start with prefix
TAG="${{ steps.diffr.outputs.tag }}"
if [ -n "$TAG" ]; then
echo "Tag: $TAG"
if ! echo "$TAG" | grep -qE '^v[0-9]+'; then
echo "FAIL: Tag '$TAG' does not start with 'v'"
exit 1
fi
fi

# Dry-run must be true
DRY_RUN="${{ steps.diffr.outputs.dry-run }}"
if [ "$DRY_RUN" != "true" ]; then
echo "FAIL: Expected dry-run=true, got '$DRY_RUN'"
exit 1
fi

# Release notes must be non-empty (if there are changes)
NOTES="${{ steps.diffr.outputs.release-notes }}"
if [ -n "$VERSION" ] && [ -z "$NOTES" ]; then
echo "FAIL: Version was resolved but release notes are empty"
exit 1
fi

echo "=== All validations passed ==="
67 changes: 67 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: pnpm/action-setup@v4
with:
version: 9

- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm typecheck

- name: Lint
run: pnpm lint

- name: Test
run: pnpm test

- name: Build
run: pnpm build

- name: Verify dist/ is up to date
run: |
git diff --exit-code dist/ || (echo "dist/ is out of date. Run 'pnpm build' and commit." && exit 1)

release:
needs: ci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: ./
id: diffr
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
llm-provider: openai
llm-api-key: ${{ secrets.OPENAI_API_KEY }}

- name: Update floating major tag
run: |
TAG="${GITHUB_REF#refs/tags/}"
MAJOR=$(echo "$TAG" | sed -n 's/^\(v[0-9]*\).*/\1/p')
git tag -fa "$MAJOR" -m "$TAG"
git push origin "$MAJOR" --force
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2026-03-07

### Added
- LLM prompt sanitization to prevent prompt injection from commit messages and PR bodies
- 60-second timeouts for LLM API requests (OpenAI and Anthropic)
- Debug logging for previously silent catch blocks in GitHub client
- Conventional commit-aware version bumping (`feat` → minor, breaking → major)
- `.env.example` with placeholder configuration values
- `SECURITY.md` with vulnerability reporting instructions
- `CHANGELOG.md` (this file)
- Production release workflow (`.github/workflows/release.yml`)
- `LICENSE` file (MIT)

### Fixed
- Unhandled promise rejection at action entry point
- Duplicate compare link when using fallback generator (orchestrator already appends it)
- Non-null assertions (`!`) replaced with safe access patterns in fallback generator
- Hardcoded `'main'` default branch now reads from GitHub context

### Changed
- Version bumping now respects conventional commit semantics instead of always using `patch`

## [0.1.0] - 2026-03-04

### Added
- Initial release
- GitHub Action with OpenAI and Anthropic LLM support
- Fallback generator for no-LLM usage
- Conventional commit parsing
- Automatic version detection from tags
- Dry-run mode
- `.diffr.yml` configuration support
- CI pipeline with typecheck, lint, test, build
- Dogfood workflow (release-self)
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 David Adegoke

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
56 changes: 56 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Security Policy

## Reporting a Vulnerability

If you discover a security vulnerability in diffr, please report it responsibly:

1. **Do not** open a public GitHub issue
2. Email **tinny-grubs.4u@icloud.com** with:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
3. You will receive a response within 48 hours

## Supported Versions

| Version | Supported |
| ------- | --------- |
| 1.x | Yes |
| < 1.0 | No |

## API Key Best Practices

diffr requires API keys for LLM providers. Follow these practices:

- **Never** commit API keys to your repository
- Use [GitHub encrypted secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) to store keys
- Use the minimum required permissions for your GitHub token
- Rotate API keys periodically
- Consider using separate API keys for CI vs. production

```yaml
# Correct: use secrets
- uses: dhaveed/diffr@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
llm-api-key: ${{ secrets.OPENAI_API_KEY }}

# Wrong: hardcoded key
- uses: dhaveed/diffr@v1
with:
llm-api-key: sk-1234567890 # NEVER do this
```

## LLM Safety Measures

diffr includes several safeguards for LLM interactions:

- **Prompt sanitization**: Commit messages and PR bodies are sanitized before being sent to the LLM to mitigate prompt injection attacks
- **Constrained system prompt**: The LLM is instructed to only summarize changes from the provided data, not fabricate information
- **Request timeouts**: LLM requests have a 60-second timeout to prevent hanging
- **Fallback generator**: If the LLM fails or returns unexpected output, diffr falls back to deterministic release notes
- **No code execution**: diffr never executes code from LLM responses

## Dependencies

diffr's production dependencies are bundled into `dist/index.js` via `@vercel/ncc`. Third-party licenses are listed in `dist/licenses.txt`.
Loading
Loading