diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..f6a87d2 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @castrojo diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..d26c3c4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + diff --git a/.github/pull.yml b/.github/pull.yml new file mode 100644 index 0000000..0fe7714 --- /dev/null +++ b/.github/pull.yml @@ -0,0 +1,6 @@ +version: "1" +rules: + - base: main + upstream: blue-build:main + mergeMethod: merge + mergeUnstable: false diff --git a/.github/semantic.yml b/.github/semantic.yml new file mode 100644 index 0000000..b5161df --- /dev/null +++ b/.github/semantic.yml @@ -0,0 +1,2 @@ +enabled: true +titleOnly: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..74d86e1 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,224 @@ +# This workflow builds every branch of the repository daily at 16:30 UTC, one hour after ublue-os/nvidia builds. +# The images are also built after pushing changes or pull requests. +# The builds can also be triggered manually in the Actions tab thanks to workflow dispatch. +# Only the branch called `live` is published. + + +name: build-ublue +on: # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows + schedule: + - cron: "30 16 * * *" + push: + branches: + - live + - template + - main + paths-ignore: # don't rebuild if only documentation has changed + - "**.md" + pull_request: + workflow_dispatch: + +env: + IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} + +# Only deploys the branch named "live". Ignores all other branches, to allow +# having "development" branches without interfering with GHCR image uploads. +jobs: + push-ghcr: + name: Build and push image + runs-on: ubuntu-22.04 + permissions: + contents: read + packages: write + id-token: write + strategy: + fail-fast: false + + matrix: +# !!! + # Add recipes for all the images you want to build here. + # Don't add module configuration files, you will get errors. + recipe: + - recipe.yml +# !!! + + steps: + # Checkout push-to-registry action GitHub repository + - name: Checkout Push to Registry action + uses: actions/checkout@v4 + + # Confirm that cosign.pub matches SIGNING_SECRET + - uses: sigstore/cosign-installer@v3.3.0 + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/live' + + - name: Check SIGNING_SECRET matches cosign.pub + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/live' + env: + COSIGN_EXPERIMENTAL: false + COSIGN_PASSWORD: "" + COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} + shell: bash + run: | + echo "Checking for difference between public key from SIGNING_SECRET and cosign.pub" + delta=$(diff -u <(cosign public-key --key env://COSIGN_PRIVATE_KEY) cosign.pub) + if [ -z "$delta" ]; then + echo "cosign.pub matches SIGNING_SECRET" + else + echo "cosign.pub does not match SIGNING_SECRET" + echo "$delta" + exit 1 + fi + + - name: Add yq (for reading recipe.yml) + uses: mikefarah/yq@v4.40.5 + + - name: Gather image data from recipe + run: | + echo "IMAGE_NAME=$(yq '.name' ./config/${{ matrix.recipe }})" >> $GITHUB_ENV + echo "IMAGE_DESCRIPTION=$(yq '.description' ./config/${{ matrix.recipe }})" >> $GITHUB_ENV + echo "IMAGE_MAJOR_VERSION=$(yq '.image-version' ./config/${{ matrix.recipe }})" >> $GITHUB_ENV + BASE_IMAGE=$(yq '.base-image' ./config/${{ matrix.recipe }}) + echo "BASE_IMAGE_URL=$BASE_IMAGE" >> $GITHUB_ENV + echo "BASE_IMAGE_NAME=$(echo $BASE_IMAGE | sed 's/.*\/.*\///')" >> $GITHUB_ENV + + - name: Verify base image + uses: EyeCantCU/cosign-action/verify@v0.2.2 + with: + containers: ${{ env.BASE_IMAGE_NAME }}:${{ env.IMAGE_MAJOR_VERSION }} + + - name: Get current version + id: labels + run: | + ver=$(skopeo inspect docker://${{ env.BASE_IMAGE_URL }}:${{ env.IMAGE_MAJOR_VERSION }} | jq -r '.Labels["org.opencontainers.image.version"]') + echo "VERSION=$ver" >> $GITHUB_OUTPUT + + - name: Generate tags + id: generate-tags + shell: bash + run: | + # Generate a timestamp for creating an image version history + TIMESTAMP="$(date +%Y%m%d)" + MAJOR_VERSION="$(echo ${{ steps.labels.outputs.VERSION }} | cut -d . -f 1)" + COMMIT_TAGS=() + BUILD_TAGS=() + # Have tags for tracking builds during pull request + SHA_SHORT="${GITHUB_SHA::7}" + + # Using clever bash string templating, https://stackoverflow.com/q/40771781 + # don't make malformed tags if $MAJOR_VERSION is empty (base-image didn't include proper labels) -- + COMMIT_TAGS+=("pr-${{ github.event.number }}${MAJOR_VERSION:+-$MAJOR_VERSION}") + COMMIT_TAGS+=("${SHA_SHORT}${MAJOR_VERSION:+-$MAJOR_VERSION}") + + BUILD_TAGS=("${MAJOR_VERSION}" "${MAJOR_VERSION:+$MAJOR_VERSION-}${TIMESTAMP}") + # -- + + BUILD_TAGS+=("${TIMESTAMP}") + BUILD_TAGS+=("latest") + + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "Generated the following commit tags: " + for TAG in "${COMMIT_TAGS[@]}"; do + echo "${TAG}" + done + alias_tags=("${COMMIT_TAGS[@]}") + else + alias_tags=("${BUILD_TAGS[@]}") + fi + echo "Generated the following build tags: " + for TAG in "${BUILD_TAGS[@]}"; do + echo "${TAG}" + done + echo "alias_tags=${alias_tags[*]}" >> $GITHUB_OUTPUT + + # Build metadata + - name: Image Metadata + uses: docker/metadata-action@v5 + id: meta + with: + images: | + ${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.title=${{ env.IMAGE_NAME }} + org.opencontainers.image.version=${{ steps.labels.outputs.VERSION }} + org.opencontainers.image.description=${{ env.IMAGE_DESCRIPTION }} + io.artifacthub.package.readme-url=https://raw.githubusercontent.com/ublue-os/startingpoint/main/README.md + io.artifacthub.package.logo-url=https://avatars.githubusercontent.com/u/120078124?s=200&v=4 + + # Workaround bug where capital letters in your GitHub username make it impossible to push to GHCR. + # https://github.com/macbre/push-to-ghcr/issues/12 + - name: Lowercase Registry + id: registry_case + uses: ASzc/change-string-case-action@v6 + with: + string: ${{ env.IMAGE_REGISTRY }} + + - name: Lowercase Image + id: image_case + uses: ASzc/change-string-case-action@v6 + with: + string: ${{ env.IMAGE_NAME }} + + - name: Maximize build space + uses: AdityaGarg8/remove-unwanted-software@v2 + with: + remove-dotnet: 'true' + remove-android: 'true' + remove-haskell: 'true' + + # Build image using Buildah action + - name: Build Image + id: build_image + uses: redhat-actions/buildah-build@v2 + with: + containerfiles: | + ./Containerfile + image: ${{ env.IMAGE_NAME }} + tags: | + ${{ steps.generate-tags.outputs.alias_tags }} + build-args: | + IMAGE_MAJOR_VERSION=${{ env.IMAGE_MAJOR_VERSION }} + BASE_IMAGE_URL=${{ env.BASE_IMAGE_URL }} + RECIPE=${{ matrix.recipe }} + IMAGE_REGISTRY=${{ steps.registry_case.outputs.lowercase }} + labels: ${{ steps.meta.outputs.labels }} + oci: false + + # Push the image to GHCR (Image Registry) + - name: Push To GHCR + uses: redhat-actions/push-to-registry@v2 + id: push + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/live' + env: + REGISTRY_USER: ${{ github.actor }} + REGISTRY_PASSWORD: ${{ github.token }} + with: + image: ${{ steps.build_image.outputs.image }} + tags: ${{ steps.build_image.outputs.tags }} + registry: ${{ steps.registry_case.outputs.lowercase }} + username: ${{ env.REGISTRY_USER }} + password: ${{ env.REGISTRY_PASSWORD }} + extra-args: | + --disable-content-trust + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/live' + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Sign container + - name: Sign container image + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/live' + run: | + cosign sign -y --key env://COSIGN_PRIVATE_KEY ${{ steps.registry_case.outputs.lowercase }}/${{ steps.image_case.outputs.lowercase }}@${TAGS} + env: + TAGS: ${{ steps.push.outputs.digest }} + COSIGN_EXPERIMENTAL: false + COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} + + - name: Echo outputs + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/live' + run: | + echo "${{ toJSON(steps.push.outputs) }}" diff --git a/.github/workflows/release-iso.yml b/.github/workflows/release-iso.yml new file mode 100644 index 0000000..899f934 --- /dev/null +++ b/.github/workflows/release-iso.yml @@ -0,0 +1,47 @@ +on: + push: + paths: + - 'boot_menu.yml' + - '.github/workflows/release-iso.yml' + workflow_dispatch: + +name: release-iso +jobs: + release-iso: + name: Generate and Release ISOs + runs-on: ubuntu-latest + permissions: + contents: write + container: + image: fedora:39 + options: --privileged + steps: + - uses: actions/checkout@v4 + - name: Generate ISO + uses: ublue-os/isogenerator@v2.3.1 + id: isogenerator + with: + image-name: ${{ github.event.repository.name }} + installer-repo: releases + installer-major-version: 39 + boot-menu-path: boot_menu.yml + - name: install github CLI + run: | + sudo dnf install 'dnf-command(config-manager)' -y + sudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo + sudo dnf install gh -y + - name: Upload ISO + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + if gh release list -R ${{ github.repository_owner }}/${{ github.event.repository.name }} | grep "auto-iso"; then + gh release view auto-iso -R ${{ github.repository_owner }}/${{ github.event.repository.name }} --json assets -q .assets[].name | xargs --no-run-if-empty -L 1 gh release delete-asset auto-iso -R ${{ github.repository_owner }}/${{ github.event.repository.name }} + gh release upload auto-iso ${{ steps.isogenerator.outputs.iso-path }} -R ${{ github.repository_owner }}/${{ github.event.repository.name }} --clobber + else + gh release create auto-iso ${{ steps.isogenerator.outputs.iso-path }} -t ISO -n "This is an automatically generated ISO release." -R ${{ github.repository_owner }}/${{ github.event.repository.name }} + fi + - name: Upload SHA256SUM + env: + GITHUB_TOKEN: ${{ github.token }} + run: + gh release upload auto-iso ${{ steps.isogenerator.outputs.sha256sum-path }} -R ${{ github.repository_owner }}/${{ github.event.repository.name }} --clobber diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a5a8ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +cosign.key +cosign.private diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9289907 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,141 @@ +# Welcome to Universal Blue + +Thanks for taking the time to look into helping out! +All contributions are appreciated! +Please refer to our [Code of Conduct](/CODE_OF_CONDUCT.md) while you're at it! + +Feel free to report issues as you find them, and [helping others in the discussions]() is always appreciated. + +# Contributing + +All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. + +> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: +> - Star the project +> - Tweet about it +> - Refer this project in your project's readme +> - Mention the project at local meetups and tell your friends/colleagues + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [I Have a Question](#i-have-a-question) +- [I Want To Contribute](#i-want-to-contribute) +- [Reporting Bugs](#reporting-bugs) +- [How to test incoming changes](#how-to-test-incoming-changes) +- [Building Locally](#building-locally) +- [Styleguides](#styleguides) +- [Commit Messages](#commit-messages) +- [Join The Project Team](#join-the-project-team) + +## Code of Conduct + +This project and everyone participating in it is governed by the +[CONTRIBUTING.md Code of Conduct](/CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. Please report unacceptable behavior +to jorge.castro@gmail.com + +## I Have a Question + +> If you want to ask a question, ask in the [discussion forum](https://github.com/orgs/ublue-os/discussions) + +## I Want To Contribute + +> ### Legal Notice +> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. + +Generally speaking we try to follow the [Lazy Concensus](http://lazyconcens.us/) model of development to keep the builds healthy and ourselves happy. + - If you're looking for concensus to make a decision post an issue for feedback and remember to account for timezones and weekends/holidays/work time. + - We want people to be opinionated in their builds so we're more of a loose confederation of repos than a top-down org. + - Try not to merge your own stuff, ask for a review. At some point when we have enough reviewers we'll be turning on branch protection. + +### Reporting Bugs + +#### Before Submitting a Bug Report + +A good bug report should describe the issue in detail. Generally speaking: + +- Make sure that you are using the latest version. +- Remember that these are unofficial builds, it's usually prudent to investigate an issue before reporting it here or in Fedora! +- Collect information about the bug: + - `rpm-ostree status -v` usually helps +- Image and Version +- Possibly your input and the output +- Can you reliably reproduce the issue? And can you also reproduce it with older versions? + +### How to test incoming changes + +One of the nice things about the image model is that we can generate an entire OS image for every change we want to commit, so this makes testing way easier than in the past. You can rebase to it, see if it works, and then move back. This also means we can increase the amount of testers! + +We strive towards a model where proposed changes are more thoroughly reviewed and tested by the community. So here's how to do it. If you see a pull request that is opened up on an image you're following you can leave a review on how it's working for you. At the bottom of every PR you'll see something like this: + +![image](https://user-images.githubusercontent.com/1264109/221305388-3860fc07-212c-4eb9-80d9-5d7a35a77f46.png) + +Click on "Add your review", and then you'll see this: + +![image](https://user-images.githubusercontent.com/1264109/221307636-5e312e48-821f-4206-848f-7fbc2c91cd78.png) + +Don't worry, you can't mess anything up, all the merging and stuff will be done by the maintainer, what this does is lets us gather information in a more formal manner than just shoving everything in a forum thread. The more people are reviewing and testing images, the better off we'll be, especially for images that are new like Sericea. + +At some point we'll have a bot that will leave you instructions on how to rebase to the image and all that stuff, but in the meantime we'll leave instructions manually. + +Here's an example: https://github.com/ublue-os/nvidia/pull/49 + +## Building Locally + +The minimum tools required are git and a working machine with podman enabled and configured. +Building locally is much faster than building in GitHub and is a good way to move fast before pushing to a remote. + +### Clone the repo you want + + git clone https://github.com/ublue-os/base.git + +### Build the image + +First make sure you can build an existing image: + + podman build . -t something + +Then confirm your image built: + + podman image ls + +TODO: Set up and push to your own local registry + +### Make your changes + +This usually involved editing the `Containerfile`. Most techniques for building containers apply here, if you're new to containers using the term "Dockerfile" in your searches usually shows more results when you're searching for information. + +Check out CoreOS's [layering examples](https://github.com/coreos/layering-examples) for more information on customizing. + +### Reporting problems to Fedora + +We endevaour to be a good partner for Fedora. + +This project is consuming new features in Fedora and ostree, it is not uncommon to find an issue. +Issues should be reported upstream, and in some cases we can help test and find fixes. +Some of the issues you find may involve other dependencies in other projects, in those cases the Fedora team will tell you where to report the issue. + +Upstream bug tracker: [https://github.com/fedora-silverblue/issue-tracker/issues](https://github.com/fedora-silverblue/issue-tracker/issues) + +## Styleguides +### Commit Messages + +We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and enforce them with a bot to keep the changelogs tidy: + +``` +chore: add Oyster build script +docs: explain hat wobble +feat: add beta sequence +fix: remove broken confirmation message +refactor: share logic between 4d3d3d3 and flarhgunnstow +style: convert tabs to spaces +test: ensure Tayne retains clothing +``` + +## Join The Project Team + +If you're interested in _maintaining_ something then let us know! + +## Attribution +This guide is based on the **contributing.md**. [Make your own](https://contributing.md/)! diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + 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 [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8012754 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# BlueBuild Template + +This is a constantly updating template repository for creating [a native container image](https://fedoraproject.org/wiki/Changes/OstreeNativeContainerStable) designed to be customized however you want. GitHub will build your image for you, and then host it for you on [ghcr.io](https://github.com/features/packages). You then just tell your computer to boot off of that image. GitHub keeps 90 days worth image backups for you, thanks Microsoft! + +For more info, check out the [BlueBuild homepage](https://blue-build.org/) and the [blue-build CLI](https://github.com/blue-build/cli) + +## Getting started + +See the [BlueBuild docs](https://blue-build.org/learn/getting-started) for quick setup instructions for setting up your own repository based on this template. + +Don't worry, it only requires some basic knowledge about using the terminal and git. + +After setup, it is recommended you update this README to describe your custom image. + +## Customization + +The easiest way to start customizing is by looking at and modifying `config/recipe.yml`. It's documented using comments and should be pretty easy to understand. + +If you want to add custom configuration files, you can just add them in the `/usr/etc/` directory, which is the official OSTree "configuration template" directory and will be applied to `/etc/` on boot. `config/files/usr` is copied into your image's `/usr` by default. If you need to add other directories in the root of your image, that can be done using the `files` module. Writing to `/var/` in the image builds of OSTree-based distros isn't supported and will not work, as that is a local user-managed directory! + +For more information about customization, see [the README in the config directory](config/README.md) + +## Installation + +> **Warning** +> [This is an experimental feature](https://www.fedoraproject.org/wiki/Changes/OstreeNativeContainerStable) and should not be used in production, try it in a VM for a while! + +To rebase an existing Silverblue/Kinoite installation to the latest build: + +- First rebase to the unsigned image, to get the proper signing keys and policies installed: + ``` + rpm-ostree rebase ostree-unverified-registry:ghcr.io/blue-build/template:latest + ``` +- Reboot to complete the rebase: + ``` + systemctl reboot + ``` +- Then rebase to the signed image, like so: + ``` + rpm-ostree rebase ostree-image-signed:docker://ghcr.io/blue-build/template:latest + ``` +- Reboot again to complete the installation + ``` + systemctl reboot + ``` + +This repository builds date tags as well, so if you want to rebase to a particular day's build: + +``` +rpm-ostree rebase ostree-image-signed:docker://ghcr.io/blue-build/template:20230403 +``` + +This repository by default also supports signing. + +The `latest` tag will automatically point to the latest build. That build will still always use the Fedora version specified in `recipe.yml`, so you won't get accidentally updated to the next major version. + +## ISO + +This template includes a simple Github Action to build and release an ISO of your image. + +To run the action, simply edit the `boot_menu.yml` by changing all the references to startingpoint to your repository. This should trigger the action automatically. + +The Action uses [isogenerator](https://github.com/ublue-os/isogenerator) and works in a similar manner to the official Universal Blue ISO. If you have any issues, you should first check [the documentation page on installation](https://universal-blue.org/installation/). The ISO is a netinstaller and should always pull the latest version of your image. + +Note that this release-iso action is not a replacement for a full-blown release automation like [release-please](https://github.com/googleapis/release-please). + +## `just` + +The [`just`](https://just.systems/) command runner is included in all `ublue-os/main`-derived images. + +You need to have a `~/.justfile` with the following contents and `just` aliased to `just --unstable` (default in posix-compatible shells on ublue) to get started with just locally. +``` +!include /usr/share/ublue-os/just/main.just +!include /usr/share/ublue-os/just/nvidia.just +!include /usr/share/ublue-os/just/custom.just +``` +Then type `just` to list the just recipes available. + +The file `/usr/share/ublue-os/just/custom.just` is intended for the custom just commands (recipes) you wish to include in your image. By default, it includes the justfiles from [`ublue-os/bling`](https://github.com/ublue-os/bling), if you wish to disable that, you need to just remove the line that includes bling.just. + +See [the just-page in the Universal Blue documentation](https://universal-blue.org/guide/just/) for more information. diff --git a/boot_menu.yml b/boot_menu.yml new file mode 100644 index 0000000..491b744 --- /dev/null +++ b/boot_menu.yml @@ -0,0 +1,5 @@ +ublue_variants: + - label: blue-build/template + ks: /kickstart/ublue-os.ks + flavors: + - label: blue-build/template diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..53892ed --- /dev/null +++ b/config/README.md @@ -0,0 +1,56 @@ +# Configuring your image + +The main file of your is *the recipe file*. You can have multiple recipe files, and the ones to build are declared in the matrix section of [build.yml](../.github/workflows/build.yml). + +## Basic options + +At the top of the recipe, there are four *mandatory* configuration options. + +`name:` is the name of the image that is used when rebasing to it. For example, the name "sapphire" would result in the final URL of the container being `ghcr.io//sapphire`. + +`description:` is a short description of your image that will be attached to your image's metadata. + +`base-image:` is the URL of the image your image will be built upon. + +`image-version:` is the version tag of the `base-image` that will be pulled. For example, Universal Blue's images build with Fedora version tags (`38`, `39`), with the `latest` tag for the latest major version, and [many other tags](https://github.com/ublue-os/main/pkgs/container/base-main/versions?filters%5Bversion_type%5D=tagged). + +## Modules + +The core of startingpoint's configuration is built around the idea of modules. Modules are scripts in the [`../modules`](../modules/) directory that you configure under `modules:` in the recipe. They are executed in order, and can run arbitrary shell commands and write any files. + +This repository fetches some useful default modules from [`ublue-os/bling`](https://github.com/ublue-os/bling/), like [`rpm-ostree`](https://universal-blue.org/tinker/modules/rpm-ostree) for pseudo-declarative package management, [`bling`](https://universal-blue.org/tinker/modules/bling) for pulling extra components from [`ublue-os/bling`](https://github.com/ublue-os/bling), and [`files`](https://universal-blue.org/tinker/modules/files) for copying files from the `config/files/` directory into your image. + +For a comprehensive list of modules, their in-depth documentation and example configuration, check out [the Modules page on the website](https://universal-blue.org/tinker/modules/). + +### Building multiple images and including module configuration from other files + +To build multiple images, you need to create another recipe.yml file, which you should name based on what kind of image you want it to build. Then, edit the [`build.yml`](../.github/workflows/build.yml) file. Inside the file, under `jobs: strategy: matrix:`, there's a list of recipe files to build images, which you need to add your new recipe file to. These should be paths to files inside the `config` directory. + +Module configuration can be included from other files using the `from-file` syntax. The value should be a path to a file inside the `config` directory. For example, the following snippet could be used to include the configuration for installing a set of packages common to multiple images. +```yaml +modules: + - from-file: common-packages.yml +``` +And inside config/common-packages.yml +```yaml +type: rpm-ostree +install: + - i3 + - dunst + - rofi + - kitty +``` +An external module can also include multiple modules. +```yaml +# config/common.yml +modules: + - type: files + files: + - usr: /usr + - type: rpm-ostree + install: + - i3 + - dunst + - rofi + - kitty +``` diff --git a/config/files/usr/share/ublue-os/just/100-bling.just b/config/files/usr/share/ublue-os/just/100-bling.just new file mode 100644 index 0000000..9e3a89b --- /dev/null +++ b/config/files/usr/share/ublue-os/just/100-bling.just @@ -0,0 +1,2 @@ +# this file is a placeholder, +# making changes here is not supported diff --git a/config/files/usr/share/ublue-os/just/60-custom.just b/config/files/usr/share/ublue-os/just/60-custom.just new file mode 100644 index 0000000..9fbf492 --- /dev/null +++ b/config/files/usr/share/ublue-os/just/60-custom.just @@ -0,0 +1,2 @@ +import '100-bling.just' +# Include some of your custom scripts here! diff --git a/config/recipe.yml b/config/recipe.yml new file mode 100644 index 0000000..cc14c1b --- /dev/null +++ b/config/recipe.yml @@ -0,0 +1,58 @@ +# image will be published to ghcr.io// +name: startingpoint +# description will be included in the image's metadata +description: A starting point for further customization of uBlue images. Make your own! https://ublue.it/making-your-own/ + +# the base image to build on top of (FROM) and the version tag to use +base-image: ghcr.io/ublue-os/silverblue-main +image-version: 39 # latest is also supported if you want new updates ASAP + +# module configuration, executed in order +# you can include multiple instances of the same module +modules: + - type: files + files: + - usr: /usr # copy static configurations + # + # copies config/files/usr into your image's /usr + # + # configuration you wish to end up in /etc/ on the booted system + # should be added into /usr/etc/ as that is the proper "distro" + # config directory on ostree. Read more in the files module's README + + - type: rpm-ostree + repos: + # - https://copr.fedorainfracloud.org/coprs/atim/starship/repo/fedora-%OS_VERSION%/atim-starship-fedora-%OS_VERSION%.repo + install: + # - micro + # - starship + remove: + - firefox # default firefox removed in favor of flatpak + - firefox-langpacks # langpacks needs to also be removed to prevent dependency problems + + - type: default-flatpaks + notify: true # Send notification after install/uninstall is finished (true/false) + system: + # If no repo information is specified, Flathub will be used by default + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: flathub + # repo-title: "Flathub (system-wide)" # Optional; this sets the remote's user-facing name in graphical frontends like GNOME Software + install: + # - org.gnome.Loupe + # - one.ablaze.floorp//lightning # This is an example of flatpak which has multiple branches in selection (flatpak//branch). + # Flatpak runtimes are not supported (like org.winehq.Wine//stable-23.08). + # Only normal flatpak applications are (like Floorp Lightning web browser in this example). + # Multiple install of same flatpaks with different branches is not supported. + remove: + # - org.gnome.eog + # + # A flatpak repo can also be added without having to install flatpaks, + # as long as one of the repo- fields is present + user: + repo-url: https://dl.flathub.org/repo/flathub.flatpakrepo + repo-name: flathub + + - type: script + scripts: + # this sets up the proper policy & signing files for signed images to work + - signing.sh diff --git a/config/scripts/example.sh b/config/scripts/example.sh new file mode 100644 index 0000000..fdb2e04 --- /dev/null +++ b/config/scripts/example.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# Tell this script to exit if there are any errors. +# You should have this in every custom script, to ensure that your completed +# builds actually ran successfully without any errors! +set -oue pipefail + +# Your code goes here. +echo 'This is an example shell script' +echo 'Scripts here will run during build if specified in recipe.yml' diff --git a/config/scripts/signing.sh b/config/scripts/signing.sh new file mode 100644 index 0000000..16b0ea8 --- /dev/null +++ b/config/scripts/signing.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# Tell build process to exit if there are any errors. +set -oue pipefail + +echo "Setting up container signing in policy.json and cosign.yaml for $IMAGE_NAME" +echo "Registry to write: $IMAGE_REGISTRY" + +cp /usr/share/ublue-os/cosign.pub /usr/etc/pki/containers/"$IMAGE_NAME".pub + +FILE=/usr/etc/containers/policy.json + +yq -i -o=j '.transports.docker |= + {"'"$IMAGE_REGISTRY"'/'"$IMAGE_NAME"'": [ + { + "type": "sigstoreSigned", + "keyPath": "/usr/etc/pki/containers/'"$IMAGE_NAME"'.pub", + "signedIdentity": { + "type": "matchRepository" + } + } + ] + } ++ .' "$FILE" + +IMAGE_REF="ostree-image-signed:docker://$IMAGE_REGISTRY/$IMAGE_NAME" +printf '{\n"image-ref": "'"$IMAGE_REF"'",\n"image-tag": "latest"\n}' > /usr/share/ublue-os/image-info.json + +cp /usr/etc/containers/registries.d/ublue-os.yaml /usr/etc/containers/registries.d/"$IMAGE_NAME".yaml +sed -i "s ghcr.io/ublue-os $IMAGE_REGISTRY g" /usr/etc/containers/registries.d/"$IMAGE_NAME".yaml diff --git a/modules/README.md b/modules/README.md new file mode 100644 index 0000000..40a8bc8 --- /dev/null +++ b/modules/README.md @@ -0,0 +1,46 @@ +# Making modules + +If you want to extend Startingpoint with custom functionality that requires configuration, you should create a module. Modules are scripts in the subdirectories of this directory. The `type:` key in the recipe.yml should be used as both the name of the folder and script, with the script having an additional `.sh` suffix. Creating a custom module with the same name as a default module will override it. + +Each module intended for public usage should include a `README.md` file inside it's directory with a short description of the module and documentation for each configuration option. + +Modules get only the configuration options given to them in the recipe.yml, not the configuration of other modules or any top-level keys. The configuration is given as the first argument as a single-line json string. You can check out the default modules for examples on how to parse such string using `yq` or `jq`. + +Additionally, each module has access to four environment variables, `CONFIG_DIRECTORY` pointing to the directory containing the confiuration files for the build (`/tmp/config`), `IMAGE_NAME` being the name of the image as declared in the recipe, `BASE_IMAGE` being the URL of the container image used as the base (FROM) in the image, and `OS_VERSION` being the `VERSION_ID` from `/usr/lib/os-release`. + +When running modules, the working directory is the `CONFIG_DIRECTORY`. + +A helper bash function called `get_yaml_array` is exported from the main build script. +```bash +# "$1" is the first cli argument, being the module configuration. +# If you need to read from some other JSON string, just replace "$1" with "$VARNAME". +get_yaml_array OUTPUT_VAR_NAME '.yq.key.to.array[]' "$1" +for THING in "${OUTPUT_VAR_NAME[@]}"; do + echo "$THING" +done +``` + +All bash-based modules should start with the following lines to ensure the image builds fail on errors, and that the correct shell is used to run them. +```bash +#!/usr/bin/env bash +set -oue pipefail +``` + +## Style directions for official modules + +These are general directions for writing official modules and their documentation to follow to keep a consistent style. Not all of these are to be mindlessly followed, especially the ones about grammar and writing style. It's good to keep these in mind if you intend to contribute back upstream, though, so that your module doesn't feel out of place. + +### Bash + +- Start with `#!/usr/bin/env bash` and `set -oue pipefail` +- Don't print "===", this is only for encapsulating the output of _different_ modules in `build.sh` +- Print something on each step and on errors for easier debugging +- Use CAPITALIZED names for variables that are read from the configuration + +### README + +- Title should be "`type` Module for Startingpoint", where the name/type of the module is a noun that shows the module's purpose +- There should be a subtitle "Example configuration", under which there should be a loosely documented yaml block showcasing each of the module's configuration options + - For a YAML block, specify the language as "yaml", not "yml" (MkDocs only supports "yaml") +- At the start of each paragraph, refer to the module using its name or with "the module", not "it" or "the script" +- Use passive grammar when talking about the user, ie. "should be used", "can be configured", preferring references to what the module does, ie. "This module downloads the answer to the question of life, the universe and everything..."