Skip to content

Feat/stackitsdk 517 service deletion - #290

Open
cgoetz-inovex wants to merge 13 commits into
mainfrom
feat/STACKITSDK-517-service-deletion
Open

Feat/stackitsdk 517 service deletion#290
cgoetz-inovex wants to merge 13 commits into
mainfrom
feat/STACKITSDK-517-service-deletion

Conversation

@cgoetz-inovex

Copy link
Copy Markdown
Contributor

Introduce go build tool to generate and read plans.

Use go build tool to create service deletion PRs.

Review Instructions

  • the first commit creates the build tool to generate plan files, this should be reviewed throughly
  • most other commits refactor the existing build to use the tool
  • commit feat(ci): create PRs for deleted branches, unify plan location creates PRs for deleted services and contains new code
  • commit feat(ci): sdk-create-pr does not push/create PR when NO_PUSH is set adds a NO_PUSH option to create-pr for easier local testing

Testing Instructions

Testing refactorings:

  • checkout this branch locally
  • the following script creates a new temp dir of the sdk-generator, checks out the last commit on main and runs the generator for all 3 langauges, then it checks out the latest commit of this branch and builds all 3 languages. This leaves you with a temp directory with 6 generated SDKs.
  • diff the SDKs to confirm the build refactoring did no changes, e.g.: diffoscope --no-default-limits --markdown diff-python.md --max-diff-block-lines 10000 --max-report-size 0 --exclude "**/.git" --exclude-command "stat.*" --exclude "**/.venv" --exclude "**/.gradle" base-python-sdk branch-python-sdk/

Testing service deletion:

  • checkout this branch and run make download-oas and make generate-go-sdk
  • run NO_PUSH=1 scripts/sdk-create-pr.sh foo bar
  • check the service deletions in stdout (lbapplication, iaasalpha etc.)

Testscript:

#!/usr/bin/env bash
# Generate every SDK at the branch base on main and at the branch tip. The
# retained directories can be compared with diff.
set -euo pipefail

BRANCH_REF=${1:-HEAD}
MAIN_REF=${2:-main}
SOURCE_ROOT=$(git rev-parse --show-toplevel)
BRANCH_COMMIT=$(git -C "${SOURCE_ROOT}" rev-parse "${BRANCH_REF}")
BASE_COMMIT=$(git -C "${SOURCE_ROOT}" merge-base "${BRANCH_COMMIT}" "${MAIN_REF}")
OUTPUT_DIR=$(mktemp -d "${TMPDIR:-/tmp}/stackit-sdk-generation.XXXXXX")
WORKTREE_DIR="${OUTPUT_DIR}/worktree"
echo "BASE_COMMIT: ${BASE_COMMIT}"
echo "BRANCH_COMMIT: ${BRANCH_COMMIT}"

cleanup() {
    if [[ -d "${WORKTREE_DIR}" ]]; then
        git -C "${SOURCE_ROOT}" worktree remove --force "${WORKTREE_DIR}" || true
    fi
}
trap cleanup EXIT

printf 'Generated SDKs will be retained in: %s\n' "${OUTPUT_DIR}"
printf 'Base commit: %s\n' "${BASE_COMMIT}"
printf 'Branch tip:  %s\n' "${BRANCH_COMMIT}"

# Use an isolated worktree so the caller's checkout and uncommitted work are
# never modified. Download the OAS once, before checking out the base commit.
git -C "${SOURCE_ROOT}" worktree add --detach "${WORKTREE_DIR}" "${BRANCH_COMMIT}" >/dev/null
(
    cd "${WORKTREE_DIR}"
    make download-oas
)

generate_sdk() {
    local label=$1
    local language=$2

    (
        cd "${WORKTREE_DIR}"
        make "generate-${language}-sdk"
    )
    cp -a "${WORKTREE_DIR}/sdk-repo-updated" "${OUTPUT_DIR}/${label}-${language}-sdk"
}

generate_all_sdks() {
    local label=$1

    generate_sdk "${label}" go
    generate_sdk "${label}" python
    generate_sdk "${label}" java
}

record_commit() {
    local label=$1
    local commit=$2

    printf '%s\t%s\t%s\n' \
        "${label}" \
        "${commit}" \
        "$(git -C "${SOURCE_ROOT}" show -s --format=%s "${commit}")" \
        >>"${OUTPUT_DIR}/commits.tsv"
}

