Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add workflow to apply override snapshots and create release CR PR #514

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
119 changes: 119 additions & 0 deletions .github/workflows/generate-release-crs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: Apply Konflux Override Snapshot and create Release CR PR
on:
workflow_dispatch:
inputs:
revision:
description: "SO branch or revision with the manifests"
default: "release-1."
required: true
environment:
description: "Environment to release to"
type: choice
options:
- stage
- prod
required: true
creydr marked this conversation as resolved.
Show resolved Hide resolved
default: "prod"
skip-apply:
description: "Skip override-snapshot apply"
type: boolean
required: false
default: "false"
defaults:
run:
shell: bash
jobs:
snapshot-apply:
name: Apply Konflux Override Snapshot
runs-on: ubuntu-latest
if: ${{ inputs.skip-apply == 'false' }}
steps:
- name: Checkout serverless operator
uses: actions/checkout@v4
with:
ref: "${{ inputs.revision }}"
repository: 'openshift-knative/serverless-operator'
path: ./src/github.com/openshift-knative/serverless-operator

- name: Setup kubeconfig
env:
KONFLUX_TOKEN: ${{ secrets.KONFLUX_SA_TOKEN }}
run: |
kubectl config set-credentials konflux-sa --token "$KONFLUX_TOKEN"
kubectl config set-cluster konflux --server=https://api.stone-prd-rh01.pg1f.p1.openshiftapps.com:6443
kubectl config set-context konflux-sa@konflux --user=konflux-sa --namespace=ocp-serverless-tenant --cluster=konflux
kubectl config use-context konflux-sa@konflux

- name: Apply Override Snapshots
working-directory: ./src/github.com/openshift-knative/hack/openshift-knative/serverless-operator
run: make apply-override-snapshot

generate-release-crs:
name: Generate Release CRs
runs-on: ubuntu-latest
needs: snapshot-apply
steps:
- name: Checkout openshift-knative/hack
uses: actions/checkout@v4
with:
repository: openshift-knative/hack
path: ./src/github.com/openshift-knative/hack
token: ${{ secrets.SERVERLESS_QE_ROBOT }}

- name: Generate Konflux component release CR
working-directory: ./src/github.com/openshift-knative/hack
run: |
make generate-konflux-release ARGS="--environment ${{ inputs.environment }} --so-revision ${{ inputs.revision }} --type component"

- name: Create component release CR Pull Request
env:
GH_TOKEN: ${{ secrets.SERVERLESS_QE_ROBOT }}
GITHUB_TOKEN: ${{ secrets.SERVERLESS_QE_ROBOT }}
run: |
set -x
git remote add fork "https://github.com/serverless-qe/hack.git"
branch="release-crs-${{inputs.revision}}-component-${{inputs.environment}}"
branch=${branch,,} #lower case of branch name
remote_exists=$(git ls-remote --heads fork "$branch")
if [ -z "$remote_exists" ]; then
# remote doesn't exist.
git push "https://serverless-qe:${GH_TOKEN}@github.com/serverless-qe/hack.git" "$branch:$branch" -f || exit 1
fi
git fetch fork "$branch"
if git diff --quiet "fork/$branch" "$branch"; then
echo "Branches are identical. No need to force push."
else
git push "https://serverless-qe:${GH_TOKEN}@github.com/serverless-qe/hack.git" "$branch:$branch" -f
fi
gh pr create --base main --head "serverless-qe:$branch" --fill --label "do-not-merge/hold" || true
# Use the repository cloned by the konflux-release-gen tool
working-directory: ./src/github.com/openshift-knative/hack/openshift-knative/hack

- name: Generate Konflux FBC release CRs
working-directory: ./src/github.com/openshift-knative/hack
run: |
make generate-konflux-release ARGS="--environment ${{ inputs.environment }} --so-revision ${{ inputs.revision }} --type fbc"

