Skip to content

Commit 4928c2a

Browse files
XOlegatorclaude
andauthored
chore(ci): add GitHub Actions, release-please, and Docker publish automation (#148)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0e25cda commit 4928c2a

9 files changed

Lines changed: 370 additions & 10 deletions

File tree

.github/workflows/ci.yml

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master]
8+
9+
concurrency:
10+
group: ci-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
15+
cs-fixer:
16+
name: PHP CS Fixer
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: shivammathur/setup-php@v2
22+
with:
23+
php-version: '8.4'
24+
extensions: intl, pdo_mysql
25+
coverage: none
26+
27+
- uses: actions/cache@v4
28+
with:
29+
path: ~/.composer/cache
30+
key: composer-8.4-${{ hashFiles('composer.lock') }}
31+
restore-keys: composer-8.4-
32+
33+
- run: composer install --no-interaction --prefer-dist --no-scripts
34+
35+
- run: vendor/bin/php-cs-fixer fix --dry-run --diff
36+
37+
phpstan:
38+
name: PHPStan
39+
runs-on: ubuntu-latest
40+
steps:
41+
- uses: actions/checkout@v4
42+
43+
- uses: shivammathur/setup-php@v2
44+
with:
45+
php-version: '8.4'
46+
extensions: intl, pdo_mysql
47+
coverage: none
48+
49+
- uses: actions/cache@v4
50+
with:
51+
path: ~/.composer/cache
52+
key: composer-8.4-${{ hashFiles('composer.lock') }}
53+
restore-keys: composer-8.4-
54+
55+
- run: composer install --no-interaction --prefer-dist --no-scripts
56+
57+
# phpstan-symfony reads the compiled DI container to resolve service types
58+
- name: Warm up Symfony dev container
59+
env:
60+
APP_ENV: dev
61+
APP_DEBUG: '1'
62+
DATABASE_URL: mysql://pinba:pinba@127.0.0.1:3306/pinba?serverVersion=8.4.0
63+
run: php bin/console cache:warmup
64+
65+
- uses: actions/cache@v4
66+
with:
67+
path: var/phpstan
68+
key: phpstan-${{ hashFiles('phpstan.neon', 'composer.lock') }}
69+
restore-keys: phpstan-
70+
71+
- run: vendor/bin/phpstan analyse --no-progress
72+
73+
phpunit:
74+
name: PHPUnit (PHP ${{ matrix.php }})
75+
runs-on: ubuntu-latest
76+
strategy:
77+
fail-fast: false
78+
matrix:
79+
php: ['8.4', '8.5']
80+
steps:
81+
- uses: actions/checkout@v4
82+
83+
- uses: shivammathur/setup-php@v2
84+
with:
85+
php-version: ${{ matrix.php }}
86+
extensions: intl, pdo_mysql
87+
coverage: none
88+
89+
- uses: actions/cache@v4
90+
with:
91+
path: ~/.composer/cache
92+
key: composer-${{ matrix.php }}-${{ hashFiles('composer.lock') }}
93+
restore-keys: composer-${{ matrix.php }}-
94+
95+
- run: composer install --no-interaction --prefer-dist --no-scripts
96+
97+
- name: Run test suite
98+
env:
99+
APP_ENV: test
100+
DATABASE_URL: mysql://pinba:pinba@127.0.0.1:3306/pinba?serverVersion=8.4.0
101+
run: vendor/bin/phpunit --no-progress

.github/workflows/docker.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Docker
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
build-push:
9+
name: Build and push Docker image
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- uses: docker/setup-buildx-action@v3
15+
16+
- uses: docker/login-action@v3
17+
with:
18+
username: ${{ secrets.DOCKERHUB_USERNAME }}
19+
password: ${{ secrets.DOCKERHUB_TOKEN }}
20+
21+
- name: Extract version from tag
22+
id: meta
23+
run: |
24+
TAG="${{ github.ref_name }}"
25+
VERSION="${TAG#v}"
26+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
27+
28+
- uses: docker/build-push-action@v6
29+
with:
30+
context: .
31+
file: Dockerfile.pinboard
32+
push: true
33+
tags: |
34+
xolegator/pinboard:${{ steps.meta.outputs.version }}
35+
xolegator/pinboard:latest
36+
cache-from: type=gha
37+
cache-to: type=gha,mode=max
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Release Please
2+
3+
on:
4+
push:
5+
branches: [master]
6+
7+
permissions:
8+
contents: write
9+
pull-requests: write
10+
11+
jobs:
12+
release-please:
13+
runs-on: ubuntu-latest
14+
outputs:
15+
release_created: ${{ steps.rp.outputs.release_created }}
16+
tag_name: ${{ steps.rp.outputs.tag_name }}
17+
steps:
18+
- uses: googleapis/release-please-action@v4
19+
id: rp
20+
with:
21+
config-file: release-please-config.json
22+
manifest-file: .release-please-manifest.json

.release-please-manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
".": "2.0.0"
3+
}

