DevOps → approve request
Fetch a ticket (or pick an awaiting_approval row), approve it, and wait until
the run finishes. Optionally post the default triage comment on the upstream ticket.
| Step | What the SDK does |
|---|---|
| 1. Fetch or list | fetch_and_wait or list_requests(status=awaiting_approval) |
| 2. Approve and wait | approve_and_wait |
| 3. Optional | post_source_comment |
Minimal
from stackgen import StackgenClient, StackgenConfig
client = StackgenClient(
StackgenConfig(
base_url="https://app.stackgen.com",
api_token="stackgen_…",
workspace_id="<workspace-uuid>",
)
)
req = client.devops.fetch_and_wait("CORE-981")
finished = client.devops.approve_and_wait(req["id"])
print(finished.get("execution_status"), finished.get("execution_result"))import { StackgenClient, StackgenConfig } from "stackgen-sdk";
const client = new StackgenClient(
new StackgenConfig({
baseUrl: "https://app.stackgen.com",
apiToken: "stackgen_…",
workspaceId: "<workspace-uuid>",
}),
);
const req = await client.devops.fetchAndWait("CORE-981");
const finished = await client.devops.approveAndWait(String(req.id));
console.log(finished.execution_status, finished.execution_result);req, err := client.DevOps.FetchAndWait(ctx, "CORE-981")
if err != nil { return err }
finished, err := client.DevOps.ApproveAndWait(ctx, fmt.Sprint(req["id"]), "", "")
if err != nil { return err }
fmt.Println(finished["execution_status"], finished["execution_result"])
return nilHow to run
python examples/devops/approve-request.py --dry-run
python examples/devops/approve-request.py --identifier CORE-981 --commentComplete script
- Python:
examples/devops/approve-request.py - TypeScript:
examples/devops/approve-request.ts - Go:
examples/go/devops/approve-request.go
#!/usr/bin/env python3
"""DevOps: approve an Ops Inbox request and wait for the run.
Documented at:
https://appcd-dev.github.io/stackgen-sdk/code-samples/devops-approve-request/
Usage:
python examples/devops/approve-request.py --dry-run
python examples/devops/approve-request.py --identifier CORE-981 --comment
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from stackgen import StackgenClient, StackgenConfig
from stackgen.errors import StackgenError
def _config(args: argparse.Namespace) -> StackgenConfig:
base = (args.base_url or os.environ.get("STACKGEN_URL", "")).strip()
token = (args.api_token or os.environ.get("STACKGEN_TOKEN", "")).strip()
org = (
(args.workspace_id or "").strip()
or os.environ.get("STACKGEN_WORKSPACE_ID", "").strip()
or os.environ.get("STACKGEN_ORG_ID", "").strip()
or os.environ.get("STACKGEN_PROJECT", "").strip()
or os.environ.get("STACKGEN_PROJECT_ID", "").strip()
)
if not base or not token:
raise SystemExit("set STACKGEN_URL / STACKGEN_TOKEN")
return StackgenConfig(
base_url=base,
api_token=token,
workspace_id=org,
timeout_seconds=args.timeout_seconds,
poll_interval_seconds=args.poll_interval_seconds,
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--base-url", default="")
parser.add_argument("--api-token", default="")
parser.add_argument("--workspace-id",
"--org-id", default="")
parser.add_argument("--identifier", default="")
parser.add_argument("--request-id", default="")
parser.add_argument("--workflow-name", default="")
parser.add_argument("--comment", action="store_true")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--timeout-seconds", type=int, default=1800)
parser.add_argument("--poll-interval-seconds", type=int, default=5)
args = parser.parse_args()
client = StackgenClient(_config(args))
request_id = args.request_id.strip()
if args.identifier.strip():
req = client.devops.fetch_and_wait(args.identifier.strip())
print("fetched:", json.dumps(req, indent=2, default=str))
request_id = str(req.get("id") or "")
elif not request_id:
listed = client.devops.list_requests(status="awaiting_approval", page_size=5)
print("candidates:", json.dumps(listed, indent=2, default=str))
rows = []
if isinstance(listed, dict):
rows = [r for r in (listed.get("requests") or []) if isinstance(r, dict)]
if not rows:
print("no awaiting_approval requests", file=sys.stderr)
return 0 if args.dry_run else 1
request_id = str(rows[0].get("id") or "")
if args.dry_run:
print(f"request_id={request_id}", file=sys.stderr)
return 0
finished = client.devops.approve_and_wait(
request_id,
workflow_name=args.workflow_name or None,
)
print("result:", json.dumps(finished, indent=2, default=str))
print(f"execution_result={finished.get('execution_result')}", file=sys.stderr)
if args.comment:
comment = client.devops.post_source_comment(request_id)
print("comment:", json.dumps(comment, indent=2, default=str))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except StackgenError as exc:
print(f"error: {exc}", file=sys.stderr)
raise SystemExit(1)/**
* DevOps: approve an Ops Inbox request and wait for the run.
* npx tsx examples/devops/approve-request.ts --dry-run
*/
import { parseArgs } from "node:util";
import { StackgenClient, StackgenConfig, StackgenError } from "stackgen-sdk";
function asRecord(value: unknown): Record<string, unknown> | undefined {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: undefined;
}
function show(label: string, value: unknown): void {
console.log(`${label}:`, JSON.stringify(value, null, 2));
}
async function main(): Promise<number> {
const { values } = parseArgs({
options: {
"base-url": { type: "string", default: "" },
"api-token": { type: "string", default: "" },
"org-id": { type: "string" },
"workspace-id": { type: "string", default: "" },
identifier: { type: "string", default: "" },
"request-id": { type: "string", default: "" },
"workflow-name": { type: "string", default: "" },
comment: { type: "boolean", default: false },
"dry-run": { type: "boolean", default: false },
"timeout-seconds": { type: "string", default: "1800" },
"poll-interval-seconds": { type: "string", default: "5" },
},
});
const baseUrl = (values["base-url"] || process.env.STACKGEN_URL || "").trim();
const apiToken = (values["api-token"] || process.env.STACKGEN_TOKEN || "").trim();
const orgId = (
(values["org-id"] || values["workspace-id"]) ||
process.env.STACKGEN_WORKSPACE_ID ||
process.env.STACKGEN_ORG_ID ||
process.env.STACKGEN_PROJECT ||
process.env.STACKGEN_PROJECT_ID ||
""
).trim();
if (!baseUrl || !apiToken) {
console.error("set STACKGEN_URL / STACKGEN_TOKEN");
return 2;
}
const client = new StackgenClient(
new StackgenConfig({
baseUrl,
apiToken,
workspaceId: orgId,
timeoutSeconds: Number(values["timeout-seconds"]),
pollIntervalSeconds: Number(values["poll-interval-seconds"]),
}),
);
let requestId = (values["request-id"] ?? "").trim();
if (values.identifier?.trim()) {
const req = await client.devops.fetchAndWait(values.identifier.trim());
show("fetched", req);
requestId = String(req.id ?? "");
} else if (!requestId) {
const listed = asRecord(
await client.devops.listRequests({ status: "awaiting_approval", page_size: 5 }),
);
show("candidates", listed);
const rows = Array.isArray(listed?.requests) ? listed.requests : [];
const first = asRecord(rows[0]);
requestId = String(first?.id ?? "");
if (!requestId) {
console.error("no awaiting_approval requests");
return values["dry-run"] ? 0 : 1;
}
}
if (values["dry-run"]) {
console.error(`request_id=${requestId}`);
return 0;
}
const finished = await client.devops.approveAndWait(requestId, {
workflowName: values["workflow-name"] || undefined,
});
show("result", finished);
console.error(`execution_result=${finished.execution_result ?? ""}`);
if (values.comment) {
show("comment", await client.devops.postSourceComment(requestId));
}
return 0;
}
main().then(
(c) => process.exit(c),
(err) => {
console.error(`error: ${err}`);
process.exit(err instanceof StackgenError ? 1 : 1);
},
);// Approve an Ops Inbox request and wait for the run.
//
// go run ./examples/go/devops/approve-request.go --dry-run
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
stackgen "github.com/appcd-dev/stackgen-sdk/clients/go"
)
func envOr(keys ...string) string {
for _, key := range keys {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
}
return ""
}
func main() {
baseURL := flag.String("base-url", "", "")
apiToken := flag.String("api-token", "", "")
orgID := flag.String("workspace-id", "", "Workspace UUID (sent as orgId)")
flag.StringVar(orgID, "org-id", *orgID, "Deprecated: use -workspace-id")
identifier := flag.String("identifier", "", "")
requestID := flag.String("request-id", "", "")
workflowName := flag.String("workflow-name", "", "")
comment := flag.Bool("comment", false, "")
dryRun := flag.Bool("dry-run", false, "")
timeoutSeconds := flag.Int("timeout-seconds", 1800, "")
pollInterval := flag.Int("poll-interval-seconds", 5, "")
flag.Parse()
base := strings.TrimSpace(*baseURL)
if base == "" {
base = envOr("STACKGEN_URL")
}
token := strings.TrimSpace(*apiToken)
if token == "" {
token = envOr("STACKGEN_TOKEN")
}
org := strings.TrimSpace(*orgID)
if org == "" {
org = envOr("STACKGEN_WORKSPACE_ID", "STACKGEN_ORG_ID", "STACKGEN_PROJECT", "STACKGEN_PROJECT_ID")
}
if base == "" || token == "" {
fmt.Fprintln(os.Stderr, "set STACKGEN_URL and STACKGEN_TOKEN")
os.Exit(2)
}
client, err := stackgen.NewClient(stackgen.Config{
BaseURL: base, APIToken: token, WorkspaceID: org,
TimeoutSeconds: *timeoutSeconds, PollIntervalSeconds: *pollInterval,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
ctx := context.Background()
id := strings.TrimSpace(*requestID)
if strings.TrimSpace(*identifier) != "" {
req, err := client.DevOps.FetchAndWait(ctx, strings.TrimSpace(*identifier))
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
raw, _ := json.MarshalIndent(req, "", " ")
fmt.Println("fetched:", string(raw))
id = fmt.Sprint(req["id"])
} else if id == "" {
listed, err := client.DevOps.ListRequests(ctx, map[string]string{"status": "awaiting_approval", "page_size": "5"})
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
raw, _ := json.MarshalIndent(listed, "", " ")
fmt.Println("candidates:", string(raw))
body, _ := listed.(map[string]any)
rows, _ := body["requests"].([]any)
if len(rows) == 0 {
fmt.Fprintln(os.Stderr, "error: no awaiting_approval requests")
os.Exit(1)
}
first, _ := rows[0].(map[string]any)
id = fmt.Sprint(first["id"])
}
if *dryRun {
fmt.Fprintln(os.Stderr, "request_id=", id)
return
}
finished, err := client.DevOps.ApproveAndWait(ctx, id, *workflowName, "")
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
raw, _ := json.MarshalIndent(finished, "", " ")
fmt.Println("result:", string(raw))
if *comment {
c, err := client.DevOps.PostSourceComment(ctx, id, "")
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
raw, _ = json.MarshalIndent(c, "", " ")
fmt.Println("comment:", string(raw))
}
}Related: Sync inbox