Skip to content

Commit f8b5667

Browse files
chore: close the known risks left open after the review (#49)
* chore(ci): update every remaining action to its current major The release, helm publish, scheduled sync, and Azure DevOps extension workflows were still on actions targeting Node 20, which GitHub force migrates to Node 24 today and will stop supporting. They were left behind earlier because no pull request exercises them; that reasoning holds for proving them, not for leaving them broken, so they are updated now and the next tag exercises the release and helm paths for real. goreleaser is pinned to its v2 major rather than latest. The action warned that latest is ambiguous and locks to ~> v2 regardless, and a release is not where a new major should first run. * chore(deps): clear the two remaining advisories golang.org/x/text carried an infinite loop on invalid input (GO-2026-5970) and klauspost/compress an out of bounds read in s2 (GO-2026-5841). Neither was reachable from SyncerD's own code, which is why they were tolerated, but both are transitive dependencies that a later change could easily start calling, and both had a published fix. govulncheck now reports nothing at all rather than nothing reachable. * test: prove SyncerD against a real provider Everything in this repository was checked against fakes written by the same hand as the code, and three defects that made pull request mirroring inoperable survived a green suite, a passing verification gate, and a review of their own change, because every fake agreed with the mistake. Adds a live suite behind the "live" build tag: it creates two throwaway private repositories, seeds one, opens a pull request, mirrors branches and then objects, and asserts what actually landed. It covers exactly the ground the fakes could not: that the destination repository path is the one the API accepts, that a pull request is findable by its head branch after state is lost, that a mirrored comment appears once rather than on every run, that mentions do not survive into the destination, and that closing the source closes the mirror. Then it deletes both repositories. It is inert without credentials and excluded from an ordinary go test, so CI is unchanged. make test-live runs it, and the runbook says when to. * docs: record the risk work in the changelog * fix(github): send an empty label array rather than null Once the mirror owns labels, a source pull request with none produced a nil slice, which marshals to null, and GitHub answers 422: the field has to be an array. Clearing labels is a legitimate and common request, so this broke the update of nearly every mirrored pull request, and the label failure was wrapped in a way that failed the whole pull request rather than just its labels. Found by the live suite on its first real run against GitHub, in the first minute it existed. No fake had an opinion about null. --------- Co-authored-by: Anmol Nagpal <ianmolnagpal@gmail.com>
1 parent 29ac323 commit f8b5667

13 files changed

Lines changed: 477 additions & 34 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
uses: actions/checkout@v7
3838

3939
- name: Set up Helm
40-
uses: azure/setup-helm@v4
40+
uses: azure/setup-helm@v5
4141
with:
4242
version: "v3.14.0"
4343

.github/workflows/helm-release.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ jobs:
2424
runs-on: ubuntu-latest
2525
steps:
2626
- name: Checkout
27-
uses: actions/checkout@v4
27+
uses: actions/checkout@v7
2828

2929
- name: Set up Helm
30-
uses: azure/setup-helm@v4
30+
uses: azure/setup-helm@v5
3131
with:
3232
version: "v3.14.0"
3333

@@ -64,7 +64,7 @@ jobs:
6464
echo "digest=${digest}" >> "$GITHUB_OUTPUT"
6565
6666
- name: Install cosign
67-
uses: sigstore/cosign-installer@v3
67+
uses: sigstore/cosign-installer@v4
6868

6969
- name: Log in to GHCR (cosign)
7070
run: |
@@ -79,7 +79,7 @@ jobs:
7979
"ghcr.io/clouddrove/charts/syncerd@${{ steps.push.outputs.digest }}"
8080
8181
- name: Install ORAS
82-
uses: oras-project/setup-oras@v1
82+
uses: oras-project/setup-oras@v2
8383

8484
- name: Log in to GHCR (ORAS)
8585
run: |

.github/workflows/publish-azure-devops-extension.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929

3030
steps:
3131
- name: Checkout
32-
uses: actions/checkout@v4
32+
uses: actions/checkout@v7
3333

3434
- name: Validate secret
3535
shell: bash
@@ -71,7 +71,7 @@ jobs:
7171
echo "path=azure-devops-extension/$vsix" >> "$GITHUB_OUTPUT"
7272
7373
- name: Upload VSIX artifact
74-
uses: actions/upload-artifact@v4
74+
uses: actions/upload-artifact@v7
7575
with:
7676
name: syncerd-azure-pipelines-vsix
7777
path: ${{ steps.vsix.outputs.path }}

.github/workflows/release.yml

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,23 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- name: Checkout
17-
uses: actions/checkout@v4
17+
uses: actions/checkout@v7
1818
with:
1919
fetch-depth: 0
2020

2121
- name: Set up Go
22-
uses: actions/setup-go@v5
22+
uses: actions/setup-go@v7
2323
with:
2424
go-version: "1.26"
2525
cache: true
2626

2727
- name: Run GoReleaser
28-
uses: goreleaser/goreleaser-action@v6
28+
uses: goreleaser/goreleaser-action@v7
2929
with:
30-
version: latest
30+
# Pinned to the major rather than latest: the action warns that
31+
# "latest" is ambiguous and silently locks to ~> v2 anyway, and a
32+
# release should not be the place a new major first runs.
33+
version: "~> v2"
3134
args: release --clean
3235
env:
3336
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -37,24 +40,24 @@ jobs:
3740
needs: goreleaser
3841
steps:
3942
- name: Checkout
40-
uses: actions/checkout@v4
43+
uses: actions/checkout@v7
4144

4245
- name: Set up QEMU
43-
uses: docker/setup-qemu-action@v3
46+
uses: docker/setup-qemu-action@v4
4447

4548
- name: Set up Docker Buildx
46-
uses: docker/setup-buildx-action@v3
49+
uses: docker/setup-buildx-action@v4
4750

4851
- name: Login to GHCR
49-
uses: docker/login-action@v3
52+
uses: docker/login-action@v4
5053
with:
5154
registry: ghcr.io
5255
username: ${{ github.actor }}
5356
password: ${{ secrets.GITHUB_TOKEN }}
5457

5558
- name: Docker meta
5659
id: meta
57-
uses: docker/metadata-action@v5
60+
uses: docker/metadata-action@v6
5861
with:
5962
# Publish to the canonical image name (Marketplace action uses this).
6063
images: ghcr.io/clouddrove/syncerd
@@ -70,7 +73,7 @@ jobs:
7073
type=raw,value=latest
7174
7275
- name: Build and push
73-
uses: docker/build-push-action@v6
76+
uses: docker/build-push-action@v7
7477
with:
7578
context: .
7679
file: ./Dockerfile

.github/workflows/syncerd.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121

2222
steps:
2323
- name: Checkout code
24-
uses: actions/checkout@v4
24+
uses: actions/checkout@v7
2525

2626
- name: Set up Go
27-
uses: actions/setup-go@v5
27+
uses: actions/setup-go@v7
2828
with:
2929
go-version: '1.26'
3030
cache: false
@@ -37,15 +37,15 @@ jobs:
3737
# Keep only the login steps for the registries you actually use.
3838

3939
- name: Login to GHCR
40-
uses: docker/login-action@v3
40+
uses: docker/login-action@v4
4141
with:
4242
registry: ghcr.io
4343
username: ${{ github.actor }}
4444
password: ${{ secrets.GITHUB_TOKEN }}
4545

4646
- name: Configure AWS credentials (ECR)
4747
continue-on-error: true
48-
uses: aws-actions/configure-aws-credentials@v4
48+
uses: aws-actions/configure-aws-credentials@v6
4949
with:
5050
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
5151
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -57,7 +57,7 @@ jobs:
5757

5858
- name: Azure login (ACR)
5959
continue-on-error: true
60-
uses: azure/login@v1
60+
uses: azure/login@v3
6161
with:
6262
creds: ${{ secrets.AZURE_CREDENTIALS }}
6363

@@ -71,13 +71,13 @@ jobs:
7171

7272
- name: Authenticate to Google Cloud (GCR)
7373
continue-on-error: true
74-
uses: google-github-actions/auth@v2
74+
uses: google-github-actions/auth@v3
7575
with:
7676
credentials_json: ${{ secrets.GCP_SA_KEY }}
7777

7878
- name: Set up gcloud (GCR)
7979
continue-on-error: true
80-
uses: google-github-actions/setup-gcloud@v2
80+
uses: google-github-actions/setup-gcloud@v3
8181

8282
- name: Configure Docker for GCR
8383
continue-on-error: true

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
- A failed state save on the fail-fast path was silent, so everything copied before the abort was copied again on the next run with no explanation
2222
- `git-sync` could not authenticate to Azure DevOps over git in `pat` mode, fixed in v0.2.1 and described there
2323

24+
### Fixed
25+
- A mirrored pull request with no labels failed to update, taking the whole pull request with it. Once the mirror owned labels, an empty set was sent as null rather than as an empty array, and GitHub rejects that with 422. Most pull requests carry no labels, so this affected most of them. Found by the live suite on its first real run
26+
27+
### Added
28+
- A live provider test suite behind the `live` build tag, run with `make test-live`. It creates two throwaway repositories against a real GitHub account, mirrors a pull request between them, and checks what actually landed, then deletes them. Every other test in the repository checks SyncerD against fakes written alongside the code, which is how three defects that made pull request mirroring inoperable passed a green suite and a review
29+
2430
### Security
31+
- Cleared two advisories in transitive Go modules, an infinite loop in `golang.org/x/text` and an out of bounds read in `klauspost/compress`. Neither was reachable from SyncerD's own code, but both had fixes and a later change could have started calling them
2532
- Patched a high severity advisory in `brace-expansion`, which the Azure DevOps extension pinned to exactly the affected version through an `overrides` entry
2633

2734
## [0.2.1] - 2026-08-19

Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: build test clean install lint
1+
.PHONY: build test test-live clean install lint
22

33
# Version info injected at build time
44
VERSION ?= $(shell git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo dev)
@@ -13,6 +13,17 @@ build:
1313
test:
1414
go test -v ./...
1515

16+
# Run the live provider tests. These talk to a real GitHub account, create
17+
# two throwaway repositories, use them, and delete them again. They are the
18+
# only tests that prove a real API call works: everything else is checked
19+
# against fakes written by the same hand as the code.
20+
#
21+
# SYNCERD_LIVE_GITHUB_TOKEN=ghp_... token with repo scope
22+
# SYNCERD_LIVE_GITHUB_OWNER=acme account or org to create under
23+
# SYNCERD_LIVE_KEEP=1 optional, skip cleanup to inspect
24+
test-live:
25+
go test -tags live -v -timeout 15m ./internal/livetest/
26+
1627
# Clean build artifacts
1728
clean:
1829
rm -f syncerd

docs/git-sync-runbook.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,34 @@ Only GitHub sources can list pull requests today. A mirror that enables
740740

741741
---
742742

743+
## Live provider tests
744+
745+
Every other test in this repository checks SyncerD against fakes written by
746+
whoever wrote the code, and that has already proved insufficient: three
747+
defects that made pull request mirroring inoperable survived a green suite,
748+
a passing verification run, and a review of their own change, because the
749+
fakes agreed with the mistake. A real API does not.
750+
751+
```bash
752+
export SYNCERD_LIVE_GITHUB_TOKEN=ghp_... # repo scope
753+
export SYNCERD_LIVE_GITHUB_OWNER=your-account
754+
make test-live
755+
```
756+
757+
It creates two throwaway private repositories named `syncerd-live-*`, seeds
758+
one with a branch, opens a pull request, mirrors the branches and then the
759+
pull request to the other, and checks what actually landed: that the
760+
destination pull request exists and is findable by its head branch, that
761+
the body carries the marker and no live mention, that a second run changes
762+
nothing, that a comment mirrors exactly once, and that closing the source
763+
closes the mirror. Then it deletes both repositories. Set
764+
`SYNCERD_LIVE_KEEP=1` to leave them behind and look.
765+
766+
Run it before a release that touches provider code. It is the only check
767+
that would have caught the defects above.
768+
769+
---
770+
743771
## Cleanup
744772

745773
```bash

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ require (
3434
github.com/fsnotify/fsnotify v1.7.0 // indirect
3535
github.com/hashicorp/hcl v1.0.0 // indirect
3636
github.com/inconshreveable/mousetrap v1.1.0 // indirect
37-
github.com/klauspost/compress v1.17.0 // indirect
37+
github.com/klauspost/compress v1.18.7 // indirect
3838
github.com/magiconair/properties v1.8.7 // indirect
3939
github.com/mitchellh/go-homedir v1.1.0 // indirect
4040
github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -53,8 +53,8 @@ require (
5353
go.uber.org/atomic v1.9.0 // indirect
5454
go.uber.org/multierr v1.9.0 // indirect
5555
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
56-
golang.org/x/sync v0.5.0 // indirect
57-
golang.org/x/text v0.14.0 // indirect
56+
golang.org/x/sync v0.21.0 // indirect
57+
golang.org/x/text v0.39.0 // indirect
5858
gopkg.in/ini.v1 v1.67.0 // indirect
5959
gopkg.in/yaml.v3 v3.0.1 // indirect
6060
)

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
5757
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
5858
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
5959
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
60-
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
61-
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
60+
github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw=
61+
github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
6262
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
6363
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
6464
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -125,14 +125,14 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
125125
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
126126
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
127127
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
128-
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
129-
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
128+
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
129+
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
130130
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
131131
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
132132
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
133133
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
134-
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
135-
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
134+
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
135+
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
136136
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
137137
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
138138
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

0 commit comments

Comments
 (0)