commit
2a13eade80
35 changed files with 797 additions and 136 deletions
|
@ -93,8 +93,6 @@ class BuildComponent(BaseComponent):
|
|||
self._build_failure('Could not load build job information', irbe)
|
||||
|
||||
base_image_information = {}
|
||||
buildpack_url = self.user_files.get_file_url(build_job.repo_build.resource_key,
|
||||
requires_cors=False)
|
||||
|
||||
# Add the pull robot information, if any.
|
||||
if build_job.pull_credentials:
|
||||
|
@ -107,6 +105,7 @@ class BuildComponent(BaseComponent):
|
|||
|
||||
# Parse the build queue item into build arguments.
|
||||
# build_package: URL to the build package to download and untar/unzip.
|
||||
# defaults to empty string to avoid requiring a pointer on the builder.
|
||||
# sub_directory: The location within the build package of the Dockerfile and the build context.
|
||||
# repository: The repository for which this build is occurring.
|
||||
# registry: The registry for which this build is occuring (e.g. 'quay.io', 'staging.quay.io').
|
||||
|
@ -119,16 +118,30 @@ class BuildComponent(BaseComponent):
|
|||
# username: The username for pulling the base image (if any).
|
||||
# password: The password for pulling the base image (if any).
|
||||
build_arguments = {
|
||||
'build_package': buildpack_url,
|
||||
'sub_directory': build_config.get('build_subdir', ''),
|
||||
'repository': repository_name,
|
||||
'registry': self.registry_hostname,
|
||||
'pull_token': build_job.repo_build.access_token.code,
|
||||
'push_token': build_job.repo_build.access_token.code,
|
||||
'tag_names': build_config.get('docker_tags', ['latest']),
|
||||
'base_image': base_image_information
|
||||
'build_package': self.user_files.get_file_url(build_job.repo_build.resource_key,
|
||||
requires_cors=False)
|
||||
if build_job.repo_build.resource_key is not None else "",
|
||||
'sub_directory': build_config.get('build_subdir', ''),
|
||||
'repository': repository_name,
|
||||
'registry': self.registry_hostname,
|
||||
'pull_token': build_job.repo_build.access_token.code,
|
||||
'push_token': build_job.repo_build.access_token.code,
|
||||
'tag_names': build_config.get('docker_tags', ['latest']),
|
||||
'base_image': base_image_information,
|
||||
}
|
||||
|
||||
# If the trigger has a private key, it's using git, thus we should add
|
||||
# git data to the build args.
|
||||
# url: url used to clone the git repository
|
||||
# sha: the sha1 identifier of the commit to check out
|
||||
# private_key: the key used to get read access to the git repository
|
||||
if build_job.repo_build.trigger.private_key is not None:
|
||||
build_arguments['git'] = {
|
||||
'url': build_config['trigger_metadata'].get('git_url', ''),
|
||||
'sha': build_config['trigger_metadata'].get('commit_sha', ''),
|
||||
'private_key': build_job.repo_build.trigger.private_key,
|
||||
}
|
||||
|
||||
# Invoke the build.
|
||||
logger.debug('Invoking build: %s', self.builder_realm)
|
||||
logger.debug('With Arguments: %s', build_arguments)
|
||||
|
|
|
@ -389,7 +389,8 @@ class RepositoryBuildTrigger(BaseModel):
|
|||
service = ForeignKeyField(BuildTriggerService, index=True)
|
||||
repository = ForeignKeyField(Repository, index=True)
|
||||
connected_user = QuayUserField()
|
||||
auth_token = CharField()
|
||||
auth_token = CharField(null=True)
|
||||
private_key = TextField(null=True)
|
||||
config = TextField(default='{}')
|
||||
write_token = ForeignKeyField(AccessToken, null=True)
|
||||
pull_robot = QuayUserField(allows_robots=True, null=True, related_name='triggerpullrobot')
|
||||
|
@ -535,7 +536,7 @@ class RepositoryBuild(BaseModel):
|
|||
uuid = CharField(default=uuid_generator, index=True)
|
||||
repository = ForeignKeyField(Repository, index=True)
|
||||
access_token = ForeignKeyField(AccessToken)
|
||||
resource_key = CharField(index=True)
|
||||
resource_key = CharField(index=True, null=True)
|
||||
job_config = TextField()
|
||||
phase = CharField(default=BUILD_PHASE.WAITING)
|
||||
started = DateTimeField(default=datetime.now)
|
||||
|
|
|
@ -2,7 +2,7 @@ set -e
|
|||
|
||||
DOCKER_IP=`echo $DOCKER_HOST | sed 's/tcp:\/\///' | sed 's/:.*//'`
|
||||
MYSQL_CONFIG_OVERRIDE="{\"DB_URI\":\"mysql+pymysql://root:password@$DOCKER_IP/genschema\"}"
|
||||
PERCONA_CONFIG_OVERRIDE="{\"DB_URI\":\"mysql+pymysql://root@$DOCKER_IP/genschema\"}"
|
||||
PERCONA_CONFIG_OVERRIDE="{\"DB_URI\":\"mysql+pymysql://root:password@$DOCKER_IP/genschema\"}"
|
||||
PGSQL_CONFIG_OVERRIDE="{\"DB_URI\":\"postgresql://postgres@$DOCKER_IP/genschema\"}"
|
||||
|
||||
up_mysql() {
|
||||
|
@ -41,14 +41,14 @@ down_mariadb() {
|
|||
|
||||
up_percona() {
|
||||
# Run a SQL database on port 3306 inside of Docker.
|
||||
docker run --name percona -p 3306:3306 -d percona
|
||||
docker run --name percona -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -d percona
|
||||
|
||||
# Sleep for 10s
|
||||
echo 'Sleeping for 10...'
|
||||
sleep 10
|
||||
|
||||
# Add the daabase to mysql.
|
||||
docker run --rm --link percona:percona percona sh -c 'echo "create database genschema" | mysql -h $PERCONA_PORT_3306_TCP_ADDR'
|
||||
docker run --rm --link percona:percona percona sh -c 'echo "create database genschema" | mysql -h $PERCONA_PORT_3306_TCP_ADDR -uroot -ppassword'
|
||||
}
|
||||
|
||||
down_percona() {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
"""add private key to build triggers
|
||||
|
||||
Revision ID: 214350b6a8b1
|
||||
Revises: 2b2529fd23ff
|
||||
Create Date: 2015-03-19 14:23:52.604505
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '214350b6a8b1'
|
||||
down_revision = '67eb43c778b'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade(tables):
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('repositorybuildtrigger', sa.Column('private_key', sa.Text(), nullable=True))
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade(tables):
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('repositorybuildtrigger', 'private_key')
|
||||
### end Alembic commands ###
|
|
@ -0,0 +1,30 @@
|
|||
"""make resource_key nullable
|
||||
|
||||
Revision ID: 31288f79df53
|
||||
Revises: 214350b6a8b1
|
||||
Create Date: 2015-03-23 14:34:04.816295
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '31288f79df53'
|
||||
down_revision = '214350b6a8b1'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
def upgrade(tables):
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('repositorybuild', 'resource_key',
|
||||
existing_type=mysql.VARCHAR(length=255),
|
||||
nullable=True)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade(tables):
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('repositorybuild', 'resource_key',
|
||||
existing_type=mysql.VARCHAR(length=255),
|
||||
nullable=False)
|
||||
### end Alembic commands ###
|
|
@ -0,0 +1,30 @@
|
|||
"""make auth_token nullable
|
||||
|
||||
Revision ID: 3fee6f979c2a
|
||||
Revises: 31288f79df53
|
||||
Create Date: 2015-03-27 11:11:24.046996
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3fee6f979c2a'
|
||||
down_revision = '31288f79df53'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
def upgrade(tables):
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('repositorybuildtrigger', 'auth_token',
|
||||
existing_type=mysql.VARCHAR(length=255),
|
||||
nullable=True)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade(tables):
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('repositorybuildtrigger', 'auth_token',
|
||||
existing_type=mysql.VARCHAR(length=255),
|
||||
nullable=False)
|
||||
### end Alembic commands ###
|
|
@ -43,14 +43,13 @@ def user_view(user):
|
|||
'is_robot': user.robot,
|
||||
}
|
||||
|
||||
def trigger_view(trigger):
|
||||
|
||||
def trigger_view(trigger, can_admin=False):
|
||||
if trigger and trigger.uuid:
|
||||
config_dict = get_trigger_config(trigger)
|
||||
build_trigger = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
return {
|
||||
'service': trigger.service.name,
|
||||
'config': config_dict,
|
||||
'config': config_dict if can_admin else {},
|
||||
'id': trigger.uuid,
|
||||
'connected_user': trigger.connected_user.username,
|
||||
'is_active': build_trigger.is_active(config_dict),
|
||||
|
@ -60,7 +59,7 @@ def trigger_view(trigger):
|
|||
return None
|
||||
|
||||
|
||||
def build_status_view(build_obj, can_write=False):
|
||||
def build_status_view(build_obj, can_write=False, can_admin=False):
|
||||
phase = build_obj.phase
|
||||
try:
|
||||
status = build_logs.get_status(build_obj.uuid)
|
||||
|
@ -92,7 +91,7 @@ def build_status_view(build_obj, can_write=False):
|
|||
'status': status or {},
|
||||
'job_config': get_job_config(build_obj) if can_write else None,
|
||||
'is_writer': can_write,
|
||||
'trigger': trigger_view(build_obj.trigger),
|
||||
'trigger': trigger_view(build_obj.trigger, can_admin),
|
||||
'resource_key': build_obj.resource_key,
|
||||
'pull_robot': user_view(build_obj.pull_robot) if build_obj.pull_robot else None,
|
||||
'repository': {
|
||||
|
@ -101,7 +100,7 @@ def build_status_view(build_obj, can_write=False):
|
|||
}
|
||||
}
|
||||
|
||||
if can_write:
|
||||
if can_write and build_obj.resource_key is not None:
|
||||
resp['archive_url'] = user_files.get_file_url(build_obj.resource_key, requires_cors=True)
|
||||
|
||||
return resp
|
||||
|
@ -208,7 +207,7 @@ class RepositoryBuildList(RepositoryParamResource):
|
|||
build_request = start_build(repo, dockerfile_id, tags, display_name, subdir, True,
|
||||
pull_robot_name=pull_robot_name)
|
||||
|
||||
resp = build_status_view(build_request, True)
|
||||
resp = build_status_view(build_request, can_write=True)
|
||||
repo_string = '%s/%s' % (namespace, repository)
|
||||
headers = {
|
||||
'Location': api.url_for(RepositoryBuildStatus, repository=repo_string,
|
||||
|
|
|
@ -41,7 +41,7 @@ class BuildTriggerList(RepositoryParamResource):
|
|||
""" List the triggers for the specified repository. """
|
||||
triggers = model.list_build_triggers(namespace, repository)
|
||||
return {
|
||||
'triggers': [trigger_view(trigger) for trigger in triggers]
|
||||
'triggers': [trigger_view(trigger, can_admin=True) for trigger in triggers]
|
||||
}
|
||||
|
||||
|
||||
|
@ -60,7 +60,7 @@ class BuildTrigger(RepositoryParamResource):
|
|||
except model.InvalidBuildTriggerException:
|
||||
raise NotFound()
|
||||
|
||||
return trigger_view(trigger)
|
||||
return trigger_view(trigger, can_admin=True)
|
||||
|
||||
@require_repo_admin
|
||||
@nickname('deleteBuildTrigger')
|
||||
|
@ -207,24 +207,25 @@ class BuildTriggerActivate(RepositoryParamResource):
|
|||
# Update the config.
|
||||
new_config_dict = request.get_json()['config']
|
||||
|
||||
token_name = 'Build Trigger: %s' % trigger.service.name
|
||||
token = model.create_delegate_token(namespace, repository, token_name,
|
||||
'write')
|
||||
write_token_name = 'Build Trigger: %s' % trigger.service.name
|
||||
write_token = model.create_delegate_token(namespace, repository, write_token_name,
|
||||
'write')
|
||||
|
||||
try:
|
||||
path = url_for('webhooks.build_trigger_webhook', trigger_uuid=trigger.uuid)
|
||||
authed_url = _prepare_webhook_url(app.config['PREFERRED_URL_SCHEME'], '$token', token.code,
|
||||
authed_url = _prepare_webhook_url(app.config['PREFERRED_URL_SCHEME'],
|
||||
'$token', write_token.code,
|
||||
app.config['SERVER_HOSTNAME'], path)
|
||||
|
||||
final_config = handler.activate(trigger.uuid, authed_url,
|
||||
final_config, trigger.private_key = handler.activate(trigger.uuid, authed_url,
|
||||
trigger.auth_token, new_config_dict)
|
||||
except TriggerActivationException as exc:
|
||||
token.delete_instance()
|
||||
write_token.delete_instance()
|
||||
raise request_error(message=exc.message)
|
||||
|
||||
# Save the updated config.
|
||||
trigger.config = json.dumps(final_config)
|
||||
trigger.write_token = token
|
||||
trigger.write_token = write_token
|
||||
trigger.save()
|
||||
|
||||
# Log the trigger setup.
|
||||
|
@ -235,7 +236,7 @@ class BuildTriggerActivate(RepositoryParamResource):
|
|||
'pull_robot': trigger.pull_robot.username if trigger.pull_robot else None,
|
||||
'config': final_config}, repo=repo)
|
||||
|
||||
return trigger_view(trigger)
|
||||
return trigger_view(trigger, can_admin=True)
|
||||
else:
|
||||
raise Unauthorized()
|
||||
|
||||
|
@ -373,6 +374,10 @@ class BuildTriggerAnalyze(RepositoryParamResource):
|
|||
'status': 'error',
|
||||
'message': rre.message
|
||||
}
|
||||
except NotImplementedError:
|
||||
return {
|
||||
'status': 'notimplemented',
|
||||
}
|
||||
|
||||
raise NotFound()
|
||||
|
||||
|
@ -392,6 +397,10 @@ class ActivateBuildTrigger(RepositoryParamResource):
|
|||
'branch_name': {
|
||||
'type': 'string',
|
||||
'description': '(GitHub Only) If specified, the name of the GitHub branch to build.'
|
||||
},
|
||||
'commit_sha': {
|
||||
'type': 'string',
|
||||
'description': '(Custom Only) If specified, the ref/SHA1 used to checkout a git repository.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +423,7 @@ class ActivateBuildTrigger(RepositoryParamResource):
|
|||
|
||||
try:
|
||||
run_parameters = request.get_json()
|
||||
specs = handler.manual_start(trigger.auth_token, config_dict, run_parameters=run_parameters)
|
||||
specs = handler.manual_start(trigger, run_parameters=run_parameters)
|
||||
dockerfile_id, tags, name, subdir, metadata = specs
|
||||
|
||||
repo = model.get_repository(namespace, repository)
|
||||
|
@ -426,7 +435,7 @@ class ActivateBuildTrigger(RepositoryParamResource):
|
|||
except TriggerStartException as tse:
|
||||
raise InvalidRequest(tse.message)
|
||||
|
||||
resp = build_status_view(build_request, True)
|
||||
resp = build_status_view(build_request, can_write=True)
|
||||
repo_string = '%s/%s' % (namespace, repository)
|
||||
headers = {
|
||||
'Location': api.url_for(RepositoryBuildStatus, repository=repo_string,
|
||||
|
@ -450,7 +459,7 @@ class TriggerBuildList(RepositoryParamResource):
|
|||
builds = list(model.list_trigger_builds(namespace, repository,
|
||||
trigger_uuid, limit))
|
||||
return {
|
||||
'builds': [build_status_view(build, True) for build in builds]
|
||||
'builds': [build_status_view(build, can_write=True) for build in builds]
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -295,7 +295,7 @@ def attach_github_build_trigger(namespace, repository):
|
|||
trigger.uuid)
|
||||
|
||||
|
||||
logger.debug('Redirecting to full url: %s' % full_url)
|
||||
logger.debug('Redirecting to full url: %s', full_url)
|
||||
return redirect(full_url)
|
||||
|
||||
abort(403)
|
||||
|
|
|
@ -4,12 +4,15 @@ import os.path
|
|||
import tarfile
|
||||
import base64
|
||||
import re
|
||||
import json
|
||||
|
||||
from github import Github, UnknownObjectException, GithubException
|
||||
from tempfile import SpooledTemporaryFile
|
||||
from jsonschema import validate
|
||||
|
||||
from app import app, userfiles as user_files, github_trigger
|
||||
from util.tarfileappender import TarfileAppender
|
||||
from util.ssh import generate_ssh_keypair
|
||||
|
||||
|
||||
client = app.config['HTTPCLIENT']
|
||||
|
@ -25,6 +28,8 @@ CHUNK_SIZE = 512 * 1024
|
|||
def should_skip_commit(message):
|
||||
return '[skip build]' in message or '[build skip]' in message
|
||||
|
||||
class InvalidPayloadException(Exception):
|
||||
pass
|
||||
|
||||
class BuildArchiveException(Exception):
|
||||
pass
|
||||
|
@ -60,16 +65,16 @@ class BuildTrigger(object):
|
|||
|
||||
def dockerfile_url(self, auth_token, config):
|
||||
"""
|
||||
Returns the URL at which the Dockerfile for the trigger can be found or None if none/not applicable.
|
||||
Returns the URL at which the Dockerfile for the trigger is found or None if none/not applicable.
|
||||
"""
|
||||
return None
|
||||
raise NotImplementedError
|
||||
|
||||
def load_dockerfile_contents(self, auth_token, config):
|
||||
"""
|
||||
Loads the Dockerfile found for the trigger's config and returns them or None if none could
|
||||
be found/loaded.
|
||||
"""
|
||||
return None
|
||||
raise NotImplementedError
|
||||
|
||||
def list_build_sources(self, auth_token):
|
||||
"""
|
||||
|
@ -85,7 +90,7 @@ class BuildTrigger(object):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def handle_trigger_request(self, request, auth_token, config):
|
||||
def handle_trigger_request(self, request, trigger):
|
||||
"""
|
||||
Transform the incoming request data into a set of actions. Returns a tuple
|
||||
of usefiles resource id, docker tags, build name, and resource subdir.
|
||||
|
@ -114,7 +119,7 @@ class BuildTrigger(object):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def manual_start(self, auth_token, config, run_parameters = None):
|
||||
def manual_start(self, trigger, run_parameters=None):
|
||||
"""
|
||||
Manually creates a repository build for this trigger.
|
||||
"""
|
||||
|
@ -146,8 +151,17 @@ class BuildTrigger(object):
|
|||
def raise_unsupported():
|
||||
raise io.UnsupportedOperation
|
||||
|
||||
def get_trigger_config(trigger):
|
||||
try:
|
||||
return json.loads(trigger.config)
|
||||
except:
|
||||
return {}
|
||||
|
||||
|
||||
class GithubBuildTrigger(BuildTrigger):
|
||||
"""
|
||||
BuildTrigger for GitHub that uses the archive API and buildpacks.
|
||||
"""
|
||||
@staticmethod
|
||||
def _get_client(auth_token):
|
||||
return Github(auth_token,
|
||||
|
@ -166,34 +180,70 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
new_build_source = config['build_source']
|
||||
gh_client = self._get_client(auth_token)
|
||||
|
||||
# Find the GitHub repository.
|
||||
try:
|
||||
to_add_webhook = gh_client.get_repo(new_build_source)
|
||||
gh_repo = gh_client.get_repo(new_build_source)
|
||||
except UnknownObjectException:
|
||||
msg = 'Unable to find GitHub repository for source: %s'
|
||||
raise TriggerActivationException(msg % new_build_source)
|
||||
msg = 'Unable to find GitHub repository for source: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
# Add a deploy key to the GitHub repository.
|
||||
public_key, private_key = generate_ssh_keypair()
|
||||
config['credentials'] = [
|
||||
{
|
||||
'name': 'SSH Public Key',
|
||||
'value': public_key,
|
||||
},
|
||||
]
|
||||
try:
|
||||
deploy_key = gh_repo.create_key('%s Builder' % app.config['REGISTRY_TITLE'],
|
||||
public_key)
|
||||
config['deploy_key_id'] = deploy_key.id
|
||||
except GithubException:
|
||||
msg = 'Unable to add deploy key to repository: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
# Add the webhook to the GitHub repository.
|
||||
webhook_config = {
|
||||
'url': standard_webhook_url,
|
||||
'content_type': 'json',
|
||||
}
|
||||
|
||||
try:
|
||||
hook = to_add_webhook.create_hook('web', webhook_config)
|
||||
hook = gh_repo.create_hook('web', webhook_config)
|
||||
config['hook_id'] = hook.id
|
||||
config['master_branch'] = to_add_webhook.default_branch
|
||||
config['master_branch'] = gh_repo.default_branch
|
||||
except GithubException:
|
||||
msg = 'Unable to create webhook on repository: %s'
|
||||
raise TriggerActivationException(msg % new_build_source)
|
||||
msg = 'Unable to create webhook on repository: %s' % new_build_source
|
||||
raise TriggerActivationException(msg)
|
||||
|
||||
return config
|
||||
return config, private_key
|
||||
|
||||
def deactivate(self, auth_token, config):
|
||||
gh_client = self._get_client(auth_token)
|
||||
|
||||
# Find the GitHub repository.
|
||||
try:
|
||||
repo = gh_client.get_repo(config['build_source'])
|
||||
to_delete = repo.get_hook(config['hook_id'])
|
||||
to_delete.delete()
|
||||
except UnknownObjectException:
|
||||
msg = 'Unable to find GitHub repository for source: %s' % config['build_source']
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
# If the trigger uses a deploy key, remove it.
|
||||
try:
|
||||
if config['deploy_key_id']:
|
||||
deploy_key = repo.get_key(config['deploy_key_id'])
|
||||
deploy_key.delete()
|
||||
except KeyError:
|
||||
# There was no config['deploy_key_id'], thus this is an old trigger without a deploy key.
|
||||
pass
|
||||
except GithubException:
|
||||
msg = 'Unable to remove deploy key: %s' % config['deploy_key_id']
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
||||
# Remove the webhook.
|
||||
try:
|
||||
hook = repo.get_hook(config['hook_id'])
|
||||
hook.delete()
|
||||
except GithubException:
|
||||
msg = 'Unable to remove hook: %s' % config['hook_id']
|
||||
raise TriggerDeactivationException(msg)
|
||||
|
@ -233,7 +283,8 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
|
||||
return repos_by_org
|
||||
|
||||
def matches_ref(self, ref, regex):
|
||||
@staticmethod
|
||||
def matches_ref(ref, regex):
|
||||
match_string = ref.split('/', 1)[1]
|
||||
if not regex:
|
||||
return False
|
||||
|
@ -257,7 +308,7 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
try:
|
||||
regex = re.compile(config['branchtag_regex'])
|
||||
branches = [branch.name for branch in repo.get_branches()
|
||||
if self.matches_ref('refs/heads/' + branch.name, regex)]
|
||||
if GithubBuildTrigger.matches_ref('refs/heads/' + branch.name, regex)]
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -275,17 +326,18 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
|
||||
raise RepositoryReadException(message)
|
||||
|
||||
def dockerfile_url(self, auth_token, config):
|
||||
def dockerfile_url(self, auth_token, config):
|
||||
source = config['build_source']
|
||||
subdirectory = config.get('subdir', '')
|
||||
path = subdirectory + '/Dockerfile' if subdirectory else 'Dockerfile'
|
||||
|
||||
gh_client = self._get_client(auth_token)
|
||||
|
||||
try:
|
||||
repo = gh_client.get_repo(source)
|
||||
master_branch = repo.default_branch or 'master'
|
||||
return 'https://github.com/%s/blob/%s/%s' % (source, master_branch, path)
|
||||
except GithubException as ge:
|
||||
except GithubException:
|
||||
logger.exception('Could not load repository for Dockerfile.')
|
||||
return None
|
||||
|
||||
def load_dockerfile_contents(self, auth_token, config):
|
||||
|
@ -341,12 +393,12 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
return commit_info
|
||||
|
||||
@staticmethod
|
||||
def _prepare_build(config, repo, commit_sha, build_name, ref):
|
||||
def _prepare_tarball(repo, commit_sha):
|
||||
# Prepare the download and upload URLs
|
||||
archive_link = repo.get_archive_link('tarball', commit_sha)
|
||||
download_archive = client.get(archive_link, stream=True)
|
||||
|
||||
tarball_subdir = ''
|
||||
|
||||
with SpooledTemporaryFile(CHUNK_SIZE) as tarball:
|
||||
for chunk in download_archive.iter_content(CHUNK_SIZE):
|
||||
tarball.write(chunk)
|
||||
|
@ -372,6 +424,25 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
|
||||
logger.debug('Successfully prepared job')
|
||||
|
||||
return tarball_subdir, dockerfile_id
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _prepare_build(trigger, config, repo, commit_sha, build_name, ref, git_url):
|
||||
repo_subdir = config['subdir']
|
||||
joined_subdir = repo_subdir
|
||||
dockerfile_id = None
|
||||
|
||||
if trigger.private_key is None:
|
||||
# If the trigger isn't using git, prepare the buildpack.
|
||||
tarball_subdir, dockerfile_id = GithubBuildTrigger._prepare_tarball(repo, commit_sha)
|
||||
logger.debug('Successfully prepared job')
|
||||
|
||||
# Join provided subdir with the tarball subdir.
|
||||
joined_subdir = os.path.join(tarball_subdir, repo_subdir)
|
||||
|
||||
logger.debug('Final subdir: %s', joined_subdir)
|
||||
|
||||
# compute the tag(s)
|
||||
branch = ref.split('/')[-1]
|
||||
tags = {branch}
|
||||
|
@ -379,18 +450,14 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
if branch == repo.default_branch:
|
||||
tags.add('latest')
|
||||
|
||||
logger.debug('Pushing to tags: %s' % tags)
|
||||
|
||||
# compute the subdir
|
||||
repo_subdir = config['subdir']
|
||||
joined_subdir = os.path.join(tarball_subdir, repo_subdir)
|
||||
logger.debug('Final subdir: %s' % joined_subdir)
|
||||
logger.debug('Pushing to tags: %s', tags)
|
||||
|
||||
# compute the metadata
|
||||
metadata = {
|
||||
'commit_sha': commit_sha,
|
||||
'ref': ref,
|
||||
'default_branch': repo.default_branch,
|
||||
'git_url': git_url,
|
||||
}
|
||||
|
||||
# add the commit info.
|
||||
|
@ -404,7 +471,7 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
def get_display_name(sha):
|
||||
return sha[0:7]
|
||||
|
||||
def handle_trigger_request(self, request, auth_token, config):
|
||||
def handle_trigger_request(self, request, trigger):
|
||||
payload = request.get_json()
|
||||
if not payload or payload.get('head_commit') is None:
|
||||
raise SkipRequestException()
|
||||
|
@ -416,14 +483,16 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
ref = payload['ref']
|
||||
commit_sha = payload['head_commit']['id']
|
||||
commit_message = payload['head_commit'].get('message', '')
|
||||
git_url = payload['repository']['git_url']
|
||||
|
||||
config = get_trigger_config(trigger)
|
||||
if 'branchtag_regex' in config:
|
||||
try:
|
||||
regex = re.compile(config['branchtag_regex'])
|
||||
except:
|
||||
regex = re.compile('.*')
|
||||
|
||||
if not self.matches_ref(ref, regex):
|
||||
if not GithubBuildTrigger.matches_ref(ref, regex):
|
||||
raise SkipRequestException()
|
||||
|
||||
if should_skip_commit(commit_message):
|
||||
|
@ -431,7 +500,7 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
|
||||
short_sha = GithubBuildTrigger.get_display_name(commit_sha)
|
||||
|
||||
gh_client = self._get_client(auth_token)
|
||||
gh_client = self._get_client(trigger.auth_token)
|
||||
|
||||
repo_full_name = '%s/%s' % (payload['repository']['owner']['name'],
|
||||
payload['repository']['name'])
|
||||
|
@ -439,24 +508,25 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
|
||||
logger.debug('Github repo: %s', repo)
|
||||
|
||||
return GithubBuildTrigger._prepare_build(config, repo, commit_sha,
|
||||
short_sha, ref)
|
||||
return GithubBuildTrigger._prepare_build(trigger, config, repo, commit_sha,
|
||||
short_sha, ref, git_url)
|
||||
|
||||
def manual_start(self, auth_token, config, run_parameters = None):
|
||||
def manual_start(self, trigger, run_parameters=None):
|
||||
config = get_trigger_config(trigger)
|
||||
try:
|
||||
source = config['build_source']
|
||||
run_parameters = run_parameters or {}
|
||||
|
||||
gh_client = self._get_client(auth_token)
|
||||
gh_client = self._get_client(trigger.auth_token)
|
||||
repo = gh_client.get_repo(source)
|
||||
branch_name = run_parameters.get('branch_name') or repo.default_branch
|
||||
branch = repo.get_branch(branch_name)
|
||||
branch_sha = branch.commit.sha
|
||||
commit_info = branch.commit
|
||||
short_sha = GithubBuildTrigger.get_display_name(branch_sha)
|
||||
ref = 'refs/heads/%s' % (branch_name)
|
||||
git_url = repo.git_url
|
||||
|
||||
return self._prepare_build(config, repo, branch_sha, short_sha, ref)
|
||||
return self._prepare_build(trigger, config, repo, branch_sha, short_sha, ref, git_url)
|
||||
except GithubException as ghe:
|
||||
raise TriggerStartException(ghe.data['message'])
|
||||
|
||||
|
@ -491,3 +561,151 @@ class GithubBuildTrigger(BuildTrigger):
|
|||
return branches
|
||||
|
||||
return None
|
||||
|
||||
class CustomBuildTrigger(BuildTrigger):
|
||||
payload_schema = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'commit': {
|
||||
'type': 'string',
|
||||
'description': 'first 7 characters of the SHA-1 identifier for a git commit',
|
||||
'pattern': '^([A-Fa-f0-9]{7})$',
|
||||
},
|
||||
'ref': {
|
||||
'type': 'string',
|
||||
'description': 'git reference for a git commit',
|
||||
'pattern': '^refs\/(heads|tags|remotes)\/(.+)$',
|
||||
},
|
||||
'default_branch': {
|
||||
'type': 'string',
|
||||
'description': 'default branch of the git repository',
|
||||
},
|
||||
'commit_info': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about a git commit',
|
||||
'properties': {
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view a git commit',
|
||||
},
|
||||
'message': {
|
||||
'type': 'string',
|
||||
'description': 'git commit message',
|
||||
},
|
||||
'date': {
|
||||
'type': 'string',
|
||||
'description': 'timestamp for a git commit'
|
||||
},
|
||||
'author': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about the author of a git commit',
|
||||
'properties': {
|
||||
'username': {
|
||||
'type': 'string',
|
||||
'description': 'username of the author',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the profile of the author',
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the avatar of the author',
|
||||
},
|
||||
},
|
||||
'required': ['username', 'url', 'avatar_url'],
|
||||
},
|
||||
'committer': {
|
||||
'type': 'object',
|
||||
'description': 'metadata about the committer of a git commit',
|
||||
'properties': {
|
||||
'username': {
|
||||
'type': 'string',
|
||||
'description': 'username of the committer',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the profile of the committer',
|
||||
},
|
||||
'avatar_url': {
|
||||
'type': 'string',
|
||||
'description': 'URL to view the avatar of the committer',
|
||||
},
|
||||
},
|
||||
'required': ['username', 'url', 'avatar_url'],
|
||||
},
|
||||
},
|
||||
'required': ['url', 'message', 'date'],
|
||||
},
|
||||
},
|
||||
'required': ['commits', 'ref', 'default_branch'],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def service_name(cls):
|
||||
return 'custom-git'
|
||||
|
||||
def is_active(self, config):
|
||||
return config.has_key('credentials')
|
||||
|
||||
def _metadata_from_payload(self, payload):
|
||||
try:
|
||||
metadata = json.loads(payload)
|
||||
validate(metadata, self.payload_schema)
|
||||
except:
|
||||
raise InvalidPayloadException()
|
||||
return metadata
|
||||
|
||||
def handle_trigger_request(self, request, trigger):
|
||||
payload = request.get_json()
|
||||
if not payload:
|
||||
raise SkipRequestException()
|
||||
|
||||
logger.debug('Payload %s', payload)
|
||||
metadata = self._metadata_from_payload(payload)
|
||||
|
||||
# The build source is the canonical git URL used to clone.
|
||||
config = get_trigger_config(trigger)
|
||||
metadata['git_url'] = config['build_source']
|
||||
|
||||
branch = metadata['ref'].split('/')[-1]
|
||||
tags = {branch}
|
||||
|
||||
build_name = metadata['commit_sha'][:6]
|
||||
dockerfile_id = None
|
||||
|
||||
return dockerfile_id, tags, build_name, trigger.config['subdir'], metadata
|
||||
|
||||
def activate(self, trigger_uuid, standard_webhook_url, auth_token, config):
|
||||
public_key, private_key = generate_ssh_keypair()
|
||||
config['credentials'] = [
|
||||
{
|
||||
'name': 'SSH Public Key',
|
||||
'value': public_key,
|
||||
},
|
||||
{
|
||||
'name': 'Webhook Endpoint URL',
|
||||
'value': standard_webhook_url,
|
||||
},
|
||||
]
|
||||
return config, private_key
|
||||
|
||||
def deactivate(self, auth_token, config):
|
||||
config.pop('credentials', None)
|
||||
return config
|
||||
|
||||
def manual_start(self, trigger, run_parameters=None):
|
||||
# commit_sha is the only required parameter
|
||||
if 'commit_sha' not in run_parameters:
|
||||
raise TriggerStartException('missing required parameter')
|
||||
|
||||
config = get_trigger_config(trigger)
|
||||
dockerfile_id = None
|
||||
tags = {run_parameters['commit_sha']}
|
||||
build_name = run_parameters['commit_sha']
|
||||
metadata = {
|
||||
'commit_sha': run_parameters['commit_sha'],
|
||||
'git_url': config['build_source'],
|
||||
}
|
||||
|
||||
return dockerfile_id, list(tags), build_name, config['subdir'], metadata
|
||||
|
|
|
@ -12,7 +12,7 @@ from data.model.oauth import DatabaseAuthorizationProvider
|
|||
from app import app, billing as stripe, build_logs, avatar, signer
|
||||
from auth.auth import require_session_login, process_oauth
|
||||
from auth.permissions import (AdministerOrganizationPermission, ReadRepositoryPermission,
|
||||
SuperUserPermission)
|
||||
SuperUserPermission, AdministerRepositoryPermission)
|
||||
|
||||
from util.invoice import renderInvoiceToPdf
|
||||
from util.seo import render_snapshot
|
||||
|
@ -20,6 +20,7 @@ from util.cache import no_cache
|
|||
from endpoints.common import common_login, render_page_template, route_show_if, param_required
|
||||
from endpoints.csrf import csrf_protect, generate_csrf_token, verify_csrf
|
||||
from endpoints.registry import set_cache_headers
|
||||
from endpoints.trigger import CustomBuildTrigger
|
||||
from util.names import parse_repository_name, parse_repository_name_and_tag
|
||||
from util.useremails import send_email_changed
|
||||
from util.systemlogs import build_logs_archive
|
||||
|
@ -494,6 +495,28 @@ def download_logs_archive():
|
|||
|
||||
abort(403)
|
||||
|
||||
@web.route('/customtrigger/setup/<path:repository>', methods=['GET'])
|
||||
@require_session_login
|
||||
@parse_repository_name
|
||||
def attach_custom_build_trigger(namespace, repository_name):
|
||||
permission = AdministerRepositoryPermission(namespace, repository_name)
|
||||
if permission.can():
|
||||
repo = model.get_repository(namespace, repository_name)
|
||||
if not repo:
|
||||
msg = 'Invalid repository: %s/%s' % (namespace, repository_name)
|
||||
abort(404, message=msg)
|
||||
|
||||
trigger = model.create_build_trigger(repo, CustomBuildTrigger.service_name(),
|
||||
None, current_user.db_user())
|
||||
|
||||
repo_path = '%s/%s' % (namespace, repository_name)
|
||||
full_url = '%s%s%s' % (url_for('web.repository', path=repo_path), '?tab=builds&newtrigger=',
|
||||
trigger.uuid)
|
||||
|
||||
logger.debug('Redirecting to full url: %s', full_url)
|
||||
return redirect(full_url)
|
||||
|
||||
abort(403)
|
||||
|
||||
@web.route('/<path:repository>')
|
||||
@no_cache
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import logging
|
||||
import json
|
||||
|
||||
from flask import request, make_response, Blueprint
|
||||
|
||||
|
@ -9,9 +8,8 @@ from auth.auth import process_auth
|
|||
from auth.permissions import ModifyRepositoryPermission
|
||||
from util.invoice import renderInvoiceToHtml
|
||||
from util.useremails import send_invoice_email, send_subscription_change, send_payment_failed
|
||||
from util.names import parse_repository_name
|
||||
from util.http import abort
|
||||
from endpoints.trigger import BuildTrigger, ValidationRequestException, SkipRequestException
|
||||
from endpoints.trigger import BuildTrigger, ValidationRequestException, SkipRequestException, InvalidPayloadException
|
||||
from endpoints.common import start_build
|
||||
|
||||
|
||||
|
@ -23,7 +21,7 @@ webhooks = Blueprint('webhooks', __name__)
|
|||
@webhooks.route('/stripe', methods=['POST'])
|
||||
def stripe_webhook():
|
||||
request_data = request.get_json()
|
||||
logger.debug('Stripe webhook call: %s' % request_data)
|
||||
logger.debug('Stripe webhook call: %s', request_data)
|
||||
|
||||
customer_id = request_data.get('data', {}).get('object', {}).get('customer', None)
|
||||
user = model.get_user_or_org_by_customer_id(customer_id) if customer_id else None
|
||||
|
@ -87,19 +85,18 @@ def build_trigger_webhook(trigger_uuid, **kwargs):
|
|||
handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
|
||||
logger.debug('Passing webhook request to handler %s', handler)
|
||||
config_dict = json.loads(trigger.config)
|
||||
try:
|
||||
specs = handler.handle_trigger_request(request, trigger.auth_token,
|
||||
config_dict)
|
||||
specs = handler.handle_trigger_request(request, trigger)
|
||||
dockerfile_id, tags, name, subdir, metadata = specs
|
||||
|
||||
except ValidationRequestException:
|
||||
# This was just a validation request, we don't need to build anything
|
||||
return make_response('Okay')
|
||||
|
||||
except SkipRequestException:
|
||||
# The build was requested to be skipped
|
||||
return make_response('Okay')
|
||||
except InvalidPayloadException:
|
||||
# The payload was malformed
|
||||
abort(400)
|
||||
|
||||
pull_robot_name = model.get_pull_robot_name(trigger)
|
||||
repo = model.get_repository(namespace, repository)
|
||||
|
|
|
@ -203,6 +203,7 @@ def initialize_database():
|
|||
LoginService.create(name='ldap')
|
||||
|
||||
BuildTriggerService.create(name='github')
|
||||
BuildTriggerService.create(name='custom-git')
|
||||
|
||||
AccessTokenKind.create(name='build-worker')
|
||||
AccessTokenKind.create(name='pushpull-token')
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
}
|
||||
|
||||
.build-view .build-icon-message.internalerror {
|
||||
color: #DFFF00;
|
||||
color: rgb(151, 168, 0);
|
||||
}
|
||||
|
||||
.build-view .build-icon-message.complete {
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
</span>
|
||||
<input type="url" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="url" required>
|
||||
<input type="text" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="string" required>
|
||||
<!-- TODO(jschorr): unify the ability to create an input box with all the usual features -->
|
||||
<div ng-switch-when="regex">
|
||||
<input type="text" class="form-control" ng-model="currentConfig[field.name]"
|
||||
ng-pattern="getPattern(field)"
|
||||
|
|
24
static/directives/credentials.html
Normal file
24
static/directives/credentials.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
<div ng-switch on="trigger.service">
|
||||
<!-- Message -->
|
||||
<div ng-switch-when="custom-git" class="alert alert-info">
|
||||
<p>
|
||||
In order to use this trigger, the following first requires action:
|
||||
<ul>
|
||||
<li>You must give the following public key read access to the git repository.</li>
|
||||
<li>You must set your repository to POST to the following URL to trigger a build.</li>
|
||||
</ul>
|
||||
For more information, refer to the <a href="http://docs.quay.io/guides/custom-trigger.html" target="_blank">Custom Git Triggers documentation</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div ng-switch-when="github" class="alert alert-info">
|
||||
<p>The following key has been automatically added to your GitHub repository.</p>
|
||||
</div>
|
||||
|
||||
<!-- Credentials -->
|
||||
<div ng-repeat="credential in trigger.config.credentials">
|
||||
<p>
|
||||
{{ credential.name }}:
|
||||
<div class="copy-box" value="credential.value"></div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -18,11 +18,20 @@
|
|||
<span ng-switch-when="option">
|
||||
<span class="quay-spinner" ng-show="!fieldOptions[field.name]"></span>
|
||||
<select ng-model="parameters[field.name]" ng-show="fieldOptions[field.name]"
|
||||
ng-options="value for value in fieldOptions[field.name]"
|
||||
ng-options="value for value in fieldOptions[field.name]"
|
||||
required>
|
||||
</select>
|
||||
</span>
|
||||
<input type="text" class="form-control" ng-model="parameters[field.name]" ng-switch-when="string" required>
|
||||
<!-- TODO(jschorr): unify the ability to create an input box with all the usual features -->
|
||||
<div ng-switch-when="regex">
|
||||
<input type="text" class="form-control" ng-model="parameters[field.name]"
|
||||
ng-pattern="getPattern(field)"
|
||||
placeholder="{{ field.placeholder }}"
|
||||
ng-name="field.name"
|
||||
id="{{ field.name }}"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right pull-right">
|
||||
<li ng-repeat="type in TriggerService.getTypes()">
|
||||
<a href="{{ TriggerService.getRedirectUrl(type, repository.namespace, repository.name) }}">
|
||||
<a href="{{ TriggerService.getRedirectUrl(type, repository.namespace, repository.name) }}" target="{{ TriggerService.getMetadata(type).is_external ? '' : '_self' }}">
|
||||
<i class="fa fa-lg" ng-class="TriggerService.getMetadata(type).icon"></i>
|
||||
{{ TriggerService.getTitle(type) }}
|
||||
</a>
|
||||
|
@ -135,13 +135,16 @@
|
|||
|
||||
<tr ng-repeat="trigger in triggers | filter:{'is_active':true}">
|
||||
<td><div class="trigger-description" trigger="trigger" short="true"></div></td>
|
||||
<td>{{ trigger.subdir || '(Root Directory)' }}</td>
|
||||
<td>{{ trigger.config.branchtag_regex || '(All)' }}</td>
|
||||
<td>{{ trigger.config.subdir || '/' }}</td>
|
||||
<td>{{ trigger.config.branchtag_regex || 'All' }}</td>
|
||||
<td>
|
||||
<span class="entity-reference" entity="trigger.pull_robot" ng-if="trigger.pull_robot"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="cor-options-menu">
|
||||
<span ng-if="trigger.config.credentials" class="cor-option" option-click="showTriggerCredentialsModal(trigger)">
|
||||
<i class="fa fa-unlock-alt"></i> View Credentials
|
||||
</span>
|
||||
<span class="cor-option" option-click="askRunTrigger(trigger)"
|
||||
ng-class="trigger.connected_user == user.username ? '' : 'disabled'">
|
||||
<i class="fa fa-chevron-right"></i> Run Trigger Now
|
||||
|
@ -159,6 +162,11 @@
|
|||
</div>
|
||||
</div> <!-- /Build Triggers -->
|
||||
|
||||
<!-- Dialogs -->
|
||||
|
||||
<!-- Trigger Credentials dialog -->
|
||||
<div class="trigger-credentials-dialog" trigger="triggerCredentialsModalTrigger" counter="triggerCredentialsModalCounter"></div>
|
||||
|
||||
<!-- Delete Tag Confirm -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="deleteTriggerInfo"
|
||||
|
@ -190,4 +198,6 @@
|
|||
counter="showTriggerStartDialogCounter"
|
||||
start-build="startTrigger(trigger, parameters)"></div>
|
||||
|
||||
</div>
|
||||
<!-- /Dialogs -->
|
||||
|
||||
</div>
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
|
||||
analyze="checkAnalyze(isValid)"></div>
|
||||
</div>
|
||||
<div ng-switch-when="custom-git">
|
||||
<div class="trigger-setup-custom" repository="repository" trigger="trigger"
|
||||
next-step-counter="nextStepCounter" current-step-valid="state.stepValid"
|
||||
analyze="checkAnalyze(isValid)"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading pull information -->
|
||||
|
@ -30,11 +35,13 @@
|
|||
<div class="trigger-option-section" ng-show="currentView == 'analyzed'">
|
||||
|
||||
<!-- Messaging -->
|
||||
<div class="alert alert-danger" ng-if="pullInfo.analysis.status == 'error'">
|
||||
{{ pullInfo.analysis.message }}
|
||||
</div>
|
||||
<div class="alert alert-warning" ng-if="pullInfo.analysis.status == 'warning'">
|
||||
{{ pullRequirements.message }}
|
||||
<div ng-switch on="pullInfo.analysis.status">
|
||||
<div ng-switch-when="error" class="alert alert-danger">{{ pullInfo.analysis.message }}</div>
|
||||
<div ng-switch-when="warning" class="alert alert-warning">{{ pullInfo.analysis.message }}</div>
|
||||
<div ng-switch-when="notimplemented" class="alert alert-warning">
|
||||
<p>For {{ TriggerService.getTitle(trigger.service) }} triggers, we are unable to determine dependencies automatically.</p>
|
||||
<p>If the git repository being built depends on a private base image, you must manually select a robot account with the proper permissions.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dockerfile-found" ng-if="pullInfo.analysis.is_public === false">
|
||||
<div class="dockerfile-found-content">
|
||||
|
@ -54,7 +61,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 12px">Please select the credentials to use when pulling the base image:</div>
|
||||
<div style="margin-bottom: 12px">
|
||||
Please select the credentials to use when pulling the base image:
|
||||
</div>
|
||||
<div ng-if="!isNamespaceAdmin(repository.namespace)" style="color: #aaa;">
|
||||
<strong>Note:</strong> In order to set pull credentials for a build trigger, you must be an
|
||||
Administrator of the namespace <strong>{{ repository.namespace }}</strong>
|
||||
|
@ -101,6 +110,13 @@
|
|||
|
||||
</div>
|
||||
|
||||
<div class="trigger-option-section" ng-show="currentView == 'postActivation'">
|
||||
<div ng-if="trigger.config.credentials" class="credentials" trigger="trigger"></div>
|
||||
<div ng-if="!trigger.config.credentials">
|
||||
<div class="alert alert-success">The trigger has been successfully created.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" ng-disabled="!state.stepValid"
|
||||
|
@ -112,7 +128,7 @@
|
|||
ng-click="activate()"
|
||||
ng-show="currentView == 'analyzed'">Create Trigger</button>
|
||||
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ currentView == 'postActivation' ? 'Done' : 'Cancel' }}</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
|
|
19
static/directives/trigger-credentials-dialog.html
Normal file
19
static/directives/trigger-credentials-dialog.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="triggercredentialsmodal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">
|
||||
Trigger Credentials
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="credentials" trigger="trigger"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Done</button>
|
||||
</div>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div> <!-- /.modal -->
|
|
@ -1,4 +1,5 @@
|
|||
<span class="trigger-description-element" ng-switch on="trigger.service">
|
||||
<!-- GitHub -->
|
||||
<span ng-switch-when="github">
|
||||
<i class="fa fa-github fa-lg" style="margin-right: 6px" data-title="GitHub" bs-tooltip="tooltip.title"></i>
|
||||
Push to GitHub <span ng-if="KeyService.isEnterprise('github-trigger')">Enterprise</span> repository
|
||||
|
@ -14,11 +15,24 @@
|
|||
|
||||
<div>
|
||||
<span class="trigger-description-subtitle">Dockerfile:</span>
|
||||
<span ng-if="trigger.config.subdir">//{{ trigger.config.subdir}}/Dockerfile</span>
|
||||
<span ng-if="!trigger.config.subdir">//Dockerfile</span>
|
||||
<span>{{ TriggerService.getDockerfileLocation(trigger) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<!-- Git -->
|
||||
<span ng-switch-when="custom-git">
|
||||
<i class="fa fa-git fa-lg" style="margin-right: 6px;" data-title="git" bs-tooltip="tooltip.title"></i>
|
||||
Push to {{ trigger.config.build_source }}
|
||||
<div style="margin-top: 4px; margin-left: 26px; font-size: 12px; color: gray;" ng-if="!short">
|
||||
<div>
|
||||
<span class="trigger-description-subtitle">Dockerfile:</span>
|
||||
<span>{{ TriggerService.getDockerfileLocation(trigger) }}</span<
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<!-- Who knows? -->
|
||||
<span ng-switch-default>
|
||||
Unknown
|
||||
</span>
|
||||
|
|
40
static/directives/trigger-setup-custom.html
Normal file
40
static/directives/trigger-setup-custom.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
<div class="trigger-setup-custom-element">
|
||||
<div class="selected-info" ng-show="nextStepCounter > 0">
|
||||
<table style="width: 100%;">
|
||||
<tr ng-show="nextStepCounter > 0">
|
||||
<td width="200px">Repository</td>
|
||||
<td>{{ state.build_source }}</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-show="nextStepCounter > 1">
|
||||
<td>Dockerfile Location:</td>
|
||||
<td>
|
||||
<div class="dockerfile-location">
|
||||
<i class="fa fa-folder fa-lg"></i> {{ state.subdir || '/' }}
|
||||
</div>
|
||||
</td>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Step view -->
|
||||
<div class="step-view" next-step-counter="nextStepCounter" current-step-valid="currentStepValid"
|
||||
steps-completed="stepsCompleted()">
|
||||
|
||||
<!-- Git URL Input -->
|
||||
<!-- TODO(jschorr): make nopLoad(callback) no longer required -->
|
||||
<div class="step-view-step" complete-condition="trigger['config']['build_source']" load-callback="nopLoad(callback)"
|
||||
load-message="Loading Git URL Input">
|
||||
<div style="margin-bottom: 12px;">Please enter an HTTP or SSH style URL used to clone your git repository:</div>
|
||||
<input class="form-control" type="text" placeholder="git@example.com:namespace/repository.git" style="width: 100%;"
|
||||
ng-model="state.build_source" ng-pattern="/(((http|https):\/\/)(.+)|\w+@(.+):(.+))/">
|
||||
</div>
|
||||
|
||||
<!-- Dockerfile folder select -->
|
||||
<div class="step-view-step" complete-condition="trigger.$ready" load-callback="nopLoad(callback)"
|
||||
load-message="Loading Folder Input">
|
||||
<div style="margin-bottom: 12px">Dockerfile Location:</div>
|
||||
<input class="form-control" type="text" placeholder="/" style="width: 100%;"
|
||||
ng-model="state.subdir" ng-pattern="/^($|\/|\/.+)/">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -33,7 +33,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<div class="dockerfile-location">
|
||||
<i class="fa fa-folder fa-lg"></i> {{ state.currentLocation || '(Repository Root)' }}
|
||||
<i class="fa fa-folder fa-lg"></i> {{ state.currentLocation || '(Repository Root)' }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
<div class="triggered-build-description-element">
|
||||
|
||||
<span class="tbd-content" class="manual" ng-if="!build.trigger && !build.job_config.manual_user">
|
||||
(Manually Triggered Build)
|
||||
</span>
|
||||
<span class="tbd-content" class="manual" ng-if="!build.trigger && !build.job_config.manual_user">
|
||||
(Manually Triggered Build)
|
||||
</span>
|
||||
|
||||
<span class="tbd-content" ng-if="!build.trigger && build.job_config.manual_user">
|
||||
<i class="fa fa-user"></i> {{ build.job_config.manual_user }}
|
||||
</span>
|
||||
<span class="tbd-content" ng-if="!build.trigger && build.job_config.manual_user">
|
||||
<i class="fa fa-user"></i> {{ build.job_config.manual_user }}
|
||||
</span>
|
||||
|
||||
<span ng-switch on="build.trigger.service" ng-if="build.trigger">
|
||||
<!-- GitHub -->
|
||||
<span ng-switch-when="github">
|
||||
<!-- Full Commit Information -->
|
||||
<span class="tbd-content" ng-if="build.job_config.trigger_metadata.commit_info">
|
||||
<!-- Full Commit Information -->
|
||||
<span class="tbd-content" ng-if="build.job_config.trigger_metadata.commit_info" ng-switch on="build.trigger.service">
|
||||
<!-- GitHub -->
|
||||
<div ng-switch-when="github">
|
||||
<div class="commit-message">
|
||||
<a ng-href="{{ getGitHubRepoURL(build) }}/commit/{{ build.job_config.trigger_metadata.commit_sha }}"
|
||||
target="_blank">
|
||||
|
@ -39,27 +38,63 @@
|
|||
branch-template="getGitHubRepoURL(build) + '/tree/{branch}'"
|
||||
tag-template="getGitHubRepoURL(build) + '/releases/tag/{tag}'"></span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Just commit SHA -->
|
||||
<span class="tbd-content" ng-if="build.job_config.trigger_metadata && !build.job_config.trigger_metadata.commit_info">
|
||||
Triggered by commit
|
||||
<!-- Git -->
|
||||
<div ng-switch-when="custom-git">
|
||||
<div class="commit-message">
|
||||
{{ build.job_config.trigger_metadata.commit_info.message }}
|
||||
</div>
|
||||
<div class="commit-information">
|
||||
<span class="commit-who-when">
|
||||
Authored
|
||||
<span am-time-ago="build.job_config.trigger_metadata.commit_info.date"></span>
|
||||
<span class="commit-who">
|
||||
{{ build.job_config.trigger_metadata.commit_info.author.username }}
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
{{ build.job_config.trigger_metadata.commit_sha }}
|
||||
</span>
|
||||
<span>
|
||||
{{ build.job_config.trigger_metadata.ref }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<!-- Just commit SHA -->
|
||||
<span class="tbd-content" ng-if="build.job_config.trigger_metadata && !build.job_config.trigger_metadata.commit_info" ng-switch on="build.trigger.service">
|
||||
Triggered by commit
|
||||
<!-- GitHub -->
|
||||
<div ng-switch-when="github">
|
||||
<span class="source-commit-link"
|
||||
commit-sha="build.job_config.trigger_metadata.commit_sha"
|
||||
url-template="getGitHubRepoURL(build) + '/commit/{sha}'"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- No information -->
|
||||
<span class="tbd-content" ng-if="!build.job_config.trigger_metadata">
|
||||
Triggered by commit to
|
||||
<!-- Git -->
|
||||
<div ng-switch-when="custom-git">
|
||||
<span>{{ build.job_config.trigger_metadata.commit_sha }}</span>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<!-- No information -->
|
||||
<span class="tbd-content" ng-if="!build.job_config.trigger_metadata" ng-switch on="build.trigger.service">
|
||||
Triggered by commit to
|
||||
<!-- GitHub -->
|
||||
<div ng-switch-when="github">
|
||||
<i class="fa fa-github fa-lg" data-title="GitHub" data-container="body" bs-tooltip></i>
|
||||
<a ng-href="{{ getGitHubRepoURL(build) }}" target="_new">
|
||||
{{ build.trigger.config.build_source }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Git -->
|
||||
<div ng-switch-when="custom-git">
|
||||
<i class="fa fa-git fa-lg" data-title="git" data-container="body" bs-tooltip></i>
|
||||
{{ build.trigger.config.build_source }}
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<!-- Unknown -->
|
||||
<span ng-switch-default></span>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -34,6 +34,9 @@ angular.module('quay').directive('repoPanelBuilds', function () {
|
|||
$scope.showTriggerStartDialogCounter = 0;
|
||||
$scope.showTriggerSetupCounter = 0;
|
||||
|
||||
$scope.triggerCredentialsModalTrigger = null;
|
||||
$scope.triggerCredentialsModalCounter = 0;
|
||||
|
||||
var updateBuilds = function() {
|
||||
if (!$scope.allBuilds) { return; }
|
||||
|
||||
|
@ -164,6 +167,11 @@ angular.module('quay').directive('repoPanelBuilds', function () {
|
|||
$scope.options.predicate = predicate;
|
||||
};
|
||||
|
||||
$scope.showTriggerCredentialsModal = function(trigger) {
|
||||
$scope.triggerCredentialsModalTrigger = trigger;
|
||||
$scope.triggerCredentialsModalCounter++;
|
||||
};
|
||||
|
||||
$scope.askDeleteTrigger = function(trigger) {
|
||||
$scope.deleteTriggerInfo = {
|
||||
'trigger': trigger
|
||||
|
|
16
static/js/directives/ui/credentials.js
Normal file
16
static/js/directives/ui/credentials.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* An element which displays a credentials for a build trigger.
|
||||
*/
|
||||
angular.module('quay').directive('credentials', function() {
|
||||
var directiveDefinitionObject = {
|
||||
templateUrl: '/static/directives/credentials.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'trigger': '=trigger'
|
||||
},
|
||||
controller: function($scope) {}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
|
@ -25,6 +25,10 @@ angular.module('quay').directive('manualTriggerBuildDialog', function () {
|
|||
});
|
||||
};
|
||||
|
||||
$scope.getPattern = function(field) {
|
||||
return new RegExp(field.regex);
|
||||
};
|
||||
|
||||
$scope.show = function() {
|
||||
$scope.parameters = {};
|
||||
$scope.fieldOptions = {};
|
||||
|
@ -58,4 +62,4 @@ angular.module('quay').directive('manualTriggerBuildDialog', function () {
|
|||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,12 +14,13 @@ angular.module('quay').directive('setupTriggerDialog', function () {
|
|||
'canceled': '&canceled',
|
||||
'activated': '&activated'
|
||||
},
|
||||
controller: function($scope, $element, ApiService, UserService) {
|
||||
controller: function($scope, $element, ApiService, UserService, TriggerService) {
|
||||
var modalSetup = false;
|
||||
|
||||
$scope.state = {};
|
||||
$scope.nextStepCounter = -1;
|
||||
$scope.currentView = 'config';
|
||||
$scope.TriggerService = TriggerService
|
||||
|
||||
$scope.show = function() {
|
||||
if (!$scope.trigger || !$scope.repository) { return; }
|
||||
|
@ -113,10 +114,11 @@ angular.module('quay').directive('setupTriggerDialog', function () {
|
|||
});
|
||||
|
||||
ApiService.activateBuildTrigger(data, params).then(function(resp) {
|
||||
$scope.hide();
|
||||
$scope.trigger['is_active'] = true;
|
||||
$scope.trigger['config'] = resp['config'];
|
||||
$scope.trigger['pull_robot'] = resp['pull_robot'];
|
||||
$scope.activated({'trigger': $scope.trigger});
|
||||
$scope.currentView = 'postActivation';
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
|
|
29
static/js/directives/ui/trigger-credentials-dialog.js
Normal file
29
static/js/directives/ui/trigger-credentials-dialog.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* An element which displays a dialog with the necessary credentials for a build trigger.
|
||||
*/
|
||||
angular.module('quay').directive('triggerCredentialsDialog', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/trigger-credentials-dialog.html',
|
||||
replace: false,
|
||||
transclude: true,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'trigger': '=trigger',
|
||||
'counter': '=counter'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
var show = function() {
|
||||
if (!$scope.trigger || !$scope.counter) {
|
||||
$('#triggercredentialsmodal').modal('hide');
|
||||
return;
|
||||
}
|
||||
$('#triggercredentialsmodal').modal({});
|
||||
};
|
||||
|
||||
$scope.$watch('trigger', show);
|
||||
$scope.$watch('counter', show);
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
49
static/js/directives/ui/trigger-setup-custom.js
Normal file
49
static/js/directives/ui/trigger-setup-custom.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* An element which displays custom git-specific setup information for its build triggers.
|
||||
*/
|
||||
angular.module('quay').directive('triggerSetupCustom', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/trigger-setup-custom.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'repository': '=repository',
|
||||
'trigger': '=trigger',
|
||||
|
||||
'nextStepCounter': '=nextStepCounter',
|
||||
'currentStepValid': '=currentStepValid',
|
||||
|
||||
'analyze': '&analyze'
|
||||
},
|
||||
controller: function($scope, $element, ApiService) {
|
||||
$scope.analyzeCounter = 0;
|
||||
$scope.setupReady = false;
|
||||
|
||||
$scope.state = {
|
||||
'build_source': null,
|
||||
'subdir': null
|
||||
};
|
||||
|
||||
$scope.stepsCompleted = function() {
|
||||
$scope.analyze({'isValid': $scope.state.build_source != null && $scope.state.subdir != null});
|
||||
};
|
||||
|
||||
$scope.$watch('state.build_source', function(build_source) {
|
||||
$scope.trigger['config']['build_source'] = build_source;
|
||||
});
|
||||
|
||||
$scope.$watch('state.subdir', function(subdir) {
|
||||
$scope.trigger['config']['subdir'] = subdir;
|
||||
$scope.trigger.$ready = subdir != null;
|
||||
});
|
||||
|
||||
$scope.nopLoad = function(callback) {
|
||||
callback();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return directiveDefinitionObject;
|
||||
});
|
|
@ -69,7 +69,7 @@
|
|||
|
||||
$scope.isBuilding = function(build) {
|
||||
if (!build) { return true; }
|
||||
return build.phase != 'complete' && build.phase != 'error';
|
||||
return build.phase != 'complete' && build.phase != 'error' && build.phase != 'internalerror';
|
||||
};
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
|
|
@ -64,4 +64,4 @@ angular.module('quay').factory('KeyService', ['$location', 'Config', function($l
|
|||
};
|
||||
|
||||
return keyService;
|
||||
}]);
|
||||
}]);
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
* Helper service for defining the various kinds of build triggers and retrieving information
|
||||
* about them.
|
||||
*/
|
||||
angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'KeyService', 'Features', 'CookieService',
|
||||
function(UtilService, $sanitize, KeyService, Features, CookieService) {
|
||||
angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'KeyService', 'Features', 'CookieService', 'Config',
|
||||
function(UtilService, $sanitize, KeyService, Features, CookieService, Config) {
|
||||
var triggerService = {};
|
||||
|
||||
var triggerTypes = {
|
||||
|
@ -15,7 +15,6 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
|||
desc += '<br>Dockerfile folder: //' + UtilService.textToSafeHtml(config['subdir']);
|
||||
return desc;
|
||||
},
|
||||
|
||||
'run_parameters': [
|
||||
{
|
||||
'title': 'Branch',
|
||||
|
@ -23,7 +22,6 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
|||
'name': 'branch_name'
|
||||
}
|
||||
],
|
||||
|
||||
'get_redirect_url': function(namespace, repository) {
|
||||
var redirect_uri = KeyService['githubRedirectUri'] + '/trigger/' +
|
||||
namespace + '/' + repository;
|
||||
|
@ -39,13 +37,11 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
|||
return authorize_url + 'client_id=' + client_id +
|
||||
'&scope=repo,user:email&redirect_uri=' + redirect_uri;
|
||||
},
|
||||
|
||||
'is_external': true,
|
||||
'is_enabled': function() {
|
||||
return Features.GITHUB_BUILD;
|
||||
},
|
||||
|
||||
'icon': 'fa-github',
|
||||
|
||||
'title': function() {
|
||||
var isEnterprise = KeyService.isEnterprise('github-trigger');
|
||||
if (isEnterprise) {
|
||||
|
@ -54,6 +50,31 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
|||
|
||||
return 'GitHub Repository Push';
|
||||
}
|
||||
},
|
||||
|
||||
'custom-git': {
|
||||
'description': function(config) {
|
||||
var source = UtilService.textToSafeHtml(config['build_source']);
|
||||
var desc = '<i class"fa fa-git fa-lg" style="margin-left:2px; margin-right: 2px"></i> Push to Custom Git Repository ' + source;
|
||||
desc += '<br>Dockerfile folder: //' + UtilService.textToSafeHtml(config['subdir']);
|
||||
return desc;
|
||||
},
|
||||
'run_parameters': [
|
||||
{
|
||||
'title': 'Commit',
|
||||
'type': 'regex',
|
||||
'name': 'commit_sha',
|
||||
'regex': '^([A-Fa-f0-9]{7})$',
|
||||
'placeholder': '1c002dd'
|
||||
}
|
||||
],
|
||||
'get_redirect_url': function(namespace, repository) {
|
||||
return Config.getUrl('/customtrigger/setup/' + namespace + '/' + repository);
|
||||
},
|
||||
'is_external': false,
|
||||
'is_enabled': function() { return true; },
|
||||
'icon': 'fa-git',
|
||||
'title': function() { return 'Custom Git Repository Push'; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,6 +97,13 @@ angular.module('quay').factory('TriggerService', ['UtilService', '$sanitize', 'K
|
|||
return type['get_redirect_url'](namespace, repository);
|
||||
};
|
||||
|
||||
triggerService.getDockerfileLocation = function(trigger) {
|
||||
if (!trigger.config.subdir) {
|
||||
return '//Dockerfile';
|
||||
}
|
||||
return '//' + trigger.config.subdir.replace(new RegExp('(^\/+|\/+$)'), '') + '/Dockerfile';
|
||||
};
|
||||
|
||||
triggerService.getTitle = function(name) {
|
||||
var type = triggerTypes[name];
|
||||
if (!type) {
|
||||
|
|
Binary file not shown.
10
util/ssh.py
Normal file
10
util/ssh.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from Crypto.PublicKey import RSA
|
||||
|
||||
def generate_ssh_keypair():
|
||||
"""
|
||||
Generates a new 2048 bit RSA public key in OpenSSH format and private key in PEM format.
|
||||
"""
|
||||
key = RSA.generate(2048)
|
||||
public_key = key.publickey().exportKey('OpenSSH')
|
||||
private_key = key.exportKey('PEM')
|
||||
return (public_key, private_key)
|
Reference in a new issue