Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to update scm project using github app #15472

Open
wants to merge 1 commit into
base: devel
Choose a base branch
from

Conversation

TheRealHaoLiu
Copy link
Member

SUMMARY
  • Add github_app_id, github_app_installation_id and github_api_url to scm credential type
  • Add ability to generate github app token to clone project with git for github
ISSUE TYPE
  • New or Enhanced Feature
COMPONENT NAME
  • API
AWX VERSION

ADDITIONAL INFORMATION

- Add github_app_id, github_app_installation_id and github_api_url to scm credential type
- Add ability to generate github app token to clone project with git for github
Copy link

sonarcloud bot commented Aug 28, 2024

@chrismeyersfsu
Copy link
Member

This should be a different credential.

@@ -645,6 +645,9 @@ def create(self):
{'id': 'password', 'label': gettext_noop('Password'), 'type': 'string', 'secret': True},
{'id': 'ssh_key_data', 'label': gettext_noop('SCM Private Key'), 'type': 'string', 'format': 'ssh_private_key', 'secret': True, 'multiline': True},
{'id': 'ssh_key_unlock', 'label': gettext_noop('Private Key Passphrase'), 'type': 'string', 'secret': True},
{'id': 'github_app_id', 'label': gettext_noop('GitHub App ID'), 'type': 'string'},
{'id': 'github_app_installation_id', 'label': gettext_noop('GitHub App Installation ID'), 'type': 'string'},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you trying to surface this into a UI field? This is usually discoverable automatically.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

automatic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So ideally, there should be no UI inputs at all. Log into https://pre-commit.ci or Codecov / RTD to see their UX for working with repos accessible through GH app installations.

Whenever the app is installed, you already get an event with all the information needed via webhooks.

@@ -645,6 +645,9 @@ def create(self):
{'id': 'password', 'label': gettext_noop('Password'), 'type': 'string', 'secret': True},
{'id': 'ssh_key_data', 'label': gettext_noop('SCM Private Key'), 'type': 'string', 'format': 'ssh_private_key', 'secret': True, 'multiline': True},
{'id': 'ssh_key_unlock', 'label': gettext_noop('Private Key Passphrase'), 'type': 'string', 'secret': True},
{'id': 'github_app_id', 'label': gettext_noop('GitHub App ID'), 'type': 'string'},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this also surfaced to the end-users? Why? It should be a platform-global value, it's not directly a credential.

{
'iat': int(time.time()), # Issued at time
'exp': int(time.time()) + (10 * 60), # JWT expiration time (10 minute maximum)
'iss': project_update.credential.get_input('github_app_id', default=''), # GitHub App's identifier
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GH App should be global for the entire AWX. The whole idea is that the users don't need to set up and maintain multiple apps. They just need to click “Install” on GH UI.


headers = {'Authorization': f'Bearer {jwt_token}', 'Accept': 'application/vnd.github.v3+json'}

github_api_url = project_update.credential.get_input('github_api_url', default='https://api.github.com')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This as well should be a deployment-global value rather than bound to a credential.

@AlanCoding
Copy link
Member

On @chrismeyersfsu 's point, the intent was never for the Project.credential field to validate the literal "Source Control" credential type.

elif cred.kind != 'scm':
raise ValidationError(_("Credential kind must be 'scm'."))

Instead, it only wants the kind of the credential to be "scm", that, I believe (incorrectly) maps to the credential type namespace

@property
def kind(self):
return self.credential_type.namespace

Again, I think that's wrong. When the Project.credential field is validated, I think it should require that the related credential_type.kind is "scm".

kind = models.CharField(max_length=32, choices=KIND_CHOICES)

you can see the choices:

KIND_CHOICES = (
('ssh', _('Machine')),
('vault', _('Vault')),
('net', _('Network')),
('scm', _('Source Control')),
('cloud', _('Cloud')),
('registry', _('Container Registry')),
('token', _('Personal Access Token')),
('insights', _('Insights')),
('external', _('External')),
('kubernetes', _('Kubernetes')),
('galaxy', _('Galaxy/Automation Hub')),
('cryptography', _('Cryptography')),
)

This CredentialType "kind" designation is like an intended use of that credential type. So any "scm" kind of credential type should be acceptable for use in a project.

headers = {'Authorization': f'Bearer {jwt_token}', 'Accept': 'application/vnd.github.v3+json'}

github_api_url = project_update.credential.get_input('github_api_url', default='https://api.github.com')
installation_id = project_update.credential.get_input('github_app_installation_id', default='')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be discovered from the GitHub repository URL and querying https://docs.github.com/en/rest/apps/installations?apiVersion=2022-11-28#list-app-installations-accessible-to-the-user-access-token, and stored somewhere in the DB, not surfaced to the users.

access_token = response.json()['token']
return access_token
else:
raise Exception(f"Failed to get access token: {response.status_code} {response.text}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance not to use an unspecified exception?

url = f'{github_api_url}/app/installations/{installation_id}/access_tokens'
response = requests.post(url, headers=headers)

if response.status_code == 201:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you invert it to !=, you can dedent a few lines of code.

@TheRealHaoLiu
Copy link
Member Author

@webknjaz for this implementation we just want to scope this to specifically project update and also this will give the ability for people to have different github app for different projects if desired

@webknjaz
Copy link
Member

also this will give the ability for people to have different github app for different projects if desired

That's kinda against the entire idea of GH Apps 🤷‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants