diff --git a/.github/workflows/build-individual.yml b/.github/workflows/build-individual.yml new file mode 100644 index 0000000..3258b0f --- /dev/null +++ b/.github/workflows/build-individual.yml @@ -0,0 +1,38 @@ +name: build-individual +on: + push: + paths-ignore: # don't rebuild if only documentation has changed + - "**.md" + pull_request: + merge_group: + workflow_dispatch: + +jobs: + build-individual: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: hustcer/setup-nu@v3.10 + with: + version: v0.93 + + - uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: sigstore/cosign-installer@v3.5.0 + + - name: Run build and push script + run: nu ./build-individual.nu + env: + REGISTRY: ghcr.io/${{ github.repository_owner }} + COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} + GH_EVENT_NAME: ${{ github.event_name }} + GH_PR_NUMBER: ${{ github.event.number }} + GH_BRANCH: ${{ github.ref_name }} diff --git a/.github/workflows/build-unified.yml b/.github/workflows/build-unified.yml new file mode 100644 index 0000000..2cc31a6 --- /dev/null +++ b/.github/workflows/build-unified.yml @@ -0,0 +1,38 @@ +name: build-unified +on: + push: + paths-ignore: # don't rebuild if only documentation has changed + - "**.md" + pull_request: + merge_group: + workflow_dispatch: + +jobs: + build-unified: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: hustcer/setup-nu@v3.10 + with: + version: v0.93 + + - uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: sigstore/cosign-installer@v3.5.0 + + - name: Run build and push script + run: nu ./build-unified.nu + env: + REGISTRY: ghcr.io/${{ github.repository_owner }} + COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} + GH_EVENT_NAME: ${{ github.event_name }} + GH_PR_NUMBER: ${{ github.event.number }} + GH_BRANCH: ${{ github.ref_name }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 55f18ac..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,107 +0,0 @@ -name: publish-modules -on: - push: - paths-ignore: # don't rebuild if only documentation has changed - - "**.md" - pull_request: - merge_group: - workflow_dispatch: -env: - IMAGE_NAME: modules - IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} - -jobs: - push-ghcr: - name: Build and push image - runs-on: ubuntu-22.04 - permissions: - contents: read - packages: write - id-token: write - steps: - # Checkout push-to-registry action GitHub repository - - name: Checkout Push to Registry action - uses: actions/checkout@v4 - - - name: Generate tags - id: generate-tags - shell: bash - run: | - echo "sha_short=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT - alias_tags=() - # Only perform the follow code when the action is spawned from a Pull Request - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - alias_tags+=("pr-${{ github.event.number }}") - elif [[ "${{ github.ref_name }}" == "main" ]]; then - # The following is run when the timer is triggered or a merge/push to main - echo "date=$(date +%Y%m%d)" >> $GITHUB_OUTPUT - alias_tags+=("latest" "main") - else - alias_tags+=("${{ github.ref_name }}") - fi - echo "alias_tags=${alias_tags[*]}" >> $GITHUB_OUTPUT - - # 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 }} - ${{ steps.generate-tags.outputs.date }} - ${{ steps.generate-tags.outputs.sha_short }} - oci: true - - # 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 }} - - # 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' - 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' - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # Sign container - - uses: sigstore/cosign-installer@v3.5.0 - if: github.event_name != 'pull_request' - - - name: Sign container image - if: github.event_name != 'pull_request' - run: | - cosign sign -y --key env://COSIGN_PRIVATE_KEY ${{ steps.registry_case.outputs.lowercase }}/${{ env.IMAGE_NAME }}@${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' - run: | - echo "${{ toJSON(steps.push.outputs) }}" diff --git a/.gitignore b/.gitignore index 2f01194..9d1f202 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ cosign.key + +/modules-latest \ No newline at end of file diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 0000000..21c874c --- /dev/null +++ b/.mise.toml @@ -0,0 +1,2 @@ +[tools] +act = "latest" diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1b1404b..d8eb8aa 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ - "timonwong.shellcheck" + "timonwong.shellcheck", + "thenuprojectcontributors.vscode-nushell-lang" ] } \ No newline at end of file diff --git a/Containerfile b/Containerfile deleted file mode 100644 index b5cb602..0000000 --- a/Containerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM scratch - -COPY modules /modules diff --git a/build-individual.nu b/build-individual.nu new file mode 100644 index 0000000..9b2f1e7 --- /dev/null +++ b/build-individual.nu @@ -0,0 +1,97 @@ +#!/usr/bin/env nu +# build separate images for each module in the repo + +print $"(ansi green_bold)Gathering images" + +let images = ls modules | each { |moduleDir| + cd $moduleDir.name + + # module is unversioned + if ($"($moduleDir.name | path basename).sh" | path exists) { + + print $"(ansi cyan)Found(ansi reset) (ansi cyan_bold)unversioned(ansi reset) (ansi cyan)module:(ansi reset) ($moduleDir.name | path basename)" + + let tags = ( + if ($env.GH_EVENT_NAME != "pull_request" and $env.GH_BRANCH == "main") { + ["latest", "v1"] + } else if ($env.GH_EVENT_NAME != "pull_request") { + [$env.GH_BRANCH, $"v1-($env.GH_BRANCH)"] + } else { + [$"pr-($env.GH_PR_NUMBER)", $"v1-pr-($env.GH_PR_NUMBER)"] + } + ) + print $"(ansi cyan)Generated tags:(ansi reset) ($tags | str join ' ')" + + { + name: ($moduleDir.name | path basename) + directory: ($moduleDir.name) + tags: $tags + } + + } else { # module is versioned + + print $"(ansi cyan)Found(ansi reset) (ansi blue_bold)versioned(ansi reset) (ansi cyan)module:(ansi reset) ($moduleDir.name | path basename)" + + let versioned = ls v*/ + | get name | str substring 1.. | into int | sort # sort versions properly + | each {|version| + let tags = ( + if ($env.GH_EVENT_NAME != "pull_request" and $env.GH_BRANCH == "main") { + [$"v($version)"] + } else if ($env.GH_EVENT_NAME != "pull_request") { + [$"v($version)-($env.GH_BRANCH)"] + } else { + [$"v($version)-pr-($env.GH_PR_NUMBER)"] + } + ) + print $"(ansi cyan)Generated tags:(ansi reset) ($tags | str join ' ')" + + { + name: ($moduleDir.name | path basename) + directory: $"($moduleDir.name)/v($version)" + tags: $tags + } + } + + let latest_tag = ( + if ($env.GH_EVENT_NAME != "pull_request" and $env.GH_BRANCH == "main") { + "latest" + } else if ($env.GH_EVENT_NAME != "pull_request") { + $env.GH_BRANCH + } else { + $"pr-($env.GH_PR_NUMBER)" + } + ) + print $"(ansi cyan)Extra tag for latest image:(ansi reset) ($latest_tag)" + let latest = ($versioned | last) + ($versioned + | update (($versioned | length) - 1) # update the last / latest item in list + ($latest | update "tags" ($latest.tags | append latest_tag)) # append tag which should only be given to the latest version + ) + + } +} | flatten directory + +print $"(ansi green_bold)Starting image build(ansi reset)" + +$images | par-each { |img| + + print $"(ansi cyan)Building image:(ansi reset) modules/($img.name)" + (docker build . + -f ./individual.Containerfile + ...($img.tags | each { |tag| ["-t", $"($env.REGISTRY)/modules/($img.name):($tag)"] } | flatten) # generate and spread list of tags + --build-arg $"DIRECTORY=($img.directory)" + --build-arg $"NAME=($img.name)") + + print $"(ansi cyan)Pushing image:(ansi reset) ($env.REGISTRY)/modules/($img.name)" + let digest = ( + docker push --all-tags $"($env.REGISTRY)/modules/($img.name)" + | split row "\n" | last | split row " " | get 2 # parse push output to get digest for signing + ) + + print $"(ansi cyan)Signing image:(ansi reset) ($env.REGISTRY)/modules/($img.name)@($digest)" + cosign sign -y --key env://COSIGN_PRIVATE_KEY $"($env.REGISTRY)/modules/($img.name)@($digest)" + +} + +print $"(ansi green_bold)DONE!(ansi reset)" \ No newline at end of file diff --git a/build-unified.nu b/build-unified.nu new file mode 100644 index 0000000..baccabe --- /dev/null +++ b/build-unified.nu @@ -0,0 +1,59 @@ +#!/usr/bin/env nu +# generates modules-latest directory with only latest versions of modules and builds the Containerfile + +print $"(ansi green_bold)Gathering images(ansi reset)" + +rm -rf ./modules-latest +mkdir ./modules-latest + +ls modules | each { |moduleDir| + + # module is unversioned + if ($"($moduleDir.name)/($moduleDir.name | path basename).sh" | path exists) { + + print $"(ansi cyan)Found(ansi reset) (ansi cyan_bold)unversioned(ansi reset) (ansi cyan)module:(ansi reset) ($moduleDir.name | path basename)" + + cp --recursive ($moduleDir.name) $"./modules-latest/($moduleDir.name | path basename)" + + } else { # module is versioned + + print -n $"(ansi cyan)Found(ansi reset) (ansi blue_bold)versioned(ansi reset) (ansi cyan)module:(ansi reset) ($moduleDir.name | path basename), " + + let latest = glob $"./($moduleDir.name)/v*" | last # the glob result is already orderer such that the last value is the biggest + + print $"(ansi blue_bold)Latest version:(ansi reset) ($latest | path basename)" + + cp --recursive ($latest) $"./modules-latest/($moduleDir.name | path basename)" + + } +} + +print $"(ansi green_bold)Starting image build(ansi reset)" + +let tags = ( + if ($env.GH_EVENT_NAME != "pull_request" and $env.GH_BRANCH == "main") { + ["latest"] + } else if ($env.GH_EVENT_NAME != "pull_request") { + [$env.GH_BRANCH] + } else { + [$"pr-($env.GH_PR_NUMBER)"] + } +) + +print $"(ansi green_bold)Generated tags for image:(ansi reset) ($tags | str join ' ')" + +(docker build . + -f ./unified.Containerfile + ...($tags | each { |tag| ["-t", $"($env.REGISTRY)/modules:($tag)"] } | flatten) # generate and spread list of tags +) + +print $"(ansi cyan)Pushing image:(ansi reset) ($env.REGISTRY)/modules" +let digest = ( + docker push --all-tags $"($env.REGISTRY)/modules" + | split row "\n" | last | split row " " | get 2 # parse push output to get digest for signing +) + +print $"(ansi cyan)Signing image:(ansi reset) ($env.REGISTRY)/modules@($digest)" +cosign sign -y --key env://COSIGN_PRIVATE_KEY $"($env.REGISTRY)/modules@($digest)" + +print $"(ansi green_bold)DONE!(ansi reset)" \ No newline at end of file diff --git a/individual.Containerfile b/individual.Containerfile new file mode 100644 index 0000000..ee6c890 --- /dev/null +++ b/individual.Containerfile @@ -0,0 +1,6 @@ +FROM scratch + +ARG DIRECTORY +ARG NAME + +COPY ${DIRECTORY} /modules/${NAME} \ No newline at end of file diff --git a/modules/default-flatpaks/config/notifications b/modules/default-flatpaks/v1/config/notifications similarity index 100% rename from modules/default-flatpaks/config/notifications rename to modules/default-flatpaks/v1/config/notifications diff --git a/modules/default-flatpaks/config/system/install b/modules/default-flatpaks/v1/config/system/install similarity index 100% rename from modules/default-flatpaks/config/system/install rename to modules/default-flatpaks/v1/config/system/install diff --git a/modules/default-flatpaks/config/system/remove b/modules/default-flatpaks/v1/config/system/remove similarity index 100% rename from modules/default-flatpaks/config/system/remove rename to modules/default-flatpaks/v1/config/system/remove diff --git a/modules/default-flatpaks/config/user/install b/modules/default-flatpaks/v1/config/user/install similarity index 100% rename from modules/default-flatpaks/config/user/install rename to modules/default-flatpaks/v1/config/user/install diff --git a/modules/default-flatpaks/config/user/remove b/modules/default-flatpaks/v1/config/user/remove similarity index 100% rename from modules/default-flatpaks/config/user/remove rename to modules/default-flatpaks/v1/config/user/remove diff --git a/modules/default-flatpaks/default-flatpaks.sh b/modules/default-flatpaks/v1/default-flatpaks.sh similarity index 100% rename from modules/default-flatpaks/default-flatpaks.sh rename to modules/default-flatpaks/v1/default-flatpaks.sh diff --git a/modules/default-flatpaks/system-flatpak-setup b/modules/default-flatpaks/v1/system-flatpak-setup similarity index 100% rename from modules/default-flatpaks/system-flatpak-setup rename to modules/default-flatpaks/v1/system-flatpak-setup diff --git a/modules/default-flatpaks/system-flatpak-setup.service b/modules/default-flatpaks/v1/system-flatpak-setup.service similarity index 100% rename from modules/default-flatpaks/system-flatpak-setup.service rename to modules/default-flatpaks/v1/system-flatpak-setup.service diff --git a/modules/default-flatpaks/user-config/notifications b/modules/default-flatpaks/v1/user-config/notifications similarity index 100% rename from modules/default-flatpaks/user-config/notifications rename to modules/default-flatpaks/v1/user-config/notifications diff --git a/modules/default-flatpaks/user-config/system/install b/modules/default-flatpaks/v1/user-config/system/install similarity index 100% rename from modules/default-flatpaks/user-config/system/install rename to modules/default-flatpaks/v1/user-config/system/install diff --git a/modules/default-flatpaks/user-config/system/remove b/modules/default-flatpaks/v1/user-config/system/remove similarity index 100% rename from modules/default-flatpaks/user-config/system/remove rename to modules/default-flatpaks/v1/user-config/system/remove diff --git a/modules/default-flatpaks/user-config/user/install b/modules/default-flatpaks/v1/user-config/user/install similarity index 100% rename from modules/default-flatpaks/user-config/user/install rename to modules/default-flatpaks/v1/user-config/user/install diff --git a/modules/default-flatpaks/user-config/user/remove b/modules/default-flatpaks/v1/user-config/user/remove similarity index 100% rename from modules/default-flatpaks/user-config/user/remove rename to modules/default-flatpaks/v1/user-config/user/remove diff --git a/modules/default-flatpaks/user-flatpak-setup b/modules/default-flatpaks/v1/user-flatpak-setup similarity index 100% rename from modules/default-flatpaks/user-flatpak-setup rename to modules/default-flatpaks/v1/user-flatpak-setup diff --git a/modules/default-flatpaks/user-flatpak-setup.service b/modules/default-flatpaks/v1/user-flatpak-setup.service similarity index 100% rename from modules/default-flatpaks/user-flatpak-setup.service rename to modules/default-flatpaks/v1/user-flatpak-setup.service diff --git a/unified.Containerfile b/unified.Containerfile new file mode 100644 index 0000000..32f2b6b --- /dev/null +++ b/unified.Containerfile @@ -0,0 +1,4 @@ +FROM scratch + +# modules-latest is generated by build-unified.nu +COPY modules-latest /modules