- name: Create FBC release CR Pull Request
env:
GH_TOKEN: ${{ secrets.SERVERLESS_QE_ROBOT }}
GITHUB_TOKEN: ${{ secrets.SERVERLESS_QE_ROBOT }}
run: |
set -x
git remote add fork "https://github.com/serverless-qe/hack.git"
branch="release-crs-${{inputs.revision}}-fbc-${{inputs.environment}}"
branch=${branch,,} #lower case of branch name
remote_exists=$(git ls-remote --heads fork "$branch")
if [ -z "$remote_exists" ]; then
# remote doesn't exist.
git push "https://serverless-qe:${GH_TOKEN}@github.com/serverless-qe/hack.git" "$branch:$branch" -f || exit 1
fi
git fetch fork "$branch"
if git diff --quiet "fork/$branch" "$branch"; then
echo "Branches are identical. No need to force push."
else
git push "https://serverless-qe:${GH_TOKEN}@github.com/serverless-qe/hack.git" "$branch:$branch" -f
fi
gh pr create --base main --head "serverless-qe:$branch" --fill --label "do-not-merge/hold" || true
# Use the repository cloned by the konflux-release-gen tool
working-directory: ./src/github.com/openshift-knative/hack/openshift-knative/hack
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ discover-branches: clean
go run github.com/openshift-knative/hack/cmd/discover $(ARGS)
.PHONY: discover-branches

generate-konflux-release: clean
go run github.com/openshift-knative/hack/cmd/konflux-release-gen $(ARGS)
.PHONY: discover-branches
creydr marked this conversation as resolved.
Show resolved Hide resolved

generate-ci-action:
go run github.com/openshift-knative/hack/cmd/generate-ci-action
.PHONY: generate-ci-action
Expand Down
170 changes: 170 additions & 0 deletions cmd/konflux-release-gen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"

"github.com/coreos/go-semver/semver"
gyaml "github.com/ghodss/yaml"
"github.com/openshift-knative/hack/pkg/konfluxgen"
"github.com/openshift-knative/hack/pkg/project"
"github.com/openshift-knative/hack/pkg/prowgen"
"github.com/openshift-knative/hack/pkg/soversion"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}

