mirror of
https://github.com/outbackdingo/cozystack.git
synced 2026-03-20 14:41:10 +00:00
Changing the container registry from GHCR to OCIR will help with more flexibe image retention policies and removes the restrictions on the GitHub token when contributors submit PRs from forks. Release PRs remain on GHCR, as before. Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
354 lines
12 KiB
YAML
354 lines
12 KiB
YAML
name: Pull Request
|
||
|
||
env:
|
||
REGISTRY: ${{ secrets.OCIR_REPO }}
|
||
on:
|
||
pull_request:
|
||
types: [opened, synchronize, reopened]
|
||
paths-ignore:
|
||
- 'docs/**/*'
|
||
|
||
# Cancel in‑flight runs for the same PR when a new push arrives.
|
||
concurrency:
|
||
group: pr-${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
build:
|
||
name: Build
|
||
runs-on: [self-hosted]
|
||
permissions:
|
||
contents: read
|
||
packages: write
|
||
|
||
# Never run when the PR carries the "release" label.
|
||
if: |
|
||
!contains(github.event.pull_request.labels.*.name, 'release')
|
||
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
fetch-tags: true
|
||
|
||
- name: Login to GitHub Container Registry
|
||
uses: docker/login-action@v3
|
||
with:
|
||
username: ${{ secrets.OCIR_USER}}
|
||
password: ${{ secrets.OCIR_TOKEN }}
|
||
registry: iad.ocir.io
|
||
env:
|
||
DOCKER_CONFIG: ${{ runner.temp }}/.docker
|
||
|
||
- name: Build
|
||
run: make build
|
||
env:
|
||
DOCKER_CONFIG: ${{ runner.temp }}/.docker
|
||
|
||
- name: Build Talos image
|
||
run: make -C packages/core/installer talos-nocloud
|
||
|
||
- name: Save git diff as patch
|
||
if: "!contains(github.event.pull_request.labels.*.name, 'release')"
|
||
run: git diff HEAD > _out/assets/pr.patch
|
||
|
||
- name: Upload git diff patch
|
||
if: "!contains(github.event.pull_request.labels.*.name, 'release')"
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: pr-patch
|
||
path: _out/assets/pr.patch
|
||
|
||
- name: Upload installer
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: cozystack-installer
|
||
path: _out/assets/cozystack-installer.yaml
|
||
|
||
- name: Upload Talos image
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: talos-image
|
||
path: _out/assets/nocloud-amd64.raw.xz
|
||
|
||
resolve_assets:
|
||
name: "Resolve assets"
|
||
runs-on: ubuntu-latest
|
||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||
outputs:
|
||
installer_id: ${{ steps.fetch_assets.outputs.installer_id }}
|
||
disk_id: ${{ steps.fetch_assets.outputs.disk_id }}
|
||
|
||
steps:
|
||
- name: Checkout code
|
||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
fetch-tags: true
|
||
|
||
- name: Extract tag from PR branch (release PR)
|
||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||
id: get_tag
|
||
uses: actions/github-script@v7
|
||
with:
|
||
script: |
|
||
const branch = context.payload.pull_request.head.ref;
|
||
const m = branch.match(/^release-(\d+\.\d+\.\d+(?:[-\w\.]+)?)$/);
|
||
if (!m) {
|
||
core.setFailed(`❌ Branch '${branch}' does not match 'release-X.Y.Z[-suffix]'`);
|
||
return;
|
||
}
|
||
core.setOutput('tag', `v${m[1]}`);
|
||
|
||
- name: Find draft release & asset IDs (release PR)
|
||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||
id: fetch_assets
|
||
uses: actions/github-script@v7
|
||
with:
|
||
github-token: ${{ secrets.GH_PAT }}
|
||
script: |
|
||
const tag = '${{ steps.get_tag.outputs.tag }}';
|
||
const releases = await github.rest.repos.listReleases({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
per_page: 100
|
||
});
|
||
const draft = releases.data.find(r => r.tag_name === tag && r.draft);
|
||
if (!draft) {
|
||
core.setFailed(`Draft release '${tag}' not found`);
|
||
return;
|
||
}
|
||
const find = (n) => draft.assets.find(a => a.name === n)?.id;
|
||
const installerId = find('cozystack-installer.yaml');
|
||
const diskId = find('nocloud-amd64.raw.xz');
|
||
if (!installerId || !diskId) {
|
||
core.setFailed('Required assets missing in draft release');
|
||
return;
|
||
}
|
||
core.setOutput('installer_id', installerId);
|
||
core.setOutput('disk_id', diskId);
|
||
|
||
|
||
prepare_env:
|
||
name: "Prepare environment"
|
||
runs-on: [self-hosted]
|
||
permissions:
|
||
contents: read
|
||
packages: read
|
||
needs: ["build", "resolve_assets"]
|
||
if: ${{ always() && (needs.build.result == 'success' || needs.resolve_assets.result == 'success') }}
|
||
|
||
steps:
|
||
# ▸ Checkout and prepare the codebase
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
|
||
# ▸ Regular PR path – download artefacts produced by the *build* job
|
||
- name: "Download Talos image (regular PR)"
|
||
if: "!contains(github.event.pull_request.labels.*.name, 'release')"
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: talos-image
|
||
path: _out/assets
|
||
|
||
- name: Download PR patch
|
||
if: "!contains(github.event.pull_request.labels.*.name, 'release')"
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: pr-patch
|
||
path: _out/assets
|
||
|
||
- name: Apply patch
|
||
if: "!contains(github.event.pull_request.labels.*.name, 'release')"
|
||
run: |
|
||
git apply _out/assets/pr.patch
|
||
|
||
# ▸ Release PR path – fetch artefacts from the corresponding draft release
|
||
- name: Download assets from draft release (release PR)
|
||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||
run: |
|
||
mkdir -p _out/assets
|
||
curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \
|
||
-o _out/assets/nocloud-amd64.raw.xz \
|
||
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.disk_id }}"
|
||
env:
|
||
GH_PAT: ${{ secrets.GH_PAT }}
|
||
|
||
- name: Set sandbox ID
|
||
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
|
||
|
||
# ▸ Start actual job steps
|
||
- name: Prepare workspace
|
||
run: |
|
||
rm -rf /tmp/$SANDBOX_NAME
|
||
cp -r ${{ github.workspace }} /tmp/$SANDBOX_NAME
|
||
|
||
- name: Prepare environment
|
||
run: |
|
||
cd /tmp/$SANDBOX_NAME
|
||
attempt=0
|
||
until make SANDBOX_NAME=$SANDBOX_NAME prepare-env; do
|
||
attempt=$((attempt + 1))
|
||
if [ $attempt -ge 3 ]; then
|
||
echo "❌ Attempt $attempt failed, exiting..."
|
||
exit 1
|
||
fi
|
||
echo "❌ Attempt $attempt failed, retrying..."
|
||
done
|
||
echo "✅ The task completed successfully after $attempt attempts"
|
||
|
||
install_cozystack:
|
||
name: "Install Cozystack"
|
||
runs-on: [self-hosted]
|
||
permissions:
|
||
contents: read
|
||
packages: read
|
||
needs: ["prepare_env", "resolve_assets"]
|
||
if: ${{ always() && needs.prepare_env.result == 'success' }}
|
||
|
||
steps:
|
||
- name: Prepare _out/assets directory
|
||
run: mkdir -p _out/assets
|
||
|
||
# ▸ Regular PR path – download artefacts produced by the *build* job
|
||
- name: "Download installer (regular PR)"
|
||
if: "!contains(github.event.pull_request.labels.*.name, 'release')"
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: cozystack-installer
|
||
path: _out/assets
|
||
|
||
# ▸ Release PR path – fetch artefacts from the corresponding draft release
|
||
- name: Download assets from draft release (release PR)
|
||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||
run: |
|
||
mkdir -p _out/assets
|
||
curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \
|
||
-o _out/assets/cozystack-installer.yaml \
|
||
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.installer_id }}"
|
||
env:
|
||
GH_PAT: ${{ secrets.GH_PAT }}
|
||
|
||
# ▸ Start actual job steps
|
||
- name: Set sandbox ID
|
||
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
|
||
|
||
- name: Sync _out/assets directory
|
||
run: |
|
||
mkdir -p /tmp/$SANDBOX_NAME/_out/assets
|
||
mv _out/assets/* /tmp/$SANDBOX_NAME/_out/assets/
|
||
|
||
- name: Install Cozystack into sandbox
|
||
run: |
|
||
cd /tmp/$SANDBOX_NAME
|
||
attempt=0
|
||
until make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME install-cozystack; do
|
||
attempt=$((attempt + 1))
|
||
if [ $attempt -ge 3 ]; then
|
||
echo "❌ Attempt $attempt failed, exiting..."
|
||
exit 1
|
||
fi
|
||
echo "❌ Attempt $attempt failed, retrying..."
|
||
done
|
||
echo "✅ The task completed successfully after $attempt attempts."
|
||
|
||
detect_test_matrix:
|
||
name: "Detect e2e test matrix"
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
matrix: ${{ steps.set.outputs.matrix }}
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- id: set
|
||
run: |
|
||
apps=$(find hack/e2e-apps -maxdepth 1 -mindepth 1 -name '*.bats' | \
|
||
awk -F/ '{sub(/\..+/, "", $NF); print $NF}' | jq -R . | jq -cs .)
|
||
echo "matrix={\"app\":$apps}" >> "$GITHUB_OUTPUT"
|
||
|
||
test_apps:
|
||
strategy:
|
||
matrix: ${{ fromJson(needs.detect_test_matrix.outputs.matrix) }}
|
||
name: Test ${{ matrix.app }}
|
||
runs-on: [self-hosted]
|
||
needs: [install_cozystack,detect_test_matrix]
|
||
if: ${{ always() && (needs.install_cozystack.result == 'success' && needs.detect_test_matrix.result == 'success') }}
|
||
|
||
steps:
|
||
- name: Set sandbox ID
|
||
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
|
||
|
||
- name: E2E Apps
|
||
run: |
|
||
cd /tmp/$SANDBOX_NAME
|
||
attempt=0
|
||
until make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-apps-${{ matrix.app }}; do
|
||
attempt=$((attempt + 1))
|
||
if [ $attempt -ge 3 ]; then
|
||
echo "❌ Attempt $attempt failed, exiting..."
|
||
exit 1
|
||
fi
|
||
echo "❌ Attempt $attempt failed, retrying..."
|
||
done
|
||
echo "✅ The task completed successfully after $attempt attempts"
|
||
|
||
collect_debug_information:
|
||
name: Collect debug information
|
||
runs-on: [self-hosted]
|
||
needs: [test_apps]
|
||
if: ${{ always() }}
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Set sandbox ID
|
||
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
|
||
|
||
- name: Collect report
|
||
run: |
|
||
cd /tmp/$SANDBOX_NAME
|
||
make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-report
|
||
|
||
- name: Upload cozyreport.tgz
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: cozyreport
|
||
path: /tmp/${{ env.SANDBOX_NAME }}/_out/cozyreport.tgz
|
||
|
||
- name: Collect images list
|
||
run: |
|
||
cd /tmp/$SANDBOX_NAME
|
||
make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-images
|
||
|
||
- name: Upload image list
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: image-list
|
||
path: /tmp/${{ env.SANDBOX_NAME }}/_out/images.txt
|
||
|
||
cleanup:
|
||
name: Tear down environment
|
||
runs-on: [self-hosted]
|
||
needs: [collect_debug_information]
|
||
if: ${{ always() && needs.test_apps.result == 'success' }}
|
||
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
fetch-tags: true
|
||
|
||
- name: Set sandbox ID
|
||
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
|
||
|
||
- name: Tear down sandbox
|
||
run: make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME delete
|
||
|
||
- name: Remove workspace
|
||
run: rm -rf /tmp/$SANDBOX_NAME
|
||
|
||
|