mirror of
https://gitea.computerliebe.org/ComputerLiebe_ORG_private/Gitea-VSCode-Clone-Plugin.git
synced 2024-12-26 23:41:52 +00:00
Merge pull request 'feat(gitea-git-clone): add support for creating repositories in Gitea' (#7) from 1.4.0-create-gitea-repos into main
Reviewed-on: ComputerLiebe_ORG_private/Gitea-VSCode-Clone-Plugin#7
This commit is contained in:
commit
4d2d1bb017
@ -2,6 +2,25 @@
|
||||
|
||||
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
|
||||
|
||||
@ -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 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
|
||||
|
||||
## 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: 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: Create Repository` → Create a new repository in a selected Gitea instance.
|
||||
|
||||
## Requirements
|
||||
|
||||
- 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
|
||||
|
||||
@ -70,6 +71,17 @@ Choose the repository you want to clone.
|
||||
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.
|
||||
|
||||
### 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
|
||||
|
||||
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.showOpenPullRequestsTooltip": "Offene Pull Requests anzeigen",
|
||||
"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.showOpenPullRequestsTooltip": "Show open pull requests",
|
||||
"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",
|
||||
"displayName": "Gitea Workflow",
|
||||
"description": "Clone from Gitea instances; Create PRs",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"publisher": "computerliebe",
|
||||
"engines": {
|
||||
"vscode": "^1.94.0"
|
||||
@ -45,6 +45,11 @@
|
||||
"command": "gitea.showOpenPullRequests",
|
||||
"title": " Show open Pull Requests",
|
||||
"category": "Gitea"
|
||||
},
|
||||
{
|
||||
"command": "gitea.createRepository",
|
||||
"title": " Create Repository",
|
||||
"category": "Gitea"
|
||||
}
|
||||
],
|
||||
"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
|
||||
async function createGiteaPullRequest() {
|
||||
// Aktuellen Workspace-Ordner abrufen
|
||||
@ -959,6 +1192,10 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
let showPRCommand = vscode.commands.registerCommand('gitea.showOpenPullRequests', showOpenPullRequests);
|
||||
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
|
||||
statusBarItem = await addStatusBarIcon();
|
||||
if (statusBarItem) {
|
||||
|
Loading…
Reference in New Issue
Block a user