func run() error {
ctx := context.TODO()

const (
componentReleaseType = "component"
fbcReleaseType = "fbc"

stageEnv = "stage"
prodEnv = "prod"
)

var environment, soRevision, overrideSnapshotDir, output, releaseType string
pflag.StringVar(&environment, "environment", "prod", fmt.Sprintf("Environment to use. Available values: [%s, %s]", stageEnv, prodEnv))
pflag.StringVar(&soRevision, "so-revision", "main", "SO revision to get snapshots from")
pflag.StringVar(&releaseType, "type", "component", fmt.Sprintf("Type of the release. Available values: [%s, %s]", componentReleaseType, fbcReleaseType))
pflag.StringVar(&overrideSnapshotDir, "so-snapshot-directory", ".konflux-release", "The directory containing Serverless Operator override snapshots")
pflag.StringVar(&output, "output", ".konflux", "Path to output directory")
pflag.Parse()

if environment != stageEnv && environment != prodEnv {
return fmt.Errorf("invalid environment: %s", environment)
}

// clone SO repo to get metadata & snapshots for given revision
soRepo := prowgen.Repository{Org: "openshift-knative", Repo: "serverless-operator"}
if err := prowgen.GitClone(ctx, soRepo); err != nil {
return fmt.Errorf("could not clone Git repository: %w", err)
}

if err := prowgen.GitCheckout(ctx, soRepo, soRevision); err != nil {
return fmt.Errorf("could not checkout Git revision %s of Serverless Operator: %w", soRevision, err)
}

soProjectYamlPath := filepath.Join(soRepo.RepositoryDirectory(), "olm-catalog", "serverless-operator", "project.yaml")
soMetadata, err := project.ReadMetadataFile(soProjectYamlPath)
if err != nil {
return fmt.Errorf("could not read project.yaml: %w", err)
}

soVersion := semver.New(soMetadata.Project.Version)
soReleaseBranch := soversion.BranchName(soVersion)

overrideSnapshotsPath := filepath.Join(soRepo.RepositoryDirectory(), overrideSnapshotDir)

// clone hack repo so we can commit the changes
hackRepo := prowgen.Repository{Org: "openshift-knative", Repo: "hack"}
outputDir := filepath.Join(hackRepo.RepositoryDirectory(), output)

if err := prowgen.GitMirror(ctx, hackRepo); err != nil {
return fmt.Errorf("could not clone Git repository: %w", err)
}

if err := prowgen.GitCheckout(ctx, hackRepo, "main"); err != nil {
return fmt.Errorf("could not checkout main branch of hack repo: %w", err)
}

if strings.ToLower(releaseType) == componentReleaseType {
snapshot, err := componentSnapshotName(overrideSnapshotsPath)
if err != nil {
return fmt.Errorf("could not get snapshot name: %w", err)
}
appName := konfluxgen.AppName(soReleaseBranch)
releasePlan := konfluxgen.ReleasePlanAdmissionName(appName, soMetadata.Project.Version, environment) // releasePlanName == releasePlanAdmissionName

cfg := konfluxgen.ReleaseConfig{
Snapshot: snapshot,
ReleasePlan: releasePlan,
Environment: environment,
ResourcesOutputPath: outputDir,
}

if err := konfluxgen.GenerateRelease(cfg); err != nil {
return fmt.Errorf("could not generate release: %w", err)
}
} else if strings.ToLower(releaseType) == fbcReleaseType {
for _, ocpVersion := range soMetadata.Requirements.OcpVersion.List {
ocpVersionFlat := strings.ReplaceAll(ocpVersion, ".", "")

snapshot, err := fbcSnapshotName(overrideSnapshotsPath, ocpVersionFlat)
if err != nil {
return fmt.Errorf("could not get snapshot name: %w", err)
}

appName := konfluxgen.FBCAppName(soReleaseBranch, ocpVersionFlat)
releasePlan := konfluxgen.ReleasePlanAdmissionName(appName, soMetadata.Project.Version, environment) // releasePlanName == releasePlanAdmissionName

cfg := konfluxgen.ReleaseConfig{
Snapshot: snapshot,
ReleasePlan: releasePlan,
Environment: environment,
ResourcesOutputPath: outputDir,
}

if err := konfluxgen.GenerateRelease(cfg); err != nil {
return fmt.Errorf("could not generate release: %w", err)
}
}
} else {
return fmt.Errorf("invalid releaseType: %s", releaseType)
}

pushBranch := strings.ToLower(fmt.Sprintf("release-crs-%s-%s-%s", soRevision, releaseType, environment))
commitMsg := fmt.Sprintf("Add %s Release CRs from %s revision for %s", releaseType, soRevision, environment)

if err := prowgen.PushBranch(ctx, hackRepo, nil, pushBranch, commitMsg); err != nil {
return fmt.Errorf("could not push to branch %s: %w", pushBranch, err)
}

return nil
}

func fbcSnapshotName(soReleaseFolder string, ocpVersion string) (string, error) {
filename := fmt.Sprintf("override-snapshot-fbc-%s.yaml", ocpVersion)
return parseSnapshotName(filepath.Join(soReleaseFolder, filename))
}

func componentSnapshotName(soReleaseFolder string) (string, error) {
return parseSnapshotName(filepath.Join(soReleaseFolder, "override-snapshot.yaml"))
}

func parseSnapshotName(snapshotFile string) (string, error) {
snapshot := metav1.PartialObjectMetadata{}

y, err := os.ReadFile(snapshotFile)
if err != nil {
return "", fmt.Errorf("failed to read snapshot file: %w", err)
}

j, err := gyaml.YAMLToJSON(y)
if err != nil {
return "", fmt.Errorf("failed to convert snapshot file to json: %w", err)
}

if err := json.Unmarshal(j, &snapshot); err != nil {
return "", fmt.Errorf("failed to unmarshal snapshot file: %w", err)
}

if snapshot.Name == "" {
return "", fmt.Errorf("snapshot.Name is empty")
}

return snapshot.Name, nil
}
Loading
Loading