@moon791017/neo-skills 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GEMINI.md +115 -0
- package/README.md +155 -0
- package/bin/install-claude-skills.js +72 -0
- package/commands/neo/cd-app-service.toml +20 -0
- package/commands/neo/cd-iis.toml +20 -0
- package/commands/neo/ci-dotnet.toml +21 -0
- package/commands/neo/clarification.toml +48 -0
- package/commands/neo/code-review.toml +33 -0
- package/commands/neo/dotnet-gen-interface.toml +31 -0
- package/commands/neo/explain.toml +44 -0
- package/commands/neo/git-commit.toml +49 -0
- package/dist/hooks/secret-guard.js +2 -0
- package/dist/server.js +220 -0
- package/gemini-extension.json +15 -0
- package/package.json +39 -0
- package/skills/azure-pipelines/SKILL.md +45 -0
- package/skills/azure-pipelines/templates/build/build-dotnet.yml +92 -0
- package/skills/azure-pipelines/templates/deploy/deploy-app-service.yml +71 -0
- package/skills/azure-pipelines/templates/deploy/deploy-iis.yml +189 -0
- package/skills/azure-pipelines/templates/util/clean-artifact.yml +40 -0
- package/skills/azure-pipelines/templates/util/extract-artifact.yml +57 -0
- package/skills/azure-pipelines/templates/util/iis/iis-backup.yml +92 -0
- package/skills/azure-pipelines/templates/util/iis/iis-deploy-files.yml +112 -0
- package/skills/azure-pipelines/templates/util/iis/iis-manage-website.yml +112 -0
- package/skills/azure-pipelines/templates/util/iis/iis-rollback.yml +98 -0
- package/skills/azure-pipelines/templates/util/iis/iis-start-website.yml +89 -0
- package/skills/azure-pipelines/templates/util/iis/iis-stop-website.yml +80 -0
- package/skills/azure-pipelines/templates/util/iis/iis-task.yml +157 -0
- package/skills/azure-pipelines/templates/util/set-aspnetcore-env.yml +77 -0
- package/skills/clarification/SKILL.md +22 -0
- package/skills/code-review/SKILL.md +72 -0
- package/skills/csharp/SKILL.md +87 -0
- package/skills/csharp/reference/anti-patterns.md +142 -0
- package/skills/csharp/reference/coding-style.md +86 -0
- package/skills/csharp/reference/patterns.md +142 -0
- package/skills/csharp-interface-generator/SKILL.md +40 -0
- package/skills/dotnet/SKILL.md +41 -0
- package/skills/dotnet-ef-core/SKILL.md +78 -0
- package/skills/dotnet-ef-core/reference/anti-patterns.md +51 -0
- package/skills/dotnet-ef-core/reference/coding-style.md +42 -0
- package/skills/dotnet-ef-core/reference/patterns.md +53 -0
- package/skills/dotnet-minimal-apis/SKILL.md +78 -0
- package/skills/dotnet-minimal-apis/reference/anti-patterns.md +59 -0
- package/skills/dotnet-minimal-apis/reference/coding-style.md +54 -0
- package/skills/dotnet-minimal-apis/reference/patterns.md +68 -0
- package/skills/dotnet-mvc/SKILL.md +78 -0
- package/skills/dotnet-mvc/reference/anti-patterns.md +49 -0
- package/skills/dotnet-mvc/reference/coding-style.md +43 -0
- package/skills/dotnet-mvc/reference/patterns.md +56 -0
- package/skills/dotnet-webapi/SKILL.md +78 -0
- package/skills/dotnet-webapi/reference/anti-patterns.md +48 -0
- package/skills/dotnet-webapi/reference/coding-style.md +47 -0
- package/skills/dotnet-webapi/reference/patterns.md +52 -0
- package/skills/explain/SKILL.md +27 -0
- package/skills/git-commit/SKILL.md +84 -0
- package/skills/python/SKILL.md +61 -0
- package/skills/python/reference/anti-patterns.md +177 -0
- package/skills/python/reference/coding-style.md +92 -0
- package/skills/python/reference/patterns.md +112 -0
- package/skills/python-manager/SKILL.md +61 -0
- package/skills/start-plan/SKILL.md +29 -0
- package/skills/swift/SKILL.md +78 -0
- package/skills/swift/reference/anti-patterns.md +75 -0
- package/skills/swift/reference/coding-style.md +56 -0
- package/skills/swift/reference/patterns.md +94 -0
- package/skills/swift-ui/SKILL.md +76 -0
- package/skills/swift-ui/reference/anti-patterns.md +52 -0
- package/skills/swift-ui/reference/coding-style.md +46 -0
- package/skills/swift-ui/reference/patterns.md +87 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Template Name: Deploy Files (iis-deploy-files-robocopy.yml)
|
|
3
|
+
# Description:
|
|
4
|
+
# Copy files from source path to destination path.
|
|
5
|
+
# Supports "Clean Mode": Empty destination folder before copying.
|
|
6
|
+
# Uses PowerShell Copy-Item instead of Robocopy to simplify logic and ensure clean deployment.
|
|
7
|
+
# ==============================================================================
|
|
8
|
+
|
|
9
|
+
parameters:
|
|
10
|
+
# Source Path (e.g. Artifact download path)
|
|
11
|
+
- name: sourcePath
|
|
12
|
+
type: string
|
|
13
|
+
|
|
14
|
+
# Destination Path (e.g. Website physical path)
|
|
15
|
+
- name: destinationPath
|
|
16
|
+
type: string
|
|
17
|
+
|
|
18
|
+
# Whether to empty destination folder first
|
|
19
|
+
- name: cleanDestination
|
|
20
|
+
type: boolean
|
|
21
|
+
default: true
|
|
22
|
+
|
|
23
|
+
# Execution Condition
|
|
24
|
+
- name: runCondition
|
|
25
|
+
type: string
|
|
26
|
+
default: 'succeeded()'
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
# ----------------------------------------------------------------------------
|
|
30
|
+
# Execute PowerShell script for file deployment
|
|
31
|
+
# ----------------------------------------------------------------------------
|
|
32
|
+
- powershell: |
|
|
33
|
+
$sourceRaw = "${{ parameters.sourcePath }}"
|
|
34
|
+
$destRaw = "${{ parameters.destinationPath }}"
|
|
35
|
+
$clean = "${{ parameters.cleanDestination }}"
|
|
36
|
+
|
|
37
|
+
# Expand Environment Variables (e.g. convert %SystemDrive% to C:)
|
|
38
|
+
$source = [System.Environment]::ExpandEnvironmentVariables($sourceRaw)
|
|
39
|
+
$dest = [System.Environment]::ExpandEnvironmentVariables($destRaw)
|
|
40
|
+
|
|
41
|
+
# 1. Path Normalization (Remove trailing slash)
|
|
42
|
+
if ($source.EndsWith("\") -or $source.EndsWith("/")) { $source = $source.Substring(0, $source.Length - 1) }
|
|
43
|
+
if ($dest.EndsWith("\") -or $dest.EndsWith("/")) { $dest = $dest.Substring(0, $dest.Length - 1) }
|
|
44
|
+
|
|
45
|
+
Write-Host "Preparing to deploy files"
|
|
46
|
+
Write-Host "Source Path (Raw): $sourceRaw"
|
|
47
|
+
Write-Host "Source Path (Expanded): $source"
|
|
48
|
+
Write-Host "Destination Path (Raw): $destRaw"
|
|
49
|
+
Write-Host "Destination Path (Expanded): $dest"
|
|
50
|
+
|
|
51
|
+
if (-not (Test-Path $source)) {
|
|
52
|
+
Write-Error "Source path does not exist: $source"
|
|
53
|
+
exit 1
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# 2. Handle Destination Directory (Create or Clean)
|
|
57
|
+
if (Test-Path $dest) {
|
|
58
|
+
if ($clean -eq 'true') {
|
|
59
|
+
Write-Host "Mode: Clean Destination"
|
|
60
|
+
Write-Host "Removing entire destination directory: $dest"
|
|
61
|
+
try {
|
|
62
|
+
# Force remove entire directory
|
|
63
|
+
Remove-Item -Path $dest -Recurse -Force -ErrorAction Stop
|
|
64
|
+
Write-Host "Directory removed."
|
|
65
|
+
} catch {
|
|
66
|
+
Write-Warning "Error removing directory (files might be locked): $_ "
|
|
67
|
+
# If remove fails, try clearing contents only as fallback
|
|
68
|
+
Get-ChildItem -Path $dest -Recurse | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
Write-Host "Mode: Overwrite/Merge (Keep existing files)"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# Ensure destination directory exists (Create if deleted or not exists)
|
|
76
|
+
if (-not (Test-Path $dest)) {
|
|
77
|
+
Write-Host "Destination directory does not exist, creating: $dest"
|
|
78
|
+
New-Item -ItemType Directory -Force -Path $dest | Out-Null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# 3. Execute Copy
|
|
82
|
+
Write-Host "Copying files..."
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
# Debug Info: Check Source
|
|
86
|
+
# Use Recurse to list all levels, check for nested directory issues
|
|
87
|
+
$items = Get-ChildItem -Path $source -Recurse
|
|
88
|
+
Write-Host "Source directory ($source) contains $($items.Count) items (subdirs/files)."
|
|
89
|
+
if ($items.Count -gt 0) {
|
|
90
|
+
Write-Host "File list preview (first 10):"
|
|
91
|
+
$items | Select-Object -First 10 | ForEach-Object { Write-Host " - $($_.FullName)" }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Debug Info: Check Destination
|
|
95
|
+
Write-Host "Destination Path: $dest"
|
|
96
|
+
Write-Host "Destination Exists: $(Test-Path $dest)"
|
|
97
|
+
|
|
98
|
+
if ($items.Count -gt 0) {
|
|
99
|
+
# Use basic Copy-Item syntax, add verbose log
|
|
100
|
+
# Note: If source contains subdirectories, -Recurse must be added
|
|
101
|
+
# Ensure Destination points to a folder path
|
|
102
|
+
Copy-Item -Path "$source\*" -Destination $dest -Recurse -Force -ErrorAction Stop -Verbose
|
|
103
|
+
Write-Host "File deployment successful."
|
|
104
|
+
} else {
|
|
105
|
+
Write-Warning "Source directory is empty, no files copied."
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
Write-Error "File copy failed: $_ "
|
|
109
|
+
exit 1
|
|
110
|
+
}
|
|
111
|
+
displayName: 'Deploy Application Files (Copy-Item)'
|
|
112
|
+
condition: ${{ parameters.runCondition }}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Template Name: Manage IIS Website (iis-manage-website.yml)
|
|
3
|
+
# Description:
|
|
4
|
+
# This template is responsible for creating or updating IIS Website, Application Pool (AppPool), and setting Site Bindings.
|
|
5
|
+
# Implemented using PowerShell (WebAdministration module) to ensure settings meet expectations.
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
|
|
8
|
+
parameters:
|
|
9
|
+
# Website Name (Display name in IIS)
|
|
10
|
+
- name: websiteName
|
|
11
|
+
type: string
|
|
12
|
+
|
|
13
|
+
# Website Physical Path (Absolute path on Server)
|
|
14
|
+
- name: websitePhysicalPath
|
|
15
|
+
type: string
|
|
16
|
+
|
|
17
|
+
# Application Pool Name
|
|
18
|
+
- name: appPoolName
|
|
19
|
+
type: string
|
|
20
|
+
|
|
21
|
+
# AppPool .NET CLR Version (For .NET Core/5+, 'No Managed Code' is recommended)
|
|
22
|
+
- name: appPoolDotNetVersion
|
|
23
|
+
type: string
|
|
24
|
+
default: 'No Managed Code'
|
|
25
|
+
values:
|
|
26
|
+
- 'No Managed Code'
|
|
27
|
+
- 'v4.0'
|
|
28
|
+
- 'v2.0'
|
|
29
|
+
|
|
30
|
+
# Site Binding Settings (JSON List)
|
|
31
|
+
# Format example: '[{"type":"http","port":80,"ip":"*","hostName":"sample.local"}]'
|
|
32
|
+
- name: bindings
|
|
33
|
+
type: string
|
|
34
|
+
default: '[]'
|
|
35
|
+
|
|
36
|
+
# Execution Condition
|
|
37
|
+
- name: runCondition
|
|
38
|
+
type: string
|
|
39
|
+
default: 'succeeded()'
|
|
40
|
+
|
|
41
|
+
steps:
|
|
42
|
+
# ----------------------------------------------------------------------------
|
|
43
|
+
# Execute PowerShell script to manage IIS Website
|
|
44
|
+
# Includes: Create/Config AppPool, Create/Config Website, Apply Bindings
|
|
45
|
+
# ----------------------------------------------------------------------------
|
|
46
|
+
- powershell: |
|
|
47
|
+
$websiteName = "${{ parameters.websiteName }}"
|
|
48
|
+
$physicalPath = "${{ parameters.websitePhysicalPath }}"
|
|
49
|
+
$appPoolName = "${{ parameters.appPoolName }}"
|
|
50
|
+
$appPoolDotNetVersion = "${{ parameters.appPoolDotNetVersion }}"
|
|
51
|
+
$bindingsJson = '${{ parameters.bindings }}'
|
|
52
|
+
|
|
53
|
+
Write-Host "Managing IIS Website: $websiteName"
|
|
54
|
+
|
|
55
|
+
# Ensure WebAdministration module is loaded
|
|
56
|
+
Import-Module WebAdministration
|
|
57
|
+
|
|
58
|
+
# --- 1. Configure Application Pool (AppPool) ---
|
|
59
|
+
if (!(Test-Path "IIS:\AppPools\$appPoolName")) {
|
|
60
|
+
Write-Host "Creating Application Pool: $appPoolName"
|
|
61
|
+
New-WebAppPool -Name $appPoolName
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Set .NET CLR Version
|
|
65
|
+
# If .NET Core/5+, usually set to 'No Managed Code' (Empty string)
|
|
66
|
+
$runtime = if ($appPoolDotNetVersion -eq 'No Managed Code') { "" } else { $appPoolDotNetVersion }
|
|
67
|
+
Set-ItemProperty "IIS:\AppPools\$appPoolName" -Name "managedRuntimeVersion" -Value $runtime
|
|
68
|
+
Write-Host "Application Pool '$appPoolName' managed runtime version set to: '$runtime'"
|
|
69
|
+
|
|
70
|
+
# --- 2. Configure Website ---
|
|
71
|
+
if (!(Test-Path "IIS:\Sites\$websiteName")) {
|
|
72
|
+
Write-Host "Creating new website: $websiteName"
|
|
73
|
+
# Assign a temporary Port 80 when creating, will be overwritten by Apply Bindings logic later
|
|
74
|
+
New-Website -Name $websiteName -PhysicalPath $physicalPath -ApplicationPool $appPoolName -Port 80 -Force
|
|
75
|
+
} else {
|
|
76
|
+
Write-Host "Updating website settings (Physical Path and Application Pool)"
|
|
77
|
+
Set-ItemProperty "IIS:\Sites\$websiteName" -Name "physicalPath" -Value $physicalPath
|
|
78
|
+
Set-ItemProperty "IIS:\Sites\$websiteName" -Name "applicationPool" -Value $appPoolName
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# --- 3. Apply Bindings ---
|
|
82
|
+
# Sync Mode: Remove all existing bindings, keep only items in JSON settings
|
|
83
|
+
if (-not [string]::IsNullOrWhiteSpace($bindingsJson)) {
|
|
84
|
+
try {
|
|
85
|
+
$bindings = $bindingsJson | ConvertFrom-Json
|
|
86
|
+
|
|
87
|
+
if ($bindings.Count -gt 0) {
|
|
88
|
+
Write-Host "Applying binding settings..."
|
|
89
|
+
|
|
90
|
+
# Remove all existing Bindings
|
|
91
|
+
Get-WebBinding -Name $websiteName | Remove-WebBinding
|
|
92
|
+
|
|
93
|
+
# Add defined Bindings one by one
|
|
94
|
+
foreach ($b in $bindings) {
|
|
95
|
+
$protocol = if ($b.type) { $b.type } else { "http" }
|
|
96
|
+
$port = if ($b.port) { $b.port } else { 80 }
|
|
97
|
+
$ip = if ($b.ip) { $b.ip } else { "*" }
|
|
98
|
+
$hostName = if ($b.hostName) { $b.hostName } else { "" }
|
|
99
|
+
|
|
100
|
+
Write-Host "Adding Binding: ${protocol}://${ip}:${port}/${hostName}"
|
|
101
|
+
New-WebBinding -Name $websiteName -Protocol $protocol -Port $port -IPAddress $ip -HostHeader $hostName
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
Write-Host "Binding settings list is empty, skipping update."
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
Write-Warning "Failed to parse Bindings JSON: $_"
|
|
108
|
+
Write-Warning "Original JSON Content: $bindingsJson"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
displayName: 'Manage IIS Website (Create/Update) - PowerShell'
|
|
112
|
+
condition: ${{ parameters.runCondition }}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Template Name: IIS Rollback (iis-rollback.yml)
|
|
3
|
+
# Description:
|
|
4
|
+
# Restore action executed when deployment fails.
|
|
5
|
+
# 1. Check if there is a backup file generated by this deployment (Variable _GeneratedBackupPath).
|
|
6
|
+
# 2. If backup exists, restore files to website physical path.
|
|
7
|
+
# 3. Ensure IIS services (WAS, W3SVC) are running.
|
|
8
|
+
# 4. Restart IIS Website.
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
|
|
11
|
+
parameters:
|
|
12
|
+
- name: websiteName
|
|
13
|
+
type: string
|
|
14
|
+
- name: websitePhysicalPath
|
|
15
|
+
type: string
|
|
16
|
+
- name: runCondition
|
|
17
|
+
type: string
|
|
18
|
+
default: 'failed()'
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- powershell: |
|
|
22
|
+
$websiteName = "${{ parameters.websiteName }}"
|
|
23
|
+
$dest = "${{ parameters.websitePhysicalPath }}"
|
|
24
|
+
$backupZip = $env:_GeneratedBackupPath
|
|
25
|
+
|
|
26
|
+
Write-Host "Starting Rollback Process..."
|
|
27
|
+
Write-Host "Website Name: $websiteName"
|
|
28
|
+
|
|
29
|
+
# --- 1. Restore Files ---
|
|
30
|
+
if (-not [string]::IsNullOrWhiteSpace($backupZip) -and (Test-Path $backupZip)) {
|
|
31
|
+
Write-Host "Detected backup file: $backupZip"
|
|
32
|
+
Write-Host "Restoring to: $dest"
|
|
33
|
+
|
|
34
|
+
# Ensure destination directory exists
|
|
35
|
+
if (-not (Test-Path $dest)) {
|
|
36
|
+
New-Item -ItemType Directory -Force -Path $dest | Out-Null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Extract and overwrite
|
|
40
|
+
try {
|
|
41
|
+
Expand-Archive -Path $backupZip -DestinationPath $dest -Force -ErrorAction Stop
|
|
42
|
+
Write-Host "Files restored successfully."
|
|
43
|
+
} catch {
|
|
44
|
+
Write-Error "Restore failed: $_"
|
|
45
|
+
# Continue attempting to start website even if restore fails
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
Write-Host "No backup file detected from this run (possibly failed before backup step), skipping file restore."
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# --- 2. Start Website ---
|
|
52
|
+
|
|
53
|
+
function Ensure-Service-Running {
|
|
54
|
+
param($Name)
|
|
55
|
+
try {
|
|
56
|
+
$svc = Get-Service -Name $Name -ErrorAction Stop
|
|
57
|
+
if ($svc.Status -ne 'Running') {
|
|
58
|
+
Write-Warning "Service '$Name' is not running. Starting..."
|
|
59
|
+
Start-Service -Name $Name
|
|
60
|
+
|
|
61
|
+
# Wait for start
|
|
62
|
+
$timeout = 15
|
|
63
|
+
$timer = [System.Diagnostics.Stopwatch]::StartNew()
|
|
64
|
+
while ($svc.Status -ne 'Running' -and $timer.Elapsed.TotalSeconds -lt $timeout) {
|
|
65
|
+
Start-Sleep -Seconds 1
|
|
66
|
+
$svc.Refresh()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
Write-Warning "Check service '$Name' exception: $_"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function Start-Site-WithAppCmd {
|
|
75
|
+
param($Name)
|
|
76
|
+
$appCmd = "$env:systemroot\system32\inetsrv\appcmd.exe"
|
|
77
|
+
if (Test-Path $appCmd) {
|
|
78
|
+
Start-Process -FilePath $appCmd -ArgumentList "start site /site.name:`"$Name`"" -Wait -NoNewWindow
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# Ensure Core Services
|
|
83
|
+
Ensure-Service-Running -Name "WAS"
|
|
84
|
+
Ensure-Service-Running -Name "W3SVC"
|
|
85
|
+
|
|
86
|
+
# Start Website
|
|
87
|
+
try {
|
|
88
|
+
Import-Module WebAdministration
|
|
89
|
+
Start-Website -Name $websiteName -ErrorAction Stop
|
|
90
|
+
Write-Host "Website '$websiteName' restarted."
|
|
91
|
+
} catch {
|
|
92
|
+
Write-Warning "PowerShell start failed, trying appcmd..."
|
|
93
|
+
Start-Site-WithAppCmd -Name $websiteName
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
displayName: 'Execute Rollback (Restore and Restart)'
|
|
97
|
+
# Execute only if previous steps failed AND deployment process was confirmed started (Variable _IISDeploymentStarted = true)
|
|
98
|
+
condition: ${{ parameters.runCondition }}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Template Name: Start IIS Website (iis-start-website.yml)
|
|
3
|
+
# Description:
|
|
4
|
+
# This template is responsible for safely starting the specified IIS website.
|
|
5
|
+
# Includes self-check and repair mechanism:
|
|
6
|
+
# 1. First check WAS and W3SVC service status, try to start if not running.
|
|
7
|
+
# 2. Prioritize using PowerShell to start the website.
|
|
8
|
+
# 3. If PowerShell fails (e.g. RPC error), automatically downgrade to use appcmd.exe to force start.
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
|
|
11
|
+
parameters:
|
|
12
|
+
# Website Name (Display name in IIS)
|
|
13
|
+
- name: websiteName
|
|
14
|
+
type: string
|
|
15
|
+
|
|
16
|
+
# Execution Condition
|
|
17
|
+
- name: runCondition
|
|
18
|
+
type: string
|
|
19
|
+
default: 'always()'
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
# ----------------------------------------------------------------------------
|
|
23
|
+
# Execute PowerShell script to start IIS Website
|
|
24
|
+
# ----------------------------------------------------------------------------
|
|
25
|
+
- powershell: |
|
|
26
|
+
$websiteName = "${{ parameters.websiteName }}"
|
|
27
|
+
Write-Host "Attempting to start website: $websiteName"
|
|
28
|
+
|
|
29
|
+
function Ensure-Service-Running {
|
|
30
|
+
param($Name)
|
|
31
|
+
try {
|
|
32
|
+
$svc = Get-Service -Name $Name -ErrorAction Stop
|
|
33
|
+
if ($svc.Status -ne 'Running') {
|
|
34
|
+
Write-Warning "Service '$Name' is not running (Current Status: $($svc.Status)). Starting..."
|
|
35
|
+
Start-Service -Name $Name
|
|
36
|
+
|
|
37
|
+
# Wait for service start (Max 15 seconds)
|
|
38
|
+
$timeout = 15
|
|
39
|
+
$timer = [System.Diagnostics.Stopwatch]::StartNew()
|
|
40
|
+
while ($svc.Status -ne 'Running' -and $timer.Elapsed.TotalSeconds -lt $timeout) {
|
|
41
|
+
Start-Sleep -Seconds 1
|
|
42
|
+
$svc.Refresh()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ($svc.Status -eq 'Running') {
|
|
46
|
+
Write-Host "Service '$Name' started successfully."
|
|
47
|
+
} else {
|
|
48
|
+
Write-Error "Unable to start service '$Name' (Timeout or Failed)."
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
Write-Warning "Exception occurred while checking service '$Name': $_"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function Start-Site-WithAppCmd {
|
|
57
|
+
param($Name)
|
|
58
|
+
Write-Host "Attempting to start website using appcmd.exe..."
|
|
59
|
+
$appCmd = "$env:systemroot\system32\inetsrv\appcmd.exe"
|
|
60
|
+
if (Test-Path $appCmd) {
|
|
61
|
+
$p = Start-Process -FilePath $appCmd -ArgumentList "start site /site.name:`"$Name`"" -Wait -PassThru -NoNewWindow
|
|
62
|
+
if ($p.ExitCode -eq 0) {
|
|
63
|
+
Write-Host "appcmd: Website started."
|
|
64
|
+
} else {
|
|
65
|
+
Write-Error "appcmd failed to start website (Exit Code: $($p.ExitCode))."
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
Write-Error "appcmd.exe not found."
|
|
70
|
+
exit 1
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# 1. Ensure IIS Core Services (WAS, W3SVC) are functioning
|
|
75
|
+
# This is key to solving "RPC server is unavailable"
|
|
76
|
+
Ensure-Service-Running -Name "WAS"
|
|
77
|
+
Ensure-Service-Running -Name "W3SVC"
|
|
78
|
+
|
|
79
|
+
# 2. Attempt to start website
|
|
80
|
+
try {
|
|
81
|
+
Import-Module WebAdministration
|
|
82
|
+
Start-Website -Name $websiteName -ErrorAction Stop
|
|
83
|
+
Write-Host "Website '$websiteName' started successfully (PowerShell)."
|
|
84
|
+
} catch {
|
|
85
|
+
Write-Warning "Failed to start website using PowerShell: $_"
|
|
86
|
+
Start-Site-WithAppCmd -Name $websiteName
|
|
87
|
+
}
|
|
88
|
+
displayName: 'Start IIS Website'
|
|
89
|
+
condition: ${{ parameters.runCondition }}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Template Name: Stop IIS Website (iis-stop-website.yml)
|
|
3
|
+
# Description:
|
|
4
|
+
# This template is responsible for safely stopping the specified IIS website.
|
|
5
|
+
# Includes error handling mechanism: Prioritize using PowerShell, if fails (e.g. RPC error), automatically downgrade to use appcmd.exe to force stop.
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
|
|
8
|
+
parameters:
|
|
9
|
+
# Website Name (Display name in IIS)
|
|
10
|
+
- name: websiteName
|
|
11
|
+
type: string
|
|
12
|
+
|
|
13
|
+
# Execution Condition
|
|
14
|
+
- name: runCondition
|
|
15
|
+
type: string
|
|
16
|
+
default: 'succeeded()'
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
# ----------------------------------------------------------------------------
|
|
20
|
+
# Execute PowerShell script to stop IIS Website
|
|
21
|
+
# ----------------------------------------------------------------------------
|
|
22
|
+
- powershell: |
|
|
23
|
+
$websiteName = "${{ parameters.websiteName }}"
|
|
24
|
+
Write-Host "Attempting to stop website: $websiteName"
|
|
25
|
+
|
|
26
|
+
if ([string]::IsNullOrWhiteSpace($websiteName)) {
|
|
27
|
+
Write-Error "Website name is empty."
|
|
28
|
+
exit 1
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Stop-Site-WithAppCmd {
|
|
32
|
+
param($Name)
|
|
33
|
+
Write-Host "Attempting to stop website using appcmd.exe..."
|
|
34
|
+
$appCmd = "$env:systemroot\system32\inetsrv\appcmd.exe"
|
|
35
|
+
if (Test-Path $appCmd) {
|
|
36
|
+
# Stop using appcmd, ignore possible errors (e.g. website already stopped)
|
|
37
|
+
$p = Start-Process -FilePath $appCmd -ArgumentList "stop site /site.name:`"$Name`"" -Wait -PassThru -NoNewWindow
|
|
38
|
+
if ($p.ExitCode -eq 0) {
|
|
39
|
+
Write-Host "appcmd: Website stopped."
|
|
40
|
+
} else {
|
|
41
|
+
Write-Warning "appcmd completed, Exit Code: $($p.ExitCode) (This is expected behavior if website was already stopped)."
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
Write-Error "appcmd.exe not found."
|
|
45
|
+
exit 1
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
Import-Module WebAdministration
|
|
51
|
+
# Attempt to get website status
|
|
52
|
+
$site = Get-Website -Name $websiteName -ErrorAction Stop
|
|
53
|
+
|
|
54
|
+
if ($site) {
|
|
55
|
+
if ($site.State -eq "Stopped") {
|
|
56
|
+
Write-Host "Website '$websiteName' is already in stopped state."
|
|
57
|
+
} else {
|
|
58
|
+
Write-Host "Website current state: $($site.State). Stopping..."
|
|
59
|
+
Stop-Website -Name $websiteName -ErrorAction Stop
|
|
60
|
+
Write-Host "Website '$websiteName' stopped successfully."
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
Write-Host "Website '$websiteName' does not exist, skipping stop action."
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
Write-Warning "Error occurred while managing website using PowerShell (Possible RPC service abnormality): $_"
|
|
67
|
+
|
|
68
|
+
# Check if error is caused by W3SVC service not running
|
|
69
|
+
$w3svc = Get-Service -Name "W3SVC" -ErrorAction SilentlyContinue
|
|
70
|
+
if ($w3svc -and $w3svc.Status -ne 'Running') {
|
|
71
|
+
Write-Host "Detected W3SVC (World Wide Web Publishing Service) is not running (Status: $($w3svc.Status))."
|
|
72
|
+
Write-Host "Website is naturally in stopped state as IIS service is not running. Skipping stop action."
|
|
73
|
+
exit 0
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# Other errors occurred, try using appcmd
|
|
77
|
+
Stop-Site-WithAppCmd -Name $websiteName
|
|
78
|
+
}
|
|
79
|
+
displayName: 'Stop IIS Website'
|
|
80
|
+
condition: ${{ parameters.runCondition }}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Template Name: IIS Task Scheduler (iis-task.yml)
|
|
3
|
+
# Description:
|
|
4
|
+
# This is a unified interface (Facade) for calling various IIS related sub-tasks.
|
|
5
|
+
# Determines the specific action to execute by passing the `command` parameter.
|
|
6
|
+
#
|
|
7
|
+
# Supported Commands (command):
|
|
8
|
+
# - manage: Create or update website and AppPool (iis-manage-website.yml)
|
|
9
|
+
# - start: Start website (iis-start-website.yml)
|
|
10
|
+
# - stop: Stop website (iis-stop-website.yml)
|
|
11
|
+
# - backup: Backup website files (iis-backup.yml)
|
|
12
|
+
# - deploy: Deploy files (iis-deploy-files.yml)
|
|
13
|
+
# - rollback: Restore backup and restart (iis-rollback.yml)
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
|
|
16
|
+
parameters:
|
|
17
|
+
# Specify the task command to execute
|
|
18
|
+
- name: command
|
|
19
|
+
type: string
|
|
20
|
+
values:
|
|
21
|
+
- manage
|
|
22
|
+
- start
|
|
23
|
+
- stop
|
|
24
|
+
- backup
|
|
25
|
+
- deploy
|
|
26
|
+
- rollback
|
|
27
|
+
|
|
28
|
+
# --- Common/Shared Parameters ---
|
|
29
|
+
# Website Name (Used by manage, start, stop, rollback)
|
|
30
|
+
- name: websiteName
|
|
31
|
+
type: string
|
|
32
|
+
default: ''
|
|
33
|
+
|
|
34
|
+
# Website Physical Path (Used by manage, backup, deploy, rollback)
|
|
35
|
+
- name: websitePhysicalPath
|
|
36
|
+
type: string
|
|
37
|
+
default: ''
|
|
38
|
+
|
|
39
|
+
# Execution Condition (Default is empty string, will be automatically filled based on default behavior of each command)
|
|
40
|
+
- name: runCondition
|
|
41
|
+
type: string
|
|
42
|
+
default: ''
|
|
43
|
+
|
|
44
|
+
# --- Manage Specific Parameters ---
|
|
45
|
+
# Application Pool Name
|
|
46
|
+
- name: appPoolName
|
|
47
|
+
type: string
|
|
48
|
+
default: ''
|
|
49
|
+
|
|
50
|
+
# AppPool .NET CLR Version
|
|
51
|
+
- name: appPoolDotNetVersion
|
|
52
|
+
type: string
|
|
53
|
+
default: 'No Managed Code'
|
|
54
|
+
|
|
55
|
+
# Site Binding Settings (JSON List)
|
|
56
|
+
- name: bindings
|
|
57
|
+
type: string
|
|
58
|
+
default: '[]'
|
|
59
|
+
|
|
60
|
+
# --- Backup Specific Parameters ---
|
|
61
|
+
# Backup Storage Path
|
|
62
|
+
- name: backupPath
|
|
63
|
+
type: string
|
|
64
|
+
default: ''
|
|
65
|
+
|
|
66
|
+
# Backup Retention Count
|
|
67
|
+
- name: backupRetentionCount
|
|
68
|
+
type: number
|
|
69
|
+
default: 5
|
|
70
|
+
|
|
71
|
+
# --- Deploy Specific Parameters ---
|
|
72
|
+
# Source Path (Artifact)
|
|
73
|
+
- name: sourcePath
|
|
74
|
+
type: string
|
|
75
|
+
default: ''
|
|
76
|
+
|
|
77
|
+
# Destination Path (If not specified, defaults to websitePhysicalPath)
|
|
78
|
+
- name: destinationPath
|
|
79
|
+
type: string
|
|
80
|
+
default: ''
|
|
81
|
+
|
|
82
|
+
# Whether to clean destination
|
|
83
|
+
- name: cleanDestination
|
|
84
|
+
type: boolean
|
|
85
|
+
default: true
|
|
86
|
+
|
|
87
|
+
steps:
|
|
88
|
+
|
|
89
|
+
# ----------------------------------------------------------------------------
|
|
90
|
+
# Command: manage
|
|
91
|
+
# ----------------------------------------------------------------------------
|
|
92
|
+
- ${{ if eq(parameters.command, 'manage') }}:
|
|
93
|
+
- template: iis-manage-website.yml
|
|
94
|
+
parameters:
|
|
95
|
+
websiteName: ${{ parameters.websiteName }}
|
|
96
|
+
websitePhysicalPath: ${{ parameters.websitePhysicalPath }}
|
|
97
|
+
appPoolName: ${{ parameters.appPoolName }}
|
|
98
|
+
appPoolDotNetVersion: ${{ parameters.appPoolDotNetVersion }}
|
|
99
|
+
bindings: ${{ parameters.bindings }}
|
|
100
|
+
# Default: succeeded()
|
|
101
|
+
runCondition: ${{ coalesce(parameters.runCondition, 'succeeded()') }}
|
|
102
|
+
|
|
103
|
+
# ----------------------------------------------------------------------------
|
|
104
|
+
# Command: start
|
|
105
|
+
# ----------------------------------------------------------------------------
|
|
106
|
+
- ${{ if eq(parameters.command, 'start') }}:
|
|
107
|
+
- template: iis-start-website.yml
|
|
108
|
+
parameters:
|
|
109
|
+
websiteName: ${{ parameters.websiteName }}
|
|
110
|
+
# Default: always() (Ensure restart attempt even if deployment failed, or adjust as needed)
|
|
111
|
+
runCondition: ${{ coalesce(parameters.runCondition, 'always()') }}
|
|
112
|
+
|
|
113
|
+
# ----------------------------------------------------------------------------
|
|
114
|
+
# Command: stop
|
|
115
|
+
# ----------------------------------------------------------------------------
|
|
116
|
+
- ${{ if eq(parameters.command, 'stop') }}:
|
|
117
|
+
- template: iis-stop-website.yml
|
|
118
|
+
parameters:
|
|
119
|
+
websiteName: ${{ parameters.websiteName }}
|
|
120
|
+
# Default: succeeded()
|
|
121
|
+
runCondition: ${{ coalesce(parameters.runCondition, 'succeeded()') }}
|
|
122
|
+
|
|
123
|
+
# ----------------------------------------------------------------------------
|
|
124
|
+
# Command: backup
|
|
125
|
+
# ----------------------------------------------------------------------------
|
|
126
|
+
- ${{ if eq(parameters.command, 'backup') }}:
|
|
127
|
+
- template: iis-backup.yml
|
|
128
|
+
parameters:
|
|
129
|
+
websitePhysicalPath: ${{ parameters.websitePhysicalPath }}
|
|
130
|
+
backupPath: ${{ parameters.backupPath }}
|
|
131
|
+
backupRetentionCount: ${{ parameters.backupRetentionCount }}
|
|
132
|
+
# Default: succeeded()
|
|
133
|
+
runCondition: ${{ coalesce(parameters.runCondition, 'succeeded()') }}
|
|
134
|
+
|
|
135
|
+
# ----------------------------------------------------------------------------
|
|
136
|
+
# Command: deploy
|
|
137
|
+
# ----------------------------------------------------------------------------
|
|
138
|
+
- ${{ if eq(parameters.command, 'deploy') }}:
|
|
139
|
+
- template: iis-deploy-files.yml
|
|
140
|
+
parameters:
|
|
141
|
+
sourcePath: ${{ parameters.sourcePath }}
|
|
142
|
+
# If destinationPath is empty, use websitePhysicalPath
|
|
143
|
+
destinationPath: ${{ coalesce(parameters.destinationPath, parameters.websitePhysicalPath) }}
|
|
144
|
+
cleanDestination: ${{ parameters.cleanDestination }}
|
|
145
|
+
# Default: succeeded()
|
|
146
|
+
runCondition: ${{ coalesce(parameters.runCondition, 'succeeded()') }}
|
|
147
|
+
|
|
148
|
+
# ----------------------------------------------------------------------------
|
|
149
|
+
# Command: rollback
|
|
150
|
+
# ----------------------------------------------------------------------------
|
|
151
|
+
- ${{ if eq(parameters.command, 'rollback') }}:
|
|
152
|
+
- template: iis-rollback.yml
|
|
153
|
+
parameters:
|
|
154
|
+
websiteName: ${{ parameters.websiteName }}
|
|
155
|
+
websitePhysicalPath: ${{ parameters.websitePhysicalPath }}
|
|
156
|
+
# Default: failed() (Execute only on failure)
|
|
157
|
+
runCondition: ${{ coalesce(parameters.runCondition, 'failed()') }}
|