diff --git a/.github/workflows/build-container-reusable.yml b/.github/workflows/build-container-reusable.yml index fc55e6bda..446859a12 100644 --- a/.github/workflows/build-container-reusable.yml +++ b/.github/workflows/build-container-reusable.yml @@ -21,6 +21,10 @@ on: required: false type: string default: eventstore-test-image + image-artifact-name: + required: false + type: string + default: eventstore-image jobs: build-container: @@ -62,6 +66,9 @@ jobs: build-args: | CONTAINER_RUNTIME=${{ inputs.container-runtime }} RUNTIME=${{ inputs.runtime }} + labels: | + org.opencontainers.image.source=https://github.com/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} cache-from: type=gha,scope=${{ format('container-{0}-{1}', inputs.container-runtime, inputs.runtime) }} cache-to: type=gha,mode=max,scope=${{ format('container-{0}-{1}', inputs.container-runtime, inputs.runtime) }} secrets: | @@ -89,6 +96,10 @@ jobs: if: ${{ inputs.mode == 'build-only' }} run: | docker save eventstore-test -o eventstore-test.tar + - name: Export Container + if: ${{ inputs.mode == 'build-only' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }} + run: | + docker save eventstore -o eventstore.tar - name: Publish Test Container uses: actions/upload-artifact@v7 if: ${{ inputs.mode == 'build-only' }} @@ -96,6 +107,15 @@ jobs: name: ${{ inputs.test-image-artifact-name }} path: eventstore-test.tar if-no-files-found: error + retention-days: 1 + - name: Publish Container + uses: actions/upload-artifact@v7 + if: ${{ inputs.mode == 'build-only' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }} + with: + name: ${{ inputs.image-artifact-name }} + path: eventstore.tar + if-no-files-found: error + retention-days: 1 - name: Download Test Container uses: actions/download-artifact@v8 if: ${{ inputs.mode == 'test-only' }} @@ -110,7 +130,7 @@ jobs: run: | docker run \ --env TEST_GROUP="${{ inputs.test-group-name }}" \ - --volume $(pwd)/test-results:/build/test-results \ + --volume "$(pwd)/test-results:/build/test-results" \ --rm \ eventstore-test - name: Normalize Test Result Permissions diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index 189396b04..8cdc48413 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -30,6 +30,7 @@ jobs: runtime: linux-x64 mode: build-only test-image-artifact-name: eventstore-test-image-noble-linux-x64 + image-artifact-name: eventstore-image-noble-linux-x64 secrets: inherit test: @@ -60,3 +61,17 @@ jobs: test-image-artifact-name: eventstore-test-image-noble-linux-x64 test-group-name: ${{ matrix.test-group-name }} secrets: inherit + + publish-image: + name: Publish Image + if: ${{ github.repository == 'TrogonStack/TrogonEventStore' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')) }} + needs: + - build-images + - test + permissions: + contents: read + packages: write + uses: ./.github/workflows/tag-docker-images.yml + with: + image-artifact-name: eventstore-image-noble-linux-x64 + secrets: inherit diff --git a/.github/workflows/tag-docker-images.yml b/.github/workflows/tag-docker-images.yml index 4ee909641..a0368b791 100644 --- a/.github/workflows/tag-docker-images.yml +++ b/.github/workflows/tag-docker-images.yml @@ -1,62 +1,54 @@ -name: Tag Docker Images +name: Publish Container Image + on: - schedule: - - cron: "0 3 * * *" # Every day at 3am UTC. - workflow_dispatch: + workflow_call: + inputs: + image-artifact-name: + required: true + type: string + jobs: - tag-docker-images: - if: github.repository == 'TrogonStack/TrogonEventStore' + publish-container-image: + name: Publish runs-on: ubuntu-latest - permissions: - contents: read - packages: write steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + - name: Download Container + uses: actions/download-artifact@v8 + with: + name: ${{ inputs.image-artifact-name }} + - name: Load Container + run: docker load --input eventstore.tar - name: Login to GitHub Container Registry uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Tag and Push - uses: EventStore/Automations/tag-docker-images@master + - name: Publish Container env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + IMAGE: ghcr.io/trogonstack/trogoneventstore + run: | + tags=() - - uses: actions/github-script@v9 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: 'TrogonStack', - repo: 'EventStore-Client-gRPC-TestData', - workflow_id: 'build.yml', - ref: 'master', - inputs: { - 'repository': "'"$GITHUB_REPOSITORY"'", - 'version': 'previous-lts' - } - }); + if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then + tags+=(ci) + elif [[ "$GITHUB_REF" =~ ^refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9A-Za-z.-]+)?$ ]]; then + major="${BASH_REMATCH[1]}" + minor="${BASH_REMATCH[2]}" + version="${GITHUB_REF_NAME#v}" + tags+=("$version") - await github.rest.actions.createWorkflowDispatch({ - owner: 'TrogonStack', - repo: 'EventStore-Client-gRPC-TestData', - workflow_id: 'build.yml', - ref: 'master', - inputs: { - 'repository': "'"$GITHUB_REPOSITORY"'", - 'version': 'lts' - } - }); + if [[ "$version" == *-* ]]; then + tags+=(preview) + else + tags+=("$major.$minor" "$major" stable) + fi + else + echo "Unsupported image publication ref: $GITHUB_REF" >&2 + exit 1 + fi - await github.rest.actions.createWorkflowDispatch({ - owner: 'TrogonStack', - repo: 'EventStore-Client-gRPC-TestData', - workflow_id: 'build.yml', - ref: 'master', - inputs: { - 'repository': "'"$GITHUB_REPOSITORY"'", - 'version': 'ci' - } - }); + for tag in "${tags[@]}"; do + docker tag eventstore "$IMAGE:$tag" + docker push "$IMAGE:$tag" + done diff --git a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs index e61ba619e..c070aac18 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs @@ -670,13 +670,23 @@ private async Task WriteExistingEvents() throw new Exception("Ephemeral streams cannot be written to."); } - var numEvents = StreamProps.NumEvents; - for (int i = 0; i < numEvents; i++) + if (StreamProps.NumEvents > 0) { - await WriteEvent(); + await WriteEvents(StreamProps.NumEvents); } } + private async Task WriteEvents(int count) + { + var events = new EventData[count]; + for (var i = 0; i < count; i++) + { + events[i] = new EventData(Guid.NewGuid(), "type", true, "{}"u8.ToArray(), Array.Empty()); + } + + await NodeConnection.AppendToStreamAsync(_stream, ExpectedVersion.Any, events); + } + private async Task WriteEvent(string stream, string eventType, string data, string metadata) { data ??= string.Empty; @@ -765,10 +775,7 @@ private Task ApplyTombstone() } numEventsAdded = LiveProps.NumEventsToAdd; - for (var i = 0; i < numEventsAdded; i++) - { - await WriteEvent(); - } + await WriteEvents(numEventsAdded); } else if (LiveProps.SoftDeleteStream) { @@ -803,10 +810,7 @@ private Task ApplyTombstone() else if (LiveProps.FallBehindThenCatchUp) { numEventsAdded = NumEventsToFallBehind; - for (var i = 0; i < NumEventsToFallBehind; i++) - { - await WriteEvent(); - } + await WriteEvents(NumEventsToFallBehind); shouldFallBehindThenCatchup = true; } @@ -929,10 +933,9 @@ public async Task SetUp() } [Test] - [Retry(3)] public async Task enumeration_is_correct() { - var sub = Subscribe(); + await using var sub = Subscribe(); if (HardDeleted) {