diff --git a/.github/workflows/deploy-avs.yml b/.github/workflows/deploy-avs.yml new file mode 100644 index 00000000..c904d919 --- /dev/null +++ b/.github/workflows/deploy-avs.yml @@ -0,0 +1,57 @@ +name: Manual AVS Deploy + +on: + workflow_dispatch: + inputs: + environment: + description: 'Target environment for deployment' + required: true + type: choice + options: + - ethereum + - sepolia + - base + - base-sepolia + +jobs: + aggregator: + runs-on: ubuntu-latest + environment: + name: ${{ inputs.environment }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: SSH and Deploy + uses: appleboy/ssh-action@v0.1.5 + with: + host: ${{ secrets.AVS_SERVER_HOST }} + username: ava + key: ${{ secrets.AVS_SSH_KEY }} + script: | + echo "Deploying to environment: ${{ inputs.environment }}" + cd $HOME/ap-aggregator-setup/${{ inputs.environment }} + docker compose pull + docker compose up -d --force-recreate + + operator: + runs-on: ubuntu-latest + environment: + name: ${{ inputs.environment }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: SSH and Deploy + uses: appleboy/ssh-action@v0.1.5 + with: + host: ${{ secrets.AVS_SERVER_HOST }} + username: ava + key: ${{ secrets.AVS_SSH_KEY }} + script: | + echo "Deploying to environment: ${{ inputs.environment }}" + cd $HOME/ap-operator-setup/${{ inputs.environment == 'sepolia' && 'holesky' || inputs.environment }} + docker compose pull + docker compose up -d --force-recreate diff --git a/.github/workflows/prerelease-and-update-docker-on-main.yml b/.github/workflows/prerelease-and-update-docker-on-main.yml new file mode 100644 index 00000000..3b3f9829 --- /dev/null +++ b/.github/workflows/prerelease-and-update-docker-on-main.yml @@ -0,0 +1,140 @@ +name: Build and Publish PR Pre-release Docker + +on: + pull_request: + types: + - opened + - synchronize + branches: + - main + +jobs: + build-and-publish-pr-docker: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - name: Checkout repository with full history + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required for go-semantic-release + + - name: Setup Go environment + uses: actions/setup-go@v4 + with: + go-version: '1.22' + + - name: Run Go linter + uses: golangci/golangci-lint-action@v3 + with: + version: latest + + - name: Run Go tests + run: go test -v ./... + + - name: Generate commit summary + id: git-summary + run: | + echo "## Summary" >> commit-summary.md + echo "" >> commit-summary.md + FEATURES=$(git log $(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)..HEAD --pretty=format:"%s" --no-merges | grep -i "^feat" | wc -l) + FIXES=$(git log $(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)..HEAD --pretty=format:"%s" --no-merges | grep -i "^fix" | wc -l) + DOCS=$(git log $(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)..HEAD --pretty=format:"%s" --no-merges | grep -i "^docs" | wc -l) + echo "* $FEATURES new features" >> commit-summary.md + echo "* $FIXES bug fixes" >> commit-summary.md + echo "* $DOCS documentation updates" >> commit-summary.md + echo "" >> commit-summary.md + echo "## Changes since last release" >> commit-summary.md + echo "" >> commit-summary.md + git log $(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)..HEAD --pretty=format:"* %s (%h)" --no-merges >> commit-summary.md + echo "COMMIT_SUMMARY<> $GITHUB_ENV + cat commit-summary.md >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Run semantic versioning and create pre-release + id: semantic-release + uses: go-semantic-release/action@v1 + with: + hooks: goreleaser + changelog: ${{ env.COMMIT_SUMMARY }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Format Pre-release Version for Docker Tag + id: formatted-version + if: steps.semantic-release.outputs.new_release_published == 'true' + run: | + SEMREL_VERSION="${{ steps.semantic-release.outputs.new_version }}" + # Extract base version (e.g., v1.2.3) + BASE_VERSION=$(echo "$SEMREL_VERSION" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+') + # Extract the part after the base version and the first hyphen (the pre-release suffix) + ORIGINAL_SUFFIX=$(echo "$SEMREL_VERSION" | sed -E "s/^${BASE_VERSION}-?(.*)$/\1/") + + if [[ -z "$BASE_VERSION" ]]; then # Should not happen with semantic versions + echo "Error: Could not parse base version from $SEMREL_VERSION" + exit 1 + fi + + if [[ -z "$ORIGINAL_SUFFIX" ]]; then # Not a pre-release, or unexpected format + # This case should ideally not occur for a PR pre-release workflow. + # Defaulting to base version with -rc.0 if suffix is missing. + FINAL_DOCKER_TAG="${BASE_VERSION}-rc.0" + elif [[ "$ORIGINAL_SUFFIX" == rc\.* ]]; then + # Already in the desired rc.N format (e.g., rc.1, rc.10) + FINAL_DOCKER_TAG="$SEMREL_VERSION" + else + # Extract trailing digits from the original suffix for the increment + INCREMENT=$(echo "$ORIGINAL_SUFFIX" | grep -oE '[0-9]+$' || echo "0") + FINAL_DOCKER_TAG="${BASE_VERSION}-rc.${INCREMENT}" + fi + echo "Original semantic-release version: $SEMREL_VERSION" + echo "Formatted Docker tag: $FINAL_DOCKER_TAG" + echo "docker_tag=${FINAL_DOCKER_TAG}" >> "$GITHUB_OUTPUT" + shell: bash + + - name: Print Formatted Docker Tag (Debug) + if: steps.semantic-release.outputs.new_release_published == 'true' + run: | + echo "The formatted Docker tag that will be used is: ${{ steps.formatted-version.outputs.docker_tag }}" + + - name: Login to Docker Hub + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up QEMU + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/setup-buildx-action@v3 + + - name: Docker meta + id: meta + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/metadata-action@v5 + with: + images: | + avaprotocol/ap-avs + tags: | + # Only tag with the formatted pre-release version number + type=raw,value=${{ steps.formatted-version.outputs.docker_tag }} + + - name: Build and push Docker image + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/build-push-action@v6 + with: + build-args: | + RELEASE_TAG=${{ steps.semantic-release.outputs.new_version }} + platforms: linux/amd64,linux/arm64 + context: . + file: dockerfiles/operator.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/publish-dev-docker-on-staging.yml b/.github/workflows/publish-dev-docker-on-staging.yml index 37552030..f659bb00 100644 --- a/.github/workflows/publish-dev-docker-on-staging.yml +++ b/.github/workflows/publish-dev-docker-on-staging.yml @@ -1,7 +1,8 @@ name: Publish Dev Docker image upon staging change on: - push: + pull_request: + types: [closed] branches: - staging paths: @@ -12,8 +13,9 @@ on: workflow_dispatch: jobs: - publish-dev-build: - name: Publish dev build docker image to dockerhub + publish-staging-build: + if: github.event.pull_request.merged == true + name: Publish staging build docker image to dockerhub runs-on: 'ubuntu-latest' steps: - name: Login to Docker Hub @@ -24,6 +26,28 @@ jobs: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 # Ensure fetch-depth is 0 for git log + + - name: Determine Tag Prefix from Source Branch + id: vars + run: | + RAW_BRANCH_NAME="${{ github.event.pull_request.head.ref }}" # Get source branch directly + TAG_PREFIX="" + + if [[ -n "$RAW_BRANCH_NAME" ]]; then + # Sanitize the branch name for use in a Docker tag: + # 1. Replace '/' with '-' + # 2. Keep only alphanumeric characters, '.', and '-' + # 3. Replace sequences of invalid characters with a single '-' + # 4. Remove leading/trailing '-' + SANITIZED_NAME=$(echo "$RAW_BRANCH_NAME" | sed 's|/|-|g' | tr -cs 'a-zA-Z0-9.-' '-' | sed 's/--\\+/-/g' | sed 's/^-*//;s/-*$//') + if [[ -n "$SANITIZED_NAME" ]]; then + TAG_PREFIX="$SANITIZED_NAME" + fi + fi + echo "TAG_PREFIX=${TAG_PREFIX}" >> $GITHUB_OUTPUT + shell: bash - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -35,26 +59,21 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - # This is a dedicated repository to house the development/preview build images: | avaprotocol/avs-dev tags: | - type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} - type=raw,value={{sha}} - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - - name: Build and push avs-dev docker image + type=pr # Generates pr-123 from the PR number + type=raw,value={{sha}} # Full commit SHA + + - name: Build and push staging docker image uses: docker/build-push-action@v6 with: build-args: | - RELEASE_TAG=${{ inputs.tag || github.sha || github.head_ref || github.ref_name }} + RELEASE_TAG=${{ steps.vars.outputs.TAG_PREFIX }}${{ steps.vars.outputs.TAG_PREFIX:+'-' }}${{ github.sha }} COMMIT_SHA=${{ github.sha }} platforms: linux/amd64,linux/arm64 context: . file: dockerfiles/operator.Dockerfile - push: ${{ github.event_name != 'pull_request' }} + push: true # Always push when this job runs (merged PR to staging) tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/release-on-pr-close.yml b/.github/workflows/release-on-pr-close.yml new file mode 100644 index 00000000..80aa6007 --- /dev/null +++ b/.github/workflows/release-on-pr-close.yml @@ -0,0 +1,118 @@ +name: Create Release and Publish Docker on Main Merge + +on: + pull_request: + types: [closed] + branches: + - main + +jobs: + release-and-publish: + # Only run this job if the PR was merged + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + permissions: + contents: write # Needed for go-semantic-release to create tags and releases + packages: write # If you use GitHub Packages for Docker images (good to have) + steps: + - name: Checkout repository with full history + uses: actions/checkout@v4 + with: + # Fetch all history so go-semantic-release can determine the version based on all commits + fetch-depth: 0 + # We need to check out the main branch itself after the merge, not the PR ref + ref: main + + - name: Setup Go environment + uses: actions/setup-go@v4 + with: + go-version: '1.22' # Or your project's Go version + + # Optional: Add linting and testing steps here if you want to be absolutely sure + # before a release, though these should ideally be covered by PR checks. + # - name: Run Go linter + # uses: golangci/golangci-lint-action@v3 + # with: + # version: latest + # - name: Run Go tests + # run: go test -v ./... + + - name: Generate commit summary for changelog + id: git-summary + run: | + echo "## Summary" >> commit-summary.md + echo "" >> commit-summary.md + # Correctly get commits since the last full tag on the main branch + LAST_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || git rev-list --max-parents=0 HEAD) + echo "Last tag for changelog: $LAST_TAG" + FEATURES=$(git log $LAST_TAG..HEAD --pretty=format:"%s" --no-merges | grep -i "^feat" | wc -l) + FIXES=$(git log $LAST_TAG..HEAD --pretty=format:"%s" --no-merges | grep -i "^fix" | wc -l) + DOCS=$(git log $LAST_TAG..HEAD --pretty=format:"%s" --no-merges | grep -i "^docs" | wc -l) + echo "* $FEATURES new features" >> commit-summary.md + echo "* $FIXES bug fixes" >> commit-summary.md + echo "* $DOCS documentation updates" >> commit-summary.md + echo "" >> commit-summary.md + echo "## Changes since last release ($LAST_TAG)" >> commit-summary.md + echo "" >> commit-summary.md + git log $LAST_TAG..HEAD --pretty=format:"* %s (%h)" --no-merges >> commit-summary.md + echo "COMMIT_SUMMARY<> $GITHUB_ENV + cat commit-summary.md >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + shell: bash + + - name: Run semantic versioning and create full release + id: semantic-release + uses: go-semantic-release/action@v1 + with: + # No pre-release flag needed; it should detect it's on main and do a full release + hooks: goreleaser # Or other hooks you use for release asset generation + changelog: ${{ env.COMMIT_SUMMARY }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Ensure these secrets are set in your repository for DockerHub push + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + + # Docker build and push steps, only if a new release was published + - name: Login to Docker Hub + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up QEMU + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/setup-buildx-action@v3 + + - name: Docker meta + id: meta + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/metadata-action@v5 + with: + images: | + avaprotocol/ap-avs + tags: | + type=raw,value=latest # Push 'latest' tag + type=raw,value=${{ steps.semantic-release.outputs.new_version }} # Push the full version tag (e.g., v1.2.3) + type=semver,pattern={{version}} # Full semver (e.g., v1.2.3) + type=semver,pattern={{major}}.{{minor}} # Major.minor (e.g., v1.2) + type=semver,pattern={{major}} # Major only (e.g., v1) + type=raw,value={{sha}} # Commit SHA + + - name: Build and push Docker image + if: steps.semantic-release.outputs.new_release_published == 'true' + uses: docker/build-push-action@v6 + with: + build-args: | + RELEASE_TAG=${{ steps.semantic-release.outputs.new_version }} + platforms: linux/amd64,linux/arm64 + context: . + file: dockerfiles/operator.Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/test.yml b/.github/workflows/run-test-on-pr.yml similarity index 64% rename from .github/workflows/test.yml rename to .github/workflows/run-test-on-pr.yml index 0e69c777..94ca643b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/run-test-on-pr.yml @@ -1,8 +1,29 @@ -name: Run Tests +name: Run Tests on PR or on-demand on: - push: + pull_request: + # Only run tests when PR to staging of main and these files are changed + branches: + - main + - staging + paths: + - "**.go" + - "go.mod" + - "go.sum" + - "core/**" + - "pkg/**" + - "aggregator/**" + - "operator/**" + - "model/**" + - "cmd/**" + - "version/**" + - "config/**" workflow_dispatch: + inputs: + branch: + description: "Branch to run tests against" + required: true + type: string jobs: test: @@ -28,6 +49,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: recursive + ref: ${{ github.event.inputs.branch || github.ref }} - name: Run Go test env: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b6b77126..6c838a19 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,24 +7,61 @@ repos: language: system types: [go] pass_filenames: false - + - id: go-mod-tidy name: go mod tidy entry: go mod tidy language: system types: [go] pass_filenames: false - + - id: go-vet name: go vet entry: go vet ./... language: system types: [go] pass_filenames: false - + - id: golangci-lint name: golangci-lint entry: golangci-lint run language: system types: [go] pass_filenames: false + + - id: conventional-commit-check + name: Conventional Commit Message Check + entry: |- + bash -c ' + COMMIT_MSG_FILE="$1" + FIRST_LINE=$(head -n1 "$COMMIT_MSG_FILE") + # Regex for conventional commit: type(optional_scope)!: subject + # Allowed types: feat, fix, docs, style, refactor, perf, test, chore, ci, build, revert + # Allows an optional scope in parentheses e.g. feat(parser): + # Allows an optional ! for breaking change e.g. feat!: + # Requires a colon and a space after type/scope/!. + # Requires some subject text. + PATTERN="^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\([\w\s-]+\))?!?:\[[:space:]].+" + if ! grep -Eq "$PATTERN" <<< "$FIRST_LINE"; then + echo "-------------------------------------------------------------------" + echo "ERROR: Commit message does not follow Conventional Commits format." + echo "-------------------------------------------------------------------" + echo "It must start with a type like feat, fix, docs, etc.," + echo "followed by an optional scope in parentheses (e.g. (api), (ui))," + echo "an optional exclamation mark for breaking changes (!)," + echo "a colon and a single space, and then the subject." + echo "" + echo "Examples:" + echo " feat: add new user authentication feature" + echo " fix(parser): correctly handle empty input" + echo " docs!: update API documentation for breaking change" + echo "" + echo "Allowed types: feat, fix, docs, style, refactor, perf, test, chore, ci, build, revert" + echo "For more details, see: https://www.conventionalcommits.org/" + echo "-------------------------------------------------------------------" + exit 1 + fi + ' + language: system + stages: [commit-msg] + pass_filenames: true # The script expects the filename as $1 diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..68484764 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,104 @@ +# AP-AVS Release Process + +## Overview + +The release process follows a staged approach from development to production, with specific Docker image management for each stage, automated via GitHub Actions. + +## Branch and Docker Image Alignment + +| Environment | Github Branch | Docker Image Repository | Docker Tag Format | Managed By | Trigger Condition | +| ----------------------- | --------------- | ---------------------------- | ------------------------------------- | --------------------------------------------------- | -------------------------------------------------------- | +| Development | `dev` | N/A | N/A | N/A | N/A | +| Staging | `staging` | `avaprotocol/avs-dev` | `pr-XXX` (PR number) | `publish-dev-docker-on-staging.yml` | Automatic on PR merge to `staging` | +| **Production (PR)** | `main` (PR) | `avaprotocol/ap-avs` | `vX.Y.Z-rc.N` (pre-release) | `prerelease-and-update-docker-on-main.yml` | Automatic on PR opened/synchronized to `main` | +| **Production (Release)**| `main` (Merged) | `avaprotocol/ap-avs` | `vX.Y.Z` (stable), `latest`, etc. | `release-on-pr-close.yml` | Automatic on PR merged to `main` | + +### Environment Details + +- **Staging Branch (`staging`)**: + - Serves as a testing and integration pool before changes are promoted. + - The `avaprotocol/avs-dev` Docker image is updated with a `pr-XXX` tag (where XXX is the PR number) after each pull request from a `dev` branch is merged into `staging`. This is handled by the `publish-dev-docker-on-staging.yml` workflow. + - Changes in `staging` are considered pre-production. + +- **Main Branch (`main`)**: + - This branch reflects production-ready code. The release process for `main` is two-phased: + 1. **Pre-release on Pull Request**: When a Pull Request is opened or updated targeting `main`, the `prerelease-and-update-docker-on-main.yml` workflow automatically builds a pre-release Docker image tagged like `vX.Y.Z-rc.N` (e.g., `v0.5.0-rc.1`) and creates a corresponding GitHub pre-release. This allows for testing the exact build that could become a full release. + 2. **Full Release on Merge**: Upon merging a Pull Request into `main`, the `release-on-pr-close.yml` workflow is triggered. This workflow creates a full GitHub Release with a stable semantic version (e.g., `v0.5.0`), and builds and publishes the `avaprotocol/ap-avs` Docker image tagged with this stable version, `latest`, and other semantic version tags (e.g., `v0.5`). + - Deployments to production environments are typically done manually after a stable version is released. + +- **Mainnet Branch (`mainnet`)**: + - Uses non-fast-forward merge to preserve mainnet-specific changes (e.g., different EigenSDK version). + - After testing the stable Docker image (from `main` branch releases) on test environments, a separate process (potentially a manual trigger of a dedicated workflow like `update-docker-mainnet`, not detailed in the provided workflow files) is used to update the `avaprotocol/ap-avs:mainnet` tag. + - Operators automatically download and upgrade their images when the `mainnet` tag is updated. + - Requires manual intervention for promoting a version to the `mainnet` Docker tag and for deployment. + +### Important Notes (EigenSDK on Mainnet) + +Due to the EigenSDK package difference on Ethereum mainnet, the implementation of the EigenSDK on the `mainnet` branch has diverged from `main`. For updating the `avaprotocol/ap-avs:mainnet` Docker image, a dedicated process (e.g., the `update-docker-mainnet` workflow) typically performs the following: + +1. Merge the working code from `main` (or a specific release tag) to `mainnet`. +2. Build a Docker image from the `mainnet` branch. +3. Publish this Docker image to `avaprotocol/ap-avs:mainnet`. + +## Versioning and Releasing + +The versioning process is automated using `go-semantic-release` within GitHub Actions. + +### Commit Message Format + +For versioning to work correctly, commit messages **must** follow the Conventional Commits format: + +- `feat:` for new features (results in a minor version bump for stable releases, or contributes to pre-release versioning). +- `fix:` for bug fixes (results in a patch version bump for stable releases, or contributes to pre-release versioning). +- `docs:` for documentation changes (typically no version bump by `go-semantic-release` unless configured otherwise). +- `BREAKING CHANGE:` in the commit body, or `type!:` (e.g., `feat!:`) for breaking changes (results in a major version bump for stable releases). + +### Release Process for `main` Branch + +The release process targeting the `main` branch involves two key automated workflows: + +**A. Pre-release on Pull Request to `main` (via `prerelease-and-update-docker-on-main.yml`)** + +This workflow triggers when a Pull Request is opened or synchronized against the `main` branch: + +1. **Pre-release Version Determination**: + - `go-semantic-release` analyzes commit messages in the PR. + - Determines the next pre-release version number (e.g., `v0.5.0-rc.1`, where `rc.1` might increment on subsequent pushes to the PR). + +2. **Pre-release Creation**: + - Creates a GitHub Pre-Release with the new pre-release version. + - Creates a Git tag for the pre-release version (e.g., `v0.5.0-rc.1`). + +3. **Docker Image Build and Push**: + - Builds a Docker image from the PR's code. + - Publishes the image to `avaprotocol/ap-avs` tagged only with the specific pre-release version (e.g., `avaprotocol/ap-avs:v0.5.0-rc.1`). The `latest` tag is **not** updated at this stage. + +**B. Full Release on Pull Request Merge to `main` (via `release-on-pr-close.yml`)** + +This workflow triggers when a Pull Request is closed and merged into the `main` branch: + +1. **Stable Version Determination**: + - `go-semantic-release` analyzes commit messages on the `main` branch since the last stable release. + - Determines the next stable semantic version number (e.g., `v0.5.0`). + +2. **Full Release Creation**: + - Creates a GitHub Full Release with the new stable version and a generated changelog. + - Creates a Git tag for the stable version (e.g., `v0.5.0`). + +3. **Docker Image Build and Push**: + - Builds a Docker image from the merged code on `main`. + - Publishes the image to `avaprotocol/ap-avs` with multiple tags: + - The stable version (e.g., `avaprotocol/ap-avs:v0.5.0`). + - `latest` (i.e., `avaprotocol/ap-avs:latest`). + - Other semantic tags like major.minor (e.g., `v0.5`) and major (e.g., `v0`). + +### Deployment + +- Deployments to various environments (e.g., `Sepolia`, `Base Sepolia`, `Ethereum`, `Base`) are handled by the **manual** GitHub Action workflow: `deploy-avs.yml`. +- To deploy, a user manually triggers this workflow, selecting the target environment and the version (implicitly by deploying the code from a specific branch, usually `main` for production environments after a release, or `staging` for staging environments). +- Each environment uses the same Docker image version (for a given release) but varies in runtime configuration. + +### Important Notes + +- Docker images are tagged as described above: pre-releases for PRs to `main`, and stable versions + `latest` upon merge to `main`. +- Git tags (both for pre-releases and full releases) are created **automatically** by `go-semantic-release` as part of the respective workflows.