Compare commits
No commits in common. "main" and "webhook_bridge" have entirely different histories.
main
...
webhook_br
@ -44,31 +44,6 @@ spec:
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
echo -n "$(date +%s)" | tee $(results.unix-time.path)
|
||||
- name: get-git-commit-time
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
image: alpine:3.20
|
||||
computeResources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 600Mi
|
||||
workingDir: "$(workspaces.repo.path)"
|
||||
results:
|
||||
- name: unix-time
|
||||
description: The time of the git commit in unix timestamp format.
|
||||
steps:
|
||||
- image: alpine/git:v2.34.2
|
||||
name: detect-tag-step
|
||||
script: |
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
echo -n "$(git log -1 --pretty=%ct)" | tee $(results.unix-time.path)
|
||||
workspaces:
|
||||
- name: repo
|
||||
workspace: git-source
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
- name: report-pending
|
||||
taskRef:
|
||||
resolver: git
|
||||
@ -81,6 +56,9 @@ spec:
|
||||
value: task/gitea-set-status/0.1/gitea-set-status.yaml
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
- fetch-repository-private
|
||||
- fetch-repository-explorer
|
||||
- fetch-repository-organic
|
||||
params:
|
||||
- name: CONTEXT
|
||||
value: "$(params.JOB_NAME)"
|
||||
@ -116,42 +94,122 @@ spec:
|
||||
value: $(params.PULL_BASE_SHA)
|
||||
- name: deleteExisting
|
||||
value: "true"
|
||||
- name: build-image
|
||||
- name: fetch-repository-private
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
|
||||
value: task/git-clone/0.9/git-clone.yaml
|
||||
workspaces:
|
||||
- name: output
|
||||
workspace: git-source-private
|
||||
params:
|
||||
- name: OUTPUT
|
||||
value: >-
|
||||
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.get-time.results.unix-time)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
|
||||
- name: url
|
||||
value: git@code.fizz.buzz:talexander/homepage_private.git
|
||||
- name: revision
|
||||
value: main
|
||||
- name: deleteExisting
|
||||
value: "true"
|
||||
- name: fetch-repository-explorer
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/git-clone/0.9/git-clone.yaml
|
||||
workspaces:
|
||||
- name: output
|
||||
workspace: git-source-explorer
|
||||
params:
|
||||
- name: url
|
||||
value: git@code.fizz.buzz:talexander/organic_ast_explorer.git
|
||||
- name: revision
|
||||
value: main
|
||||
- name: deleteExisting
|
||||
value: "true"
|
||||
- name: fetch-repository-organic
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/git-clone/0.9/git-clone.yaml
|
||||
workspaces:
|
||||
- name: output
|
||||
workspace: git-source-organic
|
||||
params:
|
||||
- name: url
|
||||
value: git@code.fizz.buzz:talexander/organic.git
|
||||
- name: revision
|
||||
value: main
|
||||
- name: deleteExisting
|
||||
value: "true"
|
||||
- name: copy-private-files
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
image: alpine:3.20
|
||||
computeResources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 600Mi
|
||||
workingDir: "$(workspaces.source.path)"
|
||||
steps:
|
||||
- image: alpine:3.20
|
||||
name: copy-private-files
|
||||
script: |
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
cp -r "$(workspaces.source-private.path)/static/"* "$(workspaces.source.path)/static/"
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: source-private
|
||||
workspace: git-source-private
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
- fetch-repository-private
|
||||
- name: build-explorer-image
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/kaniko/0.6/kaniko.yaml
|
||||
params:
|
||||
- name: IMAGE
|
||||
value: "harbor.fizz.buzz/private/homepage-build-organic:$(tasks.get-time.results.unix-time)"
|
||||
- name: CONTEXT
|
||||
value: $(params.path-to-image-context)
|
||||
value: .
|
||||
- name: DOCKERFILE
|
||||
value: $(params.path-to-dockerfile)
|
||||
value: docker/organic/Dockerfile
|
||||
- name: BUILDER_IMAGE
|
||||
value: "gcr.io/kaniko-project/executor:v1.12.1"
|
||||
- name: EXTRA_ARGS
|
||||
value:
|
||||
- "--opt"
|
||||
- "target=$(params.target-name)"
|
||||
- --import-cache
|
||||
- "type=registry,ref=$(params.image-name):buildcache"
|
||||
- --export-cache
|
||||
- "type=registry,ref=$(params.image-name):buildcache,mode=max,compression=zstd,compression-level=22,rewrite-timestamp=true,image-manifest=true,oci-mediatypes=true"
|
||||
- --opt
|
||||
- build-arg:SOURCE_DATE_EPOCH=$(tasks.get-git-commit-time.results.unix-time)
|
||||
- name: BUILDKITD_TOML
|
||||
value: |
|
||||
debug = true
|
||||
[registry."docker.io"]
|
||||
mirrors = ["dockerhub.dockerhub.svc.cluster.local"]
|
||||
[registry."dockerhub.dockerhub.svc.cluster.local"]
|
||||
http = true
|
||||
insecure = true
|
||||
- "--destination=harbor.fizz.buzz/private/homepage-build-organic" # Also write the :latest image
|
||||
- "--target="
|
||||
- --cache=true
|
||||
- --cache-copy-layers
|
||||
- --cache-repo=harbor.fizz.buzz/kanikocache/cache
|
||||
- --use-new-run # Should result in a speed-up
|
||||
- --reproducible # To remove timestamps so layer caching works.
|
||||
- --snapshot-mode=redo
|
||||
- --skip-unused-stages=true
|
||||
- --registry-mirror=dockerhub.dockerhub.svc.cluster.local
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
@ -159,6 +217,95 @@ spec:
|
||||
workspace: docker-credentials
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
- name: build-explorer-wasm
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
image: alpine:3.20
|
||||
computeResources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 600Mi
|
||||
workingDir: "$(workspaces.organic.path)"
|
||||
steps:
|
||||
- image: "$(params.IMAGE)"
|
||||
name: build-explorer-wasm
|
||||
params:
|
||||
- name: IMAGE
|
||||
value: "harbor.fizz.buzz/private/homepage-build-organic:$(tasks.get-time.results.unix-time)"
|
||||
workspaces:
|
||||
- name: organic
|
||||
workspace: git-source-organic
|
||||
runAfter:
|
||||
- build-explorer-image
|
||||
- name: copy-explorer-files
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
image: alpine:3.20
|
||||
computeResources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 600Mi
|
||||
workingDir: "$(workspaces.source-explorer.path)"
|
||||
steps:
|
||||
- image: node:lts-alpine3.20
|
||||
name: copy-explorer-files
|
||||
script: |
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
npm install
|
||||
npm run release
|
||||
mkdir -p "$(workspaces.source.path)/static/organic/ast_explorer/"
|
||||
cp -r "$(workspaces.source-explorer.path)/dist/"* "$(workspaces.source.path)/static/organic/ast_explorer/"
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: source-explorer
|
||||
workspace: git-source-explorer
|
||||
- name: organic
|
||||
workspace: git-source-organic
|
||||
runAfter:
|
||||
- build-explorer-wasm
|
||||
- name: build-image
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/kaniko/0.6/kaniko.yaml
|
||||
params:
|
||||
- name: IMAGE
|
||||
value: "$(params.image-name):$(tasks.get-time.results.unix-time)"
|
||||
- name: CONTEXT
|
||||
value: $(params.path-to-image-context)
|
||||
- name: DOCKERFILE
|
||||
value: $(params.path-to-dockerfile)
|
||||
- name: BUILDER_IMAGE
|
||||
value: "gcr.io/kaniko-project/executor:v1.12.1"
|
||||
- name: EXTRA_ARGS
|
||||
value:
|
||||
- "--destination=$(params.image-name)" # Also write the :latest image
|
||||
- "--target=$(params.target-name)"
|
||||
- --cache=true
|
||||
- --cache-copy-layers
|
||||
- --cache-repo=harbor.fizz.buzz/kanikocache/cache
|
||||
- --use-new-run # Should result in a speed-up
|
||||
- --reproducible # To remove timestamps so layer caching works.
|
||||
- --snapshot-mode=redo
|
||||
- --skip-unused-stages=true
|
||||
- --registry-mirror=dockerhub.dockerhub.svc.cluster.local
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: dockerconfig
|
||||
workspace: docker-credentials
|
||||
runAfter:
|
||||
- copy-private-files
|
||||
- copy-explorer-files
|
||||
finally:
|
||||
- name: report-success
|
||||
when:
|
||||
@ -232,6 +379,36 @@ spec:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
subPath: rust-source
|
||||
- name: git-source-private
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: "nfs-client"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
subPath: git-source
|
||||
- name: git-source-explorer
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: "nfs-client"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
subPath: git-source
|
||||
- name: git-source-organic
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: "nfs-client"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
subPath: git-source
|
||||
- name: docker-credentials
|
||||
secret:
|
||||
secretName: harbor-plain
|
||||
@ -243,4 +420,4 @@ spec:
|
||||
- name: path-to-image-context
|
||||
value: .
|
||||
- name: path-to-dockerfile
|
||||
value: docker/server
|
||||
value: docker/server/Dockerfile
|
||||
|
@ -24,7 +24,7 @@ spec:
|
||||
description: The path to the Dockerfile
|
||||
type: string
|
||||
tasks:
|
||||
- name: get-git-commit-time
|
||||
- name: get-time
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
@ -33,22 +33,17 @@ spec:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 600Mi
|
||||
workingDir: "$(workspaces.repo.path)"
|
||||
workingDir: "/"
|
||||
results:
|
||||
- name: unix-time
|
||||
description: The time of the git commit in unix timestamp format.
|
||||
description: The current date in unix timestamp format.
|
||||
steps:
|
||||
- image: alpine/git:v2.34.2
|
||||
name: detect-tag-step
|
||||
- image: alpine:3.20
|
||||
name: get-time-step
|
||||
script: |
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
echo -n "$(git log -1 --pretty=%ct)" | tee $(results.unix-time.path)
|
||||
workspaces:
|
||||
- name: repo
|
||||
workspace: git-source
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
echo -n "$(date +%s)" | tee $(results.unix-time.path)
|
||||
- name: detect-tag
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
@ -93,6 +88,9 @@ spec:
|
||||
value: task/gitea-set-status/0.1/gitea-set-status.yaml
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
- fetch-repository-private
|
||||
- fetch-repository-explorer
|
||||
- fetch-repository-organic
|
||||
params:
|
||||
- name: CONTEXT
|
||||
value: "$(params.JOB_NAME)"
|
||||
@ -128,42 +126,122 @@ spec:
|
||||
value: $(params.PULL_BASE_SHA)
|
||||
- name: deleteExisting
|
||||
value: "true"
|
||||
- name: build-image
|
||||
- name: fetch-repository-private
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
|
||||
value: task/git-clone/0.9/git-clone.yaml
|
||||
workspaces:
|
||||
- name: output
|
||||
workspace: git-source-private
|
||||
params:
|
||||
- name: OUTPUT
|
||||
value: >-
|
||||
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.detect-tag.results.tag)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
|
||||
- name: url
|
||||
value: git@code.fizz.buzz:talexander/homepage_private.git
|
||||
- name: revision
|
||||
value: main
|
||||
- name: deleteExisting
|
||||
value: "true"
|
||||
- name: fetch-repository-explorer
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/git-clone/0.9/git-clone.yaml
|
||||
workspaces:
|
||||
- name: output
|
||||
workspace: git-source-explorer
|
||||
params:
|
||||
- name: url
|
||||
value: git@code.fizz.buzz:talexander/organic_ast_explorer.git
|
||||
- name: revision
|
||||
value: main
|
||||
- name: deleteExisting
|
||||
value: "true"
|
||||
- name: fetch-repository-organic
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/git-clone/0.9/git-clone.yaml
|
||||
workspaces:
|
||||
- name: output
|
||||
workspace: git-source-organic
|
||||
params:
|
||||
- name: url
|
||||
value: git@code.fizz.buzz:talexander/organic.git
|
||||
- name: revision
|
||||
value: main
|
||||
- name: deleteExisting
|
||||
value: "true"
|
||||
- name: copy-private-files
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
image: alpine:3.20
|
||||
computeResources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 600Mi
|
||||
workingDir: "$(workspaces.source.path)"
|
||||
steps:
|
||||
- image: alpine:3.20
|
||||
name: copy-private-files
|
||||
script: |
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
cp -r "$(workspaces.source-private.path)/static/"* "$(workspaces.source.path)/static/"
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: source-private
|
||||
workspace: git-source-private
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
- fetch-repository-private
|
||||
- name: build-explorer-image
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/kaniko/0.6/kaniko.yaml
|
||||
params:
|
||||
- name: IMAGE
|
||||
value: "harbor.fizz.buzz/private/homepage-build-organic:$(tasks.get-time.results.unix-time)"
|
||||
- name: CONTEXT
|
||||
value: $(params.path-to-image-context)
|
||||
value: .
|
||||
- name: DOCKERFILE
|
||||
value: $(params.path-to-dockerfile)
|
||||
value: docker/organic/Dockerfile
|
||||
- name: BUILDER_IMAGE
|
||||
value: "gcr.io/kaniko-project/executor:v1.12.1"
|
||||
- name: EXTRA_ARGS
|
||||
value:
|
||||
- "--opt"
|
||||
- "target=$(params.target-name)"
|
||||
- --import-cache
|
||||
- "type=registry,ref=$(params.image-name):buildcache"
|
||||
- --export-cache
|
||||
- "type=registry,ref=$(params.image-name):buildcache,mode=max,compression=zstd,compression-level=22,rewrite-timestamp=true,image-manifest=true,oci-mediatypes=true"
|
||||
- --opt
|
||||
- build-arg:SOURCE_DATE_EPOCH=$(tasks.get-git-commit-time.results.unix-time)
|
||||
- name: BUILDKITD_TOML
|
||||
value: |
|
||||
debug = true
|
||||
[registry."docker.io"]
|
||||
mirrors = ["dockerhub.dockerhub.svc.cluster.local"]
|
||||
[registry."dockerhub.dockerhub.svc.cluster.local"]
|
||||
http = true
|
||||
insecure = true
|
||||
- "--destination=harbor.fizz.buzz/private/homepage-build-organic" # Also write the :latest image
|
||||
- "--target="
|
||||
- --cache=true
|
||||
- --cache-copy-layers
|
||||
- --cache-repo=harbor.fizz.buzz/kanikocache/cache
|
||||
- --use-new-run # Should result in a speed-up
|
||||
- --reproducible # To remove timestamps so layer caching works.
|
||||
- --snapshot-mode=redo
|
||||
- --skip-unused-stages=true
|
||||
- --registry-mirror=dockerhub.dockerhub.svc.cluster.local
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
@ -171,6 +249,95 @@ spec:
|
||||
workspace: docker-credentials
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
- name: build-explorer-wasm
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
image: alpine:3.20
|
||||
computeResources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 600Mi
|
||||
workingDir: "$(workspaces.organic.path)"
|
||||
steps:
|
||||
- image: "$(params.IMAGE)"
|
||||
name: build-explorer-wasm
|
||||
params:
|
||||
- name: IMAGE
|
||||
value: "harbor.fizz.buzz/private/homepage-build-organic:$(tasks.get-time.results.unix-time)"
|
||||
workspaces:
|
||||
- name: organic
|
||||
workspace: git-source-organic
|
||||
runAfter:
|
||||
- build-explorer-image
|
||||
- name: copy-explorer-files
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
image: alpine:3.20
|
||||
computeResources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 600Mi
|
||||
workingDir: "$(workspaces.source-explorer.path)"
|
||||
steps:
|
||||
- image: node:lts-alpine3.20
|
||||
name: copy-explorer-files
|
||||
script: |
|
||||
#!/usr/bin/env sh
|
||||
set -euo pipefail
|
||||
npm install
|
||||
npm run release
|
||||
mkdir -p "$(workspaces.source.path)/static/organic/ast_explorer/"
|
||||
cp -r "$(workspaces.source-explorer.path)/dist/"* "$(workspaces.source.path)/static/organic/ast_explorer/"
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: source-explorer
|
||||
workspace: git-source-explorer
|
||||
- name: organic
|
||||
workspace: git-source-organic
|
||||
runAfter:
|
||||
- build-explorer-wasm
|
||||
- name: build-image
|
||||
taskRef:
|
||||
resolver: git
|
||||
params:
|
||||
- name: url
|
||||
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
|
||||
- name: revision
|
||||
value: df36b3853a5657fd883015cdbf07ad6466918acf
|
||||
- name: pathInRepo
|
||||
value: task/kaniko/0.6/kaniko.yaml
|
||||
params:
|
||||
- name: IMAGE
|
||||
value: "$(params.image-name):$(tasks.detect-tag.results.tag)"
|
||||
- name: CONTEXT
|
||||
value: $(params.path-to-image-context)
|
||||
- name: DOCKERFILE
|
||||
value: $(params.path-to-dockerfile)
|
||||
- name: BUILDER_IMAGE
|
||||
value: "gcr.io/kaniko-project/executor:v1.12.1"
|
||||
- name: EXTRA_ARGS
|
||||
value:
|
||||
- "--destination=$(params.image-name)" # Also write the :latest image
|
||||
- "--target=$(params.target-name)"
|
||||
- --cache=true
|
||||
- --cache-copy-layers
|
||||
- --cache-repo=harbor.fizz.buzz/kanikocache/cache
|
||||
- --use-new-run # Should result in a speed-up
|
||||
- --reproducible # To remove timestamps so layer caching works.
|
||||
- --snapshot-mode=redo
|
||||
- --skip-unused-stages=true
|
||||
- --registry-mirror=dockerhub.dockerhub.svc.cluster.local
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: dockerconfig
|
||||
workspace: docker-credentials
|
||||
runAfter:
|
||||
- copy-private-files
|
||||
- copy-explorer-files
|
||||
finally:
|
||||
- name: report-success
|
||||
when:
|
||||
@ -244,6 +411,36 @@ spec:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
subPath: rust-source
|
||||
- name: git-source-private
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: "nfs-client"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
subPath: git-source
|
||||
- name: git-source-explorer
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: "nfs-client"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
subPath: git-source
|
||||
- name: git-source-organic
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: "nfs-client"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
subPath: git-source
|
||||
- name: docker-credentials
|
||||
secret:
|
||||
secretName: harbor-plain
|
||||
@ -255,4 +452,4 @@ spec:
|
||||
- name: path-to-image-context
|
||||
value: .
|
||||
- name: path-to-dockerfile
|
||||
value: docker/server
|
||||
value: docker/server/Dockerfile
|
||||
|
7
docker/organic/Dockerfile
Normal file
7
docker/organic/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM rustlang/rust:nightly-alpine3.20
|
||||
|
||||
RUN apk add --no-cache musl-dev make bash
|
||||
RUN rustup target add wasm32-unknown-unknown
|
||||
RUN cargo install wasm-bindgen-cli
|
||||
|
||||
CMD ["make", "wasm"]
|
52
docker/organic/Makefile
Normal file
52
docker/organic/Makefile
Normal file
@ -0,0 +1,52 @@
|
||||
SHELL := bash
|
||||
.ONESHELL:
|
||||
.SHELLFLAGS := -eu -o pipefail -c
|
||||
.DELETE_ON_ERROR:
|
||||
MAKEFLAGS += --warn-undefined-variables
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
OS:=$(shell uname -s)
|
||||
|
||||
ifeq ($(origin .RECIPEPREFIX), undefined)
|
||||
$(error This Make does not support .RECIPEPREFIX. Please use GNU Make 4.0 or later)
|
||||
endif
|
||||
.RECIPEPREFIX = >
|
||||
|
||||
IMAGE_NAME:=homepage-build-organic
|
||||
# REMOTE_REPO:=harbor.fizz.buzz/private
|
||||
TARGET :=
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
> @grep -h "##" $(MAKEFILE_LIST) | grep -v grep | sed -E 's/^([^:]*): *## */\1: /'
|
||||
|
||||
.PHONY: build
|
||||
build: ## Build the docker image.
|
||||
> docker build --tag $(IMAGE_NAME) --target=$(TARGET) --file Dockerfile ../../
|
||||
|
||||
.PHONY: push
|
||||
push: ## Push the docker image to a remote repository.
|
||||
ifdef REMOTE_REPO
|
||||
> docker tag $(IMAGE_NAME) $(REMOTE_REPO)/$(IMAGE_NAME)
|
||||
> docker push $(REMOTE_REPO)/$(IMAGE_NAME)
|
||||
else
|
||||
> @echo "REMOTE_REPO not defined, not pushing to a remote repo."
|
||||
endif
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
> docker rmi $(IMAGE_NAME)
|
||||
ifdef REMOTE_REPO
|
||||
> docker rmi $(REMOTE_REPO)/$(IMAGE_NAME)
|
||||
else
|
||||
> @echo "REMOTE_REPO not defined, not removing from remote repo."
|
||||
endif
|
||||
|
||||
.PHONY: run
|
||||
run: build
|
||||
run: ## Launch the docker image
|
||||
> docker run --rm -i -t $(IMAGE_NAME)
|
||||
|
||||
.PHONY: shell
|
||||
shell: ## Launch an interactive shell inside the docker image.
|
||||
shell: build
|
||||
> docker run --rm -i -t --entrypoint /bin/bash --mount type=tmpfs,destination=/tmp $(IMAGE_NAME)
|
@ -1,62 +1,10 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
ARG ALPINE_VERSION="3.20"
|
||||
FROM harbor.fizz.buzz/private/natter:latest AS builder
|
||||
|
||||
COPY . /source
|
||||
RUN ls /source/
|
||||
RUN natter build --config /source/natter.toml
|
||||
|
||||
|
||||
FROM scratch AS private
|
||||
ADD git@code.fizz.buzz:talexander/homepage_private.git /homepage_private
|
||||
|
||||
|
||||
|
||||
FROM scratch AS explorer
|
||||
ADD https://code.fizz.buzz/talexander/organic_ast_explorer.git /organic_ast_explorer
|
||||
|
||||
|
||||
|
||||
FROM scratch AS organic
|
||||
ADD git@code.fizz.buzz:talexander/organic.git /organic
|
||||
|
||||
|
||||
|
||||
FROM rustlang/rust:nightly-alpine$ALPINE_VERSION AS organic-build
|
||||
RUN apk add --no-cache musl-dev make bash
|
||||
RUN rustup target add wasm32-unknown-unknown
|
||||
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked cargo install wasm-bindgen-cli
|
||||
COPY --link --from=organic /organic /organic
|
||||
WORKDIR /organic
|
||||
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked make wasm
|
||||
|
||||
|
||||
|
||||
FROM node:lts-alpine$ALPINE_VERSION AS explorer-build
|
||||
COPY --link --from=explorer /organic_ast_explorer /organic_ast_explorer
|
||||
COPY --link --from=organic-build /organic /organic
|
||||
WORKDIR /organic_ast_explorer
|
||||
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/npmcache,sharing=locked npm set cache /npmcache && npm install
|
||||
RUN npm run release
|
||||
|
||||
|
||||
|
||||
|
||||
FROM rustlang/rust:nightly-alpine$ALPINE_VERSION AS natter-build
|
||||
RUN apk add --no-cache musl-dev
|
||||
ADD git@code.fizz.buzz:talexander/natter.git /natter
|
||||
WORKDIR /natter
|
||||
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked CARGO_TARGET_DIR=/target cargo build --profile release-lto
|
||||
|
||||
|
||||
|
||||
FROM alpine:$ALPINE_VERSION AS natter
|
||||
COPY --link --from=natter-build /target/release-lto/natter /usr/bin/
|
||||
COPY --link . /source
|
||||
COPY --link --from=private /homepage_private/static /source/static/
|
||||
COPY --link --from=explorer-build /organic_ast_explorer/dist /source/static/organic/ast_explorer/
|
||||
RUN --network=none --mount=type=tmpfs,target=/tmp natter build --config /source/natter.toml
|
||||
|
||||
|
||||
|
||||
|
||||
FROM alpine:$ALPINE_VERSION AS server
|
||||
FROM alpine:3.20 AS server
|
||||
|
||||
RUN apk add --no-cache bash nginx
|
||||
RUN addgroup web && adduser -D -G web web && install -d -D -o web -g web -m 700 /srv/http/public
|
||||
@ -64,6 +12,6 @@ RUN ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/
|
||||
|
||||
COPY --chown=web:web docker/server/nginx.conf /srv/http
|
||||
COPY --chown=web:web docker/server/headers.include /srv/http
|
||||
COPY --from=natter --chown=web:web /source/output/ /srv/http/public/
|
||||
COPY --from=builder --chown=web:web /source/output/ /srv/http/public/
|
||||
|
||||
ENTRYPOINT ["/usr/sbin/nginx", "-c", "/srv/http/nginx.conf", "-e", "stderr", "-g", "daemon off;"]
|
||||
|
@ -21,7 +21,7 @@ help:
|
||||
|
||||
.PHONY: build
|
||||
build: ## Build the docker image.
|
||||
> docker build --ssh default --tag $(IMAGE_NAME) --target=$(TARGET) --file Dockerfile ../../
|
||||
> docker build --tag $(IMAGE_NAME) --target=$(TARGET) --file Dockerfile ../../
|
||||
|
||||
.PHONY: push
|
||||
push: ## Push the docker image to a remote repository.
|
||||
|
@ -31,9 +31,6 @@ http {
|
||||
root /srv/http/public;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
autoindex on;
|
||||
|
||||
index index.html index.htm;
|
||||
if (-d $request_filename) {
|
||||
rewrite [^/]$ $http_x_forwarded_proto://$http_host$uri/ redirect;
|
||||
|
24
pages/index.org
Normal file
24
pages/index.org
Normal file
@ -0,0 +1,24 @@
|
||||
#+OPTIONS: html-postamble:nil
|
||||
#+date: <2023-12-23 Sat>
|
||||
#+author: Tom Alexander
|
||||
#+email:
|
||||
#+language: en
|
||||
#+select_tags: export
|
||||
#+exclude_tags: noexport
|
||||
|
||||
My dev blog will appear here as soon as I finish writing articles worthy of publishing. In the mean time, please check out my repos at [[https://code.fizz.buzz/explore/repos][code.fizz.buzz]].
|
||||
|
||||
Links:
|
||||
- My personal repos: [[https://code.fizz.buzz/explore/repos][code.fizz.buzz]]
|
||||
- LinkedIn: https://www.linkedin.com/in/tom-alexander-b6a18216/
|
||||
- GitHub: https://github.com/tomalexander
|
||||
- Resume: https://fizz.buzz/tom_alexander_resume.pdf
|
||||
- PGP Key: https://fizz.buzz/pgp.asc
|
||||
|
||||
* Why is your website the way it is?
|
||||
|
||||
I used to have a developer blog hosted at this domain. I quickly developed an appreciation for the power of org-mode for writing the content of the blog but I grew tired of inconsistent build results. The static site generators at the time would function by calling out to emacs itself to parse the org-mode and export HTML which meant that updates to emacs, my elisp packages, or the static site generator could cause compatibility issues. This often lead to things like escaping issues in old blog posts going unnoticed.
|
||||
|
||||
To solve the issue, and to seize the opportunity to gain more experience in Rust, I decided to write my own static site generator that would not depend on outside tools. So far I have written [[https://code.fizz.buzz/talexander/duster][the template engine]] and I am in the process of writing [[https://code.fizz.buzz/talexander/organic][an org-mode parser]]. When that is done, it should just be a matter of tying those two together with some minor glue to make a static site generator to create the new version of this site. Until that is done, I am using this hastily thrown-together manually-written html file as a placeholder.
|
||||
|
||||
That isn't to say that there are no exciting things hosted on this server, just not at the root domain. For example, this server is running kubernetes that I set up manually following [[https://github.com/kelseyhightower/kubernetes-the-hard-way][kubernetes-the-hard-way]] in a bunch of [[https://man.freebsd.org/cgi/man.cgi?bhyve][bhyve VMs]] that I networked together using [[https://man.freebsd.org/cgi/man.cgi?netgraph(4)][netgraph]]. On it I host my own [[https://www.powerdns.com/][PowerDNS]] server as the authoratative DNS server for fizz.buzz. It is integrated with [[https://cert-manager.io/][cert-manager]] and [[https://github.com/kubernetes-sigs/external-dns][ExternalDNS]] so Ingresses/LoadBalancers on my cluster automatically get valid TLS certificates and update the DNS records. I have a fully open-source self-hosted gitops workflow where a commit to a git repo I'm hosting in [[https://code.fizz.buzz/][gitea]], triggers a [[https://tekton.dev/][tekton pipeline]] through [[https://github.com/jenkins-x/lighthouse][lighthouse]] to build a docker image with [[https://github.com/GoogleContainerTools/kaniko][kaniko]], which gets pushed to my self-hosted [[https://goharbor.io/][harbor]] instance, which then gets deployed to my cluster via [[https://fluxcd.io/][flux]]. The end result is I make a commit to a repo and the result is deployed to my website in minutes.
|
@ -1,33 +0,0 @@
|
||||
set terminal svg background '#FFFFFF'
|
||||
set title 'Docker Memory Usage'
|
||||
set xdata time
|
||||
set timefmt '%s'
|
||||
set format x '%tH:%tM:%tS'
|
||||
# Please note this is in SI units (base 10), not IEC (base 2). So, for example, this would show a Gigabyte, not a Gibibyte.
|
||||
set format y '%.0s%cB'
|
||||
set datafile separator "|"
|
||||
|
||||
plot "-" using 1:2 title 'exciting\_bohr' with lines, "-" using 1:2 title 'jovial\_chandrasekhar' with lines
|
||||
0|512000
|
||||
4|512000
|
||||
9|512000
|
||||
13|4866441
|
||||
18|3166699
|
||||
23|3128950
|
||||
27|3128950
|
||||
32|3128950
|
||||
35|32547799
|
||||
40|4329570
|
||||
e
|
||||
0|528384
|
||||
5|516096
|
||||
9|561152
|
||||
14|561152
|
||||
18|561152
|
||||
23|561152
|
||||
28|8568963
|
||||
32|8528068
|
||||
37|8528068
|
||||
40|8528068
|
||||
45|8528068
|
||||
e
|
@ -1,177 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<svg
|
||||
width="600" height="480"
|
||||
viewBox="0 0 600 480"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
|
||||
<title>Gnuplot</title>
|
||||
<desc>Produced by GNUPLOT 6.0 patchlevel 2 </desc>
|
||||
|
||||
<g id="gnuplot_canvas">
|
||||
|
||||
<rect x="0" y="0" width="600" height="480" fill="#ffffff"/>
|
||||
<defs>
|
||||
|
||||
<circle id='gpDot' r='0.5' stroke-width='0.5' stroke='currentColor'/>
|
||||
<path id='gpPt0' stroke-width='0.222' stroke='currentColor' d='M-1,0 h2 M0,-1 v2'/>
|
||||
<path id='gpPt1' stroke-width='0.222' stroke='currentColor' d='M-1,-1 L1,1 M1,-1 L-1,1'/>
|
||||
<path id='gpPt2' stroke-width='0.222' stroke='currentColor' d='M-1,0 L1,0 M0,-1 L0,1 M-1,-1 L1,1 M-1,1 L1,-1'/>
|
||||
<rect id='gpPt3' stroke-width='0.222' stroke='currentColor' x='-1' y='-1' width='2' height='2'/>
|
||||
<rect id='gpPt4' stroke-width='0.222' stroke='currentColor' fill='currentColor' x='-1' y='-1' width='2' height='2'/>
|
||||
<circle id='gpPt5' stroke-width='0.222' stroke='currentColor' cx='0' cy='0' r='1'/>
|
||||
<use xlink:href='#gpPt5' id='gpPt6' fill='currentColor' stroke='none'/>
|
||||
<path id='gpPt7' stroke-width='0.222' stroke='currentColor' d='M0,-1.33 L-1.33,0.67 L1.33,0.67 z'/>
|
||||
<use xlink:href='#gpPt7' id='gpPt8' fill='currentColor' stroke='none'/>
|
||||
<use xlink:href='#gpPt7' id='gpPt9' stroke='currentColor' transform='rotate(180)'/>
|
||||
<use xlink:href='#gpPt9' id='gpPt10' fill='currentColor' stroke='none'/>
|
||||
<use xlink:href='#gpPt3' id='gpPt11' stroke='currentColor' transform='rotate(45)'/>
|
||||
<use xlink:href='#gpPt11' id='gpPt12' fill='currentColor' stroke='none'/>
|
||||
<path id='gpPt13' stroke-width='0.222' stroke='currentColor' d='M0,1.330 L1.265,0.411 L0.782,-1.067 L-0.782,-1.076 L-1.265,0.411 z'/>
|
||||
<use xlink:href='#gpPt13' id='gpPt14' fill='currentColor' stroke='none'/>
|
||||
<filter id='textbox' filterUnits='objectBoundingBox' x='0' y='0' height='1' width='1'>
|
||||
<feFlood flood-color='#FFFFFF' flood-opacity='1' result='bgnd'/>
|
||||
<feComposite in='SourceGraphic' in2='bgnd' operator='atop'/>
|
||||
</filter>
|
||||
<filter id='greybox' filterUnits='objectBoundingBox' x='0' y='0' height='1' width='1'>
|
||||
<feFlood flood-color='lightgrey' flood-opacity='1' result='grey'/>
|
||||
<feComposite in='SourceGraphic' in2='grey' operator='atop'/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g fill="none" color="#FFFFFF" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,444.00 L63.53,444.00 M574.82,444.00 L565.82,444.00 '/> <g transform="translate(46.14,447.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >0B</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,388.29 L63.53,388.29 M574.82,388.29 L565.82,388.29 '/> <g transform="translate(46.14,392.19)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >5MB</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,332.57 L63.53,332.57 M574.82,332.57 L565.82,332.57 '/> <g transform="translate(46.14,336.47)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >10MB</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,276.86 L63.53,276.86 M574.82,276.86 L565.82,276.86 '/> <g transform="translate(46.14,280.76)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >15MB</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,221.15 L63.53,221.15 M574.82,221.15 L565.82,221.15 '/> <g transform="translate(46.14,225.05)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >20MB</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,165.44 L63.53,165.44 M574.82,165.44 L565.82,165.44 '/> <g transform="translate(46.14,169.34)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >25MB</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,109.72 L63.53,109.72 M574.82,109.72 L565.82,109.72 '/> <g transform="translate(46.14,113.62)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >30MB</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,54.01 L63.53,54.01 M574.82,54.01 L565.82,54.01 '/> <g transform="translate(46.14,57.91)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >35MB</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,444.00 L54.53,435.00 M54.53,54.01 L54.53,63.01 '/> <g transform="translate(54.53,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:00</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M112.34,444.00 L112.34,435.00 M112.34,54.01 L112.34,63.01 '/> <g transform="translate(112.34,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:05</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M170.15,444.00 L170.15,435.00 M170.15,54.01 L170.15,63.01 '/> <g transform="translate(170.15,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:10</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M227.96,444.00 L227.96,435.00 M227.96,54.01 L227.96,63.01 '/> <g transform="translate(227.96,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:15</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M285.77,444.00 L285.77,435.00 M285.77,54.01 L285.77,63.01 '/> <g transform="translate(285.77,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:20</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M343.58,444.00 L343.58,435.00 M343.58,54.01 L343.58,63.01 '/> <g transform="translate(343.58,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:25</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M401.39,444.00 L401.39,435.00 M401.39,54.01 L401.39,63.01 '/> <g transform="translate(401.39,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:30</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M459.20,444.00 L459.20,435.00 M459.20,54.01 L459.20,63.01 '/> <g transform="translate(459.20,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:35</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M517.01,444.00 L517.01,435.00 M517.01,54.01 L517.01,63.01 '/> <g transform="translate(517.01,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:40</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M574.82,444.00 L574.82,435.00 M574.82,54.01 L574.82,63.01 '/> <g transform="translate(574.82,465.90)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >0:00:45</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,54.01 L54.53,444.00 L574.82,444.00 L574.82,54.01 L54.53,54.01 Z '/></g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</g>
|
||||
<g id="gnuplot_plot_1" ><title>exciting_bohr</title>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<g transform="translate(507.09,75.91)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >exciting_bohr</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='rgb(148, 0, 211)' d='M515.48,72.01 L558.04,72.01 M54.53,438.30 L100.78,438.30 L158.59,438.30 L204.84,389.78 L262.65,408.71 L320.46,409.14
|
||||
L366.70,409.14 L424.51,409.14 L459.20,81.33 L517.01,395.76 '/></g>
|
||||
</g>
|
||||
<g id="gnuplot_plot_2" ><title>jovial_chandrasekhar</title>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<g transform="translate(507.09,93.91)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="end">
|
||||
<text><tspan font-family="Arial" >jovial_chandrasekhar</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='rgb( 0, 158, 115)' d='M515.48,90.01 L558.04,90.01 M54.53,438.11 L112.34,438.25 L158.59,437.75 L216.40,437.75 L262.65,437.75 L320.46,437.75
|
||||
L378.27,348.52 L424.51,348.98 L482.32,348.98 L517.01,348.98 L574.82,348.98 '/></g>
|
||||
</g>
|
||||
<g fill="none" color="#FFFFFF" stroke="rgb( 0, 158, 115)" stroke-width="2.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="2.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="black" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<path stroke='black' d='M54.53,54.01 L54.53,444.00 L574.82,444.00 L574.82,54.01 L54.53,54.01 Z '/></g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
<g transform="translate(314.67,30.91)" stroke="none" fill="black" font-family="Arial" font-size="12.00" text-anchor="middle">
|
||||
<text><tspan font-family="Arial" >Docker Memory Usage</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="none" color="black" stroke="currentColor" stroke-width="1.00" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
@ -1,262 +0,0 @@
|
||||
#+OPTIONS: html-postamble:nil
|
||||
#+title: Graph Docker Container Memory Usage with Gnuplot
|
||||
#+date: <2025-02-08 Sat>
|
||||
#+author: Tom Alexander
|
||||
#+email:
|
||||
#+language: en
|
||||
#+select_tags: export
|
||||
#+exclude_tags: noexport
|
||||
|
||||
Sometimes it can be useful to build a graph of docker memory usage over time. For example, I was recently working on reducing the maximum memory of a long-running script. There certainly are heavy and complex options out there like setting up Prometheus and [[https://docs.docker.com/engine/daemon/prometheus/][configuring docker to export metrics to it]] but I threw together a small python script, using only the python standard library, that outputs gnuplot code to render a graph.
|
||||
|
||||
|
||||
* Usage
|
||||
Invoke the python script before starting any docker containers. Then, once a docker container is started, the script will start recording memory usage. Any additional docker containers that are started while the script is running will also get recorded. When no docker containers are left, the script will export gnuplot code over stdout that can then be rendered into a graph.
|
||||
|
||||
Each container will get its own line on the graph. All containers will have their start time aligned with the left-hand side of the graph as if they had started at the same time (so the X-axis it the number of seconds the docker container has been running, as opposed to the wall time).
|
||||
|
||||
If you'd like, you can insert a horizontal line at whatever memory quantity you'd like by uncommenting the src_python[:exports code]{horizontal_lines} array below. This can be useful for showing a maximum limit like the paltry 32GiB offered by Cloud Run.
|
||||
|
||||
* Example Invocation
|
||||
#+begin_src bash
|
||||
$ ./graph_docker_memory.py | gnuplot > graph.svg
|
||||
INFO:root:Waiting for a docker container to exist to start recording.
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 528384 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 528384 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 512000 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 516096 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 512000 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 561152 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 512000 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 561152 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 4866441 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 561152 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 3166699 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 561152 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 3128950 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 8568963 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 3128950 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 8528068 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 3128950 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 8528068 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 32547799 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 8528068 bytes
|
||||
INFO:root:Recorded stat exciting_bohr: 4329570 bytes
|
||||
INFO:root:Recorded stat jovial_chandrasekhar: 8528068 bytes
|
||||
#+end_src
|
||||
|
||||
You can also throw src_bash[:exports code]{tee} in there to save the gnuplot file to make manual adjustments or to render in some other fashion:
|
||||
#+begin_src bash
|
||||
./graph_docker_memory.py | tee graph.gnuplot | gnuplot > graph.svg
|
||||
#+end_src
|
||||
* Output
|
||||
The output from the above run would be:
|
||||
|
||||
[[./files/graph.svg]]
|
||||
|
||||
And the gnuplot source:
|
||||
#+begin_src gnuplot
|
||||
set terminal svg background '#FFFFFF'
|
||||
set title 'Docker Memory Usage'
|
||||
set xdata time
|
||||
set timefmt '%s'
|
||||
set format x '%tH:%tM:%tS'
|
||||
# Please note this is in SI units (base 10), not IEC (base 2). So, for example, this would show a Gigabyte, not a Gibibyte.
|
||||
set format y '%.0s%cB'
|
||||
set datafile separator "|"
|
||||
|
||||
plot "-" using 1:2 title 'exciting\_bohr' with lines, "-" using 1:2 title 'jovial\_chandrasekhar' with lines
|
||||
0|512000
|
||||
4|512000
|
||||
9|512000
|
||||
13|4866441
|
||||
18|3166699
|
||||
23|3128950
|
||||
27|3128950
|
||||
32|3128950
|
||||
35|32547799
|
||||
40|4329570
|
||||
e
|
||||
0|528384
|
||||
5|516096
|
||||
9|561152
|
||||
14|561152
|
||||
18|561152
|
||||
23|561152
|
||||
28|8568963
|
||||
32|8528068
|
||||
37|8528068
|
||||
40|8528068
|
||||
45|8528068
|
||||
e
|
||||
#+end_src
|
||||
* The script
|
||||
#+begin_src python
|
||||
#!/usr/bin/env python
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
from typing import Collection, Final, NewType, Tuple
|
||||
|
||||
ContainerId = NewType("ContainerId", str)
|
||||
ContainerName = NewType("ContainerName", str)
|
||||
|
||||
SAMPLE_INTERVAL_SECONDS: Final[int] = 2
|
||||
|
||||
|
||||
@dataclass
|
||||
class Sample:
|
||||
instant: datetime
|
||||
stats: dict[ContainerId, Stats]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Stats:
|
||||
memory_usage_bytes: int
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
samples: list[Sample] = []
|
||||
labels: dict[ContainerId, ContainerName] = {}
|
||||
first_pass = True
|
||||
# First wait for any docker container to exist.
|
||||
while True:
|
||||
sample, labels_in_sample = take_sample()
|
||||
if labels_in_sample:
|
||||
break
|
||||
if first_pass:
|
||||
first_pass = False
|
||||
logging.info("Waiting for a docker container to exist to start recording.")
|
||||
sleep(1)
|
||||
# And then record memory until no containers exist.
|
||||
while True:
|
||||
sample, labels_in_sample = take_sample()
|
||||
if not labels_in_sample:
|
||||
break
|
||||
samples.append(sample)
|
||||
labels = {**labels, **labels_in_sample}
|
||||
sleep(SAMPLE_INTERVAL_SECONDS)
|
||||
if labels:
|
||||
# Draws a red horizontal line at 32 GiB since that is the memory limit for cloud run.
|
||||
write_plot(
|
||||
samples,
|
||||
labels,
|
||||
# horizontal_lines=[(32 * 1024**3, "red", "Cloud Run Max Memory")],
|
||||
)
|
||||
|
||||
|
||||
def write_plot(
|
||||
samples: Collection[Sample],
|
||||
labels: dict[ContainerId, ContainerName],
|
||||
,*,
|
||||
horizontal_lines: Collection[Tuple[int, str, str | None]] = [],
|
||||
):
|
||||
starting_time_per_container = {
|
||||
container_id: min(
|
||||
(sample.instant for sample in samples if container_id in sample.stats)
|
||||
)
|
||||
for container_id in labels.keys()
|
||||
}
|
||||
print(
|
||||
"""set terminal svg background '#FFFFFF'
|
||||
set title 'Docker Memory Usage'
|
||||
set xdata time
|
||||
set timefmt '%s'
|
||||
set format x '%tH:%tM:%tS'
|
||||
# Please note this is in SI units (base 10), not IEC (base 2). So, for example, this would show a Gigabyte, not a Gibibyte.
|
||||
set format y '%.0s%cB'
|
||||
set datafile separator "|"
|
||||
"""
|
||||
)
|
||||
for y_value, color, label in horizontal_lines:
|
||||
print(
|
||||
f'''set arrow from graph 0, first {y_value} to graph 1, first {y_value} nohead linewidth 2 linecolor rgb "{color}"'''
|
||||
)
|
||||
if label is not None:
|
||||
print(f"""set label "{label}" at graph 0, first {y_value} offset 1,-0.5""")
|
||||
|
||||
# Include the horizontal lines in the range
|
||||
if len(horizontal_lines) > 0:
|
||||
print(f"""set yrange [*:{max(x[0] for x in horizontal_lines)}<*]""")
|
||||
line_definitions = ", ".join(
|
||||
[
|
||||
f""""-" using 1:2 title '{gnuplot_escape(name)}' with lines"""
|
||||
for container_id, name in sorted(labels.items())
|
||||
]
|
||||
)
|
||||
print("plot", line_definitions)
|
||||
for container_id in sorted(labels.keys()):
|
||||
start_time = int(starting_time_per_container[container_id].timestamp())
|
||||
for sample in sorted(samples, key=lambda x: x.instant):
|
||||
if container_id in sample.stats:
|
||||
print(
|
||||
"|".join(
|
||||
[
|
||||
str(int((sample.instant).timestamp()) - start_time),
|
||||
str(sample.stats[container_id].memory_usage_bytes),
|
||||
]
|
||||
)
|
||||
)
|
||||
print("e")
|
||||
|
||||
|
||||
def gnuplot_escape(inp: str) -> str:
|
||||
out = ""
|
||||
for c in inp:
|
||||
if c == "_":
|
||||
out += "\\"
|
||||
out += c
|
||||
return out
|
||||
|
||||
|
||||
def take_sample() -> Tuple[Sample, dict[ContainerId, ContainerName]]:
|
||||
labels: dict[ContainerId, ContainerName] = {}
|
||||
stats: dict[ContainerId, Stats] = {}
|
||||
docker_inspect = subprocess.run(
|
||||
["docker", "stats", "--no-stream", "--no-trunc", "--format", "json"],
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
for container_stat in (
|
||||
json.loads(l) for l in docker_inspect.stdout.decode("utf8").splitlines()
|
||||
):
|
||||
if not container_stat["ID"]:
|
||||
# When containers are starting up, they sometimes have no ID and "--" as the name.
|
||||
continue
|
||||
labels[ContainerId(container_stat["ID"])] = ContainerName(
|
||||
container_stat["Name"]
|
||||
)
|
||||
memory_usage = parse_mem_usage(container_stat["MemUsage"])
|
||||
stats[ContainerId(container_stat["ID"])] = Stats(
|
||||
memory_usage_bytes=memory_usage
|
||||
)
|
||||
for container_id, container_stat in stats.items():
|
||||
logging.info(
|
||||
f"Recorded stat {labels[container_id]}: {container_stat.memory_usage_bytes} bytes"
|
||||
)
|
||||
return Sample(instant=datetime.now(), stats=stats), labels
|
||||
|
||||
|
||||
def parse_mem_usage(mem_usage: str) -> int:
|
||||
parsed_mem_usage = re.match(
|
||||
r"(?P<number>[0-9]+\.?[0-9]*)(?P<unit>[^\s]+)", mem_usage
|
||||
)
|
||||
if parsed_mem_usage is None:
|
||||
raise Exception(f"Invalid Mem Usage: {mem_usage}")
|
||||
number = float(parsed_mem_usage.group("number"))
|
||||
unit = parsed_mem_usage.group("unit")
|
||||
for multiplier, identifier in enumerate(["B", "KiB", "MiB", "GiB", "TiB"]):
|
||||
if unit == identifier:
|
||||
return int(number * (1024**multiplier))
|
||||
raise Exception(f"Unrecognized unit: {unit}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
#+end_src
|
Binary file not shown.
Before Width: | Height: | Size: 85 KiB |
Binary file not shown.
Before Width: | Height: | Size: 61 KiB |
Binary file not shown.
Before Width: | Height: | Size: 95 KiB |
Binary file not shown.
Before Width: | Height: | Size: 30 KiB |
Binary file not shown.
Before Width: | Height: | Size: 356 KiB |
@ -1,650 +0,0 @@
|
||||
#+OPTIONS: html-postamble:nil
|
||||
#+title: Install sm64ex on the Steam Deck with Nix
|
||||
#+date: <2025-02-16 Sun>
|
||||
#+author: Tom Alexander
|
||||
#+email:
|
||||
#+language: en
|
||||
#+select_tags: export
|
||||
#+exclude_tags: noexport
|
||||
|
||||
[[https://github.com/sm64pc/sm64ex][Sm64ex]] is one of the native ports of Super Mario 64 to the PC. These ports are based on decompiled code from the Nintendo 64 game, enhanced and re-compiled for PC, so they run light and smooth without the overhead and limitations of emulation. To build the final game, you must supply a legally-acquired ROM of Super Mario 64 so that the assets can be extracted and compiled into sm64ex. Therefore a pre-built version cannot be distributed: the end-user must compile sm64ex themselves.
|
||||
|
||||
Using Nix, we can easily build our own copy. This has the following advantages:
|
||||
- Using flake.lock we'll be able to freeze the versions of all build and runtime dependencies, enabling us to rebuild exactly the same version on other Steam Decks, or after wiping our Steam Deck for a clean install.
|
||||
- All the built and installed packages will be stored under ~/nix~ so it won't alter or interfere with SteamOS and we can easily remove ~nix~ and everything installed through it by deleting ~/nix~.
|
||||
- We can optionally store our nix config and ~flake.lock~ in ~git~ so we can roll back to any previous version of our nix config and packages.
|
||||
|
||||
* Install Nix
|
||||
|
||||
"Nix" is the name of:
|
||||
- A programming language
|
||||
- A package manager
|
||||
- A Linux distribution
|
||||
|
||||
We are going to be installing Nix the *package manager* on SteamOS, and then writing a config in Nix the *programming language*. While NixOS is by far the best way to use Nix, I want my Deck to stay on SteamOS because I like the idea of having a Valve-managed standardized gaming appliance.
|
||||
|
||||
I recommend either setting up ssh access to your deck from another machine, or plugging your steam deck into a dock with a keyboard and mouse attached. This will be deeply tedious with the on-screen keyboard and touch screen.
|
||||
|
||||
First, enter desktop-mode and get to a terminal. We are going to be doing a single-user install, so we need to ~chown~ the ~/nix~ directory since it currently belongs to ~root~. If you haven't set a password for the ~deck~ user yet, then set one so we can use ~sudo~:
|
||||
#+begin_src bash
|
||||
passwd
|
||||
#+end_src
|
||||
|
||||
Then ~chown~ the ~/nix~ directory:
|
||||
#+begin_src bash
|
||||
sudo chown -R deck:deck /nix
|
||||
#+end_src
|
||||
|
||||
Then we need to download the Nix installer script and run it:
|
||||
#+begin_src bash
|
||||
wget https://nixos.org/nix/install
|
||||
# Read the install script and make sure you trust it. Then:
|
||||
sh install --no-daemon
|
||||
#+end_src
|
||||
|
||||
Now either re-launch your terminal or re-launch your ssh shell to get your environment variables set up. This will:
|
||||
- Add ~/home/deck/.nix-profile/bin~ to your ~PATH~
|
||||
- Add ~/home/deck/.nix-profile/share~ and ~/nix/var/nix/profiles/default/share~ to ~XDG_DATA_DIRS~
|
||||
- Set ~NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt~
|
||||
- Set ~NIX_PROFILES=/nix/var/nix/profiles/default /home/deck/.nix-profile~
|
||||
|
||||
* Start a Nix config with Flakes and Home-manager
|
||||
|
||||
We're going to store our config at src_text[:exports code]{~/.config/mynix}, so we create that directory and then write a few config files to it:
|
||||
#+begin_src bash
|
||||
mkdir ~/.config/mynix
|
||||
#+end_src
|
||||
|
||||
And then inside there write a src_text[:exports code]{flake.nix}.
|
||||
#+begin_src nix
|
||||
{
|
||||
description = "My system configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
...
|
||||
}:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
defaultPackage.${system} = home-manager.defaultPackage.${system};
|
||||
homeConfigurations."deck" = home-manager.lib.homeManagerConfiguration {
|
||||
inherit pkgs;
|
||||
|
||||
modules = [
|
||||
{
|
||||
home.username = "deck";
|
||||
home.homeDirectory = "/home/deck";
|
||||
home.stateVersion = "24.11";
|
||||
|
||||
programs.home-manager.enable = true;
|
||||
|
||||
# Enable flakes
|
||||
nix = {
|
||||
package = pkgs.nix;
|
||||
settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
};
|
||||
# Automatic garbage collection
|
||||
nix.gc = {
|
||||
# Runs nix-collect-garbage --delete-older-than 30d
|
||||
automatic = true;
|
||||
randomizedDelaySec = "14m";
|
||||
options = "--delete-older-than 30d";
|
||||
};
|
||||
# Deduplicate files in nix store
|
||||
nix.settings.auto-optimise-store = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
#+end_src
|
||||
|
||||
This config sets up home-manager with flakes and enables automatic garbage collection to clean up old builds in the nix store.
|
||||
|
||||
Then we need to apply the config:
|
||||
#+begin_src bash
|
||||
nix --extra-experimental-features 'nix-command flakes' run /home/deck/.config/mynix -- --extra-experimental-features 'nix-command flakes' switch --flake /home/deck/.config/mynix
|
||||
#+end_src
|
||||
|
||||
That's quite the cumbersome command, but after it has been run once it will enable ~nix-command~ and ~flakes~ so the next time we want to apply the config we will only have to run:
|
||||
#+begin_src bash
|
||||
nix run /home/deck/.config/mynix -- switch --flake /home/deck/.config/mynix
|
||||
#+end_src
|
||||
or
|
||||
#+begin_src bash
|
||||
home-manager switch --flake /home/deck/.config/mynix
|
||||
#+end_src
|
||||
|
||||
It may complain about the user systemd session being in a degraded state. That is because these units were in a degraded state before ~Nix~ was even installed. You can ignore this error:
|
||||
#+begin_src text
|
||||
The user systemd session is degraded:
|
||||
UNIT LOAD ACTIVE SUB DESCRIPTION
|
||||
● app-firewall\x2dapplet@autostart.service loaded failed failed Firewall Applet
|
||||
● obex.service loaded failed failed Bluetooth OBEX service
|
||||
#+end_src
|
||||
|
||||
The first run of this command generates a file called ~flake.lock~ which stores the revisions for all of our dependencies so if we install this config with this ~flake.lock~ on another machine, it will have exactly the same versions of everything.
|
||||
* Install sm64ex
|
||||
|
||||
We need to update our config to install sm64ex. To do that, first lets tell ~flake.nix~ to load a separate file by adding ~./sm64ex.nix~ to the ~modules~ list.
|
||||
#+begin_src nix
|
||||
{
|
||||
description = "My system configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
...
|
||||
}:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
defaultPackage.${system} = home-manager.defaultPackage.${system};
|
||||
homeConfigurations."deck" = home-manager.lib.homeManagerConfiguration {
|
||||
inherit pkgs;
|
||||
|
||||
modules = [
|
||||
{
|
||||
home.username = "deck";
|
||||
home.homeDirectory = "/home/deck";
|
||||
home.stateVersion = "24.11";
|
||||
|
||||
programs.home-manager.enable = true;
|
||||
|
||||
# Enable flakes
|
||||
nix = {
|
||||
package = pkgs.nix;
|
||||
settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
};
|
||||
# Automatic garbage collection
|
||||
nix.gc = {
|
||||
# Runs nix-collect-garbage --delete-older-than 30d
|
||||
automatic = true;
|
||||
randomizedDelaySec = "14m";
|
||||
options = "--delete-older-than 30d";
|
||||
};
|
||||
# Deduplicate files in nix store
|
||||
nix.settings.auto-optimise-store = true;
|
||||
}
|
||||
./sm64ex.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
#+end_src
|
||||
|
||||
And we need to create a src_text[:exports code]{~/.config/mynix/sm64ex.nix}:
|
||||
#+begin_src nix
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [ ];
|
||||
|
||||
config = {
|
||||
home.packages = with pkgs; [
|
||||
sm64ex
|
||||
];
|
||||
|
||||
# Allow installing sm64ex even though it is marked as non-free
|
||||
nixpkgs.config.allowUnfreePredicate =
|
||||
pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"sm64ex"
|
||||
];
|
||||
};
|
||||
}
|
||||
#+end_src
|
||||
|
||||
But we can't apply the config just yet. The sm64ex package [[https://github.com/NixOS/nixpkgs/blob/8bb37161a0488b89830168b81c48aed11569cb93/pkgs/by-name/sm/sm64baserom/package.nix#L10][is expecting that we already have a ~baserom.us.z64~ added to our nix store]]. Legally rip your ~baserom.us.z64~ and add it to your nix store via:
|
||||
#+begin_src bash
|
||||
nix-store --add-fixed sha256 baserom.us.z64
|
||||
#+end_src
|
||||
|
||||
Then we apply the config:
|
||||
#+begin_src bash
|
||||
home-manager switch --flake /home/deck/.config/mynix
|
||||
#+end_src
|
||||
|
||||
This will compile sm64ex and install it. But there's two problems we can immediately notice:
|
||||
1. It does not run when launched.
|
||||
2. There is no convenient way to launch it.
|
||||
|
||||
|
||||
We will be solving these by creating a ~.desktop~ file, introducing nixGL, and writing a wrapper script that makes sm64ex launch from within steam gaming mode properly.
|
||||
* Adding nixGL
|
||||
If we were running NixOS, graphical programs would work out-of-the-box. Unfortunately on other Linux distributions, we need to wrap our graphical programs so they know where to find the graphics drivers. To perform this wrapping, we will use [[https://github.com/nix-community/nixGL][nixGL]]. To install nixGL we need to add it as an input in our ~flake.nix~, apply its overlay, and add it as an ~extraSpecialArgs~ to have it automatically passed into each module:
|
||||
|
||||
#+begin_src nix
|
||||
{
|
||||
description = "My system configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
nixgl = {
|
||||
url = "github:nix-community/nixGL";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
nixgl,
|
||||
...
|
||||
}:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ nixgl.overlay ];
|
||||
};
|
||||
in
|
||||
{
|
||||
defaultPackage.${system} = home-manager.defaultPackage.${system};
|
||||
homeConfigurations."deck" = home-manager.lib.homeManagerConfiguration {
|
||||
inherit pkgs;
|
||||
|
||||
extraSpecialArgs = { inherit nixgl; };
|
||||
|
||||
modules = [
|
||||
{
|
||||
home.username = "deck";
|
||||
home.homeDirectory = "/home/deck";
|
||||
home.stateVersion = "24.11";
|
||||
|
||||
programs.home-manager.enable = true;
|
||||
|
||||
# Enable flakes
|
||||
nix = {
|
||||
package = pkgs.nix;
|
||||
settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
};
|
||||
# Automatic garbage collection
|
||||
nix.gc = {
|
||||
# Runs nix-collect-garbage --delete-older-than 30d
|
||||
automatic = true;
|
||||
randomizedDelaySec = "14m";
|
||||
options = "--delete-older-than 30d";
|
||||
};
|
||||
# Deduplicate files in nix store
|
||||
nix.settings.auto-optimise-store = true;
|
||||
|
||||
# Enable the nixGL wrappers
|
||||
nixGL.packages = nixgl.packages;
|
||||
}
|
||||
./sm64ex.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
#+end_src
|
||||
|
||||
And then to apply the nixGL wrapper to sm64ex we need to wrap our package install with ~config.lib.nixGL.wrap~ in ~sm64ex.nix~:
|
||||
#+begin_src nix
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [ ];
|
||||
|
||||
config = {
|
||||
home.packages = with pkgs; [
|
||||
(config.lib.nixGL.wrap sm64ex)
|
||||
];
|
||||
|
||||
# Allow installing sm64ex even though it is marked as non-free
|
||||
nixpkgs.config.allowUnfreePredicate =
|
||||
pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"sm64ex"
|
||||
];
|
||||
};
|
||||
}
|
||||
#+end_src
|
||||
|
||||
Apply the config with:
|
||||
#+begin_src bash
|
||||
home-manager switch --flake /home/deck/.config/mynix
|
||||
#+end_src
|
||||
and we should be able to launch ~sm64ex~ from the terminal in desktop-mode. Unfortunately, the Steam Deck built-in controls behave oddly in desktop mode, so unless you're using a separate stand-alone controller, you will probably want it set up to run in Steam gaming-mode where the controls behave normally.
|
||||
* Adding a ~.desktop~ file
|
||||
The first step to getting sm64ex running in gaming-mode is to create a ~.desktop~ file. ~.desktop~ files are small files with a bit of metadata about a program which can be used by programs such as your "start" / applications menu or an installed program list in Steam.
|
||||
|
||||
To add a ~.desktop~ file to the ~sm64ex~ package, we are going to use an "overlay". This overlay will replace the ~sm64ex~ package with a new one we create with the ~buildEnv~ function. This new package will contain the nixGL-wrapped version of the original ~sm64ex~ package and the ~.desktop~ file. We will create the ~.desktop~ file using the ~makeDesktopItem~ function.
|
||||
|
||||
First, we need to find a suitable icon. I used a 256x256 PNG from [[https://www.steamgriddb.com/search/icons?term=super+mario+64][SteamGridDB]]. Download whichever icon you want to src_text[:exports code]{~/.config/mynix/icon.png}. Then we can update our ~sm64ex.nix~ to:
|
||||
#+begin_src nix
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [ ];
|
||||
|
||||
config = {
|
||||
home.packages = with pkgs; [
|
||||
sm64ex
|
||||
];
|
||||
|
||||
# Allow installing sm64ex even though it is marked as non-free
|
||||
nixpkgs.config.allowUnfreePredicate =
|
||||
pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"sm64ex"
|
||||
];
|
||||
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
sm64ex =
|
||||
let
|
||||
desktop_item = pkgs.makeDesktopItem {
|
||||
name = "sm64ex";
|
||||
desktopName = "Super Mario 64";
|
||||
comment = "A PC Port of Super Mario 64.";
|
||||
categories = [
|
||||
"Game"
|
||||
];
|
||||
icon = "sm64ex";
|
||||
type = "Application";
|
||||
exec = "sm64ex";
|
||||
};
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = prev.sm64ex.name;
|
||||
paths = [
|
||||
(config.lib.nixGL.wrap prev.sm64ex)
|
||||
];
|
||||
# We have to use 555 instead of the normal 444 here because the .desktop file ends up inside $HOME on steam deck and desktop files must be either not in $HOME or must be executable, otherwise KDE Plasma refuses to execute them.
|
||||
postBuild = ''
|
||||
install -m 555 -D "${desktop_item}/share/applications/"* -t $out/share/applications/
|
||||
install -m 444 -D "${./icon.png}" $out/share/pixmaps/sm64ex.png
|
||||
'';
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
#+end_src
|
||||
|
||||
At this point, reboot to reload the ~.desktop~ files and go back into desktop-mode. You will see ~sm64ex~ in your Start/Applications menu under the "Game" category.
|
||||
|
||||
[[./files/start_menu.png]]
|
||||
* Adding a Steam wrapper
|
||||
To get controls that work correctly, we still need to add ~sm64ex~ to Steam. Unfortunately, Steam messes with the environment variables, so we need to make a small wrapper script that ensures ~sm64ex~ can find ~libGL.so~. To do this, we will create a package using the ~writeScriptBin~ function and install it with our other packages. Update ~sm64ex.nix~ to:
|
||||
#+begin_src nix
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
steam_sm64ex = pkgs.writeScriptBin "steam_sm64ex" ''
|
||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${pkgs.libglvnd}/lib"
|
||||
exec ${pkgs.sm64ex}/bin/sm64ex
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [ ];
|
||||
|
||||
config = {
|
||||
home.packages = with pkgs; [
|
||||
sm64ex
|
||||
steam_sm64ex
|
||||
];
|
||||
|
||||
# Allow installing sm64ex even though it is marked as non-free
|
||||
nixpkgs.config.allowUnfreePredicate =
|
||||
pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"sm64ex"
|
||||
];
|
||||
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
sm64ex =
|
||||
let
|
||||
desktop_item = pkgs.makeDesktopItem {
|
||||
name = "sm64ex";
|
||||
desktopName = "Super Mario 64";
|
||||
comment = "A PC Port of Super Mario 64.";
|
||||
categories = [
|
||||
"Game"
|
||||
];
|
||||
icon = "sm64ex";
|
||||
type = "Application";
|
||||
exec = "sm64ex";
|
||||
};
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = prev.sm64ex.name;
|
||||
paths = [
|
||||
(config.lib.nixGL.wrap prev.sm64ex)
|
||||
];
|
||||
# We have to use 555 instead of the normal 444 here because the .desktop file ends up inside $HOME on steam deck and desktop files must be either not in $HOME or must be executable, otherwise KDE Plasma refuses to execute them.
|
||||
postBuild = ''
|
||||
install -m 555 -D "${desktop_item}/share/applications/"* -t $out/share/applications/
|
||||
install -m 444 -D "${./icon.png}" $out/share/pixmaps/sm64ex.png
|
||||
'';
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
#+end_src
|
||||
|
||||
And apply the config with
|
||||
#+begin_src bash
|
||||
home-manager switch --flake /home/deck/.config/mynix
|
||||
#+end_src
|
||||
* Adding sm64ex to Steam
|
||||
Finally, we need to tell Steam about ~sm64ex~. Go into desktop-mode and launch Steam inside desktop-mode. Then go to "Add a Non-Steam Game to My Library".
|
||||
|
||||
[[./files/add_non_steam_game.png]]
|
||||
|
||||
Then find ~sm64ex~ in the list and add it.
|
||||
|
||||
[[./files/select_sm64ex.png]]
|
||||
|
||||
Then we need to edit the target, so find ~sm64ex~ in your Library, right click on it, and go to Properties.
|
||||
|
||||
[[./files/right_click_properties.png]]
|
||||
|
||||
And update the target to our new wrapper script to: src_text[:exports code]{"/home/deck/.nix-profile/bin/steam_sm64ex"}.
|
||||
|
||||
[[./files/set_target.png]]
|
||||
|
||||
Now you should be able to go back into gaming-mode and launch ~sm64ex~ from your Steam library. The controls should work correctly out-of-the-box.
|
||||
* Final Versions
|
||||
Now you should be able to go back into gaming-mode and launch ~sm64ex~ from your Steam library. The controls should work correctly out-of-the-box.
|
||||
|
||||
Below are the final versions of our config, but you can also browse [[https://code.fizz.buzz/talexander/machine_setup/src/commit/e3a7a410c41cccc28fe235edc802aef7d52c25bc/nix/steam_deck/configuration][my full Steam Deck Nix config]] which includes [[https://github.com/sm64pc/sm64ex][sm64ex (Super Mario 64)]], [[https://github.com/HarbourMasters/Shipwright][Ship of Harkinian (Ocarina of Time)]], and [[https://github.com/HarbourMasters/2ship2harkinian][2Ship2Harkinian (Majora's Mask)]]. It also moves the saves to src_text[:exports code]{"~/.persist"} and loads my configs for each game into place and read-only.
|
||||
|
||||
src_text[:exports code]{~/.config/mynix/flake.nix}
|
||||
#+begin_src nix
|
||||
{
|
||||
description = "My system configuration";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
nixgl = {
|
||||
url = "github:nix-community/nixGL";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
home-manager,
|
||||
nixgl,
|
||||
...
|
||||
}:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ nixgl.overlay ];
|
||||
};
|
||||
in
|
||||
{
|
||||
defaultPackage.${system} = home-manager.defaultPackage.${system};
|
||||
homeConfigurations."deck" = home-manager.lib.homeManagerConfiguration {
|
||||
inherit pkgs;
|
||||
|
||||
extraSpecialArgs = { inherit nixgl; };
|
||||
|
||||
modules = [
|
||||
{
|
||||
home.username = "deck";
|
||||
home.homeDirectory = "/home/deck";
|
||||
home.stateVersion = "24.11";
|
||||
|
||||
programs.home-manager.enable = true;
|
||||
|
||||
# Enable flakes
|
||||
nix = {
|
||||
package = pkgs.nix;
|
||||
settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
};
|
||||
# Automatic garbage collection
|
||||
nix.gc = {
|
||||
# Runs nix-collect-garbage --delete-older-than 30d
|
||||
automatic = true;
|
||||
randomizedDelaySec = "14m";
|
||||
options = "--delete-older-than 30d";
|
||||
};
|
||||
# Deduplicate files in nix store
|
||||
nix.settings.auto-optimise-store = true;
|
||||
|
||||
# Enable the nixGL wrappers
|
||||
nixGL.packages = nixgl.packages;
|
||||
}
|
||||
./sm64ex.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
#+end_src
|
||||
|
||||
src_text[:exports code]{~/.config/mynix/sm64ex.nix}
|
||||
#+begin_src nix
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
steam_sm64ex = pkgs.writeScriptBin "steam_sm64ex" ''
|
||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${pkgs.libglvnd}/lib"
|
||||
exec ${pkgs.sm64ex}/bin/sm64ex
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [ ];
|
||||
|
||||
config = {
|
||||
home.packages = with pkgs; [
|
||||
sm64ex
|
||||
steam_sm64ex
|
||||
];
|
||||
|
||||
# Allow installing sm64ex even though it is marked as non-free
|
||||
nixpkgs.config.allowUnfreePredicate =
|
||||
pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"sm64ex"
|
||||
];
|
||||
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
sm64ex =
|
||||
let
|
||||
desktop_item = pkgs.makeDesktopItem {
|
||||
name = "sm64ex";
|
||||
desktopName = "Super Mario 64";
|
||||
comment = "A PC Port of Super Mario 64.";
|
||||
categories = [
|
||||
"Game"
|
||||
];
|
||||
icon = "sm64ex";
|
||||
type = "Application";
|
||||
exec = "sm64ex";
|
||||
};
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = prev.sm64ex.name;
|
||||
paths = [
|
||||
(config.lib.nixGL.wrap prev.sm64ex)
|
||||
];
|
||||
# We have to use 555 instead of the normal 444 here because the .desktop file ends up inside $HOME on steam deck and desktop files must be either not in $HOME or must be executable, otherwise KDE Plasma refuses to execute them.
|
||||
postBuild = ''
|
||||
install -m 555 -D "${desktop_item}/share/applications/"* -t $out/share/applications/
|
||||
install -m 444 -D "${./icon.png}" $out/share/pixmaps/sm64ex.png
|
||||
'';
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
#+end_src
|
Loading…
x
Reference in New Issue
Block a user