Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .changeset/clever-towns-tan.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/flat-worms-teach.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/gold-turtles-chew.md

This file was deleted.

46 changes: 46 additions & 0 deletions .github/actions/dispatch-release-fanout/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# dispatch-release-fanout

Tells the internal deployment hub which images a release published. The hub
classifies each tag into a release channel and dispatches the image-bump
workflow in every deployment repo subscribed to that `(stream, channel)`, which
opens the tag-bump PR there.

One dispatch carries every image the build published. This repo holds no
deployment topology: which repo consumes `core` vs `ccip`, and which channel
each environment tracks, is configured in the hub and in the deployment repos.

```yaml
- uses: ./.github/actions/dispatch-release-fanout
with:
gati-profile: ${{ secrets.GATI_PROFILE_IMAGE_BUMP_HUB }}
hub-repo: ${{ secrets.REPO_IMAGE_BUMP_HUB }}
images: |
core=2.65.1-rc.0
ccip=2.65.1-ccip-rc.0
```

## Required secrets

This repository is public and the hub is internal, so neither the hub's name
nor the GATI profile that names it is committed here.

| Secret | Value |
| ------------------------------ | --------------------------------------------------------------------------- |
| `REPO_IMAGE_BUMP_HUB` | `owner/name` of the hub repository |
| `GATI_PROFILE_IMAGE_BUMP_HUB` | GATI v2 profile granting `actions:write` on that repo (a `presets/…` entry) |

The calling job needs `id-token: write` — the profile is minted from this
repository's OIDC identity, and grants nothing beyond starting one workflow in
the hub. It cannot read or write any repository's contents.

## Behaviour

- `images` entries with an empty tag are dropped, so a caller can pass an image
whose build failed or was skipped without special-casing it. If every entry
is empty the action warns and exits 0 without dispatching.
- A tag which is unrecognized by the hub for its stream is *not routed*
and does not fail: the hub warns and still delivers the other images in the
same dispatch.
- The dispatch is fire-and-forget. It returns as soon as the hub accepts it,
and reports nothing about what the hub did — read the hub's own run, linked
by the `correlation-id` in this action's job summary.
118 changes: 118 additions & 0 deletions .github/actions/dispatch-release-fanout/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: dispatch-release-fanout
description:
"Tell the internal deployment hub which images this release published, so it
can open image-bump PRs in the deployment repos subscribed to them"

# The hub classifies each tag into a release channel (beta / rc / stable) and
# dispatches each subscribed deployment repo's own image-bump workflow. One
# dispatch carries every image a build published; the hub decides what is
# routed where, so this repo holds no deployment topology.
#
# The hub repository is internal and this repository is public, so its name and
# the GATI profile that reaches it are both supplied from secrets rather than
# committed here. See the README next to this file.

inputs:
gati-profile:
description:
"GATI v2 token profile granting actions:write on the hub repository.
Supply from a secret."
required: true
hub-repo:
description:
"owner/name of the hub repository to dispatch. Supply from a secret."
required: true
hub-workflow:
description: "Workflow file in the hub to dispatch."
required: false
default: "fanout.yaml"
hub-ref:
description:
"Branch in the hub to run the workflow from. The hub's own credentials
are pinned to its default branch, so overriding this will fail its token
request."
required: false
default: "main"
producer:
description:
"Producer key the hub routes on. Must match a producers/<name>.yaml entry
in the hub."
required: false
default: "smartcontractkit/chainlink"
images:
description: |
Multiline `stream=tag` pairs, one per image this build published:

core=2.65.1-rc.0
ccip=2.65.1-ccip-rc.0

`stream` must be declared by the producer in the hub. Entries with an
empty tag are dropped, so a caller can pass an image whose build was
skipped or failed without special-casing it.
required: true
correlation-id:
description:
"Opaque id traversing along every hop of the fan-out. Defaults to this
run."
required: false
source-run-url:
description: "URL of this run, recorded by every downstream hop."
required: false

runs:
using: composite
steps:
- name: Setup GitHub Token
id: setup-github-token
uses: smartcontractkit/.github/actions/setup-github-token@setup-github-token/v1
with:
profile: ${{ inputs.gati-profile }}

- name: Dispatch fan-out
shell: bash
env:
GH_TOKEN: ${{ steps.setup-github-token.outputs.access-token }}
HUB_REPO: ${{ inputs.hub-repo }}
HUB_WORKFLOW: ${{ inputs.hub-workflow }}
HUB_REF: ${{ inputs.hub-ref }}
PRODUCER: ${{ inputs.producer }}
IMAGES: ${{ inputs.images }}
CORRELATION_ID: ${{ inputs.correlation-id }}
SOURCE_RUN_URL: ${{ inputs.source-run-url }}
run: |
set -euo pipefail

