KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_git_repos.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2018-2023 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "panel_git_repos.h"
21
22#include <bitmaps.h>
24#include <kiplatform/secrets.h>
25#include <pgm_base.h>
28#include <widgets/wx_grid.h>
29
30#include <git2.h>
31#include <wx/bmpbuttn.h>
32#include <wx/button.h>
33#include <wx/checkbox.h>
34
35
37{
38
39 m_btnAddRepo->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
40 m_btnEditRepo->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
41 m_btnDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
42
43}
44
46{
47}
48
49
51{
52 m_grid->ClearGrid();
53 m_cbDefault->SetValue( true );
54 m_author->SetValue( wxEmptyString );
55 m_authorEmail->SetValue( wxEmptyString );
56}
57
58static std::pair<wxString, wxString> getDefaultAuthorAndEmail()
59{
60 wxString name;
61 wxString email;
62 git_config_entry* name_c = nullptr;
63 git_config_entry* email_c = nullptr;
64
65 git_config* config = nullptr;
66
67 if( git_config_open_default( &config ) != 0 )
68 {
69 printf( "Failed to open default Git config: %s\n", giterr_last()->message );
70 return std::make_pair( name, email );
71 }
72
73 if( git_config_get_entry( &name_c, config, "user.name" ) != 0 )
74 {
75 printf( "Failed to get user.name from Git config: %s\n", giterr_last()->message );
76 }
77 if( git_config_get_entry( &email_c, config, "user.email" ) != 0 )
78 {
79 printf( "Failed to get user.email from Git config: %s\n", giterr_last()->message );
80 }
81
82 if( name_c )
83 name = name_c->value;
84
85 if( email_c )
86 email = email_c->value;
87
88 git_config_entry_free( name_c );
89 git_config_entry_free( email_c );
90 git_config_free( config );
91
92 return std::make_pair( name, email );
93}
94
96{
98 std::vector<COMMON_SETTINGS::GIT_REPOSITORY>& repos = settings->m_Git.repositories;
99
100 repos.clear();
101
102 for( int row = 0; row < m_grid->GetNumberRows(); row++ )
103 {
105
106 repo.active = m_grid->GetCellValue( row, COL_ACTIVE ) == "1";
107 repo.name = m_grid->GetCellValue( row, COL_NAME );
108 repo.path = m_grid->GetCellValue( row, COL_PATH );
109 repo.authType = m_grid->GetCellValue( row, COL_AUTH_TYPE );
110 repo.username = m_grid->GetCellValue( row, COL_USERNAME );
111
113 m_grid->GetCellValue( row, COL_PASSWORD ) );
114 repo.ssh_path = m_grid->GetCellValue( row, COL_SSH_PATH );
115 repo.checkValid = m_grid->GetCellValue( row, COL_STATUS ) == "1";
116 repos.push_back( repo );
117 }
118
119 settings->m_Git.useDefaultAuthor = m_cbDefault->GetValue();
120 settings->m_Git.authorName = m_author->GetValue();
121 settings->m_Git.authorEmail = m_authorEmail->GetValue();
122
123 return true;
124}
125
127{
128 git_libgit2_init();
129
130 git_remote_callbacks callbacks;
131 callbacks.version = GIT_REMOTE_CALLBACKS_VERSION;
132
133 typedef struct
134 {
136 bool success;
137 } callbacksPayload;
138
139 callbacksPayload cb_data( { &repository, true } ); // If we don't need authentication, then,
140 // we are successful
141 callbacks.payload = &cb_data;
142 callbacks.credentials =
143 [](git_cred** out, const char* url, const char* username, unsigned int allowed_types,
144 void* payload) -> int
145 {
146
147 // If we are asking for credentials, then, we need authentication
148
149 callbacksPayload* data = static_cast<callbacksPayload*>(payload);
150
151 data->success = false;
152
153 if( allowed_types & GIT_CREDTYPE_USERNAME )
154 {
155 data->success = true;
156 }
157 else if( data->repo->authType == "ssh" && ( allowed_types & GIT_CREDTYPE_SSH_KEY ) )
158 {
159 wxString sshKeyPath = data->repo->ssh_path;
160
161 // Check if the SSH key exists and is readable
162 if( wxFileExists( sshKeyPath ) && wxFile::Access( sshKeyPath, wxFile::read ) )
163 data->success = true;
164 }
165 else if( data->repo->authType == "password" )
166 {
167 data->success = ( allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT );
168 }
169
170 return 0;
171 };
172
173 // Create a temporary directory to initialize the Git repository
174 wxString tempDirPath = wxFileName::CreateTempFileName(wxT("kigit_temp"));
175
176 if( !wxFileName::Mkdir( tempDirPath, wxS_DIR_DEFAULT ) )
177 {
178 git_libgit2_shutdown();
179 wxLogError( "Failed to create temporary directory for Git repository (%s): %s", tempDirPath,
180 wxSysErrorMsg() );
181 return false;
182 }
183
184 // Initialize the Git repository
185 git_repository* repo = nullptr;
186 int result = git_repository_init( &repo, tempDirPath.mb_str( wxConvUTF8 ), 0 );
187
188 if (result != 0)
189 {
190 git_repository_free(repo);
191 git_libgit2_shutdown();
192 wxRmdir(tempDirPath);
193 return false;
194 }
195
196 git_remote* remote = nullptr;
197 result = git_remote_create_anonymous( &remote, repo, tempDirPath.mb_str( wxConvUTF8 ) );
198
199 if (result != 0)
200 {
201 git_remote_free(remote);
202 git_repository_free(repo);
203 git_libgit2_shutdown();
204 wxRmdir(tempDirPath);
205 return false;
206 }
207
208 // We don't really care about the result of this call, the authentication callback
209 // will set the return values we need
210 git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, nullptr, nullptr);
211
212 git_remote_disconnect(remote);
213 git_remote_free(remote);
214 git_repository_free(repo);
215
216 git_libgit2_shutdown();
217
218 // Clean up the temporary directory
219 wxRmdir(tempDirPath);
220
221 return cb_data.success;
222}
223
225{
226 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
227
228 m_grid->ClearGrid();
229
230 for( COMMON_SETTINGS::GIT_REPOSITORY& repo : settings->m_Git.repositories )
231 {
232 if( repo.name.IsEmpty() || repo.path.IsEmpty() )
233 continue;
234
235 int row = m_grid->GetNumberRows();
236 m_grid->AppendRows( 1 );
237
238 m_grid->SetCellRenderer( row, COL_ACTIVE, new wxGridCellBoolRenderer() );
239 m_grid->SetCellEditor( row, COL_ACTIVE, new wxGridCellBoolEditor() );
240 m_grid->SetCellValue( row, COL_ACTIVE, repo.active ? "1" : "0" );
241
242 m_grid->SetCellValue( row, COL_NAME, repo.name );
243 m_grid->SetCellValue( row, COL_PATH, repo.path );
244 m_grid->SetCellValue( row, COL_AUTH_TYPE, repo.authType );
245 m_grid->SetCellValue( row, COL_USERNAME, repo.username );
246
247 wxString password;
248 KIPLATFORM::SECRETS::GetSecret( repo.path, repo.username, password );
249 m_grid->SetCellValue( row, COL_PASSWORD, password );
250 m_grid->SetCellValue( row, COL_SSH_PATH, repo.ssh_path );
251
252 if( repo.active )
253 m_grid->SetCellValue( row, 3, testRepositoryConnection( repo ) ? "C" : "NC" );
254
255 }
256
257 m_cbDefault->SetValue( settings->m_Git.useDefaultAuthor );
258
259 if( settings->m_Git.useDefaultAuthor )
260 {
261 std::pair<wxString, wxString> defaultAuthor = getDefaultAuthorAndEmail();
262 m_author->SetValue( defaultAuthor.first );
263 m_authorEmail->SetValue( defaultAuthor.second );
264 m_author->Disable();
265 m_authorEmail->Disable();
266 }
267 else
268 {
269 m_author->SetValue( settings->m_Git.authorName );
270 m_authorEmail->SetValue( settings->m_Git.authorEmail );
271 }
272
273 return true;
274}
275
276void PANEL_GIT_REPOS::onDefaultClick( wxCommandEvent& event )
277{
278 m_author->Enable( !m_cbDefault->GetValue() );
279 m_authorEmail->Enable( !m_cbDefault->GetValue() );
280 m_authorLabel->Enable( !m_cbDefault->GetValue() );
281 m_authorEmailLabel->Enable( !m_cbDefault->GetValue() );
282}
283
284
285void PANEL_GIT_REPOS::onGridDClick( wxGridEvent& event )
286{
287 if( m_grid->GetNumberRows() <= 0 )
288 {
289 wxCommandEvent evt;
290 onAddClick( evt );
291 return;
292 }
293
294 int row = event.GetRow();
295
296 if( row < 0 || row >= m_grid->GetNumberRows() )
297 return;
298
299 DIALOG_GIT_REPOSITORY dialog( this, nullptr );
300
301 dialog.SetRepoName( m_grid->GetCellValue( row, COL_NAME ) );
302 dialog.SetRepoURL( m_grid->GetCellValue( row, COL_PATH ) );
303 dialog.SetUsername( m_grid->GetCellValue( row, COL_USERNAME ) );
304 dialog.SetRepoSSHPath( m_grid->GetCellValue( row, COL_SSH_PATH ) );
305 dialog.SetPassword( m_grid->GetCellValue( row, COL_PASSWORD ) );
306
307 wxString type = m_grid->GetCellValue( row, COL_AUTH_TYPE );
308
309 if( type == "password" )
311 else if( type == "ssh" )
313 else
315
316 if( dialog.ShowModal() == wxID_OK )
317 {
318 m_grid->SetCellValue( row, COL_NAME, dialog.GetRepoName() );
319 m_grid->SetCellValue( row, COL_PATH, dialog.GetRepoURL() );
320 m_grid->SetCellValue( row, COL_USERNAME, dialog.GetUsername() );
321 m_grid->SetCellValue( row, COL_SSH_PATH, dialog.GetRepoSSHPath() );
322 m_grid->SetCellValue( row, COL_PASSWORD, dialog.GetPassword() );
323
325 {
326 m_grid->SetCellValue( row, COL_AUTH_TYPE, "password" );
327 }
329 {
330 m_grid->SetCellValue( row, COL_AUTH_TYPE, "ssh" );
331 }
332 else
333 {
334 m_grid->SetCellValue( row, COL_AUTH_TYPE, "none" );
335 }
336 }
337
338}
339
340
341void PANEL_GIT_REPOS::onAddClick( wxCommandEvent& event )
342{
343
344 DIALOG_GIT_REPOSITORY dialog( m_parent, nullptr );
345
346 if( dialog.ShowModal() == wxID_OK )
347 {
348 int row = m_grid->GetNumberRows();
349 m_grid->AppendRows( 1 );
350
351 m_grid->SetCellValue( row, COL_NAME, dialog.GetRepoName() );
352 m_grid->SetCellValue( row, COL_PATH, dialog.GetRepoURL() );
353 m_grid->SetCellValue( row, COL_USERNAME, dialog.GetUsername() );
354 m_grid->SetCellValue( row, COL_SSH_PATH, dialog.GetRepoSSHPath() );
355 m_grid->SetCellValue( row, COL_PASSWORD, dialog.GetPassword() );
356
358 {
359 m_grid->SetCellValue( row, COL_AUTH_TYPE, "password" );
360 }
362 {
363 m_grid->SetCellValue( row, COL_AUTH_TYPE, "ssh" );
364 }
365 else
366 {
367 m_grid->SetCellValue( row, COL_AUTH_TYPE, "none" );
368 }
369
370 m_grid->MakeCellVisible( row, 0 );
371 }
372}
373
374
375void PANEL_GIT_REPOS::onEditClick( wxCommandEvent& event )
376{
377 wxGridEvent evt( m_grid->GetId(), wxEVT_GRID_CELL_LEFT_DCLICK, m_grid,
378 m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
379 onGridDClick( evt );
380}
381
382
383void PANEL_GIT_REPOS::onDeleteClick( wxCommandEvent& event )
384{
385 if( !m_grid->CommitPendingChanges() || m_grid->GetNumberRows() <= 0 )
386 return;
387
388 int curRow = m_grid->GetGridCursorRow();
389
390 m_grid->DeleteRows( curRow );
391
392 curRow = std::max( 0, curRow - 1 );
393 m_grid->MakeCellVisible( curRow, m_grid->GetGridCursorCol() );
394 m_grid->SetGridCursor( curRow, m_grid->GetGridCursorCol() );
395}
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
void SetRepoType(KIGIT_COMMON::GIT_CONN_TYPE aType)
void SetRepoSSHPath(const wxString &aPath)
KIGIT_COMMON::GIT_CONN_TYPE GetRepoType() const
void SetRepoName(const wxString &aName)
void SetPassword(const wxString &aPassword)
wxString GetRepoSSHPath() const
void SetRepoURL(const wxString &aURL)
wxString GetUsername() const
wxString GetRepoName() const
void SetUsername(const wxString &aUsername)
wxString GetPassword() const
Class PANEL_GIT_REPOS_BASE.
STD_BITMAP_BUTTON * m_btnAddRepo
wxStaticText * m_authorLabel
STD_BITMAP_BUTTON * m_btnEditRepo
wxStaticText * m_authorEmailLabel
STD_BITMAP_BUTTON * m_btnDelete
PANEL_GIT_REPOS(wxWindow *parent)
~PANEL_GIT_REPOS() override
void onGridDClick(wxGridEvent &event) override
void onEditClick(wxCommandEvent &event) override
void onDefaultClick(wxCommandEvent &event) override
void onAddClick(wxCommandEvent &event) override
void ResetPanel() override
Reset the contents of this panel.
bool TransferDataToWindow() override
bool TransferDataFromWindow() override
void onDeleteClick(wxCommandEvent &event) override
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:678
void SetBitmap(const wxBitmapBundle &aBmp)
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:558
bool StoreSecret(const wxString &aService, const wxString &aKey, const wxString &aSecret)
Definition: gtk/secrets.cpp:38
bool GetSecret(const wxString &aService, const wxString &aKey, wxString &aSecret)
Definition: gtk/secrets.cpp:62
static bool testRepositoryConnection(COMMON_SETTINGS::GIT_REPOSITORY &repository)
static std::pair< wxString, wxString > getDefaultAuthorAndEmail()
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1059
see class PGM_BASE
std::vector< GIT_REPOSITORY > repositories