cicaddy-github 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. cicaddy_github-0.1.0/.claude/skills/cicaddy-action/SKILL.md +312 -0
  2. cicaddy_github-0.1.0/.github/dependabot.yml +24 -0
  3. cicaddy_github-0.1.0/.github/workflows/changelog.yml +66 -0
  4. cicaddy_github-0.1.0/.github/workflows/ci.yml +59 -0
  5. cicaddy_github-0.1.0/.github/workflows/pr-review.yml +45 -0
  6. cicaddy_github-0.1.0/.github/workflows/release.yml +101 -0
  7. cicaddy_github-0.1.0/.gitignore +39 -0
  8. cicaddy_github-0.1.0/.pre-commit-config.yaml +56 -0
  9. cicaddy_github-0.1.0/CLAUDE.md +31 -0
  10. cicaddy_github-0.1.0/CODE_OF_CONDUCT.md +37 -0
  11. cicaddy_github-0.1.0/CONTRIBUTING.md +57 -0
  12. cicaddy_github-0.1.0/Dockerfile +27 -0
  13. cicaddy_github-0.1.0/LICENSE +190 -0
  14. cicaddy_github-0.1.0/PKG-INFO +220 -0
  15. cicaddy_github-0.1.0/README.md +200 -0
  16. cicaddy_github-0.1.0/action.yml +54 -0
  17. cicaddy_github-0.1.0/entrypoint.sh +68 -0
  18. cicaddy_github-0.1.0/pyproject.toml +62 -0
  19. cicaddy_github-0.1.0/src/cicaddy_github/__init__.py +3 -0
  20. cicaddy_github-0.1.0/src/cicaddy_github/config/__init__.py +0 -0
  21. cicaddy_github-0.1.0/src/cicaddy_github/config/settings.py +193 -0
  22. cicaddy_github-0.1.0/src/cicaddy_github/github_integration/__init__.py +0 -0
  23. cicaddy_github-0.1.0/src/cicaddy_github/github_integration/agents.py +268 -0
  24. cicaddy_github-0.1.0/src/cicaddy_github/github_integration/analyzer.py +281 -0
  25. cicaddy_github-0.1.0/src/cicaddy_github/github_integration/detector.py +23 -0
  26. cicaddy_github-0.1.0/src/cicaddy_github/github_integration/tools.py +136 -0
  27. cicaddy_github-0.1.0/src/cicaddy_github/plugin.py +107 -0
  28. cicaddy_github-0.1.0/src/cicaddy_github/security/__init__.py +0 -0
  29. cicaddy_github-0.1.0/src/cicaddy_github/security/leak_detector.py +104 -0
  30. cicaddy_github-0.1.0/src/cicaddy_github/validation.py +18 -0
  31. cicaddy_github-0.1.0/tasks/changelog_report.yml +70 -0
  32. cicaddy_github-0.1.0/tasks/pr_review.yml +91 -0
  33. cicaddy_github-0.1.0/templates/report_template.html +286 -0
  34. cicaddy_github-0.1.0/tests/__init__.py +0 -0
  35. cicaddy_github-0.1.0/tests/conftest.py +42 -0
  36. cicaddy_github-0.1.0/tests/unit/__init__.py +0 -0
  37. cicaddy_github-0.1.0/tests/unit/test_analyzer.py +321 -0
  38. cicaddy_github-0.1.0/tests/unit/test_detector.py +69 -0
  39. cicaddy_github-0.1.0/tests/unit/test_leak_detector.py +59 -0
  40. cicaddy_github-0.1.0/tests/unit/test_plugin.py +139 -0
  41. cicaddy_github-0.1.0/tests/unit/test_settings.py +176 -0
  42. cicaddy_github-0.1.0/tests/unit/test_tools.py +138 -0
