Skip to content

Commit 14dcfd7

Browse files
committed
ci: initialize basic CI
Signed-off-by: Emilien Escalle <[email protected]>
1 parent 883db9e commit 14dcfd7

File tree

19 files changed

+766
-1
lines changed

19 files changed

+766
-1
lines changed

.github/workflows/__shared-ci.yml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
name: Common Continuous Integration tasks
2+
3+
on:
4+
workflow_call:
5+
6+
permissions:
7+
contents: read
8+
packages: write
9+
10+
jobs:
11+
lint:
12+
runs-on: ubuntu-latest
13+
name: Lint
14+
steps:
15+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
16+
17+
- name: actionlint
18+
uses: raven-actions/actionlint@3a24062651993d40fed1019b58ac6fbdfbf276cc # v2.0.1
19+
20+
test-release-chart:
21+
runs-on: ubuntu-latest
22+
name: Test release chart
23+
steps:
24+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
25+
26+
- name: Act
27+
id: act
28+
uses: ./
29+
with:
30+
name: ${{ github.event.repository.name }}
31+
repository: ${{ github.repository }}/charts/application-test
32+
tag: "0.1.0-${{ github.run_id }}"
33+
path: tests/charts/application
34+
registry: ghcr.io
35+
registry_username: ${{ github.repository_owner }}
36+
registry_password: ${{ github.token }}
37+
38+
- name: "Assert: outputs"
39+
run: |
40+
if [ "${{ steps.act.outputs.image }}" != "ghcr.io/${{ github.repository }}/charts/application-test/${{ github.event.repository.name }}:0.1.0-${{ github.run_id }}" ]; then
41+
echo "Unexpected chart image name"
42+
exit 1
43+
fi
44+
45+
# Clean up package versions
46+
47+
- id: get-package-version-ids
48+
if: always()
49+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
50+
with:
51+
script: |
52+
// Retrieve package version IDs for the created chart tag
53+
const packageName = `${{ github.event.repository.name }}/charts/application-test/${{ github.event.repository.name }}`;
54+
const tag = `0.1.0-${{ github.run_id }}`;
55+
const packageVersionIds = [];
56+
57+
async function rateLimitRetryCall (fn, ...args) {
58+
try {
59+
return await fn(...args)
60+
} catch (error) {
61+
if (error.status === 403 && error.headers['retry-after']) {
62+
const retryAfter = parseInt(error.headers['retry-after'], 10)
63+
await new Promise((resolve) =>
64+
setTimeout(resolve, (retryAfter + 1) * 1000)
65+
)
66+
return rateLimitRetryCall(fn, ...args)
67+
}
68+
throw error
69+
}
70+
}
71+
72+
async function getAllPackageVersions(page = 1) {
73+
// Get all package versions for the package owned by the user
74+
const { data } = await rateLimitRetryCall(
75+
github.rest.packages.getAllPackageVersionsForPackageOwnedByUser,
76+
{
77+
package_type: 'container',
78+
package_name: packageName,
79+
username: context.repo.owner,
80+
per_page: 100,
81+
page
82+
}
83+
)
84+
return data
85+
}
86+
87+
let currentPage = 1
88+
for (;;) {
89+
const data = await getAllPackageVersions(currentPage)
90+
91+
currentPage++
92+
93+
if (!data.length) {
94+
break
95+
}
96+
97+
// Filter package versions by tag
98+
const matchingPackageVersionIds = data
99+
.filter((version) => version.metadata.container.tags.includes(tag))
100+
.map((version) => version.id)
101+
102+
packageVersionIds.push(...matchingPackageVersionIds);
103+
}
104+
105+
const uniquePackageVersionIds = [...new Set(packageVersionIds)];
106+
107+
// Return the package version IDs as a comma-separated string
108+
if (uniquePackageVersionIds.length > 0) {
109+
core.setOutput('package-version-ids', uniquePackageVersionIds.join(','));
110+
}
111+
112+
- name: Delete package versions
113+
uses: actions/delete-package-versions@v5
114+
if: always() && steps.get-package-version-ids.outputs.package-version-ids
115+
with:
116+
package-type: container
117+
package-name: ${{ github.event.repository.name }}/charts/application-test/${{ github.event.repository.name }}
118+
package-version-ids: ${{ steps.get-package-version-ids.outputs.package-version-ids }}

.github/workflows/main-ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Main - Continuous Integration
2+
3+
on:
4+
push:
5+
branches: [main]
6+
tags: ["*"]
7+
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
packages: write
13+
14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.ref }}
16+
cancel-in-progress: true
17+
18+
jobs:
19+
ci:
20+
uses: ./.github/workflows/__shared-ci.yml
21+
secrets: inherit
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Pull request - Continuous Integration
2+
3+
on:
4+
merge_group:
5+
pull_request:
6+
branches: [main]
7+
8+
permissions:
9+
contents: read
10+
packages: write
11+
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
ci:
18+
uses: ./.github/workflows/__shared-ci.yml
19+
secrets: inherit