AGENTS.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ PHPStan runs at level 10. All violations must be fixed with real engineering sol
4848
- Annotate repository methods with typed PHPDoc array shapes, e.g. `@return list<array{id: int, name: string}>`, and cast at the boundary.
4949
- For `Yaml::parseFile()` and session `get()` returning `mixed`, validate with `is_array()` and iterate to enforce string keys.
5050

51+
## Development Workflow
52+
53+
All changes enter `master` exclusively via pull request — direct pushes are blocked.
54+
55+
**Branch → PR → CI → squash merge → master → Release Please → GitHub Release → Docker Hub**
56+
57+
- Branch names: `fix/description`, `feat/description`, `chore/description`, etc.
58+
- Commit titles must follow Conventional Commits (see `docs/contributing.md`).
59+
- CI runs three mandatory jobs: `PHP CS Fixer`, `PHPStan`, `PHPUnit (8.4 + 8.5)`.
60+
- After a PR merges, Release Please automatically opens or updates a Release PR.
61+
- Merging the Release PR creates a `vX.Y.Z` tag and GitHub Release.
62+
- The Docker workflow fires on the published release and pushes `xolegator/pinboard:<version>` and `xolegator/pinboard:latest` to Docker Hub.
63+
64+
Full details: `docs/contributing.md` (PR workflow) and `docs/releasing.md` (release process).
65+
5166
## Commit Messages
5267

5368
- Use Conventional Commits style.