images_json="$(printf '%s\n' "${IMAGES}" | jq -Rn -c '
[ inputs
| gsub("^\\s+|\\s+$"; "")
| select(length > 0)
| split("=")
| select(length == 2 and (.[1] | length) > 0)
| {stream: .[0], tag: .[1]}
]')"

if [[ "${images_json}" == "[]" ]]; then
echo "::warning::No published images to fan out; nothing dispatched."
exit 0
fi

correlation_id="${CORRELATION_ID:-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}}"
source_run_url="${SOURCE_RUN_URL:-${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}}"

gh workflow run "${HUB_WORKFLOW}" \
--repo "${HUB_REPO}" \
--ref "${HUB_REF}" \
--field "producer=${PRODUCER}" \
--field "images=${images_json}" \
--field "correlation-id=${correlation_id}" \
--field "source-run-url=${source_run_url}"

{
echo "### Release fan-out dispatched"
echo
echo "| field | value |"
echo "| --- | --- |"
echo "| producer | \`${PRODUCER}\` |"
echo "| images | \`${images_json}\` |"
echo "| correlation-id | \`${correlation_id}\` |"
} | tee -a "$GITHUB_STEP_SUMMARY"
31 changes: 31 additions & 0 deletions .github/workflows/build-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,37 @@ jobs:
repo-destination: ${{ secrets.REPO_K8S_DEPLOY }}
oci-image-tag: ${{ needs.docker-core.outputs.docker-manifest-tag }}

# Hand every image this build published to the deployment hub, which opens
# the tag-bump PRs in the deployment repos subscribed to them.
#
# This will eventually replace the `deploy` job in this workflow.
dispatch-release-fanout:
name: "Dispatch release fan-out"
needs: [checks, docker-core, docker-ccip]
# Fans out whatever published. Either image alone is a valid dispatch: the
# action drops the empty entry, and the hub routes per stream.
#
# !cancelled() rather than always(): a cancelled release must not go on to
# request deployment PRs for the images it had already pushed.
if: |
!cancelled() && (
needs.docker-core.result == 'success' ||
needs.docker-ccip.result == 'success'
)
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Dispatch release fan-out
uses: $/.github/actions/dispatch-release-fanout
with:
gati-profile: ${{ secrets.GATI_PROFILE_IMAGE_BUMP_HUB }}
hub-repo: ${{ secrets.REPO_IMAGE_BUMP_HUB }}
images: |
core=${{ needs.docker-core.result == 'success' && needs.docker-core.outputs.docker-manifest-tag || '' }}
ccip=${{ needs.docker-ccip.result == 'success' && needs.docker-ccip.outputs.docker-manifest-tag || '' }}

# Notify Slack channel for new git tags associated with pre-releases.
# Final release notifications originate from the release coordinator repo.
slack-notify:
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog Chainlink Core

## 2.67.0

### Minor Changes

