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#include <wx/log.h>
35
36
38{
39
40 m_btnAddRepo->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
41 m_btnEditRepo->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
42 m_btnDelete->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
43
44}
45
47{
48}
49
50
52{
53 m_grid->ClearGrid();
54 m_cbDefault->SetValue( true );
55 m_author->SetValue( wxEmptyString );
56 m_authorEmail->SetValue( wxEmptyString );
57}
58
59static std::pair<wxString, wxString> getDefaultAuthorAndEmail()
60{
61 wxString name;
62 wxString email;
63 git_config_entry* name_c = nullptr;
64 git_config_entry* email_c = nullptr;
65
66 git_config* config = nullptr;
67
68 if( git_config_open_default( &config ) != 0 )
69 {
70 printf( "Failed to open default Git config: %s\n", giterr_last()->message );
71 return std::make_pair( name, email );
72 }
73
74 if( git_config_get_entry( &name_c, config, "user.name" ) != 0 )
75 {
76 printf( "Failed to get user.name from Git config: %s\n", giterr_last()->message );
77 }
78 if( git_config_get_entry( &email_c, config, "user.email" ) != 0 )
79 {
80 printf( "Failed to get user.email from Git config: %s\n", giterr_last()->message );
81 }
82
83 if( name_c )
84 name = name_c->value;
85
86 if( email_c )
87 email = email_c->value;
88
89 git_config_entry_free( name_c );
90 git_config_entry_free( email_c );
91 git_config_free( config );
92
93 return std::make_pair( name, email );
94}
95
97{
99 std::vector<COMMON_SETTINGS::GIT_REPOSITORY>& repos = settings->m_Git.repositories;
100
101 repos.clear();
102
103 for( int row = 0; row < m_grid->GetNumberRows(); row++ )
104 {
106
107 repo.active = m_grid->GetCellValue( row, COL_ACTIVE ) == "1";
108 repo.name = m_grid->GetCellValue( row, COL_NAME );
109 repo.path = m_grid->GetCellValue( row, COL_PATH );
110 repo.authType = m_grid->GetCellValue( row, COL_AUTH_TYPE );
111 repo.username = m_grid->GetCellValue( row, COL_USERNAME );
112
114 m_grid->GetCellValue( row, COL_PASSWORD ) );
115 repo.ssh_path = m_grid->GetCellValue( row, COL_SSH_PATH );
116 repo.checkValid = m_grid->GetCellValue( row, COL_STATUS ) == "1";
117 repos.push_back( repo );
118 }
119
120 settings->m_Git.useDefaultAuthor = m_cbDefault->GetValue();
121 settings->m_Git.authorName = m_author->GetValue();
122 settings->m_Git.authorEmail = m_authorEmail->GetValue();
123
124 return true;
125}
126
128{
129 git_libgit2_init();
130
131 git_remote_callbacks callbacks;
132 callbacks.version = GIT_REMOTE_CALLBACKS_VERSION;
133
134 typedef struct
135 {
137 bool success;
138 } callbacksPayload;
139
140 callbacksPayload cb_data( { &repository, true } ); // If we don't need authentication, then,
141 // we are successful
142 callbacks.payload = &cb_data;
143 callbacks.credentials =
144 [](git_cred** out, const char* url, const char* username, unsigned int allowed_types,
145 void* payload) -> int
146 {
147
148 // If we are asking for credentials, then, we need authentication
149
150 callbacksPayload* data = static_cast<callbacksPayload*>(payload);
151
152 data->success = false;
153
154 if( allowed_types & GIT_CREDTYPE_USERNAME )
155 {
156 data->success = true;
157 }
158 else if( data->repo->authType == "ssh" && ( allowed_types & GIT_CREDTYPE_SSH_KEY ) )
159 {
160 wxString sshKeyPath = data->repo->ssh_path;
161
162 // Check if the SSH key exists and is readable
163 if( wxFileExists( sshKeyPath ) && wxFile::Access( sshKeyPath, wxFile::read ) )
164 data->success = true;
165 }
166 else if( data->repo->authType == "password" )
167 {
168 data->success = ( allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT );
169 }
170
171 return 0;
172 };
173
174 // Create a temporary directory to initialize the Git repository
175 wxString tempDirPath = wxFileName::CreateTempFileName(wxT("kigit_temp"));
176
177 if( !wxFileName::Mkdir( tempDirPath, wxS_DIR_DEFAULT ) )
178 {
179 git_libgit2_shutdown();
180 wxLogError( "Failed to create temporary directory for Git repository (%s): %s", tempDirPath,
181 wxSysErrorMsg() );
182 return false;
183 }
184
185 // Initialize the Git repository
186 git_repository* repo = nullptr;
187 int result = git_repository_init( &repo, tempDirPath.mb_str( wxConvUTF8 ), 0 );
188
189 if (result != 0)
190 {
191 git_repository_free(repo);
192 git_libgit2_shutdown();
193 wxRmdir(tempDirPath);
194 return false;
195 }
196
197 git_remote* remote = nullptr;
198 result = git_remote_create_anonymous( &remote, repo, tempDirPath.mb_str( wxConvUTF8 ) );
199
200 if (result != 0)
201 {
202 git_remote_free(remote);
203 git_repository_free(repo);
204 git_libgit2_shutdown();
205 wxRmdir(tempDirPath);
206 return false;
207 }
208
209 // We don't really care about the result of this call, the authentication callback
210 // will set the return values we need
211 git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, nullptr, nullptr);
212
213 git_remote_disconnect(remote);
214 git_remote_free(remote);
215 git_repository_free(repo);
216
217 git_libgit2_shutdown();
218
219 // Clean up the temporary directory
220 wxRmdir(tempDirPath);
221
222 return cb_data.success;
223}
224
226{
227 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
228
229 m_grid->ClearGrid();
230
231 for( COMMON_SETTINGS::GIT_REPOSITORY& repo : settings->m_Git.repositories )
232 {
233 if( repo.name.IsEmpty() || repo.path.IsEmpty() )
234 continue;
235
236 int row = m_grid->GetNumberRows();
237 m_grid->AppendRows( 1 );
238
239 m_grid->SetCellRenderer( row, COL_ACTIVE, new wxGridCellBoolRenderer() );
240 m_grid->SetCellEditor( row, COL_ACTIVE, new wxGridCellBoolEditor() );
241 m_grid->SetCellValue( row, COL_ACTIVE, repo.active ? "1" : "0" );
242
243 m_grid->SetCellValue( row, COL_NAME, repo.name );
244 m_grid->SetCellValue( row, COL_PATH, repo.path );
245 m_grid->SetCellValue( row, COL_AUTH_TYPE, repo.authType );
246 m_grid->SetCellValue( row, COL_USERNAME, repo.username );
247
248 wxString password;
249 KIPLATFORM::SECRETS::GetSecret( repo.path, repo.username, password );
250 m_grid->SetCellValue( row, COL_PASSWORD, password );
251 m_grid->SetCellValue( row, COL_SSH_PATH, repo.ssh_path );
252
253 if( repo.active )
254 m_grid->SetCellValue( row, 3, testRepositoryConnection( repo ) ? "C" : "NC" );
255
256 }
257
258 m_cbDefault->SetValue( settings->m_Git.useDefaultAuthor );
259
260 if( settings->m_Git.useDefaultAuthor )
261 {
262 std::pair<wxString, wxString> defaultAuthor = getDefaultAuthorAndEmail();
263 m_author->SetValue( defaultAuthor.first );
264 m_authorEmail->SetValue( defaultAuthor.second );
265 m_author->Disable();
266 m_authorEmail->Disable();
267 }
268 else
269 {
270 m_author->SetValue( settings->m_Git.authorName );
271 m_authorEmail->SetValue( settings->m_Git.authorEmail );
272 }
273
274 return true;
275}
276
277void PANEL_GIT_REPOS::onDefaultClick( wxCommandEvent& event )
278{
279 m_author->Enable( !m_cbDefault->GetValue() );
280 m_authorEmail->Enable( !m_cbDefault->GetValue() );
281 m_authorLabel->Enable( !m_cbDefault->GetValue() );
282 m_authorEmailLabel->Enable( !m_cbDefault->GetValue() );
283}
284
285
286void PANEL_GIT_REPOS::onGridDClick( wxGridEvent& event )
287{
288 if( m_grid->GetNumberRows() <= 0 )
289 {
290 wxCommandEvent evt;
291 onAddClick( evt );
292 return;
293 }
294
295 int row = event.GetRow();
296
297 if( row < 0 || row >= m_grid->GetNumberRows() )
298 return;
299
300 DIALOG_GIT_REPOSITORY dialog( this, nullptr );
301
302 dialog.SetRepoName( m_grid->GetCellValue( row, COL_NAME ) );
303 dialog.SetRepoURL( m_grid->GetCellValue( row, COL_PATH ) );
304 dialog.SetUsername( m_grid->GetCellValue( row, COL_USERNAME ) );
305 dialog.SetRepoSSHPath( m_grid->GetCellValue( row, COL_SSH_PATH ) );
306 dialog.SetPassword( m_grid->GetCellValue( row, COL_PASSWORD ) );
307
308 wxString type = m_grid->GetCellValue( row, COL_AUTH_TYPE );
309
310 if( type == "password" )
312 else if( type == "ssh" )
314 else
316
317 if( dialog.ShowModal() == wxID_OK )
318 {
319 m_grid->SetCellValue( row, COL_NAME, dialog.GetRepoName() );
320 m_grid->SetCellValue( row, COL_PATH, dialog.GetRepoURL() );
321 m_grid->SetCellValue( row, COL_USERNAME, dialog.GetUsername() );
322 m_grid->SetCellValue( row, COL_SSH_PATH, dialog.GetRepoSSHPath() );
323 m_grid->SetCellValue( row, COL_PASSWORD, dialog.GetPassword() );
324
326 {
327 m_grid->SetCellValue( row, COL_AUTH_TYPE, "password" );
328 }
330 {
331 m_grid->SetCellValue( row, COL_AUTH_TYPE, "ssh" );
332 }
333 else
334 {
335 m_grid->SetCellValue( row, COL_AUTH_TYPE, "none" );
336 }
337 }
338
339}
340
341
342void PANEL_GIT_REPOS::onAddClick( wxCommandEvent& event )
343{
344
345 DIALOG_GIT_REPOSITORY dialog( m_parent, nullptr );
346
347 if( dialog.ShowModal() == wxID_OK )
348 {
349 int row = m_grid->GetNumberRows();
350 m_grid->AppendRows( 1 );
351
352 m_grid->SetCellValue( row, COL_NAME, dialog.GetRepoName() );
353 m_grid->SetCellValue( row, COL_PATH, dialog.GetRepoURL() );
354 m_grid->SetCellValue( row, COL_USERNAME, dialog.GetUsername() );
355 m_grid->SetCellValue( row, COL_SSH_PATH, dialog.GetRepoSSHPath() );
356 m_grid->SetCellValue( row, COL_PASSWORD, dialog.GetPassword() );
357
359 {
360 m_grid->SetCellValue( row, COL_AUTH_TYPE, "password" );
361 }
363 {
364 m_grid->SetCellValue( row, COL_AUTH_TYPE, "ssh" );
365 }
366 else
367 {
368 m_grid->SetCellValue( row, COL_AUTH_TYPE, "none" );
369 }
370
371 m_grid->MakeCellVisible( row, 0 );
372 }
373}
374
375
376void PANEL_GIT_REPOS::onEditClick( wxCommandEvent& event )
377{
378 wxGridEvent evt( m_grid->GetId(), wxEVT_GRID_CELL_LEFT_DCLICK, m_grid,
379 m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
380 onGridDClick( evt );
381}
382
383
384void PANEL_GIT_REPOS::onDeleteClick( wxCommandEvent& event )
385{
386 if( !m_grid->CommitPendingChanges() || m_grid->GetNumberRows() <= 0 )
387 return;
388
389 int curRow = m_grid->GetGridCursorRow();
390
391 m_grid->DeleteRows( curRow );
392
393 curRow = std::max( 0, curRow - 1 );
394 m_grid->MakeCellVisible( curRow, m_grid->GetGridCursorCol() );
395 m_grid->SetGridCursor( curRow, m_grid->GetGridCursorCol() );
396}
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
int ShowModal() override
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:679
void SetBitmap(const wxBitmapBundle &aBmp)
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:637
bool StoreSecret(const wxString &aService, const wxString &aKey, const wxString &aSecret)
bool GetSecret(const wxString &aService, const wxString &aKey, wxString &aSecret)
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:1060
see class PGM_BASE
std::vector< GIT_REPOSITORY > repositories