action.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ runs:
6565
helm package \
6666
'${{ steps.prepare-variables.outputs.path }}' \
6767
--version '${{ inputs.tag }}' \
68-
${{ inputs.app_version != null && format('--app-version {0}', inputs.app_version) || '' }}
68+
${{ inputs.app_version != null && format('--app-version {0}', inputs.app_version) || '' }} \
6969
--destination $(dirname '${{ steps.prepare-variables.outputs.chart-file }}') \
7070
env:
7171
HELM_EXPERIMENTAL_OCI: '1'
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
apiVersion: v2
3+
name: "helm-oci-chart-releaser"
4+
description: A Helm chart for Kubernetes
5+
6+
# A chart can be either an 'application' or a 'library' chart.
7+
#
8+
# Application charts are a collection of templates that can be packaged into versioned archives
9+
# to be deployed.
10+
#
11+
# Library charts provide useful utilities or functions for the chart developer. They're included as
12+
# a dependency of application charts to inject those utilities and functions into the rendering
13+
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
14+
type: application
15+
16+
# This is the chart version. This version number should be incremented each time you make changes
17+
# to the chart and its templates, including the app version.
18+
# Versions are expected to follow Semantic Versioning (https://semver.org/)
19+
version: 0.0.0
20+
21+
# This is the version number of the application being deployed. This version number should be
22+
# incremented each time you make changes to the application. Versions are not expected to
23+
# follow Semantic Versioning. They should reflect the version the application is using.
24+
# It is recommended to use it with quotes.
25+
appVersion: "0.0.0"

tests/charts/application/README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# test-application
2+
3+
![Version: 0.0.0](https://img.shields.io/badge/Version-0.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.0](https://img.shields.io/badge/AppVersion-0.0.0-informational?style=flat-square)
4+
5+
A Helm chart for Kubernetes
6+
7+
## Requirements
8+
9+
| Repository | Name | Version |
10+
| ---------- | ---- | ------- |
11+
12+
## Values
13+
14+
| Key | Type | Default | Description |
15+
| ------------------------------------------ | ------ | --------------------------------------------------------------------------- | ----------- |
16+
| affinity | object | `{}` | |
17+
| application.dbConnection | string | `"mysql"` | |
18+
| application.dbDatabase | string | `"test"` | |
19+
| application.dbHost | string | `"mysql"` | |
20+
| application.dbPassword | string | `"test"` | |
21+
| application.dbPort | int | `3306` | |
22+
| application.dbUsername | string | `"test"` | |
23+
| autoscaling.enabled | bool | `false` | |
24+
| autoscaling.maxReplicas | int | `100` | |
25+
| autoscaling.minReplicas | int | `1` | |
26+
| autoscaling.targetCPUUtilizationPercentage | int | `80` | |
27+
| fullnameOverride | string | `""` | |
28+
| image.digest | string | `"sha256:da3b65f32ea75f8041079d220b72da4f605738996256a7dc32715424cc117271"` | |
29+
| image.pullPolicy | string | `"Always"` | |
30+
| image.registry | string | `"ghcr.io"` | |
31+
| image.repository | string | `"hoverkraft-tech/ci-github-container/application-test"` | |
32+
| image.tag | string | `""` | |
33+
| imagePullSecrets | list | `[]` | |
34+
| ingress.annotations | object | `{}` | |
35+
| ingress.className | string | `""` | |
36+
| ingress.enabled | bool | `false` | |
37+
| ingress.hosts[0].host | string | `"chart-example.local"` | |
38+
| ingress.hosts[0].paths[0].path | string | `"/"` | |
39+
| ingress.hosts[0].paths[0].pathType | string | `"ImplementationSpecific"` | |
40+
| ingress.tls | list | `[]` | |
41+
| mysql.auth.database | string | `"test"` | |
42+
| mysql.auth.password | string | `"test"` | |
43+
| mysql.auth.rootPassword | string | `"root"` | |
44+
| mysql.auth.username | string | `"test"` | |
45+
| mysql.enabled | bool | `false` | |
46+
| mysql.fullnameOverride | string | `"mysql"` | |
47+
| nameOverride | string | `""` | |
48+
| nodeSelector | object | `{}` | |
49+
| podAnnotations | object | `{}` | |
50+
| podSecurityContext | object | `{}` | |
51+
| replicaCount | int | `1` | |
52+
| resources.limits.cpu | string | `"100m"` | |
53+
| resources.limits.memory | string | `"128Mi"` | |
54+
| resources.requests.cpu | string | `"100m"` | |
55+
| resources.requests.memory | string | `"128Mi"` | |
56+
| securityContext.allowPrivilegeEscalation | bool | `false` | |
57+
| securityContext.capabilities.drop[0] | string | `"ALL"` | |
58+
| securityContext.readOnlyRootFilesystem | bool | `true` | |
59+
| securityContext.runAsNonRoot | bool | `true` | |
60+
| securityContext.runAsUser | int | `10001` | |
61+
| securityContext.seccompProfile.type | string | `"RuntimeDefault"` | |
62+
| service.port | int | `8080` | |
63+
| service.type | string | `"ClusterIP"` | |
64+
| serviceAccount.annotations | object | `{}` | |
65+
| serviceAccount.create | bool | `true` | |
66+
| serviceAccount.name | string | `""` | |
67+
| tolerations | list | `[]` | |
68+
69+
---
70+
71+
Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore

tests/charts/application/ci/empty-values.yaml

Whitespace-only changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
1. Get the application URL by running these commands:
2+
{{- if .Values.ingress.enabled }}
3+
{{- range $host := .Values.ingress.hosts }}
4+
{{- range .paths }}
5+
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
6+
{{- end }}
7+
{{- end }}
8+
{{- else if contains "NodePort" .Values.service.type }}
9+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "test-application.fullname" . }})
10+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
11+
echo http://$NODE_IP:$NODE_PORT
12+
{{- else if contains "LoadBalancer" .Values.service.type }}
13+
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
14+
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "test-application.fullname" . }}'
15+
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "test-application.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
16+
echo http://$SERVICE_IP:{{ .Values.service.port }}
17+
{{- else if contains "ClusterIP" .Values.service.type }}
18+
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "test-application.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19+
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
20+
echo "Visit http://127.0.0.1:8080 to use your application"
21+
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
22+
{{- end }}

0 commit comments

Comments
 (0)