From 8cc34552c64376637d841db73644a3d5964f0dcd Mon Sep 17 00:00:00 2001 From: Donavan Fritz Date: Wed, 6 May 2026 10:01:09 -0500 Subject: [PATCH] =?UTF-8?q?strip=20docker=20type=20=E2=80=94=20site-publis?= =?UTF-8?q?h=20is=20static-content=20only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes type: docker handling from action.yaml, scripts (build/deploy/utils/setup), and templates (deployment.yaml.j2, service-docker.yaml.j2). Renamed service-static.yaml.j2 -> service.yaml.j2. If site.yaml has type: docker, parse_site_yaml() now dies with a clear message pointing to action/image-build + action/image-push + action/image-deploy with hand-authored apps-repo manifests. rainsounds.vino.network was the only docker consumer and has already migrated. Drops registry-password input from action.yaml (no longer needed). --- README.md | 36 +++++-- action.yaml | 16 ++- scripts/__pycache__/build.cpython-314.pyc | Bin 0 -> 3290 bytes scripts/__pycache__/deploy.cpython-314.pyc | Bin 0 -> 8220 bytes scripts/__pycache__/utils.cpython-314.pyc | Bin 0 -> 6648 bytes scripts/build.py | 39 +------- scripts/deploy.py | 94 +----------------- scripts/setup.py | 8 -- scripts/utils.py | 58 ++++++----- templates/deployment.yaml.j2 | 52 ---------- templates/kustomization.yaml.j2 | 3 - templates/service-docker.yaml.j2 | 13 --- ...service-static.yaml.j2 => service.yaml.j2} | 0 13 files changed, 69 insertions(+), 250 deletions(-) create mode 100644 scripts/__pycache__/build.cpython-314.pyc create mode 100644 scripts/__pycache__/deploy.cpython-314.pyc create mode 100644 scripts/__pycache__/utils.cpython-314.pyc delete mode 100644 templates/deployment.yaml.j2 delete mode 100644 templates/service-docker.yaml.j2 rename templates/{service-static.yaml.j2 => service.yaml.j2} (100%) diff --git a/README.md b/README.md index 4221c10..80fc563 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,20 @@ # action/site-publish -Composite Gitea Action that publishes a website to the fritzlab k8s cluster. -Supports `static`, `hugo`, `mkdocs` (content → Garage S3 + ExternalName Service) -and `docker` (Dockerfile → Deployment + headless Service). Handles manifest -rendering, TLS via cert-manager, and Garage bucket aliases. +Composite Gitea Action that publishes a **static-content** website to the +fritzlab k8s cluster. Supports `static`, `hugo`, and `mkdocs`. Content goes +to a Garage S3 bucket; Traefik fronts the bucket via an `ExternalName` +Service with cert-manager TLS. -Renamed from `fritzlab/publish-site` → `action/site-publish` as part of the -2026 action-org consolidation. +> **Containerized web apps (Dockerfile-based) are NOT handled here.** Use the +> standard image-producer chain instead: +> [`action/image-build`](https://code.fritzlab.net/action/image-build) + +> [`action/image-push`](https://code.fritzlab.net/action/image-push) + +> [`action/image-deploy`](https://code.fritzlab.net/action/image-deploy). +> Hand-author the apps-repo manifests once (Deployment, Service, Ingress, +> Certificate, kustomization with `images:` block) and let `image-deploy` +> pin the tag on every push. See `sjc001/websites/rainsounds.vino.network/` +> for the canonical example. site-publish errors out explicitly if +> `site.yaml` has `type: docker`. ## Convention @@ -28,7 +36,7 @@ Or do it manually. `site.yaml`: ```yaml domain: my-site.vino.network -type: static # static | hugo | mkdocs | docker +type: static # static | hugo | mkdocs # content_dir: html # subdirectory containing content (default: repo root) # aliases: # additional hostnames (each gets a globalAlias on the bucket) # - www.my-site.vino.network @@ -68,12 +76,11 @@ my-site.fritzlab.net 300 IN CNAME traefik.edge.svc.k8s.sjc001.fritzlab.net. | Input | Required | Default | Description | |---|---|---|---| | `token` | yes | | Gitea token for apps repo push | -| `s3-access-key` | static/hugo/mkdocs | | Garage `ci-deploy-key` access key id | -| `s3-secret-key` | static/hugo/mkdocs | | Garage `ci-deploy-key` secret key | +| `s3-access-key` | yes | | Garage `ci-deploy-key` access key id | +| `s3-secret-key` | yes | | Garage `ci-deploy-key` secret key | | `s3-endpoint` | no | `http://garage.storage.svc:3900` | Garage S3 endpoint | -| `garage-admin-token` | static/hugo/mkdocs (only if site has `aliases`) | | Garage admin API token (`admin-token` from `garage-rpc-secret` in `storage` ns) | +| `garage-admin-token` | only if site has `aliases` | | Garage admin API token (`admin-token` from `garage-rpc-secret` in `storage` ns) | | `garage-admin-endpoint` | no | `http://garage.storage.svc:3903` | Garage admin API endpoint | -| `registry-password` | docker | inputs.token | Container registry password | | `username` | no | `ci-bot` | Gitea username | Org secrets in `websites`: `CI_BOT_TOKEN`, `GARAGE_S3_ACCESS_KEY`, @@ -105,3 +112,10 @@ There is no "first-deploy vs. update" branching — every deploy is idempotent. No nginx pods, no per-site Docker images. Garage matches `Host:` header to bucket name (or any of its globalAliases), so every site shares a single ExternalName target. + +## History + +- 2026-05-06: removed `type: docker` support. The single docker site + (`rainsounds.vino.network`) migrated to the `image-*` chain. site-publish + is now scoped strictly to static-content sites. +- 2026-05-06: renamed from `fritzlab/publish-site` → `action/site-publish`. diff --git a/action.yaml b/action.yaml index 7e5122d..c93fd9e 100644 --- a/action.yaml +++ b/action.yaml @@ -1,24 +1,21 @@ name: Publish Site -description: Build and deploy a site (static, hugo, mkdocs, or docker) to fritzlab k8s. +description: Build and deploy a static-content site (static, hugo, mkdocs) to Garage S3 with Traefik + cert-manager. Containerized apps should use action/image-build + action/image-push + action/image-deploy. inputs: token: description: Gitea token (ci-bot) for apps repo push and API operations required: true s3-access-key: - description: Garage ci-deploy-key access key id (required for static/hugo/mkdocs) - required: false + description: Garage ci-deploy-key access key id + required: true s3-secret-key: - description: Garage ci-deploy-key secret access key (required for static/hugo/mkdocs) - required: false - registry-password: - description: Container registry password (required for docker type; defaults to token) - required: false + description: Garage ci-deploy-key secret access key + required: true s3-endpoint: description: Garage S3 endpoint URL required: false default: http://garage.storage.svc:3900 garage-admin-token: - description: Garage admin API token (required for static/hugo/mkdocs to reconcile bucket aliases) + description: Garage admin API token (required only when site.yaml has aliases — used to reconcile bucket globalAliases) required: false garage-admin-endpoint: description: Garage admin API endpoint URL @@ -43,7 +40,6 @@ runs: SITE_DIR: ${{ github.workspace }} ACTION_DIR: ${{ github.action_path }} GITHUB_RUN_NUMBER: ${{ github.run_number }} - REGISTRY_PASSWORD: ${{ inputs.registry-password || inputs.token }} CI_BOT_USER: ${{ inputs.username }} - name: Deploy diff --git a/scripts/__pycache__/build.cpython-314.pyc b/scripts/__pycache__/build.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed3961d2d98ef5d04517c241a821b76ef928ca89 GIT binary patch literal 3290 zcma)9TTC0-89w9jWqcWPHMbBv4kX1Swi7~tk~Gy8i0l$B9f(0&tCkrLFv9jsXU1Xe zsuF#uN~5hH*|fOYm5S6pxN0AG+{dcwOW$Z>z&q<|qi)r{vdv17+L!)k%-AlGs{O5* z|I9h(|IdFr-#NbSrX2`qc=?kPyR#r$Q8EGS- z@XX(~pEI18QTdF@NHQ-mYog33YC;vbfYGlADz60R{Dfv+NT?frvqn#^Og?-xH64Et zc{shOk$mQ{)+8llg^%OO@mwOE(nvX*;S7(kV;*=&cwQfxv3F7M?}if(R7o;LO)T+0 zJ!I6hKp5k7K62jt+Br{lBGijwwY3&N|3&hlsA=R}mls6|)^ww%K1VV~%{6UytZmuc zXvr+Ggd__{k&RNUd3*(-X(DOW*N1FYiM2#6E;QV*UW_uv7WC{JUsdiyhDUdaIFU7F z$#|_tvc_m*cGPNka3M~%2H)Mr>^k?MtJm!n)!}CAJ@(WGHd~z971-~2N_;~ zO1MbiIW<AhhmqE-l)y6x}lFW`bN&H4R)Cw|3+%Do&D}xb>hHvlJs4u z66af0a@km$b>T6Vj*?JEW@Ak+nhm_A+pX(nF6eIhKf0TB-7Ob&M~MZmv3{Mit*q7X zwU+fq7glSlTNou5z|V`swi`2`-XL+3BX-4@v(Rg_>fDE5T@TyAcCuY;H`~KrVtd)k zY#-}|hhh5~GU6~a2b)=OKY`2Jn;G7<>?fM!|6z^R!xR6sZ9IXcFZf~=X)AfFfpP zj%V^s&`$D-Dr5k%#SEia)0;_=Q}R7E6|rytwTW^gS;(}7L&qSE^iHdKP&Jm@W7UwkTl|E zo(cqnOcI>Z%!#Zj=9`S~fFdP0UeSo*ya#NTMJd3iB{dh|Q+yf+K{I8?1U^Sow}WC0AMR4@QLr?oCYOOz9GZc31OP8H>xjO$6GI7lg$k?}Uytb(HF-I~p$ zgv_R9fjHtaNQ;kojZ{=wh7$p4WmcYui6qn%q)y$bhn%4+Pv+Hy9wBU0T@@bGZ8+xShvJy(J z$%2{>abl7WtqZE&2W{DZxXV_RQ^qK}$y6c>xrNABSg zb-O})PU*oH^x!EyRHBE9T&43;Ve*6A!RT?Pe0jgwYbT5aBw~JSg z+&4?qEiAz8_3wF(I);kl#i1kDof0()arw#CkG6Im@2RJ@-WRrBEancBs9?F<_p82N z_7$ma4I^-Kr4}Pg)A6YYtJ(NWVl~AlvkXjL z#0Z-d6un1B5M@)4Bn*Li<qHd&w)hc3HFw!YHTs0VtVM#Yy zx9GP>#{UPZ)>8KX%(E%}zI+$@aOM=O`-~(A;uCc5b3}iNT0TWhe??vYL=!L3#J{Z9 OiLozZ1Vu2qn*RddV8k2% literal 0 HcmV?d00001 diff --git a/scripts/__pycache__/deploy.cpython-314.pyc b/scripts/__pycache__/deploy.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7570875552b5400710bf54893678df6459faa04 GIT binary patch literal 8220 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 literal 0 HcmV?d00001 diff --git a/scripts/__pycache__/utils.cpython-314.pyc b/scripts/__pycache__/utils.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29f458b3f2de0d2f10d6cca632f0ddc19071e320 GIT binary patch literal 6648 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 N{KF*=)nc6K{{d#z7=i!* literal 0 HcmV?d00001 diff --git a/scripts/build.py b/scripts/build.py index cc11e6b..14d933a 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -1,12 +1,11 @@ -"""Build phase — content prep (static) or docker image build.""" +"""Build phase — content prep for static-content sites.""" -import os import shutil import subprocess import tempfile from pathlib import Path -from utils import EXCLUDE_FILES, die, env, parse_site_yaml, run +from utils import EXCLUDE_FILES, env, parse_site_yaml, run def build_static(site_dir, cfg): @@ -55,35 +54,6 @@ def build_static(site_dir, cfg): print(f"Build complete — content at {html_dir}") -def docker_login(): - password = os.environ.get("REGISTRY_PASSWORD", "") - user = env("CI_BOT_USER", "ci-bot") - if not password: - die("REGISTRY_PASSWORD is required for docker builds") - subprocess.run( - f"echo '{password}' | docker login code.fritzlab.net -u {user} --password-stdin", - shell=True, check=True, - ) - - -def build_docker(site_dir, cfg): - docker_login() - image = cfg["image"] - run_number = env("GITHUB_RUN_NUMBER", "0") - tags = [f"{image}:latest", f"{image}:{run_number}"] - - cmd_parts = ["docker", "build"] - for tag in tags: - cmd_parts += ["-t", tag] - cmd_parts += ["--network", "host", "--provenance=false"] - for key, val in cfg.get("build_args", {}).items(): - cmd_parts += ["--build-arg", f"{key}={val}"] - cmd_parts.append(str(site_dir)) - - run(" ".join(cmd_parts)) - print(f"Docker build complete — tags: {', '.join(tags)}") - - def cmd_build(): site_dir = Path(env("SITE_DIR")) cfg = parse_site_yaml(site_dir) @@ -92,7 +62,4 @@ def cmd_build(): print("Site disabled — skipping build") return - if cfg["type"] == "docker": - build_docker(site_dir, cfg) - else: - build_static(site_dir, cfg) + build_static(site_dir, cfg) diff --git a/scripts/deploy.py b/scripts/deploy.py index 6c41aac..e2aad28 100644 --- a/scripts/deploy.py +++ b/scripts/deploy.py @@ -1,9 +1,8 @@ -"""Deploy phase — S3 sync or docker push, manifest rendering, alias reconcile.""" +"""Deploy phase — S3 sync, manifest rendering, alias reconcile.""" import json import os import shutil -import subprocess import tempfile from pathlib import Path from urllib.error import HTTPError, URLError @@ -108,62 +107,7 @@ def ensure_bucket_aliases(site_name, aliases, admin_token): raise -def docker_push(cfg): - image = cfg["image"] - run_number = env("GITHUB_RUN_NUMBER", "0") - for tag in [f"{image}:latest", f"{image}:{run_number}"]: - run(f"docker push {tag}") - - -def docker_tag_cleanup(cfg, token): - """Keep the 3 newest numeric tags, delete the rest via Gitea API.""" - image = cfg["image"] - parts = image.split("/") - if len(parts) < 3: - print(f"Cannot parse image for tag cleanup: {image}") - return - org = parts[1] - pkg_name = parts[2] - - url = f"https://{GITEA_HOST}/api/v1/packages/{org}?type=container" - req = Request(url, headers={"Authorization": f"token {token}"}) - try: - with urlopen(req) as resp: - packages = json.loads(resp.read()) - except Exception as e: - print(f"Warning: failed to list packages for cleanup: {e}") - return - - numeric_versions = [] - for pkg in packages: - if pkg.get("name") == pkg_name: - ver = pkg.get("version", "") - if ver.isdigit(): - numeric_versions.append(int(ver)) - - numeric_versions.sort() - to_delete = numeric_versions[:-3] if len(numeric_versions) > 3 else [] - - for ver in to_delete: - del_url = f"https://{GITEA_HOST}/api/v1/packages/{org}/container/{pkg_name}/{ver}" - print(f" Deleting {pkg_name}:{ver}") - req = Request(del_url, method="DELETE", headers={"Authorization": f"token {token}"}) - try: - urlopen(req) - except Exception as e: - print(f" Warning: failed to delete {pkg_name}:{ver}: {e}") - - if to_delete: - print(f" Cleaned up {len(to_delete)} old tags") - else: - print(" No old tags to clean up") - - -def docker_cleanup(): - run("docker system prune --all --force") - - -def render_site_manifests(site_name, action_dir, app_dir, manifests_dir, cfg, extra=None): +def render_site_manifests(site_name, action_dir, app_dir, manifests_dir, cfg): """Always re-render manifests from current site.yaml. Templates own domain + aliases, so changes propagate without manual edits.""" manifests_dir.mkdir(parents=True, exist_ok=True) @@ -173,11 +117,8 @@ def render_site_manifests(site_name, action_dir, app_dir, manifests_dir, cfg, ex "domain": cfg["domain"], "aliases": cfg["aliases"], "namespace": NAMESPACE, - "site_type": cfg["type"], } - if extra: - template_vars.update(extra) - render_templates(action_dir, template_vars, app_dir, manifests_dir, cfg["type"]) + render_templates(action_dir, template_vars, app_dir, manifests_dir) def deploy_static(site_name, site_dir, action_dir, token, cfg): @@ -193,30 +134,6 @@ def deploy_static(site_name, site_dir, action_dir, token, cfg): commit_and_push(apps_dir, f"Deploy {site_name}") -def deploy_docker(site_name, site_dir, action_dir, token, cfg): - docker_push(cfg) - - run_number = env("GITHUB_RUN_NUMBER", "0") - image = cfg["image"] - apps_dir = clone_apps(token) - app_dir = apps_dir / "sjc001" / "websites" / site_name - manifests_dir = app_dir / "manifests" - - render_site_manifests(site_name, action_dir, app_dir, manifests_dir, cfg, extra={ - "image": image, - "port": cfg["port"], - "health_path": cfg["health_path"], - "replicas": cfg["replicas"], - }) - # Pin to this build's tag (kustomize edit appends an `images:` override). - run(f"cd {manifests_dir} && kustomize edit set image {image}={image}:{run_number}") - - commit_and_push(apps_dir, f"Deploy {site_name} #{run_number}") - - docker_tag_cleanup(cfg, token) - docker_cleanup() - - def decommission(site_name, token): """Remove manifests from apps repo.""" user = env("CI_BOT_USER", "ci-bot") @@ -249,7 +166,4 @@ def cmd_deploy(): decommission(site_name, token) return - if cfg["type"] == "docker": - deploy_docker(site_name, site_dir, action_dir, token, cfg) - else: - deploy_static(site_name, site_dir, action_dir, token, cfg) + deploy_static(site_name, site_dir, action_dir, token, cfg) diff --git a/scripts/setup.py b/scripts/setup.py index 528ac0f..701ae1d 100644 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -24,13 +24,6 @@ def ensure_aws(): subprocess.run(["aws", "--version"], check=True) -def ensure_docker(): - if shutil.which("docker"): - subprocess.run(["docker", "version", "--format", "{{.Client.Version}}"], check=True) - return - print("WARNING: docker CLI not found — docker builds will fail", file=sys.stderr) - - def ensure_jinja2(): try: import jinja2 @@ -45,5 +38,4 @@ def ensure_jinja2(): if __name__ == "__main__": ensure_jinja2() ensure_aws() - ensure_docker() print("Setup complete") diff --git a/scripts/utils.py b/scripts/utils.py index 75ba94e..fcb7436 100644 --- a/scripts/utils.py +++ b/scripts/utils.py @@ -1,4 +1,4 @@ -"""Shared utilities for the publish-site action.""" +"""Shared utilities for the site-publish action.""" import os import shutil @@ -20,7 +20,26 @@ EXCLUDE_FILES = { "Dockerfile", ".dockerignore", "go.mod", "go.sum", } -VALID_TYPES = {"static", "hugo", "mkdocs", "docker"} +VALID_TYPES = {"static", "hugo", "mkdocs"} + +DOCKER_DEPRECATION_MSG = """\ +type: docker is no longer supported by action/site-publish. + +site-publish handles only static-content sites (static, hugo, mkdocs) +that ship to Garage S3. For containerized web apps, use the standard +image-producer chain: + + - uses: action/image-build@v1 # build + smoke-test + - uses: action/image-push@v1 # push + prune + - uses: action/image-deploy@v1 # apps repo image-pin + +Hand-author your apps-repo manifests once (Deployment, Service, Ingress, +Certificate, kustomization with images: block) under +sjc001/websites//manifests/. image-deploy will pin the tag on +every CI run. See action/image-deploy README and +sjc001/websites/rainsounds.vino.network/manifests/ for the canonical +example.\ +""" def k8s_name(name): @@ -57,6 +76,10 @@ def parse_site_yaml(site_dir): die("domain is required in site.yaml") site_type = cfg.get("type", "static") + + if site_type == "docker": + die(DOCKER_DEPRECATION_MSG) + if site_type not in VALID_TYPES: die(f"Unknown site type: {site_type} (valid: {', '.join(sorted(VALID_TYPES))})") @@ -65,20 +88,10 @@ def parse_site_yaml(site_dir): "type": site_type, "enabled": cfg.get("enabled", True), "aliases": cfg.get("aliases") or [], + "content_dir": cfg.get("content_dir", ""), + "tidy": cfg.get("tidy", True), } - if site_type == "docker": - if not cfg.get("image"): - die("image is required in site.yaml for type: docker") - site["image"] = cfg["image"] - site["port"] = cfg.get("port", 8080) - site["build_args"] = cfg.get("build_args") or {} - site["health_path"] = cfg.get("health_path", "/healthz") - site["replicas"] = cfg.get("replicas", 1) - else: - site["content_dir"] = cfg.get("content_dir", "") - site["tidy"] = cfg.get("tidy", True) - print("Site config:") for k, v in site.items(): print(f" {k}: {v}") @@ -96,30 +109,21 @@ def clone_apps(token): return apps_dir -def render_templates(action_dir, template_vars, app_dir, manifests_dir, site_type): - """Render Jinja2 templates, selecting the right set for the site type.""" +def render_templates(action_dir, template_vars, app_dir, manifests_dir): + """Render Jinja2 templates for a static-content site.""" templates_dir = Path(action_dir) / "templates" jinja_env = Environment( loader=FileSystemLoader(str(templates_dir)), keep_trailing_newline=True, ) - if site_type == "docker": - service_tmpl = "service-docker.yaml.j2" - tmpl_names = ["app.yaml.j2", "certificate.yaml.j2", "ingress.yaml.j2", - "kustomization.yaml.j2", service_tmpl, "deployment.yaml.j2"] - else: - service_tmpl = "service-static.yaml.j2" - tmpl_names = ["app.yaml.j2", "certificate.yaml.j2", "ingress.yaml.j2", - "kustomization.yaml.j2", service_tmpl] + tmpl_names = ["app.yaml.j2", "certificate.yaml.j2", "ingress.yaml.j2", + "kustomization.yaml.j2", "service.yaml.j2"] for tmpl_name in tmpl_names: tmpl = jinja_env.get_template(tmpl_name) rendered = tmpl.render(**template_vars) out_name = tmpl_name.replace(".j2", "") - # service-docker.yaml / service-static.yaml → service.yaml - if out_name.startswith("service-"): - out_name = "service.yaml" dest = app_dir / out_name if tmpl_name == "app.yaml.j2" else manifests_dir / out_name dest.write_text(rendered) print(f" Rendered {tmpl_name} -> {dest}") diff --git a/templates/deployment.yaml.j2 b/templates/deployment.yaml.j2 deleted file mode 100644 index b49f94b..0000000 --- a/templates/deployment.yaml.j2 +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ site_k8s }} - namespace: {{ namespace }} - labels: - app: {{ site_k8s }} -spec: - replicas: {{ replicas }} - strategy: - type: Recreate - selector: - matchLabels: - app: {{ site_k8s }} - template: - metadata: - labels: - app: {{ site_k8s }} - spec: - securityContext: - runAsNonRoot: true - runAsUser: 65532 - runAsGroup: 65532 - fsGroup: 65532 - seccompProfile: - type: RuntimeDefault - containers: - - name: {{ site_k8s }} - image: {{ image }}:latest - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: {{ port }} - resources: - requests: - memory: 64Mi - cpu: 50m - limits: - memory: 256Mi - livenessProbe: - httpGet: {path: {{ health_path }}, port: http} - periodSeconds: 30 - timeoutSeconds: 3 - readinessProbe: - httpGet: {path: {{ health_path }}, port: http} - periodSeconds: 10 - timeoutSeconds: 3 - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - capabilities: - drop: ["ALL"] diff --git a/templates/kustomization.yaml.j2 b/templates/kustomization.yaml.j2 index eff8360..14ebd47 100644 --- a/templates/kustomization.yaml.j2 +++ b/templates/kustomization.yaml.j2 @@ -1,9 +1,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -{% if site_type == "docker" %} -- deployment.yaml -{% endif %} - service.yaml - ingress.yaml - certificate.yaml diff --git a/templates/service-docker.yaml.j2 b/templates/service-docker.yaml.j2 deleted file mode 100644 index e8703dc..0000000 --- a/templates/service-docker.yaml.j2 +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ site_k8s }} - namespace: {{ namespace }} -spec: - clusterIP: None - selector: - app: {{ site_k8s }} - ports: - - name: http - port: 80 - targetPort: http diff --git a/templates/service-static.yaml.j2 b/templates/service.yaml.j2 similarity index 100% rename from templates/service-static.yaml.j2 rename to templates/service.yaml.j2