diff --git a/docs/08-reference/06-github-actions-image-workflows.md b/docs/08-reference/06-github-actions-image-workflows.md new file mode 100644 index 00000000..85805b64 --- /dev/null +++ b/docs/08-reference/06-github-actions-image-workflows.md @@ -0,0 +1,312 @@ +--- +title: GitHub Actions Image Workflows +--- + +This document describes the current workflow setup for shared core images and reusable downstream app images. + +# Workflow roles + +The current workflow layout is: + +- `.github/workflows/core-build-develop.yml` +- `.github/workflows/core-build-stable.yml` +- `.github/workflows/core-build-test-images.yml` +- `.github/workflows/core-publish-images.yml` +- `.github/workflows/app-build-image.yml` + +`core-build-develop.yml` and `core-build-stable.yml` are orchestration workflows. +They decide when the core image pipeline runs. + +`core-build-test-images.yml` is the reusable workflow that: + +- resolves the image versions for the requested release line +- builds the shared core images into a local registry +- runs the test suite against those images + +`core-publish-images.yml` is the reusable workflow that: + +- publishes the tested images to Docker Hub +- publishes `base` and `build` to GHCR + +`app-build-image.yml` is the reusable workflow that downstream repositories call to: + +- create an `apps.json` file from the caller's app repository and ref +- build `images/layered/Containerfile` +- consume existing `base` and `build` images +- install the requested app into the final image +- optionally push the final app image to the caller's registry + +# Current flow + +The current structure is: + +```text +core orchestration + -> core build and test + -> core publish + +downstream app workflow + -> consume published base and build + -> install app + -> publish final app image +``` + +Current Mermaid overview: + +```mermaid +flowchart TD + subgraph Core["Core image flow"] + A[core-build-develop.yml or core-build-stable.yml] + B[core-build-test-images.yml] + C[Resolve versions] + D[Build local test images] + E[Run pytest] + F[core-publish-images.yml] + G[Push Docker Hub: erpnext, base, build] + H[Push GHCR: base, build] + + A --> B + B --> C + C --> D + D --> E + E --> F + F --> G + F --> H + end + + subgraph App["Downstream app flow"] + I[Downstream repo workflow] + J[app-build-image.yml] + K[Create apps.json] + L[Build images/layered/Containerfile] + M[Install app] + N[Push final app image] + + I --> J + J --> K + K --> L + L --> M + M --> N + end + + G --> J + H --> J +``` + +More concretely: + +```text +core-build-test-images.yml + -> resolves frappe and erpnext tags + -> builds images into a local CI registry + -> runs tests + +core-publish-images.yml + -> pushes Docker Hub: erpnext, base, build + -> pushes GHCR: base, build + +app-build-image.yml + -> pulls: + - /base: + - /build: + -> installs app from app_repo + app_ref + -> pushes final image_name:image_tag +``` + +# Naming convention + +GitHub Actions requires workflow files to stay directly inside `.github/workflows`. +Subdirectories are not supported for workflow files, so structure should come from file names and `name:` values. + +Recommended file naming convention: + +```text +--.yml +``` + +Current examples: + +- `core-build-bench.yml` +- `core-build-develop.yml` +- `core-build-stable.yml` +- `core-build-test-images.yml` +- `core-publish-images.yml` +- `app-build-image.yml` +- `docs-publish-site.yml` + +Recommended visible workflow names: + +- `Core / Build Bench` +- `Core / Build Develop` +- `Core / Build Stable` +- `Core / Build and Test Images` +- `Core / Publish Images` +- `App / Build Image` +- `Docs / Publish Site` + +# Style rules + +To keep workflows predictable, use one convention per category instead of mixing styles. + +Workflow file names should use kebab-case: + +```text +core-build-test-images.yml +app-build-image.yml +``` + +Workflow display names should use short title-style groups: + +```text +Core / Build and Test Images +App / Build Image +``` + +Workflow inputs should use snake_case: + +```yaml +app_name: +frappe_ref: +image_name: +``` + +Environment variables should use upper snake case: + +```text +FRAPPE_IMAGE_PREFIX +PYTHON_VERSION +NODE_VERSION +``` + +The recommended rule set is: + +- workflow file names: kebab-case +- workflow `name:` values: grouped title case +- workflow inputs: snake_case +- job ids and step ids: snake_case where practical +- environment variables: UPPER_SNAKE_CASE + +This means `-` is preferred for file names, while `_` remains appropriate for YAML keys, inputs, and environment variables. + +# Important inputs in `app-build-image.yml` + +The reusable app workflow is controlled mainly by these inputs: + +- `app_name` + The application directory name, for example `crm` +- `app_repo` + The Git repository to install, for example `frappe/crm` +- `app_ref` + The branch or tag to install, for example `develop` +- `frappe_ref` + The tag of the existing `base` and `build` images, for example `version-16` +- `frappe_image_prefix` + Where the shared `base` and `build` images come from, for example `frappe` or `ghcr.io/frappe` +- `image_name` + The final target image name, for example `ghcr.io/acme/crm` +- `image_tag` + The final target image tag, for example `develop` +- `registry` + The registry for the final push, for example `ghcr.io` or `docker.io` + +The key distinction is: + +```text +frappe_image_prefix = source of shared base/build images +image_name = destination of the final app image +``` + +# Example: caller repository publishes to GHCR + +This example assumes: + +- shared base images exist in `ghcr.io/frappe/base` and `ghcr.io/frappe/build` +- the caller repository wants to publish its own app image to `ghcr.io/acme/crm` + +```yaml +name: App / Build CRM Image + +on: + workflow_dispatch: + push: + branches: + - develop + +permissions: + contents: read + packages: write + +jobs: + build-image: + uses: frappe/frappe_docker/.github/workflows/app-build-image.yml@main + with: + app_name: crm + app_repo: acme/crm + app_ref: develop + frappe_ref: version-16 + frappe_image_prefix: ghcr.io/frappe + image_name: ghcr.io/acme/crm + image_tag: develop + registry: ghcr.io + push: true + platforms: linux/amd64 +``` + +What happens: + +```text +1. app-build-image.yml is called +2. apps.json is generated from acme/crm + develop +3. the workflow builds images/layered/Containerfile +4. layered uses: + - ghcr.io/frappe/build:version-16 + - ghcr.io/frappe/base:version-16 +5. CRM is installed +6. the final image is pushed to ghcr.io/acme/crm:develop +``` + +For GHCR, the caller workflow should grant: + +- `permissions: packages: write` + +The reusable workflow then logs in with the workflow token. + +# Example: caller repository publishes to Docker Hub + +This example assumes: + +- shared base images come from Docker Hub under `frappe` +- the caller repository wants to publish its app image to Docker Hub as `acme/crm` + +```yaml +name: App / Build CRM Image + +on: + workflow_dispatch: + push: + branches: + - develop + +jobs: + build-image: + uses: frappe/frappe_docker/.github/workflows/app-build-image.yml@main + with: + app_name: crm + app_repo: acme/crm + app_ref: develop + frappe_ref: version-16 + frappe_image_prefix: frappe + image_name: acme/crm + image_tag: develop + registry: docker.io + push: true + platforms: linux/amd64 + secrets: + REGISTRY_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} +``` + +In this case: + +- shared images are pulled from `frappe/base:version-16` and `frappe/build:version-16` +- the final image is pushed to Docker Hub as `acme/crm:develop`