-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
329 lines (292 loc) · 13.4 KB
/
action.yml
File metadata and controls
329 lines (292 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
name: "Azure Automation Source Control"
description: "Synchronizes your repository with Azure Automation runbooks"
branding:
icon: "cloud-lightning"
color: "blue"
inputs:
subscription_id:
description: "Azure subscription ID"
required: true
resource_group:
description: "Azure resource group containing the automation account"
required: true
automation_account:
description: "Name of the Azure automation account"
required: true
path_filter:
description: 'Path filter to process (e.g., "runbooks/" for specific folder, "." for all files)'
required: false
default: "."
recursive:
description: "Process files recursively within the path_filter directory (true/false)"
required: false
default: "true"
exclude_paths:
description: 'Comma-separated list of paths to exclude (e.g., ".github/,.vscode/")'
required: false
default: ".github/,.vscode/,docs/,README.md,LICENSE"
validate_all:
description: "Process all files rather than just changed ones"
required: false
default: "false"
added_modified_files:
description: "Space-separated list of added or modified files (optional, overrides auto-detection)"
required: false
deleted_files:
description: "Space-separated list of deleted files (optional, overrides auto-detection)"
required: false
runs:
using: "composite"
steps:
- name: Get changed files
id: changed-files
if: inputs.added_modified_files == '' && inputs.deleted_files == ''
shell: bash
run: |
# Get path filter from input or use default
PATH_FILTER="${{ inputs.path_filter || '.' }}"
echo "Using path filter: $PATH_FILTER"
# Get recursive flag
RECURSIVE="${{ inputs.recursive || 'true' }}"
echo "Recursive processing: $RECURSIVE"
# Process exclude paths
EXCLUDE_PATHS="${{ inputs.exclude_paths || '.github/,.vscode/,docs/,README.md,LICENSE' }}"
echo "Excluding paths: $EXCLUDE_PATHS"
# Convert exclude paths to grep pattern
EXCLUDE_PATTERN=""
IFS=',' read -ra EXCLUDE_ARRAY <<< "$EXCLUDE_PATHS"
for i in "${EXCLUDE_ARRAY[@]}"; do
if [[ -n "$EXCLUDE_PATTERN" ]]; then
EXCLUDE_PATTERN="$EXCLUDE_PATTERN|"
fi
EXCLUDE_PATTERN="${EXCLUDE_PATTERN}^$i"
done
echo "DEBUG: Exclude pattern: $EXCLUDE_PATTERN"
# Different approach based on event type
if [[ "${{ inputs.validate_all }}" == "true" ]]; then
# For manual validation of all files
echo "Manual validation of all files"
if [[ "$PATH_FILTER" == "." ]]; then
# Process all files in repo, excluding specified paths
if [[ -n "$EXCLUDE_PATTERN" ]]; then
if [[ "$RECURSIVE" == "true" ]]; then
DIFF_OUTPUT=$(find . -type f -not -path "*/\.*" -not -path "*/node_modules/*" | grep -v -E "$EXCLUDE_PATTERN" | sed 's/^\.\//A /')
else
DIFF_OUTPUT=$(find . -maxdepth 1 -type f -not -path "*/\.*" | grep -v -E "$EXCLUDE_PATTERN" | sed 's/^\.\//A /')
fi
else
if [[ "$RECURSIVE" == "true" ]]; then
DIFF_OUTPUT=$(find . -type f -not -path "*/\.*" -not -path "*/node_modules/*" | sed 's/^\.\//A /')
else
DIFF_OUTPUT=$(find . -maxdepth 1 -type f -not -path "*/\.*" | sed 's/^\.\//A /')
fi
fi
else
# Process only files in specified path
if [[ -n "$EXCLUDE_PATTERN" ]]; then
if [[ "$RECURSIVE" == "true" ]]; then
DIFF_OUTPUT=$(find $PATH_FILTER -type f -not -path "*/\.*" -not -path "*/node_modules/*" | grep -v -E "$EXCLUDE_PATTERN" | sed 's/^\.\//A /')
else
DIFF_OUTPUT=$(find $PATH_FILTER -maxdepth 1 -type f -not -path "*/\.*" | grep -v -E "$EXCLUDE_PATTERN" | sed 's/^\.\//A /')
fi
else
if [[ "$RECURSIVE" == "true" ]]; then
DIFF_OUTPUT=$(find $PATH_FILTER -type f -not -path "*/\.*" -not -path "*/node_modules/*" | sed 's/^\.\//A /')
else
DIFF_OUTPUT=$(find $PATH_FILTER -maxdepth 1 -type f -not -path "*/\.*" | sed 's/^\.\//A /')
fi
fi
fi
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
# For pull requests, compare base and head commits
echo "Getting changes from PR"
DIFF_RAW=$(git diff --name-status ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }})
# Apply path filter and exclusions
if [[ "$PATH_FILTER" == "." ]]; then
# Process all changed files, excluding specified paths
if [[ -n "$EXCLUDE_PATTERN" ]]; then
DIFF_OUTPUT=$(echo "$DIFF_RAW" | grep -v -E "$EXCLUDE_PATTERN")
else
DIFF_OUTPUT="$DIFF_RAW"
fi
else
# Process only changed files in specified path
if [[ "$RECURSIVE" == "true" ]]; then
DIFF_OUTPUT=$(echo "$DIFF_RAW" | grep "^[ADMR]\s\+$PATH_FILTER")
else
# Only match files directly in the path, not subdirectories
DIFF_OUTPUT=$(echo "$DIFF_RAW" | grep -E "^[ADMR]\s\+$PATH_FILTER[^/]*$")
fi
# Also apply exclusions if specified
if [[ -n "$EXCLUDE_PATTERN" ]]; then
DIFF_OUTPUT=$(echo "$DIFF_OUTPUT" | grep -v -E "$EXCLUDE_PATTERN")
fi
fi
elif [[ "${{ github.event_name }}" == "push" ]]; then
# For pushes, compare previous and current commits
echo "Getting changes from push"
if [[ -n "${{ github.event.before }}" && "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]]; then
DIFF_RAW=$(git diff --name-status ${{ github.event.before }} ${{ github.sha }})
else
# First push to branch case - compare with the first parent commit
echo "First push to branch, getting all files"
DIFF_RAW=$(git diff --name-status $(git rev-parse HEAD^) ${{ github.sha }})
fi
# Apply path filter and exclusions
if [[ "$PATH_FILTER" == "." ]]; then
# Process all changed files, excluding specified paths
if [[ -n "$EXCLUDE_PATTERN" ]]; then
DIFF_OUTPUT=$(echo "$DIFF_RAW" | grep -v -E "$EXCLUDE_PATTERN")
else
DIFF_OUTPUT="$DIFF_RAW"
fi
else
# Process only changed files in specified path
if [[ "$RECURSIVE" == "true" ]]; then
# Include R (renamed) files in the grep pattern
DIFF_OUTPUT=$(echo "$DIFF_RAW" | grep "^[ADMR]\s\+$PATH_FILTER")
else
# Only match files directly in the path, not subdirectories
# Include R (renamed) files in the grep pattern
DIFF_OUTPUT=$(echo "$DIFF_RAW" | grep -E "^[ADMR]\s\+$PATH_FILTER[^/]*$")
fi
# Also apply exclusions if specified
if [[ -n "$EXCLUDE_PATTERN" ]]; then
DIFF_OUTPUT=$(echo "$DIFF_OUTPUT" | grep -v -E "$EXCLUDE_PATTERN")
fi
fi
fi
echo "Changes detected:"
echo "$DIFF_OUTPUT"
# Create separate files for added/modified and deleted files
ADDED_MODIFIED_FILES=""
DELETED_FILES=""
# Process each line individually to handle all cases, including renames
while IFS= read -r line; do
if [[ -z "$line" ]]; then
continue # Skip empty lines
fi
status=${line:0:1} # Get first character (status code)
echo "DEBUG: Processing line: $line with status: $status"
if [[ "$status" == "A" || "$status" == "M" ]]; then
# Added or modified file
file=$(echo "$line" | awk '{print $2}')
echo "DEBUG: Added/Modified file: $file"
ADDED_MODIFIED_FILES+="$file "
elif [[ "$status" == "D" ]]; then
# Deleted file
file=$(echo "$line" | awk '{print $2}')
echo "DEBUG: Deleted file: $file"
DELETED_FILES+="$file "
elif [[ "$status" == "R" ]]; then
# Renamed file - treat as both deletion and addition
# Format for R might be either "R old-name new-name" or "R100 old-name new-name"
if [[ $line =~ R[0-9]+ ]]; then
# R100 format (percentage of similarity)
old_file=$(echo "$line" | awk '{print $2}')
new_file=$(echo "$line" | awk '{print $3}')
else
# Simple R format
old_file=$(echo "$line" | awk '{print $2}')
new_file=$(echo "$line" | awk '{print $3}')
fi
echo "DEBUG: Renamed file: $old_file -> $new_file"
DELETED_FILES+="$old_file "
ADDED_MODIFIED_FILES+="$new_file "
fi
done <<< "$DIFF_OUTPUT"
# Trim trailing spaces
ADDED_MODIFIED_FILES=$(echo "$ADDED_MODIFIED_FILES" | xargs)
DELETED_FILES=$(echo "$DELETED_FILES" | xargs)
# Debug final outputs
echo "DEBUG: Final ADDED_MODIFIED_FILES: $ADDED_MODIFIED_FILES"
echo "DEBUG: Final DELETED_FILES: $DELETED_FILES"
# Set outputs
echo "added_modified_files=$ADDED_MODIFIED_FILES" >> $GITHUB_OUTPUT
echo "deleted_files=$DELETED_FILES" >> $GITHUB_OUTPUT
echo "Processed changes:"
echo "Path filter: $PATH_FILTER"
echo "Added/Modified: $ADDED_MODIFIED_FILES"
echo "Deleted: $DELETED_FILES"
- name: Set files from input
if: inputs.added_modified_files != '' || inputs.deleted_files != ''
shell: bash
id: input-files
run: |
echo "Using provided file lists instead of auto-detection"
echo "added_modified_files=${{ inputs.added_modified_files }}" >> $GITHUB_OUTPUT
echo "deleted_files=${{ inputs.deleted_files }}" >> $GITHUB_OUTPUT
- name: Display changed files
shell: bash
run: |
ADDED_MODIFIED_FILES="${{ steps.changed-files.outputs.added_modified_files || steps.input-files.outputs.added_modified_files }}"
DELETED_FILES="${{ steps.changed-files.outputs.deleted_files || steps.input-files.outputs.deleted_files }}"
echo "Path filter: ${{ inputs.path_filter }}"
echo "Added or Modified files:"
echo "$ADDED_MODIFIED_FILES"
echo "----------------------"
echo "Deleted files:"
echo "$DELETED_FILES"
# Export to environment for next step
echo "ADDED_MODIFIED_FILES=$ADDED_MODIFIED_FILES" >> $GITHUB_ENV
echo "DELETED_FILES=$DELETED_FILES" >> $GITHUB_ENV
- name: Sync Azure Automation Runbooks
shell: pwsh
run: |
$subscriptionId = "${{ inputs.subscription_id }}"
$resourceGroup = "${{ inputs.resource_group }}"
$automationAccount = "${{ inputs.automation_account }}"
$addedModifiedFiles = "${{ env.ADDED_MODIFIED_FILES }}"
$deletedFiles = "${{ env.DELETED_FILES }}"
# Validate essential inputs
if ([string]::IsNullOrWhiteSpace($subscriptionId) -or
[string]::IsNullOrWhiteSpace($resourceGroup) -or
[string]::IsNullOrWhiteSpace($automationAccount)) {
Write-Error "Required parameters are missing. Ensure subscription ID, resource group, and automation account name are provided."
exit 1
}
Write-Output "Processing files from GitHub Actions workflow..."
Write-Output "Added/Modified files: $addedModifiedFiles"
Write-Output "Deleted files: $deletedFiles"
if ([string]::IsNullOrEmpty($addedModifiedFiles) -and [string]::IsNullOrEmpty($deletedFiles)) {
Write-Output "No files to process. Exiting."
exit 0
}
# Get Azure token using OIDC credentials from GitHub
if ($env:GITHUB_ACTIONS -eq "true") {
try {
Write-Output "Getting Azure token using GitHub OIDC credentials..."
# This requires the azure/login action to be run first with appropriate permissions
$token = (az account get-access-token --subscription $subscriptionId --query accessToken -o tsv)
if (-not $token) {
throw "Failed to get Azure token"
}
Write-Output "Successfully obtained Azure token"
# Add token to script parameters
$splat = @{
subscriptionId = $subscriptionId
resourceGroup = $resourceGroup
automationAccount = $automationAccount
addedModifiedFiles = $addedModifiedFiles
deletedFiles = $deletedFiles
token = $token
}
}
catch {
Write-Error "Failed to get Azure token: $_"
Write-Error "Ensure the azure/login step was run before this action"
exit 1
}
}
else {
# For local testing - no token provided
$splat = @{
subscriptionId = $subscriptionId
resourceGroup = $resourceGroup
automationAccount = $automationAccount
addedModifiedFiles = $addedModifiedFiles
deletedFiles = $deletedFiles
}
}
# Call the main script
& ${{ github.action_path }}/Start-AzureAutomationSourceControl.ps1 @splat