@@ -0,0 +1,312 @@
1
+ ---
2
+ name: cicaddy-action
3
+ description: >
4
+ Use this skill when working with cicaddy-action, the GitHub Action that wraps
5
+ cicaddy for running AI agent tasks in GitHub Actions workflows. Covers the
6
+ action inputs, Docker entrypoint, plugin architecture, task definitions, and
7
+ local development.
8
+ ---
9
+
10
+ # cicaddy-action
11
+
12
+ GitHub Action that wraps [cicaddy](https://github.com/waynesun09/cicaddy)
13
+ for running AI agent tasks in GitHub Actions workflows. The `cicaddy-github`
14
+ plugin extends cicaddy with GitHub-specific agents, tools, and configuration.
15
+
16
+ ## Working Directory
17
+
18
+ - **Repository root**: `cicaddy-action/` (project root)
19
+ - **Plugin source**: `src/cicaddy_github/`
20
+ - **Task definitions**: `tasks/`
21
+ - **GitHub workflows**: `.github/workflows/`
22
+ - **Docker files**: `Dockerfile`, `entrypoint.sh`
23
+ - **Action definition**: `action.yml`
24
+
25
+ ## Project Structure
26
+
27
+ ```
28
+ cicaddy-action/
29
+ action.yml # GitHub Action definition (inputs/outputs)
30
+ Dockerfile # Container image (python:3.12-slim + uv)
31
+ entrypoint.sh # Maps GitHub Action inputs to cicaddy env vars
32
+ pyproject.toml # Package config (cicaddy-github plugin)
33
+ tasks/
34
+ pr_review.yml # DSPy task for PR code review
35
+ changelog_report.yml # DSPy task for changelog generation
36
+ src/cicaddy_github/
37
+ plugin.py # Entry points: register_agents, get_cli_args, etc.
38
+ config/settings.py # Settings class extending CoreSettings
39
+ github_integration/
40
+ agents.py # GitHubPRAgent, GitHubTaskAgent
41
+ analyzer.py # PyGithub wrapper (diff, PR data, comments)
42
+ detector.py # Auto-detect agent type from GitHub env
43
+ tools.py # Git operations (@tool decorated)
44
+ security/
45
+ leak_detector.py # Secret redaction via detect-secrets
46
+ .github/workflows/
47
+ ci.yml # Lint, test (3.11-3.13), docker build
48
+ pr-review.yml # AI code review on PRs (uses ./action)
49
+ changelog.yml # Changelog report on releases
50
+ release.yml # PyPI publish
51
+ ```
52
+
53
+ ## Key Commands
54
+
55
+ ```bash
56
+ # Install with test dependencies
57
+ uv pip install -e ".[test]"
58
+
59
+ # Run tests with coverage
60
+ pytest tests/ -q --cov=src/cicaddy_github
61
+
62
+ # Run all linters (pre-commit)
63
+ pre-commit run --all-files
64
+
65
+ # Build Docker image
66
+ docker build -t cicaddy-action:test .
67
+
68
+ # Test Docker image
69
+ docker run --rm --entrypoint cicaddy cicaddy-action:test --version
70
+ ```
71
+
72
+ ## GitHub Secrets
73
+
74
+ AI API keys must be configured in GitHub repository settings
75
+ (Settings > Secrets and variables > Actions). Two approaches work:
76
+
77
+ **Option 1: Generic secret via `ai_api_key` input (recommended)**
78
+
79
+ Set `AI_API_KEY` as a GitHub secret. The entrypoint maps it to the correct
80
+ provider-specific env var (`GEMINI_API_KEY`, `OPENAI_API_KEY`, or
81
+ `ANTHROPIC_API_KEY`) based on the `ai_provider` input.
82
+
83
+ ```yaml
84
+ with:
85
+ ai_provider: gemini
86
+ ai_api_key: ${{ secrets.AI_API_KEY }}
87
+ ```
88
+
89
+ **Option 2: Provider-specific secret via `env:`**
90
+
91
+ Set the provider-specific secret directly (e.g. `GEMINI_API_KEY`) and pass
92
+ it as an environment variable. Cicaddy reads these env vars directly.
93
+
94
+ ```yaml
95
+ with:
96
+ ai_provider: gemini
97
+ env:
98
+ GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
99
+ ```
100
+
101
+ | Secret | Required | Description |
102
+ |--------|----------|-------------|
103
+ | `AI_API_KEY` | Yes* | Generic AI provider API key (used via `ai_api_key` input) |
104
+ | `GEMINI_API_KEY` | Yes* | Gemini API key (alternative, passed via `env:`) |
105
+ | `OPENAI_API_KEY` | Yes* | OpenAI API key (alternative, passed via `env:`) |
106
+ | `ANTHROPIC_API_KEY` | Yes* | Anthropic API key (alternative, passed via `env:`) |
107
+ | `CONTEXT7_API_KEY` | No | API key for Context7 MCP server (optional) |
108
+
109
+ *One of `AI_API_KEY` or the provider-specific key is required.
110
+
111
+ `GITHUB_TOKEN` is provided automatically by GitHub Actions.
112
+
113
+ ## Action Inputs
114
+
115
+ All inputs use **underscores** (not hyphens) so GitHub Actions Docker containers
116
+ can reference them as bash variables (`INPUT_AI_PROVIDER`, `INPUT_AI_API_KEY`, etc.).
117
+
118
+ | Input | Required | Description |
119
+ |-------|----------|-------------|
120
+ | `ai_provider` | Yes | `gemini`, `openai`, `claude` |
121
+ | `ai_model` | Yes | Model identifier |
122
+ | `ai_api_key` | Yes* | AI provider API key (mapped to provider-specific env var) |
123
+ | `task_file` | No | Path to DSPy YAML task file |
124
+ | `task_prompt` | No | Inline task prompt |
125
+ | `post_pr_comment` | No | Post results as PR comment (default: `false`) |
126
+ | `github_token` | No | GitHub token (default: `${{ github.token }}`) |
127
+ | `mcp_servers_config` | No | JSON array of MCP server configs |
128
+ | `slack_webhook_url` | No | Slack webhook URL |
129
+ | `report_template` | No | Custom HTML report template path |
130
+
131
+ *Not required if provider-specific key is set via `env:`.
132
+
133
+ ## Entrypoint Flow
134
+
135
+ `entrypoint.sh` bridges GitHub Action inputs to cicaddy environment:
136
+
137
+ 1. Exports `AI_PROVIDER` and `AI_MODEL` from `INPUT_*` vars
138
+ 2. Maps `INPUT_AI_API_KEY` to provider-specific env var (`GEMINI_API_KEY`, etc.)
139
+ 3. Resolves `AI_TASK_FILE` and `REPORT_TEMPLATE` to absolute paths (required
140
+ because step 5 changes the working directory)
141
+ 4. Extracts `GITHUB_PR_NUMBER` from `GITHUB_REF` (`refs/pull/<N>/merge`)
142
+ 5. Creates `.cicaddy/` subdirectory and `cd`s into it (cicaddy writes reports
143
+ to `../` relative to cwd; this ensures `../` resolves to the writable workspace)
144
+ 6. Runs `cicaddy run`
145
+
146
+ ### Known Pitfalls
147
+
148
+ - **Relative paths break after `cd .cicaddy/`**: Any file path input (task file,
149
+ report template) must be resolved to an absolute path before the `cd`. The
150
+ entrypoint handles this with a `_to_abs` helper. If new file-path inputs are
151
+ added, they must also use this helper.
152
+ - **Secrets in workflow `run:` blocks**: Never use `${{ secrets.* }}` directly
153
+ in `run:` shell blocks. Pass secrets via `env:` to avoid literal interpolation
154
+ in logs. See `.github/workflows/pr-review.yml` for the correct pattern.
155
+
156
+ ## Plugin Architecture
157
+
158
+ The `cicaddy-github` package registers with cicaddy via `pyproject.toml` entry points:
159
+
160
+ ```toml
161
+ [project.entry-points."cicaddy.agents"]
162
+ github = "cicaddy_github.plugin:register_agents"
163
+
164
+ [project.entry-points."cicaddy.settings_loader"]
165
+ github = "cicaddy_github.config.settings:load_settings"
166
+
167
+ [project.entry-points."cicaddy.cli_args"]
168
+ github = "cicaddy_github.plugin:get_cli_args"
169
+
170
+ [project.entry-points."cicaddy.env_vars"]
171
+ github = "cicaddy_github.plugin:get_env_vars"
172
+
173
+ [project.entry-points."cicaddy.validators"]
174
+ github = "cicaddy_github.plugin:validate"
175
+ ```
176
+
177
+ ### Registered Agents
178
+
179
+ | Type | Class | Triggered by |
180
+ |------|-------|--------------|
181
+ | `github_pr` | `GitHubPRAgent` | `GITHUB_EVENT_NAME=pull_request` + `GITHUB_PR_NUMBER` |
182
+ | `github_task` | `GitHubTaskAgent` | `GITHUB_EVENT_NAME` present but not a PR |
183
+
184
+ ### Settings
185
+
186
+ `Settings` extends `CoreSettings` with GitHub-specific fields:
187
+
188
+ - `github_token`, `github_repository`, `github_ref`, `github_event_name`
189
+ - `github_sha`, `github_run_id`, `github_pr_number`
190
+ - `post_pr_comment` (bool)
191
+
192
+ All loaded from environment variables via `load_settings()`.
193
+
194
+ ## DSPy Task Files
195
+
196
+ Task YAML files define analysis prompts. Inputs use `{{VAR}}` placeholders
197
+ resolved from environment variables.
198
+
199
+ ```yaml
200
+ name: pr_code_review
201
+ type: code_review
202
+ version: "1.0"
203
+
204
+ persona: >
205
+ expert code reviewer
206
+
207
+ tools:
208
+ servers:
209
+ - local
210
+ required_tools:
211
+ - read_file
212
+ - glob_files
213
+
214
+ output_format: markdown
215
+
216
+ context: |
217
+ Review the PR diff for the repository...
218
+ ```
219
+
220
+ ## Workflow Usage
221
+
222
+ ```yaml
223
+ - uses: redhat-community-ai-tools/cicaddy-action@v0
224
+ with:
225
+ ai_provider: gemini
226
+ ai_model: gemini-3-flash-preview
227
+ ai_api_key: ${{ secrets.AI_API_KEY }}
228
+ task_file: tasks/pr_review.yml
229
+ post_pr_comment: 'true'
230
+ github_token: ${{ secrets.GITHUB_TOKEN }}
231
+ ```
232
+
233
+ ## Running Locally
234
+
235
+ Cicaddy can run locally to review PRs without GitHub Actions. Create an env
236
+ file and use `uv run cicaddy run --env-file <file>`.
237
+
238
+ ### Env File Template (PR Review)
239
+
240
+ ```bash
241
+ # AI Provider
242
+ AI_PROVIDER=gemini
243
+ AI_MODEL=gemini-3-flash-preview
244
+ GEMINI_API_KEY=<key>
245
+
246
+ # GitHub Configuration
247
+ GITHUB_TOKEN=<token> # gh auth token
248
+ GITHUB_REPOSITORY=owner/repo
249
+ GITHUB_EVENT_NAME=pull_request
250
+ GITHUB_PR_NUMBER=42
251
+
252
+ # Agent Settings
253
+ POST_PR_COMMENT=true
254
+ ENABLE_LOCAL_TOOLS=true
255
+ LOCAL_TOOLS_WORKING_DIR=.
256
+
257
+ # Optional: MCP servers
258
+ MCP_SERVERS_CONFIG=[{"name": "context7", "protocol": "http", "endpoint": "https://mcp.context7.com/mcp", "headers": {"CONTEXT7_API_KEY": "<key>"}}]
259
+
260
+ LOG_LEVEL=INFO
261
+ ```
262
+
263
+ ### Key Commands
264
+
265
+ ```bash
266
+ # Install plugin in editable mode (uses live code)
267
+ uv pip install -e ".[test]"
268
+
269
+ # Run a PR review
270
+ uv run cicaddy run --env-file .env.my-review
271
+
272
+ # Validate configuration
273
+ uv run cicaddy validate --env-file .env.my-review
274
+ ```
275
+
276
+ ### Agent Type Detection
277
+
278
+ The agent type is auto-detected by `_detect_github_agent_type` in
279
+ `src/cicaddy_github/github_integration/detector.py` (registered at priority 40):
280
+ - `GITHUB_EVENT_NAME=pull_request` → `github_pr`
281
+ - `GITHUB_PR_NUMBER` set (fallback) → `github_pr`
282
+ - Otherwise → falls through to core detectors
283
+
284
+ ### Additional Env Vars
285
+
286
+ - `AI_TASK_FILE`: Path to DSPy YAML task file for custom workflows
287
+ - `GIT_DIFF_CONTEXT_LINES`: Number of context lines in diffs (default: 10)
288
+
289
+ ### MCP Server Config Format
290
+
291
+ JSON array. Each server object has:
292
+ - `name` (string): Server identifier
293
+ - `protocol` (string): `http`, `sse`, `stdio`, or `websocket`
294
+ - `endpoint` (string): Server URL
295
+ - `headers` (object, optional): HTTP headers (e.g. API keys)
296
+
297
+ ### Notes
298
+
299
+ - **Never commit env files with secrets.** Use `.env.*` pattern (already in `.gitignore`)
300
+ - `POST_PR_COMMENT=true` requires a token with write access to pull requests
301
+ (`repo` scope includes this; for fine-grained tokens, enable "Pull requests: Write")
302
+ - The `github_pr` agent updates its PR comment in-place on re-runs
303
+ - Use `gh auth token` to generate a GitHub token quickly
304
+
305
+ ## Code Style
306
+
307
+ - Python 3.11+ with type hints
308
+ - Ruff for linting and formatting (line-length 100)
309
+ - Google-style docstrings
310
+ - Async methods for I/O operations
311
+ - `@tool` decorator for local tool definitions
312
+ - pytest with pytest-asyncio for testing
@@ -0,0 +1,24 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ groups:
8
+ python-dependencies:
9
+ patterns:
10
+ - "*"
11
+
12
+ - package-ecosystem: "github-actions"
13
+ directory: "/"
14
+ schedule:
15
+ interval: "weekly"
16
+ groups:
17
+ github-actions:
18
+ patterns:
19
+ - "*"
20
+
21
+ - package-ecosystem: "docker"
22
+ directory: "/"
23
+ schedule:
24
+ interval: "weekly"
@@ -0,0 +1,66 @@
1
+ name: Generate Changelog Report
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ from_tag:
9
+ description: 'Start tag'
10
+ required: false
11
+ to_tag:
12
+ description: 'End tag (default: latest)'
13
+ required: false
14
+
15
+ permissions:
16
+ contents: read
17
+ pages: write
18
+ id-token: write
19
+
20
+ jobs:
21
+ generate:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v6
25
+ with:
26
+ fetch-depth: 0
27
+
28
+ - name: Generate Report
29
+ uses: ./
30
+ id: report
31
+ with:
32
+ ai_provider: gemini
33
+ ai_model: gemini-2.5-flash
34
+ ai_api_key: ${{ secrets.AI_API_KEY }}
35
+ task_file: tasks/changelog_report.yml
36
+ env:
37
+ FROM_TAG: ${{ inputs.from_tag || '' }}
38
+ TO_TAG: ${{ inputs.to_tag || github.event.release.tag_name || 'HEAD' }}
39
+ REPORT_TITLE: "Cicaddy Action — Release Update"
40
+
41
+ - name: Upload report artifact
42
+ uses: actions/upload-artifact@v7
43
+ with:
44
+ name: changelog-report
45
+ path: ${{ steps.report.outputs.report_html }}
46
+
47
+ - name: Prepare Pages
48
+ run: |
49
+ mkdir -p _site
50
+ cp "${{ steps.report.outputs.report_html }}" _site/index.html
51
+
52
+ - name: Upload Pages artifact
53
+ uses: actions/upload-pages-artifact@v4
54
+ with:
55
+ path: _site
56
+
57
+ deploy:
58
+ needs: generate
59
+ runs-on: ubuntu-latest
60
+ environment:
61
+ name: github-pages
62
+ url: ${{ steps.deployment.outputs.page_url }}
63
+ steps:
64
+ - name: Deploy to GitHub Pages
65
+ id: deployment
66
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,59 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - uses: actions/setup-python@v6
18
+ with:
19
+ python-version: "3.13"
20
+ - uses: astral-sh/setup-uv@v7
21
+ - name: Install dependencies
22
+ run: |
23
+ uv pip install --system pre-commit
24
+ uv pip install --system -e ".[test]"
25
+ - name: Run pre-commit (skip ty)
26
+ run: SKIP=ty pre-commit run --all-files
27
+ - name: Run ty check
28
+ run: uvx ty check --python "$(python3 -c 'import sys; print(sys.prefix)')"
29
+
30
+ test:
31
+ runs-on: ubuntu-latest
32
+ strategy:
33
+ matrix:
34
+ python-version: ["3.11", "3.12", "3.13"]
35
+ steps:
36
+ - uses: actions/checkout@v6
37
+ - uses: actions/setup-python@v6
38
+ with:
39
+ python-version: ${{ matrix.python-version }}
40
+ - uses: astral-sh/setup-uv@v7
41
+ - name: Install dependencies
42
+ run: uv pip install --system -e ".[test]"
43
+ - name: Run tests with coverage
44
+ run: pytest tests/ -q --cov=src/cicaddy_github --cov-report=xml
45
+ - name: Upload coverage
46
+ if: matrix.python-version == '3.13'
47
+ uses: actions/upload-artifact@v7
48
+ with:
49
+ name: coverage-report
50
+ path: coverage.xml
51
+
52
+ docker-build:
53
+ runs-on: ubuntu-latest
54
+ steps:
55
+ - uses: actions/checkout@v6
56
+ - name: Build Docker image
57
+ run: docker build -t cicaddy-action:test .
58
+ - name: Test Docker image runs
59
+ run: docker run --rm --entrypoint cicaddy cicaddy-action:test --version
@@ -0,0 +1,45 @@
1
+ name: AI PR Review
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize]
6
+
7
+ permissions:
8
+ contents: read
9
+ pull-requests: write
10
+
11
+ jobs:
12
+ review:
13
+ if: ${{ github.actor != 'dependabot[bot]' }}
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ with:
18
+ fetch-depth: 0
19
+
20
+ - name: Build MCP config
21
+ id: mcp
22
+ env:
23
+ CONTEXT7_KEY: ${{ secrets.CONTEXT7_API_KEY }}
24
+ run: |
25
+ # Context7 is optional — only include if secret is configured
26
+ if [ -n "${CONTEXT7_KEY}" ]; then
27
+ echo "config=[{\"name\":\"Context7\",\"protocol\":\"http\",\"endpoint\":\"https://mcp.context7.com/mcp\",\"headers\":{\"CONTEXT7_API_KEY\":\"${CONTEXT7_KEY}\"},\"timeout\":300,\"idle_timeout\":60}]" >> "$GITHUB_OUTPUT"
28
+ else
29
+ echo 'config=[]' >> "$GITHUB_OUTPUT"
30
+ fi
31
+
32
+ - name: AI Code Review
33
+ uses: ./
34
+ id: review
35
+ with:
36
+ ai_provider: gemini
37
+ ai_model: gemini-3-flash-preview
38
+ ai_api_key: ${{ secrets.AI_API_KEY }}
39
+ task_file: tasks/pr_review.yml
40
+ post_pr_comment: 'true'
41
+ github_token: ${{ secrets.GITHUB_TOKEN }}
42
+ mcp_servers_config: ${{ steps.mcp.outputs.config }}
43
+ env:
44
+ ANALYSIS_FOCUS: "general"
45
+ GIT_DIFF_CONTEXT_LINES: "15"
@@ -0,0 +1,101 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ permissions:
9
+ contents: write
10
+ packages: write
11
+ id-token: write
12
+
13
+ jobs:
14
+ release-build:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+ - uses: astral-sh/setup-uv@v7
19
+ - uses: actions/setup-python@v6
20
+ with:
21
+ python-version: "3.13"
22
+ - name: Build release distributions
23
+ run: uv build
24
+ - name: Upload distributions
25
+ uses: actions/upload-artifact@v7
26
+ with:
27
+ name: release-dists
28
+ path: dist/
29
+
30
+ docker-publish:
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - uses: actions/checkout@v6
34
+ - name: Set up QEMU
35
+ uses: docker/setup-qemu-action@v4
36
+ - name: Set up Docker Buildx
37
+ uses: docker/setup-buildx-action@v4
38
+ - name: Log in to GHCR
39
+ uses: docker/login-action@v4
40
+ with:
41
+ registry: ghcr.io
42
+ username: ${{ github.actor }}
43
+ password: ${{ secrets.GITHUB_TOKEN }}
44
+ - name: Extract metadata
45
+ id: meta
46
+ uses: docker/metadata-action@v6
47
+ with:
48
+ images: ghcr.io/${{ github.repository }}
49
+ tags: |
50
+ type=semver,pattern={{version}}
51
+ type=semver,pattern={{major}}.{{minor}}
52
+ type=semver,pattern={{major}}
53
+ - name: Build and push Docker image
54
+ uses: docker/build-push-action@v7
55
+ with:
56
+ context: .
57
+ platforms: linux/amd64,linux/arm64
58
+ push: true
59
+ tags: ${{ steps.meta.outputs.tags }}
60
+ labels: ${{ steps.meta.outputs.labels }}
61
+ cache-from: type=gha
62
+ cache-to: type=gha,mode=max
63
+
64
+ github-release:
65
+ needs: [release-build, docker-publish]
66
+ runs-on: ubuntu-latest
67
+ steps:
68
+ - uses: actions/checkout@v6
69
+ - name: Create GitHub Release
70
+ uses: softprops/action-gh-release@v2
71
+ with:
72
+ generate_release_notes: true
73
+
74
+ pypi-publish:
75
+ needs: release-build
76
+ runs-on: ubuntu-latest
77
+ permissions:
78
+ id-token: write
79
+ environment:
80
+ name: pypi
81
+ url: https://pypi.org/p/cicaddy-github
82
+ steps:
83
+ - uses: actions/download-artifact@v8
84
+ with:
85
+ name: release-dists
86
+ path: dist/
87
+ - uses: pypa/gh-action-pypi-publish@release/v1
88
+ with:
89
+ packages-dir: dist/
90
+
91
+ update-major-tag:
92
+ needs: github-release
93
+ runs-on: ubuntu-latest
94
+ steps:
95
+ - uses: actions/checkout@v6
96
+ - name: Update major version tag
97
+ run: |
98
+ TAG="${GITHUB_REF#refs/tags/}"
99
+ MAJOR="${TAG%%.*}"
100
+ git tag -f "$MAJOR" "$TAG"
101
+ git push origin "$MAJOR" --force
@@ -0,0 +1,39 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ *.egg
9
+
10
+ # Virtual environments
11
+ .venv/
12
+ venv/
13
+
14
+ # IDE
15
+ .idea/
16
+ .vscode/
17
+ *.swp
18
+ *.swo
19
+
20
+ # Testing
21
+ .coverage
22
+ coverage.xml
23
+ htmlcov/
24
+ .pytest_cache/
25
+
26
+ # OS
27
+ .DS_Store
28
+ Thumbs.db
29
+
30
+ # Environment
31
+ .env
32
+ .env.*
33
+ .env.local
34
+
35
+ # Claude Code local settings (contains developer-specific paths)
36
+ .claude/settings.local.json
37
+
38
+ # UV
39
+ uv.lock
@@ -0,0 +1,56 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: check-yaml
6
+ args: ['--unsafe']
7
+ - id: end-of-file-fixer
8
+ - id: trailing-whitespace
9
+ - id: detect-private-key
10
+ - id: check-added-large-files
11
+ args: ['--maxkb=1000']
12
+ - id: check-merge-conflict
13
+ - id: check-json
14
+ - id: check-toml
15
+ - id: debug-statements
16
+ - id: mixed-line-ending
17
+
18
+ - repo: https://github.com/astral-sh/ruff-pre-commit
19
+ rev: v0.15.0
20
+ hooks:
21
+ - id: ruff
22
+ args: [--fix]
23
+ - id: ruff-format
24
+
25
+ - repo: local
26
+ hooks:
27
+ - id: ty
28
+ name: ty check
29
+ entry: uvx ty check
30
+ language: system
31
+ types: [python]
32
+ pass_filenames: false
33
+ exclude: ^(tests/)
34
+
35
+ - repo: https://github.com/PyCQA/bandit
36
+ rev: 1.9.3
37
+ hooks:
38
+ - id: bandit
39
+ args: ['-c', 'pyproject.toml', '-r', 'src/']
40
+ additional_dependencies: ['bandit[toml]']
41
+ pass_filenames: false
42
+
43
+ - repo: https://github.com/zricethezav/gitleaks
44
+ rev: v8.30.0
45
+ hooks:
46
+ - id: gitleaks
47
+
48
+ - repo: https://github.com/hadolint/hadolint
49
+ rev: v2.13.1-beta
50
+ hooks:
51
+ - id: hadolint-docker
52
+
53
+ - repo: https://github.com/rhysd/actionlint
54
+ rev: v1.7.7
55
+ hooks:
56
+ - id: actionlint