Skip to content

Commit

Permalink
Merge branch 'jlantz/update-auth-structure' into jlantz/refactor-pyda…
Browse files Browse the repository at this point in the history
…ntic-models
  • Loading branch information
jlantz authored Oct 31, 2024
2 parents 592e824 + ea2df94 commit f8675e3
Show file tree
Hide file tree
Showing 23 changed files with 733 additions and 87 deletions.
Binary file added .coverage
Binary file not shown.
47 changes: 47 additions & 0 deletions .github/workflows/delete-org-session.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Delete Org Session

on:
workflow_call:
inputs:
environment_name:
description: "The name of the GitHub Environment to delete the org session from"
required: true
type: string
github_auth_environment:
description: "The name of the GitHub Environment to get the GitHub Access token from"
required: true
type: string
secrets:
github-token:
required: true

jobs:
delete-org-session:
name: "Delete Org Session"
runs-on: ubuntu-latest
steps:
- name: Get GitHub Access Token
run: |
echo "Retrieving GitHub Access Token from environment: ${{ inputs.github_auth_environment }}"
GITHUB_ACCESS_TOKEN=$(gh api \
-H "Authorization: token ${{ secrets.github-token }}" \
"/repos/${{ github.repository }}/environments/${{ inputs.github_auth_environment }}/variables/GITHUB_ACCESS_TOKEN" \
| jq -r '.value')
echo "GITHUB_ACCESS_TOKEN=${GITHUB_ACCESS_TOKEN}" >> $GITHUB_ENV
shell: bash

- name: Delete Org Session
run: |
echo "Deleting org session from environment: ${{ inputs.environment_name }}"
gh api \
-X DELETE \
-H "Authorization: token ${{ env.GITHUB_ACCESS_TOKEN }}" \
"/repos/${{ github.repository }}/environments/${{ inputs.environment_name }}/variables/ACCESS_TOKEN"
shell: bash

- name: Add Job Summary
run: |
echo "## Org Session Deletion Summary" >> $GITHUB_STEP_SUMMARY
echo "Environment: ${{ inputs.environment_name }}" >> $GITHUB_STEP_SUMMARY
echo "Status: Org session deleted successfully" >> $GITHUB_STEP_SUMMARY
shell: bash
35 changes: 35 additions & 0 deletions .github/workflows/test-github-auth.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Test GitHub Auth

on:
push:
branches:
- "**"

jobs:
test-github-auth:
runs-on: ubuntu-latest
environment: test
container:
image: ghcr.io/muselab-d2x/d2x:cumulusci-next-snapshots
options: --user root
credentials:
username: ${{ github.actor }}
password: ${{ secrets.github-token }}
env:
DEV_HUB_AUTH_URL: "${{ secrets.dev-hub-auth-url }}"
CUMULUSCI_SERVICE_github: '{ "username": "${{ github.actor }}", "token": "${{ secrets.github-token }}", "email": "${{ secrets.gh-email }}" }'
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Auth to DevHub
run: /usr/local/bin/devhub.sh
- name: Test GitHub Auth
run: |
d2x auth url
d2x auth login
shell: bash
- name: Record API Requests
run: |
pip install vcrpy
vcrpy --record-mode=once --filter-headers Authorization --filter-headers X-Auth-Token --filter-headers X-API-Key
shell: bash
15 changes: 14 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,17 @@ jobs:
- name: Run tests
run: |
poetry run pytest tests/
poetry run pytest tests/ --cov=d2x --cov-report=xml --junitxml=tests/results.xml
echo "✅ Tests and coverage completed"
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.xml

- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results
path: tests/results.xml
1 change: 1 addition & 0 deletions d2x/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# d2x.api module
91 changes: 91 additions & 0 deletions d2x/api/gh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import os
import requests

GITHUB_REPO = os.environ.get("GITHUB_REPOSITORY")


def get_github_token() -> str:
"""Get the GitHub token from the environment"""
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable not set")
return token


def get_repo_full_name() -> str:
"""Get the full name of the GitHub repository"""
repo = os.environ.get("GITHUB_REPOSITORY")
if not repo:
raise ValueError("GITHUB_REPOSITORY environment variable not set")
return repo


def set_environment_variable(env_name: str, var_name: str, var_value: str) -> None:
"""Set a variable in a GitHub Environment"""
token = os.environ.get("GITHUB_TOKEN")
repo = os.environ.get("GITHUB_REPOSITORY")
if not token:
raise ValueError("GITHUB_TOKEN environment variable not set")

url = f"https://api.github.com/repos/{GITHUB_REPO}/environments/{env_name}/variables/{var_name}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json",
}
data = {"name": var_name, "value": var_value}

response = requests.put(url, headers=headers, json=data)
response.raise_for_status()


def get_environment_variable(env_name: str, var_name: str) -> str:
"""Get a variable from a GitHub Environment"""
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable not set")

url = f"https://api.github.com/repos/{GITHUB_REPO}/environments/{env_name}/variables/{var_name}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json",
}

response = requests.get(url, headers=headers)
response.raise_for_status()

return response.json()["value"]


def set_environment_secret(env_name: str, secret_name: str, secret_value: str) -> None:
"""Set a secret in a GitHub Environment"""
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable not set")

url = f"https://api.github.com/repos/{GITHUB_REPO}/environments/{env_name}/secrets/{secret_name}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json",
}
data = {"encrypted_value": secret_value}

response = requests.put(url, headers=headers, json=data)
response.raise_for_status()


def get_environment_secret(env_name: str, secret_name: str) -> str:
"""Get a secret from a GitHub Environment"""
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable not set")