: >"${OUTPUT_DIR}/commits.tsv"
git -C "${WORKTREE_DIR}" checkout --detach "${BASE_COMMIT}" >/dev/null
generate_all_sdks base
record_commit base "${BASE_COMMIT}"

printf '\n=== Branch tip: %s ===\n' "${BRANCH_COMMIT}"
git -C "${WORKTREE_DIR}" checkout --detach "${BRANCH_COMMIT}" >/dev/null
generate_all_sdks branch
record_commit branch "${BRANCH_COMMIT}"

printf '\nDone. Compare, for example:\n'
printf '  diff -qr %s %s\n' "${OUTPUT_DIR}/base-go-sdk" "${OUTPUT_DIR}/branch-go-sdk"
printf 'Commit labels:\n'
printf '  %s\n' "${OUTPUT_DIR}/commits.tsv"

@cgoetz-inovex
cgoetz-inovex requested a review from a team as a code owner August 10, 2026 12:34
Comment thread scripts/sdk-create-pr.sh

case "${LANGUAGE}" in
go)
rm -rf "services/${service}" "examples/${service}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use relative paths here with the rm command. Maybe we should establish the working directory in the beginning of the loop? Or use an absolute path for the rm command here? Otherwise cds before the loop might break this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cd in line 62 before the previous loop starts

Comment thread scripts/build/plan.go
Comment on lines +117 to +147
oasService := entry.Name()
service := language.NormalizeServiceName(oasService)
if service == "" {
return Plan{}, fmt.Errorf("OAS service %q has no valid service name", oasService)
}
if previous, exists := services[service]; exists {
return Plan{}, fmt.Errorf("OAS services %q and %q both normalize to %q", previous.OASService, oasService, service)
}

action := "generate"
if _, isBlocked := blocked[service]; isBlocked {
action = "block"
} else if len(params.IncludedService) > 0 && !isIncluded(params.IncludedService, service) {
action = "not_included"
}
services[service] = PlannedService{Service: service, OASService: oasService, Action: action}
}

serviceEntries, err := fs.ReadDir(filesystem, params.ServiceDir)
if err != nil {
return Plan{}, fmt.Errorf("read service directory: %w", err)
}
for _, entry := range serviceEntries {
if !entry.IsDir() {
continue
}
service := entry.Name()
if _, exists := services[service]; !exists {
services[service] = PlannedService{Service: service, Action: "delete"}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like when a service is on the block list and removed from the spec, it does not get the delete action but block action. Is that intended?

--spec-dir "oas/services" \
--service-dir "sdk-repo-updated/services" \
--blocklist "languages/golang/blocklist.txt" \
--output "generation-plan.json"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should name the output langeuage-specific, so there is no possibility of concurrent scripts overriding each others plans

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a feature for later, concurrent execution would also clobber the sdk-repo-updated dir.

Comment thread scripts/build/readme.md Outdated
Comment thread scripts/build/readme.md Outdated
Comment thread scripts/build/readme.md Outdated
Comment thread scripts/build/main.go Outdated
FS: rootFileSystem{root: root},
}

if ok := run(ctx, stdio); !ok {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do all run functions return a bool instead of the error? this makes it harder to debug, no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored runGenerate etc. to return an error instead. Errors are loggen in main.go/run as the first function that receives an Environment struct wit Err as logging writer.
main.go/run still returns a bool to communicate which exit code to use to main itself.

Comment thread scripts/build/main.go Outdated
Comment on lines +71 to +77
type IO struct {
Args []string
In io.Reader
Out io.Writer
Err io.Writer
FS FileSystem
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: maybe let's pick another name since we also import the IO package here. Maybe call it Streams?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed to Envrionment

Comment thread scripts/build/plan.go Outdated
return Plan{}, fmt.Errorf("OAS services %q and %q both normalize to %q", previous.OASService, oasService, service)
}

action := "generate"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: might be worth introducing a action type here with explicit support actions instead of plain strings

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice idea, introduced Action type and constants

cgoetz-inovex and others added 2 commits August 12, 2026 09:50
Co-authored-by: Jonas Schlecht <73650029+SerseusWasTaken@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants