mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-20 02:11:36 +00:00
source-controls (#193)
* edit source control * assign project after creation * global and project scoped source controls
This commit is contained in:
parent
e704a13d6b
commit
179aefefac
@ -14,7 +14,7 @@ public function create(User $user, array $input): Project
|
|||||||
$input['name'] = strtolower($input['name']);
|
$input['name'] = strtolower($input['name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->validate($user, $input);
|
$this->validate($input);
|
||||||
|
|
||||||
$project = new Project([
|
$project = new Project([
|
||||||
'name' => $input['name'],
|
'name' => $input['name'],
|
||||||
@ -22,10 +22,12 @@ public function create(User $user, array $input): Project
|
|||||||
|
|
||||||
$project->save();
|
$project->save();
|
||||||
|
|
||||||
|
$project->users()->attach($user);
|
||||||
|
|
||||||
return $project;
|
return $project;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validate(User $user, array $input): void
|
private function validate(array $input): void
|
||||||
{
|
{
|
||||||
Validator::make($input, [
|
Validator::make($input, [
|
||||||
'name' => [
|
'name' => [
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Actions\SourceControl;
|
namespace App\Actions\SourceControl;
|
||||||
|
|
||||||
use App\Models\SourceControl;
|
use App\Models\SourceControl;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
@ -10,7 +11,7 @@
|
|||||||
|
|
||||||
class ConnectSourceControl
|
class ConnectSourceControl
|
||||||
{
|
{
|
||||||
public function connect(array $input): void
|
public function connect(User $user, array $input): void
|
||||||
{
|
{
|
||||||
$this->validate($input);
|
$this->validate($input);
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ public function connect(array $input): void
|
|||||||
'provider' => $input['provider'],
|
'provider' => $input['provider'],
|
||||||
'profile' => $input['name'],
|
'profile' => $input['name'],
|
||||||
'url' => Arr::has($input, 'url') ? $input['url'] : null,
|
'url' => Arr::has($input, 'url') ? $input['url'] : null,
|
||||||
|
'project_id' => isset($input['global']) && $input['global'] ? null : $user->current_project_id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->validateProvider($sourceControl, $input);
|
$this->validateProvider($sourceControl, $input);
|
||||||
|
54
app/Actions/SourceControl/EditSourceControl.php
Normal file
54
app/Actions/SourceControl/EditSourceControl.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\SourceControl;
|
||||||
|
|
||||||
|
use App\Models\SourceControl;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
|
class EditSourceControl
|
||||||
|
{
|
||||||
|
public function edit(SourceControl $sourceControl, User $user, array $input): void
|
||||||
|
{
|
||||||
|
$this->validate($input);
|
||||||
|
|
||||||
|
$sourceControl->profile = $input['name'];
|
||||||
|
$sourceControl->url = isset($input['url']) ? $input['url'] : null;
|
||||||
|
$sourceControl->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id;
|
||||||
|
|
||||||
|
$this->validateProvider($sourceControl, $input);
|
||||||
|
|
||||||
|
$sourceControl->provider_data = $sourceControl->provider()->createData($input);
|
||||||
|
|
||||||
|
if (! $sourceControl->provider()->connect()) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $sourceControl->provider]
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourceControl->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ValidationException
|
||||||
|
*/
|
||||||
|
private function validate(array $input): void
|
||||||
|
{
|
||||||
|
$rules = [
|
||||||
|
'name' => [
|
||||||
|
'required',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
Validator::make($input, $rules)->validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ValidationException
|
||||||
|
*/
|
||||||
|
private function validateProvider(SourceControl $sourceControl, array $input): void
|
||||||
|
{
|
||||||
|
Validator::make($input, $sourceControl->provider()->createRules($input))->validate();
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Actions\SourceControl\ConnectSourceControl;
|
use App\Actions\SourceControl\ConnectSourceControl;
|
||||||
use App\Actions\SourceControl\DeleteSourceControl;
|
use App\Actions\SourceControl\DeleteSourceControl;
|
||||||
|
use App\Actions\SourceControl\EditSourceControl;
|
||||||
use App\Facades\Toast;
|
use App\Facades\Toast;
|
||||||
use App\Helpers\HtmxResponse;
|
use App\Helpers\HtmxResponse;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
@ -14,16 +15,23 @@
|
|||||||
|
|
||||||
class SourceControlController extends Controller
|
class SourceControlController extends Controller
|
||||||
{
|
{
|
||||||
public function index(): View
|
public function index(Request $request): View
|
||||||
{
|
{
|
||||||
return view('settings.source-controls.index', [
|
$data = [
|
||||||
'sourceControls' => SourceControl::query()->orderByDesc('id')->get(),
|
'sourceControls' => SourceControl::getByCurrentProject(),
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if ($request->has('edit')) {
|
||||||
|
$data['editSourceControl'] = SourceControl::find($request->input('edit'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('settings.source-controls.index', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function connect(Request $request): HtmxResponse
|
public function connect(Request $request): HtmxResponse
|
||||||
{
|
{
|
||||||
app(ConnectSourceControl::class)->connect(
|
app(ConnectSourceControl::class)->connect(
|
||||||
|
$request->user(),
|
||||||
$request->input(),
|
$request->input(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -32,6 +40,19 @@ public function connect(Request $request): HtmxResponse
|
|||||||
return htmx()->redirect(route('settings.source-controls'));
|
return htmx()->redirect(route('settings.source-controls'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function update(SourceControl $sourceControl, Request $request): HtmxResponse
|
||||||
|
{
|
||||||
|
app(EditSourceControl::class)->edit(
|
||||||
|
$sourceControl,
|
||||||
|
$request->user(),
|
||||||
|
$request->input(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Toast::success('Source control updated.');
|
||||||
|
|
||||||
|
return htmx()->redirect(route('settings.source-controls'));
|
||||||
|
}
|
||||||
|
|
||||||
public function delete(SourceControl $sourceControl): RedirectResponse
|
public function delete(SourceControl $sourceControl): RedirectResponse
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* @property User $user
|
* @property User $user
|
||||||
* @property Collection<Server> $servers
|
* @property Collection<Server> $servers
|
||||||
* @property Collection<NotificationChannel> $notificationChannels
|
* @property Collection<NotificationChannel> $notificationChannels
|
||||||
|
* @property Collection<SourceControl> $sourceControls
|
||||||
*/
|
*/
|
||||||
class Project extends Model
|
class Project extends Model
|
||||||
{
|
{
|
||||||
@ -59,4 +60,9 @@ public function users(): BelongsToMany
|
|||||||
{
|
{
|
||||||
return $this->belongsToMany(User::class, 'user_project')->withTimestamps();
|
return $this->belongsToMany(User::class, 'user_project')->withTimestamps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function sourceControls(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(SourceControl::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\SourceControlProviders\SourceControlProvider;
|
use App\SourceControlProviders\SourceControlProvider;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,6 +14,7 @@
|
|||||||
* @property ?string $profile
|
* @property ?string $profile
|
||||||
* @property ?string $url
|
* @property ?string $url
|
||||||
* @property string $access_token
|
* @property string $access_token
|
||||||
|
* @property ?int $project_id
|
||||||
*/
|
*/
|
||||||
class SourceControl extends AbstractModel
|
class SourceControl extends AbstractModel
|
||||||
{
|
{
|
||||||
@ -23,11 +26,13 @@ class SourceControl extends AbstractModel
|
|||||||
'profile',
|
'profile',
|
||||||
'url',
|
'url',
|
||||||
'access_token',
|
'access_token',
|
||||||
|
'project_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'access_token' => 'encrypted',
|
'access_token' => 'encrypted',
|
||||||
'provider_data' => 'encrypted:array',
|
'provider_data' => 'encrypted:array',
|
||||||
|
'project_id' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function provider(): SourceControlProvider
|
public function provider(): SourceControlProvider
|
||||||
@ -46,4 +51,16 @@ public function sites(): HasMany
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Site::class);
|
return $this->hasMany(Site::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function project(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Project::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByCurrentProject(): Collection
|
||||||
|
{
|
||||||
|
return self::query()
|
||||||
|
->where('project_id', auth()->user()->current_project_id)
|
||||||
|
->orWhereNull('project_id')->get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('source_controls', function (Blueprint $table) {
|
||||||
|
$table->unsignedBigInteger('project_id')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('source_controls', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('project_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
19
resources/views/components/checkbox.blade.php
Normal file
19
resources/views/components/checkbox.blade.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
@props([
|
||||||
|
"disabled" => false,
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"value",
|
||||||
|
])
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input
|
||||||
|
id="{{ $id }}"
|
||||||
|
name="{{ $name }}"
|
||||||
|
type="checkbox"
|
||||||
|
value="{{ $value }}"
|
||||||
|
{{ $attributes->merge(["disabled" => $disabled, "class" => "rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-900 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800"]) }}
|
||||||
|
/>
|
||||||
|
<label for="{{ $id }}" class="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300">
|
||||||
|
{{ $slot }}
|
||||||
|
</label>
|
||||||
|
</div>
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
x-data="{
|
x-data="{
|
||||||
show: @js($show),
|
forceShow: @js($show),
|
||||||
|
show: false,
|
||||||
focusables() {
|
focusables() {
|
||||||
// All focusable element types...
|
// All focusable element types...
|
||||||
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
|
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
|
||||||
@ -34,6 +35,7 @@
|
|||||||
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
|
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
|
||||||
}"
|
}"
|
||||||
x-init="
|
x-init="
|
||||||
|
setTimeout(() => (show = forceShow), 100)
|
||||||
$watch('show', (value) => {
|
$watch('show', (value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
document.body.classList.add('overflow-y-hidden')
|
document.body.classList.add('overflow-y-hidden')
|
||||||
|
@ -131,6 +131,15 @@ class="text-primary-500"
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<x-checkbox id="global" name="global" :checked="old('global')" value="1">
|
||||||
|
Is Global (Accessible in all projects)
|
||||||
|
</x-checkbox>
|
||||||
|
@error("global")
|
||||||
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-6 flex justify-end">
|
<div class="mt-6 flex justify-end">
|
||||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||||
{{ __("Cancel") }}
|
{{ __("Cancel") }}
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
<x-modal name="edit-source-control" :show="true">
|
||||||
|
<form
|
||||||
|
id="edit-source-control-form"
|
||||||
|
hx-post="{{ route("settings.source-controls.update", ["sourceControl" => $sourceControl->id]) }}"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-select="#edit-source-control-form"
|
||||||
|
hx-ext="disable-element"
|
||||||
|
hx-disable-element="#btn-edit-source-control"
|
||||||
|
class="p-6"
|
||||||
|
>
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||||
|
{{ __("Edit Source Control") }}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<x-input-label for="name" value="Name" />
|
||||||
|
<x-text-input value="{{ $sourceControl->profile }}" id="name" name="name" type="text" class="mt-1 w-full" />
|
||||||
|
@error("name")
|
||||||
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if ($sourceControl->provider == \App\Enums\SourceControl::GITLAB)
|
||||||
|
<div class="mt-6">
|
||||||
|
<x-input-label for="url" value="Url (optional)" />
|
||||||
|
<x-text-input
|
||||||
|
value="{{ old('url', $sourceControl->url) }}"
|
||||||
|
id="url"
|
||||||
|
name="url"
|
||||||
|
type="text"
|
||||||
|
class="mt-1 w-full"
|
||||||
|
placeholder="e.g. https://gitlab.example.com/"
|
||||||
|
/>
|
||||||
|
<x-input-help>
|
||||||
|
If you run a self-managed gitlab enter the url here, leave empty to use gitlab.com
|
||||||
|
</x-input-help>
|
||||||
|
@error("url")
|
||||||
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (in_array($sourceControl->provider, [\App\Enums\SourceControl::GITLAB, \App\Enums\SourceControl::GITHUB]))
|
||||||
|
<div class="mt-6">
|
||||||
|
<x-input-label for="token" value="API Key" />
|
||||||
|
<x-text-input
|
||||||
|
value="{{ old('token', $sourceControl->provider()->data()['token']) }}"
|
||||||
|
id="token"
|
||||||
|
name="token"
|
||||||
|
type="text"
|
||||||
|
class="mt-1 w-full"
|
||||||
|
/>
|
||||||
|
@error("token")
|
||||||
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($sourceControl->provider == \App\Enums\SourceControl::BITBUCKET)
|
||||||
|
<div>
|
||||||
|
<div class="mt-6">
|
||||||
|
<x-input-label for="username" value="Username" />
|
||||||
|
<x-text-input
|
||||||
|
value="{{ old('username', $sourceControl->provider()->data()['username']) }}"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
type="text"
|
||||||
|
class="mt-1 w-full"
|
||||||
|
/>
|
||||||
|
<x-input-help>Your Bitbucket username</x-input-help>
|
||||||
|
@error("username")
|
||||||
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<x-input-label for="password" value="Password" />
|
||||||
|
<x-text-input
|
||||||
|
value="{{ old('password', $sourceControl->provider()->data()['password']) }}"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
type="text"
|
||||||
|
class="mt-1 w-full"
|
||||||
|
/>
|
||||||
|
<x-input-help>
|
||||||
|
Create a new
|
||||||
|
<a
|
||||||
|
class="text-primary-500"
|
||||||
|
href="https://bitbucket.org/account/settings/app-passwords/new"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
App Password
|
||||||
|
</a>
|
||||||
|
in your Bitbucket account with write and admin access to Workspaces, Projects, Repositories and
|
||||||
|
Webhooks
|
||||||
|
</x-input-help>
|
||||||
|
@error("password")
|
||||||
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<x-checkbox
|
||||||
|
id="global"
|
||||||
|
name="global"
|
||||||
|
:checked="old('global', $sourceControl->project_id === null ? 1 : null)"
|
||||||
|
value="1"
|
||||||
|
>
|
||||||
|
Is Global (Accessible in all projects)
|
||||||
|
</x-checkbox>
|
||||||
|
@error("global")
|
||||||
|
<x-input-error class="mt-2" :messages="$message" />
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 flex justify-end">
|
||||||
|
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||||
|
{{ __("Cancel") }}
|
||||||
|
</x-secondary-button>
|
||||||
|
|
||||||
|
<x-primary-button id="btn-edit-source-control" class="ml-3">
|
||||||
|
{{ __("Save") }}
|
||||||
|
</x-primary-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</x-modal>
|
@ -14,13 +14,26 @@
|
|||||||
@include("settings.source-controls.partials.icons." . $sourceControl->provider . "-icon")
|
@include("settings.source-controls.partials.icons." . $sourceControl->provider . "-icon")
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3 flex flex-grow flex-col items-start justify-center">
|
<div class="ml-3 flex flex-grow flex-col items-start justify-center">
|
||||||
<span class="mb-1">{{ $sourceControl->profile }}</span>
|
<div class="mb-1 flex items-center">
|
||||||
|
{{ $sourceControl->profile }}
|
||||||
|
@if (! $sourceControl->project_id)
|
||||||
|
<x-status status="disabled" class="ml-2">GLOBAL</x-status>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
<span class="text-sm text-gray-400">
|
<span class="text-sm text-gray-400">
|
||||||
<x-datetime :value="$sourceControl->created_at" />
|
<x-datetime :value="$sourceControl->created_at" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="inline">
|
<div class="inline">
|
||||||
|
<x-icon-button
|
||||||
|
hx-get="{{ route('settings.source-controls', ['edit' => $sourceControl->id]) }}"
|
||||||
|
hx-replace-url="true"
|
||||||
|
hx-select="#edit"
|
||||||
|
hx-target="#edit"
|
||||||
|
>
|
||||||
|
<x-heroicon name="o-pencil" class="h-5 w-5" />
|
||||||
|
</x-icon-button>
|
||||||
<x-icon-button
|
<x-icon-button
|
||||||
x-on:click="deleteAction = '{{ route('settings.source-controls.delete', $sourceControl->id) }}'; $dispatch('open-modal', 'delete-source-control')"
|
x-on:click="deleteAction = '{{ route('settings.source-controls.delete', $sourceControl->id) }}'; $dispatch('open-modal', 'delete-source-control')"
|
||||||
>
|
>
|
||||||
@ -32,6 +45,12 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
@include("settings.source-controls.partials.delete-source-control")
|
@include("settings.source-controls.partials.delete-source-control")
|
||||||
|
|
||||||
|
<div id="edit">
|
||||||
|
@if (isset($editSourceControl))
|
||||||
|
@include("settings.source-controls.partials.edit-source-control", ["sourceControl" => $editSourceControl])
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@else
|
@else
|
||||||
<x-simple-card>
|
<x-simple-card>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="mt-1 flex items-center">
|
<div class="mt-1 flex items-center">
|
||||||
<x-select-input id="source_control" name="source_control" class="mt-1 w-full">
|
<x-select-input id="source_control" name="source_control" class="mt-1 w-full">
|
||||||
<option value="" selected>{{ __("Select") }}</option>
|
<option value="" selected>{{ __("Select") }}</option>
|
||||||
@foreach ($sourceControls as $sourceControl)
|
@foreach (\App\Models\SourceControl::getByCurrentProject() as $sourceControl)
|
||||||
<option
|
<option
|
||||||
value="{{ $sourceControl->id }}"
|
value="{{ $sourceControl->id }}"
|
||||||
@if($sourceControl->id == old('source_control', isset($site) ? $site->source_control_id : null)) selected @endif
|
@if($sourceControl->id == old('source_control', isset($site) ? $site->source_control_id : null)) selected @endif
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
Route::get('/', [SourceControlController::class, 'index'])->name('settings.source-controls');
|
Route::get('/', [SourceControlController::class, 'index'])->name('settings.source-controls');
|
||||||
Route::post('connect', [SourceControlController::class, 'connect'])->name('settings.source-controls.connect');
|
Route::post('connect', [SourceControlController::class, 'connect'])->name('settings.source-controls.connect');
|
||||||
Route::delete('delete/{sourceControl}', [SourceControlController::class, 'delete'])->name('settings.source-controls.delete');
|
Route::delete('delete/{sourceControl}', [SourceControlController::class, 'delete'])->name('settings.source-controls.delete');
|
||||||
|
Route::post('edit/{sourceControl}', [SourceControlController::class, 'update'])->name('settings.source-controls.update');
|
||||||
});
|
});
|
||||||
|
|
||||||
// storage-providers
|
// storage-providers
|
||||||
|
@ -35,6 +35,20 @@ public function test_connect_provider(string $provider, ?string $customUrl, arra
|
|||||||
'provider' => $provider,
|
'provider' => $provider,
|
||||||
'url' => $customUrl,
|
'url' => $customUrl,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (isset($input['global'])) {
|
||||||
|
$this->assertDatabaseHas('source_controls', [
|
||||||
|
'provider' => $provider,
|
||||||
|
'url' => $customUrl,
|
||||||
|
'project_id' => null,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$this->assertDatabaseHas('source_controls', [
|
||||||
|
'provider' => $provider,
|
||||||
|
'url' => $customUrl,
|
||||||
|
'project_id' => $this->user->current_project_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,10 +99,38 @@ public function test_cannot_delete_provider(string $provider): void
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider data
|
||||||
|
*/
|
||||||
|
public function test_edit_source_control(string $provider, ?string $url, array $input): void
|
||||||
|
{
|
||||||
|
Http::fake();
|
||||||
|
|
||||||
|
$this->actingAs($this->user);
|
||||||
|
|
||||||
|
/** @var SourceControl $sourceControl */
|
||||||
|
$sourceControl = SourceControl::factory()->create([
|
||||||
|
'provider' => $provider,
|
||||||
|
'profile' => 'old-name',
|
||||||
|
'url' => $url,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->post(route('settings.source-controls.update', $sourceControl->id), array_merge([
|
||||||
|
'name' => 'new-name',
|
||||||
|
'url' => $url,
|
||||||
|
], $input))->assertSessionDoesntHaveErrors();
|
||||||
|
|
||||||
|
$sourceControl->refresh();
|
||||||
|
|
||||||
|
$this->assertEquals('new-name', $sourceControl->profile);
|
||||||
|
$this->assertEquals($url, $sourceControl->url);
|
||||||
|
}
|
||||||
|
|
||||||
public static function data(): array
|
public static function data(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['github', null, ['token' => 'test']],
|
['github', null, ['token' => 'test']],
|
||||||
|
['github', null, ['token' => 'test', 'global' => '1']],
|
||||||
['gitlab', null, ['token' => 'test']],
|
['gitlab', null, ['token' => 'test']],
|
||||||
['gitlab', 'https://git.example.com/', ['token' => 'test']],
|
['gitlab', 'https://git.example.com/', ['token' => 'test']],
|
||||||
['bitbucket', null, ['username' => 'test', 'password' => 'test']],
|
['bitbucket', null, ['username' => 'test', 'password' => 'test']],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user