File Coverage

blib/lib/App/GHGen/Generator.pm
Criterion Covered Total %
statement 31 66 46.9
branch 2 2 100.0
condition 1 2 50.0
subroutine 9 20 45.0
pod 3 3 100.0
total 46 93 49.4


line stmt bran cond sub pod time code
1             package App::GHGen::Generator;
2              
3 1     1   1150 use v5.36;
  1         3  
4              
5 1     1   3 use strict;
  1         2  
  1         16  
6 1     1   3 use warnings;
  1         1  
  1         36  
7              
8 1     1   1037 use Path::Tiny;
  1         11776  
  1         89  
9 1     1   768 use App::GHGen::PerlCustomizer qw(detect_perl_requirements generate_custom_perl_workflow);
  1         4  
  1         86  
10              
11 1     1   12 use Exporter 'import';
  1         1  
  1         513  
12             our @EXPORT_OK = qw(
13             generate_workflow
14             list_workflow_types
15             get_workflow_description
16             );
17              
18             our $VERSION = '0.03';
19              
20             =head1 NAME
21              
22             App::GHGen::Generator - Generate GitHub Actions workflows
23              
24             =head1 SYNOPSIS
25              
26             use App::GHGen::Generator qw(generate_workflow);
27              
28             my $yaml = generate_workflow('perl');
29             path('.github/workflows/ci.yml')->spew_utf8($yaml);
30              
31             =head1 FUNCTIONS
32              
33             =head2 generate_workflow($type)
34              
35             Generate a workflow for the specified type. Returns YAML as a string.
36              
37             =cut
38              
39 2     2 1 1238 sub generate_workflow($type) {
  2         6  
  2         4  
40 2         34 my %generators = (
41             perl => \&_generate_perl_workflow,
42             node => \&_generate_node_workflow,
43             python => \&_generate_python_workflow,
44             rust => \&_generate_rust_workflow,
45             go => \&_generate_go_workflow,
46             ruby => \&_generate_ruby_workflow,
47             java => \&_generate_java_workflow,
48             cpp => \&_generate_cpp_workflow,
49             php => \&_generate_php_workflow,
50             docker => \&_generate_docker_workflow,
51             static => \&_generate_static_workflow,
52             );
53              
54 2 100       12 return undef unless exists $generators{$type};
55 1         4 return $generators{$type}->();
56             }
57              
58             =head2 list_workflow_types()
59              
60             Returns a hash of available workflow types and their descriptions.
61              
62             =cut
63              
64 1     1 1 2672 sub list_workflow_types() {
  1         2  
65             return (
66 1         16 node => 'Node.js/npm projects with testing and linting',
67             python => 'Python projects with pytest and coverage',
68             rust => 'Rust projects with cargo, clippy, and formatting',
69             go => 'Go projects with testing and race detection',
70             ruby => 'Ruby projects with bundler and rake',
71             perl => 'Perl projects with cpanm, prove, and coverage',
72             java => 'Java projects with Maven or Gradle',
73             cpp => 'C++ projects with CMake',
74             php => 'PHP projects with Composer and PHPUnit',
75             docker => 'Docker image build and push workflow',
76             static => 'Static site deployment to GitHub Pages',
77             );
78             }
79              
80             =head2 get_workflow_description($type)
81              
82             Get the description for a specific workflow type.
83              
84             =cut
85              
86 0     0 1 0 sub get_workflow_description($type) {
  0         0  
  0         0  
87 0         0 my %types = list_workflow_types();
88 0         0 return $types{$type};
89             }
90              
91             # Private workflow generators
92              
93 1     1   2 sub _generate_perl_workflow() {
  1         2  
94             # Try to detect requirements from project
95 1         7 my $reqs = detect_perl_requirements();
96              
97             # Use detected min version or default to 5.36
98 1   50     5 my $min_version = $reqs->{min_version} // '5.36';
99              
100             # Generate custom workflow with detected settings
101 1         13 return generate_custom_perl_workflow({
102             min_perl_version => $min_version,
103             max_perl_version => '5.40',
104             os => ['macos-latest', 'ubuntu-latest', 'windows-latest'],
105             enable_critic => 1,
106             enable_coverage => 1,
107             });
108             }
109              
110 0     0     sub _generate_node_workflow() {
  0            
111 0           return <<'YAML';
112             ---
113             name: Node.js CI
114              
115             'on':
116             push:
117             branches:
118             - main
119             - develop
120             pull_request:
121             branches:
122             - main
123             - develop
124              
125             concurrency:
126             group: ${{ github.workflow }}-${{ github.ref }}
127             cancel-in-progress: true
128              
129             permissions:
130             contents: read
131              
132             jobs:
133             test:
134             runs-on: ubuntu-latest
135             strategy:
136             matrix:
137             node-version:
138             - 18.x
139             - 20.x
140             - 22.x
141             steps:
142             - name: Checkout code
143             uses: actions/checkout@v6
144              
145             - name: Setup Node.js ${{ matrix.node-version }}
146             uses: actions/setup-node@v4
147             with:
148             node-version: ${{ matrix.node-version }}
149              
150             - name: Cache dependencies
151             uses: actions/cache@v5
152             with:
153             path: ~/.npm
154             key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
155             restore-keys: |
156             ${{ runner.os }}-node-
157              
158             - name: Install dependencies
159             run: npm ci
160              
161             - name: Run linter
162             run: npm run lint --if-present
163              
164             - name: Run tests
165             run: npm test
166              
167             - name: Build project
168             run: npm run build --if-present
169             YAML
170             }
171              
172 0     0     sub _generate_python_workflow() {
  0            
173 0           return <<'YAML';
174             ---
175             name: Python CI
176              
177             'on':
178             push:
179             branches:
180             - main
181             - develop
182             pull_request:
183             branches:
184             - main
185             - develop
186              
187             concurrency:
188             group: ${{ github.workflow }}-${{ github.ref }}
189             cancel-in-progress: true
190              
191             permissions:
192             contents: read
193              
194             jobs:
195             test:
196             runs-on: ubuntu-latest
197             strategy:
198             matrix:
199             python-version:
200             - '3.9'
201             - '3.10'
202             - '3.11'
203             - '3.12'
204             steps:
205             - name: Checkout code
206             uses: actions/checkout@v6
207              
208             - name: Set up Python ${{ matrix.python-version }}
209             uses: actions/setup-python@v5
210             with:
211             python-version: ${{ matrix.python-version }}
212              
213             - name: Cache pip packages
214             uses: actions/cache@v5
215             with:
216             path: ~/.cache/pip
217             key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
218             restore-keys: |
219             ${{ runner.os }}-pip-
220              
221             - name: Install dependencies
222             run: |
223             pip install -r requirements.txt
224             pip install pytest pytest-cov flake8
225              
226             - name: Lint with flake8
227             run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
228              
229             - name: Run tests with coverage
230             run: pytest --cov=. --cov-report=xml
231             YAML
232             }
233              
234 0     0     sub _generate_rust_workflow() {
  0            
235 0           return <<'YAML';
236             ---
237             name: Rust CI
238              
239             'on':
240             push:
241             branches:
242             - main
243             pull_request:
244             branches:
245             - main
246              
247             concurrency:
248             group: ${{ github.workflow }}-${{ github.ref }}
249             cancel-in-progress: true
250              
251             permissions:
252             contents: read
253              
254             jobs:
255             test:
256             runs-on: ubuntu-latest
257             steps:
258             - name: Checkout code
259             uses: actions/checkout@v6
260              
261             - name: Setup Rust
262             uses: dtolnay/rust-toolchain@stable
263              
264             - name: Cache cargo
265             uses: actions/cache@v5
266             with:
267             path: |
268             ~/.cargo/bin/
269             ~/.cargo/registry/index/
270             ~/.cargo/registry/cache/
271             ~/.cargo/git/db/
272             target/
273             key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
274              
275             - name: Check formatting
276             run: cargo fmt -- --check
277              
278             - name: Run clippy
279             run: cargo clippy -- -D warnings
280              
281             - name: Run tests
282             run: cargo test --verbose
283              
284             - name: Build release
285             run: cargo build --release --verbose
286             YAML
287             }
288              
289 0     0     sub _generate_go_workflow() {
  0            
290 0           return <<'YAML';
291             ---
292             name: Go CI
293              
294             'on':
295             push:
296             branches:
297             - main
298             pull_request:
299             branches:
300             - main
301              
302             concurrency:
303             group: ${{ github.workflow }}-${{ github.ref }}
304             cancel-in-progress: true
305              
306             permissions:
307             contents: read
308              
309             jobs:
310             test:
311             runs-on: ubuntu-latest
312             steps:
313             - name: Checkout code
314             uses: actions/checkout@v6
315              
316             - name: Setup Go
317             uses: actions/setup-go@v5
318             with:
319             go-version: '1.22'
320              
321             - name: Cache Go modules
322             uses: actions/cache@v5
323             with:
324             path: ~/go/pkg/mod
325             key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
326             restore-keys: |
327             ${{ runner.os }}-go-
328              
329             - name: Download dependencies
330             run: go mod download
331              
332             - name: Run go vet
333             run: go vet ./...
334              
335             - name: Run tests
336             run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
337              
338             - name: Build
339             run: go build -v ./...
340             YAML
341             }
342              
343 0     0     sub _generate_ruby_workflow() {
  0            
344 0           return <<'YAML';
345             ---
346             name: Ruby CI
347              
348             'on':
349             push:
350             branches:
351             - main
352             pull_request:
353             branches:
354             - main
355              
356             concurrency:
357             group: ${{ github.workflow }}-${{ github.ref }}
358             cancel-in-progress: true
359              
360             permissions:
361             contents: read
362              
363             jobs:
364             test:
365             runs-on: ubuntu-latest
366             strategy:
367             matrix:
368             ruby-version:
369             - '3.1'
370             - '3.2'
371             - '3.3'
372             steps:
373             - name: Checkout code
374             uses: actions/checkout@v6
375              
376             - name: Set up Ruby
377             uses: ruby/setup-ruby@v1
378             with:
379             ruby-version: ${{ matrix.ruby-version }}
380             bundler-cache: true
381              
382             - name: Run tests
383             run: bundle exec rake test
384             YAML
385             }
386              
387 0     0     sub _generate_docker_workflow() {
  0            
388 0           return <<'YAML';
389             ---
390             name: Docker Build
391              
392             'on':
393             push:
394             branches:
395             - main
396             tags:
397             - v*
398             pull_request:
399             branches:
400             - main
401              
402             concurrency:
403             group: ${{ github.workflow }}-${{ github.ref }}
404             cancel-in-progress: true
405              
406             permissions:
407             contents: read
408             packages: write
409              
410             jobs:
411             build:
412             runs-on: ubuntu-latest
413             steps:
414             - name: Checkout code
415             uses: actions/checkout@v6
416              
417             - name: Set up Docker Buildx
418             uses: docker/setup-buildx-action@v3
419              
420             - name: Log in to Docker Hub
421             uses: docker/login-action@v3
422             with:
423             username: ${{ secrets.DOCKER_USERNAME }}
424             password: ${{ secrets.DOCKER_PASSWORD }}
425             if: github.event_name != 'pull_request'
426              
427             - name: Extract metadata
428             id: meta
429             uses: docker/metadata-action@v5
430             with:
431             images: your-username/your-image
432              
433             - name: Build and push
434             uses: docker/build-push-action@v5
435             with:
436             context: .
437             push: ${{ github.event_name != 'pull_request' }}
438             tags: ${{ steps.meta.outputs.tags }}
439             labels: ${{ steps.meta.outputs.labels }}
440             cache-from: type=gha
441             cache-to: type=gha,mode=max
442             YAML
443             }
444              
445 0     0     sub _generate_static_workflow() {
  0            
446 0           return <<'YAML';
447             ---
448             name: Deploy Static Site
449              
450             'on':
451             push:
452             branches:
453             - main
454              
455             permissions:
456             contents: read
457             pages: write
458             id-token: write
459              
460             concurrency:
461             group: pages
462             cancel-in-progress: false
463              
464             jobs:
465             build:
466             runs-on: ubuntu-latest
467             steps:
468             - name: Checkout
469             uses: actions/checkout@v6
470              
471             - name: Setup Pages
472             uses: actions/configure-pages@v4
473              
474             - name: Build
475             run: echo "Add your build command here"
476              
477             - name: Upload artifact
478             uses: actions/upload-pages-artifact@v3
479             with:
480             path: ./public
481              
482             deploy:
483             environment:
484             name: github-pages
485             url: ${{ steps.deployment.outputs.page_url }}
486             runs-on: ubuntu-latest
487             needs: build
488             steps:
489             - name: Deploy to GitHub Pages
490             id: deployment
491             uses: actions/deploy-pages@v4
492             YAML
493             }
494              
495 0     0     sub _generate_java_workflow() {
  0            
496 0           return <<'YAML';
497             ---
498             name: Java CI
499              
500             'on':
501             push:
502             branches:
503             - main
504             - develop
505             pull_request:
506             branches:
507             - main
508             - develop
509              
510             concurrency:
511             group: ${{ github.workflow }}-${{ github.ref }}
512             cancel-in-progress: true
513              
514             permissions:
515             contents: read
516              
517             jobs:
518             test:
519             runs-on: ubuntu-latest
520             strategy:
521             matrix:
522             java-version:
523             - '11'
524             - '17'
525             - '21'
526             steps:
527             - name: Checkout code
528             uses: actions/checkout@v6
529              
530             - name: Set up JDK ${{ matrix.java-version }}
531             uses: actions/setup-java@v4
532             with:
533             java-version: ${{ matrix.java-version }}
534             distribution: 'temurin'
535              
536             - name: Cache Maven packages
537             uses: actions/cache@v5
538             with:
539             path: ~/.m2/repository
540             key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
541             restore-keys: |
542             ${{ runner.os }}-maven-
543              
544             - name: Build with Maven
545             run: mvn -B package --file pom.xml
546              
547             - name: Run tests
548             run: mvn -B test
549              
550             - name: Generate test report
551             if: always()
552             run: mvn surefire-report:report
553             YAML
554             }
555              
556 0     0     sub _generate_cpp_workflow() {
  0            
557 0           return <<'YAML';
558             ---
559             name: C++ CI
560              
561             'on':
562             push:
563             branches:
564             - main
565             pull_request:
566             branches:
567             - main
568              
569             concurrency:
570             group: ${{ github.workflow }}-${{ github.ref }}
571             cancel-in-progress: true
572              
573             permissions:
574             contents: read
575              
576             jobs:
577             test:
578             runs-on: ${{ matrix.os }}
579             strategy:
580             matrix:
581             os:
582             - ubuntu-latest
583             - macos-latest
584             - windows-latest
585             build-type:
586             - Debug
587             - Release
588             steps:
589             - name: Checkout code
590             uses: actions/checkout@v6
591              
592             - name: Install CMake
593             uses: lukka/get-cmake@latest
594              
595             - name: Cache build artifacts
596             uses: actions/cache@v5
597             with:
598             path: |
599             build
600             ~/.cache/ccache
601             key: ${{ runner.os }}-${{ matrix.build-type }}-${{ hashFiles('**/CMakeLists.txt') }}
602              
603             - name: Configure CMake
604             run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build-type }}
605              
606             - name: Build
607             run: cmake --build build --config ${{ matrix.build-type }}
608              
609             - name: Run tests
610             working-directory: build
611             run: ctest -C ${{ matrix.build-type }} --output-on-failure
612             YAML
613             }
614              
615 0     0     sub _generate_php_workflow() {
  0            
616 0           return <<'YAML';
617             ---
618             name: PHP CI
619              
620             'on':
621             push:
622             branches:
623             - main
624             - develop
625             pull_request:
626             branches:
627             - main
628             - develop
629              
630             concurrency:
631             group: ${{ github.workflow }}-${{ github.ref }}
632             cancel-in-progress: true
633              
634             permissions:
635             contents: read
636              
637             jobs:
638             test:
639             runs-on: ubuntu-latest
640             strategy:
641             matrix:
642             php-version:
643             - '8.1'
644             - '8.2'
645             - '8.3'
646             steps:
647             - name: Checkout code
648             uses: actions/checkout@v6
649              
650             - name: Setup PHP ${{ matrix.php-version }}
651             uses: shivammathur/setup-php@v2
652             with:
653             php-version: ${{ matrix.php-version }}
654             extensions: mbstring, xml, ctype, json
655             coverage: xdebug
656              
657             - name: Validate composer.json
658             run: composer validate --strict
659              
660             - name: Cache Composer packages
661             uses: actions/cache@v5
662             with:
663             path: vendor
664             key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
665             restore-keys: |
666             ${{ runner.os }}-php-
667              
668             - name: Install dependencies
669             run: composer install --prefer-dist --no-progress
670              
671             - name: Run PHPUnit tests
672             run: vendor/bin/phpunit --coverage-text
673              
674             - name: Run PHP CodeSniffer
675             run: vendor/bin/phpcs --standard=PSR12 src tests
676             continue-on-error: true
677             YAML
678             }
679              
680             =head1 AUTHOR
681              
682             Nigel Horne Enjh@nigelhorne.comE
683              
684             L
685              
686             =head1 LICENSE
687              
688             This is free software; you can redistribute it and/or modify it under
689             the same terms as the Perl 5 programming language system itself.
690              
691             =cut
692              
693             1;