From oraclecloud-pack
Configures OCI multi-environment workflows with config profiles, compartment OCIDs, and validation to separate dev/staging/prod and prevent production accidents.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin oraclecloud-packThis skill is limited to using the following tools:
OCI has no "accounts" like AWS — you use compartments plus OCI config profiles for dev/staging/prod separation. But profile switching is manual, compartment OCIDs are easy to confuse, and one wrong `--compartment-id` deploys to production. This skill sets up safe multi-environment workflows with named profiles, compartment aliasing, environment validation, and deployment guardrails.
Sets up local OCI CLI/SDK dev workflow with multi-profile config, shell aliases, and env vars for dev/staging/prod. Replaces slow web console for resource ops.
Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). **PROACTIVE ACTIVATION**: Use this skill automatically when working in Next.js projects that have `cacheComponents: true` in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations. **DETECTION**: At the start of a session in a Next.js project, check for `cacheComponents: true` in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions. **USE CASES**: Implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, debugging cache issues, and reviewing Cache Component implementations.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
OCI has no "accounts" like AWS — you use compartments plus OCI config profiles for dev/staging/prod separation. But profile switching is manual, compartment OCIDs are easy to confuse, and one wrong --compartment-id deploys to production. This skill sets up safe multi-environment workflows with named profiles, compartment aliasing, environment validation, and deployment guardrails.
Purpose: Configure safe, repeatable multi-environment OCI workflows that prevent accidental cross-environment operations.
pip install ocioci setup config)The OCI config file supports named profiles. Each profile can point to different tenancies, regions, or use different API keys:
# ~/.oci/config
[DEFAULT]
user=ocid1.user.oc1..exampleuniqueID
fingerprint=aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99
tenancy=ocid1.tenancy.oc1..exampleuniqueID
region=us-ashburn-1
key_file=~/.oci/oci_api_key.pem
[DEV]
user=ocid1.user.oc1..exampleuniqueID
fingerprint=aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99
tenancy=ocid1.tenancy.oc1..exampleuniqueID
region=us-ashburn-1
key_file=~/.oci/oci_api_key_dev.pem
[STAGING]
user=ocid1.user.oc1..exampleuniqueID
fingerprint=11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00
tenancy=ocid1.tenancy.oc1..exampleuniqueID
region=us-phoenix-1
key_file=~/.oci/oci_api_key_staging.pem
[PROD]
user=ocid1.user.oc1..exampleuniqueID
fingerprint=ff:ee:dd:cc:bb:aa:00:99:88:77:66:55:44:33:22:11
tenancy=ocid1.tenancy.oc1..exampleuniqueID
region=us-ashburn-1
key_file=~/.oci/oci_api_key_prod.pem
Best practice: Use different API key pairs per environment. If the dev key is compromised, prod is unaffected.
Centralize compartment OCIDs and profile mappings to prevent OCID confusion:
import oci
import os
# Environment configuration — single source of truth for OCIDs
ENVIRONMENTS = {
"dev": {
"profile": "DEV",
"compartment_id": "ocid1.compartment.oc1..dev_example",
"region": "us-ashburn-1",
"allow_destructive": True,
},
"staging": {
"profile": "STAGING",
"compartment_id": "ocid1.compartment.oc1..staging_example",
"region": "us-phoenix-1",
"allow_destructive": True,
},
"prod": {
"profile": "PROD",
"compartment_id": "ocid1.compartment.oc1..prod_example",
"region": "us-ashburn-1",
"allow_destructive": False, # Safety: block destructive ops
},
}
def get_oci_config(env_name):
"""Load OCI config for the specified environment.
Validates the environment name and returns a configured
OCI config dict ready for client construction.
"""
if env_name not in ENVIRONMENTS:
raise ValueError(
f"Unknown environment: {env_name}. "
f"Valid: {list(ENVIRONMENTS.keys())}"
)
env = ENVIRONMENTS[env_name]
config = oci.config.from_file("~/.oci/config", profile_name=env["profile"])
oci.config.validate_config(config)
return config, env
def get_compartment_id(env_name):
"""Get the compartment OCID for an environment."""
return ENVIRONMENTS[env_name]["compartment_id"]
def is_destructive_allowed(env_name):
"""Check if destructive operations are allowed in this environment."""
return ENVIRONMENTS[env_name]["allow_destructive"]
Wrap OCI clients with environment validation to prevent cross-environment mistakes:
import oci
import sys
class OCIEnvironment:
"""Environment-aware OCI client factory with safety guardrails."""
def __init__(self, env_name):
self.env_name = env_name
self.config, self.env = get_oci_config(env_name)
self.compartment_id = self.env["compartment_id"]
# Verify we can authenticate
identity = oci.identity.IdentityClient(self.config)
user = identity.get_user(self.config["user"]).data
print(f"[{env_name.upper()}] Authenticated as {user.name} "
f"in {self.config['region']}")
def compute(self):
return oci.core.ComputeClient(self.config)
def network(self):
return oci.core.VirtualNetworkClient(self.config)
def storage(self):
return oci.object_storage.ObjectStorageClient(self.config)
def database(self):
return oci.database.DatabaseClient(self.config)
def safe_delete(self, operation, resource_name):
"""Execute a delete operation with environment safety checks."""
if not is_destructive_allowed(self.env_name):
print(f"BLOCKED: Destructive operation on {resource_name} "
f"not allowed in {self.env_name.upper()}")
print("Set allow_destructive=True in ENVIRONMENTS to override")
sys.exit(1)
print(f"WARNING: Deleting {resource_name} in {self.env_name.upper()}")
return operation()
# Usage
dev = OCIEnvironment("dev")
instances = dev.compute().list_instances(compartment_id=dev.compartment_id).data
print(f"Dev instances: {len(instances)}")
prod = OCIEnvironment("prod")
# prod.safe_delete(...) would be blocked by allow_destructive=False
Use profiles with the OCI CLI to target specific environments:
# List instances in dev
oci compute instance list --compartment-id ocid1.compartment.oc1..dev_example --profile DEV
# List instances in prod (read-only)
oci compute instance list --compartment-id ocid1.compartment.oc1..prod_example --profile PROD
# Set default profile via environment variable
export OCI_CLI_PROFILE=DEV
# Override per-command
OCI_CLI_PROFILE=PROD oci compute instance list --compartment-id ocid1.compartment.oc1..prod_example
Shell aliases for safety:
# Add to ~/.bashrc or ~/.zshrc
alias oci-dev='OCI_CLI_PROFILE=DEV oci'
alias oci-staging='OCI_CLI_PROFILE=STAGING oci'
alias oci-prod='OCI_CLI_PROFILE=PROD oci'
# Usage
oci-dev compute instance list --compartment-id ocid1.compartment.oc1..dev_example
oci-prod compute instance list --compartment-id ocid1.compartment.oc1..prod_example
Always validate the config file and profile before running automation:
import oci
def validate_all_profiles():
"""Validate all OCI config profiles are properly configured."""
profiles = ["DEFAULT", "DEV", "STAGING", "PROD"]
results = {}
for profile in profiles:
try:
config = oci.config.from_file("~/.oci/config", profile_name=profile)
oci.config.validate_config(config)
# Test authentication
identity = oci.identity.IdentityClient(config)
user = identity.get_user(config["user"]).data
results[profile] = f"OK — {user.name} in {config['region']}"
except oci.exceptions.ConfigFileNotFound:
results[profile] = "FAIL — config file not found"
except oci.exceptions.ProfileNotFound:
results[profile] = "FAIL — profile not found in config"
except oci.exceptions.ServiceError as e:
results[profile] = f"FAIL — {e.status}: {e.code}"
except Exception as e:
results[profile] = f"FAIL — {str(e)}"
print("OCI Profile Validation:")
for profile, status in results.items():
print(f" [{profile}] {status}")
return all("OK" in s for s in results.values())
validate_all_profiles()
For CI/CD pipelines where config files are impractical, use environment variables:
# Set OCI config via environment variables (CI/CD pipelines)
export OCI_CLI_USER="ocid1.user.oc1..exampleuniqueID"
export OCI_CLI_FINGERPRINT="aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99"
export OCI_CLI_TENANCY="ocid1.tenancy.oc1..exampleuniqueID"
export OCI_CLI_REGION="us-ashburn-1"
export OCI_CLI_KEY_FILE="/path/to/key.pem"
# Or use key content directly:
export OCI_CLI_KEY_CONTENT="-----BEGIN RSA PRIVATE KEY-----\n..."
import oci
# Python SDK reads from environment when no config file exists
config = oci.config.from_file() # Falls back to env vars if ~/.oci/config missing
# Or construct config dict directly for CI/CD
config = {
"user": os.environ["OCI_CLI_USER"],
"fingerprint": os.environ["OCI_CLI_FINGERPRINT"],
"tenancy": os.environ["OCI_CLI_TENANCY"],
"region": os.environ["OCI_CLI_REGION"],
"key_file": os.environ["OCI_CLI_KEY_FILE"],
}
oci.config.validate_config(config)
Successful completion produces:
~/.oci/config file with named profiles for each environment (DEV, STAGING, PROD)| Error | Code | Cause | Solution |
|---|---|---|---|
| ProfileNotFound | — | Wrong profile name in from_file() | Check ~/.oci/config profile names match exactly (case-sensitive) |
| ConfigFileNotFound | — | Missing ~/.oci/config | Run oci setup config or create the file manually |
| NotAuthenticated | 401 | Wrong key for the selected profile | Verify key_file path and fingerprint match the uploaded public key |
| NotAuthorizedOrNotFound | 404 | Profile's user lacks access to target compartment | Add IAM policies for the user/group in the target compartment |
| TooManyRequests | 429 | Rate limited | Back off; see oraclecloud-rate-limits |
| InternalError | 500 | OCI service error | Retry after 30s; check https://ocistatus.oraclecloud.com |
Critical mistake: Using the DEFAULT profile's compartment OCID with the PROD profile's credentials (or vice versa). The environment config module in Step 2 prevents this by coupling profile names to compartment OCIDs.
Quick profile test from the command line:
# Test all profiles in one shot
for profile in DEFAULT DEV STAGING PROD; do
echo -n "[$profile] "
oci iam user get --user-id "$(oci iam user list --profile "$profile" --query 'data[0].id' --raw-output 2>/dev/null)" --profile "$profile" --query 'data.name' --raw-output 2>/dev/null || echo "FAILED"
done
Check which profile is active:
import oci
import os
profile = os.environ.get("OCI_CLI_PROFILE", "DEFAULT")
config = oci.config.from_file("~/.oci/config", profile_name=profile)
print(f"Active profile: {profile}")
print(f"Region: {config['region']}")
print(f"Tenancy: {config['tenancy']}")
After environments are configured, see oraclecloud-enterprise-rbac for compartment hierarchy design with least-privilege policies, or oraclecloud-deploy-integration for CI/CD pipeline integration.