Skip to content
edgesync Artifact [0eaf3c23dc]
Login

Artifact [0eaf3c23dc]

Artifact 0eaf3c23dc42c606fb43b680bd95a453492c1a1ad4db213b29254e90b0b2cff5:


name: Bump upstream
on:
  repository_dispatch:
    types: [upstream-bump]
  workflow_dispatch:
    inputs:
      repo:        { required: true,  type: string }
      module_path: { required: true,  type: string }
      version:     { required: true,  type: string }
      release_url: { required: false, type: string }
      tag_sha:     { required: false, type: string }
      actor:       { required: false, type: string }

permissions:
  contents: write
  pull-requests: write

jobs:
  bump:
    runs-on: ubuntu-latest
    env:
      EXPECTED_REPO: libfossil
      EXPECTED_MODULE: github.com/danmestas/libfossil
    steps:
      - name: Resolve inputs
        id: in
        env:
          PAYLOAD_REPO: ${{ github.event.client_payload.repo }}
          PAYLOAD_MODULE: ${{ github.event.client_payload.module_path }}
          PAYLOAD_VERSION: ${{ github.event.client_payload.version }}
          PAYLOAD_RELEASE_URL: ${{ github.event.client_payload.release_url }}
          PAYLOAD_TAG_SHA: ${{ github.event.client_payload.tag_sha }}
          PAYLOAD_ACTOR: ${{ github.event.client_payload.actor }}
          INPUT_REPO: ${{ inputs.repo }}
          INPUT_MODULE: ${{ inputs.module_path }}
          INPUT_VERSION: ${{ inputs.version }}
          INPUT_RELEASE_URL: ${{ inputs.release_url }}
          INPUT_TAG_SHA: ${{ inputs.tag_sha }}
          INPUT_ACTOR: ${{ inputs.actor }}
        run: |
          REPO="${PAYLOAD_REPO:-$INPUT_REPO}"
          MODULE_PATH="${PAYLOAD_MODULE:-$INPUT_MODULE}"
          VERSION="${PAYLOAD_VERSION:-$INPUT_VERSION}"
          RELEASE_URL="${PAYLOAD_RELEASE_URL:-$INPUT_RELEASE_URL}"
          TAG_SHA="${PAYLOAD_TAG_SHA:-$INPUT_TAG_SHA}"
          ACTOR="${PAYLOAD_ACTOR:-${INPUT_ACTOR:-${{ github.actor }}}}"

          if [[ "$REPO" != "$EXPECTED_REPO" ]]; then
            echo "::notice::Ignoring dispatch for repo=$REPO (expected $EXPECTED_REPO)"
            echo "skip=true" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          if [[ "$MODULE_PATH" != "$EXPECTED_MODULE" ]]; then
            echo "::error::module_path mismatch: got '$MODULE_PATH', expected '$EXPECTED_MODULE'"
            exit 1
          fi
          if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.+)?$ ]]; then
            echo "::error::bad version: '$VERSION'"
            exit 1
          fi

          {
            echo "repo=$REPO"
            echo "module=$MODULE_PATH"
            echo "version=$VERSION"
            echo "release_url=$RELEASE_URL"
            echo "tag_sha=$TAG_SHA"
            echo "actor=$ACTOR"
            echo "branch=chore/bump-${REPO}-${VERSION}"
          } >> "$GITHUB_OUTPUT"

      - name: Idempotency check
        if: steps.in.outputs.skip != 'true'
        id: idem
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          BRANCH: ${{ steps.in.outputs.branch }}
        run: |
          if gh pr list --repo "${{ github.repository }}" --head "$BRANCH" --state open --json number -q '.[0].number' | grep -q .; then
            echo "exists=true" >> "$GITHUB_OUTPUT"
            echo "::notice::PR already open for $BRANCH; skipping"
          fi

      - if: steps.in.outputs.skip != 'true' && steps.idem.outputs.exists != 'true'
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - if: steps.in.outputs.skip != 'true' && steps.idem.outputs.exists != 'true'
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod

      - if: steps.in.outputs.skip != 'true' && steps.idem.outputs.exists != 'true'
        name: Bump module in every go.mod
        id: bump
        env:
          BRANCH: ${{ steps.in.outputs.branch }}
          MODULE: ${{ steps.in.outputs.module }}
          VERSION: ${{ steps.in.outputs.version }}
        run: |
          git switch -c "$BRANCH"

          # Walk every go.mod in the repo. Skip modules that don't depend on
          # $MODULE (otherwise `go get` would add it as a new dep).
          mapfile -t GOMODS < <(find . -name go.mod -not -path './.git/*' -not -path './node_modules/*' | sort)
          echo "::notice::Found ${#GOMODS[@]} go.mod file(s): ${GOMODS[*]}"

          any_changed=false
          for gomod in "${GOMODS[@]}"; do
            d=$(dirname "$gomod")
            if ! grep -qE "^[[:space:]]*${MODULE}([[:space:]]|$)" "$gomod"; then
              echo "::notice::${d}: ${MODULE} not a direct dep — skipping"
              continue
            fi
            pushd "$d" >/dev/null
            for i in 1 2 3; do
              if go get "${MODULE}@${VERSION}"; then
                break
              fi
              if [ "$i" -lt 3 ]; then
                echo "::notice::${d}: go get failed (attempt ${i}/3) — likely GOPROXY propagation race; retrying in 15s..."
                sleep 15
              else
                echo "::error::${d}: go get failed after 3 attempts"
                exit 1
              fi
            done
            go mod tidy
            if ! git diff --quiet go.mod go.sum; then
              any_changed=true
              echo "::notice::${d}: bumped"
            else
              echo "::notice::${d}: already at ${VERSION}, no diff"
            fi
            popd >/dev/null
          done

          if [ "$any_changed" = "false" ]; then
            echo "::notice::No-op bump — version already pinned in every module"
            echo "noop=true" >> "$GITHUB_OUTPUT"
          fi

      - if: steps.in.outputs.skip != 'true' && steps.idem.outputs.exists != 'true' && steps.bump.outputs.noop != 'true'
        name: Verify build for every module
        run: |
          mapfile -t GOMODS < <(find . -name go.mod -not -path './.git/*' -not -path './node_modules/*' | sort)
          for gomod in "${GOMODS[@]}"; do
            d=$(dirname "$gomod")
            echo "::group::go build ./... in ${d}"
            (cd "$d" && go build ./...)
            echo "::endgroup::"
          done

      - if: steps.in.outputs.skip != 'true' && steps.idem.outputs.exists != 'true' && steps.bump.outputs.noop != 'true'
        name: Commit + push + open PR
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          BRANCH: ${{ steps.in.outputs.branch }}
          REPO: ${{ steps.in.outputs.repo }}
          VERSION: ${{ steps.in.outputs.version }}
          MODULE: ${{ steps.in.outputs.module }}
          RELEASE_URL: ${{ steps.in.outputs.release_url }}
          TAG_SHA: ${{ steps.in.outputs.tag_sha }}
          ACTOR: ${{ steps.in.outputs.actor }}
        run: |
          git config user.name  "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          # Stage every changed go.mod and go.sum across the repo.
          mapfile -t GOMODS < <(find . -name go.mod -not -path './.git/*' -not -path './node_modules/*' | sort)
          for gomod in "${GOMODS[@]}"; do
            d=$(dirname "$gomod")
            git add "${d}/go.mod" "${d}/go.sum" 2>/dev/null || true
          done
          git commit -m "chore: bump ${REPO} → ${VERSION}"
          git push -u origin "$BRANCH"
          gh pr create \
            --title "chore: bump ${REPO} → ${VERSION}" \
            --body "Auto-bump from upstream release.

          - Module: \`${MODULE}\`
          - Version: \`${VERSION}\`
          - Upstream release: ${RELEASE_URL}
          - Tag SHA: \`${TAG_SHA}\`
          - Triggered by: @${ACTOR}

          Updated every \`go.mod\` in the repo that depends on \`${MODULE}\`. CI must pass before merge. Review \`go.sum\` diffs for transitive surprises."