@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.
Files changed (69) hide show
  1. package/GEMINI.md +115 -0
  2. package/README.md +155 -0
  3. package/bin/install-claude-skills.js +72 -0
  4. package/commands/neo/cd-app-service.toml +20 -0
  5. package/commands/neo/cd-iis.toml +20 -0
  6. package/commands/neo/ci-dotnet.toml +21 -0
  7. package/commands/neo/clarification.toml +48 -0
  8. package/commands/neo/code-review.toml +33 -0
  9. package/commands/neo/dotnet-gen-interface.toml +31 -0
  10. package/commands/neo/explain.toml +44 -0
  11. package/commands/neo/git-commit.toml +49 -0
  12. package/dist/hooks/secret-guard.js +2 -0
  13. package/dist/server.js +220 -0
  14. package/gemini-extension.json +15 -0
  15. package/package.json +39 -0
  16. package/skills/azure-pipelines/SKILL.md +45 -0
  17. package/skills/azure-pipelines/templates/build/build-dotnet.yml +92 -0
  18. package/skills/azure-pipelines/templates/deploy/deploy-app-service.yml +71 -0
  19. package/skills/azure-pipelines/templates/deploy/deploy-iis.yml +189 -0
  20. package/skills/azure-pipelines/templates/util/clean-artifact.yml +40 -0
  21. package/skills/azure-pipelines/templates/util/extract-artifact.yml +57 -0
  22. package/skills/azure-pipelines/templates/util/iis/iis-backup.yml +92 -0
  23. package/skills/azure-pipelines/templates/util/iis/iis-deploy-files.yml +112 -0
  24. package/skills/azure-pipelines/templates/util/iis/iis-manage-website.yml +112 -0
  25. package/skills/azure-pipelines/templates/util/iis/iis-rollback.yml +98 -0
  26. package/skills/azure-pipelines/templates/util/iis/iis-start-website.yml +89 -0
  27. package/skills/azure-pipelines/templates/util/iis/iis-stop-website.yml +80 -0
  28. package/skills/azure-pipelines/templates/util/iis/iis-task.yml +157 -0
  29. package/skills/azure-pipelines/templates/util/set-aspnetcore-env.yml +77 -0
  30. package/skills/clarification/SKILL.md +22 -0
  31. package/skills/code-review/SKILL.md +72 -0
  32. package/skills/csharp/SKILL.md +87 -0
  33. package/skills/csharp/reference/anti-patterns.md +142 -0
  34. package/skills/csharp/reference/coding-style.md +86 -0
  35. package/skills/csharp/reference/patterns.md +142 -0
  36. package/skills/csharp-interface-generator/SKILL.md +40 -0
  37. package/skills/dotnet/SKILL.md +41 -0
  38. package/skills/dotnet-ef-core/SKILL.md +78 -0
  39. package/skills/dotnet-ef-core/reference/anti-patterns.md +51 -0
  40. package/skills/dotnet-ef-core/reference/coding-style.md +42 -0
  41. package/skills/dotnet-ef-core/reference/patterns.md +53 -0
  42. package/skills/dotnet-minimal-apis/SKILL.md +78 -0
  43. package/skills/dotnet-minimal-apis/reference/anti-patterns.md +59 -0
  44. package/skills/dotnet-minimal-apis/reference/coding-style.md +54 -0
  45. package/skills/dotnet-minimal-apis/reference/patterns.md +68 -0
  46. package/skills/dotnet-mvc/SKILL.md +78 -0
  47. package/skills/dotnet-mvc/reference/anti-patterns.md +49 -0
  48. package/skills/dotnet-mvc/reference/coding-style.md +43 -0
  49. package/skills/dotnet-mvc/reference/patterns.md +56 -0
  50. package/skills/dotnet-webapi/SKILL.md +78 -0
  51. package/skills/dotnet-webapi/reference/anti-patterns.md +48 -0
  52. package/skills/dotnet-webapi/reference/coding-style.md +47 -0
  53. package/skills/dotnet-webapi/reference/patterns.md +52 -0
  54. package/skills/explain/SKILL.md +27 -0
  55. package/skills/git-commit/SKILL.md +84 -0
  56. package/skills/python/SKILL.md +61 -0
  57. package/skills/python/reference/anti-patterns.md +177 -0
  58. package/skills/python/reference/coding-style.md +92 -0
  59. package/skills/python/reference/patterns.md +112 -0
  60. package/skills/python-manager/SKILL.md +61 -0
  61. package/skills/start-plan/SKILL.md +29 -0
  62. package/skills/swift/SKILL.md +78 -0
  63. package/skills/swift/reference/anti-patterns.md +75 -0
  64. package/skills/swift/reference/coding-style.md +56 -0
  65. package/skills/swift/reference/patterns.md +94 -0
  66. package/skills/swift-ui/SKILL.md +76 -0
  67. package/skills/swift-ui/reference/anti-patterns.md +52 -0
  68. package/skills/swift-ui/reference/coding-style.md +46 -0
  69. package/skills/swift-ui/reference/patterns.md +87 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "neo-skills",
