mirror bufbuild/buf-action@v1.1.1
Buf action CI / build (push) Canceled after 0s
Buf action CI / test-setup (push) Canceled after 0s
Buf action CI / test-setup-latest (push) Canceled after 0s
Buf action CI / test-setup-macos (push) Canceled after 0s
Buf action CI / test-setup-windows (push) Canceled after 0s
Buf action CI / test-setup-self-hosted (push) Canceled after 0s
Buf action CI / test-empty-build (push) Canceled after 0s
Buf action CI / test-lint (push) Canceled after 0s
Buf action CI / test-format (push) Canceled after 0s
Buf action CI / test-breaking (push) Canceled after 0s
Buf action CI / test-comment (push) Canceled after 0s
Buf action CI / test-push (push) Canceled after 0s
Buf action CI / test-push-token-only (push) Canceled after 0s
Buf action CI / test-push-unnamed (push) Canceled after 0s
Buf action CI / test-archive (push) Canceled after 0s
Buf action CI / test-archive-root (push) Canceled after 0s
Buf action CI / test-archive-unnamed (push) Canceled after 0s
Buf action CI / build (push) Canceled after 0s
Buf action CI / test-setup (push) Canceled after 0s
Buf action CI / test-setup-latest (push) Canceled after 0s
Buf action CI / test-setup-macos (push) Canceled after 0s
Buf action CI / test-setup-windows (push) Canceled after 0s
Buf action CI / test-setup-self-hosted (push) Canceled after 0s
Buf action CI / test-empty-build (push) Canceled after 0s
Buf action CI / test-lint (push) Canceled after 0s
Buf action CI / test-format (push) Canceled after 0s
Buf action CI / test-breaking (push) Canceled after 0s
Buf action CI / test-comment (push) Canceled after 0s
Buf action CI / test-push (push) Canceled after 0s
Buf action CI / test-push-token-only (push) Canceled after 0s
Buf action CI / test-push-unnamed (push) Canceled after 0s
Buf action CI / test-archive (push) Canceled after 0s
Buf action CI / test-archive-root (push) Canceled after 0s
Buf action CI / test-archive-unnamed (push) Canceled after 0s
This commit is contained in:
commit
38d0d7e18f
50 files changed
+85157
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
dist/*.js binary
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width="85" height="80" viewBox="0 0 85 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="25.4114" y="20.7058" width="8.47059" height="59.2941" fill="#0E5DF5"/>
|
||||||
|
<rect x="42.3533" y="20.7058" width="8.47059" height="59.2941" fill="#0E5DF5"/>
|
||||||
|
<rect x="59.2942" y="20.7058" width="8.47059" height="59.2941" fill="#0E5DF5"/>
|
||||||
|
<rect x="76.2352" y="20.7058" width="8.47059" height="59.2941" fill="#0E5DF5"/>
|
||||||
|
<rect width="8.47059" height="59.2941" fill="#5FDCFF"/>
|
||||||
|
<rect x="16.9409" width="8.47059" height="59.2941" fill="#5FDCFF"/>
|
||||||
|
<rect x="33.8821" width="8.47059" height="59.2941" fill="#5FDCFF"/>
|
||||||
|
<rect x="50.8237" width="8.47059" height="59.2941" fill="#5FDCFF"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 683 B |
@@ -0,0 +1,6 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Add issues and PRs to project
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- transferred
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
issue_comment:
|
||||||
|
types:
|
||||||
|
- created
|
||||||
|
permissions:
|
||||||
|
issues: read
|
||||||
|
pull-requests: read
|
||||||
|
repository-projects: write
|
||||||
|
jobs:
|
||||||
|
call-workflow-add-to-project:
|
||||||
|
name: Call workflow to add issue to project
|
||||||
|
uses: bufbuild/base-workflows/.github/workflows/add-to-project.yaml@main
|
||||||
|
secrets: inherit
|
||||||
@@ -0,0 +1,394 @@
|
|||||||
|
name: Buf action CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
env:
|
||||||
|
BUF_VERSION: "1.35.0" # Minimum version we support.
|
||||||
|
BUF_MODULE: ${{ vars.BUF_MODULE }}
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
make build
|
||||||
|
- name: Generate
|
||||||
|
run: |
|
||||||
|
make generate
|
||||||
|
- name: Check diff
|
||||||
|
run: |
|
||||||
|
make checkgenerate
|
||||||
|
test-setup:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- run: buf --version | grep $BUF_VERSION
|
||||||
|
test-setup-latest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test-setup
|
||||||
|
env:
|
||||||
|
BUF_VERSION: "" # Use the latest version
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- run: buf --version
|
||||||
|
test-setup-macos:
|
||||||
|
needs: test-setup
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- run: buf --version | grep $BUF_VERSION
|
||||||
|
test-setup-windows:
|
||||||
|
needs: test-setup
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- run: buf --version | Select-String -Pattern $env:BUF_VERSION -CaseSensitive -SimpleMatch
|
||||||
|
test-setup-self-hosted:
|
||||||
|
needs: test-setup
|
||||||
|
# Disable this job, only for testing.
|
||||||
|
if: false
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- run: buf --version | grep $BUF_VERSION
|
||||||
|
test-empty-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: false
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
continue-on-error: true # build fails
|
||||||
|
test-lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
mkdir -p proto/test/v1
|
||||||
|
printf 'version: v2\nmodules:\n - path: proto' > buf.yaml
|
||||||
|
printf 'syntax = "proto3"; package test.v1; message A { string a = 1; }' > proto/test/v1/test.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
lint: true
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: false
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
test-format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
mkdir -p proto
|
||||||
|
printf 'version: v2\nmodules:\n - path: proto' > buf.yaml
|
||||||
|
printf 'syntax = "proto3";\n\nmessage A {\n string a = 1;\n}\n' > proto/test.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
lint: false
|
||||||
|
format: true
|
||||||
|
breaking: false
|
||||||
|
push: false
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
test-breaking:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
mkdir -p proto
|
||||||
|
printf 'version: v2\nmodules:\n - path: proto' > buf.yaml
|
||||||
|
printf 'syntax = "proto3"; message A { string a = 1; }' > proto/test.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: true
|
||||||
|
breaking_against: .
|
||||||
|
push: false
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
test-comment:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Only run on pull requests from the same repository, comments from forks are not tested. Dependabot is skipped.
|
||||||
|
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]'
|
||||||
|
needs:
|
||||||
|
- test-lint
|
||||||
|
- test-format
|
||||||
|
- test-breaking
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
# Lint skipped, format fails, breaking succeeds
|
||||||
|
mkdir -p proto
|
||||||
|
printf 'version: v2\nmodules:\n - path: proto' > buf.yaml
|
||||||
|
printf 'syntax = "proto3"; message A { string a = 1; }' > proto/test.proto
|
||||||
|
- uses: ./
|
||||||
|
continue-on-error: true # format fails
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false # lint skipped
|
||||||
|
format: true
|
||||||
|
breaking: true
|
||||||
|
breaking_against: .
|
||||||
|
push: false
|
||||||
|
archive: false
|
||||||
|
pr_comment: true
|
||||||
|
- uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const expects = [
|
||||||
|
"The latest Buf updates on your PR.",
|
||||||
|
"✅ passed",
|
||||||
|
"❌ failed (1)",
|
||||||
|
"⏩ skipped",
|
||||||
|
];
|
||||||
|
const commentTag = "<!-- buf Buf action CI:test-comment -->";
|
||||||
|
const { owner, repo } = context.repo;
|
||||||
|
const prNumber = context.payload.pull_request?.number;
|
||||||
|
if (!prNumber) {
|
||||||
|
core.setFailed('No pull request number found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||||
|
owner: owner,
|
||||||
|
repo: repo,
|
||||||
|
issue_number: prNumber,
|
||||||
|
});
|
||||||
|
const previousComment = comments.find((comment) =>
|
||||||
|
comment.body?.includes(commentTag),
|
||||||
|
);
|
||||||
|
if (!previousComment) {
|
||||||
|
core.setFailed('Missing comment on pull request.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const line of expects) {
|
||||||
|
if (!previousComment.body?.includes(line)) {
|
||||||
|
core.setFailed(`Comment does not include expected line: ${line}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await github.rest.issues.updateComment({
|
||||||
|
owner: owner,
|
||||||
|
repo: repo,
|
||||||
|
comment_id: previousComment.id,
|
||||||
|
body: `${previousComment.body}\nNB: This is a test comment.`,
|
||||||
|
});
|
||||||
|
console.log(await github.graphql(`
|
||||||
|
mutation {
|
||||||
|
minimizeComment(input:{subjectId:"${previousComment.node_id}",classifier:RESOLVED}) {
|
||||||
|
clientMutationId
|
||||||
|
minimizedComment {
|
||||||
|
isMinimized
|
||||||
|
minimizedReason
|
||||||
|
viewerCanMinimize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`))
|
||||||
|
core.info('Updated comment.');
|
||||||
|
test-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'push' }} # --git-metadata is only available on push
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
mkdir -p proto/foo/v1
|
||||||
|
printf "version: v2\nmodules:\n - path: proto\n name: ${BUF_MODULE}\n" > buf.yaml
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > proto/foo/v1/bar.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: true
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
test-push-token-only:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'push' }} # --git-metadata is only available on push
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
mkdir -p proto/foo/v1
|
||||||
|
printf "version: v2\nmodules:\n - path: proto\n name: ${BUF_MODULE}\n" > buf.yaml
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > proto/foo/v1/bar.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: true
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
test-push-unnamed:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'push' }} # --git-metadata is only available on push
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# Test v2 buf.yaml config.
|
||||||
|
- run: |
|
||||||
|
mkdir -p proto/foo/v1
|
||||||
|
printf "version: v2\nmodules:\n - path: proto\n" > buf.yaml
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > proto/foo/v1/bar.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: true
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
# Test v1 buf.yaml config.
|
||||||
|
- run: |
|
||||||
|
rm -rf proto
|
||||||
|
mkdir -p proto/foo/v1
|
||||||
|
printf "version: v1\n" > proto/buf.yaml
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > proto/foo/v1/bar.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: true
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
input: proto
|
||||||
|
# Test v2 with multiple modules, one without a name.
|
||||||
|
- run: |
|
||||||
|
mkdir -p foo/foo/v1 bar/bar/v1
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > foo/foo/v1/bar.proto
|
||||||
|
printf "syntax = \"proto3\";\npackage bar.v1;\nmessage Foo {}\n" > bar/bar/v1/foo.proto
|
||||||
|
printf "version: v2\nmodules:\n - path: foo\n name: ${BUF_MODULE}\n - path: bar\n" > buf.yaml
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: true
|
||||||
|
archive: false
|
||||||
|
pr_comment: false
|
||||||
|
test-archive:
|
||||||
|
if: github.event_name == 'push' && github.ref_name != github.event.repository.default_branch
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- test-push
|
||||||
|
- test-push-token-only
|
||||||
|
- test-push-unnamed
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
mkdir -p proto/foo/v1
|
||||||
|
printf "version: v2\nmodules:\n - path: proto\n name: ${BUF_MODULE}\n" > buf.yaml
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > proto/foo/v1/bar.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: false
|
||||||
|
archive: true
|
||||||
|
pr_comment: false
|
||||||
|
test-archive-unnamed:
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- test-push
|
||||||
|
- test-push-token-only
|
||||||
|
- test-push-unnamed
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
mkdir -p proto/foo/v1
|
||||||
|
printf "version: v2\nmodules:\n - path: proto\n" > buf.yaml
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > proto/foo/v1/bar.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: false
|
||||||
|
archive: true
|
||||||
|
pr_comment: false
|
||||||
|
- run: |
|
||||||
|
rm -rf proto
|
||||||
|
mkdir -p proto/foo/v1
|
||||||
|
printf "version: v1\n" > proto/buf.yaml
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > proto/foo/v1/bar.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: false
|
||||||
|
archive: true
|
||||||
|
pr_comment: false
|
||||||
|
input: proto
|
||||||
|
test-archive-root:
|
||||||
|
# Check the archive command parses non-module names.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- test-archive
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: |
|
||||||
|
mkdir -p foo/v1
|
||||||
|
printf "version: v2\nname: ${BUF_MODULE}\n" > buf.yaml
|
||||||
|
printf "syntax = \"proto3\";\npackage foo.v1;\nmessage Bar {}\n" > foo/v1/bar.proto
|
||||||
|
- uses: ./
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: false
|
||||||
|
format: false
|
||||||
|
breaking: false
|
||||||
|
push: true # Push to re-create.
|
||||||
|
archive: true # Archive the pushed module.
|
||||||
|
comment: false
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
name: Dependabot Rebuild
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: read
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
- name: rebuild
|
||||||
|
run: |
|
||||||
|
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
|
||||||
|
git config user.name github-actions
|
||||||
|
git config user.email github-actions@github.com
|
||||||
|
make build
|
||||||
|
git add -A
|
||||||
|
git commit -m "[dependabot skip] build" || true
|
||||||
|
git push -u origin HEAD:${{ github.event.pull_request.head.ref }}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
name: Bypass review in case of emergency
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- labeled
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
jobs:
|
||||||
|
approve:
|
||||||
|
if: github.event.label.name == 'Emergency Bypass Review'
|
||||||
|
uses: bufbuild/base-workflows/.github/workflows/emergency-review-bypass.yaml@main
|
||||||
|
secrets: inherit
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
name: PR Approval Bypass Notifier
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- closed
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
jobs:
|
||||||
|
approval:
|
||||||
|
uses: bufbuild/base-workflows/.github/workflows/notify-approval-bypass.yaml@main
|
||||||
|
secrets: inherit
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
name: Lint PR Title
|
||||||
|
# Prevent writing to the repository using the CI token.
|
||||||
|
# Ref: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#permissions
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
# By default, a workflow only runs when a pull_request's activity type is opened,
|
||||||
|
# synchronize, or reopened. We explicity override here so that PR titles are
|
||||||
|
# re-linted when the PR text content is edited.
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
uses: bufbuild/base-workflows/.github/workflows/pr-title.yaml@main
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/.idea/
|
||||||
|
/.tmp/
|
||||||
|
/.vscode/
|
||||||
|
/node_modules/
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Ignore artifacts
|
||||||
|
dist
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
# Migration Guide
|
||||||
|
|
||||||
|
This guide explains what changed when migrating from the individual Buf actions
|
||||||
|
([buf-setup-action][buf-setup], [buf-breaking-action][buf-breaking],
|
||||||
|
[buf-lint-action][buf-lint], [buf-push-action][buf-push]) to the new
|
||||||
|
consolidated `buf-action`.
|
||||||
|
|
||||||
|
## [buf-setup-action][buf-setup]
|
||||||
|
|
||||||
|
To migrate from `buf-setup-action` to `buf-action`, refer to the table below for the necessary changes:
|
||||||
|
|
||||||
|
| Old Parameter | New Parameter | Description |
|
||||||
|
|:--------------------|:--------------------|:--------------------|
|
||||||
|
| `version` | `version` | Unchanged. Version of `buf` CLI to install. |
|
||||||
|
| `buf_user` | N/A | Removed. The username is not required for login. |
|
||||||
|
| `buf_api_token` | `token` | Renamed. Buf API token for BSR requests. |
|
||||||
|
| `buf_domain` | `domain` | Renamed. Buf domain for BSR requests. Enterprise only. |
|
||||||
|
| N/A | `setup_only` | Added. Installs `buf` and optionally login to the BSR but no additional commands will be run. Set to `true` to make `buf-action` behave like `buf-setup-action`. Defaults to `false`. For more details, refer to the setup-only example in the [README](./README.md#setup-only) |
|
||||||
|
| N/A | `github_action` | Added. Github actor for API requests. Defaults to the current actor in the Github context. |
|
||||||
|
| `github_token` | `github_token` | Unchanged. GitHub token for API requests. Defaults to token in the Github Context. |
|
||||||
|
|
||||||
|
Example migration:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- - uses: bufbuild/buf-setup-action@v1.45.0
|
||||||
|
+ - uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
- buf_user: ${{ secrets.buf_user }}
|
||||||
|
- buf_api_token: ${{ secrets.buf_api_token }}
|
||||||
|
+ version: '1.45.0' # Optional. Defaults to the latest release.
|
||||||
|
+ token: ${{ secrets.buf_api_token }}
|
||||||
|
+ setup_only: true
|
||||||
|
- run: buf --version
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The `buf` CLI version is no longer dependent on the action version.
|
||||||
|
|
||||||
|
## [buf-breaking-action][buf-breaking]
|
||||||
|
|
||||||
|
To migrate from `buf-breaking-action` to `buf-action`, refer to the table below for the necessary changes:
|
||||||
|
|
||||||
|
| Old Parameter | New Parameter | Description |
|
||||||
|
|:--------------------|:--------------------|:-----------------------------------------------------------------|
|
||||||
|
| `input` | `input` | Unchanged. The input directory to build. Defaults to the current directory. See the [README](./README.md#specify-the-input-directory) for more details on specifying inputs. |
|
||||||
|
| `against` | `breaking_against` | Renamed. Specifies the reference to check breaking changes against. We recommend not setting this parameter and instead relying on the default behavior. |
|
||||||
|
| `buf_input_https_username`| N/A | Removed. The username is not required for login. |
|
||||||
|
| `buf_input_https_password`| N/A | Removed. To support multiple registries use the mulitple format of `BUF_TOKEN` as an enviornemt variable. See the [buf token formats documentation][buf-token-formats] for more details. |
|
||||||
|
| `buf_token` | `token` | Renamed. Buf API token for BSR requests. |
|
||||||
|
|
||||||
|
Example migration:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- - uses: bufbuild/buf-setup-action@v1
|
||||||
|
- - uses: bufbuild/buf-breaking-action@v1
|
||||||
|
+ - uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
- buf_token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
+ token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
- against: 'https://github.com/acme/weather.git#branch=main'
|
||||||
|
+ # This example shows how to maintain behavior when migrating, but we recommend unsetting this parameter and relying on the default behavior instead.
|
||||||
|
+ breaking_against: 'https://github.com/acme/weather.git#branch=main'
|
||||||
|
```
|
||||||
|
|
||||||
|
## [buf-lint-action][buf-lint]
|
||||||
|
|
||||||
|
To migrate from `buf-lint-action` to `buf-action`, refer to the table below for the necessary changes.
|
||||||
|
|
||||||
|
| Old Parameter | New Parameter | Description |
|
||||||
|
|:--------------------|:--------------------|:-----------------------------------------------------------------|
|
||||||
|
| `input` | `input` | Unchanged. The input directory to lint. Defaults to the current directory. See the [README](./README.md#specify-the-input-directory) for more details on specifying inputs. |
|
||||||
|
| `buf_token` | `token` | Renamed. Buf API token for BSR requests. |
|
||||||
|
|
||||||
|
Example migration:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- - uses: bufbuild/buf-setup-action@v1
|
||||||
|
- - uses: bufbuild/buf-lint-action@v1
|
||||||
|
+ - uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
input: proto
|
||||||
|
- buf_token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
+ token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## [buf-push-action][buf-push]
|
||||||
|
|
||||||
|
To migrate from `buf-push-action` to `buf-action`, refer to the table below for the necessary changes:
|
||||||
|
|
||||||
|
| Old Parameter | New Parameter | Description |
|
||||||
|
|:--------------------|:--------------------|:-----------------------------------------------------------------|
|
||||||
|
| `input` | `input` | Unchanged. The input directory to push. Defaults to the current directory. See the [README](./README.md#specify-the-input-directory) for more details on specifying inputs. |
|
||||||
|
| `buf_token` | `token` | Renamed. Buf API token for BSR requests. |
|
||||||
|
| `draft` | N/A | Removed. Drafts are deprecated in favor of labels. Labels are set using [git metadata][git-metadata]. |
|
||||||
|
| `create_visibility` | N/A | Removed. Repositories will always be created with `private` visibility if they do not exist. |
|
||||||
|
| N/A | `push_disable_create`| Added. Disables automatic creation of repositories. Defaults to `false`. |
|
||||||
|
|
||||||
|
Example migration:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- - uses: bufbuild/buf-setup-action@v1
|
||||||
|
- - uses: bufbuild/buf-push-action@v1
|
||||||
|
+ - uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
- buf_token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
+ token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
- create_visibility: private
|
||||||
|
- draft: ${{ github.ref_name != 'main'}}
|
||||||
|
```
|
||||||
|
|
||||||
|
To further customize the push action call the `buf push` command directly.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
+ setup_only: true # Disables default behavior.
|
||||||
|
+ - if: ${{ github.event_name == 'push' }}
|
||||||
|
+ run: buf push --error-format github-actions --create --git-metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Labels are a new feature in the `buf` CLI. For more information, see the [commits and labels][commits-labels] documentation.
|
||||||
|
|
||||||
|
[buf]: https://buf.build
|
||||||
|
[buf-setup]: https://github.com/marketplace/actions/buf-setup
|
||||||
|
[buf-breaking]: https://github.com/marketplace/actions/buf-breaking
|
||||||
|
[buf-cli]: https://github.com/bufbuild/buf
|
||||||
|
[buf-lint]: https://github.com/marketplace/actions/buf-lint
|
||||||
|
[buf-push]: https://github.com/marketplace/actions/buf-push
|
||||||
|
[git-metadata]: https://buf.build/docs/reference/cli/buf/push/?h=git+metadata#git-metadata
|
||||||
|
[buf-token-formats]: https://buf.build/docs/bsr/authentication/#buf_token-formats
|
||||||
|
[commits-labels]: https://buf.build/docs/concepts/commits-labels/
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# See https://tech.davis-hansson.com/p/make/
|
||||||
|
SHELL := bash
|
||||||
|
.DELETE_ON_ERROR:
|
||||||
|
.SHELLFLAGS := -eu -o pipefail -c
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
MAKEFLAGS += --warn-undefined-variables
|
||||||
|
MAKEFLAGS += --no-builtin-rules
|
||||||
|
MAKEFLAGS += --no-print-directory
|
||||||
|
BUF_VERSION ?= 1.50.1
|
||||||
|
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
SED_I := sed -E -i ''
|
||||||
|
else
|
||||||
|
SED_I := sed -E -i
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: help
|
||||||
|
help: ## Describe useful make targets
|
||||||
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-30s %s\n", $$1, $$2}'
|
||||||
|
|
||||||
|
PHONY: all
|
||||||
|
all: ## Format, Lint and build (default)
|
||||||
|
$(MAKE) build
|
||||||
|
|
||||||
|
.PHONY: format
|
||||||
|
format: node_modules
|
||||||
|
npm run format
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: node_modules
|
||||||
|
npm run lint
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: node_modules format lint
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
.PHONY: updateversion
|
||||||
|
updateversion:
|
||||||
|
ifndef VERSION
|
||||||
|
$(error "VERSION must be set")
|
||||||
|
endif
|
||||||
|
ifndef VERSION_SHORT
|
||||||
|
$(error "VERSION_SHORT must be set")
|
||||||
|
endif
|
||||||
|
$(SED_I) "s/version: [0-9]+\.[0-9]+\.[0-9]+/version: $(BUF_VERSION)/g" action.yml README.md examples/*.yaml examples/*/*.yaml
|
||||||
|
$(SED_I) "s/buf-action@v[0-9]+(\.[0-9]+)?(\.[0-9]+)?/buf-action@v$(VERSION_SHORT)/g" README.md examples/*.yaml examples/*/*.yaml
|
||||||
|
$(SED_I) "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+\"/\"version\": \"$(VERSION)\"/g" package.json
|
||||||
|
npm prune
|
||||||
|
|
||||||
|
.PHONY: generate
|
||||||
|
generate: node_modules ## Regenerate licenses
|
||||||
|
npm run generate
|
||||||
|
|
||||||
|
.PHONY: checkgenerate
|
||||||
|
checkgenerate:
|
||||||
|
@# Used in CI to verify that `make generate` doesn't produce a diff.
|
||||||
|
test -z "$$(git status --porcelain | tee /dev/stderr)"
|
||||||
|
|
||||||
|
node_modules: package-lock.json
|
||||||
|
npm ci
|
||||||
@@ -0,0 +1,320 @@
|
|||||||
|

