Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72fcf2f74f |
34
.gitea/workflows/validate.yml
Normal file
34
.gitea/workflows/validate.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Validate Actions
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Valida YAML
|
||||||
|
run: |
|
||||||
|
for f in */action.yml; do
|
||||||
|
echo "Validazione: $f"
|
||||||
|
python3 -c "import yaml; yaml.safe_load(open('$f')); print('OK')"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Verifica struttura action
|
||||||
|
run: |
|
||||||
|
required_fields=("name" "runs")
|
||||||
|
for f in */action.yml; do
|
||||||
|
for field in "${required_fields[@]}"; do
|
||||||
|
if ! grep -q "^$field:" "$f"; then
|
||||||
|
echo "ERRORE: $f manca del campo '$field'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
echo "Struttura azioni valida"
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.idea/
|
||||||
65
README.md
65
README.md
@@ -0,0 +1,65 @@
|
|||||||
|
# Gitea Actions
|
||||||
|
|
||||||
|
Repository contenente action [Gitea Actions](https://docs.gitea.com/usage/actions/overview) centralizzate
|
||||||
|
utilizzabili da altri repository del workspace.
|
||||||
|
|
||||||
|
## Action disponibili
|
||||||
|
|
||||||
|
### `version-from-tag`
|
||||||
|
|
||||||
|
Estrae la versione da un tag (formato `v1.2.3.4[-suffix]`) e produce le variabili
|
||||||
|
`appver`, `fullver`, `suffix` e `version`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Calcola versione
|
||||||
|
uses: https://<host>/<owner>/Actions/version-from-tag@<ref>
|
||||||
|
with:
|
||||||
|
ref-name: ${{ github.ref_name }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `publish-dotnet`
|
||||||
|
|
||||||
|
Compila (restore + publish) un progetto .NET e sincronizza l'output su un path
|
||||||
|
locale via `rsync`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Publish
|
||||||
|
uses: https://<host>/<owner>/Actions/publish-dotnet@<ref>
|
||||||
|
with:
|
||||||
|
project: src/MyApp/MyApp.csproj
|
||||||
|
output-path: /var/publish/myapp
|
||||||
|
version: ${{ steps.versione.outputs.appver }}
|
||||||
|
# opzionali:
|
||||||
|
configuration: Release
|
||||||
|
subpath: "wwwroot"
|
||||||
|
exclude-dirs: store
|
||||||
|
exclude-files: appsettings.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### `deploy-iis`
|
||||||
|
|
||||||
|
Esegue il deploy su IIS: ferma sito/application pool, copia i file via `robocopy`,
|
||||||
|
riavvia i servizi.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Deploy IIS
|
||||||
|
uses: https://<host>/<owner>/Actions/deploy-iis@<ref>
|
||||||
|
with:
|
||||||
|
source-path: /var/publish/myapp
|
||||||
|
destination-path: C:\inetpub\wwwroot\myapp
|
||||||
|
site-name: MySite
|
||||||
|
app-pool-name: MyAppPool
|
||||||
|
exclude-dirs: store
|
||||||
|
exclude-files: appsettings.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Versionamento delle action
|
||||||
|
|
||||||
|
Per puntare a una versione stabile, crea un tag su questo repository
|
||||||
|
(es. `v1.0.0`) e usalo nel riferimento:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
uses: https://<host>/<owner>/Actions/publish-dotnet@v1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Oppure punta a un branch (`@main`) per avere sempre l'ultima versione.
|
||||||
|
|||||||
128
deploy-iis/action.yml
Normal file
128
deploy-iis/action.yml
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
name: Deploy IIS
|
||||||
|
description: Ferma sito e application pool IIS, sincronizza i file pubblicati e riavvia i servizi.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
source-path:
|
||||||
|
description: Cartella sorgente da distribuire su IIS.
|
||||||
|
required: true
|
||||||
|
destination-path:
|
||||||
|
description: Cartella di destinazione sul server IIS.
|
||||||
|
required: true
|
||||||
|
site-name:
|
||||||
|
description: Nome del sito IIS da fermare e riavviare.
|
||||||
|
required: true
|
||||||
|
app-pool-name:
|
||||||
|
description: Nome dell'application pool IIS da fermare e riavviare.
|
||||||
|
required: true
|
||||||
|
exclude-dirs:
|
||||||
|
description: Elenco di directory da escludere dal mirroring (separate da virgola, punto e virgola o newline).
|
||||||
|
required: false
|
||||||
|
default: store
|
||||||
|
exclude-files:
|
||||||
|
description: Elenco di file da escludere dal mirroring (separati da virgola, punto e virgola o newline).
|
||||||
|
required: false
|
||||||
|
default: appsettings.json
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Deploy su IIS
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$appcmd = Join-Path $env:SystemRoot 'System32\inetsrv\appcmd.exe'
|
||||||
|
$src = '${{ inputs.source-path }}'
|
||||||
|
$dst = '${{ inputs.destination-path }}'
|
||||||
|
$site = '${{ inputs.site-name }}'
|
||||||
|
$appPool = '${{ inputs.app-pool-name }}'
|
||||||
|
|
||||||
|
function Split-InputList {
|
||||||
|
param([string]$Value)
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($Value)) {
|
||||||
|
return @()
|
||||||
|
}
|
||||||
|
|
||||||
|
return @(
|
||||||
|
$Value -split '[,;\r\n]+'
|
||||||
|
| ForEach-Object { $_.Trim() }
|
||||||
|
| Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $appcmd)) {
|
||||||
|
throw "appcmd.exe non trovato: $appcmd"
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($entry in @{
|
||||||
|
'source-path' = $src
|
||||||
|
'destination-path' = $dst
|
||||||
|
'site-name' = $site
|
||||||
|
'app-pool-name' = $appPool
|
||||||
|
}.GetEnumerator()) {
|
||||||
|
if ([string]::IsNullOrWhiteSpace($entry.Value)) {
|
||||||
|
throw "Input '$($entry.Key)' mancante."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $src)) {
|
||||||
|
throw "Cartella sorgente non trovata: $src"
|
||||||
|
}
|
||||||
|
|
||||||
|
$excludeDirs = Split-InputList '${{ inputs.exclude-dirs }}'
|
||||||
|
$excludeFiles = Split-InputList '${{ inputs.exclude-files }}'
|
||||||
|
|
||||||
|
$robocopyArgs = @(
|
||||||
|
$src
|
||||||
|
$dst
|
||||||
|
'/MIR'
|
||||||
|
'/R:2'
|
||||||
|
'/W:1'
|
||||||
|
'/NFL'
|
||||||
|
'/NDL'
|
||||||
|
'/NP'
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($excludeDirs.Count -gt 0) {
|
||||||
|
$robocopyArgs += '/XD'
|
||||||
|
$robocopyArgs += $excludeDirs
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($excludeFiles.Count -gt 0) {
|
||||||
|
$robocopyArgs += '/XF'
|
||||||
|
$robocopyArgs += $excludeFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
$siteStopped = $false
|
||||||
|
$poolStopped = $false
|
||||||
|
|
||||||
|
try {
|
||||||
|
& $appcmd stop site "/site.name:$site"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "stop site fallito ($LASTEXITCODE)" }
|
||||||
|
$siteStopped = $true
|
||||||
|
|
||||||
|
& $appcmd stop apppool "/apppool.name:$appPool"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "stop apppool fallito ($LASTEXITCODE)" }
|
||||||
|
$poolStopped = $true
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path $dst | Out-Null
|
||||||
|
|
||||||
|
& robocopy @robocopyArgs
|
||||||
|
$robocopyExitCode = $LASTEXITCODE
|
||||||
|
if ($robocopyExitCode -ge 8) {
|
||||||
|
throw "robocopy fallito ($robocopyExitCode)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($poolStopped) {
|
||||||
|
& $appcmd start apppool "/apppool.name:$appPool"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "start apppool fallito ($LASTEXITCODE)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($siteStopped) {
|
||||||
|
& $appcmd start site "/site.name:$site"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "start site fallito ($LASTEXITCODE)" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
32
example-workflow.yml
Normal file
32
example-workflow.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Build and Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: [v*]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Estrai versione dal tag
|
||||||
|
id: ver
|
||||||
|
uses: https://<host>/<owner>/Actions/version-from-tag@v1.2.3
|
||||||
|
with:
|
||||||
|
ref-name: ${{ github.ref_name }}
|
||||||
|
|
||||||
|
- name: Publish .NET
|
||||||
|
uses: https://<host>/<owner>/Actions/publish-dotnet@v1.2.3
|
||||||
|
with:
|
||||||
|
project: src/MyApp/MyApp.csproj
|
||||||
|
output-path: publish
|
||||||
|
version: ${{ steps.ver.outputs.appver }}
|
||||||
|
|
||||||
|
- name: Deploy IIS
|
||||||
|
uses: https://<host>/<owner>/Actions/deploy-iis@v1.2.3
|
||||||
|
with:
|
||||||
|
source-path: publish
|
||||||
|
destination-path: C:\inetpub\wwwroot\myapp
|
||||||
|
site-name: MySite
|
||||||
|
app-pool-name: MyAppPool
|
||||||
79
publish-dotnet/action.yml
Normal file
79
publish-dotnet/action.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
name: Publish .NET
|
||||||
|
description: >
|
||||||
|
Esegue restore, publish e rsync per un progetto .NET.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
project:
|
||||||
|
description: Path al file .csproj del progetto.
|
||||||
|
required: true
|
||||||
|
output-path:
|
||||||
|
description: Directory di destinazione per i file pubblicati.
|
||||||
|
required: true
|
||||||
|
version:
|
||||||
|
description: Versione applicativa da applicare (es. 1.0.0.0).
|
||||||
|
required: true
|
||||||
|
subpath:
|
||||||
|
description: >
|
||||||
|
Sottopath relativo alla cartella di publish da copiare
|
||||||
|
(es. "wwwroot" per Blazor WASM). Vuoto per copiare tutto.
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
configuration:
|
||||||
|
description: Configurazione di build (es. Release, Debug).
|
||||||
|
required: false
|
||||||
|
default: Release
|
||||||
|
exclude-dirs:
|
||||||
|
description: Directory da escludere dal rsync (separate da virgola, punto e virgola o newline).
|
||||||
|
required: false
|
||||||
|
default: store
|
||||||
|
exclude-files:
|
||||||
|
description: File da escludere dal rsync (separati da virgola, punto e virgola o newline).
|
||||||
|
required: false
|
||||||
|
default: appsettings.json
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Restore
|
||||||
|
shell: bash
|
||||||
|
run: dotnet restore "${{ inputs.project }}"
|
||||||
|
|
||||||
|
- name: Publish e rsync
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
split_input_list() {
|
||||||
|
local value="$1"
|
||||||
|
if [ -z "$value" ]; then
|
||||||
|
echo ""
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "$value" | tr ',;\r\n' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep -v '^$'
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpdir=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$tmpdir"' EXIT
|
||||||
|
|
||||||
|
dotnet publish "${{ inputs.project }}" \
|
||||||
|
-c "${{ inputs.configuration }}" \
|
||||||
|
-p:Version="${{ inputs.version }}" \
|
||||||
|
-o "$tmpdir"
|
||||||
|
|
||||||
|
src="$tmpdir"
|
||||||
|
if [ -n "${{ inputs.subpath }}" ]; then
|
||||||
|
src="$tmpdir/${{ inputs.subpath }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${{ inputs.output-path }}"
|
||||||
|
|
||||||
|
exclude_args=()
|
||||||
|
while IFS= read -r dir; do
|
||||||
|
[ -n "$dir" ] && exclude_args+=(--exclude="$dir")
|
||||||
|
done < <(split_input_list "${{ inputs.exclude-dirs }}")
|
||||||
|
|
||||||
|
while IFS= read -r file; do
|
||||||
|
[ -n "$file" ] && exclude_args+=(--exclude="$file")
|
||||||
|
done < <(split_input_list "${{ inputs.exclude-files }}")
|
||||||
|
|
||||||
|
rsync -a --delete "${exclude_args[@]}" "$src/" "${{ inputs.output-path }}/"
|
||||||
55
version-from-tag/action.yml
Normal file
55
version-from-tag/action.yml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
name: Versione da tag
|
||||||
|
description: Valida il tag in compilazione e popola le variabili di versionamento.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
ref-name:
|
||||||
|
description: Nome del tag o ref da elaborare.
|
||||||
|
required: true
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
appver:
|
||||||
|
description: Versione applicativa in formato n.n.n.n.
|
||||||
|
value: ${{ steps.versione.outputs.appver }}
|
||||||
|
fullver:
|
||||||
|
description: Versione completa, comprensiva di eventuale suffisso.
|
||||||
|
value: ${{ steps.versione.outputs.fullver }}
|
||||||
|
suffix:
|
||||||
|
description: Suffisso estratto dal tag, comprensivo del trattino iniziale quando presente.
|
||||||
|
value: ${{ steps.versione.outputs.suffix }}
|
||||||
|
version:
|
||||||
|
description: Versione normalizzata senza punti, mantenendo l'eventuale suffisso con trattino.
|
||||||
|
value: ${{ steps.versione.outputs.version }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Calcola versione da tag
|
||||||
|
id: versione
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ref_name='${{ inputs.ref-name }}'
|
||||||
|
|
||||||
|
if [[ -z "$ref_name" ]]; then
|
||||||
|
echo "Input 'ref-name' mancante."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
tag="${ref_name#v}"
|
||||||
|
|
||||||
|
if [[ "$tag" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(-.+)?$ ]]; then
|
||||||
|
appver="${BASH_REMATCH[1]}"
|
||||||
|
suffix="${BASH_REMATCH[2]:-}"
|
||||||
|
else
|
||||||
|
echo "Formato tag non valido: '$ref_name'. Atteso: v1.0.0.0 oppure v1.0.0.0-suffisso"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
fullver="$appver$suffix"
|
||||||
|
version="${appver//./}$suffix"
|
||||||
|
|
||||||
|
echo "appver=$appver" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "fullver=$fullver" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "suffix=$suffix" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||||
Reference in New Issue
Block a user