@hybridlabor-api/aos 4.8.0 → 4.9.0
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/.agents/agents.md +1 -1
- package/.agents/nodes.json +1 -1
- package/.agents/vendor-manifest.json +23 -1
- package/.claude/agents/godmode-media-eventtech.md +1 -1
- package/.claude/hooks/conventional-commits.mjs +125 -0
- package/.claude/hooks/env-file-protection.mjs +105 -0
- package/.claude/hooks/go-gate.mjs +101 -81
- package/.claude/settings.json +13 -0
- package/.opencode/agents/godmode-media-eventtech.md +1 -1
- package/.opencode/plugins/bdb-aos.js +31 -4
- package/README.de.md +1 -1
- package/README.md +1 -1
- package/README.pt.md +1 -1
- package/THIRD_PARTY_NOTICES.md +88 -0
- package/docs/skills_table.md +1 -1
- package/installer.js +174 -46
- package/package.json +6 -2
- package/packages/aos-cli/README.md +80 -0
- package/packages/aos-cli/bin/aos-cli.mjs +134 -0
- package/packages/aos-cli/core-skills.json +12 -0
- package/packages/aos-cli/extensions/aos.ts +321 -0
- package/packages/aos-cli/package-lock.json +1923 -0
- package/packages/aos-cli/package.json +29 -0
- package/packages/aos-cli/scripts/check-theme.mjs +63 -0
- package/packages/aos-cli/themes/aos.json +97 -0
- package/scripts/build-plugin-manifest.mjs +131 -0
- package/scripts/validate-skills.mjs +81 -6
- package/skills/basic/ao-orchestrator/SKILL.md +116 -0
- package/skills/global_config/agenttrail/SKILL.md +6 -1
- package/skills/global_config/ask-tim/SKILL.md +4 -4
- package/skills/global_config/bash-script-generator/SKILL.md +201 -0
- package/skills/global_config/bash-script-generator/assets/templates/standard-template.sh +96 -0
- package/skills/global_config/bash-script-generator/docs/bash-scripting-guide.md +729 -0
- package/skills/global_config/bash-script-generator/docs/generation-best-practices.md +193 -0
- package/skills/global_config/bash-script-generator/docs/script-patterns.md +566 -0
- package/skills/global_config/bash-script-generator/docs/text-processing-guide.md +437 -0
- package/skills/global_config/bash-script-generator/examples/log-analyzer.sh +92 -0
- package/skills/global_config/bash-script-generator/scripts/generate_script_template.sh +123 -0
- package/skills/global_config/bash-script-generator/scripts/run_ci_checks.sh +172 -0
- package/skills/global_config/bash-script-generator/scripts/test_generator.sh +413 -0
- package/skills/global_config/bash-script-validator/SKILL.md +249 -0
- package/skills/global_config/bash-script-validator/docs/awk-reference.md +449 -0
- package/skills/global_config/bash-script-validator/docs/bash-reference.md +468 -0
- package/skills/global_config/bash-script-validator/docs/common-mistakes.md +623 -0
- package/skills/global_config/bash-script-validator/docs/grep-reference.md +395 -0
- package/skills/global_config/bash-script-validator/docs/regex-reference.md +391 -0
- package/skills/global_config/bash-script-validator/docs/sed-reference.md +454 -0
- package/skills/global_config/bash-script-validator/docs/shell-reference.md +463 -0
- package/skills/global_config/bash-script-validator/docs/shellcheck-reference.md +399 -0
- package/skills/global_config/bash-script-validator/examples/bad-bash.sh +55 -0
- package/skills/global_config/bash-script-validator/examples/bad-shell.sh +54 -0
- package/skills/global_config/bash-script-validator/examples/good-bash.sh +71 -0
- package/skills/global_config/bash-script-validator/examples/good-shell.sh +69 -0
- package/skills/global_config/bash-script-validator/scripts/run_ci_checks.sh +23 -0
- package/skills/global_config/bash-script-validator/scripts/shellcheck_wrapper.sh +174 -0
- package/skills/global_config/bash-script-validator/scripts/test_validate.sh +446 -0
- package/skills/global_config/bash-script-validator/scripts/validate.sh +512 -0
- package/skills/global_config/ci-pipeline/SKILL.md +135 -0
- package/skills/global_config/dispatching-parallel-agents/SKILL.md +170 -0
- package/skills/global_config/dockerfile-generator/SKILL.md +1038 -0
- package/skills/global_config/dockerfile-generator/examples/example.dockerignore +95 -0
- package/skills/global_config/dockerfile-generator/examples/golang-distroless.Dockerfile +34 -0
- package/skills/global_config/dockerfile-generator/examples/java-springboot.Dockerfile +45 -0
- package/skills/global_config/dockerfile-generator/examples/nextjs-production.Dockerfile +49 -0
- package/skills/global_config/dockerfile-generator/examples/nodejs-multistage.Dockerfile +55 -0
- package/skills/global_config/dockerfile-generator/examples/python-fastapi.Dockerfile +48 -0
- package/skills/global_config/dockerfile-generator/references/language_specific_guides.md +510 -0
- package/skills/global_config/dockerfile-generator/references/multistage_builds.md +570 -0
- package/skills/global_config/dockerfile-generator/references/optimization_patterns.md +492 -0
- package/skills/global_config/dockerfile-generator/references/security_best_practices.md +375 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_dockerignore.sh +199 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_golang.sh +172 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_java.sh +185 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_nodejs.sh +279 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_python.sh +218 -0
- package/skills/global_config/dockerfile-generator/scripts/test_generator.sh +210 -0
- package/skills/global_config/dockerfile-validator/SKILL.md +300 -0
- package/skills/global_config/dockerfile-validator/examples/.dockerignore.example +34 -0
- package/skills/global_config/dockerfile-validator/examples/bad-example.Dockerfile +37 -0
- package/skills/global_config/dockerfile-validator/examples/golang-distroless.Dockerfile +50 -0
- package/skills/global_config/dockerfile-validator/examples/good-example.Dockerfile +52 -0
- package/skills/global_config/dockerfile-validator/examples/python-optimized.Dockerfile +53 -0
- package/skills/global_config/dockerfile-validator/examples/security-issues.Dockerfile +42 -0
- package/skills/global_config/dockerfile-validator/references/docker_best_practices.md +348 -0
- package/skills/global_config/dockerfile-validator/references/optimization_guide.md +473 -0
- package/skills/global_config/dockerfile-validator/references/security_checklist.md +208 -0
- package/skills/global_config/dockerfile-validator/scripts/dockerfile-validate.sh +699 -0
- package/skills/global_config/dockerfile-validator/scripts/test_validate.sh +35 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn-lock-read.Dockerfile +6 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn.Dockerfile +6 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/from-platform-nonroot.Dockerfile +7 -0
- package/skills/global_config/dockerfile-validator/tests/test_regression.sh +164 -0
- package/skills/global_config/finishing-a-development-branch/SKILL.md +228 -0
- package/skills/global_config/github-actions-generator/SKILL.md +353 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/composite/action.yml +82 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/Dockerfile +25 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/action.yml +42 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/entrypoint.sh +27 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/action.yml +33 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/index.js +50 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/package.json +27 -0
- package/skills/global_config/github-actions-generator/assets/templates/workflow/basic_workflow.yml +242 -0
- package/skills/global_config/github-actions-generator/assets/templates/workflow/reusable_workflow.yml +106 -0
- package/skills/global_config/github-actions-generator/examples/README.md +147 -0
- package/skills/global_config/github-actions-generator/examples/actions/setup-node-cached/action.yml +93 -0
- package/skills/global_config/github-actions-generator/examples/caching/docker-buildkit.yml +256 -0
- package/skills/global_config/github-actions-generator/examples/security/dependency-review.yml +62 -0
- package/skills/global_config/github-actions-generator/examples/security/sbom-attestation.yml +119 -0
- package/skills/global_config/github-actions-generator/examples/triggers/chatops-commands.yml +475 -0
- package/skills/global_config/github-actions-generator/examples/triggers/repository-dispatch.yml +418 -0
- package/skills/global_config/github-actions-generator/examples/triggers/workflow-orchestration.yml +404 -0
- package/skills/global_config/github-actions-generator/examples/workflows/docker-build-push.yml +68 -0
- package/skills/global_config/github-actions-generator/examples/workflows/go-ci.yml +161 -0
- package/skills/global_config/github-actions-generator/examples/workflows/monorepo-ci.yml +340 -0
- package/skills/global_config/github-actions-generator/examples/workflows/multi-environment-deploy.yml +406 -0
- package/skills/global_config/github-actions-generator/examples/workflows/nodejs-ci.yml +122 -0
- package/skills/global_config/github-actions-generator/examples/workflows/python-ci.yml +157 -0
- package/skills/global_config/github-actions-generator/examples/workflows/scheduled-tasks.yml +376 -0
- package/skills/global_config/github-actions-generator/references/advanced-triggers.md +917 -0
- package/skills/global_config/github-actions-generator/references/best-practices.md +755 -0
- package/skills/global_config/github-actions-generator/references/common-actions.md +715 -0
- package/skills/global_config/github-actions-generator/references/custom-actions.md +320 -0
- package/skills/global_config/github-actions-generator/references/expressions-and-contexts.md +688 -0
- package/skills/global_config/github-actions-generator/references/modern-features.md +421 -0
- package/skills/global_config/github-actions-generator/scripts/test_generator.sh +344 -0
- package/skills/global_config/github-actions-templates/SKILL.md +7 -0
- package/skills/global_config/github-actions-validator/SKILL.md +576 -0
- package/skills/global_config/github-actions-validator/examples/README.md +88 -0
- package/skills/global_config/github-actions-validator/examples/outdated-versions.yml +76 -0
- package/skills/global_config/github-actions-validator/examples/valid-ci.yml +79 -0
- package/skills/global_config/github-actions-validator/examples/with-errors.yml +47 -0
- package/skills/global_config/github-actions-validator/references/act_usage.md +233 -0
- package/skills/global_config/github-actions-validator/references/action_versions.md +122 -0
- package/skills/global_config/github-actions-validator/references/actionlint_usage.md +343 -0
- package/skills/global_config/github-actions-validator/references/common_errors.md +512 -0
- package/skills/global_config/github-actions-validator/references/modern_features.md +384 -0
- package/skills/global_config/github-actions-validator/references/runners.md +317 -0
- package/skills/global_config/github-actions-validator/scripts/install_tools.sh +113 -0
- package/skills/global_config/github-actions-validator/scripts/validate_workflow.sh +910 -0
- package/skills/global_config/github-actions-validator/tests/test_validate_workflow.sh +237 -0
- package/skills/global_config/makefile-generator/SKILL.md +614 -0
- package/skills/global_config/makefile-generator/assets/templates/.gitkeep +1 -0
- package/skills/global_config/makefile-generator/docs/makefile-structure.md +530 -0
- package/skills/global_config/makefile-generator/docs/optimization-guide.md +784 -0
- package/skills/global_config/makefile-generator/docs/patterns-guide.md +642 -0
- package/skills/global_config/makefile-generator/docs/security-guide.md +361 -0
- package/skills/global_config/makefile-generator/docs/targets-guide.md +642 -0
- package/skills/global_config/makefile-generator/docs/variables-guide.md +596 -0
- package/skills/global_config/makefile-generator/scripts/add_standard_targets.sh +539 -0
- package/skills/global_config/makefile-generator/scripts/generate_makefile_template.sh +690 -0
- package/skills/global_config/makefile-generator/test/test_helper_scripts.sh +190 -0
- package/skills/global_config/makefile-validator/SKILL.md +244 -0
- package/skills/global_config/makefile-validator/docs/bake-tool.md +1000 -0
- package/skills/global_config/makefile-validator/docs/best-practices.md +858 -0
- package/skills/global_config/makefile-validator/docs/common-mistakes.md +944 -0
- package/skills/global_config/makefile-validator/examples/bad-makefile.mk +77 -0
- package/skills/global_config/makefile-validator/examples/good-makefile.mk +103 -0
- package/skills/global_config/makefile-validator/scripts/test_validate.sh +382 -0
- package/skills/global_config/makefile-validator/scripts/validate_makefile.sh +712 -0
- package/skills/global_config/{MCP_Manage → mcp-manage}/SKILL.md +3 -3
- package/skills/global_config/requesting-code-review/SKILL.md +98 -0
- package/skills/global_config/requesting-code-review/code-reviewer.md +198 -0
- package/skills/global_config/using-git-worktrees/SKILL.md +170 -0
- package/skills/global_config/verification-before-completion/SKILL.md +123 -0
- package/skills/global_config/writing-plans/SKILL.md +126 -46
- package/skills/global_config/writing-plans-legacy/SKILL.md +139 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
# Multi-Environment Deployment Example
|
|
2
|
+
# Demonstrates: Environment protection rules, approval gates, deployment strategies, rollback
|
|
3
|
+
|
|
4
|
+
name: Multi-Environment Deployment
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main, develop, staging]
|
|
9
|
+
release:
|
|
10
|
+
types: [published]
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
inputs:
|
|
13
|
+
environment:
|
|
14
|
+
description: 'Target environment'
|
|
15
|
+
required: true
|
|
16
|
+
type: choice
|
|
17
|
+
options:
|
|
18
|
+
- dev
|
|
19
|
+
- staging
|
|
20
|
+
- production
|
|
21
|
+
default: 'dev'
|
|
22
|
+
skip-tests:
|
|
23
|
+
description: 'Skip pre-deployment tests'
|
|
24
|
+
required: false
|
|
25
|
+
type: boolean
|
|
26
|
+
default: false
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
deployments: write
|
|
31
|
+
id-token: write # For OIDC authentication
|
|
32
|
+
|
|
33
|
+
concurrency:
|
|
34
|
+
# Prevent concurrent deployments to same environment
|
|
35
|
+
group: deploy-${{ github.event.inputs.environment || (github.ref == 'refs/heads/main' && 'production' || github.ref == 'refs/heads/staging' && 'staging' || 'dev') }}
|
|
36
|
+
cancel-in-progress: false
|
|
37
|
+
|
|
38
|
+
env:
|
|
39
|
+
NODE_VERSION: '24'
|
|
40
|
+
AWS_REGION: 'us-east-1'
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
# Determine deployment environment based on trigger
|
|
44
|
+
determine-environment:
|
|
45
|
+
name: Determine Deployment Environment
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
timeout-minutes: 5
|
|
48
|
+
|
|
49
|
+
outputs:
|
|
50
|
+
environment: ${{ steps.determine.outputs.environment }}
|
|
51
|
+
deploy: ${{ steps.determine.outputs.deploy }}
|
|
52
|
+
|
|
53
|
+
steps:
|
|
54
|
+
- name: Determine environment
|
|
55
|
+
id: determine
|
|
56
|
+
run: |
|
|
57
|
+
# Manual dispatch takes precedence
|
|
58
|
+
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
|
59
|
+
echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
|
|
60
|
+
echo "deploy=true" >> $GITHUB_OUTPUT
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Release event goes to production
|
|
65
|
+
if [ "${{ github.event_name }}" == "release" ]; then
|
|
66
|
+
echo "environment=production" >> $GITHUB_OUTPUT
|
|
67
|
+
echo "deploy=true" >> $GITHUB_OUTPUT
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Branch-based deployment
|
|
72
|
+
case "${{ github.ref }}" in
|
|
73
|
+
refs/heads/main)
|
|
74
|
+
echo "environment=production" >> $GITHUB_OUTPUT
|
|
75
|
+
echo "deploy=true" >> $GITHUB_OUTPUT
|
|
76
|
+
;;
|
|
77
|
+
refs/heads/staging)
|
|
78
|
+
echo "environment=staging" >> $GITHUB_OUTPUT
|
|
79
|
+
echo "deploy=true" >> $GITHUB_OUTPUT
|
|
80
|
+
;;
|
|
81
|
+
refs/heads/develop)
|
|
82
|
+
echo "environment=dev" >> $GITHUB_OUTPUT
|
|
83
|
+
echo "deploy=true" >> $GITHUB_OUTPUT
|
|
84
|
+
;;
|
|
85
|
+
*)
|
|
86
|
+
echo "environment=dev" >> $GITHUB_OUTPUT
|
|
87
|
+
echo "deploy=false" >> $GITHUB_OUTPUT
|
|
88
|
+
;;
|
|
89
|
+
esac
|
|
90
|
+
|
|
91
|
+
# Build application
|
|
92
|
+
build:
|
|
93
|
+
name: Build Application
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
needs: [determine-environment]
|
|
96
|
+
if: needs.determine-environment.outputs.deploy == 'true'
|
|
97
|
+
timeout-minutes: 20
|
|
98
|
+
|
|
99
|
+
outputs:
|
|
100
|
+
artifact-name: ${{ steps.build.outputs.artifact-name }}
|
|
101
|
+
|
|
102
|
+
steps:
|
|
103
|
+
- name: Checkout code
|
|
104
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
105
|
+
|
|
106
|
+
- name: Setup Node.js
|
|
107
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
108
|
+
with:
|
|
109
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
110
|
+
cache: 'npm'
|
|
111
|
+
|
|
112
|
+
- name: Install dependencies
|
|
113
|
+
run: npm ci
|
|
114
|
+
|
|
115
|
+
- name: Run tests
|
|
116
|
+
if: github.event.inputs.skip-tests != 'true'
|
|
117
|
+
run: npm test
|
|
118
|
+
|
|
119
|
+
- name: Build application
|
|
120
|
+
id: build
|
|
121
|
+
env:
|
|
122
|
+
NODE_ENV: production
|
|
123
|
+
ENVIRONMENT: ${{ needs.determine-environment.outputs.environment }}
|
|
124
|
+
run: |
|
|
125
|
+
npm run build
|
|
126
|
+
ARTIFACT_NAME="build-${{ github.sha }}"
|
|
127
|
+
echo "artifact-name=$ARTIFACT_NAME" >> $GITHUB_OUTPUT
|
|
128
|
+
|
|
129
|
+
- name: Upload build artifacts
|
|
130
|
+
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
|
131
|
+
with:
|
|
132
|
+
name: ${{ steps.build.outputs.artifact-name }}
|
|
133
|
+
path: dist/
|
|
134
|
+
retention-days: 30
|
|
135
|
+
|
|
136
|
+
# Deploy to Development
|
|
137
|
+
deploy-dev:
|
|
138
|
+
name: Deploy to Development
|
|
139
|
+
runs-on: ubuntu-latest
|
|
140
|
+
needs: [determine-environment, build]
|
|
141
|
+
if: needs.determine-environment.outputs.environment == 'dev' && needs.determine-environment.outputs.deploy == 'true'
|
|
142
|
+
timeout-minutes: 15
|
|
143
|
+
|
|
144
|
+
environment:
|
|
145
|
+
name: dev
|
|
146
|
+
url: https://dev.example.com
|
|
147
|
+
|
|
148
|
+
steps:
|
|
149
|
+
- name: Download build artifacts
|
|
150
|
+
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
|
151
|
+
with:
|
|
152
|
+
name: ${{ needs.build.outputs.artifact-name }}
|
|
153
|
+
path: dist/
|
|
154
|
+
|
|
155
|
+
- name: Configure AWS credentials
|
|
156
|
+
uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1
|
|
157
|
+
with:
|
|
158
|
+
role-to-assume: ${{ secrets.AWS_ROLE_DEV }}
|
|
159
|
+
role-session-name: github-actions-dev-deploy
|
|
160
|
+
aws-region: ${{ env.AWS_REGION }}
|
|
161
|
+
|
|
162
|
+
- name: Deploy to S3
|
|
163
|
+
run: |
|
|
164
|
+
aws s3 sync dist/ s3://${{ secrets.S3_BUCKET_DEV }} --delete
|
|
165
|
+
|
|
166
|
+
- name: Invalidate CloudFront cache
|
|
167
|
+
run: |
|
|
168
|
+
aws cloudfront create-invalidation \
|
|
169
|
+
--distribution-id ${{ secrets.CLOUDFRONT_DIST_DEV }} \
|
|
170
|
+
--paths "/*"
|
|
171
|
+
|
|
172
|
+
- name: Run smoke tests
|
|
173
|
+
run: |
|
|
174
|
+
echo "Running smoke tests against dev environment..."
|
|
175
|
+
curl -f https://dev.example.com/health || exit 1
|
|
176
|
+
echo "✅ Smoke tests passed"
|
|
177
|
+
|
|
178
|
+
# Deploy to Staging (with approval requirement)
|
|
179
|
+
deploy-staging:
|
|
180
|
+
name: Deploy to Staging
|
|
181
|
+
runs-on: ubuntu-latest
|
|
182
|
+
needs: [determine-environment, build]
|
|
183
|
+
if: needs.determine-environment.outputs.environment == 'staging' && needs.determine-environment.outputs.deploy == 'true'
|
|
184
|
+
timeout-minutes: 20
|
|
185
|
+
|
|
186
|
+
environment:
|
|
187
|
+
name: staging
|
|
188
|
+
url: https://staging.example.com
|
|
189
|
+
|
|
190
|
+
steps:
|
|
191
|
+
- name: Download build artifacts
|
|
192
|
+
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
|
193
|
+
with:
|
|
194
|
+
name: ${{ needs.build.outputs.artifact-name }}
|
|
195
|
+
path: dist/
|
|
196
|
+
|
|
197
|
+
- name: Configure AWS credentials
|
|
198
|
+
uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1
|
|
199
|
+
with:
|
|
200
|
+
role-to-assume: ${{ secrets.AWS_ROLE_STAGING }}
|
|
201
|
+
role-session-name: github-actions-staging-deploy
|
|
202
|
+
aws-region: ${{ env.AWS_REGION }}
|
|
203
|
+
|
|
204
|
+
- name: Deploy to S3
|
|
205
|
+
run: |
|
|
206
|
+
aws s3 sync dist/ s3://${{ secrets.S3_BUCKET_STAGING }} --delete
|
|
207
|
+
|
|
208
|
+
- name: Invalidate CloudFront cache
|
|
209
|
+
run: |
|
|
210
|
+
aws cloudfront create-invalidation \
|
|
211
|
+
--distribution-id ${{ secrets.CLOUDFRONT_DIST_STAGING }} \
|
|
212
|
+
--paths "/*"
|
|
213
|
+
|
|
214
|
+
- name: Run smoke tests
|
|
215
|
+
run: |
|
|
216
|
+
echo "Running smoke tests against staging environment..."
|
|
217
|
+
curl -f https://staging.example.com/health || exit 1
|
|
218
|
+
echo "✅ Smoke tests passed"
|
|
219
|
+
|
|
220
|
+
- name: Run integration tests
|
|
221
|
+
run: |
|
|
222
|
+
echo "Running integration tests..."
|
|
223
|
+
# Add your integration test commands here
|
|
224
|
+
echo "✅ Integration tests passed"
|
|
225
|
+
|
|
226
|
+
# Deploy to Production (with approval requirement and blue-green strategy)
|
|
227
|
+
deploy-production:
|
|
228
|
+
name: Deploy to Production
|
|
229
|
+
runs-on: ubuntu-latest
|
|
230
|
+
needs: [determine-environment, build]
|
|
231
|
+
if: needs.determine-environment.outputs.environment == 'production' && needs.determine-environment.outputs.deploy == 'true'
|
|
232
|
+
timeout-minutes: 30
|
|
233
|
+
|
|
234
|
+
environment:
|
|
235
|
+
name: production
|
|
236
|
+
url: https://example.com
|
|
237
|
+
|
|
238
|
+
steps:
|
|
239
|
+
- name: Download build artifacts
|
|
240
|
+
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
|
241
|
+
with:
|
|
242
|
+
name: ${{ needs.build.outputs.artifact-name }}
|
|
243
|
+
path: dist/
|
|
244
|
+
|
|
245
|
+
- name: Configure AWS credentials
|
|
246
|
+
uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1
|
|
247
|
+
with:
|
|
248
|
+
role-to-assume: ${{ secrets.AWS_ROLE_PRODUCTION }}
|
|
249
|
+
role-session-name: github-actions-prod-deploy
|
|
250
|
+
aws-region: ${{ env.AWS_REGION }}
|
|
251
|
+
|
|
252
|
+
- name: Backup current deployment
|
|
253
|
+
id: backup
|
|
254
|
+
run: |
|
|
255
|
+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
256
|
+
BACKUP_BUCKET="s3://${{ secrets.S3_BACKUP_BUCKET }}/backups/${TIMESTAMP}"
|
|
257
|
+
|
|
258
|
+
echo "Creating backup at ${BACKUP_BUCKET}"
|
|
259
|
+
aws s3 sync s3://${{ secrets.S3_BUCKET_PRODUCTION }} ${BACKUP_BUCKET}
|
|
260
|
+
echo "backup-path=${BACKUP_BUCKET}" >> $GITHUB_OUTPUT
|
|
261
|
+
|
|
262
|
+
- name: Deploy to production (Blue-Green)
|
|
263
|
+
id: deploy
|
|
264
|
+
run: |
|
|
265
|
+
# Deploy to blue environment first
|
|
266
|
+
echo "Deploying to blue environment..."
|
|
267
|
+
aws s3 sync dist/ s3://${{ secrets.S3_BUCKET_PRODUCTION_BLUE }} --delete
|
|
268
|
+
|
|
269
|
+
# Update CloudFront to point to blue environment
|
|
270
|
+
echo "Switching traffic to blue environment..."
|
|
271
|
+
aws cloudfront create-invalidation \
|
|
272
|
+
--distribution-id ${{ secrets.CLOUDFRONT_DIST_PRODUCTION }} \
|
|
273
|
+
--paths "/*"
|
|
274
|
+
|
|
275
|
+
- name: Run smoke tests (production)
|
|
276
|
+
id: smoke-tests
|
|
277
|
+
run: |
|
|
278
|
+
echo "Running smoke tests against production..."
|
|
279
|
+
|
|
280
|
+
# Wait for CloudFront to propagate
|
|
281
|
+
sleep 30
|
|
282
|
+
|
|
283
|
+
# Basic health check
|
|
284
|
+
curl -f https://example.com/health || exit 1
|
|
285
|
+
|
|
286
|
+
# Check critical endpoints
|
|
287
|
+
curl -f https://example.com/api/status || exit 1
|
|
288
|
+
|
|
289
|
+
echo "✅ Production smoke tests passed"
|
|
290
|
+
|
|
291
|
+
- name: Monitor metrics
|
|
292
|
+
run: |
|
|
293
|
+
echo "Monitoring deployment metrics for 5 minutes..."
|
|
294
|
+
# In real scenario, this would monitor CloudWatch metrics
|
|
295
|
+
# Error rates, latency, etc.
|
|
296
|
+
sleep 10
|
|
297
|
+
echo "✅ Metrics look good"
|
|
298
|
+
|
|
299
|
+
- name: Rollback on failure
|
|
300
|
+
if: failure()
|
|
301
|
+
run: |
|
|
302
|
+
echo "❌ Deployment failed, initiating rollback..."
|
|
303
|
+
|
|
304
|
+
# Restore from backup
|
|
305
|
+
aws s3 sync ${{ steps.backup.outputs.backup-path }} s3://${{ secrets.S3_BUCKET_PRODUCTION }} --delete
|
|
306
|
+
|
|
307
|
+
# Invalidate cache
|
|
308
|
+
aws cloudfront create-invalidation \
|
|
309
|
+
--distribution-id ${{ secrets.CLOUDFRONT_DIST_PRODUCTION }} \
|
|
310
|
+
--paths "/*"
|
|
311
|
+
|
|
312
|
+
echo "🔄 Rollback completed"
|
|
313
|
+
exit 1
|
|
314
|
+
|
|
315
|
+
# Deployment verification
|
|
316
|
+
post-deployment-verification:
|
|
317
|
+
name: Post-Deployment Verification
|
|
318
|
+
runs-on: ubuntu-latest
|
|
319
|
+
needs: [determine-environment, deploy-dev, deploy-staging, deploy-production]
|
|
320
|
+
if: always() && needs.determine-environment.outputs.deploy == 'true'
|
|
321
|
+
timeout-minutes: 10
|
|
322
|
+
|
|
323
|
+
steps:
|
|
324
|
+
- name: Verify deployment
|
|
325
|
+
env:
|
|
326
|
+
ENVIRONMENT: ${{ needs.determine-environment.outputs.environment }}
|
|
327
|
+
run: |
|
|
328
|
+
case "${ENVIRONMENT}" in
|
|
329
|
+
dev)
|
|
330
|
+
URL="https://dev.example.com"
|
|
331
|
+
;;
|
|
332
|
+
staging)
|
|
333
|
+
URL="https://staging.example.com"
|
|
334
|
+
;;
|
|
335
|
+
production)
|
|
336
|
+
URL="https://example.com"
|
|
337
|
+
;;
|
|
338
|
+
esac
|
|
339
|
+
|
|
340
|
+
echo "Verifying deployment at ${URL}"
|
|
341
|
+
|
|
342
|
+
# Extended health check
|
|
343
|
+
for i in {1..5}; do
|
|
344
|
+
if curl -f "${URL}/health"; then
|
|
345
|
+
echo "✅ Health check passed (attempt $i)"
|
|
346
|
+
break
|
|
347
|
+
else
|
|
348
|
+
echo "⚠️ Health check failed (attempt $i/5)"
|
|
349
|
+
if [ $i -eq 5 ]; then
|
|
350
|
+
echo "❌ Health check failed after 5 attempts"
|
|
351
|
+
exit 1
|
|
352
|
+
fi
|
|
353
|
+
sleep 10
|
|
354
|
+
fi
|
|
355
|
+
done
|
|
356
|
+
|
|
357
|
+
- name: Update deployment status
|
|
358
|
+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
|
359
|
+
with:
|
|
360
|
+
script: |
|
|
361
|
+
const environment = '${{ needs.determine-environment.outputs.environment }}';
|
|
362
|
+
const devResult = '${{ needs.deploy-dev.result }}';
|
|
363
|
+
const stagingResult = '${{ needs.deploy-staging.result }}';
|
|
364
|
+
const productionResult = '${{ needs.deploy-production.result }}';
|
|
365
|
+
|
|
366
|
+
let deploymentResult = 'skipped';
|
|
367
|
+
if (environment === 'dev') deploymentResult = devResult;
|
|
368
|
+
if (environment === 'staging') deploymentResult = stagingResult;
|
|
369
|
+
if (environment === 'production') deploymentResult = productionResult;
|
|
370
|
+
|
|
371
|
+
const status = deploymentResult === 'success' ? '✅' : '❌';
|
|
372
|
+
|
|
373
|
+
core.summary
|
|
374
|
+
.addHeading(`${status} Deployment to ${environment.toUpperCase()}`)
|
|
375
|
+
.addRaw(`**Environment:** ${environment}`)
|
|
376
|
+
.addBreak()
|
|
377
|
+
.addRaw(`**Status:** ${deploymentResult}`)
|
|
378
|
+
.addBreak()
|
|
379
|
+
.addRaw(`**Commit:** ${context.sha}`)
|
|
380
|
+
.addBreak()
|
|
381
|
+
.addRaw(`**Actor:** ${context.actor}`)
|
|
382
|
+
.write();
|
|
383
|
+
|
|
384
|
+
# Notify stakeholders
|
|
385
|
+
notify:
|
|
386
|
+
name: Notify Stakeholders
|
|
387
|
+
runs-on: ubuntu-latest
|
|
388
|
+
needs: [determine-environment, post-deployment-verification]
|
|
389
|
+
if: always() && needs.determine-environment.outputs.deploy == 'true'
|
|
390
|
+
timeout-minutes: 5
|
|
391
|
+
|
|
392
|
+
steps:
|
|
393
|
+
- name: Send notification
|
|
394
|
+
env:
|
|
395
|
+
ENVIRONMENT: ${{ needs.determine-environment.outputs.environment }}
|
|
396
|
+
VERIFICATION_RESULT: ${{ needs.post-deployment-verification.result }}
|
|
397
|
+
run: |
|
|
398
|
+
STATUS_EMOJI="✅"
|
|
399
|
+
if [ "$VERIFICATION_RESULT" != "success" ]; then
|
|
400
|
+
STATUS_EMOJI="❌"
|
|
401
|
+
fi
|
|
402
|
+
|
|
403
|
+
echo "${STATUS_EMOJI} Deployment to ${ENVIRONMENT} completed with status: ${VERIFICATION_RESULT}"
|
|
404
|
+
|
|
405
|
+
# In real scenario, send to Slack/Teams/Email
|
|
406
|
+
# Example: curl -X POST $SLACK_WEBHOOK -d "{\"text\":\"${STATUS_EMOJI} Deployment to ${ENVIRONMENT}: ${VERIFICATION_RESULT}\"}"
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Node.js CI Pipeline Example
|
|
2
|
+
# Demonstrates: Matrix testing, caching, artifact uploading, code coverage
|
|
3
|
+
|
|
4
|
+
name: Node.js CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main, develop]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [main]
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
env:
|
|
21
|
+
NODE_VERSION: '24'
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
lint:
|
|
25
|
+
name: Lint Code
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
timeout-minutes: 10
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- name: Checkout code
|
|
31
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
32
|
+
|
|
33
|
+
- name: Setup Node.js
|
|
34
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
35
|
+
with:
|
|
36
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
37
|
+
cache: 'npm'
|
|
38
|
+
|
|
39
|
+
- name: Install dependencies
|
|
40
|
+
run: npm ci
|
|
41
|
+
|
|
42
|
+
- name: Run ESLint
|
|
43
|
+
run: npm run lint
|
|
44
|
+
|
|
45
|
+
test:
|
|
46
|
+
name: Test on Node ${{ matrix.node-version }}
|
|
47
|
+
runs-on: ${{ matrix.os }}
|
|
48
|
+
timeout-minutes: 30
|
|
49
|
+
|
|
50
|
+
permissions:
|
|
51
|
+
contents: read
|
|
52
|
+
checks: write
|
|
53
|
+
|
|
54
|
+
strategy:
|
|
55
|
+
matrix:
|
|
56
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
57
|
+
node-version: [20, 22, 24]
|
|
58
|
+
exclude:
|
|
59
|
+
- os: macos-latest
|
|
60
|
+
node-version: 20
|
|
61
|
+
fail-fast: false
|
|
62
|
+
|
|
63
|
+
steps:
|
|
64
|
+
- name: Checkout code
|
|
65
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
66
|
+
|
|
67
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
68
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
69
|
+
with:
|
|
70
|
+
node-version: ${{ matrix.node-version }}
|
|
71
|
+
cache: 'npm'
|
|
72
|
+
|
|
73
|
+
- name: Install dependencies
|
|
74
|
+
run: npm ci
|
|
75
|
+
|
|
76
|
+
- name: Run tests
|
|
77
|
+
run: npm test
|
|
78
|
+
|
|
79
|
+
- name: Upload test results
|
|
80
|
+
if: always()
|
|
81
|
+
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
|
82
|
+
with:
|
|
83
|
+
name: test-results-${{ matrix.os }}-${{ matrix.node-version }}
|
|
84
|
+
path: test-results/
|
|
85
|
+
retention-days: 7
|
|
86
|
+
|
|
87
|
+
- name: Upload coverage
|
|
88
|
+
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 24
|
|
89
|
+
uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # v4.0.1
|
|
90
|
+
with:
|
|
91
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
92
|
+
fail_ci_if_error: true
|
|
93
|
+
|
|
94
|
+
build:
|
|
95
|
+
name: Build Application
|
|
96
|
+
needs: [lint, test]
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
timeout-minutes: 15
|
|
99
|
+
|
|
100
|
+
steps:
|
|
101
|
+
- name: Checkout code
|
|
102
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
103
|
+
|
|
104
|
+
- name: Setup Node.js
|
|
105
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
106
|
+
with:
|
|
107
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
108
|
+
cache: 'npm'
|
|
109
|
+
|
|
110
|
+
- name: Install dependencies
|
|
111
|
+
run: npm ci
|
|
112
|
+
|
|
113
|
+
- name: Build application
|
|
114
|
+
run: npm run build
|
|
115
|
+
|
|
116
|
+
- name: Upload build artifacts
|
|
117
|
+
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
|
118
|
+
with:
|
|
119
|
+
name: build-${{ github.sha }}
|
|
120
|
+
path: dist/
|
|
121
|
+
retention-days: 7
|
|
122
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Python CI Pipeline Example
|
|
2
|
+
# Demonstrates: Matrix testing, caching, linting, testing, code coverage
|
|
3
|
+
|
|
4
|
+
name: Python CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main, develop]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [main]
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
env:
|
|
21
|
+
PYTHON_VERSION: '3.12'
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
lint:
|
|
25
|
+
name: Lint Code
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
timeout-minutes: 10
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- name: Checkout code
|
|
31
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
32
|
+
|
|
33
|
+
- name: Setup Python
|
|
34
|
+
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
37
|
+
cache: 'pip'
|
|
38
|
+
|
|
39
|
+
- name: Install linting tools
|
|
40
|
+
run: |
|
|
41
|
+
python -m pip install --upgrade pip
|
|
42
|
+
pip install black flake8 mypy pylint isort
|
|
43
|
+
|
|
44
|
+
- name: Run Black
|
|
45
|
+
run: black --check .
|
|
46
|
+
|
|
47
|
+
- name: Run isort
|
|
48
|
+
run: isort --check-only .
|
|
49
|
+
|
|
50
|
+
- name: Run Flake8
|
|
51
|
+
run: flake8 .
|
|
52
|
+
|
|
53
|
+
- name: Run Pylint
|
|
54
|
+
run: |
|
|
55
|
+
FILES=$(git ls-files '*.py')
|
|
56
|
+
if [ -z "$FILES" ]; then
|
|
57
|
+
echo "No Python files found to lint"
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
pylint $FILES
|
|
61
|
+
|
|
62
|
+
- name: Run MyPy
|
|
63
|
+
run: mypy .
|
|
64
|
+
|
|
65
|
+
test:
|
|
66
|
+
name: Test on Python ${{ matrix.python-version }}
|
|
67
|
+
runs-on: ${{ matrix.os }}
|
|
68
|
+
timeout-minutes: 30
|
|
69
|
+
|
|
70
|
+
permissions:
|
|
71
|
+
contents: read
|
|
72
|
+
checks: write
|
|
73
|
+
|
|
74
|
+
strategy:
|
|
75
|
+
matrix:
|
|
76
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
77
|
+
python-version: ['3.10', '3.11', '3.12']
|
|
78
|
+
exclude:
|
|
79
|
+
- os: macos-latest
|
|
80
|
+
python-version: '3.10'
|
|
81
|
+
fail-fast: false
|
|
82
|
+
|
|
83
|
+
steps:
|
|
84
|
+
- name: Checkout code
|
|
85
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
86
|
+
|
|
87
|
+
- name: Setup Python ${{ matrix.python-version }}
|
|
88
|
+
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
|
|
89
|
+
with:
|
|
90
|
+
python-version: ${{ matrix.python-version }}
|
|
91
|
+
cache: 'pip'
|
|
92
|
+
cache-dependency-path: |
|
|
93
|
+
requirements.txt
|
|
94
|
+
requirements-dev.txt
|
|
95
|
+
|
|
96
|
+
- name: Install dependencies
|
|
97
|
+
run: |
|
|
98
|
+
python -m pip install --upgrade pip
|
|
99
|
+
pip install -r requirements.txt
|
|
100
|
+
pip install -r requirements-dev.txt
|
|
101
|
+
|
|
102
|
+
- name: Run tests with pytest
|
|
103
|
+
run: |
|
|
104
|
+
pytest --cov=. --cov-report=xml --cov-report=html --junitxml=junit.xml
|
|
105
|
+
|
|
106
|
+
- name: Upload test results
|
|
107
|
+
if: always()
|
|
108
|
+
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
|
109
|
+
with:
|
|
110
|
+
name: test-results-${{ matrix.os }}-py${{ matrix.python-version }}
|
|
111
|
+
path: |
|
|
112
|
+
junit.xml
|
|
113
|
+
htmlcov/
|
|
114
|
+
retention-days: 7
|
|
115
|
+
|
|
116
|
+
- name: Upload coverage to Codecov
|
|
117
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
|
|
118
|
+
uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # v4.0.1
|
|
119
|
+
with:
|
|
120
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
121
|
+
files: ./coverage.xml
|
|
122
|
+
fail_ci_if_error: true
|
|
123
|
+
|
|
124
|
+
build:
|
|
125
|
+
name: Build Package
|
|
126
|
+
needs: [lint, test]
|
|
127
|
+
runs-on: ubuntu-latest
|
|
128
|
+
timeout-minutes: 15
|
|
129
|
+
|
|
130
|
+
steps:
|
|
131
|
+
- name: Checkout code
|
|
132
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
133
|
+
|
|
134
|
+
- name: Setup Python
|
|
135
|
+
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
|
|
136
|
+
with:
|
|
137
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
138
|
+
cache: 'pip'
|
|
139
|
+
|
|
140
|
+
- name: Install build tools
|
|
141
|
+
run: |
|
|
142
|
+
python -m pip install --upgrade pip
|
|
143
|
+
pip install build twine
|
|
144
|
+
|
|
145
|
+
- name: Build package
|
|
146
|
+
run: python -m build
|
|
147
|
+
|
|
148
|
+
- name: Check package
|
|
149
|
+
run: twine check dist/*
|
|
150
|
+
|
|
151
|
+
- name: Upload build artifacts
|
|
152
|
+
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
|
153
|
+
with:
|
|
154
|
+
name: python-package-distributions
|
|
155
|
+
path: dist/
|
|
156
|
+
retention-days: 7
|
|
157
|
+
if-no-files-found: error
|