provisioning-core/nulib/domain/gitea/workspace_git.nu

469 lines
10 KiB
Text

# Gitea Workspace Git Operations
#
# Workspace-level git integration with Gitea
#
# Version: 1.0.0
# Selective imports (ADR-025 Phase 3 Layer 2).
use domain/gitea/api_client.nu [create-repository get-gitea-config get-repository]
# Initialize workspace as git repository
export def init-workspace-git [
workspace_path: string
workspace_name: string
--remote: string = "gitea" # gitea, github, gitlab, none
--private: bool = true
] -> bool {
let ws_path = $workspace_path | path expand
if not ($ws_path | path exists) {
error make {
msg: $"Workspace path does not exist: ($ws_path)"
}
}
# Initialize git repository
cd $ws_path
^git init
# Configure git
^git config user.name "Provisioning System"
^git config user.email "provisioning@system.local"
# Create initial commit
^git add .
^git commit -m "Initial workspace commit"
# Create main branch
^git branch -M main
# Add remote if requested
if $remote == "gitea" {
let config = get-gitea-config
let org = $config.repositories.workspaces_org
# Create repository on Gitea
let repo = create-repository $org $workspace_name $"Workspace: ($workspace_name)" $private
let repo_url = $repo.clone_url
# Add remote
^git remote add origin $repo_url
# Push initial commit
^git push -u origin main
print $"✓ Workspace initialized with Gitea remote: ($repo_url)"
} else if $remote != "none" {
print $"⚠️ Remote type '($remote)' not yet implemented"
}
true
}
# Create repository on Gitea for workspace
export def create-workspace-repo [
workspace_name: string
--description: string = ""
--private: bool = true
] -> record {
let config = get-gitea-config
let org = $config.repositories.workspaces_org
let desc = if ($description | is-empty) {
$"Provisioning workspace: ($workspace_name)"
} else {
$description
}
create-repository $org $workspace_name $desc $private
}
# Clone workspace from Gitea
export def clone-workspace [
workspace_identifier: string # org/workspace-name or just workspace-name
destination_path: string
--branch: string = "main"
] -> bool {
let config = get-gitea-config
# Parse workspace identifier
let parts = $workspace_identifier | split row "/"
let repo_name = if ($parts | length) == 2 {
$parts.1
} else {
$workspace_identifier
}
let org = if ($parts | length) == 2 {
$parts.0
} else {
$config.repositories.workspaces_org
}
# Get repository info
let repo = get-repository $org $repo_name
let clone_url = $repo.clone_url
# Clone repository
^git clone --branch $branch $clone_url $destination_path
print $"✓ Workspace cloned from ($clone_url)"
true
}
# Push workspace changes
export def push-workspace [
workspace_path: string
commit_message?: string
--branch: string = "main"
--add-all: bool = true
] -> bool {
let ws_path = $workspace_path | path expand
cd $ws_path
# Check if git repository
if not ($"($ws_path)/.git" | path exists) {
error make {
msg: "Not a git repository"
help: "Initialize with 'workspace init --git'"
}
}
# Add changes
if $add_all {
^git add .
}
# Check if there are changes
let status = ^git status --porcelain | complete
if ($status.stdout | is-empty) {
print "No changes to commit"
return false
}
# Commit changes
let msg = if ($commit_message | is-empty) {
let timestamp = date now | format date "%Y-%m-%d %H:%M:%S"
$"Workspace update: ($timestamp)"
} else {
$commit_message
}
^git commit -m $msg
# Push to remote
^git push origin $branch
print $"✓ Workspace changes pushed to ($branch)"
true
}
# Pull workspace changes
export def pull-workspace [
workspace_path: string
--branch: string = "main"
] -> bool {
let ws_path = $workspace_path | path expand
cd $ws_path
# Check if git repository
if not ($"($ws_path)/.git" | path exists) {
error make {
msg: "Not a git repository"
}
}
# Pull changes
^git pull origin $branch
print $"✓ Workspace updated from ($branch)"
true
}
# Create branch
export def create-workspace-branch [
workspace_path: string
branch_name: string
--from-branch: string = "main"
] -> bool {
let ws_path = $workspace_path | path expand
cd $ws_path
# Create local branch
^git checkout -b $branch_name $from_branch
# Push to remote
^git push -u origin $branch_name
print $"✓ Branch created: ($branch_name)"
true
}
# Switch branch
export def switch-workspace-branch [
workspace_path: string
branch_name: string
] -> bool {
let ws_path = $workspace_path | path expand
cd $ws_path
# Switch branch
^git checkout $branch_name
print $"✓ Switched to branch: ($branch_name)"
true
}
# List branches
export def list-workspace-branches [
workspace_path: string
--remote: bool = false
] -> list {
let ws_path = $workspace_path | path expand
cd $ws_path
if $remote {
^git branch -r | lines | each {|line| $line | str trim | str replace "origin/" ""}
} else {
^git branch | lines | each {|line| $line | str trim | str replace "* " ""}
}
}
# Delete branch
export def delete-workspace-branch [
workspace_path: string
branch_name: string
--remote: bool = false
--force: bool = false
] -> bool {
let ws_path = $workspace_path | path expand
cd $ws_path
# Delete local branch
if $force {
^git branch -D $branch_name
} else {
^git branch -d $branch_name
}
# Delete remote branch
if $remote {
^git push origin --delete $branch_name
}
print $"✓ Branch deleted: ($branch_name)"
true
}
# Sync workspace (pull + push)
export def sync-workspace [
workspace_path: string
--branch: string = "main"
--commit-message?: string
] -> record {
let ws_path = $workspace_path | path expand
# Pull latest changes
pull-workspace $ws_path --branch $branch
# Push local changes
let pushed = push-workspace $ws_path $commit_message --branch $branch
{
pulled: true
pushed: $pushed
branch: $branch
}
}
# Get workspace git status
export def get-workspace-git-status [
workspace_path: string
] -> record {
let ws_path = $workspace_path | path expand
cd $ws_path
# Check if git repository
if not ($"($ws_path)/.git" | path exists) {
return {
is_git_repo: false
branch: null
status: null
remote: null
}
}
# Get current branch
let branch = ^git branch --show-current | str trim
# Get status
let status = ^git status --porcelain | lines
# Get remote
let remote_result = (do {
^git remote get-url origin
} | complete)
let remote = if $remote_result.exit_code == 0 {
$remote_result.stdout | str trim
} else {
null
}
# Get last commit
let commit_result = (do {
^git log -1 --format="%H|%an|%ae|%at|%s"
} | complete)
let last_commit = if $commit_result.exit_code == 0 {
$commit_result.stdout | str trim
} else {
null
}
let commit_info = if ($last_commit | is-not-empty) {
let parts = $last_commit | split row "|"
{
hash: $parts.0
author_name: $parts.1
author_email: $parts.2
timestamp: ($parts.3 | into int)
message: $parts.4
}
} else {
null
}
{
is_git_repo: true
branch: $branch
status: $status
remote: $remote
last_commit: $commit_info
has_changes: ($status | length) > 0
}
}
# Get workspace remote info
export def get-workspace-remote-info [
workspace_path: string
] -> record {
let ws_path = $workspace_path | path expand
cd $ws_path
let remote_result = (do {
^git remote get-url origin
} | complete)
if $remote_result.exit_code != 0 {
return {has_remote: false}
}
let remote_url = $remote_result.stdout | str trim
# Parse Gitea URL
let config = get-gitea-config
let base_url = if $config.mode == "local" {
$"http://localhost:($config.local.port)"
} else {
$config.remote.url
}
let is_gitea = $remote_url | str starts-with $base_url
if $is_gitea {
# Extract owner and repo from URL
let parts = $remote_url | str replace $"($base_url)/" "" | str replace ".git" "" | split row "/"
{
has_remote: true
type: "gitea"
url: $remote_url
owner: $parts.0
repo: $parts.1
}
} else {
{
has_remote: true
type: "external"
url: $remote_url
}
}
}
# Check if workspace has uncommitted changes
export def has-uncommitted-changes [
workspace_path: string
] -> bool {
let status = get-workspace-git-status $workspace_path
$status.has_changes
}
# Get workspace diff
export def get-workspace-diff [
workspace_path: string
--staged: bool = false
] -> string {
let ws_path = $workspace_path | path expand
cd $ws_path
if $staged {
^git diff --staged
} else {
^git diff
}
}
# Stash workspace changes
export def stash-workspace-changes [
workspace_path: string
message?: string
] -> bool {
let ws_path = $workspace_path | path expand
cd $ws_path
let msg = if ($message | is-empty) {
"Workspace stash"
} else {
$message
}
^git stash push -m $msg
print "✓ Changes stashed"
true
}
# Pop stashed changes
export def pop-workspace-stash [
workspace_path: string
] -> bool {
let ws_path = $workspace_path | path expand
cd $ws_path
^git stash pop
print "✓ Stash applied"
true
}
# List workspace stashes
export def list-workspace-stashes [
workspace_path: string
] -> list {
let ws_path = $workspace_path | path expand
cd $ws_path
^git stash list | lines
}