1+ # ==============================================================================
2+ # STATIC ANALYSIS WORKFLOW
3+ # ==============================================================================
4+ # This workflow performs automated static code analysis on Go and JavaScript/JSX
5+ # files when pull requests are made to the dev-review-setup branch.
6+ #
7+ # Key Features:
8+ # - Intelligent path filtering to only analyze changed directories
9+ # - Dynamic matrix strategy for parallel execution across multiple projects
10+ # - Integration with reviewdog for inline PR comments
11+ # - Automatic dependency management for both Go and JS projects
12+ # - Concurrency control to prevent resource conflicts
13+ # ==============================================================================
14+
115name : Static Analysis
216
17+ # permissions:
18+ # contents: read
19+ # pull-requests: write
20+ # checks: write
21+ # statuses: write
22+
23+ permissions : write-all
24+
25+
26+ # ==============================================================================
27+ # WORKFLOW TRIGGERS
28+ # ==============================================================================
29+ # This workflow is triggered on pull request events targeting the
30+ # dev-review-setup branch. It only runs when specific file types are modified
31+ # to optimize CI/CD resource usage.
32+ # ==============================================================================
333on :
434 pull_request :
5- branches : [ main ]
35+ branches : [ dev-review-setup ]
36+ # Trigger on these PR events to catch all code changes
37+ types : [opened, synchronize, reopened, edited]
38+ # Path-based filtering to prevent unnecessary runs
639 paths :
7- - ' **.go'
8- - ' **.js'
9- - ' **.jsx'
10- - ' .github/workflows/static-analysis.yml'
40+ - ' **.go' # Go source files
41+ - ' **.js' # JavaScript files
42+ - ' **.jsx' # React JSX files
43+ - ' .github/workflows/golangci-lint.yml' # This workflow file itself
44+
45+ # ==============================================================================
46+ # CONCURRENCY CONTROL
47+ # ==============================================================================
48+ # Prevents multiple instances of this workflow from running simultaneously
49+ # on the same PR or branch, canceling older runs to save resources
50+ # ==============================================================================
51+ concurrency :
52+ group : ${{ github.workflow }}-${{ github.head_ref || github.sha }}
53+ cancel-in-progress : true
1154
1255jobs :
13- lint_and_fix_go :
14- name : Go Static Analysis
56+ # ============================================================================
57+ # JOB 1: CHANGE DETECTION AND MATRIX PREPARATION
58+ # ============================================================================
59+ # This job determines which directories contain changed files and builds
60+ # dynamic matrices for the subsequent linting jobs. This approach ensures
61+ # we only run static analysis on projects that have actual changes.
62+ # ============================================================================
63+ filter :
64+ name : Determine Changed Directories
1565 runs-on : ubuntu-latest
16- permissions :
17- contents : write
18- pull-requests : write
66+
67+ # Output matrices that will be consumed by downstream jobs
68+ outputs :
69+ go_matrix : ${{ steps.matrix_builder.outputs.go_matrix }} # Array of Go project directories
70+ js_matrix : ${{ steps.matrix_builder.outputs.js_matrix }} # Array of JS project directories
71+
1972 steps :
20- - uses : actions/checkout@v4
73+ # ------------------------------------------------------------------------
74+ # Checkout the repository code to analyze file changes
75+ # ------------------------------------------------------------------------
76+ - name : Check out code
77+ uses : actions/checkout@v4
2178
22- - name : Set up Go
23- uses : actions/setup-go@v5
79+ # ------------------------------------------------------------------------
80+ # Use dorny/paths-filter to detect changes in specific directory patterns
81+ # This action compares the current PR against the target branch
82+ # ------------------------------------------------------------------------
83+ - name : Use paths-filter action
84+ uses : dorny/paths-filter@v3
85+ id : changes
2486 with :
25- go-version : ' 1.21'
87+ # Return list of changed files as JSON for processing
88+ list-files : ' json'
89+ filters : |
90+ go:
91+ - 'static-code-go/**'
92+ - 'echo-mysql/**'
93+ - 'echo-sql/**'
94+ - 'fasthttp-postgres/**'
95+ - 'gin-mongo/**'
96+ - 'gin-redis/**'
97+ - 'go-grpc/**'
98+ - 'go-jwt/**'
99+ - 'go-twilio/**'
100+ - 'graphql-sql/**'
101+ - 'http-pokeapi/**'
102+ - 'mux-elasticsearch/**'
103+ - 'mux-mysql/**'
104+ - 'mux-sql/**'
105+ - 'S3-Keploy/**'
106+ - 'sse-svelte/**'
107+ - 'users-profile/**'
108+ - 'book-store-inventory/**'
109+
110+ js:
111+ - 'static-code-js/**'
112+
113+ # ------------------------------------------------------------------------
114+ # Build dynamic matrices from the changed file lists
115+ # This script extracts unique directory names from changed file paths
116+ # and formats them as JSON arrays for use in matrix strategies
117+ # This reduces unnecessary runs and speeds up CI/CD and saves a lot of resources
118+ # ------------------------------------------------------------------------
119+ - name : Build Matrix from changed files
120+ id : matrix_builder
121+ if : steps.changes.outputs.go == 'true' || steps.changes.outputs.js == 'true'
122+ run : |
123+ # Process Go files: extract root directory names and create unique JSON array
124+ # Example: "echo-mysql/main.go" -> "echo-mysql"
125+ go_dirs=$(echo '${{ steps.changes.outputs.go_files }}' | jq -r '[.[] | split("/")[0]] | unique | tojson')
126+ echo "go_matrix=$go_dirs" >> $GITHUB_OUTPUT
127+
128+ # Process JavaScript files: extract root directory names and create unique JSON array
129+ # Example: "frontend/src/app.js" -> "frontend"
130+ js_dirs=$(echo '${{ steps.changes.outputs.js_files }}' | jq -r '[.[] | split("/")[0]] | unique | tojson')
131+ echo "js_matrix=$js_dirs" >> $GITHUB_OUTPUT
26132
27- - name : Cache Go modules
28- uses : actions/cache@v4
133+ # ============================================================================
134+ # JOB 2: GO STATIC ANALYSIS
135+ # ============================================================================
136+ # Runs golangci-lint on each Go project directory that contains changes.
137+ # Uses reviewdog to provide inline PR comments for any linting issues found.
138+ # ============================================================================
139+ lint_and_fix_go :
140+ name : Go Static Analysis
141+ needs : filter # Wait for change detection
142+ if : ${{ needs.filter.outputs.go_matrix != '[]' }} # Only run if Go changes detected
143+ runs-on : ubuntu-latest
144+
145+
146+ # Matrix strategy: run this job in parallel for each changed Go directory
147+ strategy :
148+ fail-fast : false # Continue running other matrix jobs even if one fails
149+ matrix :
150+ working-directory : ${{ fromJSON(needs.filter.outputs.go_matrix) }}
151+
152+ steps :
153+ # ------------------------------------------------------------------------
154+ # Checkout with full git history for accurate diff analysis
155+ # ------------------------------------------------------------------------
156+ - name : Check out code into the Go module directory
157+ uses : actions/checkout@v4
29158 with :
30- path : |
31- ~/.cache/go-build
32- ./code/go.sum
33- key : ${{ runner.os }}-go-${{ hashFiles('./code/go.sum') }}
159+ fetch-depth : 0 # Full history needed for proper linting context
34160
161+ # ------------------------------------------------------------------------
162+ # Ensure Go module is properly initialized and dependencies are resolved
163+ # This step handles cases where go.mod might be missing or outdated
164+ # It is required by reviewdog with Golangci-lint
165+ # ------------------------------------------------------------------------
35166 - name : Ensure Go module exists and tidy
36167 run : |
168+ # Initialize go.mod if it doesn't exist (defensive programming)
37169 if [ ! -f go.mod ]; then
38170 go mod init github.com/${{ github.repository }}
39171 fi
172+ # Resolve and cleanup dependencies
40173 go mod tidy
41- working-directory : ./code
42-
43- - name : Install reviewdog
44- run : |
45- curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b /usr/local/bin
174+ working-directory : ./${{ matrix.working-directory }}
46175
47- - name : Install golangci-lint
48- run : |
49- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.57.2
50- echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
51- working-directory : ./code
52-
53- - name : Run golangci-lint and report via reviewdog
54- run : |
55- golangci-lint run --out-format=checkstyle ./... | reviewdog -f=checkstyle -name="golangci-lint" -reporter=github-pr-review -fail-on-error=true
56- env :
57- REVIEWDOG_GITHUB_API_TOKEN : ${{ secrets.GITHUB_TOKEN }}
58- working-directory : ./code
59-
60- - name : Upload golangci-lint summary
61- if : always()
62- uses : actions/upload-artifact@v4
176+ # ------------------------------------------------------------------------
177+ # Run golangci-lint with reviewdog integration
178+ # This provides inline PR comments for any issues found
179+ # ------------------------------------------------------------------------
180+ - name : Run lint and report issues
181+ uses : reviewdog/action-golangci-lint@v2
63182 with :
64- name : golangci-lint-summary
65- path : ./code
66-
67- - name : Summarize lint results
68- if : always()
69- uses : actions/github-script@v7
70- with :
71- script : |
72- core.summary.addRaw("## Go Static Analysis Run in ./code\n")
73-
183+ github_token : ${{ secrets.GITHUB_TOKEN }}
184+ reporter : github-pr-review # Post comments on PR
185+ workdir : ./${{ matrix.working-directory }}
186+ fail_level : warning # Fail on warnings and above
74187
188+ # ============================================================================
189+ # JOB 3: JAVASCRIPT/JSX STATIC ANALYSIS
190+ # ============================================================================
191+ # Runs ESLint on each JavaScript/JSX project directory that contains changes.
192+ # Automatically sets up Node.js environment and ESLint configuration if needed.
193+ # ============================================================================
75194 lint_js :
76195 name : JavaScript Static Analysis
196+ needs : filter # Wait for change detection
197+ if : ${{ needs.filter.outputs.js_matrix != '[]' }} # Only run if JS changes detected
77198 runs-on : ubuntu-latest
78- permissions :
79- contents : read
80- pull-requests : write
199+
200+
201+ # Matrix strategy: run this job in parallel for each changed JS directory
202+ strategy :
203+ fail-fast : false # Continue running other matrix jobs even if one fails
204+ matrix :
205+ working-directory : ${{ fromJSON(needs.filter.outputs.js_matrix) }}
206+
81207 steps :
208+ # ------------------------------------------------------------------------
209+ # Checkout repository code
210+ # ------------------------------------------------------------------------
82211 - uses : actions/checkout@v4
83212
84- - name : Find all JS/TS projects
85- id : find_projects
86- run : |
87- find . -name package.json -not -path "*/node_modules/*" > projects.txt
88- cat projects.txt
89-
213+ # ------------------------------------------------------------------------
214+ # Setup Node.js environment with LTS version
215+ # Using version 18 for stability and wide compatibility
216+ # ------------------------------------------------------------------------
90217 - name : Set up Node.js
91218 uses : actions/setup-node@v4
92219 with :
93220 node-version : ' 18'
94221
222+ # ------------------------------------------------------------------------
223+ # Ensure ESLint environment is properly configured
224+ # This step handles projects that might not have ESLint setup yet
225+ # ------------------------------------------------------------------------
226+ - name : Ensure package.json and eslint.config.mjs exist
227+ run : |
228+ # Create package.json if missing (defensive programming)
229+ if [ ! -f package.json ]; then
230+ npm init -y # Create with defaults
231+ npm install eslint --save-dev # Install ESLint as dev dependency
232+ fi
233+
234+ # Create basic ESLint configuration if missing
235+ # Using flat config format (ESLint 9+) with essential rules
236+ if [ ! -f eslint.config.mjs ]; then
237+ echo "export default [{ rules: { semi: 'error', quotes: ['error', 'single'] } }];" > eslint.config.mjs
238+ fi
239+ working-directory : ./${{ matrix.working-directory }}
240+
241+ # ------------------------------------------------------------------------
242+ # Run ESLint with reviewdog integration
243+ # This provides inline PR comments for any JavaScript/JSX issues found
244+ # ------------------------------------------------------------------------
95245 - name : Run ESLint via reviewdog (PR review)
96246 uses : reviewdog/action-eslint@v1
97247 with :
98248 github_token : ${{ secrets.GITHUB_TOKEN }}
99- reporter : github-pr-review
100- eslint_flags : ' **/*.{js,jsx,ts,tsx} --no-error-on-unmatched-pattern'
101- fail_level : error
102- workdir : ./js-code
249+ reporter : github-pr-review # Post comments on PR
250+ # ESLint flags explanation:
251+ # --config: Use our generated flat config file
252+ # **/*.{js,jsx,ts,tsx}: Lint all JS/JSX/TS/TSX files recursively
253+ # --no-error-on-unmatched-pattern: Don't fail if no files match pattern
254+ eslint_flags : ' --config eslint.config.mjs **/*.{js,jsx,ts,tsx} --no-error-on-unmatched-pattern'
255+ fail_level : error
256+ workdir : ./${{ matrix.working-directory }}
0 commit comments