Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8fb5938
Add dto csv yaml back
waodim Dec 3, 2025
4a88314
Merge branch 'main' of github.com:Dynatrace/dynatrace-operator
waodim Dec 3, 2025
2d712be
Merge branch 'main' of github.com:Dynatrace/dynatrace-operator
waodim Dec 3, 2025
8ce7f0e
Add composite action to update csv bundles
waodim Dec 3, 2025
0141a3f
Add composite action to release workflow; Move GCR Deployer step
waodim Dec 3, 2025
00b6d54
Add helper scripts
waodim Dec 3, 2025
aa1934e
Add new actions to create forks for marketplaces
waodim Dec 3, 2025
6a71e3b
Fix missing new lines
waodim Dec 4, 2025
3c1148b
Fix actions to use digest instead of tag
waodim Dec 4, 2025
293a9e8
Move scripts to scripts/release/ and scripts/release/csv/
waodim Dec 4, 2025
2d991a0
Prepare for testing
waodim Dec 4, 2025
cb91b11
Revert "Prepare for testing"
waodim Dec 4, 2025
8083562
Remove set_property.py as it's not needed
waodim Dec 5, 2025
3419bc6
Update fork workflows
waodim Dec 5, 2025
167c3d5
Update finalize scripts
waodim Dec 5, 2025
aab93c4
Remove obsolete env vars
waodim Dec 5, 2025
2437856
Merge branch 'main' into migrate/concourse-to-gha
waodim Dec 5, 2025
0f9b9f1
Merge branch 'main' into migrate/concourse-to-gha
waodim Dec 9, 2025
eda26ad
Export args for make bundle command to env vars
waodim Dec 9, 2025
bcbbd3d
Remove unused command
waodim Dec 9, 2025
8361ccf
Fix leftovers in py scripts
waodim Dec 9, 2025
a527dfe
Fix bundle command
waodim Dec 9, 2025
69e76c4
Create generic fork creation step
waodim Dec 9, 2025
017c48d
Adapt file name
waodim Dec 9, 2025
47d6452
Merge branch 'main' into migrate/concourse-to-gha
waodim Dec 10, 2025
2f30507
Fix issues
waodim Dec 10, 2025
7d32673
Fix new lines; apply suggestions
waodim Dec 11, 2025
bc2fec1
Final test
waodim Dec 11, 2025
33db045
Revert "Final test"
waodim Dec 11, 2025
59ca577
Simplify sdk step
waodim Dec 11, 2025
6ffcc11
Merge branch 'main' into migrate/concourse-to-gha
waodim Dec 11, 2025
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
140 changes: 140 additions & 0 deletions .github/actions/update-csv-bundles/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: "Update CSV bundles"
description: "Generate and finalize OLM CSV bundles for a given version"

inputs:
version:
description: "Version without v-prefix"
required: true
token:
description: "GitHub token to create PRs"
required: true
changelog-path:
description: "Path to the changelog file"
required: false

outputs:
major_minor:
description: "Major.minor part of the version"
value: ${{ steps.vars.outputs.major_minor }}

runs:
using: "composite"
steps:
- name: Derive release branch name
id: vars
shell: bash
env:
VERSION: ${{ inputs.version }}
run: |
MAJOR_MINOR="${VERSION%.*}"
echo "major_minor=${MAJOR_MINOR}" >> "$GITHUB_OUTPUT"
- name: Checkout release branch
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
ref: release-${{ steps.vars.outputs.major_minor }}
fetch-depth: 0
clean: false
- name: Setup Go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c #6.0.1
with:
go-version-file: "${{ github.workspace }}/go.mod"
- name: Setting properties
shell: bash
env:
CHANGELOG_CONTENT: ${{ inputs.changelog-path }}
DIGEST: ${{ inputs.digest }}
VERSION: ${{ inputs.version }}
run: |
echo "Setting version to v${VERSION} in files"
yq -i ".appVersion = \"$VERSION\"" config/helm/chart/default/Chart.yaml
yq -i ".version = \"$VERSION\"" config/helm/chart/default/Chart.yaml

echo "Setting version in schema.yaml"
yq -i \
".x-google-marketplace.publishedVersion = \"$VERSION\"" \
"config/helm/schema.yaml"

echo "Setting changelog in schema.yaml"

yq -i \
".x-google-marketplace.publishedVersionMetadata.releaseNote = load_str(env(CHANGELOG_CONTENT))" \
"config/helm/schema.yaml"
- name: Install operator-sdk v1.36.0
shell: bash
env:
OPERATOR_SDK_VERSION: v1.36.0
VERSION: ${{ inputs.version }}
run: |
GOBIN="$(go env GOPATH)/bin"
mkdir -p "$GOBIN"

