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
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
rebase-strategy: "auto"
86 changes: 86 additions & 0 deletions .github/workflows/auto-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Auto release

on:
pull_request:
types: [closed]
branches:
- main

permissions:
contents: write

jobs:
release:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- name: Determine version bump
id: bump
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
if [[ "$PR_TITLE" =~ ^[a-z]+(\(.+\))?\!: ]]; then
echo "type=major" >> "$GITHUB_OUTPUT"
elif [[ "$PR_TITLE" =~ ^feat(\(.+\))?: ]]; then
echo "type=minor" >> "$GITHUB_OUTPUT"
elif [[ "$PR_TITLE" =~ ^(fix|perf|refactor)(\(.+\))?\!?: ]]; then
echo "type=patch" >> "$GITHUB_OUTPUT"
else
echo "Skipping release for: $PR_TITLE"
echo "type=skip" >> "$GITHUB_OUTPUT"
fi

- uses: actions/checkout@v6.0.2
if: steps.bump.outputs.type != 'skip'
with:
fetch-depth: 0

- name: Calculate next version
if: steps.bump.outputs.type != 'skip'
id: version
env:
BUMP_TYPE: ${{ steps.bump.outputs.type }}
run: |
latest=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n1)
if [ -z "$latest" ]; then
latest="v0.0.0"
fi
version="${latest#v}"
IFS='.' read -r major minor patch <<< "$version"

case "$BUMP_TYPE" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
esac

next="v${major}.${minor}.${patch}"
echo "tag=$next" >> "$GITHUB_OUTPUT"
echo "major=v${major}" >> "$GITHUB_OUTPUT"
echo "Bumping ${BUMP_TYPE}: $latest -> $next"

- name: Create GitHub release
if: steps.bump.outputs.type != 'skip'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_TAG: ${{ steps.version.outputs.tag }}
run: |
gh release create "$NEW_TAG" \
--title "$NEW_TAG" \
--generate-notes \
--target main

# Major bump: creates the new simple tag (e.g. v2) at the new commit;
# the previous major tag (v1) stays at its last commit so existing
# pins keep receiving only v1.x updates.
# Minor/patch bump: moves the existing major tag (v1) to the new commit.
- name: Update major version tag
if: steps.bump.outputs.type != 'skip'
env:
NEW_TAG: ${{ steps.version.outputs.tag }}
MAJOR_TAG: ${{ steps.version.outputs.major }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -fa "$MAJOR_TAG" -m "$NEW_TAG"
git push --force origin "$MAJOR_TAG"
18 changes: 18 additions & 0 deletions .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Auto-merge Dependabot PRs

on: pull_request

permissions:
contents: write
pull-requests: write

jobs:
auto-merge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Enable auto-merge
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge --auto --rebase "$PR_URL"
29 changes: 29 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Validate PR title

on:
pull_request:
types: [opened, edited, synchronize, reopened]
branches:
- main

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Check conventional commit format
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
pattern='^(feat|fix|docs|chore|refactor|test|ci|perf|build|style)(\(.+\))?\!?: .+'
if [[ ! "$PR_TITLE" =~ $pattern ]]; then
echo "::error::PR title must follow conventional commit format:"
echo "::error:: <type>[optional scope][!]: <description>"
echo "::error::Allowed types: feat, fix, docs, chore, refactor, test, ci, perf, build, style"
echo "::error::Add '!' before ':' for breaking changes (e.g., 'feat!: redesign API')"
echo "::error::Got: '$PR_TITLE'"
exit 1
fi
echo "PR title is valid: $PR_TITLE"
102 changes: 102 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Test

on:
push:
branches:
- main
pull_request:
branches:
- main

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- name: Run actionlint
uses: reviewdog/action-actionlint@v1
with:
fail_level: error

smoke:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v6.0.2

# Install pacto directly instead of using the action's `setup` command:
# the upstream install script makes unauthenticated api.github.com calls
# that get rate-limited on shared macOS runner IPs. We resolve "latest"
# ourselves with an authenticated request (5000/h token limit) so the
# smoke test always runs against the newest pacto release.
- name: Install pacto (latest)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
tag=$(curl -fsSL \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/TrianaLab/pacto/releases/latest \
| grep -E '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
if [ -z "$tag" ]; then
echo "::error::Could not resolve latest pacto release"
exit 1
fi
case "$(uname -m)" in
x86_64|amd64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "unsupported arch: $(uname -m)" >&2; exit 1 ;;
esac
os="$(uname | tr '[:upper:]' '[:lower:]')"
url="https://github.com/TrianaLab/pacto/releases/download/${tag}/pacto_${os}_${arch}"
echo "Installing pacto ${tag} from ${url}"
curl -fsSL -o /tmp/pacto "$url"
chmod +x /tmp/pacto
sudo mv /tmp/pacto /usr/local/bin/pacto
pacto version

- name: Create fixture contract
run: |
mkdir -p contract
cat > contract/pacto.yaml <<'EOF'
pactoVersion: "1.0"
service:
name: smoke-test
version: 1.0.0
EOF

- name: Validate contract
uses: ./
with:
command: validate
path: contract

- name: Generate doc
uses: ./
with:
command: doc
path: contract
add-to-summary: 'false'

- name: Diff contract against itself
id: diff
uses: ./
with:
command: diff
old: contract
new: contract
fail-on-breaking: 'true'

- name: Verify no breaking changes detected
run: |
if [ "${{ steps.diff.outputs.has-breaking-changes }}" != "false" ]; then
echo "::error::Expected has-breaking-changes=false, got '${{ steps.diff.outputs.has-breaking-changes }}'"
exit 1
fi