- [#23854](https://github.com/smartcontractkit/chainlink/pull/23854) [`abf0837`](https://github.com/smartcontractkit/chainlink/commit/abf08375e7630761d012366e2522ca496657f294) - Added feature to the EVM BalanceMonitor to allow tracking balance of ERC-20 type fee tokens #added

- [#23762](https://github.com/smartcontractkit/chainlink/pull/23762) [`5e84e21`](https://github.com/smartcontractkit/chainlink/commit/5e84e21688d668b32e858f945013677a35d6df28) - postgres: adding support for v18, removing support for v14 #nops

- [#23799](https://github.com/smartcontractkit/chainlink/pull/23799) [`5a5156a`](https://github.com/smartcontractkit/chainlink/commit/5a5156a9230f06974e719aa5185515e8b445f6c5) - Minor bump to start next version

### Patch Changes

- [#23797](https://github.com/smartcontractkit/chainlink/pull/23797) [`e33d885`](https://github.com/smartcontractkit/chainlink/commit/e33d885589671852c40999501946b834b886313e) - #updated observation cache TTL and bridge cache-fallback test coverage

## 2.66.0

### Minor Changes
Expand Down
16 changes: 8 additions & 8 deletions deployment/ccip/view/v1_6/ccip_home.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
capabilities_registry "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0"

"github.com/smartcontractkit/chainlink-common/keystore/corekeys/p2pkey"
ccipocr3common "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
"github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"

"github.com/smartcontractkit/chainlink/deployment/ccip/shared"
"github.com/smartcontractkit/chainlink/deployment/internal/view"
Expand Down Expand Up @@ -201,12 +201,12 @@ func toCCIPHomeVersionedConfig(cfg ccip_home.CCIPHomeVersionedConfig, pluginType
peerID := p2pkey.PeerID(n.P2pId)
nodes = append(nodes, CCIPHomeOCR3Node{
P2pID: peerID.String(),
SignerKey: ccipocr3common.UnknownAddress(n.SignerKey).String(),
TransmitterKey: ccipocr3common.UnknownAddress(n.TransmitterKey).String(),
SignerKey: ccipocr3.UnknownAddress(n.SignerKey).String(),
TransmitterKey: ccipocr3.UnknownAddress(n.TransmitterKey).String(),
})
}
offRampAddr := ccipocr3common.UnknownAddress(cfg.Config.OfframpAddress).String()
rmnAddr := ccipocr3common.UnknownAddress(cfg.Config.RmnHomeAddress).String()
offRampAddr := ccipocr3.UnknownAddress(cfg.Config.OfframpAddress).String()
rmnAddr := ccipocr3.UnknownAddress(cfg.Config.RmnHomeAddress).String()
c := CCIPHomeVersionedConfig{
Version: cfg.Version,
ConfigDigest: cfg.ConfigDigest[:],
Expand Down Expand Up @@ -238,10 +238,10 @@ func populateDecodedOCRParams(config *CCIPHomeOCR3Config, ccipHomeCfg ccip_home.
identities := make([]OracleIdentity, 0, len(publicConfig.OracleIdentities))
for _, o := range publicConfig.OracleIdentities {
identities = append(identities, OracleIdentity{
OffchainPublicKey: ccipocr3common.UnknownAddress(o.OffchainPublicKey[:]).String(),
OnchainPublicKey: ccipocr3common.UnknownAddress(o.OnchainPublicKey[:]).String(),
OffchainPublicKey: ccipocr3.UnknownAddress(o.OffchainPublicKey[:]).String(),
OnchainPublicKey: ccipocr3.UnknownAddress(o.OnchainPublicKey[:]).String(),
PeerID: o.PeerID,
TransmitAccount: ccipocr3common.UnknownAddress(o.TransmitAccount[:]).String(),
TransmitAccount: ccipocr3.UnknownAddress(o.TransmitAccount[:]).String(),
})
}
if pluginType == cciptypes.PluginTypeCCIPCommit {
Expand Down
36 changes: 17 additions & 19 deletions deployment/common/view/nops.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,24 +221,22 @@ func GenerateNOPsViewV2(ctx context.Context, lggr logger.Logger, nodeIDs []strin
}

fullNodeInfo := NopNodeInfoV2{
NopView: NopView{
NodeID: node.NodeID,
PeerID: node.PeerID.String(),
IsBootstrap: node.IsBootstrap,
OCRKeys: make(map[string]OCRKeyView),
PayeeAddress: node.AdminAddr,
CSAKey: nodeDetails.PublicKey,
WorkflowKey: nodeDetails.GetWorkflowKey(),
IsConnected: nodeDetails.IsConnected,
IsEnabled: nodeDetails.IsEnabled,
Version: nodeDetails.Version,
Labels: labels,
ApprovedJobspecs: jobspecs[node.NodeID],
ProposedJobspecs: proposedSpecs[node.NodeID],
},
NodeName: nodeName,
Deployment: deploymentKey,
Networks: networks,
NodeID: node.NodeID,
PeerID: node.PeerID.String(),
IsBootstrap: node.IsBootstrap,
OCRKeys: make(map[string]OCRKeyView),
PayeeAddress: node.AdminAddr,
CSAKey: nodeDetails.PublicKey,
WorkflowKey: nodeDetails.GetWorkflowKey(),
IsConnected: nodeDetails.IsConnected,
IsEnabled: nodeDetails.IsEnabled,
Version: nodeDetails.Version,
Labels: labels,
ApprovedJobspecs: jobspecs[node.NodeID],
ProposedJobspecs: proposedSpecs[node.NodeID],
NodeName: nodeName,
Deployment: deploymentKey,
Networks: networks,
}
for details, ocrConfig := range node.SelToOCRConfig {
fullNodeInfo.OCRKeys[details.ChainName] = OCRKeyView{
Expand Down Expand Up @@ -294,7 +292,7 @@ func defaultNopNameRemapper(nodeName, deploymentKey string) string {
return nopName
}

func ApprovedJobspecs(ctx context.Context, lggr logger.Logger, nodeIDs []string, oc cldf_offchain.Client) (nodeJobsView map[string]map[string]JobView, proposedJobsView map[string]map[string]JobView, verr error) {
func ApprovedJobspecs(ctx context.Context, lggr logger.Logger, nodeIDs []string, oc cldf_offchain.Client) (nodeJobsView, proposedJobsView map[string]map[string]JobView, verr error) {
nodeJobsView = make(map[string]map[string]JobView)
proposedJobsView = make(map[string]map[string]JobView)

Expand Down
Loading
Loading