url = f"https://api.github.com/repos/{GITHUB_REPO}/environments/{env_name}/secrets/{secret_name}"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github.v3+json",
}

response = requests.get(url, headers=headers)
response.raise_for_status()

return response.json()["encrypted_value"]
2 changes: 1 addition & 1 deletion d2x/auth/sf/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# ...existing code or leave empty...
# This is the __init__.py file for the d2x.auth.sf module.
8 changes: 7 additions & 1 deletion d2x/auth/sf/auth_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
from d2x.ux.gh.actions import summary as gha_summary, output as gha_output
from d2x.models.sf.org import SalesforceOrgInfo
from d2x.base.types import CLIOptions
from d2x.api.gh import set_environment_variable # Add this import


def exchange_token(org_info: SalesforceOrgInfo, cli_options: CLIOptions):
"""Exchange refresh token for access token with detailed error handling"""
console = cli_options.console
debug_info = None # Initialize debug_info before the try block
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
Expand Down Expand Up @@ -122,10 +124,14 @@ def exchange_token(org_info: SalesforceOrgInfo, cli_options: CLIOptions):
)
console.print(success_panel)

# Store access token in GitHub Environment
set_environment_variable("salesforce", "ACCESS_TOKEN", token_response.access_token.get_secret_value())

return token_response

except Exception as e:
debug_info.error = str(e)
if debug_info is not None:
debug_info.error = str(e)
error_panel = Panel(
f"[red]Error: {str(e)}",
title="[red]Authentication Failed",
Expand Down
12 changes: 7 additions & 5 deletions d2x/auth/sf/login_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,25 @@ def main(cli_options: CLIOptions):
"Salesforce Auth Url not found. Set the SFDX_AUTH_URL environment variable."
)


org_info = SfdxAuthUrlModel(auth_url=auth_url).parse_sfdx_auth_url()

from d2x.auth.sf.auth_url import exchange_token

try:
token_response = exchange_token(org_info, cli_options)
access_token = get_environment_variable("salesforce", "ACCESS_TOKEN")
except Exception as e:
console.print(f"[red]Error: {e}")
console.print(f"[red]Error retrieving access token: {e}")
sys.exit(1)

start_url = generate_login_url(
instance_url=token_response.instance_url,
access_token=token_response.access_token.get_secret_value(),
instance_url=org_info.auth_info.instance_url,
access_token=access_token,
)

output("access_token", token_response.access_token.get_secret_value())
output("instance_url", token_response.instance_url)

output("start_url", start_url)
output("org_type", org_info["org_type"])

Expand All @@ -65,7 +67,7 @@ def main(cli_options: CLIOptions):
- **Status**: ✅ Success
- **Timestamp**: {token_response.issued_at.strftime('%Y-%m-%d %H:%M:%S')}
- **Token Expiry**: {token_response.expires_in} seconds
- **Instance URL**: {token_response.instance_url}
- **Instance URL**: {org_info.auth_info.instance_url}
"""
summary(summary_md)

Expand Down
1 change: 1 addition & 0 deletions d2x/base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# d2x.base
79 changes: 79 additions & 0 deletions d2x/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from d2x.base.types import OutputFormat, OutputFormatType, CLIOptions
from typing import Optional
from importlib.metadata import version, PackageNotFoundError
from d2x.env.gh import set_environment_variable, get_environment_variable, set_environment_secret, get_environment_secret

# Disable rich_click's syntax highlighting
click.SHOW_ARGUMENTS = False
Expand Down Expand Up @@ -79,6 +80,84 @@ def url(output_format: OutputFormatType, debug: bool):
raise


@d2x_cli.group()
def env():
"""Environment commands"""
pass


@env.command()
@click.argument("env_name")
@click.argument("var_name")
@click.argument("var_value")
@common_options
def set_var(env_name: str, var_name: str, var_value: str, output_format: OutputFormatType, debug: bool):
"""Set an environment variable"""
cli_options = CLIOptions(output_format=output_format, debug=debug)
try:
set_environment_variable(env_name, var_name, var_value)
except:
if debug:
type, value, tb = sys.exc_info()
pdb.post_mortem(tb)
else:
raise


@env.command()
@click.argument("env_name")
@click.argument("var_name")
@common_options
def get_var(env_name: str, var_name: str, output_format: OutputFormatType, debug: bool):
"""Get an environment variable"""
cli_options = CLIOptions(output_format=output_format, debug=debug)
try:
value = get_environment_variable(env_name, var_name)
click.echo(value)
except:
if debug:
type, value, tb = sys.exc_info()
pdb.post_mortem(tb)
else:
raise


@env.command()
@click.argument("env_name")
@click.argument("secret_name")
@click.argument("secret_value")
@common_options
def set_secret(env_name: str, secret_name: str, secret_value: str, output_format: OutputFormatType, debug: bool):
"""Set an environment secret"""
cli_options = CLIOptions(output_format=output_format, debug=debug)
try:
set_environment_secret(env_name, secret_name, secret_value)
except:
if debug:
type, value, tb = sys.exc_info()
pdb.post_mortem(tb)
else:
raise


@env.command()
@click.argument("env_name")
@click.argument("secret_name")
@common_options
def get_secret(env_name: str, secret_name: str, output_format: OutputFormatType, debug: bool):
"""Get an environment secret"""
cli_options = CLIOptions(output_format=output_format, debug=debug)
try:
value = get_environment_secret(env_name, secret_name)
click.echo(value)
except:
if debug:
type, value, tb = sys.exc_info()
pdb.post_mortem(tb)
else:
raise


def get_cli():
"""Get the CLI entry point"""
return d2x_cli
Expand Down
Loading

0 comments on commit f8675e3

Please sign in to comment.