3
+ "description": "A universal capability extension for Gemini CLI",
4
+ "version": "0.20.0",
5
+ "mcpServers": {
6
+ "neo-skills": {
7
+ "command": "node",
8
+ "args": [
9
+ "${extensionPath}/dist/server.js"
10
+ ]
11
+ }
12
+ },
13
+ "hooks": "hooks/hooks.json",
14
+ "contextFileName": "GEMINI.md"
15
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@moon791017/neo-skills",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "description": "Neo Skills: A Universal Expert Agent Extension",
6
+ "bin": {
7
+ "neo-install-claude-skills": "./bin/install-claude-skills.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "bin",
12
+ "skills",
13
+ "commands",
14
+ "gemini-extension.json",
15
+ "GEMINI.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "bun run clean && bun run build:server && bun run build:hooks",
19
+ "build:server": "bun build src/server.ts --outdir dist --target node --minify",
20
+ "build:hooks": "bun build src/hooks/*.ts --outdir dist/hooks --target node --minify",
21
+ "install-deps": "npm install",
22
+ "clean": "rm -rf dist",
23
+ "start": "node dist/server.js",
24
+ "dev": "bun src/server.ts",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "echo \"No tests specified\" && exit 0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/bun": "latest",
30
+ "@types/node": "^25.5.0"
31
+ },
32
+ "dependencies": {
33
+ "@modelcontextprotocol/sdk": "^1.28.0",
34
+ "axios": "^1.13.6",
35
+ "cheerio": "^1.2.0",
36
+ "toml": "^3.0.0",
37
+ "zod": "^4.3.6"
38
+ }
39
+ }
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: azure-pipeline-architect
3
+ version: "1.0.0"
4
+ category: "DevOps"
5
+ description: "根據微軟官方文件與專案需求,設計並生成符合最新標準的 Azure Pipelines YAML 腳本。優先使用模組化範本 (Templates)。"
6
+ compatibility: "Supports .NET 6.0 up to .NET 10.0."
7
+ ---
8
+
9
+ # Azure Pipeline Script Design Specifications
10
+
11
+ ## Perceive
12
+ 1. Access and retrieve official documentation at `https://learn.microsoft.com/en-us/azure/devops/pipelines/?view=azure-devops` to obtain the latest YAML syntax architecture, Task update notes, and security best practices.
13
+ 2. Identify the application's development language (e.g., .NET, Java, Python, Node.js) and its specific version requirements.
14
+ 3. Identify the target deployment platform (e.g., Azure App Service, Azure Kubernetes Service, Function App, or on-premises servers).
15
+ 4. Detect the project source code structure to confirm build tools (e.g., Maven, Gradle, Npm, NuGet) and testing frameworks.
16
+ 5. Read security and compliance requirements, including Static Application Security Testing (SAST), package vulnerability scanning, and container image scanning.
17
+ 6. Confirm environment variable requirements, secret information sources (e.g., Azure Key Vault), and Service Connection permissions.
18
+ 7. **Proactively scan the `skills/azure-pipelines/templates/` directory to identify existing reusable template resources. Includes:**
19
+ * **Build**: `build/build-dotnet.yml`
20
+ * **Deploy**: `deploy/deploy-app-service.yml`, `deploy/deploy-iis.yml`
21
+ * **Utils**: `util/clean-artifact.yml`, `util/extract-artifact.yml`, `util/iis/*.yml`, etc.
22
+
23
+ ## Reason
24
+ 1. Compare the latest versions in official documentation with existing configurations to determine if Task versions need updating (e.g., using Checkout@v1 vs. Checkout@v4).
25
+ 2. Determine whether to adopt a multi-stage architecture based on project scale to achieve logical isolation of Build, Test, Staging, and Production.
26
+ 3. Evaluate and design build caching strategies, optimizing dependency package folders to reduce execution time.
27
+ 4. Design trigger mechanisms based on branching strategies, distinguishing trigger paths for Continuous Integration (CI) and Continuous Deployment (CD).
28
+ 5. Determine the applicability of deployment strategies, such as Blue-Green, Canary, or Rolling Update.
29
+ 6. Validate conditional execution syntax (Conditions) in Pipeline logic to ensure subsequent steps only execute on specific branches or after successful prerequisites.
30
+ 7. **Prioritize using templates from `skills/azure-pipelines/templates/` to assemble the Pipeline, rather than writing raw YAML from scratch.**
31
+ * If the project is .NET and needs deployment to IIS, combine `build-dotnet.yml` and `deploy-iis.yml`.
32
+ * If artifact manipulation is required, prioritize using `util/extract-artifact.yml`.
33
+
34
+ ## Act
35
+ 1. Output global YAML configuration scripts that comply with the latest Azure DevOps Schema standards.
36
+ 2. Provide comprehensive parameter definitions to increase the flexibility and reusability of Pipeline execution.
37
+ 3. Generate configuration recommendations for Environments and Approvals and Checks.
38
+ 4. Output a list of Task resource references used in the script, labeling version numbers to ensure execution environment consistency.
39
+ 5. Provide preventive comments and explanations for common execution errors (e.g., insufficient permissions, dependency conflicts).
40
+ 6. **Use `template` syntax to reference selected template files and correctly pass required parameters. For example:**
41
+ ```yaml
42
+ - template: skills/azure-pipelines/templates/build/build-dotnet.yml
43
+ parameters:
44
+ buildConfiguration: 'Release'
45
+ ```
@@ -0,0 +1,92 @@
1
+ # 範本名稱: .NET CI Build Steps (Optimized with Caching)
2
+ # 說明: 此範本包含 .NET 專案持續整合所需的核心步驟,並已整合 NuGet 快取機制。
3
+
4
+ parameters:
5
+ - name: solution
6
+ type: string
7
+ - name: testSolution
8
+ type: string
9
+ default: ""
10
+ - name: buildPlatform
11
+ type: string
12
+ default: "Any CPU"
13
+ - name: buildConfiguration
14
+ type: string
15
+ default: "Release"
16
+ - name: dotnetSdkVersion
17
+ type: string
18
+ - name: nugetCachePath
19
+ type: string
20
+ default: "$(Pipeline.Workspace)/.nuget/packages"
21
+ - name: publishWebProjects
22
+ type: boolean
23
+ default: true
24
+
25
+
26
+ steps:
27
+ # 步驟 1: 設定 .NET 環境
28
+ - task: UseDotNet@2
29
+ displayName: "安裝 .NET SDK"
30
+ inputs:
31
+ packageType: "sdk"
32
+ version: "${{ parameters.dotnetSdkVersion }}"
33
+
34
+ # 步驟 2: NuGet 快取
35
+ - task: Cache@2
36
+ displayName: "快取 NuGet 套件"
37
+ inputs:
38
+ # 你的 Key 設定現在看起來很正確 (matches: 2 表示找到了 2 個專案檔)
39
+ key: 'nuget | "$(Agent.OS)" | **/*.csproj,!**/bin/**,!**/obj/**'
40
+ restoreKeys: |
41
+ nuget | "$(Agent.OS)"
42
+ path: "${{ parameters.nugetCachePath }}"
43
+
44
+ # 步驟 3: 還原專案相依性
45
+ # - env: NUGET_PACKAGES: 強制將還原路徑指向快取資料夾。
46
+ - task: DotNetCoreCLI@2
47
+ displayName: "還原 NuGet 套件"
48
+ inputs:
49
+ command: "restore"
50
+ projects: "${{ parameters.solution }}"
51
+ feedsToUse: "select"
52
+ env:
53
+ NUGET_PACKAGES: "${{ parameters.nugetCachePath }}"
54
+
55
+ # 步驟 4: 建置解決方案
56
+ # - noRestore: 因為上一步驟已還原,這裡加入 --no-restore 加快速度。
57
+ - task: DotNetCoreCLI@2
58
+ displayName: "建置專案"
59
+ inputs:
60
+ command: "build"
61
+ projects: "${{ parameters.solution }}"
62
+ arguments: "--configuration ${{ parameters.buildConfiguration }} --no-restore"
63
+
64
+ # 步驟 5: 如果testSolution是true,才執行單元測試
65
+ - ${{ if ne(parameters.testSolution, '') }}:
66
+ - task: DotNetCoreCLI@2
67
+ displayName: "執行測試"
68
+ inputs:
69
+ command: "test"
70
+ projects: "${{ parameters.testSolution }}"
71
+ # 加入 --no-build 與 --no-restore 避免重複編譯與還原,因為建置步驟已完成編譯
72
+ arguments: "--configuration ${{ parameters.buildConfiguration }} --no-build --no-restore"
73
+
74
+ # 步驟 6: 發佈應用程式
75
+ - task: DotNetCoreCLI@2
76
+ displayName: "產生發佈檔案"
77
+ inputs:
78
+ command: "publish"
79
+ publishWebProjects: ${{ parameters.publishWebProjects }}
80
+ projects: "${{ parameters.solution }}"
81
+ # 注意: Publish 通常需要重新建置以確保正確的 artifacts 結構,視專案設定而定,這裡保留標準行為
82
+ arguments: "--configuration ${{ parameters.buildConfiguration }} --output $(Build.ArtifactStagingDirectory) --no-restore"
83
+ # zipAfterPublish: false
84
+ # modifyOutputPath: false # 禁止自動建立專案名稱目錄,避免 Artifact 產生多餘的嵌套層級 (e.g. drop/Sample.WebSite/...)
85
+
86
+ # 步驟 7: 上傳發佈檔案
87
+ - task: PublishPipelineArtifact@1
88
+ displayName: "上傳發佈檔案"
89
+ inputs:
90
+ targetPath: "$(Build.ArtifactStagingDirectory)" # 必填 (要發佈的資料夾或檔案路徑。)
91
+ artifact: "drop" # 選填 (預設是drop)
92
+ publishLocation: "pipeline" # 選填 (預設是pipeline)
@@ -0,0 +1,71 @@
1
+ # ==============================================================================
2
+ # Template Name: Deploy to Azure App Service (deploy-app-service.yml)
3
+ # Description:
4
+ # This template encapsulates the standard process for deploying a website to Azure App Service.
5
+ # It includes two main steps:
6
+ # 1. Download Pipeline Artifact
7
+ # 2. Deploy to Azure App Service
8
+ # ==============================================================================
9
+
10
+ parameters:
11
+ # Azure Service Connection Name
12
+ - name: azureSubscription
13
+ type: string
14
+
15
+ # App Service Name
16
+ - name: webAppName
17
+ type: string
18
+
19
+ # App Service Type
20
+ - name: appType
21
+ type: string
22
+ default: "webApp"
23
+ values:
24
+ - "webApp" # Windows Web App
25
+ - "webAppLinux" # Linux Web App
26
+
27
+ # Source Pipeline Name (used for downloading Artifacts)
28
+ - name: sourcePipeline
29
+ type: string
30
+ default: "current"
31
+
32
+ # Name of the Artifact to download
33
+ - name: artifactName
34
+ type: string
35
+ default: "drop"
36
+
37
+ # Deployment package path (supports wildcards, e.g., $(Pipeline.Workspace)/drop/**/*.zip)
38
+ - name: packagePath
39
+ type: string
40
+ default: "$(Pipeline.Workspace)/drop/**/*.zip"
41
+
42
+ # Deployment slot name (default is production; if not production, it will try to deploy to that Slot)
43
+ - name: slotName
44
+ type: string
45
+ default: "production"
46
+
47
+ # Resource Group Name
48
+ - name: resourceGroupName
49
+ type: string
50
+
51
+ steps:
52
+ # ----------------------------------------------------------------------------
53
+ # Step 1: Download Artifact
54
+ # ----------------------------------------------------------------------------
55
+ - download: ${{ parameters.sourcePipeline }}
56
+ artifact: ${{ parameters.artifactName }}
57
+ displayName: "Download Website Publish Files"
58
+
59
+ # ----------------------------------------------------------------------------
60
+ # Step 2: Deploy to Azure App Service
61
+ # ----------------------------------------------------------------------------
62
+ - task: AzureWebApp@1
63
+ displayName: "Deploy to Azure App Service"
64
+ inputs:
65
+ azureSubscription: ${{ parameters.azureSubscription }} # Azure subscription service connection name
66
+ appType: ${{ parameters.appType }} # Web App type (webApp or webAppLinux)
67
+ appName: ${{ parameters.webAppName }} # Azure App Service name
68
+ package: ${{ parameters.packagePath }} # Deployment package path (.zip)
69
+ deployToSlotOrASE: ${{ ne(parameters.slotName, 'production') }} # Whether to deploy to Slot or App Service Environment
70
+ resourceGroupName: ${{ parameters.resourceGroupName }} # Resource group name (required when deploying to Slot)
71
+ slotName: ${{ parameters.slotName }} # Deployment Slot name (default is production)
@@ -0,0 +1,189 @@
1
+ # ==============================================================================
2
+ # Template Name: Deploy to IIS on Machine Group (deploy-iis.yml)
3
+ # Description:
4
+ # This template encapsulates the standard process for deploying a website to On-Premises IIS.
5
+ # Executed using Machine Group Agents (Environment).
6
+ # Includes three main steps:
7
+ # 1. Download Artifact
8
+ # 2. Manage IIS Website and Application Pool (Task) - Handles site creation, binding, and AppPool settings
9
+ # 3. Deploy application physical files - Includes file cleanup and variable substitution
10
+ # ==============================================================================
11
+
12
+ parameters:
13
+ # Website Name (Display name in IIS)
14
+ - name: websiteName
15
+ type: string
16
+
17
+ # Website Physical Path (Absolute path on the server)
18
+ - name: websitePhysicalPath
19
+ type: string
20
+
21
+ # Site Binding Settings (JSON List)
22
+ # Format example: '[{"type":"http","port":80,"ip":"*","hostName":"sample.local"}]'
23
+ - name: bindings
24
+ type: string
25
+ default: "[]"
26
+
27
+ # Whether to create or update the Application Pool (AppPool)
28
+ - name: createOrUpdateAppPool
29
+ type: boolean
30
+ default: true
31
+
32
+ # Application Pool Name
33
+ - name: appPoolName
34
+ type: string
35
+
36
+ # AppPool .NET CLR Version (For .NET Core/5+, 'No Managed Code' is recommended)
37
+ - name: appPoolDotNetVersion
38
+ type: string
39
+ default: "No Managed Code"
40
+ values:
41
+ - "No Managed Code"
42
+ - "v4.0"
43
+ - "v2.0"
44
+
45
+ # ASP.NET Core Environment Variable (e.g., Development, Production)
46
+ - name: aspNetCoreEnvironment
47
+ type: string
48
+ default: "Production"
49
+
50
+ # Source Pipeline Name (used for downloading Artifacts)
51
+ - name: sourcePipeline
52
+ type: string
53
+ default: "current"
54
+
55
+ # Name of the Artifact to download
56
+ - name: artifactName
57
+ type: string
58
+ default: "drop"
59
+
60
+ # Whether to clean extra files in the destination folder (Recommended to keep environment clean, default true)
61
+ - name: cleanDestination
62
+ type: boolean
63
+ default: true
64
+
65
+ # Backup Path
66
+ - name: backupPath
67
+ type: string
68
+
69
+ # Backup Retention Count
70
+ - name: backupRetentionCount
71
+ type: number
72
+ default: 5
73
+
74
+ steps:
75
+ # ----------------------------------------------------------------------------
76
+ # Step 1: Download Artifact
77
+ # ----------------------------------------------------------------------------
78
+ - download: ${{ parameters.sourcePipeline }}
79
+ artifact: ${{ parameters.artifactName }}
80
+ displayName: "Download Website Publish Files"
81
+
82
+ # ----------------------------------------------------------------------------
83
+ # Step 2: Extract Artifact (if it is a zip)
84
+ # This is to allow subsequent web.config modification and JSON variable substitution to work
85
+ # ----------------------------------------------------------------------------
86
+ - template: ../util/extract-artifact.yml
87
+ parameters:
88
+ artifactName: ${{ parameters.artifactName }}
89
+ sourcePipeline: ${{ parameters.sourcePipeline }}
90
+ runCondition: "succeeded()"
91
+
92
+ # ----------------------------------------------------------------------------
93
+ # Step 3: Pre-modify web.config (Set Environment Variables)
94
+ # Handle web.config modification through a separate Template to keep the main file clean.
95
+ # ----------------------------------------------------------------------------
96
+ - template: ../util/set-aspnetcore-env.yml
97
+ parameters:
98
+ aspNetCoreEnvironment: ${{ parameters.aspNetCoreEnvironment }}
99
+ packagePath: "$(WebDeployPackagePath)"
100
+ # Execution condition: succeeded + environment variable is not empty
101
+ runCondition: "and(succeeded(), ne('${{ parameters.aspNetCoreEnvironment }}', ''))"
102
+
103
+ # ----------------------------------------------------------------------------
104
+ # Step 4: Manage IIS Website (Create/Update)
105
+ # Create website and assign AppPool. For .NET Core/5+ apps, AppPool is recommended to be 'No Managed Code'.
106
+ # Execute PowerShell script using external template
107
+ # ----------------------------------------------------------------------------
108
+ - template: ../util/iis/iis-task.yml
109
+ parameters:
110
+ command: manage
111
+ websiteName: ${{ parameters.websiteName }}
112
+ websitePhysicalPath: ${{ parameters.websitePhysicalPath }}
113
+ appPoolName: ${{ parameters.appPoolName }}
114
+ appPoolDotNetVersion: ${{ parameters.appPoolDotNetVersion }}
115
+ bindings: ${{ parameters.bindings }}
116
+ runCondition: "succeeded()"
117
+
118
+ # ----------------------------------------------------------------------------
119
+ # Step 5: Mark Deployment Start
120
+ # ----------------------------------------------------------------------------
121
+ - powershell: Write-Host "##vso[task.setvariable variable=_IISDeploymentStarted]true"
122
+ displayName: "Mark Deployment Start (Set Deployment Flag)"
123
+ # ----------------------------------------------------------------------------
124
+ # Step 6: Stop IIS Website
125
+ # ----------------------------------------------------------------------------
126
+ - template: ../util/iis/iis-task.yml
127
+ parameters:
128
+ command: stop
129
+ websiteName: ${{ parameters.websiteName }}
130
+ runCondition: "and(succeeded(), eq(variables['_IISDeploymentStarted'], 'true'))"
131
+
132
+ # ----------------------------------------------------------------------------
133
+ # Step 7: Backup Old Website Content
134
+ # ----------------------------------------------------------------------------
135
+ - template: ../util/iis/iis-task.yml
136
+ parameters:
137
+ command: backup
138
+ websitePhysicalPath: ${{ parameters.websitePhysicalPath }}
139
+ backupPath: ${{ parameters.backupPath }}
140
+ backupRetentionCount: ${{ parameters.backupRetentionCount }}
141
+ # Execution condition: succeeded + deployment started + backup path is not empty
142
+ runCondition: "and(succeeded(), eq(variables['_IISDeploymentStarted'], 'true'), ne('${{ parameters.backupPath }}', ''))"
143
+
144
+ # ----------------------------------------------------------------------------
145
+ # Step 8: Deploy Application Files
146
+ # Copy Artifact to destination path.
147
+ # Execute Copy-Item using external template
148
+ # ----------------------------------------------------------------------------
149
+ - template: ../util/iis/iis-task.yml
150
+ parameters:
151
+ command: deploy
152
+ sourcePath: "$(WebDeployPackagePath)"
153
+ destinationPath: ${{ parameters.websitePhysicalPath }}
154
+ cleanDestination: ${{ parameters.cleanDestination }}
155
+ # Execution condition: succeeded + deployment started
156
+ runCondition: "and(succeeded(), eq(variables['_IISDeploymentStarted'], 'true'))"
157
+
158
+ # ----------------------------------------------------------------------------
159
+ # Step 9: Start IIS Website
160
+ # Execute PowerShell script using external template
161
+ # ----------------------------------------------------------------------------
162
+ - template: ../util/iis/iis-task.yml
163
+ parameters:
164
+ command: start
165
+ websiteName: ${{ parameters.websiteName }}
166
+ # Execution condition: succeeded + deployment started
167
+ runCondition: "and(succeeded(), eq(variables['_IISDeploymentStarted'], 'true'))"
168
+
169
+ # ----------------------------------------------------------------------------
170
+ # Step 10: Rollback Mechanism
171
+ # Execute restore and restart when deployment fails (any of Step 6~9 fails)
172
+ # ----------------------------------------------------------------------------
173
+ - template: ../util/iis/iis-task.yml
174
+ parameters:
175
+ command: rollback
176
+ websiteName: ${{ parameters.websiteName }}
177
+ websitePhysicalPath: ${{ parameters.websitePhysicalPath }}
178
+ # Execution condition: failed + deployment started
179
+ runCondition: "and(failed(), eq(variables['_IISDeploymentStarted'], 'true'))"
180
+
181
+ # ----------------------------------------------------------------------------
182
+ # Step 9: Clean Temporary Files
183
+ # Execute cleanup regardless of deployment success or failure to release space (Avoid Artifact accumulation in local IIS azagent folder)
184
+ # ----------------------------------------------------------------------------
185
+ - template: ../util/clean-artifact.yml
186
+ parameters:
187
+ artifactName: ${{ parameters.artifactName }}
188
+ sourcePipeline: ${{ parameters.sourcePipeline }}
189
+ runCondition: "always()"
@@ -0,0 +1,40 @@
1
+ # ==============================================================================
2
+ # Template Name: Clean Artifact (clean-artifact.yml)
3
+ # Description:
4
+ # Clean up Artifact temporary files downloaded to the Agent.
5
+ # Usually executed at the end of the deployment process to ensure disk space is released.
6
+ # Configured with condition: always() to ensure execution regardless of deployment success or failure.
7
+ # ==============================================================================
8
+
9
+ parameters:
10
+ - name: artifactName
11
+ type: string
12
+ - name: sourcePipeline
13
+ type: string
14
+
15
+ # Execution Condition
16
+ - name: runCondition
17
+ type: string
18
+ default: 'always()'
19
+
20
+ steps:
21
+ - powershell: |
22
+ $artifactName = "${{ parameters.artifactName }}"
23
+ $sourcePipeline = "${{ parameters.sourcePipeline }}"
24
+
25
+ # Determine Artifact Path
26
+ $artifactPath = "$(Pipeline.Workspace)/$artifactName"
27
+ if ($sourcePipeline -ne 'current') {
28
+ $artifactPath = "$(Pipeline.Workspace)/$sourcePipeline/$artifactName"
29
+ }
30
+
31
+ Write-Host "Cleaning up artifact at: $artifactPath"
32
+
33
+ if (Test-Path $artifactPath) {
34
+ Remove-Item -Path $artifactPath -Recurse -Force -ErrorAction SilentlyContinue
35
+ Write-Host "Artifact cleaned up successfully."
36
+ } else {
37
+ Write-Host "Artifact path not found, nothing to clean."
38
+ }
39
+ displayName: 'Clean Artifact Temporary Files'
40
+ condition: ${{ parameters.runCondition }}
@@ -0,0 +1,57 @@
1
+ # ==============================================================================
2
+ # Template Name: Extract Artifact (extract-artifact.yml)
3
+ # Description:
4
+ # Check if the specified Artifact is a ZIP archive.
5
+ # If so, extract it to the 'extracted' subdirectory.
6
+ # Finally, set the variable 'WebDeployPackagePath' for subsequent deployment steps.
7
+ # ==============================================================================
8
+
9
+ parameters:
10
+ - name: artifactName
11
+ type: string
12
+ - name: sourcePipeline
13
+ type: string
14
+ default: 'current'
15
+
16
+ # Execution Condition
17
+ - name: runCondition
18
+ type: string
19
+ default: 'succeeded()'
20
+
21
+ steps:
22
+ - powershell: |
23
+ $artifactName = "${{ parameters.artifactName }}"
24
+ $sourcePipeline = "${{ parameters.sourcePipeline }}"
25
+
26
+ # Determine base path
27
+ $basePath = "$(Pipeline.Workspace)/$artifactName"
28
+ if ($sourcePipeline -ne 'current') {
29
+ $basePath = "$(Pipeline.Workspace)/$sourcePipeline/$artifactName"
30
+ }
31
+
32
+ Write-Host "Checking artifact at: $basePath"
33
+ $packagePath = $basePath
34
+
35
+ # Check for ZIP file
36
+ if (Test-Path $basePath) {
37
+ $zipFile = Get-ChildItem -Path $basePath -Filter "*.zip" | Select-Object -First 1
38
+
39
+ if ($zipFile) {
40
+ Write-Host "Found compressed artifact: $($zipFile.FullName)"
41
+ $extractPath = "$basePath/extracted"
42
+
43
+ Write-Host "Extracting to: $extractPath"
44
+ # Clean old extraction directory (if exists)
45
+ if (Test-Path $extractPath) { Remove-Item $extractPath -Recurse -Force }
46
+
47
+ Expand-Archive -Path $zipFile.FullName -DestinationPath $extractPath -Force
48
+ $packagePath = $extractPath
49
+ } else {
50
+ Write-Host "No zip file found. Assuming loose files."
51
+ }
52
+ }
53
+
54
+ Write-Host "Setting WebDeployPackagePath to: $packagePath"
55
+ Write-Host "##vso[task.setvariable variable=WebDeployPackagePath]$packagePath"
56
+ displayName: 'Check and Extract Artifact'
57
+ condition: ${{ parameters.runCondition }}
@@ -0,0 +1,92 @@
1
+ # ==============================================================================
2
+ # Template Name: IIS Website Backup (backup-iis.yml)
3
+ # Description:
4
+ # Compress and backup the contents of the specified website physical path.
5
+ # 1. Compress to zip (Filename: yyyy-MM-dd-HH-mm-ss.zip)
6
+ # 2. Save to backupPath
7
+ # 3. Keep the latest N backups based on backupRetentionCount
8
+ # ==============================================================================
9
+
10
+ parameters:
11
+ - name: websitePhysicalPath
12
+ type: string
13
+ - name: backupPath
14
+ type: string
15
+ - name: backupRetentionCount
16
+ type: number
17
+ default: 5
18
+
19
+ # Execution Condition
20
+ - name: runCondition
21
+ type: string
22
+ default: 'succeeded()'
23
+
24
+ steps:
25
+ - powershell: |
26
+ $srcRaw = "${{ parameters.websitePhysicalPath }}"
27
+ $destDirRaw = "${{ parameters.backupPath }}"
28
+ $limit = ${{ parameters.backupRetentionCount }}
29
+
30
+ # Expand Environment Variables (e.g. %SystemDrive% -> C:)
31
+ $src = [System.Environment]::ExpandEnvironmentVariables($srcRaw)
32
+ $destDir = [System.Environment]::ExpandEnvironmentVariables($destDirRaw)
33
+
34
+ Write-Host "Starting Backup Process..."
35
+ Write-Host "Source (Raw): $srcRaw"
36
+ Write-Host "Source (Expanded): $src"
37
+ Write-Host "Destination (Raw): $destDirRaw"
38
+ Write-Host "Destination (Expanded): $destDir"
39
+ Write-Host "Retention Count: $limit"
40
+
41
+ # 1. Check if source exists
42
+ if (-not (Test-Path $src)) {
43
+ Write-Warning "Source path '$src' does not exist. Skipping backup."
44
+ exit 0
45
+ }
46
+
47
+ # 2. Check if source is empty
48
+ if ((Get-ChildItem $src -Force).Count -eq 0) {
49
+ Write-Host "Source directory is empty. Nothing to backup."
50
+ exit 0
51
+ }
52
+
53
+ # 3. Create backup directory (if not exists)
54
+ if (-not (Test-Path $destDir)) {
55
+ Write-Host "Backup directory does not exist. Creating: $destDir"
56
+ New-Item -ItemType Directory -Force -Path $destDir | Out-Null
57
+ }
58
+
59
+ # 4. Execute Compression
60
+ $timestamp = Get-Date -Format "yyyy-MM-dd-HH-mm-ss"
61
+ $zipName = "$timestamp.zip"
62
+ $zipPath = Join-Path $destDir $zipName
63
+
64
+ Write-Host "Archiving content to: $zipPath"
65
+
66
+ try {
67
+ Compress-Archive -Path "$src\*" -DestinationPath $zipPath -Force -ErrorAction Stop
68
+ Write-Host "Backup created successfully."
69
+ # Set variable for Rollback use
70
+ Write-Host "##vso[task.setvariable variable=_GeneratedBackupPath]$zipPath"
71
+ }
72
+ catch {
73
+ Write-Warning "Failed to create backup. Error: $_"
74
+ exit 0
75
+ }
76
+
77
+ # 5. Apply Retention Policy
78
+ Write-Host "Applying retention policy (Keep latest $limit)..."
79
+ $backups = Get-ChildItem -Path $destDir -Filter "*.zip" | Sort-Object CreationTime -Descending
80
+
81
+ if ($backups.Count -gt $limit) {
82
+ $toDelete = $backups | Select-Object -Skip $limit
83
+ foreach ($file in $toDelete) {
84
+ Write-Host "Deleting old backup: $($file.Name)"
85
+ Remove-Item $file.FullName -Force
86
+ }
87
+ } else {
88
+ Write-Host "Current backup count ($($backups.Count)) is within limit ($limit)."
89
+ }
90
+
91
+ displayName: 'Execute IIS Website Backup'
92
+ condition: ${{ parameters.runCondition }}