From 431d50d930913f5634ad0ce2f9df9847959f21ef Mon Sep 17 00:00:00 2001 From: Tommy Skaug Date: Sat, 12 Oct 2024 10:01:23 +0200 Subject: [PATCH] testing: initial commit with port from buroa and significant changes --- .forgejo/scripts/json-to-yaml.py | 26 +++ .forgejo/scripts/prepare_matrices.py | 222 +++++++++++++++++++++++ .forgejo/scripts/render_readme.py | 67 +++++++ .forgejo/scripts/requirements.txt | 4 + .forgejo/scripts/templates/README.org.j2 | 121 ++++++++++++ .forgejo/workflows/render-readme.yaml | 47 +++++ LICENSE | 202 +++++++++++++++++++++ Taskfile.yaml | 20 ++ apps/forgejo-runner/Dockerfile | 47 +++++ apps/forgejo-runner/ci/goss.yaml | 4 + apps/forgejo-runner/ci/latest.sh | 5 + apps/forgejo-runner/metadata.yaml | 9 + assets/macchiato-palette.png | Bin 0 -> 1037 bytes metadata.rules.cue | 19 ++ 14 files changed, 793 insertions(+) create mode 100644 .forgejo/scripts/json-to-yaml.py create mode 100644 .forgejo/scripts/prepare_matrices.py create mode 100644 .forgejo/scripts/render_readme.py create mode 100644 .forgejo/scripts/requirements.txt create mode 100644 .forgejo/scripts/templates/README.org.j2 create mode 100644 .forgejo/workflows/render-readme.yaml create mode 100644 LICENSE create mode 100644 Taskfile.yaml create mode 100644 apps/forgejo-runner/Dockerfile create mode 100644 apps/forgejo-runner/ci/goss.yaml create mode 100644 apps/forgejo-runner/ci/latest.sh create mode 100644 apps/forgejo-runner/metadata.yaml create mode 100644 assets/macchiato-palette.png create mode 100644 metadata.rules.cue diff --git a/.forgejo/scripts/json-to-yaml.py b/.forgejo/scripts/json-to-yaml.py new file mode 100644 index 0000000..2653a04 --- /dev/null +++ b/.forgejo/scripts/json-to-yaml.py @@ -0,0 +1,26 @@ +import os +import json +import yaml + + +def json_to_yaml(subdir, file): + obj = None + + json_file = os.path.join(subdir, file) + with open(json_file) as f: + obj = json.load(f) + + yaml_file = os.path.join(subdir, "metadata.yaml") + with open(yaml_file, "w") as f: + yaml.dump(obj, f) + + os.remove(json_file) + + +if __name__ == "__main__": + + for subdir, dirs, files in os.walk("./apps"): + for f in files: + if f != "metadata.json": + continue + json_to_yaml(subdir, f) \ No newline at end of file diff --git a/.forgejo/scripts/prepare_matrices.py b/.forgejo/scripts/prepare_matrices.py new file mode 100644 index 0000000..f801994 --- /dev/null +++ b/.forgejo/scripts/prepare_matrices.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 +import importlib.util +import sys +import os + +import json +import yaml +import requests + +from os.path import isfile +from subprocess import check_output +from packaging.version import Version + +repo_owner = os.environ.get("REPO_OWNER", os.environ.get("GITHUB_REPOSITORY_OWNER")) + +TESTABLE_PLATFORMS = ["linux/amd64"] + + +def load_metadata_file_yaml(file_path): + with open(file_path, "r") as f: + return yaml.safe_load(f) + + +def load_metadata_file_json(file_path): + with open(file_path, "r") as f: + return json.load(f) + + +def get_latest_version_py(latest_py_path, channel_name): + spec = importlib.util.spec_from_file_location("latest", latest_py_path) + latest = importlib.util.module_from_spec(spec) + sys.modules["latest"] = latest + spec.loader.exec_module(latest) + return latest.get_latest(channel_name) + + +def get_latest_version_sh(latest_sh_path, channel_name): + out = check_output([latest_sh_path, channel_name]) + return out.decode("utf-8").strip() + + +def get_latest_version(subdir, channel_name): + ci_dir = os.path.join(subdir, "ci") + if os.path.isfile(os.path.join(ci_dir, "latest.py")): + return get_latest_version_py(os.path.join(ci_dir, "latest.py"), channel_name) + elif os.path.isfile(os.path.join(ci_dir, "latest.sh")): + return get_latest_version_sh(os.path.join(ci_dir, "latest.sh"), channel_name) + elif os.path.isfile(os.path.join(subdir, channel_name, "latest.py")): + return get_latest_version_py( + os.path.join(subdir, channel_name, "latest.py"), channel_name + ) + elif os.path.isfile(os.path.join(subdir, channel_name, "latest.sh")): + return get_latest_version_sh( + os.path.join(subdir, channel_name, "latest.sh"), channel_name + ) + return None + + +def get_published_version(image_name): + r = requests.get( + f"https://api.github.com/users/{repo_owner}/packages/container/{image_name}/versions", + headers={ + "Accept": "application/vnd.github.v3+json", + "Authorization": "token " + os.environ["TOKEN"], + }, + ) + + if r.status_code != 200: + return None + + data = json.loads(r.text) + for image in data: + tags = image["metadata"]["container"]["tags"] + if "rolling" in tags: + tags.remove("rolling") + return max(tags, key=len) + + +def get_image_metadata(subdir, meta, forRelease=False, force=False, channels=None): + imagesToBuild = {"images": [], "imagePlatforms": []} + + if channels is None: + channels = meta["channels"] + else: + channels = [ + channel for channel in meta["channels"] if channel["name"] in channels + ] + + for channel in channels: + version = get_latest_version(subdir, channel["name"]) + if not version: + continue + + # Image Name + toBuild = {} + if channel.get("stable", False): + toBuild["name"] = meta["app"] + else: + toBuild["name"] = "-".join([meta["app"], channel["name"]]) + + # Skip if latest version already published + if not force: + published = get_published_version(toBuild["name"]) + if published is not None and published == version: + continue + toBuild["published_version"] = published + + toBuild["version"] = version + + # Image Tags + toBuild["tags"] = ["rolling", version] + if meta.get("semver", False): + parts = version.split(".")[:-1] + while len(parts) > 0: + toBuild["tags"].append(".".join(parts)) + parts = parts[:-1] + + # Platform Metadata + for platform in channel["platforms"]: + + if platform not in TESTABLE_PLATFORMS and not forRelease: + continue + + toBuild.setdefault("platforms", []).append(platform) + + target_os = platform.split("/")[0] + target_arch = platform.split("/")[1] + + platformToBuild = {} + platformToBuild["name"] = toBuild["name"] + platformToBuild["platform"] = platform + platformToBuild["target_os"] = target_os + platformToBuild["target_arch"] = target_arch + platformToBuild["version"] = version + platformToBuild["channel"] = channel["name"] + platformToBuild["label_type"] = "org.opencontainers.image" + + if isfile(os.path.join(subdir, channel["name"], "Dockerfile")): + platformToBuild["dockerfile"] = os.path.join( + subdir, channel["name"], "Dockerfile" + ) + platformToBuild["context"] = os.path.join(subdir, channel["name"]) + platformToBuild["goss_config"] = os.path.join( + subdir, channel["name"], "goss.yaml" + ) + else: + platformToBuild["dockerfile"] = os.path.join(subdir, "Dockerfile") + platformToBuild["context"] = subdir + platformToBuild["goss_config"] = os.path.join(subdir, "ci", "goss.yaml") + + platformToBuild["goss_args"] = ( + "tail -f /dev/null" + if channel["tests"].get("type", "web") == "cli" + else "" + ) + + platformToBuild["tests_enabled"] = ( + channel["tests"]["enabled"] and platform in TESTABLE_PLATFORMS + ) + + imagesToBuild["imagePlatforms"].append(platformToBuild) + imagesToBuild["images"].append(toBuild) + return imagesToBuild + + +if __name__ == "__main__": + apps = sys.argv[1] + forRelease = sys.argv[2] == "true" + force = sys.argv[3] == "true" + imagesToBuild = {"images": [], "imagePlatforms": []} + + if apps != "all": + channels = None + apps = apps.split(",") + if len(sys.argv) == 5: + channels = sys.argv[4].split(",") + + for app in apps: + if not os.path.exists(os.path.join("./apps", app)): + print(f'App "{app}" not found') + exit(1) + + meta = None + if os.path.isfile(os.path.join("./apps", app, "metadata.yaml")): + meta = load_metadata_file_yaml( + os.path.join("./apps", app, "metadata.yaml") + ) + elif os.path.isfile(os.path.join("./apps", app, "metadata.json")): + meta = load_metadata_file_json( + os.path.join("./apps", app, "metadata.json") + ) + + imageToBuild = get_image_metadata( + os.path.join("./apps", app), + meta, + forRelease, + force=force, + channels=channels, + ) + if imageToBuild is not None: + imagesToBuild["images"].extend(imageToBuild["images"]) + imagesToBuild["imagePlatforms"].extend(imageToBuild["imagePlatforms"]) + else: + for subdir, dirs, files in os.walk("./apps"): + for file in files: + meta = None + if file == "metadata.yaml": + meta = load_metadata_file_yaml(os.path.join(subdir, file)) + elif file == "metadata.json": + meta = load_metadata_file_json(os.path.join(subdir, file)) + else: + continue + if meta is not None: + imageToBuild = get_image_metadata( + subdir, meta, forRelease, force=force + ) + if imageToBuild is not None: + imagesToBuild["images"].extend(imageToBuild["images"]) + imagesToBuild["imagePlatforms"].extend( + imageToBuild["imagePlatforms"] + ) + print(json.dumps(imagesToBuild)) \ No newline at end of file diff --git a/.forgejo/scripts/render_readme.py b/.forgejo/scripts/render_readme.py new file mode 100644 index 0000000..f9e1479 --- /dev/null +++ b/.forgejo/scripts/render_readme.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +import os +import yaml +import logging + +from jinja2 import Environment, PackageLoader, select_autoescape + +logging.basicConfig(level=logging.INFO) + +repo_owner = os.getenv("REPO_OWNER") or os.getenv( + "GITHUB_REPOSITORY_OWNER", "default_owner" +) +repo_name = os.getenv("REPO_NAME") or os.getenv("GITHUB_REPOSITORY", "default_repo") + +env = Environment(loader=PackageLoader("render-readme"), autoescape=select_autoescape()) + + +def load_metadata(file_path): + try: + with open(file_path, "r") as f: + return yaml.safe_load(f) + except yaml.YAMLError as e: + logging.error(f"Error loading YAML file {file_path}: {e}") + except FileNotFoundError: + logging.error(f"File {file_path} not found.") + return None + + +def process_metadata(apps_dir): + app_images = [] + for subdir, _, files in os.walk(apps_dir): + if "metadata.yaml" not in files: + continue # Skip if metadata file not found + + meta = load_metadata(os.path.join(subdir, "metadata.yaml")) + if not meta: + continue # Skip if metadata couldn't be loaded + + # Iterate through the channels and build image metadata + for channel in meta.get("channels", []): + name = ( + meta["app"] + if channel.get("stable", False) + else f"{meta['app']}-{channel['name']}" + ) + image = { + "name": name, + "channel": channel["name"], + "html_url": f"https://code.252.no/{repo_owner}/pkgs/container/{name}", + "owner": repo_owner, + } + app_images.append(image) + logging.info(f"Added image {name} from channel {channel['name']}") + return app_images + + +if __name__ == "__main__": + apps_dir = "./apps" + app_images = process_metadata(apps_dir) + + try: + template = env.get_template("README.md.j2") + with open("./README.md", "w") as f: + f.write(template.render(app_images=app_images)) + logging.info("README.md successfully generated.") + except Exception as e: + logging.error(f"Error rendering template: {e}") \ No newline at end of file diff --git a/.forgejo/scripts/requirements.txt b/.forgejo/scripts/requirements.txt new file mode 100644 index 0000000..37641f6 --- /dev/null +++ b/.forgejo/scripts/requirements.txt @@ -0,0 +1,4 @@ +requests +pyyaml +packaging +jinja2 \ No newline at end of file diff --git a/.forgejo/scripts/templates/README.org.j2 b/.forgejo/scripts/templates/README.org.j2 new file mode 100644 index 0000000..15f3253 --- /dev/null +++ b/.forgejo/scripts/templates/README.org.j2 @@ -0,0 +1,121 @@ +#+DATE: 2024-10-12 +#+OPTIONS: toc:nil +#+macro: issue [[https://code.252.no/tommy/containers/issues/$1][issue #$1]] +#+macro: pr [[https://code.252.no/tommy/containers/pulls/$1][PR #$1]] +#+export_file_name: kagi +#+property: header-args:elisp :results none :exports code + +#+BEGIN_EXPORT html +
+

