Compare commits
1
Commits
f587a8baa2
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b7209dcc1 |
@@ -16,3 +16,13 @@ COOLIFY_TOKEN=REPLACE_WITH_COOLIFY_TOKEN
|
|||||||
# Generate at https://github.com/settings/tokens (classic 'repo' scope, or
|
# Generate at https://github.com/settings/tokens (classic 'repo' scope, or
|
||||||
# fine-grained with Contents:Read+Write and Metadata:Read).
|
# fine-grained with Contents:Read+Write and Metadata:Read).
|
||||||
GITHUB_TOKEN=REPLACE_WITH_GITHUB_PAT
|
GITHUB_TOKEN=REPLACE_WITH_GITHUB_PAT
|
||||||
|
|
||||||
|
# Used by gitea_skill (Invoke-GiteaApi.ps1, New-GiteaRepo.ps1, Sync-GiteaRemote.ps1).
|
||||||
|
# Self-hosted Gitea behind the Cloudflare tunnel. Generate a token at:
|
||||||
|
# <GITEA_URL>/user/settings/applications
|
||||||
|
# Scopes needed: read:repository, write:repository, read:user (and write:admin
|
||||||
|
# only if you manage other users). git push uses a one-shot http.extraHeader
|
||||||
|
# injected by Sync-GiteaRemote.ps1 — the token is NOT persisted to .git/config.
|
||||||
|
GITEA_URL=https://gitea-hjwh0svsoo9p5w5kj2j6b1bd.urieljareth.org
|
||||||
|
GITEA_USER=urieljareth
|
||||||
|
GITEA_TOKEN=REPLACE_WITH_GITEA_TOKEN
|
||||||
|
|||||||
@@ -61,6 +61,12 @@ Invoke-ProxmoxApi -Path "/version"
|
|||||||
|
|
||||||
# Cloudflare API (requires CLOUDFLARE_API_TOKEN) — full tunnel/DNS control
|
# Cloudflare API (requires CLOUDFLARE_API_TOKEN) — full tunnel/DNS control
|
||||||
.\scripts\Invoke-CloudflareApi.ps1 -Path "/user/tokens/verify"
|
.\scripts\Invoke-CloudflareApi.ps1 -Path "/user/tokens/verify"
|
||||||
|
|
||||||
|
# Gitea API (requires GITEA_URL + GITEA_TOKEN) — self-hosted git hosting
|
||||||
|
.\gitea_skill\scripts\Test-GiteaConnection.ps1
|
||||||
|
.\gitea_skill\scripts\Get-GiteaRepo.ps1 -List
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Path "/user"
|
||||||
|
.\gitea_skill\scripts\Sync-GiteaRemote.ps1 -AppPath .\my-app -CreateIfMissing -Force
|
||||||
```
|
```
|
||||||
|
|
||||||
For the Coolify API reference: don't read the whole tree. Search
|
For the Coolify API reference: don't read the whole tree. Search
|
||||||
@@ -118,4 +124,5 @@ root cause of "database connection" failures here — check with
|
|||||||
- [coolify_skill/SKILL.md](coolify_skill/SKILL.md) + [coolify_skill/TOOLS.md](coolify_skill/TOOLS.md) — Coolify agent operating skill and API reference index.
|
- [coolify_skill/SKILL.md](coolify_skill/SKILL.md) + [coolify_skill/TOOLS.md](coolify_skill/TOOLS.md) — Coolify agent operating skill and API reference index.
|
||||||
- [docs/AGENTS-coolify-apps.md](docs/AGENTS-coolify-apps.md) — compatibility guide for agents **developing** an app to deploy on this Coolify instance (networking, ports, domains/TLS, env, volumes, healthchecks) + pre-deploy checklist. Source this when building a new app, not when operating an existing one.
|
- [docs/AGENTS-coolify-apps.md](docs/AGENTS-coolify-apps.md) — compatibility guide for agents **developing** an app to deploy on this Coolify instance (networking, ports, domains/TLS, env, volumes, healthchecks) + pre-deploy checklist. Source this when building a new app, not when operating an existing one.
|
||||||
- [deploy_skill/SKILL.md](deploy_skill/SKILL.md) + [deploy_skill/TOOLS.md](deploy_skill/TOOLS.md) — deploy pipeline skill (scaffold → push → Coolify API → verify → rollback).
|
- [deploy_skill/SKILL.md](deploy_skill/SKILL.md) + [deploy_skill/TOOLS.md](deploy_skill/TOOLS.md) — deploy pipeline skill (scaffold → push → Coolify API → verify → rollback).
|
||||||
|
- [gitea_skill/SKILL.md](gitea_skill/SKILL.md) + [gitea_skill/TOOLS.md](gitea_skill/TOOLS.md) — self-hosted Gitea skill: list/create/mirror repos, wire a `gitea` remote, push headlessly via one-shot `http.extraHeader` (token never persisted). Distinct from deploy_skill (which targets Coolify); this manages the git hosting layer.
|
||||||
- `PROXMOX/`, `proxmox-agent/`, `proxmox-skill/` are **legacy pointer folders** — they only redirect to the live docs above. Don't add content there.
|
- `PROXMOX/`, `proxmox-agent/`, `proxmox-skill/` are **legacy pointer folders** — they only redirect to the live docs above. Don't add content there.
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ Verificado el 2026-05-30 desde esta maquina:
|
|||||||
punta (scaffold Dockerfile/compose → checklist → repo GitHub → push → alta
|
punta (scaffold Dockerfile/compose → checklist → repo GitHub → push → alta
|
||||||
via API → deploy → verificacion → rollback). Necesita `GITHUB_TOKEN` y
|
via API → deploy → verificacion → rollback). Necesita `GITHUB_TOKEN` y
|
||||||
`COOLIFY_TOKEN` en `.env.local.ps1`.
|
`COOLIFY_TOKEN` en `.env.local.ps1`.
|
||||||
|
- `gitea_skill/`: skill para operar la instancia Gitea self-hosted (crear/listar/
|
||||||
|
buscar repos, migrar desde GitHub, wire de un remoto `gitea` y push headless
|
||||||
|
con token inyectado por invocacion — sin persistirlo en `.git/config`).
|
||||||
|
Necesita `GITEA_URL`, `GITEA_USER`, `GITEA_TOKEN` en `.env.local.ps1`.
|
||||||
- `docs/proxmox-inventory.md`: inventario verificado y notas de arquitectura.
|
- `docs/proxmox-inventory.md`: inventario verificado y notas de arquitectura.
|
||||||
- `docs/runbooks/`: procedimientos concretos para conexion, diagnostico y Coolify.
|
- `docs/runbooks/`: procedimientos concretos para conexion, diagnostico y Coolify.
|
||||||
- `docs/runbooks/nextcloud.md`: recuperacion y fix HTTPS para Nextcloud.
|
- `docs/runbooks/nextcloud.md`: recuperacion y fix HTTPS para Nextcloud.
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
---
|
||||||
|
name: gitea-agent
|
||||||
|
description: Operate the local self-hosted Gitea instance (gitea-...urieljareth.org) for this Proxmox and Coolify Manager project. Use when Codex needs to create/list/search/mirror Gitea repositories, push a local project to Gitea headlessly (token in extraHeader, no wincred), inspect branches/releases/hooks, or wire a `gitea` remote alongside a GitHub `origin`. Distinct from coolify-deploy (which ships to Coolify) — this skill only manages the git hosting layer on Gitea.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gitea Agent
|
||||||
|
|
||||||
|
Use this project-local skill for Gitea work in this repository. The Gitea
|
||||||
|
instance lives behind the same Cloudflare tunnel as Coolify and is reachable
|
||||||
|
only via its public URL.
|
||||||
|
|
||||||
|
## Startup routine
|
||||||
|
|
||||||
|
1. Load private values: `. .\.env.local.ps1` (provides `GITEA_URL`, `GITEA_USER`,
|
||||||
|
`GITEA_TOKEN`).
|
||||||
|
2. Run the smoke test: `.\gitea_skill\scripts\Test-GiteaConnection.ps1`.
|
||||||
|
3. Only after it passes, perform the requested operation.
|
||||||
|
|
||||||
|
## Local context (verified 2026-07-19)
|
||||||
|
|
||||||
|
- Gitea URL: `https://gitea-hjwh0svsoo9p5w5kj2j6b1bd.urieljareth.org`
|
||||||
|
- Gitea version: `1.26.2`
|
||||||
|
- Authenticated user: `urieljareth` (admin)
|
||||||
|
- Auth header: `Authorization: token <GITEA_TOKEN>` (canonical Gitea form;
|
||||||
|
`Bearer` also works on 1.16+).
|
||||||
|
- The Gitea instance runs as a Coolify container inside LXC `102` on the
|
||||||
|
Proxmox host `192.168.0.200`. Container-level work goes through
|
||||||
|
`pct exec 102 -- docker ...` (see `coolify_skill`); this skill speaks only
|
||||||
|
to the public REST API.
|
||||||
|
- Secrets live only in `.env.local.ps1` (gitignored). Never write tokens into
|
||||||
|
Markdown, scripts, or `.git/config`.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
- "Push this project to Gitea" / "crea un repo en Gitea y sube esto"
|
||||||
|
- "List my Gitea repos" / "¿qué repos tengo en Gitea?"
|
||||||
|
- "Mirror this GitHub repo to Gitea" (`/repos/migrate`)
|
||||||
|
- "Set up a `gitea` remote alongside GitHub `origin`"
|
||||||
|
- "Create a release / tag on Gitea"
|
||||||
|
|
||||||
|
## When NOT to use
|
||||||
|
|
||||||
|
- Deploying an app to run on the server → use `deploy_skill/` (Coolify).
|
||||||
|
- Operating an already-deployed container → use `coolify_skill/`.
|
||||||
|
- Proxmox host / LXC maintenance → use `agent/`.
|
||||||
|
|
||||||
|
## Safety policy
|
||||||
|
|
||||||
|
- **Read-only by default**: prefer `Get-GiteaRepo.ps1` and GET endpoints before
|
||||||
|
any write.
|
||||||
|
- **Confirm before mutating**: repo creation, visibility changes, branch
|
||||||
|
protection, deletes, releases, and any `git push` require explicit
|
||||||
|
confirmation unless `-Force` is supplied.
|
||||||
|
- **Never write secrets into the repo.** The token is injected per-invocation
|
||||||
|
via `http.extraHeader` for pushes; it is NOT baked into remote URLs and NOT
|
||||||
|
stored in wincred by these scripts.
|
||||||
|
- **No force-push to main** unless the operator explicitly asks.
|
||||||
|
- **State the rollback path** before destructive actions (delete repo, force
|
||||||
|
push): once a Gitea repo is deleted the data is gone (no Coolify-side
|
||||||
|
recycle bin).
|
||||||
|
|
||||||
|
## Tooling
|
||||||
|
|
||||||
|
See [`TOOLS.md`](TOOLS.md) for the full command reference. Core scripts:
|
||||||
|
|
||||||
|
- `scripts/Invoke-GiteaApi.ps1` — low-level REST wrapper (curl-based, BOM-safe).
|
||||||
|
- `scripts/Test-GiteaConnection.ps1` — smoke test (version + auth + repos).
|
||||||
|
- `scripts/Get-GiteaRepo.ps1` — list / search / get a repo.
|
||||||
|
- `scripts/New-GiteaRepo.ps1` — idempotent repo creation.
|
||||||
|
- `scripts/Sync-GiteaRemote.ps1` — wire a `gitea` remote + push headlessly.
|
||||||
|
|
||||||
|
## API reference
|
||||||
|
|
||||||
|
See [`references/api-index.md`](references/api-index.md) for the verified
|
||||||
|
endpoint cheatsheet. Full Swagger at `$GITEA_URL/api/swagger`.
|
||||||
|
|
||||||
|
## Common workflows
|
||||||
|
|
||||||
|
### Push an existing local repo to Gitea (one shot)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
. .\.env.local.ps1
|
||||||
|
.\gitea_skill\scripts\Sync-GiteaRemote.ps1 `
|
||||||
|
-AppPath .\my-app `
|
||||||
|
-Name my-app `
|
||||||
|
-CreateIfMissing -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates `urieljareth/my-app` if missing, adds a `gitea` remote (so it
|
||||||
|
coexists with a GitHub `origin`), and pushes the current branch with a
|
||||||
|
one-shot auth header (token not persisted).
|
||||||
|
|
||||||
|
### Mirror a GitHub repo into Gitea
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
. .\.env.local.ps1
|
||||||
|
$body = @{
|
||||||
|
clone_addr = "https://github.com/urieljarethbusiness-cpu/my-app.git"
|
||||||
|
repo_owner = "urieljareth"
|
||||||
|
repo_name = "my-app"
|
||||||
|
service = "github"
|
||||||
|
mirror = $true
|
||||||
|
private = $false
|
||||||
|
wiki = $false
|
||||||
|
issues = $false
|
||||||
|
pull_requests = $false
|
||||||
|
releases = $true
|
||||||
|
} | ConvertTo-Json -Compress
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Method POST -Path "/repos/migrate" -BodyJson $body
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add a Gitea webhook so Coolify auto-pulls on push
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$body = @{
|
||||||
|
type = "gitea"
|
||||||
|
active = $true
|
||||||
|
events = @("push")
|
||||||
|
config = @{ url = "https://coolify.urieljareth.org/webhooks/source.manual_webhook_secret_gitea"; content_type = "json" }
|
||||||
|
} | ConvertTo-Json -Depth 5 -Compress
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Method POST `
|
||||||
|
-Path "/repos/urieljareth/my-app/hooks" -BodyJson $body
|
||||||
|
```
|
||||||
|
(Replace the `manual_webhook_secret_*` segment with the real webhook URL from
|
||||||
|
the Coolify app's "Webhooks" section.)
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
# Gitea — Tooling
|
||||||
|
|
||||||
|
All commands assume PowerShell from the project root. Load env first:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
. .\.env.local.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
Required env vars (in `.env.local.ps1`, gitignored):
|
||||||
|
|
||||||
|
- `GITEA_URL` — base URL, e.g. `https://gitea-hjwh0svsoo9p5w5kj2j6b1bd.urieljareth.org`.
|
||||||
|
- `GITEA_USER` — auth user (informational; `GITEA_TOKEN` encodes the identity).
|
||||||
|
- `GITEA_TOKEN` — app token with `read:repository`, `write:repository`,
|
||||||
|
`read:user` scopes (generate at `$GITEA_URL/user/settings/applications`).
|
||||||
|
|
||||||
|
## Smoke test
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\gitea_skill\scripts\Test-GiteaConnection.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
Calls `/version`, `/user`, `/settings/api`, `/repos/search`. Exits 1 on any
|
||||||
|
FAIL — gate operational work on this.
|
||||||
|
|
||||||
|
## Read-only
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Who am I?
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Path "/user"
|
||||||
|
|
||||||
|
# List visible repos
|
||||||
|
.\gitea_skill\scripts\Get-GiteaRepo.ps1 -List
|
||||||
|
|
||||||
|
# Get one repo (owner defaults to auth user)
|
||||||
|
.\gitea_skill\scripts\Get-GiteaRepo.ps1 -Name Proxmox-Coolify-Manager
|
||||||
|
.\gitea_skill\scripts\Get-GiteaRepo.ps1 -Owner urieljareth -Name MP-Manager
|
||||||
|
|
||||||
|
# Search
|
||||||
|
.\gitea_skill\scripts\Get-GiteaRepo.ps1 -Search manager
|
||||||
|
|
||||||
|
# Branches + tags
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Path "/repos/urieljareth/MP-Manager/branches"
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Path "/repos/urieljareth/MP-Manager/tags"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create a repo (idempotent)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\gitea_skill\scripts\New-GiteaRepo.ps1 -Name my-app -Description "demo" [-Private] [-NoAutoInit]
|
||||||
|
```
|
||||||
|
|
||||||
|
If the repo already exists, prints its URL and returns the existing object
|
||||||
|
(no failure). `-NoAutoInit` skips the Gitea-side README so you can push an
|
||||||
|
existing local history without conflicts.
|
||||||
|
|
||||||
|
## Push a local repo to Gitea (headless, token NOT persisted)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\gitea_skill\scripts\Sync-GiteaRemote.ps1 `
|
||||||
|
-AppPath .\my-app `
|
||||||
|
-Name my-app `
|
||||||
|
-RemoteName gitea `
|
||||||
|
-CreateIfMissing [-Private] [-Force]
|
||||||
|
```
|
||||||
|
|
||||||
|
What it does:
|
||||||
|
|
||||||
|
1. Ensures `urieljareth/<Name>` exists (creates if `-CreateIfMissing`).
|
||||||
|
2. Adds (or updates) the `gitea` remote with the **clean** HTTPS URL — **no
|
||||||
|
token in `.git/config`**.
|
||||||
|
3. Pushes the current branch with a **one-shot** `http.extraHeader=Authorization:
|
||||||
|
token <T>` and a blanked `credential.helper`. The token never reaches disk.
|
||||||
|
|
||||||
|
`-RemoteName gitea` (default) coexists with a GitHub `origin`. To replace an
|
||||||
|
existing `origin` that already points at Gitea (as in this manager repo),
|
||||||
|
pass `-RemoteName origin`.
|
||||||
|
|
||||||
|
### If you prefer wincred for interactive pushes (one-time)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cmdkey /generic:"git:$env:GITEA_URL" /user:"$env:GITEA_USER" /pass:"$env:GITEA_TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, plain `git push` works. The skill scripts do NOT require this —
|
||||||
|
they work headlessly out of the box.
|
||||||
|
|
||||||
|
## Low-level API
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# GET
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Path "/repos/urieljareth/MP-Manager/releases"
|
||||||
|
|
||||||
|
# POST with body
|
||||||
|
$body = @{ name = "v1.0.0"; tag_name = "v1.0.0" } | ConvertTo-Json -Compress
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Method POST `
|
||||||
|
-Path "/repos/urieljareth/MP-Manager/releases" -BodyJson $body
|
||||||
|
|
||||||
|
# Raw (status + headers + body string)
|
||||||
|
.\gitea_skill\scripts\Invoke-GiteaApi.ps1 -Path "/user" -Raw
|
||||||
|
```
|
||||||
|
|
||||||
|
`Invoke-GiteaApi.ps1` uses `curl.exe --data-binary @<tmpfile>` (UTF-8 no BOM)
|
||||||
|
so Gitea's JSON parser never trips on a BOM — same pattern as
|
||||||
|
`Invoke-GitHubApi.ps1`.
|
||||||
|
|
||||||
|
## Commands that always require confirmation
|
||||||
|
|
||||||
|
- Creating a repo (`New-GiteaRepo.ps1` warns if it already exists).
|
||||||
|
- Any `git push` (`Sync-GiteaRemote.ps1` asks unless `-Force`).
|
||||||
|
- Any Gitea `POST`/`PATCH`/`DELETE` (repo, release, hook, branch protection).
|
||||||
|
- Mirror/migrate operations.
|
||||||
|
- Deleting a repo (no Coolify-side recycle bin — data is gone).
|
||||||
|
|
||||||
|
`-Force` skips the interactive prompts. Use only after manually reviewing the plan.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Likely cause | Fix |
|
||||||
|
|---------|--------------|-----|
|
||||||
|
| `GITEA_TOKEN not set` | `.env.local.ps1` not loaded | `. .\.env.local.ps1` |
|
||||||
|
| `401`/`403` on writes | Token lacks `write:repository` | Regenerate token with the right scope at `$GITEA_URL/user/settings/applications` |
|
||||||
|
| `invalid character '\u00ef'` | Body sent with a BOM | Already handled by `Invoke-GiteaApi.ps1` (uses `[IO.File]::WriteAllText` no-BOM); do not switch to `Set-Content -Encoding utf8` |
|
||||||
|
| `git push` prompts for credentials | The one-shot header was overridden by a stored helper | The script blanks `credential.helper` for the push; if you configured wincred manually, that's fine too. If a different helper intercepts, run from a clean shell. |
|
||||||
|
| `404` creating repo under an org | Org does not exist or token lacks access | Create the org first at `$GITEA_URL/org/create` |
|
||||||
|
| Cloudflare 5xx on Gitea URL | Tunnel routing or the Gitea container is down | `coolify_skill/scripts/Get-CoolifyDockerStatus.ps1 -Filter gitea`; tunnel is dashboard-managed, see `docs/runbooks/cloudflare-tunnel.md` |
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Gitea API quick reference (v1.26.x)
|
||||||
|
|
||||||
|
Base URL: `$GITEA_URL/api/v1` (default `https://gitea-hjwh0svsoo9p5w5kj2j6b1bd.urieljareth.org/api/v1`).
|
||||||
|
Auth header: `Authorization: token <GITEA_TOKEN>` (also accepts `Bearer` on 1.16+).
|
||||||
|
Docs (Swagger): `$GITEA_URL/api/swagger` (browsable in a browser).
|
||||||
|
|
||||||
|
## Verified 2026-07-19 on this instance
|
||||||
|
|
||||||
|
| Endpoint | Method | Purpose |
|
||||||
|
|----------|--------|---------|
|
||||||
|
| `/version` | GET | Server version (anonymous). Returns `{"version":"1.26.2"}`. |
|
||||||
|
| `/user` | GET | Authenticated user. Proves token. |
|
||||||
|
| `/settings/api` | GET | Pagination/limits config. |
|
||||||
|
| `/repos/search?limit=50` | GET | All repos visible to the token. |
|
||||||
|
| `/repos/{owner}/{name}` | GET | Single repo (404 if missing or no access). |
|
||||||
|
| `/user/repos` | POST | Create repo under auth user. |
|
||||||
|
| `/orgs/{org}/repos` | POST | Create repo under an org. |
|
||||||
|
| `/repos/{owner}/{name}` | PATCH | Update description, visibility, default branch. |
|
||||||
|
| `/repos/{owner}/{name}` | DELETE | Delete repo. |
|
||||||
|
| `/repos/{owner}/{name}/branches` | GET | List branches. |
|
||||||
|
| `/repos/{owner}/{name}/branch protections` | POST | Branch protection rules. |
|
||||||
|
| `/repos/{owner}/{name}/releases` | GET/POST | List / create releases. |
|
||||||
|
| `/repos/{owner}/{name}/tags` | GET | List tags. |
|
||||||
|
| `/repos/{owner}/{name}/hooks` | GET/POST | Webhooks (e.g. Coolify pull). |
|
||||||
|
| `/repos/{owner}/{name}/mirror-sync` | POST | Sync a mirror repo. |
|
||||||
|
| `/repos/migrate` | POST | Migrate/mirror from GitHub/GitLab/etc. |
|
||||||
|
| `/users/{username}/tokens` | POST | Create a new app token (needs username+password or admin). |
|
||||||
|
| `/orgs` | GET | List orgs. |
|
||||||
|
| `/admin/users` | GET | List users (admin only). |
|
||||||
|
|
||||||
|
## Token scopes (Gitea 1.20+ fine-grained)
|
||||||
|
|
||||||
|
Tokens carry scopes. The current skill token (`GITEA_TOKEN` in `.env.local.ps1`)
|
||||||
|
needs at minimum: `read:repository`, `write:repository`, `read:user`. Create at
|
||||||
|
`$GITEA_URL/user/settings/applications` (or via API `/users/{user}/tokens` with
|
||||||
|
basic auth). Verify scopes with:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\gitea_skill\scripts\Test-GiteaConnection.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Auth header convention
|
||||||
|
|
||||||
|
Gitea's canonical header is `Authorization: token <T>` (note: literal `token`,
|
||||||
|
not `Bearer`). This differs from GitHub/Coolify. `Invoke-GiteaApi.ps1` uses the
|
||||||
|
canonical form. `Bearer` also works on 1.16+ but stick to `token` for max
|
||||||
|
compat.
|
||||||
|
|
||||||
|
## Git push with token (headless, no wincred)
|
||||||
|
|
||||||
|
The token is NOT persisted into `.git/config` by `Sync-GiteaRemote.ps1`. It is
|
||||||
|
sent via a one-shot `http.extraHeader`:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git -c "http.extraHeader=Authorization: token $env:GITEA_TOKEN" `
|
||||||
|
-c "credential.helper=" `
|
||||||
|
push -u gitea main
|
||||||
|
```
|
||||||
|
|
||||||
|
If you prefer wincred (so plain `git push` works afterwards), seed it once:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cmdkey /generic:"git:$env:GITEA_URL" /user:"$env:GITEA_USER" /pass:"$env:GITEA_TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
But note: the skill scripts do not require this. They work headlessly.
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Get or search a Gitea repository.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Three modes:
|
||||||
|
-Name <repo> -> GET /repos/{owner}/{name} (default owner = auth user)
|
||||||
|
-Search <term> -> GET /repos/search?q=<term>
|
||||||
|
-List -> GET /repos/search (all visible to the token)
|
||||||
|
|
||||||
|
.PARAMETER Owner
|
||||||
|
Owner (user or org). Defaults to the authenticated user.
|
||||||
|
|
||||||
|
.PARAMETER Name
|
||||||
|
Exact repository name to look up.
|
||||||
|
|
||||||
|
.PARAMETER Search
|
||||||
|
Free-text search term.
|
||||||
|
|
||||||
|
.PARAMETER List
|
||||||
|
Switch: list all visible repos.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Get-GiteaRepo.ps1 -Name Proxmox-Coolify-Manager
|
||||||
|
.\Get-GiteaRepo.ps1 -Search manager
|
||||||
|
.\Get-GiteaRepo.ps1 -List
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[string]$Owner,
|
||||||
|
[Parameter(ParameterSetName = "ByName")]
|
||||||
|
[string]$Name,
|
||||||
|
[Parameter(ParameterSetName = "BySearch")]
|
||||||
|
[string]$Search,
|
||||||
|
[Parameter(ParameterSetName = "ListAll")]
|
||||||
|
[switch]$List
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$api = Join-Path $here "Invoke-GiteaApi.ps1"
|
||||||
|
|
||||||
|
function Resolve-GiteaOwner {
|
||||||
|
param([string]$OwnerArg)
|
||||||
|
if ($OwnerArg) { return $OwnerArg }
|
||||||
|
$me = & $api -Path "/user"
|
||||||
|
return $me.login
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($PSCmdlet.ParameterSetName) {
|
||||||
|
"ByName" {
|
||||||
|
if (-not $Name) { throw "Provide -Name." }
|
||||||
|
$o = Resolve-GiteaOwner -OwnerArg $Owner
|
||||||
|
try {
|
||||||
|
$r = & $api -Path "/repos/$o/$Name"
|
||||||
|
[PSCustomObject]@{
|
||||||
|
full_name = $r.full_name
|
||||||
|
private = $r.private
|
||||||
|
default_branch = $r.default_branch
|
||||||
|
clone_url = $r.clone_url
|
||||||
|
html_url = $r.html_url
|
||||||
|
ssh_url = $r.ssh_url
|
||||||
|
updated_at = $r.updated_at
|
||||||
|
} | Format-List
|
||||||
|
} catch {
|
||||||
|
if ($_.Exception.Message -match "404") {
|
||||||
|
Write-Host "Repository not found: $o/$Name" -ForegroundColor Yellow
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"BySearch" {
|
||||||
|
$term = [uri]::EscapeDataString($Search)
|
||||||
|
$r = & $api -Path "/repos/search?q=$term&limit=50"
|
||||||
|
$r.data | Select-Object full_name, private, default_branch, updated_at | Format-Table -AutoSize
|
||||||
|
}
|
||||||
|
"ListAll" {
|
||||||
|
$r = & $api -Path "/repos/search?limit=50"
|
||||||
|
$r.data | Select-Object full_name, private, default_branch, updated_at | Format-Table -AutoSize
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Low-level Gitea REST API wrapper (curl-based, BOM-safe).
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Mirrors Invoke-GitHubApi.ps1's design: uses curl.exe so JSON bodies are
|
||||||
|
written via --data-binary @file (no PowerShell Set-Content BOM, which
|
||||||
|
Gitea rejects with "invalid character '\u00ef' looking for beginning of
|
||||||
|
value"). Reads GITEA_URL + GITEA_TOKEN from the environment.
|
||||||
|
|
||||||
|
.PARAMETER Method
|
||||||
|
HTTP verb. Default GET.
|
||||||
|
|
||||||
|
.PARAMETER Path
|
||||||
|
Path under /api/v1 (with or without leading slash). Query params allowed.
|
||||||
|
|
||||||
|
.PARAMETER BodyJson
|
||||||
|
JSON body for write verbs. Validated as JSON before sending.
|
||||||
|
|
||||||
|
.PARAMETER Raw
|
||||||
|
Return a pscustomobject with Status, Headers, Body instead of parsed JSON.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Invoke-GiteaApi.ps1 -Path "/version"
|
||||||
|
.\Invoke-GiteaApi.ps1 -Path "/user"
|
||||||
|
.\Invoke-GiteaApi.ps1 -Method POST -Path "/user/repos" -BodyJson $json
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[ValidateSet("GET", "POST", "PATCH", "PUT", "DELETE")]
|
||||||
|
[string]$Method = "GET",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Path,
|
||||||
|
|
||||||
|
[string]$BodyJson,
|
||||||
|
|
||||||
|
[switch]$Raw
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:GITEA_TOKEN)) {
|
||||||
|
throw "Set GITEA_TOKEN before calling the Gitea API. Store it in .env.local.ps1 (gitignored)."
|
||||||
|
}
|
||||||
|
if (-not (Get-Command curl.exe -ErrorAction SilentlyContinue)) {
|
||||||
|
throw "curl.exe not found in PATH."
|
||||||
|
}
|
||||||
|
|
||||||
|
$base = if ($env:GITEA_URL) { $env:GITEA_URL.TrimEnd("/") } else { "https://gitea-hjwh0svsoo9p5w5kj2j6b1bd.urieljareth.org" }
|
||||||
|
$base = "$base/api/v1"
|
||||||
|
$cleanPath = $Path.TrimStart("/")
|
||||||
|
$uri = "$base/$cleanPath"
|
||||||
|
|
||||||
|
# Gitea accepts "token <T>" (canonical) and "Bearer <T>" (since 1.16+).
|
||||||
|
$curlArgs = @(
|
||||||
|
"-sS", "-i",
|
||||||
|
"-X", $Method.ToUpper(),
|
||||||
|
"-H", "Authorization: token $env:GITEA_TOKEN",
|
||||||
|
"-H", "Accept: application/json",
|
||||||
|
"-H", "User-Agent: gitea-skill"
|
||||||
|
)
|
||||||
|
$tmpBody = $null
|
||||||
|
if ($PSBoundParameters.ContainsKey("BodyJson")) {
|
||||||
|
try { $null = $BodyJson | ConvertFrom-Json } catch { throw "BodyJson is not valid JSON: $($_.Exception.Message)" }
|
||||||
|
$tmpBody = [System.IO.Path]::GetTempFileName()
|
||||||
|
[System.IO.File]::WriteAllText($tmpBody, $BodyJson, (New-Object System.Text.UTF8Encoding($false)))
|
||||||
|
$curlArgs += @("--data-binary", "@$tmpBody", "-H", "Content-Type: application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$output = & curl.exe @curlArgs $uri
|
||||||
|
$exitCode = $LASTEXITCODE
|
||||||
|
if ($exitCode -ne 0) { throw "Gitea API request failed with exit code $exitCode." }
|
||||||
|
|
||||||
|
$rawStr = ($output | Out-String)
|
||||||
|
$parts = $rawStr -split "(?:`r`n`r`n|`n`n)", 2
|
||||||
|
$headersBlock = if ($parts.Count -ge 1) { $parts[0] } else { "" }
|
||||||
|
$bodyBlock = if ($parts.Count -ge 2) { $parts[1] } else { "{}" }
|
||||||
|
|
||||||
|
$statusLine = ($headersBlock -split "`n" | Select-Object -First 1).Trim()
|
||||||
|
if ($statusLine -notmatch " 20[04-9] ") {
|
||||||
|
$snippet = $bodyBlock.Trim()
|
||||||
|
if ($snippet.Length -gt 500) { $snippet = $snippet.Substring(0, 500) + "..." }
|
||||||
|
throw "Gitea API HTTP error: $statusLine`nURI: $uri`nBody: $snippet"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Raw) {
|
||||||
|
[pscustomobject]@{ Status = $statusLine; Headers = $headersBlock; Body = $bodyBlock }
|
||||||
|
} else {
|
||||||
|
try { $bodyBlock | ConvertFrom-Json }
|
||||||
|
catch { $bodyBlock }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($tmpBody -and (Test-Path -LiteralPath $tmpBody)) {
|
||||||
|
Remove-Item -LiteralPath $tmpBody -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Create a Gitea repository (idempotent).
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Mirrors deploy_skill/scripts/New-GitHubRepo.ps1. If the repo already exists,
|
||||||
|
prints its URL and returns the existing object instead of failing.
|
||||||
|
|
||||||
|
.PARAMETER Name
|
||||||
|
Repository name (required).
|
||||||
|
|
||||||
|
.PARAMETER Description
|
||||||
|
Short description.
|
||||||
|
|
||||||
|
.PARAMETER Private
|
||||||
|
Switch. If set, creates a private repo. Default public.
|
||||||
|
|
||||||
|
.PARAMETER Owner
|
||||||
|
Optional. User (default) or org to create under.
|
||||||
|
|
||||||
|
.PARAMETER NoAutoInit
|
||||||
|
Skip the Gitea-side README init (use when you will push an existing local
|
||||||
|
repo with its own history).
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\New-GiteaRepo.ps1 -Name my-app -Description "demo" -Private
|
||||||
|
.\New-GiteaRepo.ps1 -Name my-app -NoAutoInit
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[ValidatePattern('^[A-Za-z0-9._-]+$')]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
[string]$Description = "",
|
||||||
|
|
||||||
|
[switch]$Private,
|
||||||
|
|
||||||
|
[string]$Owner,
|
||||||
|
|
||||||
|
[switch]$NoAutoInit
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$api = Join-Path $here "Invoke-GiteaApi.ps1"
|
||||||
|
|
||||||
|
$me = & $api -Path "/user"
|
||||||
|
$effectiveOwner = if ($Owner) { $Owner } else { $me.login }
|
||||||
|
Write-Host "Authenticated Gitea user: $($me.login)" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$existing = $null
|
||||||
|
try {
|
||||||
|
$existing = & $api -Path "/repos/$effectiveOwner/$Name"
|
||||||
|
} catch {
|
||||||
|
if ($_.Exception.Message -notmatch "404") { throw }
|
||||||
|
$existing = $null
|
||||||
|
}
|
||||||
|
if ($existing) {
|
||||||
|
Write-Host "Repository already exists: $($existing.html_url)" -ForegroundColor Yellow
|
||||||
|
[PSCustomObject]@{
|
||||||
|
full_name = $existing.full_name
|
||||||
|
html_url = $existing.html_url
|
||||||
|
clone_url = $existing.clone_url
|
||||||
|
ssh_url = $existing.ssh_url
|
||||||
|
private = $existing.private
|
||||||
|
default_branch = $existing.default_branch
|
||||||
|
} | Format-List
|
||||||
|
return $existing
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = [ordered]@{
|
||||||
|
name = $Name
|
||||||
|
description = $Description
|
||||||
|
private = [bool]$Private
|
||||||
|
auto_init = (-not $NoAutoInit)
|
||||||
|
has_issues = $true
|
||||||
|
has_wiki = $false
|
||||||
|
default_branch = "main"
|
||||||
|
} | ConvertTo-Json -Compress
|
||||||
|
|
||||||
|
$endpoint = if ($Owner -and $Owner -ne $me.login) {
|
||||||
|
"/orgs/$Owner/repos"
|
||||||
|
} else {
|
||||||
|
"/user/repos"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Creating repository at: $endpoint" -ForegroundColor Cyan
|
||||||
|
$created = & $api -Method POST -Path $endpoint -BodyJson $body
|
||||||
|
|
||||||
|
[PSCustomObject]@{
|
||||||
|
full_name = $created.full_name
|
||||||
|
html_url = $created.html_url
|
||||||
|
clone_url = $created.clone_url
|
||||||
|
ssh_url = $created.ssh_url
|
||||||
|
private = $created.private
|
||||||
|
default_branch = $created.default_branch
|
||||||
|
} | Format-List
|
||||||
|
|
||||||
|
$created
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Wire a local git repo to Gitea and push the current branch headlessly.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
1. Ensures the target Gitea repo exists (creates it if -CreateIfMissing).
|
||||||
|
2. Adds (or updates) a git remote pointing at Gitea. Default remote name
|
||||||
|
is 'gitea' so it sits alongside a GitHub 'origin' without conflict.
|
||||||
|
3. Pushes the current branch with a one-shot Authorization header so the
|
||||||
|
Gitea token is NOT persisted into .git/config or wincred.
|
||||||
|
|
||||||
|
The token is injected via `git -c http.extraHeader=...` for the single
|
||||||
|
push invocation only. credential.helper is blanked for that push so the
|
||||||
|
helper does not prompt and does not cache the header.
|
||||||
|
|
||||||
|
.PARAMETER AppPath
|
||||||
|
Path to the local git working copy. Defaults to current directory.
|
||||||
|
|
||||||
|
.PARAMETER Name
|
||||||
|
Repository name on Gitea. Defaults to the basename of AppPath.
|
||||||
|
|
||||||
|
.PARAMETER Owner
|
||||||
|
Owner (user or org). Defaults to the authenticated Gitea user.
|
||||||
|
|
||||||
|
.PARAMETER RemoteName
|
||||||
|
Remote name to add/update. Default 'gitea'.
|
||||||
|
|
||||||
|
.PARAMETER Branch
|
||||||
|
Branch to push. Defaults to the current HEAD of AppPath.
|
||||||
|
|
||||||
|
.PARAMETER CreateIfMissing
|
||||||
|
Create the Gitea repo if it does not exist. If omitted and the repo is
|
||||||
|
missing, the script aborts with a clear message.
|
||||||
|
|
||||||
|
.PARAMETER Private
|
||||||
|
Only used when -CreateIfMissing creates a new repo.
|
||||||
|
|
||||||
|
.PARAMETER Force
|
||||||
|
Skip the interactive confirmation before pushing.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Sync-GiteaRemote.ps1 -AppPath .\my-app -CreateIfMissing
|
||||||
|
.\Sync-GiteaRemote.ps1 -Name Proxmox-Coolify-Manager -RemoteName origin -Force
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[string]$AppPath = (Get-Location).Path,
|
||||||
|
[string]$Name,
|
||||||
|
[string]$Owner,
|
||||||
|
[string]$RemoteName = "gitea",
|
||||||
|
[string]$Branch,
|
||||||
|
[switch]$CreateIfMissing,
|
||||||
|
[switch]$Private,
|
||||||
|
[switch]$Force
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$api = Join-Path $here "Invoke-GiteaApi.ps1"
|
||||||
|
$newRepo = Join-Path $here "New-GiteaRepo.ps1"
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:GITEA_TOKEN)) {
|
||||||
|
throw "Set GITEA_TOKEN (and GITEA_URL) before running this. Load .\.env.local.ps1."
|
||||||
|
}
|
||||||
|
|
||||||
|
$repoPath = (Resolve-Path -LiteralPath $AppPath).ProviderPath
|
||||||
|
if (-not (Test-Path -LiteralPath (Join-Path $repoPath ".git"))) {
|
||||||
|
throw "Not a git repository: $repoPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
$me = & $api -Path "/user"
|
||||||
|
$effectiveOwner = if ($Owner) { $Owner } else { $me.login }
|
||||||
|
$effectiveName = if ($Name) { $Name } else { (Split-Path -Leaf $repoPath) }
|
||||||
|
|
||||||
|
if (-not $Branch) {
|
||||||
|
$Branch = (& git -C $repoPath rev-parse --abbrev-ref HEAD).Trim()
|
||||||
|
}
|
||||||
|
if ([string]::IsNullOrWhiteSpace($Branch) -or $Branch -eq "HEAD") {
|
||||||
|
throw "Could not determine current branch in $repoPath. Pass -Branch explicitly."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[*] repo=$effectiveOwner/$effectiveName branch=$Branch remote=$RemoteName" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$repo = $null
|
||||||
|
try {
|
||||||
|
$repo = & $api -Path "/repos/$effectiveOwner/$effectiveName"
|
||||||
|
} catch {
|
||||||
|
if ($_.Exception.Message -notmatch "404") { throw }
|
||||||
|
$repo = $null
|
||||||
|
}
|
||||||
|
if (-not $repo) {
|
||||||
|
if (-not $CreateIfMissing) {
|
||||||
|
throw "Gitea repo $effectiveOwner/$effectiveName does not exist. Re-run with -CreateIfMissing, or create it first with New-GiteaRepo.ps1."
|
||||||
|
}
|
||||||
|
Write-Host "[*] Creating missing repo $effectiveOwner/$effectiveName ..." -ForegroundColor Cyan
|
||||||
|
& $newRepo -Name $effectiveName -Owner $effectiveOwner -Private:$Private -NoAutoInit | Out-Null
|
||||||
|
$repo = & $api -Path "/repos/$effectiveOwner/$effectiveName"
|
||||||
|
}
|
||||||
|
|
||||||
|
$giteaHost = if ($env:GITEA_URL) { $env:GITEA_URL.TrimEnd("/") } else { "https://gitea-hjwh0svsoo9p5w5kj2j6b1bd.urieljareth.org" }
|
||||||
|
$remoteUrl = "$giteaHost/$effectiveOwner/$effectiveName.git"
|
||||||
|
|
||||||
|
$existingRemote = $null
|
||||||
|
try {
|
||||||
|
$existingRemote = & git -C $repoPath remote get-url $RemoteName 2>$null
|
||||||
|
} catch { $existingRemote = $null }
|
||||||
|
|
||||||
|
if ($existingRemote) {
|
||||||
|
if ($existingRemote.Trim() -ne $remoteUrl) {
|
||||||
|
Write-Host "[*] Updating remote '$RemoteName' URL -> $remoteUrl" -ForegroundColor Yellow
|
||||||
|
& git -C $repoPath remote set-url $RemoteName $remoteUrl
|
||||||
|
} else {
|
||||||
|
Write-Host "[*] Remote '$RemoteName' already -> $remoteUrl" -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "[*] Adding remote '$RemoteName' -> $remoteUrl" -ForegroundColor Cyan
|
||||||
|
& git -C $repoPath remote add $RemoteName $remoteUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $Force) {
|
||||||
|
$answer = Read-Host "Push '$Branch' to $RemoteName ($effectiveOwner/$effectiveName)? [y/N]"
|
||||||
|
if ($answer -notmatch '^[yY]') {
|
||||||
|
Write-Host "Aborted (no push)." -ForegroundColor Yellow
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[*] Pushing $Branch -> $RemoteName ..." -ForegroundColor Cyan
|
||||||
|
$env:GIT_TERMINAL_PROMPT = "0"
|
||||||
|
& git -C $repoPath `
|
||||||
|
-c "http.extraHeader=Authorization: token $env:GITEA_TOKEN" `
|
||||||
|
-c "credential.helper=" `
|
||||||
|
push -u $RemoteName $Branch
|
||||||
|
$pushExit = $LASTEXITCODE
|
||||||
|
if ($pushExit -ne 0) {
|
||||||
|
throw "git push failed (exit $pushExit). Check the token scopes (needs 'write:repository')."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[OK] Pushed to $($repo.html_url)" -ForegroundColor Green
|
||||||
|
Write-Host " remote '$RemoteName' -> $remoteUrl"
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Gitea smoke test: config + API auth + token scopes.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Calls /version, /user (auth), /settings/api, and lists the authenticated
|
||||||
|
user's repos. Exits 1 on any FAIL so it composes into CI / agent startup.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\Test-GiteaConnection.ps1
|
||||||
|
#>
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$api = Join-Path $here "Invoke-GiteaApi.ps1"
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:GITEA_TOKEN)) {
|
||||||
|
Write-Host "[FAIL] GITEA_TOKEN not set. Load .\.env.local.ps1 first." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if ([string]::IsNullOrWhiteSpace($env:GITEA_URL)) {
|
||||||
|
Write-Host "[WARN] GITEA_URL not set; using default https://gitea-hjwh0svsoo9p5w5kj2j6b1bd.urieljareth.org" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
$failures = 0
|
||||||
|
|
||||||
|
# 1) Version (anonymous, but proves reachability)
|
||||||
|
try {
|
||||||
|
$v = & $api -Path "/version"
|
||||||
|
Write-Host "[PASS] /version -> Gitea $($v.version)" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[FAIL] /version: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
$failures++
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2) Authenticated user (proves token validity)
|
||||||
|
try {
|
||||||
|
$u = & $api -Path "/user"
|
||||||
|
Write-Host "[PASS] /user -> login='$($u.login)' is_admin=$($u.is_admin)" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[FAIL] /user (token invalid or expired): $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
$failures++
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3) Token scopes (Gitea 1.20+: /users/me/tokens only lists tokens created via API; for app tokens, /user suffices)
|
||||||
|
try {
|
||||||
|
$settings = & $api -Path "/settings/api"
|
||||||
|
Write-Host "[PASS] /settings/api -> max_response_items=$($settings.max_response_items)" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[WARN] /settings/api unavailable on this version: $($_.Exception.Message)" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4) List repos (proves read scope on /repos/search)
|
||||||
|
try {
|
||||||
|
$repos = & $api -Path "/repos/search?limit=50"
|
||||||
|
$count = @($repos.data).Count
|
||||||
|
Write-Host "[PASS] /repos/search -> $count repos visible" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[FAIL] /repos/search: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
$failures++
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
if ($failures -gt 0) {
|
||||||
|
Write-Host "[RESULT] $failures FAIL(s). Gitea skill NOT ready." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host "[RESULT] OK. Gitea skill ready." -ForegroundColor Green
|
||||||
Reference in New Issue
Block a user