mirror of
https://gitea.computerliebe.org/ComputerLiebe_ORG_private/Gitea-VSCode-Clone-Plugin.git
synced 2024-12-27 15:51:52 +00:00
feat(gitea-git-clone): add support for creating repositories in Gitea
* Added: * - Users can now create new repositories in a selected Gitea instance directly from VSCode. Options include: * - Selecting the Gitea instance (if multiple are configured). * - Choosing the owner (personal account or organization). * - Setting the repository visibility (private by default, or public). * - Selecting a `.gitignore` template (if available via API). * - The repository is initialized with an MIT license. * - After creation, the new repository is automatically cloned and opened in VSCode. * Changed: * - Updated localization files to include new messages related to repository creation. * * Fixed: * - Improved error handling and messages throughout the extension.
This commit is contained in:
parent
218970e57b
commit
e874cbefd6
@ -2,6 +2,25 @@
|
|||||||
|
|
||||||
All notable changes to the “gitea-workflow” extension will be documented in this file.
|
All notable changes to the “gitea-workflow” extension will be documented in this file.
|
||||||
|
|
||||||
|
## 1.4.0
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Create Repositories**: Users can now create new repositories in a selected Gitea instance directly from VSCode. Options include:
|
||||||
|
- Selecting the Gitea instance (if multiple are configured).
|
||||||
|
- Choosing the owner (personal account or organization).
|
||||||
|
- Setting the repository visibility (private by default, or public).
|
||||||
|
- Selecting a `.gitignore` template (if available via API).
|
||||||
|
- The repository is initialized with an MIT license.
|
||||||
|
- **Automatic Cloning**: After creation, the new repository is automatically cloned and opened in VSCode.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Localization**: Updated localization files to include new messages related to repository creation.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Minor Bug Fixes**: Improved error handling and messages throughout the extension.
|
||||||
|
|
||||||
## 1.3
|
## 1.3
|
||||||
|
|
||||||
@ -34,7 +53,7 @@ All notable changes to the “gitea-workflow” extension will be documented in
|
|||||||
|
|
||||||
- no errors if not a Git-Repository
|
- no errors if not a Git-Repository
|
||||||
- no errors if not a Gitea-Repository with configured upstream
|
- no errors if not a Gitea-Repository with configured upstream
|
||||||
- no Statusbar icons if not Git/Gitea-Repository
|
- no Status bar icons if not Git/Gitea-Repository
|
||||||
- truncate Commit Titles longer than 255 characters
|
- truncate Commit Titles longer than 255 characters
|
||||||
|
|
||||||
## 1.2.1
|
## 1.2.1
|
||||||
|
@ -19,11 +19,12 @@ A Visual Studio Code extension that allows you to manage your Gitea repositories
|
|||||||
- `Gitea: Clone Repository` → Clone a repository from a selected Gitea instance via SSH.
|
- `Gitea: Clone Repository` → Clone a repository from a selected Gitea instance via SSH.
|
||||||
- `Gitea: Create Pull Request` → Create a pull request for the current repository.
|
- `Gitea: Create Pull Request` → Create a pull request for the current repository.
|
||||||
- `Gitea: Show Open Pull Requests` → Show open pull requests for the current repository and link to them.
|
- `Gitea: Show Open Pull Requests` → Show open pull requests for the current repository and link to them.
|
||||||
|
- `Gitea: Create Repository` → Create a new repository in a selected Gitea instance.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- A Gitea instance with SSH access.
|
- A Gitea instance with SSH access.
|
||||||
- A Personal Access Token (PAT) from your Gitea instance(s) for authentication.
|
- A Personal Access Token (PAT) from your Gitea instance(s) with necessary permissions (e.g., `repo`, `admin:org`).
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
@ -70,6 +71,17 @@ Choose the repository you want to clone.
|
|||||||
Select the folder where you want to clone the repository.
|
Select the folder where you want to clone the repository.
|
||||||
The repository will be cloned via SSH and automatically opened in a new VSCode window.
|
The repository will be cloned via SSH and automatically opened in a new VSCode window.
|
||||||
|
|
||||||
|
### Creating a Repository
|
||||||
|
|
||||||
|
Run `Gitea: Create Repository` from the command palette.
|
||||||
|
Select the Gitea instance where you want to create the repository.
|
||||||
|
Choose the owner (your personal account or an organization you belong to).
|
||||||
|
Enter the name of the new repository.
|
||||||
|
The repository name will be sanitized and formatted to meet Gitea's requirements.
|
||||||
|
Select the visibility of the repository (Private or Public).
|
||||||
|
The repository will be created with an MIT license and initialized with a `.gitignore` template for Visual Studio Code.
|
||||||
|
After creation, the repository will be cloned, and you can start working immediately.
|
||||||
|
|
||||||
### Creating a Pull Request
|
### Creating a Pull Request
|
||||||
|
|
||||||
Ensure that you are on the branch for which you want to create a Pull Request.
|
Ensure that you are on the branch for which you want to create a Pull Request.
|
||||||
|
@ -36,5 +36,14 @@
|
|||||||
"giteaClone.selectPullRequest": "Wählen Sie einen Pull Request zum Anzeigen aus",
|
"giteaClone.selectPullRequest": "Wählen Sie einen Pull Request zum Anzeigen aus",
|
||||||
"giteaClone.showOpenPullRequestsTooltip": "Offene Pull Requests anzeigen",
|
"giteaClone.showOpenPullRequestsTooltip": "Offene Pull Requests anzeigen",
|
||||||
"giteaClone.branchStatusTooltip": "Aktueller Branch-Status",
|
"giteaClone.branchStatusTooltip": "Aktueller Branch-Status",
|
||||||
"giteaClone.buildStatusTooltip": "Build-Status des aktuellen Commits"
|
"giteaClone.buildStatusTooltip": "Build-Status des aktuellen Commits",
|
||||||
|
"giteaClone.createRepository": "Repository erstellen",
|
||||||
|
"giteaClone.selectOwner": "Wählen Sie den Besitzer für das Repository aus",
|
||||||
|
"giteaClone.noOwnerSelected": "Kein Besitzer ausgewählt.",
|
||||||
|
"giteaClone.enterRepoName": "Geben Sie den Namen des neuen Repositories ein",
|
||||||
|
"giteaClone.noRepoName": "Kein Repository-Name angegeben.",
|
||||||
|
"giteaClone.selectVisibility": "Wählen Sie die Sichtbarkeit des Repositories",
|
||||||
|
"giteaClone.selectGitignore": ".gitignore-Template auswählen (optional)",
|
||||||
|
"giteaClone.repoCreated": "Repository '{0}' erfolgreich erstellt.",
|
||||||
|
"giteaClone.createRepoError": "Fehler beim Erstellen des Repositories: {0}"
|
||||||
}
|
}
|
||||||
|
@ -36,5 +36,14 @@
|
|||||||
"giteaClone.selectPullRequest": "Select a pull request to view",
|
"giteaClone.selectPullRequest": "Select a pull request to view",
|
||||||
"giteaClone.showOpenPullRequestsTooltip": "Show open pull requests",
|
"giteaClone.showOpenPullRequestsTooltip": "Show open pull requests",
|
||||||
"giteaClone.branchStatusTooltip": "Current branch status",
|
"giteaClone.branchStatusTooltip": "Current branch status",
|
||||||
"giteaClone.buildStatusTooltip": "Build status of the current commit"
|
"giteaClone.buildStatusTooltip": "Build status of the current commit",
|
||||||
|
"giteaClone.createRepository": "Create Repository",
|
||||||
|
"giteaClone.selectOwner": "Select the owner for the repository",
|
||||||
|
"giteaClone.noOwnerSelected": "No owner selected.",
|
||||||
|
"giteaClone.enterRepoName": "Enter the name of the new repository",
|
||||||
|
"giteaClone.noRepoName": "No repository name provided.",
|
||||||
|
"giteaClone.selectVisibility": "Select the visibility of the repository",
|
||||||
|
"giteaClone.selectGitignore": "Select a .gitignore template (optional)",
|
||||||
|
"giteaClone.repoCreated": "Repository '{0}' created successfully.",
|
||||||
|
"giteaClone.createRepoError": "Error creating repository: {0}"
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"name": "gitea-workflow",
|
"name": "gitea-workflow",
|
||||||
"displayName": "Gitea Workflow",
|
"displayName": "Gitea Workflow",
|
||||||
"description": "Clone from Gitea instances; Create PRs",
|
"description": "Clone from Gitea instances; Create PRs",
|
||||||
"version": "1.3.0",
|
"version": "1.4.0",
|
||||||
"publisher": "computerliebe",
|
"publisher": "computerliebe",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.94.0"
|
"vscode": "^1.94.0"
|
||||||
@ -45,6 +45,11 @@
|
|||||||
"command": "gitea.showOpenPullRequests",
|
"command": "gitea.showOpenPullRequests",
|
||||||
"title": " Show open Pull Requests",
|
"title": " Show open Pull Requests",
|
||||||
"category": "Gitea"
|
"category": "Gitea"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gitea.createRepository",
|
||||||
|
"title": " Create Repository",
|
||||||
|
"category": "Gitea"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configuration": {
|
"configuration": {
|
||||||
|
BIN
gitea-git-clone/releases/gitea-workflow-1.4.0.vsix
Normal file
BIN
gitea-git-clone/releases/gitea-workflow-1.4.0.vsix
Normal file
Binary file not shown.
@ -88,6 +88,239 @@ async function isGitRepository(folderPath: string): Promise<boolean> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to slugify the repository name
|
||||||
|
function slugify(text: string): string {
|
||||||
|
return text
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.trim()
|
||||||
|
.replace(/[\s\W-]+/g, '-'); // Replace spaces and non-word characters with a dash
|
||||||
|
}
|
||||||
|
|
||||||
|
// UPDATED FUNCTION: Creating a Repository in Gitea
|
||||||
|
async function createGiteaRepository() {
|
||||||
|
const instances = getGiteaInstances();
|
||||||
|
|
||||||
|
if (instances.length === 0) {
|
||||||
|
vscode.window.showErrorMessage(localize('giteaClone.configMissing', 'No Gitea instances are configured.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User selects a Gitea instance
|
||||||
|
const instanceNames = instances.map(instance => instance.name);
|
||||||
|
const selectedInstanceName = await vscode.window.showQuickPick(instanceNames, {
|
||||||
|
placeHolder: localize('giteaClone.selectInstance', 'Select a Gitea instance to create the repository in')
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!selectedInstanceName) {
|
||||||
|
vscode.window.showInformationMessage(localize('giteaClone.noInstanceSelected', 'No Gitea instance selected.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedInstance = instances.find(instance => instance.name === selectedInstanceName);
|
||||||
|
|
||||||
|
if (!selectedInstance) {
|
||||||
|
vscode.window.showErrorMessage(localize('giteaClone.invalidInstance', 'Invalid Gitea instance selected.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const instanceUrl = selectedInstance.url;
|
||||||
|
const token = selectedInstance.token;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch user organizations
|
||||||
|
const organizations = await getUserOrganizations(instanceUrl, token);
|
||||||
|
const ownerChoices = ['(Personal)'].concat(organizations.map(org => org.username));
|
||||||
|
|
||||||
|
// User selects the owner (personal account or organization)
|
||||||
|
const selectedOwner = await vscode.window.showQuickPick(ownerChoices, {
|
||||||
|
placeHolder: localize('giteaClone.selectOwner', 'Select the owner for the repository')
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selectedOwner === undefined) {
|
||||||
|
vscode.window.showInformationMessage(localize('giteaClone.noOwnerSelected', 'No owner selected.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const owner = selectedOwner === '(Personal)' ? null : selectedOwner;
|
||||||
|
|
||||||
|
// User inputs the repository name
|
||||||
|
const repoNameInput = await vscode.window.showInputBox({
|
||||||
|
prompt: localize('giteaClone.enterRepoName', 'Enter the name of the new repository'),
|
||||||
|
placeHolder: 'my-new-repo'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!repoNameInput) {
|
||||||
|
vscode.window.showInformationMessage(localize('giteaClone.noRepoName', 'No repository name provided.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize and slugify the repository name
|
||||||
|
const repoName = slugify(repoNameInput);
|
||||||
|
|
||||||
|
if (!repoName) {
|
||||||
|
vscode.window.showErrorMessage(localize('giteaClone.invalidRepoName', 'Invalid repository name.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User selects the visibility
|
||||||
|
const visibilityChoice = await vscode.window.showQuickPick(['Private', 'Public'], {
|
||||||
|
placeHolder: localize('giteaClone.selectVisibility', 'Select the visibility of the repository'),
|
||||||
|
canPickMany: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const isPrivate = visibilityChoice !== 'Public'; // Default to Private if undefined
|
||||||
|
|
||||||
|
// Set the .gitignore template to 'VisualStudioCode' by default
|
||||||
|
const gitignoreTemplate = 'VisualStudioCode';
|
||||||
|
|
||||||
|
// Repository creation data
|
||||||
|
const repoData = {
|
||||||
|
name: repoName, // Slugified name
|
||||||
|
repo_name: repoNameInput, // Original name for display purposes
|
||||||
|
private: isPrivate,
|
||||||
|
auto_init: true,
|
||||||
|
license: 'MIT',
|
||||||
|
gitignores: gitignoreTemplate
|
||||||
|
};
|
||||||
|
|
||||||
|
const apiUrl = owner
|
||||||
|
? `${instanceUrl}/api/v1/orgs/${owner}/repos`
|
||||||
|
: `${instanceUrl}/api/v1/user/repos`;
|
||||||
|
|
||||||
|
const response = await axios.post(apiUrl, repoData, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `token ${token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const repo = response.data;
|
||||||
|
|
||||||
|
vscode.window.showInformationMessage(localize('giteaClone.repoCreated', `Repository '${repoNameInput}' created successfully.`));
|
||||||
|
|
||||||
|
// Clone the repository
|
||||||
|
await cloneRepository(repo.ssh_url, repoName);
|
||||||
|
|
||||||
|
} catch (err: any) {
|
||||||
|
let errMessage = '';
|
||||||
|
|
||||||
|
if (axios.isAxiosError(err)) {
|
||||||
|
const axiosError = err as AxiosError;
|
||||||
|
if (axiosError.response && axiosError.response.data) {
|
||||||
|
const data = axiosError.response.data as GiteaErrorResponse;
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
errMessage = data;
|
||||||
|
} else if (data.message) {
|
||||||
|
errMessage = data.message;
|
||||||
|
}
|
||||||
|
} else if (axiosError.message) {
|
||||||
|
errMessage = axiosError.message;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errMessage = err.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
let errorMessage = localize('giteaClone.createRepoError', `Error creating repository: ${errMessage}`);
|
||||||
|
vscode.window.showErrorMessage(errorMessage);
|
||||||
|
console.error('Error creating repository:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion zum Abrufen der Benutzerorganisationen
|
||||||
|
async function getUserOrganizations(instanceUrl: string, token: string): Promise<any[]> {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${instanceUrl}/api/v1/user/orgs`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `token ${token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
return response.data;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching user organizations:', err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion zum Abrufen der verfügbaren .gitignore-Templates
|
||||||
|
async function getGitignoreTemplates(instanceUrl: string, token: string): Promise<string[]> {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${instanceUrl}/api/v1/gitignores`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `token ${token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
return response.data;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching gitignore templates:', err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion zum Klonen eines Repositories
|
||||||
|
async function cloneRepository(sshUrl: string, repoName: string) {
|
||||||
|
const folderUri = await vscode.window.showOpenDialog({
|
||||||
|
canSelectFiles: false,
|
||||||
|
canSelectFolders: true,
|
||||||
|
canSelectMany: false,
|
||||||
|
openLabel: localize('giteaClone.selectFolder', 'Select folder to clone into')
|
||||||
|
});
|
||||||
|
|
||||||
|
if (folderUri && folderUri[0]) {
|
||||||
|
const folderPath = folderUri[0].fsPath;
|
||||||
|
const targetPath = path.join(folderPath, repoName);
|
||||||
|
|
||||||
|
// Überprüfen, ob das Zielverzeichnis bereits existiert
|
||||||
|
if (fs.existsSync(targetPath)) {
|
||||||
|
vscode.window.showErrorMessage(localize('giteaClone.targetExists', `The target directory "${targetPath}" already exists.`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vscode.window.withProgress({
|
||||||
|
location: vscode.ProgressLocation.Notification,
|
||||||
|
title: localize('giteaClone.cloningRepo', `Cloning ${repoName}`),
|
||||||
|
cancellable: false
|
||||||
|
}, async (progress, token) => {
|
||||||
|
progress.report({ message: localize('giteaClone.cloningProgress', 'Cloning in progress...'), increment: 0 });
|
||||||
|
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
exec(`git clone ${sshUrl} "${targetPath}"`, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
vscode.window.showErrorMessage(localize('giteaClone.cloneError', 'Error cloning the repository.'));
|
||||||
|
console.error(stderr);
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
progress.report({ message: localize('giteaClone.cloneComplete', 'Repository cloned successfully.'), increment: 100 });
|
||||||
|
vscode.window.showInformationMessage(localize('giteaClone.cloneSuccess', `Repository ${repoName} cloned successfully.`));
|
||||||
|
|
||||||
|
// Öffne das geklonte Repository im VSCode
|
||||||
|
try {
|
||||||
|
vscode.commands.executeCommand('vscode.openFolder', vscode.Uri.file(targetPath), true)
|
||||||
|
.then(() => resolve());
|
||||||
|
} catch (err: unknown) {
|
||||||
|
vscode.window.showErrorMessage(localize('giteaClone.openRepoError', 'Error opening the cloned repository.'));
|
||||||
|
console.error(err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
vscode.window.showInformationMessage(localize('giteaClone.noFolderSelected', 'No target folder selected.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Funktion zum Erstellen eines Pull Requests
|
// Funktion zum Erstellen eines Pull Requests
|
||||||
async function createGiteaPullRequest() {
|
async function createGiteaPullRequest() {
|
||||||
// Aktuellen Workspace-Ordner abrufen
|
// Aktuellen Workspace-Ordner abrufen
|
||||||
@ -959,6 +1192,10 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
let showPRCommand = vscode.commands.registerCommand('gitea.showOpenPullRequests', showOpenPullRequests);
|
let showPRCommand = vscode.commands.registerCommand('gitea.showOpenPullRequests', showOpenPullRequests);
|
||||||
context.subscriptions.push(showPRCommand);
|
context.subscriptions.push(showPRCommand);
|
||||||
|
|
||||||
|
// NEUER BEFEHL zum Erstellen eines Repositories
|
||||||
|
let createRepoCommand = vscode.commands.registerCommand('gitea.createRepository', createGiteaRepository);
|
||||||
|
context.subscriptions.push(createRepoCommand);
|
||||||
|
|
||||||
// Statusleisten-Icon erstellen
|
// Statusleisten-Icon erstellen
|
||||||
statusBarItem = await addStatusBarIcon();
|
statusBarItem = await addStatusBarIcon();
|
||||||
if (statusBarItem) {
|
if (statusBarItem) {
|
||||||
|
Loading…
Reference in New Issue
Block a user