Container Collection

+_Containers for Kubernetes deployment_ + +

+ + Nix Flakes Ready + +

+
+
+#+END_EXPORT + +** Available Images + +| Container | Channel | Image | +|-------------------------------------------+---------------------+------------------------------------------------| +{% for image in app_images | sort(attribute="name") -%} +|[[{{ image.html_url }}][{{ image.name }}]] | {{ image.channel }} | code.252.no/{{ image.owner }}/{{ image.name }} | +{% endfor %} + +** Container Rules + +Containers in this project should be useful in Kubernetes. They will be: + +- [[https://semver.org/][semantically versioned]] +- [[https://rootlesscontaine.rs/][rootless]] +- logging to stdout in JSON format if possible +- using [[https://testdriven.io/tips/59de3279-4a2d-4556-9cd0-b444249ed31e/][one process per container]] +- having no [[https://github.com/just-containers/s6-overlay][s6-overlay]] +- built on [[https://hub.docker.com/_/alpine][Alpine]] +- [[https://glossary.cncf.io/immutable-infrastructure/][immutable]] +- do no monkey business after deployed + +Additionally I may in the future support: + +- [[https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/][multiple architecture]]. For now containers are + generated for amd64 + + +## Tag immutability + +Instead of using immutable version tags as seen on e.g. [[https://fleet.linuxserver.io/][linuxserver.io]] we +use [[https://www.letsdebug.it/post/41-calendar-versioning/][calver]] and sha256-digests. + +If pinning an image to the sha256 digest, tools like [Renovate](https://github.com/renovatebot/renovate) support +updating the container on a digest or application version change. + +The schema used is: =YYYY.MM.Minor@sha256:digest=. This is not as pretty, but functional and immutable. Examples: + +| Container | Immutable | +|------------------------------------------------------------------------+-----------| +| =code.252.no/tommy/containers/forgejo-runner:v24.10.1= | ❌ | +| =code.252.no/tommy/containers/forgejo-runner:v24.10.1@sha256:1234...= | ✅ | + + + +** Kubernetes Pod Security Standard + +In Kubernetes we assume that you have pod-security.kubernetes.io/enforce set to +[[https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted][restricted]]. There may be +some exceptions to this if the container actually requires more privileges. + +E.g. for the =forgejo-runner=, which runs as user ID =1000=, this means that the following settings should be +used for the pod (all containers in a pod): + +#+begin_src yaml +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 +#+end_src + +For a container this means: + +#+begin_src yaml +spec: + [...] + containers: + - name: forgejo-runner + [...] + securityContext: + runAsUser: 1001 + capabilities: + drop: ["ALL"] + privileged: false + allowPrivilegeEscalation: false + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault +#+end_src + +** Configuration volume + +For applications that need to have persistent configuration data the config volume is hardcoded to `/config` +inside the container. + + +** Deprecations + +Containers here can be **deprecated** at any point, this could be for any reason described below. + +1. The upstream application is **no longer actively developed** +2. The upstream application has an **official upstream container** that follows closely to the mission statement described here +3. The upstream application has been **replaced with a better alternative** +4. The **maintenance burden** of keeping the container here **is too bothersome** + + +** Credits + +The structure of this repo was inspired and partially ripped off from +[Buroa@github](https://github.com/buroa/containers/tree/master). \ No newline at end of file diff --git a/.forgejo/workflows/render-readme.yaml b/.forgejo/workflows/render-readme.yaml new file mode 100644 index 0000000..1405763 --- /dev/null +++ b/.forgejo/workflows/render-readme.yaml @@ -0,0 +1,47 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: "Render Readme" + +on: + workflow_call: + secrets: + BOT_APP_ID: + description: The App ID of the GitHub App + required: true + BOT_APP_PRIVATE_KEY: + description: The private key of the GitHub App + required: true + +jobs: + render-readme: + name: Render README + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.x + cache: pip + + - name: Install Python Requirements + shell: bash + run: pip install -r ./.forgejo/scripts/requirements.txt && pip freeze + + - name: Render README + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + shell: bash + run: python ./.github/scripts/render-readme.py + + - name: Commit Changes + shell: bash + run: | + git config --global user.name "forgejo-workflow" + git config --global user.email "tommy+forgejo@252.no" + git add ./README.org + git commit -m "chore: render README.org" || echo "No changes to commit" + git push origin || echo "No changes to push" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c151529 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 @ Tommy Skaug, tommy@252.no + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + \ No newline at end of file diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..f6c7671 --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,20 @@ +version: "3" + +vars: + LABELS_CONFIG_FILE: '{{.ROOT_DIR}}/.github/labels.yaml' + +tasks: + default: + cmd: task -l + silent: true + + append-app-labels: + desc: Append app labels to the labels config file + cmds: + - for: {var: apps} + cmd: | + yq -i '. += [{"name": "app/{{.ITEM}}", "color": "0e8a16"}]' {{.LABELS_CONFIG_FILE}} + vars: + apps: + sh: for dir in {{.ROOT_DIR}}/apps/*/; do basename "${dir}"; done + silent: true diff --git a/apps/forgejo-runner/Dockerfile b/apps/forgejo-runner/Dockerfile new file mode 100644 index 0000000..50f1965 --- /dev/null +++ b/apps/forgejo-runner/Dockerfile @@ -0,0 +1,47 @@ +FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/tonistiigi/xx AS xx + +FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.21-alpine3.19 as build-env + +# +# Transparently cross compile for the target platform +# +COPY --from=xx / / +ARG TARGETPLATFORM +RUN apk --no-cache add clang lld +RUN xx-apk --no-cache add gcc musl-dev +RUN xx-go --wrap + +# Do not remove `git` here, it is required for getting runner version when executing `make build` +RUN apk add --no-cache build-base git + +COPY . /srv +WORKDIR /srv + +RUN make clean && make build + +FROM code.forgejo.org/oci/alpine:3.19 +ARG RELEASE_VERSION +RUN apk add --no-cache git bash + +COPY --from=build-env /srv/forgejo-runner /bin/forgejo-runner + +LABEL maintainer="contact@forgejo.org" \ + org.opencontainers.image.authors="Forgejo" \ + org.opencontainers.image.url="https://forgejo.org" \ + org.opencontainers.image.documentation="https://forgejo.org/docs/latest/admin/actions/#forgejo-runner" \ + org.opencontainers.image.source="https://code.forgejo.org/forgejo/runner" \ + org.opencontainers.image.version="${RELEASE_VERSION}" \ + org.opencontainers.image.vendor="Forgejo" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.title="Forgejo Runner" \ + org.opencontainers.image.description="A runner for Forgejo Actions." + +ENV HOME=/data + +USER 1000:1000 + +WORKDIR /data + +VOLUME ["/data"] + +CMD ["/bin/forgejo-runner"] diff --git a/apps/forgejo-runner/ci/goss.yaml b/apps/forgejo-runner/ci/goss.yaml new file mode 100644 index 0000000..e1d81ad --- /dev/null +++ b/apps/forgejo-runner/ci/goss.yaml @@ -0,0 +1,4 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml +file: + /usr/bin/git: + exists: true diff --git a/apps/forgejo-runner/ci/latest.sh b/apps/forgejo-runner/ci/latest.sh new file mode 100644 index 0000000..c62d868 --- /dev/null +++ b/apps/forgejo-runner/ci/latest.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +version=$(curl -sX GET "https://api.github.com/repos/actions/runner/releases/latest" | jq --raw-output '.tag_name') +version="${version#*v}" +version="${version#*release-}" +printf "%s" "${version}" \ No newline at end of file diff --git a/apps/forgejo-runner/metadata.yaml b/apps/forgejo-runner/metadata.yaml new file mode 100644 index 0000000..f2a77a3 --- /dev/null +++ b/apps/forgejo-runner/metadata.yaml @@ -0,0 +1,9 @@ +app: forgejo-runner +versioning: calver +channels: +- name: stable + platforms: ["linux/amd64"] + stable: false + tests: + enabled: true + type: cli diff --git a/assets/macchiato-palette.png b/assets/macchiato-palette.png new file mode 100644 index 0000000000000000000000000000000000000000..94b088303e428db39a2b130334209247fa693d8f GIT binary patch literal 1037 zcmeAS@N?(olHy`uVBq!ia0y~yU{?dOWjL6Cg?@;1B=u?U!LXU^WbA@Pb%lG>f4SToII;vM@(C=`lBRJIDK

Ns}4)NPq@;GxBlO)R?i|K5EPckr*p>DJhqOJ`rbf5cnFa5-j* z3KI*5fPzB<10ynH3-^qe+tOw7D1uOBj*OGD1)@c;NxfxKyZ){1t~;7(AdLikVMdI?3j`8R5@xu1pAXPskCAU3H{uc1fx cu#UC=I}VE+Tvq#!2b2{(UHx3vIVCg!0D-yld;kCd literal 0 HcmV?d00001 diff --git a/metadata.rules.cue b/metadata.rules.cue new file mode 100644 index 0000000..b0cff90 --- /dev/null +++ b/metadata.rules.cue @@ -0,0 +1,19 @@ +#Spec: { + app: #AppName + semver?: bool + channels: [...#Channels] +} + +#Channels: { + name: #ChannelName + platforms: [...#Platforms] + stable: bool + tests: { + enabled: bool + type?: =~"^(cli|web)$" + } +} + +#AppName: string & !="" & =~"^[a-zA-Z0-9_-]+$" +#ChannelName: string & !="" & =~"^[a-zA-Z0-9._-]+$" +#Platforms: "linux/amd64" | "linux/arm64" \ No newline at end of file