Promotion request workflow
Submit a fictional request, inspect the generated pull request, approve and merge it, then follow the simulated private runner through the Oracle SOAP promotion.
Enterprise software · J.M. Smucker
Production deploymentA production workflow that converts Oracle BI Publisher promotions into validated pull requests and approved CI/CD deployments.
Interactive project overview
These demonstrations present the project's state, timing, and key tradeoffs. The full case study and implementation details follow below.
Submit a fictional request, inspect the generated pull request, approve and merge it, then follow the simulated private runner through the Oracle SOAP promotion.
Oracle BI Publisher lacked a direct, governed path for moving report objects between development and production. Each manual promotion required DBA time and could remain blocked for weeks.
The delay discouraged teams from using the feature. The replacement needed to reduce repetitive work while preserving authentication, DBA approval, private configuration, and control of the final production action.
Follow a report object through request validation, Oracle verification, generated metadata, DBA approval, and the private deployment runner.
Interactive architecture
The public Next.js proof of concept creates the request and review record. A private runner performs the approved environment move.
Selected component
The requester supplies promotion metadata and authenticates with their own Oracle BI Publisher account.
The components I designed, implemented, tested, or integrated.
Designed the request workflow as a reusable Next.js application instead of a one-off operator script.
Validated and sanitized request data, checked the source object against Oracle BI Publisher, and generated deterministic promotion metadata.
Used the GitHub API to create a branch, commit the request record, fill a review template, and open a pull request for authorized DBAs.
Validated recursive catalog discovery and SOAP download/upload operations before integrating them with the private CI runner.
Stored environment endpoints in GitHub Secrets, required Oracle authentication, and restricted repository review to authorized DBAs.
The constraints and tradeoffs that shaped the implementation.
Constraint. The process needed an auditable review gate that fit the administrators' existing access model.
Decision. Store each promotion as versioned metadata on a dedicated branch and use a protected pull request for DBA review and approval.
Constraint. A syntactically valid form does not prove that the requester can authenticate or that the source object exists.
Decision. Verify the source object through Oracle with the requester's account before creating repository state. The portfolio demo shows this boundary without collecting credentials.
Constraint. The public proof of concept demonstrates the interface and integration pattern, but production endpoints and promotion internals are company-controlled.
Decision. Publish only the safe UI/API proof of concept and describe the private runner at an architectural level. Production configuration remains in repository secrets.
Important revisions, technical pivots, and lessons from each stage.
01 / Proof
Python scripts established recursive object discovery and the download/upload sequence needed to move a report object between catalog paths.
02 / Interface
A Next.js form made the source, target, request type, and report metadata explicit and reusable across departments.
03 / Governance
The API created branches, YAML metadata, and templated pull requests in a repository accessible only to authorized DBAs.
04 / Production
The private GitHub Actions workflow consumed the approved request and performed the controlled environment-to-environment SOAP deployment.
Measurements, configuration boundaries, and outcomes that show the scope of the work.
Lifecycle
Production
The automated deployment workflow was released for real company use.
Organizational reach
>90%
The workflow made the Oracle capability available to business areas representing more than 90% of the company workforce. This measures organizational reach, not active users.
Previous wait
Weeks+
Manual promotions could take weeks or longer before this workflow addressed the administrative bottleneck.
Governance
PR → merge → runner
Authenticated submission, private DBA review, and protected repository controls guard the production action.
Focused excerpts paired with the engineering behavior each one implements.
The public proof of concept accepts JSON only, requires an object body, sanitizes each field, and applies a strict schema before any Oracle or GitHub operation. Sensitive fields are intentionally omitted from this public excerpt.
Open source file01if (request.headers.get("content-type") !== "application/json") {02 return NextResponse.json(03 { error: "Expected application/json" },04 { status: 415 }05 );06}07 08const rawRequestBody: unknown = await request.json();09 10if (11 rawRequestBody === null ||12 typeof rawRequestBody !== "object" ||13 Array.isArray(rawRequestBody)14) {15 return NextResponse.json(16 { error: "Request body must be a JSON object" },17 { status: 422 }18 );19}20 21const parseResult = requestBodySchema.safeParse(22 sanitizedRequest23);Each request receives a Git reference from the latest protected base revision, creating a discrete and reviewable change.
Open source file01export async function createBranch(02 newBranch: string,03 sha: string04): Promise<void> {05 await gitFetch(06 `/repos/${GITHUB_OWNER}/${GITHUB_REPO}/git/refs`,07 {08 method: 'POST',09 body: { ref: `refs/heads/${newBranch}`, sha },10 }11 )12}The API opens a pull request from the generated branch to the configured base. Repository access and protected review provide the authorization boundary for the production workflow.
Open source file01export async function createPullRequest(02 newBranch: string,03 pullRequestMarkDownBody: string04): Promise<PrResponse> {05 return gitFetch<PrResponse>(06 `/repos/${GITHUB_OWNER}/${GITHUB_REPO}/pulls`,07 {08 method: 'POST',09 body: {10 title: newBranch,11 head: newBranch,12 base: `${GITHUB_BRANCH}`,13 body: pullRequestMarkDownBody,14 draft: false,15 },16 }17 )18}The Python proof of concept parsed SOAP catalog items and recursively traversed folders to build a complete object list.
01objects: list[FolderContents] = []02 03for item in items_parent.findall("ns2:item", ns):04 obj = FolderContents(05 absolutePath=item.findtext(06 ".//ns2:absolutePath",07 default="",08 namespaces=ns09 ),10 fileName=item.findtext(11 ".//ns2:fileName",12 default="",13 namespaces=ns14 ),15 type=item.findtext(16 ".//ns2:type",17 default="",18 namespaces=ns19 )20 )21 22 if obj.type.lower() == "folder":23 objects.extend(24 getAllFileNameAbsolutePath(obj.absolutePath)25 )26 27 objects.append(obj)28 29return objectsAn allow-listed substitution helper fills the review template with validated promotion metadata and keeps pull-request formatting consistent.
Open source file01export async function markDownEnvSub(02 filePath: string,03 envVars: Record<string, string | number>04): Promise<string> {05 const template = await readFile(06 resolve(filePath),07 "utf8"08 );09 10 return template.replace(11 /${([A-Z0-9_]+)}/g,12 (_, key) => key in envVars13 ? String(envVars[key])14 : ""15 );16}Public links open in a new tab. Private code and project artifacts are summarized without exposing infrastructure details or credentials.