echo "Installing operator-sdk ${OPERATOR_SDK_VERSION} into $GOBIN"
curl -OJL \
"https://github.com/operator-framework/operator-sdk/releases/download/${OPERATOR_SDK_VERSION}/operator-sdk_linux_amd64"
chmod +x operator-sdk_linux_amd64

mv operator-sdk_linux_amd64 ${GOBIN}/operator-sdk
"${GOBIN}/operator-sdk" version

echo "GOBIN=$GOBIN" >> $GITHUB_ENV
- name: Generate OLM bundles (openshift)
shell: bash
env:
VERSION: ${{ inputs.version }}
PLATFORM: openshift
CHANNELS: stable
DEFAULT_CHANNEL: stable
IMAGE: registry.connect.redhat.com/dynatrace/dynatrace-operator
OLM_IMAGE: registry.connect.redhat.com/dynatrace/dynatrace-operator:v${{ inputs.version }}
run: |
make bundle
- name: Generate OLM bundles (kubernetes)
shell: bash
env:
VERSION: ${{ inputs.version }}
PLATFORM: kubernetes
CHANNELS: stable
DEFAULT_CHANNEL: stable
IMAGE: docker.io/dynatrace/dynatrace-operator
OLM_IMAGE: docker.io/dynatrace/dynatrace-operator:v${{ inputs.version }}
run: |
make bundle
- name: Finalize CSV files
shell: bash
env:
VERSION: ${{ inputs.version }}
run: |

echo "Finalizing CSV files for Kubernetes"
./.github/scripts/release/csv/finalize_csv_files.py \
--platform "kubernetes" \
--version "${VERSION}" \

echo "Finalizing CSV files for Openshift"
./.github/scripts/release/csv/finalize_csv_files.py \
--platform "openshift" \
--version "${VERSION}" \
- name: Create GitHub app token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
id: create-github-app-token
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_KEY }}
- name: Create CSV update PR
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
with:
base: release-${{ steps.vars.outputs.major_minor }}
branch: csv-${{ steps.vars.outputs.major_minor}}
token: ${{ steps.create-github-app-token.token }}
delete-branch: true
add-paths: |
config/olm/**
config/deploy/kubernetes/kustomization.yaml
config/deploy/openshift/kustomization.yaml
config/manifests/bases/**
config/helm/**
commit-message: "[Automatic] Update OLM bundles for v${{ inputs.version }}"
signoff: true
title: "[Automatic] Update OLM CSV bundles for v${{ inputs.version }}"
body: |
This PR updates the OLM bundles/CSVs on `release-${{ steps.vars.outputs.major_minor }}` for version `v${{ inputs.version }}`.
56 changes: 56 additions & 0 deletions .github/scripts/release/csv/finalize_csv_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
import argparse
import sys
import datetime
import yaml


def read_yaml(path):
with open(path, "r") as yaml_file:
try:
data = yaml.safe_load(yaml_file)
except yaml.YAMLError as e:
print(f"Could not load file: {e}")
return None
return data


def write_yaml(data, path):
with open(path, "w") as file:
yaml.dump(data, file, default_flow_style=False, sort_keys=False)


if __name__ == "__main__":
argument_parser = argparse.ArgumentParser(description="Finalize CSV files by automatically setting the createdAt annotation and olm.skipRange")
argument_parser.add_argument("--platform", type=str, required=True, choices=["openshift", "kubernetes"],
help="Sets the platform for which the CSV files are finalized")
argument_parser.add_argument("--version", type=str, required=True,
help="Sets the version for which the CSV files are finalized")

args = argument_parser.parse_args()

platform = args.platform
version = args.version
csv_filepath = \
f"config/olm/{platform}/{version}/manifests/" \
f"dynatrace-operator.clusterserviceversion.yaml"
kustomize_filepath = f"config/olm/{platform}/kustomization.yaml"

csv = read_yaml(csv_filepath)
kustomize = read_yaml(kustomize_filepath)

if csv is None or kustomize is None:
sys.exit(1)

kustomize.pop("images", None)
csv["metadata"]["annotations"]["createdAt"] = datetime.datetime.now().isoformat()

for deployment_index, deployment in enumerate(csv["spec"]["install"]["spec"]["deployments"]):
for container_index, container in enumerate(deployment["spec"]["template"]["spec"]["containers"]):
deployment["spec"]["template"]["spec"]["containers"][container_index] = container
csv["spec"]["install"]["spec"]["deployments"][deployment_index] = deployment

csv["metadata"]["annotations"]["olm.skipRange"] = f"<{version}"

write_yaml(csv, csv_filepath)
write_yaml(kustomize, kustomize_filepath)
71 changes: 71 additions & 0 deletions .github/scripts/release/csv/make_release_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env python3

import argparse
import yaml

def prepare_for_RHCC(csv):
csv["metadata"]["annotations"]["marketplace.openshift.io/remote-workflow"] = \
"https://marketplace.redhat.com/en-us/operators/dynatrace-operator-rhmp/pricing?utm_source=openshift_console"
csv["metadata"]["annotations"]["marketplace.openshift.io/support-workflow"] = \
"https://marketplace.redhat.com/en-us/operators/dynatrace-operator-rhmp/support?utm_source=openshift_console"

return csv

def configure_deployment(deployment, image, marketplace):
containers = deployment["spec"]["template"]["spec"]["containers"]
for container_index in range(len(containers)):
containers[container_index]["image"] = image

if deployment["name"] == "dynatrace-operator":
formattedMarketplace = "operatorhub" + "-" + marketplace
deployment["spec"]["template"]["metadata"]["labels"]["dynatrace.com/install-source"] = formattedMarketplace

return deployment

if __name__ == "__main__":
argument_parser = argparse.ArgumentParser()
argument_parser.add_argument("path", type=str)
argument_parser.add_argument("--image", type=str, required=True)
argument_parser.add_argument("--isRHCC", type=bool, default=False)
argument_parser.add_argument("--marketplace", type=str, required=True)

arguments = argument_parser.parse_args()
csv_path = arguments.path
image = arguments.image
isRHCC = arguments.isRHCC
marketplace = arguments.marketplace

with open(csv_path, 'r') as csv_file:
csv = yaml.safe_load(csv_file)

if isRHCC:
csv = prepare_for_RHCC(csv)

csv["metadata"]["annotations"]["containerImage"] = image

deployments = csv["spec"]["install"]["spec"]["deployments"]
for deployment_index in range(len(deployments)):
deployments[deployment_index] = configure_deployment(deployments[deployment_index], image, marketplace)

csv["metadata"]["annotations"]["operators.openshift.io/valid-subscription"] = \
'[\"Dynatrace Platform Subscription (DPS)\",\"Dynatrace Classic License\"]'

csv["metadata"]["annotations"]["features.operators.openshift.io/disconnected"] = "true"
csv["metadata"]["annotations"]["features.operators.openshift.io/proxy-aware"] = "true"
csv["metadata"]["annotations"]["features.operators.openshift.io/fips-compliant"] = "false"
csv["metadata"]["annotations"]["features.operators.openshift.io/tls-profiles"] = "false"
csv["metadata"]["annotations"]["features.operators.openshift.io/token-auth-aws"] = "false"
csv["metadata"]["annotations"]["features.operators.openshift.io/token-auth-azure"] = "false"
csv["metadata"]["annotations"]["features.operators.openshift.io/token-auth-gcp"] = "false"


csv["spec"]["relatedImages"] = [
{
"name": "dynatrace-operator",
"image": image
}
]

with open(csv_path, "w") as csv_file:
yaml.safe_dump(csv, csv_file, sort_keys=False)

88 changes: 88 additions & 0 deletions .github/scripts/release/directory_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
import argparse


def _compare_normalized_version_arrays(a, b):
a_is_parseable = True
b_is_parseable = True
for i in range(0, 3):
version_num_a = 0
version_num_b = 0

try:
version_num_a = int(a[i])
except ValueError:
a_is_parseable = False
try:
version_num_b = int(b[i])
except ValueError:
b_is_parseable = False

if not a_is_parseable and not b_is_parseable:
return 0
elif not a_is_parseable:
return -1
elif not b_is_parseable:
return 1

if version_num_a > version_num_b:
return 1
elif version_num_b > version_num_a:
return -1
return 0


def normalize_version(version):
version_array = _split_semantic_version(version)
version_array = _normalize_version_array(version_array)
return ".".join(version_array)


def _split_semantic_version(version):
return version.split(".")


def _normalize_version_array(version_array):
while len(version_array) < 3:
version_array.append("0")
return version_array[0:3]


def get_latest_version_from_collection(version_collection):
if len(version_collection) <= 0:
return ""

if len(version_collection) == 1:
return version_collection[0]

latest_version = ""
for version in version_collection:
latest = _normalize_version_array(_split_semantic_version(latest_version))
current = _normalize_version_array(_split_semantic_version(version))
comparison = _compare_normalized_version_arrays(latest, current)

if comparison < 0:
latest_version = version

return normalize_version(latest_version)


if __name__ == "__main__":
argument_parser = argparse.ArgumentParser(
description="Returns the latest version from a list of strings in the semver format")
argument_parser.add_argument("versions",
help="Newline separated list of versions. E.g. the output of 'ls -d */'. "
"Trailing slashes will be removed for your convenience",
type=str)
arguments = argument_parser.parse_args()

versions_string = arguments.versions
version_dirs = versions_string.split("\n")
versions = []

for version_dir in version_dirs:
if version_dir.endswith("/"):
version_dir = version_dir[:-1]
versions.append(version_dir.split("/")[-1])

print(get_latest_version_from_collection(versions))
Loading