Skip to content

Commit 3a86786

Browse files
committed
add config-update workflow and update_admin_access script
The config-update workflow runs one of the scripts in bin/config_update, commits and PRs the config changes if there are any, and auto-merges that PR if it passes checks. The workflow can be triggered from op-admin-dashboard given that it has credentials to trigger workflows (which are provided through a Github app: https://github.com/settings/apps/op-config-updates) Currently, this works to add/remove admin users from the admin_access list: e-mission/op-admin-dashboard#167 (comment) Tested end-to-end from admin dash: e-mission/op-admin-dashboard#168
1 parent 813d7a5 commit 3a86786

File tree

5 files changed

+162
-0
lines changed

5 files changed

+162
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Update Deployment Config
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
deployment:
7+
required: true
8+
type: string
9+
description: Name of the deployment to update config for (e.g. nrel-commute)
10+
user_email:
11+
required: true
12+
type: string
13+
description: Email of the user triggering the workflow
14+
script_name:
15+
required: true
16+
type: choice
17+
options:
18+
- update_admin_access
19+
script_args:
20+
required: false
21+
type: string
22+
description: Additional arguments for the script (space-separated)
23+
token:
24+
required: false
25+
type: string
26+
description: GitHub token for authentication. If not provided, the workflow will use the default token.
27+
28+
env:
29+
GITHUB_TOKEN: ${{ inputs.token || secrets.GITHUB_TOKEN }}
30+
31+
jobs:
32+
update_config:
33+
runs-on: ubuntu-latest
34+
35+
outputs:
36+
PR_URL: ${{ steps.commit_and_pr.outputs.PR_URL }}
37+
38+
steps:
39+
- name: Checkout code
40+
uses: actions/checkout@v3
41+
42+
- name: Set up Python
43+
uses: actions/setup-python@v4
44+
with:
45+
python-version: '3.9'
46+
47+
- name: Create update branch
48+
id: create_branch
49+
run: |
50+
BRANCH_NAME="config-update-${{ github.event.inputs.script_name }}-${{ github.run_id }}"
51+
git checkout -b $BRANCH_NAME
52+
echo "BRANCH_NAME=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
53+
54+
- name: Run config update script
55+
run: |
56+
python bin/config_update/${{ inputs.script_name }}.py ${{ inputs.deployment }} ${{ inputs.script_args }}
57+
58+
- name: Commit and PR changes if needed
59+
id: commit_and_pr
60+
run: |
61+
git config user.name "GitHub Actions"
62+
git config user.email "actions@github.com"
63+
git add .
64+
git commit \
65+
-m "[${{ inputs.deployment }}] ${{ inputs.script_name }}" \
66+
-m "Updated [${{ inputs.deployment }}] configuration using \`${{ inputs.script_name }}\` with args \`${{ inputs.script_args }}\`" \
67+
-m "This commit was generated by the \`config-update\` workflow: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
68+
-m "Triggered by admin user: ${{ inputs.user_email }}"
69+
git push --set-upstream origin ${{ steps.create_branch.outputs.BRANCH_NAME }}
70+
echo "Creating PR..."
71+
PR_URL=$(gh pr create --base main --head ${{ steps.create_branch.outputs.BRANCH_NAME }} --fill)
72+
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} gh pr merge "$PR_URL" --auto --squash
73+
echo "PR created and will merge if checks pass: $PR_URL"

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
.DS_Store
22
.vscode/settings.json
3+
.env
4+
__pycache__

bin/config_update/_util.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
import sys
3+
import json
4+
5+
6+
def read_config(deployment):
7+
config_file_path = f'configs/{deployment}.nrel-op.json'
8+
if not os.path.exists(config_file_path):
9+
print(f"There is no deployment called {deployment} - no changes made.")
10+
sys.exit(1)
11+
with open(config_file_path, 'r', encoding='utf-8') as f:
12+
return json.load(f)
13+
14+
15+
def update_config(deployment, new_config, msg):
16+
config_file_path = f'configs/{deployment}.nrel-op.json'
17+
if new_config and msg:
18+
with open(config_file_path, 'w', encoding='utf-8') as f:
19+
json.dump(new_config, f, indent=4, ensure_ascii=False)
20+
f.write("\n")
21+
print(f"Updated {config_file_path} with new config.")
22+
else:
23+
print("No changes made.")
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""
2+
Add or remove admin email addresses from admin_dashboard.admin_access
3+
"""
4+
5+
import sys
6+
import argparse
7+
import re
8+
9+
from _util import read_config, update_config
10+
11+
12+
def add_admin_email(config, email):
13+
if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w{2,}$', email):
14+
print(f"{email} is an invalid email format - no changes made.")
15+
sys.exit(1)
16+
17+
if email not in config.get('admin_dashboard', {}).get('admin_access', []):
18+
if 'admin_dashboard' not in config:
19+
config['admin_dashboard'] = {}
20+
if 'admin_access' not in config['admin_dashboard']:
21+
config['admin_dashboard']['admin_access'] = []
22+
config['admin_dashboard']['admin_access'].append(email)
23+
print(f"Added {email} to the admin_access list.")
24+
else:
25+
print(f"{email} is already an admin - no changes made.")
26+
return
27+
28+
return config
29+
30+
31+
def remove_admin_email(config, email):
32+
if email in config.get('admin_dashboard', {}).get('admin_access', []):
33+
config['admin_dashboard']['admin_access'].remove(email)
34+
else:
35+
print(f"{email} is not an admin - no changes made.")
36+
return
37+
38+
return config
39+
40+
41+
def update_admin_access(config, action, email):
42+
new_config = None
43+
if action == 'add':
44+
new_config = add_admin_email(config, email)
45+
elif action == 'remove':
46+
new_config = remove_admin_email(config, email)
47+
48+
return new_config
49+
50+
51+
if __name__ == '__main__':
52+
parser = argparse.ArgumentParser()
53+
parser.add_argument('deployment', type=str)
54+
parser.add_argument('action', type=str, help='Action to perform (add/remove)')
55+
parser.add_argument('email', type=str, help='Email address to add/remove')
56+
args = parser.parse_args()
57+
58+
config = read_config(args.deployment)
59+
new_config = update_admin_access(config, args.action, args.email)
60+
if new_config:
61+
msg = f"update admin_access\n\n - {args.action} {args.email}"
62+
update_config(args.deployment, new_config, msg)
63+
else:
64+
print("No changes made.")
File renamed without changes.

0 commit comments

Comments
 (0)