15 CI CD and GitHub Actions - seadogger-tech/seadogger-homelab GitHub Wiki
This repository uses three main GitHub Actions workflows for automation: Check and Publish Wiki (checks wiki changes on pull requests and publishes reviewed source from master), Upstream Bedrock Gateway Rebuild (keeps the AWS gateway up-to-date every 6 hours, with 6 unmerged upstream PRs cherry-picked in), and Mealie AI-Import Rebuild (a pinned-tag build with 2 unmerged upstream PRs cherry-picked in, rebuilt only on demand). The latter two both follow the same "adopting unmerged upstream PRs" pattern โ see ADR-011 for the full rationale.
๐ Diagram Viewing Recommendation
The interactive Mermaid diagrams below are optimized for GitHub Dark Mode to provide maximum readability and visual impact.
To enable Dark Mode: GitHub Settings โ Appearance โ Theme โ Dark default
Light mode users can still view the diagrams, though colors may appear less vibrant.
graph TB
subgraph Triggers["๐ Workflow Triggers"]
Push[Git Push to master]
PR[Pull Request]
Schedule[Cron: Every 6 hours]
Manual[Manual Trigger]
end
subgraph WikiFlow["๐ Publish Wiki Workflow"]
WikiTrigger[On Push, PR or Manual]
Checkout1[Checkout Source Repo]
Preflight[Stage Allowed Files<br/>and Check Against Live Wiki]
PublishCheck{Default Branch<br/>and Not PR?}
CloneWiki[Clone Wiki Repo]
Rsync[rsync: Copy Checked Payload]
CommitWiki[Commit & Push to Wiki]
end
subgraph BedrockFlow["๐ Bedrock Gateway Rebuild"]
BedrockTrigger[Every 6 hours / Manual]
Checkout2[Checkout This Repo]
CheckUpstream[Checkout Upstream main<br/>aws-samples/bedrock-access-gateway]
PRStatus{Any adopted PR<br/>merged/closed?}
WarnPR[โ ๏ธ Emit warning annotation<br/>does not stop build]
CherryPick[Cherry-pick 6 unmerged PRs<br/>#255 #246 #247 #239 #198 #249]
ResolveConflicts[Resolve 2 known conflicts via<br/>resolve_bedrock_gateway_conflicts.py]
SyntaxCheck{Valid Python<br/>syntax?}
CompareSSHA{Build identity<br/>changed?}
SetupBuildx[Setup QEMU + Buildx]
BuildPush[Build Multi-Arch<br/>linux/amd64,arm64]
PushGHCR[Push to ghcr.io]
UpdateSHA[Commit New Identity<br/>to .github/upstream_sha]
end
subgraph MealieFlow["๐ฒ Mealie AI-Import Rebuild"]
MealieTrigger[Manual / on cherry-pick change]
Checkout3[Checkout This Repo]
CheckoutTag[Checkout upstream Mealie<br/>@ pinned tag v3.21.0]
MealiePRStatus{Adopted PR<br/>merged/closed?}
MealieCherryPick[Cherry-pick 2 unmerged PRs<br/>#7618 #7825]
MealieBuild[Build Multi-Arch<br/>Yarn frontend + Python backend]
MealiePush[Push to ghcr.io<br/>mealie:v3.21.0-ai-import]
end
Push --> WikiTrigger
PR --> WikiTrigger
Manual --> WikiTrigger
Schedule --> BedrockTrigger
Manual --> BedrockTrigger
Manual --> MealieTrigger
WikiTrigger --> Checkout1
Checkout1 --> Preflight
Preflight --> PublishCheck
PublishCheck -->|Yes| CloneWiki
PublishCheck -->|No| CheckDone[Check Only]
CloneWiki --> Rsync
Rsync --> CommitWiki
BedrockTrigger --> Checkout2
Checkout2 --> CheckUpstream
CheckUpstream --> PRStatus
PRStatus -->|Yes| WarnPR
PRStatus -->|No| CherryPick
WarnPR --> CherryPick
CherryPick --> ResolveConflicts
ResolveConflicts --> SyntaxCheck
SyntaxCheck -->|Fail| BuildFail1[โ Fail build loudly]
SyntaxCheck -->|Pass| CompareSSHA
CompareSSHA -->|Changed| SetupBuildx
CompareSSHA -->|No Change| End1[Skip Build]
SetupBuildx --> BuildPush
BuildPush --> PushGHCR
PushGHCR --> UpdateSHA
MealieTrigger --> Checkout3
Checkout3 --> CheckoutTag
CheckoutTag --> MealiePRStatus
MealiePRStatus -->|Yes| MealieCherryPick
MealiePRStatus -->|No| MealieCherryPick
MealieCherryPick --> MealieBuild
MealieBuild --> MealiePush
style WikiFlow fill:#2d5016,stroke:#5a9216,stroke-width:3px
style BedrockFlow fill:#1e3a5f,stroke:#4a90e2,stroke-width:3px
style MealieFlow fill:#8b4513,stroke:#c17a3d,stroke-width:3px
style Triggers fill:#ff9900,stroke:#ff9900,stroke-width:3px
Purpose: Validate the exact public wiki payload on pull requests and pushes, then sync checked-in source from master to the GitHub Wiki.
File Location: core/.github/workflows/publish-wiki.yml
sequenceDiagram
participant Dev as Developer
participant GH as GitHub Actions
participant Source as Source Repo
participant Wiki as Wiki Repo
Dev->>Dev: Run useful_scripts/wiki/preflight.py before commit
Dev->>Source: Open PR or push to master
Source->>GH: Trigger: publish-wiki.yml
GH->>Source: Checkout source code
GH->>GH: Test preflight and stage allowed files
GH->>Wiki: Compare staged pages with live wiki
alt Push to master or manual run on master
GH->>Wiki: Clone wiki and recheck staged payload
GH->>Wiki: rsync staged payload โ wiki repo
alt Changes Detected
GH->>Wiki: Commit and push to master
else No Changes
GH-->>Dev: Wiki already up to date
end
GH->>Wiki: Verify master and synced content
else Pull request or non-default branch
GH-->>Dev: Report check result without publishing
end
Trigger Events:
- Automatic check: Pull requests and pushes
-
Automatic publish: Successful check on a push to
master -
Manual:
workflow_dispatch; publishing only when run onmaster
Requirements:
- Check job has
contents: read; publish job hascontents: writefor itsGITHUB_TOKEN - Safety guard: Only runs on
seadogger-tech/seadogger-homelabrepository
Steps:
-
Generate locally if needed - Run
docs/wiki/tools/build_all.pyin the Core checkout, inspect the diff and commit only reviewed pages. CI does not generate pages. -
Run local preflight before committing -
python3 -B useful_scripts/wiki/preflight.pyfrom the Core root. - Check in CI - Checkout source without persisted credentials, test the checker, stage allowed files, and compare their Markdown delta with the live wiki. Pull requests stop here.
-
Publish from
master- Recheck the staged payload against a fresh wiki clone, sync it, then commit and push to wikimasterif changed. -
Verify publication - Confirm remote
masterand the local wiki copy match the staged payload.
The preflight blocks unclosed fences, large page reductions, personal
checkout paths, account-bearing S3 bucket names, dated evidence headings and
operational result tables.
An intentional large reduction needs a reason and exact old/new content hashes
in useful_scripts/wiki/publication-exceptions.json. The automated patterns
catch common mistakes; review other operational details for the public audience
before publishing.
Use the wiki-relative page name as the key. Hash the exact old and new UTF-8
file contents with SHA-256; use null for the new hash when deleting a page:
{
"Page.md": {
"published_sha256": "OLD_SHA256",
"source_sha256": "NEW_SHA256_OR_NULL",
"reason": "Reviewed reason for replacing this page"
}
}Files synced: Root docs/wiki/*.md pages, docs/wiki/memory_bank/**/*.md, and rendered images under docs/wiki/images/ with .png, .jpg, .jpeg, or .svg extensions. Finder metadata, editable artwork (images/Logo.key), and generator scripts (tools/) stay in Core. The preflight rejects other unexpected files before sync.
Purpose: Monitor AWS Bedrock Gateway upstream repository, cherry-pick 6 real-but-unmerged upstream PRs on top, and automatically rebuild our multi-arch container image when anything changes.
File Location: core/.github/workflows/upstream-rebuild.yaml
Why cherry-picks at all: the plain upstream image is missing fixes two active clients of this gateway actually need โ Mealie (AI recipe import/parsing) needs schema-enforced structured output, and OpenWebUI (confirmed connected at 192.168.1.242:6880) needs tool-calling reliability fixes and Claude Opus 4.7 support. All 6 are real, currently-open, community PRs โ not something we wrote from scratch. See ADR-011 for the full list and rationale, and 09-Apps.md ยง Adopted Unmerged Upstream PRs for verification details.
sequenceDiagram
participant Cron as GitHub Cron
participant GH as GitHub Actions
participant This as This Repo
participant Upstream as aws-samples/bedrock-access-gateway
participant PRs as 6 Contributor Forks<br/>(unmerged PR branches)
participant GHCR as ghcr.io Registry
participant K8s as K3s Cluster
Cron->>GH: Trigger (every 6 hours)
GH->>This: Checkout master branch
GH->>This: Read .github/upstream_sha
Note over GH: Last known build identity
GH->>Upstream: Checkout default branch (main)
Note over GH: Always the LATEST main โ<br/>this is not pinned
GH->>Upstream: Check status of #255,#246,#247,#239,#198,#249
alt Any PR no longer OPEN
GH-->>GH: โ ๏ธ Warning annotation<br/>(does not stop the build)
end
GH->>PRs: Fetch + cherry-pick each PR's commit(s)
Note over GH: #255,#246,#247 apply cleanly
GH->>GH: #239 conflicts with #255 (adjacent branches)<br/>โ resolved by resolve_bedrock_gateway_conflicts.py
GH->>GH: #198 conflicts with #239 (same log line)<br/>โ resolved by resolve_bedrock_gateway_conflicts.py
GH->>PRs: Cherry-pick #249 (Opus 4.7)
GH->>GH: Validate Python syntax (ast.parse)
Note over GH: Fails loudly if a cherry-pick<br/>left the file broken
GH->>GH: Compute build identity (upstream SHA + patches)
alt Build identity changed
GH->>GH: Setup QEMU (ARM64 emulation)
GH->>GH: Setup Docker Buildx
GH->>GHCR: Login (GITHUB_TOKEN)
GH->>GH: Build multi-arch image
Note over GH: Platforms: linux/amd64, linux/arm64
GH->>GHCR: Push images
Note over GHCR: Tags: latest, sha-<short>
GH->>This: Update .github/upstream_sha
GH->>This: git commit + push
GHCR-->>K8s: โ
New image available
Note over K8s: kubectl rollout restart<br/>deployment/bedrock-access-gateway
else Build identity unchanged
GH-->>Cron: โน๏ธ No changes, skip build
end
Trigger Events:
-
Automatic: Every 6 hours (cron:
0 */6 * * *) -
Manual:
workflow_dispatchfor immediate rebuild
Container Registry:
-
Registry:
ghcr.io/seadogger-tech/aws-bedrock-gateway -
Tags:
-
latest- Always points to most recent build -
sha-<short>- Specific commit from upstream (e.g.,sha-a1b2c3d)
-
-
Architectures:
linux/amd64,linux/arm64
Change Detection:
- Stores last built identity (upstream SHA + patch state) in
.github/upstream_sha - Skips build if unchanged (saves CI minutes and resources)
- Because upstream
mainis checked out fresh every run, the identity changes whenever either upstream advances or any adopted PR's branch changes โ both trigger a rebuild
Steps:
-
Checkout this repo (branch
master) to read/write.github/upstream_shaand load.github/scripts/resolve_bedrock_gateway_conflicts.py - Checkout upstream repo (default branch, always latest โ not pinned)
- Check adopted-PR status โ warns (doesn't fail) if any of the 6 cherry-picked PRs has merged/closed upstream
- Cherry-pick all 6 PRs, resolving 2 known conflicts via the committed Python script rather than inline shell/Python (more testable, avoids YAML-escaping fragility)
-
Validate syntax โ
ast.parse()on the patched file; fails the build loudly rather than shipping broken code - Compare build identity - Skip remaining steps if unchanged
- Setup build environment - QEMU for ARM64 emulation, Buildx for multi-arch
-
Login to GHCR - Use
GITHUB_TOKENfor authentication - Build & push - Multi-arch build for AMD64 and ARM64
-
Update tracking file - Commit new build identity to
.github/upstream_sha
Why This Matters:
- AWS Bedrock Gateway is actively developed by AWS samples team, but real, useful fixes often sit unreviewed for weeks โ waiting on maintainer bandwidth isn't a substitute for having a working gateway today
- Automatic rebuilds keep our deployment current with both upstream security patches AND our adopted fixes
- ARM64 support critical for Raspberry Pi 5 cluster
- Build-identity tracking prevents unnecessary rebuilds (cost optimization)
- The merge-status warning step means adopted PRs don't silently rot forever once they land upstream for real
Purpose: Build a custom Mealie image with 2 unmerged upstream PRs cherry-picked in, giving a real "Force AI Import" button and a "Create from Text" page inside Mealie's own UI โ the stock image has neither, and Mealie's scraper-selection logic is hardcoded server-side with no request-level override.
File Location: core/.github/workflows/mealie-rebuild.yaml
Key architectural difference from the Bedrock workflow: this pipeline checks out a pinned upstream release tag (v3.21.0), not the active development branch (mealie-next). Mealie holds real household data โ recipes, meal plans โ so unattended tracking of in-progress upstream commits every 6 hours would be inappropriate. This workflow has no cron schedule; it only runs on workflow_dispatch (manual) or when re-triggered after bumping UPSTREAM_TAG or the cherry-picked PRs change.
sequenceDiagram
participant Dev as Developer, manual trigger
participant GH as GitHub Actions
participant This as This Repo
participant Upstream as mealie-recipes/mealie<br/>@ v3.21.0 (pinned tag)
participant PR7618 as zdenek-stursa/mealie<br/>(Force AI Scraper checkbox)
participant PR7825 as bferd/mealie<br/>(Create from Text page)
participant GHCR as ghcr.io Registry
Dev->>GH: workflow_dispatch
GH->>This: Checkout master branch
GH->>Upstream: Checkout tag v3.21.0 (NOT mealie-next)
GH->>Upstream: Check status of #7618, #7825
alt Either PR no longer OPEN
GH-->>GH: โ ๏ธ Warning annotation<br/>check if it shipped in a release > v3.21.0
end
GH->>PR7618: Cherry-pick "Force OpenAI Scraper" checkbox
Note over GH: Applies cleanly on top of v3.21.0
GH->>PR7825: Cherry-pick "Create from Text" page
Note over GH: PR branch tip is a MERGE commit โ<br/>cherry-pick the 2 real feature commits<br/>(fd8ab18e, 9fea1830) directly instead
GH->>GH: Compute build identity
alt Build identity changed
GH->>GH: Setup QEMU + Buildx
GH->>GHCR: Login (GITHUB_TOKEN)
GH->>GH: Build multi-arch image<br/>(full Yarn frontend + Python backend)
Note over GH: Much heavier build than the<br/>gateway's small Python-only image
GH->>GHCR: Push image<br/>Tags: v3.21.0-ai-import, sha-<short>
GH->>This: Update .github/mealie_upstream_sha
else Unchanged
GH-->>Dev: No changes, skip build
end
Trigger Events:
- Automatic: None โ no cron schedule (deliberate; see rationale above)
-
Manual:
workflow_dispatchonly
Container Registry:
-
Registry:
ghcr.io/seadogger-tech/mealie -
Tags:
v3.21.0-ai-import,sha-<short>
Known cherry-pick gotcha: PR #7825's contributor branch has a merge commit at its tip (upstream mealie-next was merged into the feature branch mid-review). git cherry-pick cannot replay a merge commit without -m <parent>, so the workflow targets the two actual feature commits (fd8ab18e, 9fea1830) directly instead of the branch HEAD. If the contributor rebases/force-pushes, these SHAs may need updating โ check git log <branch> for the real feature commits if this step starts failing.
Status as of 2026-07-25: first real build triggered and in progress at time of writing โ not yet cut over to the live Mealie deployment. Per ADR-011, verify in a throwaway test pod before touching production, since the live Mealie instance holds real recipes and meal-plan data.
graph LR
subgraph Developer["๐จโ๐ป Developer Workflow"]
EditWiki[Edit docs/wiki/]
EditCode[Update Bedrock Config]
AdoptPR[Adopt a new unmerged<br/>upstream PR]
Commit[git commit + push]
end
subgraph GitHub["๐ GitHub Actions"]
WikiAction[publish-wiki.yml]
BedrockAction[upstream-rebuild.yaml<br/>cron: every 6h]
MealieAction[mealie-rebuild.yaml<br/>manual only]
end
subgraph Outputs["๐ฆ Outputs"]
WikiSite[GitHub Wiki<br/>Updated Docs]
GHCRBedrock[ghcr.io<br/>aws-bedrock-gateway image]
GHCRMealie[ghcr.io<br/>mealie:v3.21.0-ai-import image]
end
subgraph Deployment["๐ Deployment"]
ArgoCD[ArgoCD]
K3sCluster[K3s Cluster]
BedrockPod[Bedrock Gateway Pod<br/>serves Mealie + OpenWebUI]
MealiePod[Mealie Pod]
end
EditWiki --> Commit
EditCode --> Commit
AdoptPR --> Commit
Commit --> WikiAction
Commit -.->|Indirectly| BedrockAction
Commit -.->|Indirectly| MealieAction
WikiAction --> WikiSite
BedrockAction --> GHCRBedrock
MealieAction --> GHCRMealie
GHCRBedrock --> ArgoCD
GHCRMealie -.->|Manual cutover only โ<br/>not yet in prod| K3sCluster
ArgoCD --> K3sCluster
K3sCluster --> BedrockPod
K3sCluster --> MealiePod
BedrockPod -.->|API calls| MealiePod
style Developer fill:#2d5016,stroke:#5a9216,stroke-width:3px
style GitHub fill:#1e3a5f,stroke:#4a90e2,stroke-width:3px
style Outputs fill:#ff9900,stroke:#ff9900,stroke-width:3px
style Deployment fill:#7b1fa2,stroke:#9c27b0,stroke-width:3px
-
Token:
GITHUB_TOKEN(automatic)- Type: Workflow-scoped token (provided by GitHub Actions)
- Permission:
contents: readfor the check job;contents: writefor the publish job - Target: The wiki's default
masterbranch - Setup: No repository PAT is required
-
Token:
GITHUB_TOKEN(automatic)- Type: Workflow-scoped token (provided by GitHub Actions)
- Permissions:
packages:write,contents:write(declared in workflow) - Why: Push container images to GHCR, commit
.github/upstream_sha, and query adopted-PR merge status viagh pr view - Setup: No action required (automatic)
-
Token:
GITHUB_TOKEN(automatic)- Type: Workflow-scoped token (provided by GitHub Actions)
- Permissions:
packages:write,contents:writeโ same shape as the Bedrock workflow - Setup: No action required (automatic)
- Copy
publish-wiki.ymlto your.github/workflows/ - Update the safety guard condition:
if: github.repository == 'your-org/your-repo'
- Give the publish job
contents: writeand configure Git withgh auth setup-git - Update
useful_scripts/wiki/preflight.pywith the destination wiki URL - Ensure reviewed wiki content is in
docs/wiki/and publish to the wiki's default branch
Change Build Schedule:
schedule:
- cron: '0 */12 * * *' # Every 12 hours instead of 6Change Image Registry:
env:
IMAGE: ghcr.io/your-org/your-image-nameAdd Additional Platforms:
platforms: linux/amd64,linux/arm64,linux/arm/v7Requirements:
- Workflow must have
packages: writepermission - Repository must enable GitHub Container Registry
- QEMU/Buildx required for cross-platform builds
Follow this checklist โ established as ADR-011 โ before adding a new cherry-pick to either workflow:
-
Verify the PR is real and current. Check
gh pr view <N> --repo <upstream> --json mergeable,updatedAtโMERGEABLEand recently updated is a good sign;CONFLICTINGmeans it's already stale against current upstream and needs its own rebase first. -
Test the cherry-pick locally first, against a fresh clone, using the exact commands you intend to put in the workflow โ not an approximation.
git clonethe upstream repo, add the contributor's fork as a remote,git fetchtheir branch,git cherry-pickthe commit(s). -
If it conflicts with another already-adopted PR, resolve it and write the resolution as a separate, committed, testable Python script (see
.github/scripts/resolve_bedrock_gateway_conflicts.pyfor the pattern) โ do not embed the fix as an inline Python heredoc inside the workflow's YAMLrun:block; that approach is fragile and broke silently once already during this pattern's development. -
Check for merge commits. If
git log <branch>shows the PR branch's tip is a merge commit (upstream was merged into the feature branch mid-review),git cherry-pickwill refuse it (is a merge but no -m option was given). Cherry-pick the actual feature commit(s) directly instead โ find them viagit log <branch>. - Add a merge-status check for the new PR number in the "Check adopted-PR status" step, so its eventual merge gets flagged.
- Add an explanatory comment at the cherry-pick step: which PR, why, which real client/use-case needs it, and what to do once it merges.
-
Add a syntax/sanity check after all cherry-picks apply (e.g.
python3 -c "import ast; ast.parse(...)"for Python targets). - Document it in the relevant app's wiki section and in the ADR-011 table.
# View specific run details
gh run view <run-id>
# View run logs
gh run view <run-id> --log
# Check a specific job's step-by-step status while it's still running
# (gh run view --log only works after the run completes)
gh api repos/seadogger-tech/seadogger-homelab/actions/jobs/<job-id> \
--jq '.steps[] | "\(.name): \(.status) \(.conclusion)"'Wiki Publish Fails:
- Symptom: "Authentication failed" error
-
Fix: Check the workflow's
contents: writepermission andgh auth setup-gitstep - Verify: Inspect the run log and confirm the wiki's default branch advanced
Wiki Preflight Fails:
- Symptom: The publication-delta check reports a page and line or a large reduction
- Fix: Move live evidence to private storage or repair the Markdown structure. For an intentional rewrite, record a reviewed exact-hash exception
-
Verify: Rerun
python3 -B useful_scripts/wiki/preflight.pyfrom the Core root
Bedrock/Mealie Rebuild Fails on Docker build:
- Symptom: "buildx failed with: ERROR: failed to solve"
- Fix: Check Dockerfile in upstream repo is valid
- Common cause: Upstream introduced ARM64-incompatible dependencies
Bedrock/Mealie Rebuild Fails on a Cherry-Pick Step:
-
Symptom:
::error::Cherry-pick of PR #N (<sha>) failed โ likely diverged from upstream main - Cause: Upstream has changed the same lines the adopted PR touches, since the last time the cherry-pick was verified
-
Fix: Re-run the cherry-pick locally against current upstream, resolve the new conflict, update
.github/scripts/resolve_bedrock_gateway_conflicts.py(or the workflow's inline logic for a one-off conflict) accordingly -
Also check: whether the PR's own branch was rebased/force-pushed โ if so, the hardcoded commit SHA in the workflow is stale; get the current SHA from
git log <branch>on the contributor's fork
Adopted-PR Merge Warning Appears:
-
Symptom:
::warning::...#N is now MERGED/CLOSED upstream. The cherry-pick step below is likely no longer needed - Expected: This is not a failure โ it's the intended signal from the pattern in ADR-011
- Action: Remove that PR's cherry-pick step from the workflow (and its entry from the ADR-011 table), then verify the next build still produces a working image
No Changes Detected:
- Symptom: Workflow runs but commits nothing
- Expected: When no actual changes exist in wiki, upstream, or the adopted-PR branches
- Action: No action needed - this is normal behavior
- 02-Architecture - System architecture showing CI/CD integration
- 05-GitOps-and-IaC - ArgoCD deployment workflow
- 09-Apps - Bedrock Gateway and Mealie application details, including the full adopted-PR table and text-to-image/image-to-text capability status
- 13-ADR-Index - ADR-011: the "Adopting Unmerged Upstream PRs" reusable pattern
- 21-Deployment-Dependencies - Understanding deployment order
Related Issues:
- #48 - Pure GitOps Refactor - Eliminate deployment dependencies
- Pro #6 - OAuth2 Improvements - Future CI/CD for OAuth credential management