docs/contributing.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Contributing
2+
3+
## Workflow overview
4+
5+
All changes enter `master` exclusively via pull request. Direct pushes to `master` are blocked by branch protection rules.
6+
7+
```
8+
feature-branch → PR → CI (cs-fixer, phpstan, phpunit) → merge to master
9+
```
10+
11+
After merge, CI runs again on `master`. Release Please then opens or updates a "Release PR" that tracks the next version. When that Release PR is merged, a GitHub Release is created automatically and the Docker image is published.
12+
13+
## Branch naming
14+
15+
Use a descriptive lowercase name, optionally prefixed by the change type:
16+
17+
```
18+
fix/timer-table-layout
19+
feat/db-user-sync
20+
refactor/sass-build
21+
chore/composer-update
22+
```
23+
24+
No enforced pattern beyond keeping it readable.
25+
26+
## Commit messages
27+
28+
This project follows [Conventional Commits](https://www.conventionalcommits.org/). Every commit reachable from `master` must have a conforming title because Release Please reads them to determine the next version and generate the changelog.
29+
30+
```
31+
type(scope): short imperative description
32+
```
33+
34+
Common types and their effect on versioning:
35+
36+
| Type | SemVer bump | Appears in changelog |
37+
|------------|-------------|----------------------|
38+
| `feat` | minor | yes |
39+
| `fix` | patch | yes |
40+
| `perf` | patch | yes |
41+
| `refactor` | patch | yes |
42+
| `chore` | patch | yes |
43+
| `docs` | patch | hidden |
44+
| `test` | patch | hidden |
45+
| `ci` | patch | hidden |
46+
47+
A `!` suffix or `BREAKING CHANGE:` footer triggers a **major** bump regardless of type.
48+
49+
Useful scopes for this project: `ui`, `auth`, `api`, `db`, `config`, `docker`, `ci`, `docs`.
50+
51+
## Creating a pull request
52+
53+
1. Create a branch from the latest `master`.
54+
2. Make commits with Conventional Commit titles.
55+
3. Push and open a PR against `master`.
56+
4. CI runs three jobs automatically: `PHP CS Fixer`, `PHPStan`, `PHPUnit`.
57+
5. All three must pass before the PR can be merged.
58+
6. Request review or merge it yourself if you are a maintainer.
59+
60+
## Merge policy
61+
62+
Use **squash merge** when merging PRs. The squash commit title becomes the changelog entry, so make sure it is a valid Conventional Commit title (GitHub pre-fills it from the PR title).
63+
64+
After merge, CI reruns on `master`. If it passes, Release Please updates or creates its Release PR.
65+
66+
## Code style
67+
68+
Run the following before pushing to avoid CI failures:
69+
70+
```bash
71+
vendor/bin/php-cs-fixer fix
72+
vendor/bin/phpstan analyse --no-progress
73+
vendor/bin/phpunit --no-progress
74+
```
75+
76+
See [testing.md](testing.md) for the full checklist and [standards.md](standards.md) for code conventions.

docs/releasing.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Releasing
2+
3+
Releases are fully automated via [Release Please](https://github.com/googleapis/release-please). No manual tagging or changelog editing is needed.
4+
5+
## How it works
6+
7+
1. A developer merges a PR into `master` (squash merge, Conventional Commit title).
8+
2. The `Release Please` GitHub Actions workflow runs and inspects unreleased commits since the last tag.
9+
3. Release Please opens (or updates) a **Release PR** titled `chore(main): release X.Y.Z`.
10+
- The PR contains a changelog diff for `CHANGELOG.md` and the new version number.
11+
4. When a maintainer merges the Release PR, Release Please creates a GitHub Release and pushes a `vX.Y.Z` tag.
12+
5. The `Docker` workflow triggers on the new release and publishes the image to Docker Hub.
13+
14+
## Version bump rules
15+
16+
Release Please derives the next version from Conventional Commits:
17+
18+
- `BREAKING CHANGE` footer or `!` suffix → major bump
19+
- `feat` → minor bump
20+
- anything else (`fix`, `perf`, `refactor`, `chore`, …) → patch bump
21+
22+
## Docker image publishing
23+
24+
The `docker.yml` workflow fires on every published GitHub Release. It:
25+
26+
- Builds `Dockerfile.pinboard` from the release commit.
27+
- Tags the image as `xolegator/pinboard:<version>` and `xolegator/pinboard:latest`.
28+
- Pushes both tags to Docker Hub using BuildKit layer caching.
29+
30+
### Required repository secrets
31+
32+
| Secret | Purpose |
33+
|---------------------|----------------------------------------------|
34+
| `DOCKERHUB_USERNAME`| Docker Hub account username |
35+
| `DOCKERHUB_TOKEN` | Docker Hub access token (read/write, no delete) |
36+
37+
Add these under **Settings → Secrets and variables → Actions** in the GitHub repository.
38+
39+
## Manifest seed
40+
41+
`.release-please-manifest.json` records the current released version (`2.0.0`). Release Please reads this to know where to start. Do not edit this file manually — Release Please updates it automatically when it creates a Release PR.
42+
43+
## Configuration
44+
45+
`release-please-config.json` controls:
46+
47+
- `release-type: simple` — manages `CHANGELOG.md` and git tags; does not modify `composer.json`.
48+
- `changelog-path: CHANGELOG.md` — separate from the hand-maintained `CHANGELOG` file which covers the pre-2.x history.
49+
- Changelog sections and which commit types are shown or hidden.
50+
51+
## Branch protection requirements
52+
53+
For the workflow to function correctly, `master` should have the following branch protection rules active:
54+
55+
- Require a pull request before merging (no direct pushes).
56+
- Require status checks to pass before merging: `PHP CS Fixer`, `PHPStan`, `PHPUnit (PHP 8.4)`, `PHPUnit (PHP 8.5)`.
57+
- Require branches to be up to date before merging.
58+
- Allow repository admins to bypass (so Release Please can push the release commit).
59+
60+
See [GitHub documentation](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches) for how to configure rulesets.

docs/testing.md

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,51 @@
11
# Testing
22

3-
## Code checks
3+
## Local checks before pushing
44

5-
- PHP syntax: `php -l path/to/file.php`
6-
- Twig syntax: `php bin/console lint:twig templates/...`
7-
- Frontend build: `pnpm build`
8-
- Static analysis: `vendor/bin/phpstan analyse`
9-
- Code style: `vendor/bin/php-cs-fixer fix --dry-run`
5+
Run these before every push to avoid CI failures:
6+
7+
```bash
8+
# PHP syntax (touched files only)
9+
php -l src/Path/To/File.php
10+
11+
# Twig syntax (touched templates only)
12+
php bin/console lint:twig templates/
13+
14+
# Code style — check, then fix
15+
vendor/bin/php-cs-fixer fix --dry-run --diff
16+
vendor/bin/php-cs-fixer fix
17+
18+
# Static analysis (level 10)
19+
vendor/bin/phpstan analyse --no-progress
20+
21+
# Unit and functional tests
22+
vendor/bin/phpunit --no-progress
23+
24+
# Frontend assets
25+
pnpm build
26+
```
27+
28+
## CI pipeline
29+
30+
Three jobs run automatically on every PR and on every push to `master`:
31+
32+
| Job | Command |
33+
|----------------|-------------------------------------------------|
34+
| PHP CS Fixer | `vendor/bin/php-cs-fixer fix --dry-run --diff` |
35+
| PHPStan | `vendor/bin/phpstan analyse --no-progress` |
36+
| PHPUnit | `vendor/bin/phpunit --no-progress` (PHP 8.4 + 8.5) |
37+
38+
All three must pass before a PR can be merged. See `.github/workflows/ci.yml` for the full configuration.
1039

1140
## What to verify after changes
1241

13-
- Templates and layout — in a browser.
14-
- Sass and JS — after rebuilding the frontend.
15-
- Symfony Console commands — on a real local environment.
16-
- Pinba data changes — on admin pages and in the database.
42+
- Templates and layout — open the relevant pages in a browser.
43+
- Sass and JS — after rebuilding with `pnpm build`.
44+
- Symfony Console commands — run on a real local environment.
45+
- Pinba data changes — verify on admin pages and in the database.
1746

1847
## Practices
1948

2049
- For targeted changes, run only the checks relevant to the modified files.
2150
- If a change touches the shared layout or build pipeline, rebuild the frontend and open key pages manually.
51+
- PHPStan runs at level 10. All violations must be fixed with real solutions — see `AGENTS.md` for the full list of allowed and forbidden patterns.

0 commit comments

Comments
 (0)