|
||||||
|
|
||||||
|
# buf-action
|
||||||
|
|
||||||
|
[][ci]
|
||||||
|
[][slack]
|
||||||
|
|
||||||
|
This GitHub action makes it easy to run [`buf`][buf] within a workflow to check for
|
||||||
|
[build](https://buf.build/docs/reference/cli/buf/build),
|
||||||
|
[lint](https://buf.build/docs/lint/overview),
|
||||||
|
[format](https://buf.build/docs/format/style),
|
||||||
|
and [breaking change](https://buf.build/docs/breaking/overview) errors,
|
||||||
|
as well as to automatically [publish schema changes](https://buf.build/docs/bsr/module/publish) to the [Buf Schema Registry (BSR)](https://buf.build/product/bsr).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use this action with the recommended default behavior, create a new `.github/workflows/buf-ci.yaml` file in your repository with the following content:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default behavior
|
||||||
|
|
||||||
|
When you push a Git commit, tag, or branch to GitHub, the action will [push named modules to the BSR](https://buf.build/docs/bsr/module/publish) using `buf push`.
|
||||||
|
|
||||||
|
On a pull request, the action will run all checks (using `buf build`, `buf lint`, `buf format`, `buf breaking`) and then post a [summary comment](#summary-comment) on the PR.
|
||||||
|
|
||||||
|
When you delete a Git branch or tag, the action will archive the corresponding label on the BSR.
|
||||||
|
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
To customize the behavior of the action, you can set the following parameters in the workflow file.
|
||||||
|
Add these parameters under the `with` section of the `uses` step in the workflow file.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|:--------------------------------|:---------------------------------------------------|:-------------------|
|
||||||
|
| `version` | Version of the `buf` CLI to use. | Latest [version][buf-releases] |
|
||||||
|
| `token` | API token for [logging into the BSR](https://buf.build/docs/bsr/authentication). | |
|
||||||
|
| `domain` | Domain for logging into the BSR, enterprise only.| `buf.build` |
|
||||||
|
| `input` | [Input](https://buf.build/docs/reference/inputs) for the `buf` command. | |
|
||||||
|
| `paths` | Limit to specific files or directories (separated by newlines). | |
|
||||||
|
| `exclude_imports` | Exclude files imported by the target modules. | False |
|
||||||
|
| `exclude_paths` | Exclude specific files or directories, e.g. "proto/a/a.proto", "proto/a" (separated by newlines). | |
|
||||||
|
| `pr_comment` | Comment the results on the pull request. The workflow and job name combination must be unique. | Only on pull requests (non forks) |
|
||||||
|
| `format` | Whether to run the formatting step. | Runs on pushes to Git PR |
|
||||||
|
| `lint` | Whether to run the linting step. | Runs on pushes to Git PR |
|
||||||
|
| `breaking` | Whether to run the breaking change detection step. | Runs on pushes to Git PR |
|
||||||
|
| `breaking_against` | [Input](https://buf.build/docs/reference/inputs) to compare against. | Base of the PR or the commit before the event |
|
||||||
|
| `push` | Whether to run the push step. | Runs on Git pushes (non forks) |
|
||||||
|
| `push_disable_create` | Disables repository creation if it does not exist. | False |
|
||||||
|
| `archive` | Whether to run the archive step. | Runs on Git deletes (non forks) |
|
||||||
|
| `setup_only` | Setup only the `buf` environment, optionally logging into the BSR, but without executing other commands. | |
|
||||||
|
| `github_actor` | GitHub actor for API requests. | Actor from GitHub context |
|
||||||
|
| `github_token` | GitHub token for API requests. Ensures requests aren't rate limited | Token from GitHub context |
|
||||||
|
| `public_github_token` | GitHub token for github.com. Must be set when running on a private GitHub Enterprise instance to authenticate requests, otherwise ignored. | |
|
||||||
|
|
||||||
|
|
||||||
|
### Skip the breaking change detection step
|
||||||
|
|
||||||
|
By default, the action runs the breaking change detection step on every pull request.
|
||||||
|
To skip this step, add the label `buf skip breaking` to the PR.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Ensure the workflow file includes the `pull_request` event types `labeled` and `unlabeled` so checks re-run on label changes.
|
||||||
|
|
||||||
|
To disable the ability to skip breaking change checks via a label, set the `breaking` parameter to the value `${{ github.event_name == 'pull_request' }}` so it runs on all PRs.
|
||||||
|
See [examples/disable-skip/buf-ci.yaml](examples/disable-skip/buf-ci.yaml) for an example.
|
||||||
|
|
||||||
|
### Disable steps
|
||||||
|
|
||||||
|
To disable parts of the workflow, each step corresponds to a boolean flag in the parameters.
|
||||||
|
For example to disable formatting set the parameter `format` to `false`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
format: false
|
||||||
|
```
|
||||||
|
|
||||||
|
See [action.yml](action.yml) for all available parameters.
|
||||||
|
|
||||||
|
### Versioning
|
||||||
|
|
||||||
|
For reproducible builds, you can pin to an explicit version of `buf` by setting `version`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
version: 1.50.1
|
||||||
|
```
|
||||||
|
|
||||||
|
If no version is specified in the workflow config, the action will resolve the version in order of precedence:
|
||||||
|
- A version specified in the environment variable `${BUF_VERSION}`.
|
||||||
|
- The version of `buf` that is already installed on the runner (if it exists).
|
||||||
|
- The latest version of the `buf` binary from the official releases on GitHub.
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
[Publishing schemas](https://buf.build/docs/bsr/module/publish) to the BSR provides a seamless
|
||||||
|
way for consumers of your APIs to generate code.
|
||||||
|
Authenticating with the BSR is required for both the push and archive label steps.
|
||||||
|
|
||||||
|
Generate a token from the [BSR UI](https://buf.build/docs/bsr/authentication#create-an-api-token) and add it to the [repository secrets](https://docs.github.com/en/actions/reference/encrypted-secrets).
|
||||||
|
To authenticate with the BSR, set the API token as the parameter `token`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
A post-action step will logout from the BSR to ensure the token is not stored in the `.netrc` file.
|
||||||
|
For more information on authentication, see the [BSR Authentication Reference](https://buf.build/docs/bsr/authentication).
|
||||||
|
|
||||||
|
### Summary comment
|
||||||
|
|
||||||
|
The action reports the status of the most recent checks in a comment on each pull request.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
To disable the comment, set the parameter `comment` to `false` and remove the permission `pull_request: write` as this is no longer required.
|
||||||
|
|
||||||
|
```diff
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
- pull-requests: write
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
+ pr_comment: false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Specify the input directory
|
||||||
|
|
||||||
|
To run the action for inputs not declared at the root of the repository,
|
||||||
|
set the parameter `input` to the directory of your `buf.yaml` file.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
input: <path/to/module>
|
||||||
|
```
|
||||||
|
|
||||||
|
Breaking change detection by default will use the `input` value as a subdirectory for the breaking against value.
|
||||||
|
To customize this behavior, set the parameter `breaking_against` to the desired input.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
input: <path/to/module>
|
||||||
|
breaking_against: ${{ github.event.repository.clone_url }}#format=git,commit=${{ github.event.pull_request.base.sha }},subdir=<path/to/module>
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can checkout the base for the breaking comparison to a local folder
|
||||||
|
and then set the value of `breaking_against` to the path of the base.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: head
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: base
|
||||||
|
ref: ${{ github.event.pull_request.base.sha }}
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
input: head/<path/to/module>
|
||||||
|
breaking_against: base/<path/to/module>
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information on inputs, see the [Buf Inputs Reference](https://buf.build/docs/reference/inputs).
|
||||||
|
|
||||||
|
### Setup only
|
||||||
|
|
||||||
|
To only setup the action without running any commands, set the parameter `setup_only` to `true`.
|
||||||
|
This will install `buf` and optionally login to the schema registry but no additional commands will be run.
|
||||||
|
Subsequent steps will have `buf` available in their $PATH and can invoke `buf` directly.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- run: buf build --error-format github-actions
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [only-setup.yaml](examples/only-setup/buf-ci.yaml) example.
|
||||||
|
|
||||||
|
### Customize when steps run
|
||||||
|
|
||||||
|
To trigger steps on different events use the GitHub action context to deduce the event type.
|
||||||
|
For example to enable formatting checks on both pull requests and push create an expression for the parameter `format`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
format: ${{ contains(fromJSON('["push", "pull_request"]'), github.event_name) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
See [GitHub Actions expressions](https://docs.github.com/en/actions/learn-github-actions/expressions) documentation.
|
||||||
|
|
||||||
|
### Skip checks if commit message matches a specific pattern
|
||||||
|
|
||||||
|
To conditionally run checks based on user input, use the GitHub action context to check for the contents of the commit.
|
||||||
|
For example to disable breaking change detection on commits, create an expression on the parameter `breaking` to check the contents of the commit message:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
breaking: |
|
||||||
|
contains(fromJSON('["push", "pull_request"]'), github.event_name) &&
|
||||||
|
!contains(github.event.head_commit.message, 'buf skip breaking')
|
||||||
|
```
|
||||||
|
|
||||||
|
See [GitHub Actions job context](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#job-context) documentation.
|
||||||
|
|
||||||
|
### Only push on changes to APIs
|
||||||
|
|
||||||
|
To push only on changes to when your module changes, restrict the push step for any changes to `buf` related files.
|
||||||
|
This can be achieved by using the `paths` filter on the `push` event.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '**.proto'
|
||||||
|
- '**/buf.yaml'
|
||||||
|
- '**/buf.lock'
|
||||||
|
- '**/buf.md'
|
||||||
|
- '**/README.md'
|
||||||
|
- '**/LICENSE'
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [push-on-changes.yaml](examples/push-on-changes/buf-ci.yaml) example.
|
||||||
|
|
||||||
|
### Example workflows
|
||||||
|
|
||||||
|
Check out the [examples](examples) directory for more detailed workflows.
|
||||||
|
|
||||||
|
## Migrating from individual Buf actions
|
||||||
|
|
||||||
|
If you're currently using any of our individual actions
|
||||||
|
([buf-setup-action][buf-setup], [buf-breaking-action][buf-breaking], [buf-lint-action][buf-lint], [buf-push-action][buf-push]),
|
||||||
|
we recommend migrating to this consolidated action that has additional capabilities. Benefits to migrating include:
|
||||||
|
- Less configuration and setup, with built-in best practices.
|
||||||
|
- Enhanced integration with Git data when pushing to the BSR.
|
||||||
|
- Status comments on pull requests.
|
||||||
|
- Easy configuration for custom behavior.
|
||||||
|
|
||||||
|
See the [migration guide](MIGRATION.md) for more information.
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
To debug the action, rerun the workflow with debug logging enabled.
|
||||||
|
This will run all buf commands with the `--debug` flag.
|
||||||
|
See the [re-run jobs with debug logging](https://github.blog/changelog/2022-05-24-github-actions-re-run-jobs-with-debug-logging/) for more information.
|
||||||
|
|
||||||
|
## Feedback and support
|
||||||
|
|
||||||
|
If you have any feedback or need support, please reach out to us on the [Buf Slack][slack],
|
||||||
|
or [GitHub Issues](https://github.com/bufbuild/buf-action/issues).
|
||||||
|
|
||||||
|
## Status: stable
|
||||||
|
|
||||||
|
This action is stable and ready for production use.
|
||||||
|
|
||||||
|
## Legal
|
||||||
|
|
||||||
|
Offered under the [Apache 2 license][license].
|
||||||
|
|
||||||
|
[buf]: https://buf.build
|
||||||
|
[buf-setup]: https://github.com/marketplace/actions/buf-setup
|
||||||
|
[buf-breaking]: https://github.com/marketplace/actions/buf-breaking
|
||||||
|
[buf-cli]: https://github.com/bufbuild/buf
|
||||||
|
[buf-lint]: https://github.com/marketplace/actions/buf-lint
|
||||||
|
[buf-push]: https://github.com/marketplace/actions/buf-push
|
||||||
|
[buf-releases]: https://github.com/bufbuild/buf/releases
|
||||||
|
[ci]: https://github.com/bufbuild/buf-action/actions/workflows/ci.yaml
|
||||||
|
[license]: https://github.com/bufbuild/bufisk/blob/main/LICENSE
|
||||||
|
[slack]: https://buf.build/links/slack
|
||||||
|
[bsr]: https://buf.build/docs/introduction
|
||||||
|
[push-event]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
|
||||||
|
[pull-request-event]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
||||||
|
[delete-event]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#delete
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
# Releasing buf-action
|
||||||
|
|
||||||
|
This document outlines how to create a release of buf-action.
|
||||||
|
We follow the best practices outlined in the [GitHub Actions documentation][github-release-docs] and [GitHub Actions Toolkit tutorial][github-release-tutorial].
|
||||||
|
|
||||||
|
1. Clone the repo, ensuring you have the latest main.
|
||||||
|
|
||||||
|
2. On a new branch, run the following commands to update the version of the action:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VERSION=X.Y.Z VERSION_SHORT=X make updateversion
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Open a PR titled "Prepare for vX.Y.Z". Once it's reviewed and CI passes, merge it.
|
||||||
|
|
||||||
|
*Make sure no new commits are merged until the release is complete.*
|
||||||
|
|
||||||
|
4. Create a new release using the Github UI:
|
||||||
|
- Under “Choose a tag”, type in “vX.Y.Z” to create a new tag for the release upon publish.
|
||||||
|
- Target the main branch.
|
||||||
|
- Title the Release “vX.Y.Z”.
|
||||||
|
- Click “set as latest release”.
|
||||||
|
- Click "Publish this Action to the GitHub Marketplace".
|
||||||
|
|
||||||
|
|
||||||
|
5. Publish the release.
|
||||||
|
|
||||||
|
6. Pull down the latest main and move the tags for major (vX) and minor (vX.Y) releases.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag -fa vX -m "Update vX tag"
|
||||||
|
git tag -fa vX.Y -m "Update vX.Y tag"
|
||||||
|
git push origin vX vX.Y --force
|
||||||
|
```
|
||||||
|
|
||||||
|
[github-release-docs]: https://docs.github.com/en/actions/creating-actions/releasing-and-maintaining-actions
|
||||||
|
[github-release-tutorial]: https://github.com/actions/toolkit/blob/master/docs/action-versioning.md
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
# Copyright 2024 Buf Technologies, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
name: "Buf Action"
|
||||||
|
author: "Buf"
|
||||||
|
description: |-
|
||||||
|
Run build, lint, format, and breaking change checks on Protobuf files, and publish schemas to the Buf Schema Registry.
|
||||||
|
|
||||||
|
branding:
|
||||||
|
icon: play
|
||||||
|
color: blue
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: |-
|
||||||
|
Version of the Buf CLI to use.
|
||||||
|
Example:
|
||||||
|
with:
|
||||||
|
version: 1.50.1
|
||||||
|
required: false
|
||||||
|
token:
|
||||||
|
description: |-
|
||||||
|
API token for logging into the BSR.
|
||||||
|
required: false
|
||||||
|
domain:
|
||||||
|
description: |-
|
||||||
|
Domain for logging into the BSR, enterprise only.
|
||||||
|
required: false
|
||||||
|
default: "buf.build"
|
||||||
|
github_actor:
|
||||||
|
description: |-
|
||||||
|
GitHub actor for API requests.
|
||||||
|
required: false
|
||||||
|
default: ${{ github.actor }}
|
||||||
|
github_token:
|
||||||
|
description: |-
|
||||||
|
GitHub token for the GitHub instance this action is running on.
|
||||||
|
required: false
|
||||||
|
default: ${{ github.token }}
|
||||||
|
public_github_token:
|
||||||
|
description: |-
|
||||||
|
GitHub token for github.com. Must be set when running on a private GitHub Enterprise instance to authenticate requests, otherwise ignored.
|
||||||
|
setup_only:
|
||||||
|
description: |-
|
||||||
|
Setup only the buf environment, optionally logging into the BSR, but without executing other commands.
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
pr_comment:
|
||||||
|
description: |-
|
||||||
|
Comment on the pull request with the results of each step. The workflow and job name combination must be unique.
|
||||||
|
Only runs on pull requests, for non forked repositories.
|
||||||
|
required: false
|
||||||
|
default: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }}
|
||||||
|
|
||||||
|
input:
|
||||||
|
description: |-
|
||||||
|
Input for the buf command.
|
||||||
|
required: false
|
||||||
|
paths:
|
||||||
|
description: |-
|
||||||
|
Limit to specific files or directories (separated by newlines).
|
||||||
|
Example:
|
||||||
|
with:
|
||||||
|
paths: |
|
||||||
|
proto/a/a.proto
|
||||||
|
proto/a
|
||||||
|
required: false
|
||||||
|
exclude_paths:
|
||||||
|
description: |-
|
||||||
|
Exclude specific files or directories (separated by newlines).
|
||||||
|
Example:
|
||||||
|
with:
|
||||||
|
exclude_paths: |
|
||||||
|
proto/a/a.proto
|
||||||
|
proto/a
|
||||||
|
required: false
|
||||||
|
exclude_imports:
|
||||||
|
description: |-
|
||||||
|
Exclude files imported by the target modules.
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
|
||||||
|
lint:
|
||||||
|
description: |-
|
||||||
|
Whether to run the linting step.
|
||||||
|
Runs by default on pull requests.
|
||||||
|
required: false
|
||||||
|
default: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
format:
|
||||||
|
description: |-
|
||||||
|
Whether to run the formatting step.
|
||||||
|
Runs by default on pull requests.
|
||||||
|
required: false
|
||||||
|
default: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
breaking:
|
||||||
|
description: |-
|
||||||
|
Whether to run the breaking change detection step.
|
||||||
|
Runs by default on pull requests if the "buf skip breaking" label is not applied.
|
||||||
|
required: false
|
||||||
|
default: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'buf skip breaking') }}
|
||||||
|
breaking_against:
|
||||||
|
description: |-
|
||||||
|
Input to compare against for breaking change detection.
|
||||||
|
Defaults to the base branch of the pull request or the commit before the push.
|
||||||
|
required: false
|
||||||
|
|
||||||
|
push:
|
||||||
|
description: |-
|
||||||
|
Whether to run the push step. Runs by default on pushes, for non forked repositories.
|
||||||
|
required: false
|
||||||
|
default: ${{ github.event_name == 'push' }}
|
||||||
|
push_disable_create:
|
||||||
|
description: |-
|
||||||
|
Disables repository creation if it does not exist. Defaults to false.
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
|
||||||
|
archive:
|
||||||
|
description: |-
|
||||||
|
Whether to run the archive step. Runs by default on deletes, for non forked repositories.
|
||||||
|
required: false
|
||||||
|
default: ${{ github.event_name == 'delete' }}
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
buf_version:
|
||||||
|
description: |-
|
||||||
|
Version of the Buf CLI used.
|
||||||
|
buf_path:
|
||||||
|
description: |-
|
||||||
|
Path to the Buf CLI binary.
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "node20"
|
||||||
|
main: "./dist/index.js"
|
||||||
|
post: "./dist/post/index.js"
|
||||||
Vendored
+48506
File diff suppressed because it is too large.
Load diff
Vendored
+31972
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import globals from "globals";
|
||||||
|
import pluginJs from "@eslint/js";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{ languageOptions: { globals: globals.node } },
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
];
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Examples
|
||||||
|
|
||||||
|
This directory contains examples of how to use the `buf` GitHub Action.
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
- [buf-ci.yaml](./buf-ci.yaml): Recommended configuration for using `buf` in a CI environment.
|
||||||
|
|
||||||
|
- [only-checks/buf-ci.yaml](./only-checks/buf-ci.yaml): How to use `buf` to only run checks on pull requests.
|
||||||
|
- [only-setup/buf-ci.yaml](./only-setup/buf-ci.yaml): How to use `buf` to only install `buf`.
|
||||||
|
- [only-setup-defaults/buf-ci.yaml](./only-setup-defaults/buf-ci.yaml): Showcases the default steps of the `buf` action.
|
||||||
|
- [only-sync/buf-ci.yaml](./only-sync/buf-ci.yaml): How to use `buf` to only sync the repository labels.
|
||||||
|
|
||||||
|
- [push-on-changes/buf-ci.yaml](./push-on-changes/buf-ci.yaml): Pushes changes only on detecting protobuf file changes.
|
||||||
|
|
||||||
|
- [skip-on-commits/buf-ci.yaml](./skip-on-commits/buf-ci.yaml): How to use commit messages to skip `buf` checks on push.
|
||||||
|
- [skip-on-labels/buf-ci.yaml](./skip-on-labels/buf-ci.yaml): How to use custom labels to skip `buf` checks on pull requests.
|
||||||
|
|
||||||
|
- [validate-push/buf-ci.yaml](./validate-push/buf-ci.yaml): Validate checks before pushing to the repository.
|
||||||
|
|
||||||
|
- [version-env/buf-ci.yaml](./version-env/buf-ci.yaml): Resolve the `buf` version from an environment variable (`BUF_VERSION`).
|
||||||
|
- [version-input/buf-ci.yaml](./version-input/buf-ci.yaml): Resolve the `buf` version from an action input (`version`).
|
||||||
|
- [version-latest/buf-ci.yaml](./version-latest/buf-ci.yaml): Use the latest `buf` version.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# This is a GitHub Actions workflow file that uses the buf-action to run Buf CI.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# This workflow runs checks before pushing to the BSR.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: ${{ github.event_name == 'push' }}
|
||||||
|
format: ${{ github.event_name == 'push' }}
|
||||||
|
breaking: ${{ github.event_name == 'push' && !contains(github.event.head_commit.message, 'buf skip breaking') }}
|
||||||
|
# Specify the branch to compare against for breaking change detection.
|
||||||
|
# The default for push is to compare to the commit before. We want to
|
||||||
|
# compare against the main branch for a GitHub flow style workflow.
|
||||||
|
breaking_against: .git#branch=main
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# This workflow disables the use of labels for skipping checks. It does this by
|
||||||
|
# removing the expressions that check for the presence of the 'buf skip' labels.
|
||||||
|
# As a result, the pull_request types for labeled and unlabeled events can now
|
||||||
|
# also be removed (NB: the 'pull_request' default event types include opened,
|
||||||
|
# synchronize, and reopened).
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
breaking: ${{ github.event_name == 'pull_request' }}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# This workflow shows the default configuration for GitHub Enterprise Server.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
# Specify the public GitHub token to authenticate requests to github.com.
|
||||||
|
# This is required when running on a private GitHub Enterprise instance.
|
||||||
|
# Will be used when resolving buf versions and downloading buf binaries.
|
||||||
|
public_github_token: ${{ secrets.PUBLIC_GITHUB_TOKEN }}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# This workflow runs checks on pull_requests only, without push and archive steps.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# This file showcases the Buf CI workflow in a step by step breakdown.
|
||||||
|
# Based on the buf-ci.yaml file, this action walks through the Buf CI workflow
|
||||||
|
# and shows what each step does.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- env:
|
||||||
|
BUF_TOKEN: ${{ secrets.BUF_TOKEN }}
|
||||||
|
run: echo ${BUF_TOKEN} | buf registry login buf.build --token-stdin
|
||||||
|
- run: buf build --error-format=github-actions
|
||||||
|
- if: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'buf skip lint') }}
|
||||||
|
run: buf lint --error-format github-actions
|
||||||
|
- if: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'buf skip format') }}
|
||||||
|
run: buf format --diff --error-format github-actions --exit-code
|
||||||
|
- if: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'buf skip breaking') }}
|
||||||
|
run: |
|
||||||
|
buf breaking --error-format github-actions --against \
|
||||||
|
${{ github.event.repository.clone_url }}#format=git,tag=${{ github.event.pull_request.base.sha || github.event.before }}
|
||||||
|
- if: ${{ github.event_name == 'push' }}
|
||||||
|
run: buf push --error-format github-actions --create --git-metadata
|
||||||
|
- if: ${{ github.event_name == 'delete' }}
|
||||||
|
run: buf beta registry archive --label ${{ github.ref_name }}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# This file is used to setup the Buf CLI in the GitHub Actions environment.
|
||||||
|
# After the buf-action is run, the Buf CLI is available for use in subsequent steps.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
# Add the parameter token as a secret in your repository settings
|
||||||
|
# to authenticate with the Buf Schema Registry.
|
||||||
|
setup_only: true
|
||||||
|
- run: buf build --error-format=github-actions
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# This workflow syncs the changes to the BSR without running any checks.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# This workflow shows syncing changes only when the buf.yaml, README.md, LICENSE
|
||||||
|
# or .proto files are modified.
|
||||||
|
# Caution: Restricting push can cause unintended behavior. Review the paths carefully.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
# Caution: This workflow could miss changes if the paths are not correctly specified.
|
||||||
|
- '**.proto'
|
||||||
|
- '**/buf.yaml'
|
||||||
|
- '**/buf.lock'
|
||||||
|
- '**/buf.md'
|
||||||
|
- '**/README.md'
|
||||||
|
- '**/LICENSE'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# This workflow skips checks when `buf skip` is used in the commit message.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: ${{ github.event_name == 'push' && !contains(github.event.head_commit.message, 'buf skip lint') }}
|
||||||
|
format: ${{ github.event_name == 'push' && !contains(github.event.head_commit.message, 'buf skip format') }}
|
||||||
|
breaking: ${{ github.event_name == 'push' && !contains(github.event.head_commit.message, 'buf skip breaking') }}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# This workflow skips checks when `buf skip` is applied to a PR.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'buf skip lint') }}
|
||||||
|
format: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'buf skip format') }}
|
||||||
|
breaking: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'buf skip breaking') }}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# This workflow runs checks on push events before pushing the changes to the BSR.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
delete:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
# The token paramater is required to authenticate with the Buf Schema Registry.
|
||||||
|
token: ${{ secrets.BUF_TOKEN }}
|
||||||
|
lint: ${{ github.event_name == 'push' }}
|
||||||
|
format: ${{ github.event_name == 'push' }}
|
||||||
|
breaking: ${{ github.event_name == 'push' }}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# This workflow downloads the version of buf specified in the env var $BUF_VERSION.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
env:
|
||||||
|
BUF_VERSION: "1.32.0"
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- run: buf version
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# This workflow downloads the version of buf specified in the action input.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
version: 1.50.1
|
||||||
|
- run: buf version
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# This workflow downloads the latest version of buf as none is specified.
|
||||||
|
name: Buf CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
buf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: bufbuild/buf-action@v1
|
||||||
|
with:
|
||||||
|
setup_only: true
|
||||||
|
- run: buf version
|
||||||
Generated
+1881
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "buf-action",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"description": "GitHub Action for buf",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"format": "prettier --write 'src/*.{json,js,jsx,ts,tsx,css}' --log-level error",
|
||||||
|
"lint": "eslint --max-warnings 0 src",
|
||||||
|
"lint:fix": "eslint --max-warnings 0 --fix src",
|
||||||
|
"build": "ncc build -o dist src/main.ts && ncc build -o dist/post src/post.ts",
|
||||||
|
"generate": "license-header --ignore 'dist/**'"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"actions",
|
||||||
|
"buf",
|
||||||
|
"proto",
|
||||||
|
"protobuf"
|
||||||
|
],
|
||||||
|
"author": "Buf Technologies, Inc.",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"licenseHeader": {
|
||||||
|
"licenseType": "apache",
|
||||||
|
"yearRange": "2024-2025",
|
||||||
|
"copyrightHolder": "Buf Technologies, Inc."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@bufbuild/license-header": "^0.0.4",
|
||||||
|
"@eslint/js": "^9.23.0",
|
||||||
|
"@types/node": "^22.13.11",
|
||||||
|
"@types/semver": "^7.5.8",
|
||||||
|
"@vercel/ncc": "^0.38.3",
|
||||||
|
"eslint": "^9.23.0",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"prettier": "3.5.3",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"typescript-eslint": "^8.27.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.11.1",
|
||||||
|
"@actions/exec": "^1.1.1",
|
||||||
|
"@actions/github": "^6.0.0",
|
||||||
|
"@actions/tool-cache": "^2.0.2",
|
||||||
|
"@buf/bufbuild_registry.bufbuild_es": "^2.2.3-20250116203702-1c024d64352b.1",
|
||||||
|
"@bufbuild/protobuf": "^2.2.5",
|
||||||
|
"@connectrpc/connect": "^2.0.1",
|
||||||
|
"@connectrpc/connect-web": "^2.0.1",
|
||||||
|
"@octokit/webhooks-types": "^7.6.1",
|
||||||
|
"parse-diff": "^0.11.1",
|
||||||
|
"semver": "^7.7.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import { context } from "@actions/github";
|
||||||
|
import { Context } from "@actions/github/lib/context";
|
||||||
|
import { GitHub } from "@actions/github/lib/utils";
|
||||||
|
|
||||||
|
// oldCommentTag is the previous tag used to identify the comment. This is
|
||||||
|
// temporary and will be removed in a future release.
|
||||||
|
const oldCommentTag = "<!-- Buf results -->";
|
||||||
|
|
||||||
|
// commentTag returns the tag used to identify the comment. This is a non-visible
|
||||||
|
// string injected into the comment body. It is unique to the workflow and job.
|
||||||
|
function commentTag(): string {
|
||||||
|
return `<!-- buf ${context.workflow}:${context.job} -->`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// findCommentOnPR finds the comment on the PR that contains the Buf results.
|
||||||
|
// If the comment is found, it returns the comment ID. If the comment is not
|
||||||
|
// found, it returns undefined.
|
||||||
|
export async function findCommentOnPR(
|
||||||
|
context: Context,
|
||||||
|
github: InstanceType<typeof GitHub>,
|
||||||
|
): Promise<number | undefined> {
|
||||||
|
const { owner, repo } = context.repo;
|
||||||
|
const prNumber = context.payload.pull_request?.number;
|
||||||
|
if (!prNumber) {
|
||||||
|
core.info("This is not a PR, skipping finding comment");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||||
|
owner: owner,
|
||||||
|
repo: repo,
|
||||||
|
issue_number: prNumber,
|
||||||
|
});
|
||||||
|
const tag = commentTag();
|
||||||
|
const previousComment = comments.find(
|
||||||
|
(comment) =>
|
||||||
|
// TODO: Remove the old comment tag check in a future release.
|
||||||
|
comment.body?.includes(tag) || comment.body?.includes(oldCommentTag),
|
||||||
|
);
|
||||||
|
if (previousComment) {
|
||||||
|
core.info(`Found previous comment ${previousComment.id}`);
|
||||||
|
return previousComment.id;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// commentOnPR comments on the PR with the summary of the Buf results. The
|
||||||
|
// summary should be a markdown formatted string. This function returns true if
|
||||||
|
// the comment was successfully created or updated.
|
||||||
|
export async function commentOnPR(
|
||||||
|
context: Context,
|
||||||
|
github: InstanceType<typeof GitHub>,
|
||||||
|
commentID: number | undefined,
|
||||||
|
body: string,
|
||||||
|
): Promise<number | undefined> {
|
||||||
|
const { owner, repo } = context.repo;
|
||||||
|
const prNumber = context.payload.pull_request?.number;
|
||||||
|
if (!prNumber) {
|
||||||
|
core.info("This is not a PR, skipping commenting");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const content = {
|
||||||
|
owner: owner,
|
||||||
|
repo: repo,
|
||||||
|
body: body + commentTag(),
|
||||||
|
};
|
||||||
|
if (commentID) {
|
||||||
|
await github.rest.issues.updateComment({
|
||||||
|
...content,
|
||||||
|
comment_id: commentID,
|
||||||
|
});
|
||||||
|
core.info(`Updated comment ${commentID} on PR #${prNumber}`);
|
||||||
|
return commentID;
|
||||||
|
}
|
||||||
|
const comment = await github.rest.issues.createComment({
|
||||||
|
...content,
|
||||||
|
issue_number: prNumber,
|
||||||
|
});
|
||||||
|
core.info(`Commented ${comment.data.id} on PR #${prNumber}`);
|
||||||
|
return comment.data.id;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import * as exec from "@actions/exec";
|
||||||
|
|
||||||
|
export interface ModuleName {
|
||||||
|
name: string; // Full module name
|
||||||
|
registry: string;
|
||||||
|
owner: string;
|
||||||
|
module: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseModuleNames extracts the module names from the given input. The input
|
||||||
|
// is a directory containing a buf.yaml file.
|
||||||
|
export async function parseModuleNames(
|
||||||
|
bufPath: string,
|
||||||
|
input: string,
|
||||||
|
): Promise<ModuleName[]> {
|
||||||
|
return exec
|
||||||
|
.getExecOutput(bufPath, ["config", "ls-modules", "--format", "name"], {
|
||||||
|
cwd: input,
|
||||||
|
})
|
||||||
|
.then((output) => {
|
||||||
|
const stdout = output.stdout.trim();
|
||||||
|
if (stdout === "") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return stdout.split("\n").map((n) => parseModuleName(n));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseModuleName parses the module name into its registry, owner, and
|
||||||
|
// repository parts.
|
||||||
|
export function parseModuleName(moduleName: string): ModuleName {
|
||||||
|
const parts = moduleName.split("/");
|
||||||
|
if (parts.length != 3) {
|
||||||
|
throw new Error(`Invalid module name: ${moduleName}`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: moduleName,
|
||||||
|
registry: parts[0],
|
||||||
|
owner: parts[1],
|
||||||
|
module: parts[2],
|
||||||
|
};
|
||||||
|
}
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
// Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import * as github from "@actions/github";
|
||||||
|
import {
|
||||||
|
PushEvent,
|
||||||
|
PullRequestEvent,
|
||||||
|
DeleteEvent,
|
||||||
|
} from "@octokit/webhooks-types";
|
||||||
|
|
||||||
|
// Inputs are the inputs to the action, matching the inputs in the action.yml.
|
||||||
|
export interface Inputs {
|
||||||
|
version: string;
|
||||||
|
token: string;
|
||||||
|
domain: string;
|
||||||
|
setup_only: boolean;
|
||||||
|
pr_comment: boolean;
|
||||||
|
github_actor: string;
|
||||||
|
github_token: string;
|
||||||
|
public_github_token: string;
|
||||||
|
|
||||||
|
input: string;
|
||||||
|
paths: string[];
|
||||||
|
exclude_paths: string[];
|
||||||
|
exclude_imports: boolean;
|
||||||
|
|
||||||
|
lint: boolean;
|
||||||
|
format: boolean;
|
||||||
|
breaking: boolean;
|
||||||
|
breaking_against: string;
|
||||||
|
push: boolean;
|
||||||
|
push_disable_create: boolean;
|
||||||
|
archive: boolean;
|
||||||
|
archive_labels: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInputs decodes the inputs from the environment variables.
|
||||||
|
export function getInputs(): Inputs {
|
||||||
|
const inputs: Inputs = {
|
||||||
|
version: core.getInput("version"),
|
||||||
|
token: core.getInput("token") || getEnv("BUF_TOKEN"),
|
||||||
|
domain: core.getInput("domain"),
|
||||||
|
setup_only: core.getBooleanInput("setup_only"),
|
||||||
|
pr_comment: core.getBooleanInput("pr_comment"),
|
||||||
|
github_actor: core.getInput("github_actor"),
|
||||||
|
github_token: core.getInput("github_token"),
|
||||||
|
public_github_token: core.getInput("public_github_token"),
|
||||||
|
// Inputs shared between buf steps.
|
||||||
|
input: core.getInput("input"),
|
||||||
|
paths: core.getMultilineInput("paths"),
|
||||||
|
exclude_paths: core.getMultilineInput("exclude_paths"),
|
||||||
|
exclude_imports: core.getBooleanInput("exclude_imports"),
|
||||||
|
// Inputs specific to buf steps.
|
||||||
|
lint: core.getBooleanInput("lint"),
|
||||||
|
format: core.getBooleanInput("format"),
|
||||||
|
breaking: core.getBooleanInput("breaking"),
|
||||||
|
breaking_against: core.getInput("breaking_against"),
|
||||||
|
push: core.getBooleanInput("push"),
|
||||||
|
push_disable_create: core.getBooleanInput("push_disable_create"),
|
||||||
|
archive: core.getBooleanInput("archive"),
|
||||||
|
archive_labels: [],
|
||||||
|
};
|
||||||
|
if (github.context.eventName === "push") {
|
||||||
|
const event = github.context.payload as PushEvent;
|
||||||
|
if (inputs.breaking_against === "") {
|
||||||
|
inputs.breaking_against = `${event.repository.clone_url}#format=git,commit=${event.before}`;
|
||||||
|
if (inputs.input) {
|
||||||
|
inputs.breaking_against += `,subdir=${inputs.input}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputs.archive_labels.push(github.context.ref);
|
||||||
|
}
|
||||||
|
if (github.context.eventName === "pull_request") {
|
||||||
|
const event = github.context.payload as PullRequestEvent;
|
||||||
|
if (inputs.breaking_against === "") {
|
||||||
|
inputs.breaking_against = `${event.repository.clone_url}#format=git,commit=${event.pull_request.base.sha}`;
|
||||||
|
if (inputs.input) {
|
||||||
|
inputs.breaking_against += `,subdir=${inputs.input}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputs.archive_labels.push(github.context.ref);
|
||||||
|
}
|
||||||
|
if (github.context.eventName === "delete") {
|
||||||
|
const event = github.context.payload as DeleteEvent;
|
||||||
|
inputs.archive_labels.push(event.ref);
|
||||||
|
}
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEnv returns the case insensitive value of the environment variable.
|
||||||
|
// Prefers the lowercase version of the variable if it exists.
|
||||||
|
export function getEnv(name: string): string {
|
||||||
|
return (
|
||||||
|
process.env[name.toLowerCase()] ?? process.env[name.toUpperCase()] ?? ""
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
// Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import * as exec from "@actions/exec";
|
||||||
|
import * as tc from "@actions/tool-cache";
|
||||||
|
import { GitHub } from "@actions/github/lib/utils";
|
||||||
|
import * as semver from "semver";
|
||||||
|
import { getEnv } from "./inputs";
|
||||||
|
|
||||||
|
// requiredVersion is the minimum version of buf required.
|
||||||
|
const requiredVersion = ">=1.35.0";
|
||||||
|
|
||||||
|
// installBuf installs the buf binary and returns the path to the binary. The
|
||||||
|
// versionInput should be an explicit version of buf.
|
||||||
|
export async function installBuf(
|
||||||
|
github: InstanceType<typeof GitHub> | undefined,
|
||||||
|
githubToken: string,
|
||||||
|
inputVersion: string,
|
||||||
|
): Promise<[string, string]> {
|
||||||
|
let resolvedVersion = resolveVersion(inputVersion);
|
||||||
|
if (resolvedVersion != "" && !tc.isExplicitVersion(resolvedVersion)) {
|
||||||
|
throw new Error(
|
||||||
|
`The version provided must be an explicit version: ${resolvedVersion}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Check if the binary is already available.
|
||||||
|
const bufName = "buf";
|
||||||
|
const binName = process.platform === "win32" ? "buf.exe" : "buf";
|
||||||
|
const whichBuf = await exec.exec("which", [binName], {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
if (whichBuf === 0) {
|
||||||
|
const bufVersion = await exec.getExecOutput(binName, ["--version"], {
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
const version = bufVersion.stdout.trim();
|
||||||
|
if (
|
||||||
|
semver.satisfies(version, requiredVersion) &&
|
||||||
|
(resolvedVersion == "" || semver.eq(version, resolvedVersion))
|
||||||
|
) {
|
||||||
|
core.info(`Using buf (${version}) found in $PATH`);
|
||||||
|
return [binName, version];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resolvedVersion === "") {
|
||||||
|
if (!github) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to resolve the latest version of buf. Must set the "version" parameter or provide a "public_github_token" to authenticate requests.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
resolvedVersion = await latestVersion(github);
|
||||||
|
}
|
||||||
|
if (!semver.satisfies(resolvedVersion, requiredVersion)) {
|
||||||
|
throw new Error(
|
||||||
|
`The resolved version of buf (${resolvedVersion}) does not satisfy the required version (${requiredVersion})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Fetch the version of buf to use.
|
||||||
|
let cachePath = tc.find(bufName, resolvedVersion);
|
||||||
|
if (!cachePath) {
|
||||||
|
core.info(`Downloading buf (${resolvedVersion})`);
|
||||||
|
const downloadPath = await downloadBuf(resolvedVersion, githubToken);
|
||||||
|
await exec.exec("chmod", ["+x", downloadPath]);
|
||||||
|
cachePath = await tc.cacheFile(
|
||||||
|
downloadPath,
|
||||||
|
binName,
|
||||||
|
bufName,
|
||||||
|
resolvedVersion,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
core.addPath(cachePath);
|
||||||
|
core.info(`Setup buf (${resolvedVersion}) at ${cachePath}`);
|
||||||
|
return [binName, resolvedVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveVersion from the input or environment.
|
||||||
|
function resolveVersion(versionSpec: string): string {
|
||||||
|
if (versionSpec) {
|
||||||
|
core.info(`Using the version of buf from the input: ${versionSpec}`);
|
||||||
|
return versionSpec;
|
||||||
|
} else if (getEnv("BUF_VERSION")) {
|
||||||
|
const resolvedVersion = getEnv("BUF_VERSION");
|
||||||
|
core.info(`Using the version of buf from $BUF_VERSION: ${resolvedVersion}`);
|
||||||
|
return resolvedVersion;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// latestVersion returns the latest version of buf from GitHub releases.
|
||||||
|
async function latestVersion(
|
||||||
|
github: InstanceType<typeof GitHub>,
|
||||||
|
): Promise<string> {
|
||||||
|
const release = await github.rest.repos.getLatestRelease({
|
||||||
|
owner: "bufbuild",
|
||||||
|
repo: "buf",
|
||||||
|
});
|
||||||
|
const resolvedVersion = release.data.tag_name.replace(/^v/, "");
|
||||||
|
core.info(`Using the latest release of buf: ${resolvedVersion}`);
|
||||||
|
return resolvedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// platformTable is a mapping of platform to architecture to executable URL.
|
||||||
|
// The platform and architecture are based on the Node.js process properties.
|
||||||
|
// The available platforms can be found at:
|
||||||
|
// - https://nodejs.org/api/process.html#process_process_platform
|
||||||
|
// - https://nodejs.org/api/process.html#process_process_arch
|
||||||
|
type platformTable = {
|
||||||
|
[platform in NodeJS.Platform]?: {
|
||||||
|
[arch in NodeJS.Architecture]?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// downloadBuf downloads the buf binary and returns the path to the binary.
|
||||||
|
async function downloadBuf(
|
||||||
|
version: string,
|
||||||
|
githubToken: string,
|
||||||
|
): Promise<string> {
|
||||||
|
const table: platformTable = {
|
||||||
|
darwin: {
|
||||||
|
x64: "buf-Darwin-x86_64",
|
||||||
|
arm64: "buf-Darwin-arm64",
|
||||||
|
},
|
||||||
|
linux: {
|
||||||
|
x64: "buf-Linux-x86_64",
|
||||||
|
arm64: "buf-Linux-aarch64",
|
||||||
|
},
|
||||||
|
win32: {
|
||||||
|
x64: "buf-Windows-x86_64.exe",
|
||||||
|
arm64: "buf-Windows-arm64.exe",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const platform = table[process.platform];
|
||||||
|
if (!platform) {
|
||||||
|
throw new Error(
|
||||||
|
`The "${process.platform}" platform is not supported with a Buf release.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const executable = platform[process.arch];
|
||||||
|
if (!executable) {
|
||||||
|
throw new Error(
|
||||||
|
`The "${process.arch}" architecture is not supported with a Buf release.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const downloadURL = `https://github.com/bufbuild/buf/releases/download/v${version}/${executable}`;
|
||||||
|
const auth = githubToken ? `token ${githubToken}` : undefined;
|
||||||
|
if (!auth) {
|
||||||
|
// Warn if the GitHub token is not set. Don't fail as not required.
|
||||||
|
core.warning(
|
||||||
|
"Downloading buf without a GitHub API token, rate limits may apply.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await tc.downloadTool(downloadURL, undefined, auth);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to download buf version ${version} from "${downloadURL}": ${error}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+488
@@ -0,0 +1,488 @@
|
|||||||
|
// Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import * as exec from "@actions/exec";
|
||||||
|
import { GitHub } from "@actions/github/lib/utils";
|
||||||
|
import { context, getOctokit } from "@actions/github";
|
||||||
|
|
||||||
|
import { create } from "@bufbuild/protobuf";
|
||||||
|
|
||||||
|
import { createConnectTransport } from "@connectrpc/connect-web";
|
||||||
|
import { createClient, ConnectError, Code } from "@connectrpc/connect";
|
||||||
|
import { LabelService } from "@buf/bufbuild_registry.bufbuild_es/buf/registry/module/v1/label_service_pb";
|
||||||
|
import { LabelRefSchema } from "@buf/bufbuild_registry.bufbuild_es/buf/registry/module/v1/label_pb";
|
||||||
|
import * as parseDiff from "parse-diff";
|
||||||
|
|
||||||
|
import { getInputs, Inputs, getEnv } from "./inputs";
|
||||||
|
import { Outputs } from "./outputs";
|
||||||
|
import { installBuf } from "./installer";
|
||||||
|
import { findCommentOnPR, commentOnPR } from "./comment";
|
||||||
|
import { parseModuleNames, ModuleName } from "./config";
|
||||||
|
|
||||||
|
// URL for the public GitHub API.
|
||||||
|
const publicGitHubApiUrl = "https://api.github.com";
|
||||||
|
|
||||||
|
// main is the entrypoint for the action.
|
||||||
|
async function main() {
|
||||||
|
const inputs = getInputs();
|
||||||
|
const github = getOctokit(inputs.github_token);
|
||||||
|
// Check if the action is running on a GitHub Enterprise instance.
|
||||||
|
// If so, use the public GitHub API for resolving the Buf version etc.
|
||||||
|
let publicGithubToken = inputs.github_token;
|
||||||
|
let publicGithub: InstanceType<typeof GitHub> | undefined = github;
|
||||||
|
const apiUrl = process.env.GITHUB_API_URL || ``;
|
||||||
|
if (!apiUrl.startsWith(publicGitHubApiUrl)) {
|
||||||
|
core.info("Running on GitHub Enterprise, using public GitHub API.");
|
||||||
|
publicGithubToken = inputs.public_github_token;
|
||||||
|
if (publicGithubToken == "") {
|
||||||
|
publicGithub = undefined;
|
||||||
|
} else {
|
||||||
|
publicGithub = getOctokit(publicGithubToken, {
|
||||||
|
baseUrl: publicGitHubApiUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const [bufPath, bufVersion] = await installBuf(
|
||||||
|
publicGithub,
|
||||||
|
publicGithubToken,
|
||||||
|
inputs.version,
|
||||||
|
);
|
||||||
|
core.setOutput(Outputs.BufVersion, bufVersion);
|
||||||
|
core.setOutput(Outputs.BufPath, bufPath);
|
||||||
|
core.saveState(Outputs.BufPath, bufPath);
|
||||||
|
if (inputs.github_actor == "dependabot[bot]") {
|
||||||
|
core.info("Skipping steps for dependabot");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await login(bufPath, inputs);
|
||||||
|
if (inputs.setup_only) {
|
||||||
|
core.info("Setup only, skipping steps");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Parse the module names from the input.
|
||||||
|
const moduleNames = await parseModuleNames(bufPath, inputs.input);
|
||||||
|
// Run the buf workflow.
|
||||||
|
const steps = await runWorkflow(bufPath, inputs, moduleNames);
|
||||||
|
// Create a summary of the steps.
|
||||||
|
const summary = createSummary(inputs, steps, moduleNames);
|
||||||
|
// Comment on the PR with the summary, if requested.
|
||||||
|
if (inputs.pr_comment) {
|
||||||
|
const commentID = await findCommentOnPR(context, github);
|
||||||
|
await commentOnPR(
|
||||||
|
context,
|
||||||
|
github,
|
||||||
|
commentID,
|
||||||
|
`The latest Buf updates on your PR. Results from workflow <a href="${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}">${context.workflow} / ${context.job} (pull_request)</a>.\n\n${summary.stringify()}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Write the summary to a file defined by GITHUB_STEP_SUMMARY.
|
||||||
|
// NB: Write empties the buffer and must be after the comment.
|
||||||
|
await summary.write();
|
||||||
|
// Finally, set the status of the action.
|
||||||
|
for (const [key, value] of Object.entries(steps) as [
|
||||||
|
keyof Steps,
|
||||||
|
Result | undefined,
|
||||||
|
][]) {
|
||||||
|
if (value?.status == Status.Failed) {
|
||||||
|
core.setFailed(`Failed ${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((err) => core.setFailed(err.message))
|
||||||
|
.then(() => core.debug(`done in ${process.uptime()} s`));
|
||||||
|
|
||||||
|
// Steps is the result of each step in the workflow. Each step is optional.
|
||||||
|
// Steps are run in order, buy may be batched together if independent.
|
||||||
|
interface Steps {
|
||||||
|
build?: Result;
|
||||||
|
lint?: Result;
|
||||||
|
format?: Result;
|
||||||
|
breaking?: Result;
|
||||||
|
push?: Result;
|
||||||
|
archive?: Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// createSummary creates a GitHub summary of the steps. The summary is a table
|
||||||
|
// with the name and status of each step.
|
||||||
|
function createSummary(
|
||||||
|
inputs: Inputs,
|
||||||
|
steps: Steps,
|
||||||
|
moduleNames: ModuleName[],
|
||||||
|
): typeof core.summary {
|
||||||
|
const table = [
|
||||||
|
[
|
||||||
|
{ data: "Build", header: true },
|
||||||
|
{ data: "Format", header: true },
|
||||||
|
{ data: "Lint", header: true },
|
||||||
|
{ data: "Breaking", header: true },
|
||||||
|
{ data: "Updated (UTC)", header: true },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
message(steps.build),
|
||||||
|
message(steps.format),
|
||||||
|
message(steps.lint),
|
||||||
|
message(steps.breaking),
|
||||||
|
new Date().toLocaleString("en-US", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "short",
|
||||||
|
year: "numeric",
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
hour12: true,
|
||||||
|
timeZone: "UTC",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
// If push or archive is enabled add a link to the registry.
|
||||||
|
let output = core.summary.addTable(table);
|
||||||
|
if (inputs.push && moduleNames.length > 0) {
|
||||||
|
const modules = moduleNames.map(
|
||||||
|
(moduleName) =>
|
||||||
|
`<a href="https://${moduleName.name}">${moduleName.name}</a>`,
|
||||||
|
);
|
||||||
|
output = output.addRaw(`Pushed to ${modules.join(", ")}.`, true);
|
||||||
|
}
|
||||||
|
if (inputs.archive && moduleNames.length > 0) {
|
||||||
|
const modules = moduleNames.map(
|
||||||
|
(moduleName) =>
|
||||||
|
`<a href="https://${moduleName.name}">${moduleName.name}</a>`,
|
||||||
|
);
|
||||||
|
output = output.addRaw(
|
||||||
|
`Archived labels ${inputs.archive_labels.join(", ")} to ${modules.join(", ")}.`,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// runWorkflow runs the buf workflow. It returns the results of each step.
|
||||||
|
// First, it builds the input. If the build fails, the workflow stops.
|
||||||
|
// Next, it runs lint, format, and breaking checks. If any of these fail, the workflow stops.
|
||||||
|
// Finally, it pushes or archives the label to the registry.
|
||||||
|
async function runWorkflow(
|
||||||
|
bufPath: string,
|
||||||
|
inputs: Inputs,
|
||||||
|
moduleNames: ModuleName[],
|
||||||
|
): Promise<Steps> {
|
||||||
|
const steps: Steps = {};
|
||||||
|
steps.build = await build(bufPath, inputs);
|
||||||
|
if (steps.build.status == Status.Failed) {
|
||||||
|
if (steps.build.stderr.match(/had no .proto files/)) {
|
||||||
|
core.info(
|
||||||
|
'Did you forget to add the "actions/checkout@v4" checkout step to your workflow?',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
const checks = await Promise.all([
|
||||||
|
lint(bufPath, inputs),
|
||||||
|
format(bufPath, inputs),
|
||||||
|
breaking(bufPath, inputs),
|
||||||
|
]);
|
||||||
|
steps.lint = checks[0];
|
||||||
|
steps.format = checks[1];
|
||||||
|
steps.breaking = checks[2];
|
||||||
|
if (checks.some((result) => result.status == Status.Failed)) {
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
steps.push = await push(bufPath, inputs, moduleNames);
|
||||||
|
steps.archive = await archive(inputs, moduleNames);
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// login logs in to the Buf registry, storing credentials.
|
||||||
|
async function login(bufPath: string, inputs: Inputs) {
|
||||||
|
const { token, domain } = inputs;
|
||||||
|
if (token == "") {
|
||||||
|
core.debug("Skipping login, no token provided");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
core.debug(`Logging in to ${domain}`);
|
||||||
|
await exec.exec(bufPath, ["registry", "login", domain, "--token-stdin"], {
|
||||||
|
input: Buffer.from(token + "\n"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// build runs the "buf build" step.
|
||||||
|
async function build(bufPath: string, inputs: Inputs): Promise<Result> {
|
||||||
|
const args = ["build", "--error-format", "github-actions"];
|
||||||
|
if (inputs.input) {
|
||||||
|
args.push(inputs.input);
|
||||||
|
}
|
||||||
|
for (const path of inputs.paths) {
|
||||||
|
args.push("--path", path);
|
||||||
|
}
|
||||||
|
for (const path of inputs.exclude_paths) {
|
||||||
|
args.push("--exclude-path", path);
|
||||||
|
}
|
||||||
|
if (inputs.exclude_imports) {
|
||||||
|
args.push("--exclude-imports");
|
||||||
|
}
|
||||||
|
return run(bufPath, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lint runs the "buf lint" step.
|
||||||
|
async function lint(bufPath: string, inputs: Inputs): Promise<Result> {
|
||||||
|
if (!inputs.lint) {
|
||||||
|
core.debug("Skipping lint");
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
const args = ["lint", "--error-format", "github-actions"];
|
||||||
|
if (inputs.input) {
|
||||||
|
args.push(inputs.input);
|
||||||
|
}
|
||||||
|
for (const path of inputs.paths) {
|
||||||
|
args.push("--path", path);
|
||||||
|
}
|
||||||
|
for (const path of inputs.exclude_paths) {
|
||||||
|
args.push("--exclude-path", path);
|
||||||
|
}
|
||||||
|
return run(bufPath, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// format runs the "buf format" step.
|
||||||
|
async function format(bufPath: string, inputs: Inputs): Promise<Result> {
|
||||||
|
if (!inputs.format) {
|
||||||
|
core.debug("Skipping format");
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
const args = [
|
||||||
|
"format",
|
||||||
|
"--diff",
|
||||||
|
"--error-format",
|
||||||
|
"github-actions",
|
||||||
|
"--exit-code",
|
||||||
|
];
|
||||||
|
if (inputs.input) {
|
||||||
|
args.push(inputs.input);
|
||||||
|
}
|
||||||
|
for (const path of inputs.paths) {
|
||||||
|
args.push("--path", path);
|
||||||
|
}
|
||||||
|
for (const path of inputs.exclude_paths) {
|
||||||
|
args.push("--exclude-path", path);
|
||||||
|
}
|
||||||
|
const result = await run(bufPath, args);
|
||||||
|
if (result.status == Status.Failed && result.stdout.startsWith("diff")) {
|
||||||
|
// If the format step fails, parse the diff and write github annotations.
|
||||||
|
const diff = parseDiff(result.stdout);
|
||||||
|
result.stdout = ""; // Clear the stdout to count the number of changes.
|
||||||
|
for (const file of diff) {
|
||||||
|
result.stdout += `::error file=${file.to}::Format diff -${file.deletions}/+${file.additions}.\n`;
|
||||||
|
}
|
||||||
|
console.log(result.stdout);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// breaking runs the "buf breaking" step.
|
||||||
|
async function breaking(bufPath: string, inputs: Inputs): Promise<Result> {
|
||||||
|
if (!inputs.breaking) {
|
||||||
|
core.debug("Skipping breaking");
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
const args = [
|
||||||
|
"breaking",
|
||||||
|
"--error-format",
|
||||||
|
"github-actions",
|
||||||
|
"--against",
|
||||||
|
inputs.breaking_against,
|
||||||
|
];
|
||||||
|
if (inputs.input) {
|
||||||
|
args.push(inputs.input);
|
||||||
|
}
|
||||||
|
for (const path of inputs.paths) {
|
||||||
|
args.push("--path", path);
|
||||||
|
}
|
||||||
|
for (const path of inputs.exclude_paths) {
|
||||||
|
args.push("--exclude-path", path);
|
||||||
|
}
|
||||||
|
if (inputs.exclude_imports) {
|
||||||
|
args.push("--exclude-imports");
|
||||||
|
}
|
||||||
|
return run(bufPath, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// push runs the "buf push" step.
|
||||||
|
async function push(
|
||||||
|
bufPath: string,
|
||||||
|
inputs: Inputs,
|
||||||
|
moduleNames: ModuleName[],
|
||||||
|
): Promise<Result> {
|
||||||
|
if (!inputs.push) {
|
||||||
|
core.debug("Skipping push");
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
if (moduleNames.length == 0) {
|
||||||
|
core.debug("Skipping push, no named modules detected");
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
// We want push to succeed without additional user configuration even if the action is being run on an
|
||||||
|
// enterprise GitHub instance. Because enterprise GitHub instances can have an arbitrary URL, the Buf CLI with not be
|
||||||
|
// able to automatically detect the source control url, so we set it explicitly.
|
||||||
|
const sourceControlUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/commit/${context.sha}`;
|
||||||
|
const args = [
|
||||||
|
"push",
|
||||||
|
"--error-format",
|
||||||
|
"github-actions",
|
||||||
|
"--exclude-unnamed",
|
||||||
|
"--git-metadata",
|
||||||
|
"--source-control-url",
|
||||||
|
sourceControlUrl,
|
||||||
|
];
|
||||||
|
if (!inputs.push_disable_create) {
|
||||||
|
args.push("--create");
|
||||||
|
}
|
||||||
|
if (inputs.input) {
|
||||||
|
args.push(inputs.input);
|
||||||
|
}
|
||||||
|
return run(bufPath, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// archive runs the "buf archive" step.
|
||||||
|
async function archive(
|
||||||
|
inputs: Inputs,
|
||||||
|
moduleNames: ModuleName[],
|
||||||
|
): Promise<Result> {
|
||||||
|
if (!inputs.archive) {
|
||||||
|
core.debug("Skipping archive");
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
if (moduleNames.length == 0) {
|
||||||
|
core.debug("Skipping archive, no named modules detected");
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
if (inputs.archive_labels.length == 0) {
|
||||||
|
core.debug("Skipping archive, no labels provided");
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
for (const moduleName of moduleNames) {
|
||||||
|
const baseURL = `https://${moduleName.registry}`;
|
||||||
|
const transport = createConnectTransport({
|
||||||
|
baseUrl: baseURL,
|
||||||
|
});
|
||||||
|
const client = createClient(LabelService, transport);
|
||||||
|
for (const label of inputs.archive_labels) {
|
||||||
|
const labelRef = create(LabelRefSchema, {
|
||||||
|
value: {
|
||||||
|
case: "name",
|
||||||
|
value: {
|
||||||
|
owner: moduleName.owner,
|
||||||
|
module: moduleName.module,
|
||||||
|
label: label,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await client.archiveLabels(
|
||||||
|
{ labelRefs: [labelRef] },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${inputs.token}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
core.info(`Archived label ${label} for ${moduleName.name}`);
|
||||||
|
} catch (err) {
|
||||||
|
const connectError = ConnectError.from(err);
|
||||||
|
if (connectError.code == Code.NotFound) {
|
||||||
|
core.info(
|
||||||
|
`Skipping archive, label ${label} not found for ${moduleName.name}`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
core.error(
|
||||||
|
`Failed to archive label ${label} for ${moduleName.name}: ${connectError.message}`,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
status: Status.Failed,
|
||||||
|
exitCode: 1,
|
||||||
|
stdout: "",
|
||||||
|
stderr: connectError.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status is the status of a command execution.
|
||||||
|
enum Status {
|
||||||
|
// Unknown is the default status.
|
||||||
|
Unknown = 0,
|
||||||
|
// Passed is the status of a successful command execution.
|
||||||
|
Passed,
|
||||||
|
// Failed is the status of a failed command execution.
|
||||||
|
Failed,
|
||||||
|
// Skipped is the status of a skipped command execution.
|
||||||
|
Skipped,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result is the result of a command execution.
|
||||||
|
interface Result extends exec.ExecOutput {
|
||||||
|
status: Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// run executes the buf command with the given arguments.
|
||||||
|
async function run(bufPath: string, args: string[]): Promise<Result> {
|
||||||
|
if (core.isDebug()) {
|
||||||
|
args = ["--debug", ...args];
|
||||||
|
}
|
||||||
|
return exec
|
||||||
|
.getExecOutput(bufPath, args, {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
// See: https://buf.build/docs/bsr/authentication
|
||||||
|
BUF_TOKEN: core.getInput("token") || getEnv("BUF_TOKEN"),
|
||||||
|
// See: https://buf.build/docs/reference/inputs#https
|
||||||
|
BUF_INPUT_HTTPS_USERNAME:
|
||||||
|
getEnv("BUF_INPUT_HTTPS_USERNAME") || core.getInput("github_actor"),
|
||||||
|
BUF_INPUT_HTTPS_PASSWORD:
|
||||||
|
getEnv("BUF_INPUT_HTTPS_PASSWORD") || core.getInput("github_token"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((output) => ({
|
||||||
|
...output,
|
||||||
|
status: output.exitCode == 0 ? Status.Passed : Status.Failed,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip returns a skipped result.
|
||||||
|
function skip(): Result {
|
||||||
|
return { status: Status.Skipped, exitCode: 0, stdout: "", stderr: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass returns a successful result.
|
||||||
|
function pass(): Result {
|
||||||
|
return { status: Status.Passed, exitCode: 0, stdout: "", stderr: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// message returns a human-readable message for the status. An undefined status
|
||||||
|
// is considered cancelled.
|
||||||
|
function message(result: Result | undefined): string {
|
||||||
|
switch (result?.status) {
|
||||||
|
case Status.Passed:
|
||||||
|
return "<code>✅ passed</code>";
|
||||||
|
case Status.Failed:
|
||||||
|
return `<code>❌ failed (${result.stdout.split("\n").length - 1})</code>`;
|
||||||
|
case Status.Skipped:
|
||||||
|
return "<code>⏩ skipped</code>";
|
||||||
|
default:
|
||||||
|
return "<code>🚫 cancelled</code>";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Outputs are the outputs of the action, matching the outputs in the action.yml.
|
||||||
|
export enum Outputs {
|
||||||
|
BufVersion = "buf_version",
|
||||||
|
BufPath = "buf_path",
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2024-2025 Buf Technologies, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import * as exec from "@actions/exec";
|
||||||
|
|
||||||
|
import { getInputs, Inputs } from "./inputs";
|
||||||
|
import { Outputs } from "./outputs";
|
||||||
|
|
||||||
|
// main is the entrypoint for the action's post step.
|
||||||
|
async function main() {
|
||||||
|
const inputs = getInputs();
|
||||||
|
const bufPath = core.getState(Outputs.BufPath);
|
||||||
|
await logout(bufPath, inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((err) => core.setFailed(err.message))
|
||||||
|
.then(() => core.debug(`done in ${process.uptime()} s`));
|
||||||
|
|
||||||
|
// logout logs out of the Buf registry, removing credentials.
|
||||||
|
async function logout(bufPath: string, inputs: Inputs) {
|
||||||
|
const { domain } = inputs;
|
||||||
|
core.debug(`Logging out of ${domain}`);
|
||||||
|
await exec.exec(bufPath, ["registry", "logout", domain]);
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2022",
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user