Compare commits
159 Commits
050b426f6f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3929f22f3 | ||
|
|
bad12160ac | ||
|
|
c43679fda9 | ||
|
|
9cc28f6f0d | ||
|
|
d2256b8333 | ||
|
|
fa8753077a | ||
|
|
0420f58d02 | ||
|
|
0250aa106e | ||
|
|
ca1c456571 | ||
|
|
4403980e2e | ||
|
|
dbfbce955d | ||
|
|
2e08d2e59a | ||
|
|
9f14534c10 | ||
|
|
4e34ebc29e | ||
|
|
8d85d5ef79 | ||
|
|
7d73a3c948 | ||
|
|
c501f7cedc | ||
|
|
41927764fc | ||
|
|
75a763569b | ||
|
|
c67eb32774 | ||
|
|
04952895cf | ||
|
|
749f6d7a55 | ||
|
|
c4cf814f8d | ||
|
|
3245e830d2 | ||
|
|
57eb1b81ec | ||
|
|
c601c8697a | ||
|
|
4ea1a46705 | ||
|
|
4cc04bda46 | ||
|
|
7e934cd360 | ||
|
|
e34e2ef75f | ||
|
|
c067ca9cc8 | ||
|
|
b06424cb17 | ||
|
|
ae6f18d19c | ||
|
|
c371b999d5 | ||
|
|
073ac0ac25 | ||
|
|
1c356737c1 | ||
|
|
339bd433f6 | ||
|
|
bb5fa6a487 | ||
|
|
71b6db14d5 | ||
|
|
69fb91db37 | ||
|
|
5fd93fc648 | ||
|
|
033a17e355 | ||
|
|
824f34bd4c | ||
|
|
d93e91c625 | ||
|
|
db21bd8a55 | ||
|
|
69729bd329 | ||
|
|
ddea8fdceb | ||
|
|
71f639e503 | ||
|
|
8714d3b650 | ||
|
|
88064409a6 | ||
|
|
7c17087920 | ||
|
|
c1837addd0 | ||
|
|
79c36476bd | ||
|
|
4a0cbf3ba5 | ||
|
|
59ee13345e | ||
|
|
bf7f37260c | ||
|
|
ff478253c3 | ||
|
|
3e952ef0f4 | ||
|
|
4e0f66401d | ||
|
|
3867f965d2 | ||
|
|
5cac44c625 | ||
|
|
463be34302 | ||
|
|
8fd37cbf22 | ||
|
|
4fb67c18ae | ||
|
|
eb18185131 | ||
|
|
7c92b602bc | ||
|
|
aeca958cef | ||
|
|
495a3c0b4e | ||
|
|
9402c453da | ||
|
|
328b2ba520 | ||
|
|
4a76f61aa7 | ||
|
|
2c9396615e | ||
|
|
e7b6dc9059 | ||
|
|
bcdf441d77 | ||
|
|
884a28e63a | ||
|
|
1c3e2ca4d9 | ||
|
|
2081d25066 | ||
|
|
7ddc4011b3 | ||
|
|
379850fe3d | ||
|
|
8ab69e480e | ||
|
|
c5621212bc | ||
|
|
b9f74b7eca | ||
|
|
493adb4688 | ||
|
|
b6cc7a70b7 | ||
|
|
8868cfb63f | ||
|
|
ae3add9c81 | ||
|
|
98fa43575d | ||
|
|
5d3a6c4174 | ||
|
|
be467c8074 | ||
|
|
0da375c529 | ||
|
|
1b740b1f2f | ||
|
|
d94ecefab9 | ||
|
|
7501a018fd | ||
|
|
ce31397372 | ||
|
|
7e1e070e23 | ||
|
|
fa16a7dd39 | ||
|
|
59a91331cc | ||
|
|
d2ea6b6a0f | ||
|
|
fdff5a667b | ||
|
|
35cf675c87 | ||
|
|
3b63bbdfde | ||
|
|
24218f2979 | ||
|
|
6a086d57de | ||
|
|
93e0a2fe98 | ||
|
|
86d4e25aa0 | ||
|
|
cb5d131097 | ||
|
|
eb67327574 | ||
|
|
5228851c0e | ||
|
|
4fc08f4375 | ||
|
|
8905c9356b | ||
|
|
424a970014 | ||
|
|
138d694b27 | ||
|
|
397d4ea0bc | ||
|
|
818fca87f2 | ||
|
|
df339f20fa | ||
|
|
d5572c93cd | ||
|
|
322dbb8f4f | ||
|
|
904f834c86 | ||
|
|
48af194da0 | ||
|
|
bcb6b2d75f | ||
|
|
134444b2c3 | ||
|
|
4447f1ed4a | ||
|
|
457ff9e759 | ||
|
|
6f244a0a5f | ||
|
|
cb5838345e | ||
|
|
1107a653cf | ||
|
|
95d4ee7080 | ||
|
|
fa2dd96f78 | ||
|
|
7741e192f5 | ||
|
|
5dfd46852f | ||
|
|
88e10010d8 | ||
|
|
52c564d4fd | ||
|
|
f7874c1843 | ||
|
|
40120667f7 | ||
|
|
b06798f23f | ||
|
|
35dff5cdaf | ||
|
|
d641c8d638 | ||
|
|
7e2fd70212 | ||
|
|
4fb08bc7d0 | ||
|
|
01b55b7256 | ||
|
|
bd68681e44 | ||
|
|
27ff13e675 | ||
|
|
2914e42ba1 | ||
|
|
a8969f141d | ||
|
|
6f049e00d4 | ||
|
|
45a1076d18 | ||
|
|
775c88d67a | ||
|
|
efbf6cfc0c | ||
|
|
5af4a372ae | ||
|
|
72952adb6b | ||
|
|
8b85c02ef1 | ||
|
|
2ae4839ce0 | ||
|
|
5654c40d03 | ||
|
|
b538750287 | ||
|
|
65ed754bfe | ||
|
|
80cdf5166b | ||
|
|
3968121d54 | ||
|
|
a29b625631 | ||
|
|
e193fcc2ba |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
**/.git
|
||||||
|
target/
|
||||||
|
org_test_documents/
|
||||||
224
.webhook_bridge/pipeline-build-hash.yaml
Normal file
224
.webhook_bridge/pipeline-build-hash.yaml
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
apiVersion: tekton.dev/v1
|
||||||
|
kind: PipelineRun
|
||||||
|
metadata:
|
||||||
|
name: build
|
||||||
|
spec:
|
||||||
|
timeouts:
|
||||||
|
pipeline: "2h0m0s"
|
||||||
|
tasks: "1h0m0s"
|
||||||
|
finally: "0h30m0s"
|
||||||
|
taskRunTemplate:
|
||||||
|
serviceAccountName: build-bot
|
||||||
|
pipelineSpec:
|
||||||
|
params:
|
||||||
|
- name: image-name
|
||||||
|
description: The name for the built image
|
||||||
|
type: string
|
||||||
|
- name: target-name
|
||||||
|
description: The dockerfile target to build
|
||||||
|
type: string
|
||||||
|
- name: path-to-image-context
|
||||||
|
description: The path to the build context
|
||||||
|
type: string
|
||||||
|
- name: path-to-dockerfile
|
||||||
|
description: The path to the Dockerfile
|
||||||
|
type: string
|
||||||
|
tasks:
|
||||||
|
- name: report-pending
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has started"
|
||||||
|
- name: STATE
|
||||||
|
value: pending
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: fetch-repository
|
||||||
|
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
|
||||||
|
params:
|
||||||
|
- name: url
|
||||||
|
value: $(params.REPO_URL)
|
||||||
|
- name: revision
|
||||||
|
value: $(params.PULL_BASE_SHA)
|
||||||
|
- name: deleteExisting
|
||||||
|
value: "true"
|
||||||
|
- 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: build-image
|
||||||
|
taskRef:
|
||||||
|
resolver: git
|
||||||
|
params:
|
||||||
|
- name: url
|
||||||
|
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
|
||||||
|
- name: revision
|
||||||
|
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
|
||||||
|
- name: pathInRepo
|
||||||
|
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
|
||||||
|
params:
|
||||||
|
- name: OUTPUT
|
||||||
|
value: >-
|
||||||
|
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.fetch-repository.results.commit)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
|
||||||
|
- name: CONTEXT
|
||||||
|
value: $(params.path-to-image-context)
|
||||||
|
- name: DOCKERFILE
|
||||||
|
value: $(params.path-to-dockerfile)
|
||||||
|
- 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
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: dockerconfig
|
||||||
|
workspace: docker-credentials
|
||||||
|
runAfter:
|
||||||
|
- fetch-repository
|
||||||
|
finally:
|
||||||
|
- name: report-success
|
||||||
|
when:
|
||||||
|
- input: "$(tasks.status)"
|
||||||
|
operator: in
|
||||||
|
values: ["Succeeded", "Completed"]
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has succeeded"
|
||||||
|
- name: STATE
|
||||||
|
value: success
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: report-failure
|
||||||
|
when:
|
||||||
|
- input: "$(tasks.status)"
|
||||||
|
operator: in
|
||||||
|
values: ["Failed"]
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has failed"
|
||||||
|
- name: STATE
|
||||||
|
value: failure
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
workspaces:
|
||||||
|
- name: git-source
|
||||||
|
- name: docker-credentials
|
||||||
|
workspaces:
|
||||||
|
- name: git-source
|
||||||
|
volumeClaimTemplate:
|
||||||
|
spec:
|
||||||
|
storageClassName: "local-path"
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
subPath: rust-source
|
||||||
|
- name: docker-credentials
|
||||||
|
secret:
|
||||||
|
secretName: harbor-plain
|
||||||
|
params:
|
||||||
|
- name: image-name
|
||||||
|
value: "harbor.fizz.buzz/private/natter"
|
||||||
|
- name: target-name
|
||||||
|
value: ""
|
||||||
|
- name: path-to-image-context
|
||||||
|
value: .
|
||||||
|
- name: path-to-dockerfile
|
||||||
|
value: docker/natter/
|
||||||
369
.webhook_bridge/pipeline-format.yaml
Normal file
369
.webhook_bridge/pipeline-format.yaml
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
apiVersion: tekton.dev/v1
|
||||||
|
kind: PipelineRun
|
||||||
|
metadata:
|
||||||
|
name: rust-format
|
||||||
|
spec:
|
||||||
|
timeouts:
|
||||||
|
pipeline: "2h0m0s"
|
||||||
|
tasks: "1h0m0s"
|
||||||
|
finally: "0h30m0s"
|
||||||
|
taskRunTemplate:
|
||||||
|
serviceAccountName: build-bot
|
||||||
|
pipelineSpec:
|
||||||
|
params:
|
||||||
|
- name: image-name
|
||||||
|
description: The name for the built image
|
||||||
|
type: string
|
||||||
|
- name: target-name
|
||||||
|
description: The dockerfile target to build
|
||||||
|
type: string
|
||||||
|
- name: path-to-image-context
|
||||||
|
description: The path to the build context
|
||||||
|
type: string
|
||||||
|
- name: path-to-dockerfile
|
||||||
|
description: The path to the Dockerfile
|
||||||
|
type: string
|
||||||
|
tasks:
|
||||||
|
- name: report-pending
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has started"
|
||||||
|
- name: STATE
|
||||||
|
value: pending
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: fetch-repository
|
||||||
|
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
|
||||||
|
params:
|
||||||
|
- name: url
|
||||||
|
value: $(params.REPO_URL)
|
||||||
|
- name: revision
|
||||||
|
value: $(params.PULL_BASE_SHA)
|
||||||
|
- name: deleteExisting
|
||||||
|
value: "true"
|
||||||
|
- 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: build-image
|
||||||
|
taskRef:
|
||||||
|
resolver: git
|
||||||
|
params:
|
||||||
|
- name: url
|
||||||
|
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
|
||||||
|
- name: revision
|
||||||
|
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
|
||||||
|
- name: pathInRepo
|
||||||
|
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
|
||||||
|
params:
|
||||||
|
- name: OUTPUT
|
||||||
|
value: >-
|
||||||
|
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.fetch-repository.results.commit)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
|
||||||
|
- name: CONTEXT
|
||||||
|
value: $(params.path-to-image-context)
|
||||||
|
- name: DOCKERFILE
|
||||||
|
value: $(params.path-to-dockerfile)
|
||||||
|
- 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
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: dockerconfig
|
||||||
|
workspace: docker-credentials
|
||||||
|
runAfter:
|
||||||
|
- fetch-repository
|
||||||
|
- name: run-cargo-fmt
|
||||||
|
taskSpec:
|
||||||
|
metadata: {}
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
type: string
|
||||||
|
description: Docker image to run.
|
||||||
|
default: alpine:3.20
|
||||||
|
stepTemplate:
|
||||||
|
image: alpine:3.20
|
||||||
|
computeResources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 600Mi
|
||||||
|
workingDir: /workspace/source
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
mountPath: /source
|
||||||
|
- name: cargo-cache
|
||||||
|
mountPath: /usr/local/cargo/registry
|
||||||
|
optional: true
|
||||||
|
steps:
|
||||||
|
- name: run
|
||||||
|
image: $(params.docker-image)
|
||||||
|
workingDir: "$(workspaces.source.path)"
|
||||||
|
command: ["cargo", "fmt"]
|
||||||
|
args: []
|
||||||
|
env:
|
||||||
|
- name: CARGO_TARGET_DIR
|
||||||
|
value: /target
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
runAfter:
|
||||||
|
- build-image
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(tasks.build-image.results.IMAGE_URL[1])"
|
||||||
|
- name: run-prettier
|
||||||
|
taskSpec:
|
||||||
|
metadata: {}
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
type: string
|
||||||
|
description: Docker image to run.
|
||||||
|
default: alpine:3.20
|
||||||
|
stepTemplate:
|
||||||
|
image: alpine:3.20
|
||||||
|
computeResources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 600Mi
|
||||||
|
workingDir: /workspace/source
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
mountPath: /source
|
||||||
|
steps:
|
||||||
|
- name: run
|
||||||
|
image: $(params.docker-image)
|
||||||
|
workingDir: "$(workspaces.source.path)"
|
||||||
|
command: ["sh", "-c"]
|
||||||
|
args:
|
||||||
|
- |
|
||||||
|
prettier --write --no-error-on-unmatched-pattern "default_environment/**/*.js" "default_environment/**/*.css"
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
runAfter:
|
||||||
|
- build-image
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(tasks.build-image.results.IMAGE_URL[1])"
|
||||||
|
- name: commit-changes
|
||||||
|
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-cli/0.4/git-cli.yaml
|
||||||
|
params:
|
||||||
|
- name: GIT_USER_NAME
|
||||||
|
value: fluxcdbot
|
||||||
|
- name: GIT_USER_EMAIL
|
||||||
|
value: "fluxcdbot@users.noreply.github.com"
|
||||||
|
- name: GIT_SCRIPT
|
||||||
|
value: |
|
||||||
|
pwd
|
||||||
|
git config --global --add safe.directory /workspace/source
|
||||||
|
git_status=$(git status --porcelain)
|
||||||
|
if [ -n "$git_status" ]; then
|
||||||
|
git commit -a -m "CI: autofix rust code."
|
||||||
|
git push origin HEAD:$(params.PULL_BASE_REF)
|
||||||
|
else
|
||||||
|
echo "No changes to commit."
|
||||||
|
fi
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
runAfter:
|
||||||
|
- run-cargo-fmt
|
||||||
|
- run-prettier
|
||||||
|
finally:
|
||||||
|
- name: report-success
|
||||||
|
when:
|
||||||
|
- input: "$(tasks.status)"
|
||||||
|
operator: in
|
||||||
|
values: ["Succeeded", "Completed"]
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has succeeded"
|
||||||
|
- name: STATE
|
||||||
|
value: success
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: report-failure
|
||||||
|
when:
|
||||||
|
- input: "$(tasks.status)"
|
||||||
|
operator: in
|
||||||
|
values: ["Failed"]
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has failed"
|
||||||
|
- name: STATE
|
||||||
|
value: failure
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: cargo-cache-autoclean
|
||||||
|
taskSpec:
|
||||||
|
metadata: {}
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
type: string
|
||||||
|
description: Docker image to run.
|
||||||
|
default: alpine:3.20
|
||||||
|
stepTemplate:
|
||||||
|
image: alpine:3.20
|
||||||
|
computeResources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 600Mi
|
||||||
|
workingDir: /workspace/source
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
mountPath: /source
|
||||||
|
- name: cargo-cache
|
||||||
|
mountPath: /usr/local/cargo/registry
|
||||||
|
optional: true
|
||||||
|
steps:
|
||||||
|
- name: run
|
||||||
|
image: $(params.docker-image)
|
||||||
|
workingDir: "$(workspaces.source.path)"
|
||||||
|
command: [cargo, cache, --autoclean]
|
||||||
|
args: []
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(tasks.build-image.results.IMAGE_URL[1])"
|
||||||
|
workspaces:
|
||||||
|
- name: git-source
|
||||||
|
- name: docker-credentials
|
||||||
|
- name: cargo-cache
|
||||||
|
workspaces:
|
||||||
|
- name: git-source
|
||||||
|
volumeClaimTemplate:
|
||||||
|
spec:
|
||||||
|
storageClassName: "local-path"
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
subPath: rust-source
|
||||||
|
- name: cargo-cache
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: natter-cargo-cache-fmt
|
||||||
|
- name: docker-credentials
|
||||||
|
secret:
|
||||||
|
secretName: harbor-plain
|
||||||
|
params:
|
||||||
|
- name: image-name
|
||||||
|
value: "harbor.fizz.buzz/private/natter-development-format"
|
||||||
|
- name: target-name
|
||||||
|
value: ""
|
||||||
|
- name: path-to-image-context
|
||||||
|
value: docker/natter_development/
|
||||||
|
- name: path-to-dockerfile
|
||||||
|
value: docker/natter_development/
|
||||||
313
.webhook_bridge/pipeline-rust-clippy.yaml
Normal file
313
.webhook_bridge/pipeline-rust-clippy.yaml
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
apiVersion: tekton.dev/v1
|
||||||
|
kind: PipelineRun
|
||||||
|
metadata:
|
||||||
|
name: rust-clippy
|
||||||
|
spec:
|
||||||
|
taskRunTemplate:
|
||||||
|
serviceAccountName: build-bot
|
||||||
|
timeouts:
|
||||||
|
pipeline: "2h0m0s"
|
||||||
|
tasks: "1h0m40s"
|
||||||
|
finally: "0h30m0s"
|
||||||
|
pipelineSpec:
|
||||||
|
params:
|
||||||
|
- name: image-name
|
||||||
|
description: The name for the built image
|
||||||
|
type: string
|
||||||
|
- name: target-name
|
||||||
|
description: The dockerfile target to build
|
||||||
|
type: string
|
||||||
|
- name: path-to-image-context
|
||||||
|
description: The path to the build context
|
||||||
|
type: string
|
||||||
|
- name: path-to-dockerfile
|
||||||
|
description: The path to the Dockerfile
|
||||||
|
type: string
|
||||||
|
tasks:
|
||||||
|
- name: report-pending
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has started"
|
||||||
|
- name: STATE
|
||||||
|
value: pending
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: fetch-repository
|
||||||
|
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
|
||||||
|
params:
|
||||||
|
- name: url
|
||||||
|
value: $(params.REPO_URL)
|
||||||
|
- name: revision
|
||||||
|
value: $(params.PULL_BASE_SHA)
|
||||||
|
- name: deleteExisting
|
||||||
|
value: "true"
|
||||||
|
- 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: build-image
|
||||||
|
taskRef:
|
||||||
|
resolver: git
|
||||||
|
params:
|
||||||
|
- name: url
|
||||||
|
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
|
||||||
|
- name: revision
|
||||||
|
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
|
||||||
|
- name: pathInRepo
|
||||||
|
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
|
||||||
|
params:
|
||||||
|
- name: OUTPUT
|
||||||
|
value: >-
|
||||||
|
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.fetch-repository.results.commit)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
|
||||||
|
- name: CONTEXT
|
||||||
|
value: $(params.path-to-image-context)
|
||||||
|
- name: DOCKERFILE
|
||||||
|
value: $(params.path-to-dockerfile)
|
||||||
|
- 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
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: dockerconfig
|
||||||
|
workspace: docker-credentials
|
||||||
|
runAfter:
|
||||||
|
- fetch-repository
|
||||||
|
- name: run-cargo-clippy
|
||||||
|
taskSpec:
|
||||||
|
metadata: {}
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
type: string
|
||||||
|
description: Docker image to run.
|
||||||
|
default: alpine:3.20
|
||||||
|
stepTemplate:
|
||||||
|
image: alpine:3.20
|
||||||
|
computeResources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 600Mi
|
||||||
|
workingDir: /workspace/source
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
mountPath: /source
|
||||||
|
- name: cargo-cache
|
||||||
|
mountPath: /usr/local/cargo/registry
|
||||||
|
optional: true
|
||||||
|
steps:
|
||||||
|
- name: run
|
||||||
|
image: $(params.docker-image)
|
||||||
|
workingDir: "$(workspaces.source.path)"
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"cargo",
|
||||||
|
"clippy",
|
||||||
|
"--no-deps",
|
||||||
|
"--all-targets",
|
||||||
|
"--all-features",
|
||||||
|
"--",
|
||||||
|
"-D",
|
||||||
|
"warnings",
|
||||||
|
]
|
||||||
|
args: []
|
||||||
|
env:
|
||||||
|
- name: CARGO_TARGET_DIR
|
||||||
|
value: /target
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
runAfter:
|
||||||
|
- build-image
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(tasks.build-image.results.IMAGE_URL[1])"
|
||||||
|
finally:
|
||||||
|
- name: report-success
|
||||||
|
when:
|
||||||
|
- input: "$(tasks.status)"
|
||||||
|
operator: in
|
||||||
|
values: ["Succeeded", "Completed"]
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has succeeded"
|
||||||
|
- name: STATE
|
||||||
|
value: success
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: report-failure
|
||||||
|
when:
|
||||||
|
- input: "$(tasks.status)"
|
||||||
|
operator: in
|
||||||
|
values: ["Failed"]
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has failed"
|
||||||
|
- name: STATE
|
||||||
|
value: failure
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: cargo-cache-autoclean
|
||||||
|
taskSpec:
|
||||||
|
metadata: {}
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
type: string
|
||||||
|
description: Docker image to run.
|
||||||
|
default: alpine:3.20
|
||||||
|
stepTemplate:
|
||||||
|
image: alpine:3.20
|
||||||
|
computeResources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 600Mi
|
||||||
|
workingDir: /workspace/source
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
mountPath: /source
|
||||||
|
- name: cargo-cache
|
||||||
|
mountPath: /usr/local/cargo/registry
|
||||||
|
optional: true
|
||||||
|
steps:
|
||||||
|
- name: run
|
||||||
|
image: $(params.docker-image)
|
||||||
|
workingDir: "$(workspaces.source.path)"
|
||||||
|
command: [cargo, cache, --autoclean]
|
||||||
|
args: []
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(tasks.build-image.results.IMAGE_URL[1])"
|
||||||
|
workspaces:
|
||||||
|
- name: git-source
|
||||||
|
- name: docker-credentials
|
||||||
|
- name: cargo-cache
|
||||||
|
workspaces:
|
||||||
|
- name: git-source
|
||||||
|
volumeClaimTemplate:
|
||||||
|
spec:
|
||||||
|
storageClassName: "local-path"
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
subPath: rust-source
|
||||||
|
- name: cargo-cache
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: natter-cargo-cache-clippy
|
||||||
|
- name: docker-credentials
|
||||||
|
secret:
|
||||||
|
secretName: harbor-plain
|
||||||
|
params:
|
||||||
|
- name: image-name
|
||||||
|
value: "harbor.fizz.buzz/private/natter-development-clippy"
|
||||||
|
- name: target-name
|
||||||
|
value: ""
|
||||||
|
- name: path-to-image-context
|
||||||
|
value: docker/natter_development/
|
||||||
|
- name: path-to-dockerfile
|
||||||
|
value: docker/natter_development/
|
||||||
303
.webhook_bridge/pipeline-rust-test.yaml
Normal file
303
.webhook_bridge/pipeline-rust-test.yaml
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
apiVersion: tekton.dev/v1
|
||||||
|
kind: PipelineRun
|
||||||
|
metadata:
|
||||||
|
name: rust-test
|
||||||
|
spec:
|
||||||
|
timeouts:
|
||||||
|
pipeline: "2h0m0s"
|
||||||
|
tasks: "1h0m40s"
|
||||||
|
finally: "0h30m0s"
|
||||||
|
taskRunTemplate:
|
||||||
|
serviceAccountName: build-bot
|
||||||
|
pipelineSpec:
|
||||||
|
params:
|
||||||
|
- name: image-name
|
||||||
|
description: The name for the built image
|
||||||
|
type: string
|
||||||
|
- name: target-name
|
||||||
|
description: The dockerfile target to build
|
||||||
|
type: string
|
||||||
|
- name: path-to-image-context
|
||||||
|
description: The path to the build context
|
||||||
|
type: string
|
||||||
|
- name: path-to-dockerfile
|
||||||
|
description: The path to the Dockerfile
|
||||||
|
type: string
|
||||||
|
tasks:
|
||||||
|
- name: report-pending
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has started"
|
||||||
|
- name: STATE
|
||||||
|
value: pending
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: fetch-repository
|
||||||
|
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
|
||||||
|
params:
|
||||||
|
- name: url
|
||||||
|
value: $(params.REPO_URL)
|
||||||
|
- name: revision
|
||||||
|
value: $(params.PULL_BASE_SHA)
|
||||||
|
- name: deleteExisting
|
||||||
|
value: "true"
|
||||||
|
- 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: build-image
|
||||||
|
taskRef:
|
||||||
|
resolver: git
|
||||||
|
params:
|
||||||
|
- name: url
|
||||||
|
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
|
||||||
|
- name: revision
|
||||||
|
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
|
||||||
|
- name: pathInRepo
|
||||||
|
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
|
||||||
|
params:
|
||||||
|
- name: OUTPUT
|
||||||
|
value: >-
|
||||||
|
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.fetch-repository.results.commit)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
|
||||||
|
- name: CONTEXT
|
||||||
|
value: $(params.path-to-image-context)
|
||||||
|
- name: DOCKERFILE
|
||||||
|
value: $(params.path-to-dockerfile)
|
||||||
|
- 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
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: dockerconfig
|
||||||
|
workspace: docker-credentials
|
||||||
|
runAfter:
|
||||||
|
- fetch-repository
|
||||||
|
- name: run-cargo-test
|
||||||
|
taskSpec:
|
||||||
|
metadata: {}
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
type: string
|
||||||
|
description: Docker image to run.
|
||||||
|
default: alpine:3.20
|
||||||
|
stepTemplate:
|
||||||
|
image: alpine:3.20
|
||||||
|
computeResources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 600Mi
|
||||||
|
workingDir: /workspace/source
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
mountPath: /source
|
||||||
|
- name: cargo-cache
|
||||||
|
mountPath: /usr/local/cargo/registry
|
||||||
|
optional: true
|
||||||
|
steps:
|
||||||
|
- name: run
|
||||||
|
image: $(params.docker-image)
|
||||||
|
workingDir: "$(workspaces.source.path)"
|
||||||
|
command: [cargo, test, --no-fail-fast]
|
||||||
|
args: []
|
||||||
|
env:
|
||||||
|
- name: CARGO_TARGET_DIR
|
||||||
|
value: /target
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
runAfter:
|
||||||
|
- build-image
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(tasks.build-image.results.IMAGE_URL[1])"
|
||||||
|
finally:
|
||||||
|
- name: report-success
|
||||||
|
when:
|
||||||
|
- input: "$(tasks.status)"
|
||||||
|
operator: in
|
||||||
|
values: ["Succeeded", "Completed"]
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has succeeded"
|
||||||
|
- name: STATE
|
||||||
|
value: success
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: report-failure
|
||||||
|
when:
|
||||||
|
- input: "$(tasks.status)"
|
||||||
|
operator: in
|
||||||
|
values: ["Failed"]
|
||||||
|
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/gitea-set-status/0.1/gitea-set-status.yaml
|
||||||
|
params:
|
||||||
|
- name: CONTEXT
|
||||||
|
value: "$(params.JOB_NAME)"
|
||||||
|
- name: REPO_FULL_NAME
|
||||||
|
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
|
||||||
|
- name: GITEA_HOST_URL
|
||||||
|
value: code.fizz.buzz
|
||||||
|
- name: SHA
|
||||||
|
value: "$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: DESCRIPTION
|
||||||
|
value: "Build $(params.JOB_NAME) has failed"
|
||||||
|
- name: STATE
|
||||||
|
value: failure
|
||||||
|
- name: TARGET_URL
|
||||||
|
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
|
||||||
|
- name: cargo-cache-autoclean
|
||||||
|
taskSpec:
|
||||||
|
metadata: {}
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
type: string
|
||||||
|
description: Docker image to run.
|
||||||
|
default: alpine:3.20
|
||||||
|
stepTemplate:
|
||||||
|
image: alpine:3.20
|
||||||
|
computeResources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 600Mi
|
||||||
|
workingDir: /workspace/source
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
mountPath: /source
|
||||||
|
- name: cargo-cache
|
||||||
|
mountPath: /usr/local/cargo/registry
|
||||||
|
optional: true
|
||||||
|
steps:
|
||||||
|
- name: run
|
||||||
|
image: $(params.docker-image)
|
||||||
|
workingDir: "$(workspaces.source.path)"
|
||||||
|
command: [cargo, cache, --autoclean]
|
||||||
|
args: []
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
params:
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(tasks.build-image.results.IMAGE_URL[1])"
|
||||||
|
workspaces:
|
||||||
|
- name: git-source
|
||||||
|
- name: docker-credentials
|
||||||
|
- name: cargo-cache
|
||||||
|
workspaces:
|
||||||
|
- name: git-source
|
||||||
|
volumeClaimTemplate:
|
||||||
|
spec:
|
||||||
|
storageClassName: "local-path"
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 10Gi
|
||||||
|
subPath: rust-source
|
||||||
|
- name: cargo-cache
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: natter-cargo-cache-test
|
||||||
|
- name: docker-credentials
|
||||||
|
secret:
|
||||||
|
secretName: harbor-plain
|
||||||
|
params:
|
||||||
|
- name: image-name
|
||||||
|
value: "harbor.fizz.buzz/private/natter-development-test"
|
||||||
|
- name: target-name
|
||||||
|
value: ""
|
||||||
|
- name: path-to-image-context
|
||||||
|
value: docker/natter_development/
|
||||||
|
- name: path-to-dockerfile
|
||||||
|
value: docker/natter_development/
|
||||||
25
.webhook_bridge/webhook_bridge.toml
Normal file
25
.webhook_bridge/webhook_bridge.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[[push]]
|
||||||
|
name = "rust-test"
|
||||||
|
source = "pipeline-rust-test.yaml"
|
||||||
|
clone_uri = "git@code.fizz.buzz:talexander/natter.git"
|
||||||
|
skip_branches = [ "^v[0-9]+\\.[0-9]+\\.[0-9]+$" ]
|
||||||
|
|
||||||
|
[[push]]
|
||||||
|
name = "clippy"
|
||||||
|
source = "pipeline-rust-clippy.yaml"
|
||||||
|
clone_uri = "git@code.fizz.buzz:talexander/natter.git"
|
||||||
|
skip_branches = [ "^v[0-9]+\\.[0-9]+\\.[0-9]+$" ]
|
||||||
|
|
||||||
|
[[push]]
|
||||||
|
name = "format"
|
||||||
|
source = "pipeline-format.yaml"
|
||||||
|
clone_uri = "git@code.fizz.buzz:talexander/natter.git"
|
||||||
|
skip_branches = [ "^v[0-9]+\\.[0-9]+\\.[0-9]+$" ]
|
||||||
|
|
||||||
|
[[push]]
|
||||||
|
name = "build"
|
||||||
|
source = "pipeline-build-hash.yaml"
|
||||||
|
clone_uri = "git@code.fizz.buzz:talexander/natter.git"
|
||||||
|
branches = [ "^main$", "^master$" ]
|
||||||
1464
Cargo.lock
generated
1464
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
47
Cargo.toml
47
Cargo.toml
@@ -1,21 +1,54 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "writer"
|
name = "natter"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
||||||
|
description = "A static site generator using org source files."
|
||||||
|
license = "0BSD"
|
||||||
|
repository = "https://code.fizz.buzz/talexander/natter"
|
||||||
|
readme = "README.md"
|
||||||
|
keywords = ["static", "site", "generator"]
|
||||||
|
categories = ["command-line-utilities"]
|
||||||
|
resolver = "2"
|
||||||
|
include = [
|
||||||
|
"LICENSE",
|
||||||
|
"**/*.rs",
|
||||||
|
"Cargo.toml",
|
||||||
|
"Cargo.lock",
|
||||||
|
"default_environment/"
|
||||||
|
]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.4.6", default-features = false, features = ["std", "color", "help", "derive"] }
|
clap = { version = "4.4.6", default-features = false, features = ["std", "color", "help", "derive"] }
|
||||||
duster = { git = "https://code.fizz.buzz/talexander/duster.git", branch = "master" }
|
duster = "0.1.1"
|
||||||
tree-sitter-highlight = { git = "https://github.com/tree-sitter/tree-sitter.git", rev = "52f7eaff3182a726eb064a91d4e49dfbaecd4ee3" }
|
|
||||||
futures = "0.3.29"
|
futures = "0.3.29"
|
||||||
include_dir = "0.7.3"
|
include_dir = "0.7.3"
|
||||||
# TODO: This is temporary to work on the latest organic code. Eventually switch back to using the published crate.
|
# TODO: This is temporary to work on the latest organic code. Eventually switch back to using the published crate.
|
||||||
# organic = { path = "../organic" }
|
# organic = { path = "../organic" }
|
||||||
organic = "0.1.13"
|
organic = "0.1.16"
|
||||||
serde = { version = "1.0.189", default-features = false, features = ["std", "derive"] }
|
serde = { version = "1.0.189", default-features = false, features = ["std", "derive"] }
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] }
|
tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] }
|
||||||
toml = "0.8.2"
|
toml = "0.8.2"
|
||||||
walkdir = "2.4.0"
|
tree-sitter-bash = "0.23.3"
|
||||||
|
tree-sitter-highlight = "0.25.2"
|
||||||
|
tree-sitter-nix = "0.0.2"
|
||||||
|
tree-sitter-python = "0.23.6"
|
||||||
|
url = "2.5.0"
|
||||||
|
tracing = { version = "0.1.37", optional = true }
|
||||||
|
tracing-opentelemetry = { version = "0.20.0", optional = true }
|
||||||
|
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
|
||||||
|
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
|
||||||
|
opentelemetry-otlp = { version = "0.13.0", optional = true }
|
||||||
|
opentelemetry-semantic-conventions = { version = "0.12.0", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["tracing"]
|
||||||
|
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
||||||
|
|
||||||
|
# Optimized build for any sort of release.
|
||||||
|
[profile.release-lto]
|
||||||
|
inherits = "release"
|
||||||
|
lto = true
|
||||||
|
strip = "symbols"
|
||||||
|
|||||||
39
Makefile
Normal file
39
Makefile
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
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:=natter
|
||||||
|
TARGET :=
|
||||||
|
|
||||||
|
.PHONY: help
|
||||||
|
help:
|
||||||
|
> @grep -h "##" $(MAKEFILE_LIST) | grep -v grep | sed -E 's/^([^:]*): *## */\1: /'
|
||||||
|
|
||||||
|
.PHONY: docker_test
|
||||||
|
docker_test: ## Run the rust tests
|
||||||
|
> $(MAKE) -C docker/natter_development build
|
||||||
|
> docker run --rm -i -t --mount type=tmpfs,destination=/tmp -v "$(shell readlink -f .):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" natter-development cargo test
|
||||||
|
|
||||||
|
.PHONY: docker_clippy
|
||||||
|
docker_clippy: ## Run static analysis of the code.
|
||||||
|
> $(MAKE) -C docker/natter_development build
|
||||||
|
> docker run --rm -i -t --mount type=tmpfs,destination=/tmp -v "$(shell readlink -f .):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" natter-development cargo clippy --no-deps --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
.PHONY: docker_format
|
||||||
|
docker_format: ## Auto-format source files.
|
||||||
|
> $(MAKE) -C docker/natter_development build
|
||||||
|
> docker run --rm -i -t --mount type=tmpfs,destination=/tmp -v "$(shell readlink -f .):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" natter-development cargo fmt
|
||||||
|
> docker run --rm -i -t --mount type=tmpfs,destination=/tmp -v "$(shell readlink -f .):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" natter-development prettier --write --no-error-on-unmatched-pattern "default_environment/**/*.js" "default_environment/**/*.css"
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
> $(MAKE) -C docker/natter_development clean
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Natter
|
||||||
|
|
||||||
|
Natter is a static site generator for blogs using org as an input format. It is at a very early stage, and will have deep sweeping changes.
|
||||||
21
TODO.org
Normal file
21
TODO.org
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
* Things to do [6/17]
|
||||||
|
** DONE If the paragraph only contains an image, text-align center
|
||||||
|
** DONE Syntax highlighting for code blocks
|
||||||
|
** TODO Render gnuplot
|
||||||
|
** TODO Pretty-print the timestamps
|
||||||
|
** TODO Support Table of Contents
|
||||||
|
** TODO Support line numbers in code blocks
|
||||||
|
** TODO Support references to code block lines
|
||||||
|
** TODO Only include text up to first heading on homepage and include a "read more" link
|
||||||
|
** DONE Make loading language-specific CSS files conditional on the presence of src blocks using those languages
|
||||||
|
** DONE Set up tracing so I can use warning and such
|
||||||
|
** TODO Make copying of language-specific CSS files conditional on the presence of src blocks using those languages
|
||||||
|
** TODO Switch to an entirely lazily-evaluated output tree
|
||||||
|
** TODO Add highlighting for languages [1/2]
|
||||||
|
*** DONE bash
|
||||||
|
*** TODO gnuplot
|
||||||
|
https://github.com/dpezto/tree-sitter-gnuplot is not on crates.io so I'd have to add a git dependency to use it. This would prevent publishing this crate to crates.io.
|
||||||
|
** DONE Bug: carry over highlight starts when breaking lines
|
||||||
|
** TODO Add dates to posts
|
||||||
|
** DONE Add support for unlisted posts (posts that do not show up on the homepage).
|
||||||
|
** TODO Add support for showing file name where we currently show language
|
||||||
39
default_environment/stylesheet/language_bash.css
Normal file
39
default_environment/stylesheet/language_bash.css
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
:root {
|
||||||
|
--srclg-bash-srchl-comment-color: #048a81;
|
||||||
|
--srclg-bash-srchl-function-color: #e95a62;
|
||||||
|
--srclg-bash-srchl-keyword-color: #1a936f;
|
||||||
|
--srclg-bash-srchl-property-color: inherit;
|
||||||
|
--srclg-bash-srchl-string-color: #ecc30b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--srclg-bash-srchl-comment-color: #fb757e;
|
||||||
|
--srclg-bash-srchl-function-color: #16a59d;
|
||||||
|
--srclg-bash-srchl-keyword-color: #e56c90;
|
||||||
|
--srclg-bash-srchl-property-color: inherit;
|
||||||
|
--srclg-bash-srchl-string-color: #133cf4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_content {
|
||||||
|
.src_block {
|
||||||
|
&.srclg_bash {
|
||||||
|
.srchl_comment {
|
||||||
|
color: var(--srclg-bash-srchl-comment-color);
|
||||||
|
}
|
||||||
|
.srchl_function {
|
||||||
|
color: var(--srclg-bash-srchl-function-color);
|
||||||
|
}
|
||||||
|
.srchl_keyword {
|
||||||
|
color: var(--srclg-bash-srchl-keyword-color);
|
||||||
|
}
|
||||||
|
.srchl_property {
|
||||||
|
color: var(--srclg-bash-srchl-property-color);
|
||||||
|
}
|
||||||
|
.srchl_string {
|
||||||
|
color: var(--srclg-bash-srchl-string-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
default_environment/stylesheet/language_nix.css
Normal file
39
default_environment/stylesheet/language_nix.css
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
:root {
|
||||||
|
--srclg-nix-srchl-keyword-color: #1a936f;
|
||||||
|
--srclg-nix-srchl-comment-color: #048a81;
|
||||||
|
--srclg-nix-srchl-property-color: #bfbccb;
|
||||||
|
--srclg-nix-srchl-string-color: #ecc30b;
|
||||||
|
--srclg-nix-srchl-string-special-path-color: #067bc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--srclg-nix-srchl-keyword-color: #e56c90;
|
||||||
|
--srclg-nix-srchl-comment-color: #fb757e;
|
||||||
|
--srclg-nix-srchl-property-color: #404334;
|
||||||
|
--srclg-nix-srchl-string-color: #133cf4;
|
||||||
|
--srclg-nix-srchl-string-special-path-color: #f9843d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_content {
|
||||||
|
.src_block {
|
||||||
|
&.srclg_nix {
|
||||||
|
.srchl_keyword {
|
||||||
|
color: var(--srclg-nix-srchl-keyword-color);
|
||||||
|
}
|
||||||
|
.srchl_comment {
|
||||||
|
color: var(--srclg-nix-srchl-comment-color);
|
||||||
|
}
|
||||||
|
.srchl_property {
|
||||||
|
color: var(--srclg-nix-srchl-property-color);
|
||||||
|
}
|
||||||
|
.srchl_string {
|
||||||
|
color: var(--srclg-nix-srchl-string-color);
|
||||||
|
}
|
||||||
|
.srchl_string_special_path {
|
||||||
|
color: var(--srclg-nix-srchl-string-special-path-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
default_environment/stylesheet/language_python.css
Normal file
51
default_environment/stylesheet/language_python.css
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/* ea912c */
|
||||||
|
/* e95a62 */
|
||||||
|
:root {
|
||||||
|
--srclg-python-srchl-comment-color: #048a81;
|
||||||
|
--srclg-python-srchl-function-builtin-color: #e95a62;
|
||||||
|
--srclg-python-srchl-keyword-color: #1a936f;
|
||||||
|
--srclg-python-srchl-property-color: inherit;
|
||||||
|
--srclg-python-srchl-string-color: #ecc30b;
|
||||||
|
--srclg-python-srchl-type-color: #067bc2;
|
||||||
|
--srclg-python-srchl-variable-color: #ea912c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--srclg-python-srchl-comment-color: #fb757e;
|
||||||
|
--srclg-python-srchl-function-builtin-color: #16a59d;
|
||||||
|
--srclg-python-srchl-keyword-color: #e56c90;
|
||||||
|
--srclg-python-srchl-property-color: inherit;
|
||||||
|
--srclg-python-srchl-string-color: #133cf4;
|
||||||
|
--srclg-python-srchl-type-color: #f9843d;
|
||||||
|
--srclg-python-srchl-variable-color: #156ed3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_content {
|
||||||
|
.src_block {
|
||||||
|
&.srclg_python {
|
||||||
|
.srchl_comment {
|
||||||
|
color: var(--srclg-python-srchl-comment-color);
|
||||||
|
}
|
||||||
|
.srchl_function_builtin {
|
||||||
|
color: var(--srclg-python-srchl-function-builtin-color);
|
||||||
|
}
|
||||||
|
.srchl_keyword {
|
||||||
|
color: var(--srclg-python-srchl-keyword-color);
|
||||||
|
}
|
||||||
|
.srchl_property {
|
||||||
|
color: var(--srclg-python-srchl-property-color);
|
||||||
|
}
|
||||||
|
.srchl_string {
|
||||||
|
color: var(--srclg-python-srchl-string-color);
|
||||||
|
}
|
||||||
|
.srchl_type {
|
||||||
|
color: var(--srclg-python-srchl-type-color);
|
||||||
|
}
|
||||||
|
.srchl_variable {
|
||||||
|
color: var(--srclg-python-srchl-variable-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,358 @@
|
|||||||
|
:root {
|
||||||
|
--main-max-width: 800px;
|
||||||
|
--site-background-color: #0a0a0a;
|
||||||
|
--site-text-color: #fffffc;
|
||||||
|
--header-divider-color: #6a687a;
|
||||||
|
|
||||||
|
--stream-divider-color: #6ccff6;
|
||||||
|
--stream-post-background-color: #0a0a0a;
|
||||||
|
|
||||||
|
--blog-post-background-color: #0a0a0a;
|
||||||
|
|
||||||
|
--src-font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo,
|
||||||
|
Consolas, "DejaVu Sans Mono", monospace;
|
||||||
|
|
||||||
|
--src-block-background-color: #141414;
|
||||||
|
--src-block-border-color: #84828f;
|
||||||
|
--src-block-language-color: #0a0a0a;
|
||||||
|
--src-block-language-background: #84828f;
|
||||||
|
|
||||||
|
--quote-block-border-color: #84828f;
|
||||||
|
|
||||||
|
--table-border-color: #6a687a;
|
||||||
|
--table-odd-background-color: #0a0a0a;
|
||||||
|
--table-even-background-color: #141414;
|
||||||
|
|
||||||
|
--header-nav-regular-font-color: var(--site-text-color);
|
||||||
|
--header-nav-regular-background-color: var(--site-background-color);
|
||||||
|
--header-nav-hover-font-color: var(--site-background-color);
|
||||||
|
--header-nav-hover-background-color: var(--site-text-color);
|
||||||
|
--header-home-regular-font-color: var(--site-text-color);
|
||||||
|
--header-home-hover-font-color: #6ccff6;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--site-background-color: #f5f5f5;
|
||||||
|
--site-text-color: #000003;
|
||||||
|
--header-divider-color: #959785;
|
||||||
|
|
||||||
|
--stream-divider-color: #933009;
|
||||||
|
--stream-post-background-color: #f5f5f5;
|
||||||
|
|
||||||
|
--blog-post-background-color: #f5f5f5;
|
||||||
|
|
||||||
|
--src-block-background-color: #ebebeb;
|
||||||
|
--src-block-border-color: #7b7d70;
|
||||||
|
--src-block-language-color: #f5f5f5;
|
||||||
|
--src-block-language-background: #7b7d70;
|
||||||
|
|
||||||
|
--quote-block-border-color: #7b7d70;
|
||||||
|
|
||||||
|
--table-border-color: #959785;
|
||||||
|
--table-odd-background-color: #f5f5f5;
|
||||||
|
--table-even-background-color: #ebebeb;
|
||||||
|
|
||||||
|
--header-nav-regular-font-color: var(--site-text-color);
|
||||||
|
--header-nav-regular-background-color: var(--site-background-color);
|
||||||
|
--header-nav-hover-font-color: var(--site-background-color);
|
||||||
|
--header-nav-hover-background-color: var(--site-text-color);
|
||||||
|
--header-home-regular-font-color: var(--site-text-color);
|
||||||
|
--header-home-hover-font-color: #933009;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--site-text-color);
|
||||||
|
background-color: var(--site-background-color);
|
||||||
|
font-family: source-sans-pro, Seravek, "Gill Sans Nova", Ubuntu, Calibri,
|
||||||
|
"DejaVu Sans", sans-serif;
|
||||||
|
|
||||||
|
a:link,
|
||||||
|
a:visited {
|
||||||
|
/* TODO: Should I use a different color for links? */
|
||||||
|
color: var(--site-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page_centering {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page_header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: stretch;
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--main-max-width);
|
||||||
|
border-bottom: 0.1rem solid var(--header-divider-color);
|
||||||
|
|
||||||
|
.home_link {
|
||||||
|
display: block;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--header-home-regular-font-color);
|
||||||
|
transition-property: color;
|
||||||
|
transition-duration: 0.1s;
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--header-home-hover-font-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:link,
|
||||||
|
&:visited {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header_nav_bar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: end;
|
||||||
|
align-items: stretch;
|
||||||
|
column-gap: 1rem;
|
||||||
|
|
||||||
|
.nav_link {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
color: var(--header-nav-regular-font-color);
|
||||||
|
background: var(--header-nav-regular-background-color);
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
transition-property: background, color;
|
||||||
|
transition-duration: 0.1s;
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--header-nav-hover-font-color);
|
||||||
|
background: var(--header-nav-hover-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_content {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--main-max-width);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding-bottom: 8rem;
|
||||||
|
|
||||||
|
/* A stand-alone blog post (not in a blog stream). */
|
||||||
|
.blog_post {
|
||||||
|
padding: 1rem 0.2rem 0 0.2rem;
|
||||||
|
background: var(--blog-post-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog_stream {
|
||||||
|
.stream_divider {
|
||||||
|
color: var(--stream-divider-color);
|
||||||
|
margin: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream_nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
> a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
> .spacer {
|
||||||
|
display: inline-block;
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A blog post in a blog stream (for example, the homepage). */
|
||||||
|
.blog_stream_post {
|
||||||
|
background: var(--stream-post-background-color);
|
||||||
|
padding: 0 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog_stream_post {
|
||||||
|
background: var(--stream-post-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog_stream_post:nth-child(1) {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog_post_title {
|
||||||
|
font-size: 2.9rem;
|
||||||
|
font-weight: 700;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 1rem 0;
|
||||||
|
|
||||||
|
&.image {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.src_block {
|
||||||
|
background: var(--src-block-background-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid var(--src-block-border-color);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: var(--src-font-family);
|
||||||
|
margin: 1rem 0;
|
||||||
|
font-variant-ligatures: none;
|
||||||
|
|
||||||
|
.src_language {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--src-block-language-color);
|
||||||
|
background: var(--src-block-language-background);
|
||||||
|
border-radius: 0 0 3px 0;
|
||||||
|
padding: 0.1rem 0.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.src_body {
|
||||||
|
margin: 0.5rem;
|
||||||
|
|
||||||
|
.src_line {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline_source_block {
|
||||||
|
font-family: var(--src-font-family);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-variant-ligatures: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code,
|
||||||
|
.verbatim {
|
||||||
|
font-family: var(--src-font-family);
|
||||||
|
font-variant-ligatures: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote_block {
|
||||||
|
border-left: 1px solid var(--quote-block-border-color);
|
||||||
|
padding: 0 0 0 1rem;
|
||||||
|
margin: 1rem 0 1rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 2.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plain_list {
|
||||||
|
&.unordered {
|
||||||
|
list-style-type: disc;
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ordered {
|
||||||
|
list-style-type: decimal;
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.descriptive {
|
||||||
|
font-size: 1rem;
|
||||||
|
> dt {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
> dd {
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnote_reference {
|
||||||
|
vertical-align: super;
|
||||||
|
font-size: 80%;
|
||||||
|
> a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footnote_definition {
|
||||||
|
.label {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.definition {
|
||||||
|
display: inline;
|
||||||
|
|
||||||
|
> p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.org_table {
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid var(--table-border-color);
|
||||||
|
> tbody {
|
||||||
|
border-width: 1px 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--table-border-color);
|
||||||
|
> tr {
|
||||||
|
> td {
|
||||||
|
padding: 0.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> tr:nth-child(odd) {
|
||||||
|
background-color: var(--table-odd-background-color);
|
||||||
|
}
|
||||||
|
> tr:nth-child(even) {
|
||||||
|
background-color: var(--table-even-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> thead {
|
||||||
|
border-width: 1px 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--table-border-color);
|
||||||
|
> tr {
|
||||||
|
> th {
|
||||||
|
padding: 0.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Never have media larger than its container */
|
||||||
|
img,
|
||||||
|
picture,
|
||||||
|
video {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,19 +3,87 @@
|
|||||||
License: none (public domain)
|
License: none (public domain)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
html, body, div, span, applet, object, iframe,
|
html,
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
body,
|
||||||
a, abbr, acronym, address, big, cite, code,
|
div,
|
||||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
span,
|
||||||
small, strike, strong, sub, sup, tt, var,
|
applet,
|
||||||
b, u, i, center,
|
object,
|
||||||
dl, dt, dd, ol, ul, li,
|
iframe,
|
||||||
fieldset, form, label, legend,
|
h1,
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
h2,
|
||||||
article, aside, canvas, details, embed,
|
h3,
|
||||||
figure, figcaption, footer, header, hgroup,
|
h4,
|
||||||
menu, nav, output, ruby, section, summary,
|
h5,
|
||||||
time, mark, audio, video {
|
h6,
|
||||||
|
p,
|
||||||
|
blockquote,
|
||||||
|
pre,
|
||||||
|
a,
|
||||||
|
abbr,
|
||||||
|
acronym,
|
||||||
|
address,
|
||||||
|
big,
|
||||||
|
cite,
|
||||||
|
code,
|
||||||
|
del,
|
||||||
|
dfn,
|
||||||
|
em,
|
||||||
|
img,
|
||||||
|
ins,
|
||||||
|
kbd,
|
||||||
|
q,
|
||||||
|
s,
|
||||||
|
samp,
|
||||||
|
small,
|
||||||
|
strike,
|
||||||
|
strong,
|
||||||
|
sub,
|
||||||
|
sup,
|
||||||
|
tt,
|
||||||
|
var,
|
||||||
|
b,
|
||||||
|
u,
|
||||||
|
i,
|
||||||
|
center,
|
||||||
|
dl,
|
||||||
|
dt,
|
||||||
|
dd,
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
li,
|
||||||
|
fieldset,
|
||||||
|
form,
|
||||||
|
label,
|
||||||
|
legend,
|
||||||
|
table,
|
||||||
|
caption,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
thead,
|
||||||
|
tr,
|
||||||
|
th,
|
||||||
|
td,
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
canvas,
|
||||||
|
details,
|
||||||
|
embed,
|
||||||
|
figure,
|
||||||
|
figcaption,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
output,
|
||||||
|
ruby,
|
||||||
|
section,
|
||||||
|
summary,
|
||||||
|
time,
|
||||||
|
mark,
|
||||||
|
audio,
|
||||||
|
video {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
@@ -24,22 +92,35 @@ time, mark, audio, video {
|
|||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
/* HTML5 display-role reset for older browsers */
|
/* HTML5 display-role reset for older browsers */
|
||||||
article, aside, details, figcaption, figure,
|
article,
|
||||||
footer, header, hgroup, menu, nav, section {
|
aside,
|
||||||
|
details,
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
section {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
ol, ul {
|
ol,
|
||||||
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
blockquote, q {
|
blockquote,
|
||||||
|
q {
|
||||||
quotes: none;
|
quotes: none;
|
||||||
}
|
}
|
||||||
blockquote:before, blockquote:after,
|
blockquote:before,
|
||||||
q:before, q:after {
|
blockquote:after,
|
||||||
content: '';
|
q:before,
|
||||||
|
q:after {
|
||||||
|
content: "";
|
||||||
content: none;
|
content: none;
|
||||||
}
|
}
|
||||||
table {
|
table {
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
{@eq value="code"}{>code/}{/eq}
|
{@eq value="code"}{>code/}{/eq}
|
||||||
{@eq value="verbatim"}{>verbatim/}{/eq}
|
{@eq value="verbatim"}{>verbatim/}{/eq}
|
||||||
{@eq value="plain_text"}{>plain_text/}{/eq}
|
{@eq value="plain_text"}{>plain_text/}{/eq}
|
||||||
{@eq value="regular_link"}{>regular_link/}{/eq}
|
{@eq value="regular_link_anchor"}{>regular_link_anchor/}{/eq}
|
||||||
|
{@eq value="regular_link_image"}{>regular_link_image/}{/eq}
|
||||||
{@eq value="radio_link"}{>radio_link/}{/eq}
|
{@eq value="radio_link"}{>radio_link/}{/eq}
|
||||||
{@eq value="radio_target"}{>radio_target/}{/eq}
|
{@eq value="radio_target"}{>radio_target/}{/eq}
|
||||||
{@eq value="plain_link"}{>plain_link/}{/eq}
|
{@eq value="plain_link"}{>plain_link/}{/eq}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<div class="blog_post">
|
<article class="blog_post">
|
||||||
<div class="blog_post_intro">
|
{?.title}<h1 class="blog_post_title"><span>{.title}</span></h1>{/.title}
|
||||||
{?.title}{?.self_link}<a class="blog_post_title" href="{.self_link}">{.title}</a>{:else}<div class="blog_post_title">{.title}</div>{/.self_link}{/.title}
|
|
||||||
{! TODO: date? !}
|
{! TODO: date? !}
|
||||||
</div>
|
|
||||||
|
|
||||||
{! TODO: Table of contents? !}
|
{! TODO: Table of contents? !}
|
||||||
|
|
||||||
@@ -18,4 +16,4 @@
|
|||||||
{/.footnotes}
|
{/.footnotes}
|
||||||
{/.footnotes}
|
{/.footnotes}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<div class="blog_stream">
|
<div class="blog_stream">
|
||||||
{#.children}
|
{#.children}
|
||||||
|
{@gt key=$idx value=0}<hr class="stream_divider" />{/gt}
|
||||||
<div class="blog_stream_post">
|
<div class="blog_stream_post">
|
||||||
<div class="blog_post_intro">
|
<div class="blog_post_intro">
|
||||||
{?.title}{?.self_link}<a class="blog_post_title" href="{.self_link}">{.title}</a>{:else}<div class="blog_post_title">{.title}</div>{/.self_link}{/.title}
|
{?.title}{?.self_link}<a class="blog_post_title" href="{.self_link}">{.title}</a>{:else}<div class="blog_post_title">{.title}</div>{/.self_link}{/.title}
|
||||||
@@ -23,8 +24,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{/.children}
|
{/.children}
|
||||||
{#.stream_pagination}
|
{#.stream_pagination}
|
||||||
|
<hr class="stream_divider" />
|
||||||
<div class="stream_nav">
|
<div class="stream_nav">
|
||||||
{?.older_link}<a href="{.older_link}">Older</a>{/.older_link}
|
{?.older_link}<a href="{.older_link}">Older</a>{/.older_link}
|
||||||
|
<div class="spacer"></div>
|
||||||
{?.newer_link}<a href="{.newer_link}">Newer</a>{/.newer_link}
|
{?.newer_link}<a href="{.newer_link}">Newer</a>{/.newer_link}
|
||||||
</div>
|
</div>
|
||||||
{/.stream_pagination}
|
{/.stream_pagination}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<code>{.contents}</code>
|
<code class="code">{.contents}</code>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{@select key=.type}
|
{@select key=.type}
|
||||||
{@eq value="paragraph"}{>paragraph/}{/eq}
|
{@eq value="paragraph"}{>paragraph/}{/eq}
|
||||||
{@eq value="plain_list"}{>plain_list/}{/eq}
|
{@eq value="plain_list"}{>plain_list/}{/eq}
|
||||||
|
{@eq value="plain_list_simple_item"}{>plain_list_simple_item/}{/eq}
|
||||||
{@eq value="center_block"}{>center_block/}{/eq}
|
{@eq value="center_block"}{>center_block/}{/eq}
|
||||||
{@eq value="quote_block"}{>quote_block/}{/eq}
|
{@eq value="quote_block"}{>quote_block/}{/eq}
|
||||||
{@eq value="special_block"}{>special_block/}{/eq}
|
{@eq value="special_block"}{>special_block/}{/eq}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<sup><a id="{.reference_id}" href="{.definition_link}">{.label}</a></sup>
|
<sup class="footnote_reference"><a id="{.reference_id}" href="{.definition_link}">{.label}</a></sup>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<code>{.value}</code>
|
<code class="inline_source_block">{.value}</code>
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
{#global_settings.css_files}<link rel="stylesheet" href="{.}">{/global_settings.css_files}
|
{#global_settings.css_files}<link rel="stylesheet" href="{.}">{/global_settings.css_files}
|
||||||
{#global_settings.js_files}<script type="text/javascript" src="{.}"></script>{/global_settings.js_files}
|
{#global_settings.js_files}<script type="text/javascript" src="{.}"></script>{/global_settings.js_files}
|
||||||
{?global_settings.page_title}<title>{global_settings.page_title}</title>{/global_settings.page_title}
|
{?global_settings.page_title}<title>{global_settings.page_title}</title>{/global_settings.page_title}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="page_centering">
|
||||||
{#.page_header}{>page_header/}{/.page_header}
|
{#.page_header}{>page_header/}{/.page_header}
|
||||||
<div class="main_content">
|
<main class="main_content">
|
||||||
{@select key=.type}
|
{@select key=.type}
|
||||||
|
{@eq value="page"}{>page/}{/eq}
|
||||||
{@eq value="blog_post_page"}{>blog_post_page/}{/eq}
|
{@eq value="blog_post_page"}{>blog_post_page/}{/eq}
|
||||||
{@eq value="blog_stream"}{>blog_stream/}{/eq}
|
{@eq value="blog_stream"}{>blog_stream/}{/eq}
|
||||||
{@none}{!TODO: make this panic!}ERROR: Unrecognized page content type{/none}
|
{@none}{!TODO: make this panic!}ERROR: Unrecognized page content type{/none}
|
||||||
{/select}
|
{/select}
|
||||||
</div>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
{@eq value="code"}{>code/}{/eq}
|
{@eq value="code"}{>code/}{/eq}
|
||||||
{@eq value="verbatim"}{>verbatim/}{/eq}
|
{@eq value="verbatim"}{>verbatim/}{/eq}
|
||||||
{@eq value="plain_text"}{>plain_text/}{/eq}
|
{@eq value="plain_text"}{>plain_text/}{/eq}
|
||||||
{@eq value="regular_link"}{>regular_link/}{/eq}
|
{@eq value="regular_link_anchor"}{>regular_link_anchor/}{/eq}
|
||||||
|
{@eq value="regular_link_image"}{>regular_link_image/}{/eq}
|
||||||
{@eq value="radio_link"}{>radio_link/}{/eq}
|
{@eq value="radio_link"}{>radio_link/}{/eq}
|
||||||
{@eq value="radio_target"}{>radio_target/}{/eq}
|
{@eq value="radio_target"}{>radio_target/}{/eq}
|
||||||
{@eq value="plain_link"}{>plain_link/}{/eq}
|
{@eq value="plain_link"}{>plain_link/}{/eq}
|
||||||
@@ -27,5 +28,4 @@
|
|||||||
{@eq value="superscript"}{>superscript/}{/eq}
|
{@eq value="superscript"}{>superscript/}{/eq}
|
||||||
{@eq value="timestamp"}{>timestamp/}{/eq}
|
{@eq value="timestamp"}{>timestamp/}{/eq}
|
||||||
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
|
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
|
||||||
{/select}{~s}
|
{/select}{@gt key=.post_blank value=0}{~s}{/gt}
|
||||||
{! TODO: Maybe the final space should be conditional on end blank in the org source !}
|
|
||||||
|
|||||||
19
default_environment/templates/html/page.dust
Normal file
19
default_environment/templates/html/page.dust
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<article class="page">
|
||||||
|
{?.title}<h1 class="blog_post_title"><span>{.title}</span></h1>{/.title}
|
||||||
|
{! TODO: date? !}
|
||||||
|
|
||||||
|
{! TODO: Table of contents? !}
|
||||||
|
|
||||||
|
<div class="blog_post_body">
|
||||||
|
{#.children}
|
||||||
|
{>document_element/}
|
||||||
|
{/.children}
|
||||||
|
|
||||||
|
{?.footnotes}
|
||||||
|
<h2>Footnotes:</h2>
|
||||||
|
{#.footnotes}
|
||||||
|
{>real_footnote_definition/}
|
||||||
|
{/.footnotes}
|
||||||
|
{/.footnotes}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
<div class="page_header">
|
<header class="page_header">
|
||||||
<a href="{.home_link}">{.website_title}</a>
|
<a class="home_link" href="{.home_link}">{.website_title}</a>
|
||||||
{! TODO: Additional links? !}
|
{! TODO: Additional links? Probably using the nav semantic element. !}
|
||||||
</div>
|
<nav class="header_nav_bar">
|
||||||
|
{#.nav_links}
|
||||||
|
<a class="nav_link" href="{.url}"><div>{.text}</div></a>
|
||||||
|
{/.nav_links}
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<p>{#.children}
|
<p class="{?.is_single_image}image{/.is_single_image}">{#.children}
|
||||||
{>object/}
|
{>object/}
|
||||||
{/.children}</p>
|
{/.children}</p>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
!!!!!!!! plain_link
|
<a href="{.raw_link}">{.raw_link}</a>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{@select key=.list_type}
|
{@select key=.list_type}
|
||||||
{@eq value="unordered"}<ul>{#.children}{>plain_list_item/}{/.children}</ul>{/eq}
|
{@eq value="unordered"}<ul class="plain_list unordered">{#.children}{>plain_list_item/}{/.children}</ul>{/eq}
|
||||||
{@eq value="ordered"}<ol>{#.children}{>plain_list_item/}{/.children}</ol>{/eq}
|
{@eq value="ordered"}<ol class="plain_list ordered">{#.children}{>plain_list_item/}{/.children}</ol>{/eq}
|
||||||
{@eq value="descriptive"}<dl>{#.children}{>plain_list_item/}{/.children}</dl>{/eq}
|
{@eq value="descriptive"}<dl class="plain_list descriptive">{#.children}{>plain_list_item/}{/.children}</dl>{/eq}
|
||||||
{@none}{!TODO: make this panic!}ERROR: Unrecognized list type {.list_type}.{/none}
|
{@none}{!TODO: make this panic!}ERROR: Unrecognized list type {.list_type}.{/none}
|
||||||
{/select}
|
{/select}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{#.children}
|
||||||
|
{>object/}
|
||||||
|
{/.children}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
<blockquote>{#.children}
|
<blockquote class="quote_block">{#.children}
|
||||||
{>element/}
|
{>element/}
|
||||||
{/.children}</blockquote>
|
{/.children}</blockquote>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<div><sup><a id="{.definition_id}" href="{.reference_link}">{.label}</a></sup><div>{#.contents}{>ast_node/}{/.contents}</div></div>
|
<div class="footnote_definition"><a id="{.definition_id}" href="{.reference_link}" class="label">{.label}.</a> <div class="definition">{#.contents}{>ast_node/}{/.contents}</div></div>
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<a href="{.raw_link}">{#.children}{>object/}{/.children}</a>
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<a href="{.target}">{#.children}{>object/}{/.children}</a>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<img src="{.src}" alt="{.alt}" />
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
<table>
|
<div class="src_block{?.language} srclg_{.language}{/.language}">
|
||||||
|
{?.language}<div class="src_language">{.language}</div>{/.language}
|
||||||
|
<table class="src_body">
|
||||||
<tbody>
|
<tbody>
|
||||||
{#.lines}
|
{#.lines}
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>{.}</code></td>
|
<td>{#.children}{@select key=.type}
|
||||||
|
{@eq value="raw_text"}<code class="src_line">{.content}</code>{/eq}
|
||||||
|
{@eq value="highlight_start"}<span class="srchl_{.name}">{/eq}
|
||||||
|
{@eq value="highlight_end"}</span>{/eq}
|
||||||
|
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
|
||||||
|
{/select}{/.children}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/.lines}
|
{/.lines}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
<table>{#.children}{>table_row/}{/.children}</table>
|
<table class="org_table">{#.children}{@select key=.type}
|
||||||
|
{@eq value="head"}{>table_head/}{/eq}
|
||||||
|
{@eq value="body"}{>table_body/}{/eq}
|
||||||
|
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
|
||||||
|
{/select}{/.children}</table>
|
||||||
|
|||||||
1
default_environment/templates/html/table_body.dust
Normal file
1
default_environment/templates/html/table_body.dust
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<tbody>{#.children}{>table_row/}{/.children}</tbody>
|
||||||
1
default_environment/templates/html/table_head.dust
Normal file
1
default_environment/templates/html/table_head.dust
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<thead>{#.children}{>table_head_row/}{/.children}</thead>
|
||||||
1
default_environment/templates/html/table_head_cell.dust
Normal file
1
default_environment/templates/html/table_head_cell.dust
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<th scope="col">{#.children}{>object/}{/.children}</th>
|
||||||
1
default_environment/templates/html/table_head_row.dust
Normal file
1
default_environment/templates/html/table_head_row.dust
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<tr>{#.children}{>table_head_cell/}{/.children}</tr>
|
||||||
@@ -1 +1 @@
|
|||||||
!!!!!!!! timestamp
|
<span class="timestamp">{.source}</span>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<code>{.contents}</code>
|
<code class="verbatim">{.contents}</code>
|
||||||
|
|||||||
16
docker/natter/Dockerfile
Normal file
16
docker/natter/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
ARG ALPINE_VERSION="3.20"
|
||||||
|
|
||||||
|
FROM rustlang/rust:nightly-alpine$ALPINE_VERSION AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache musl-dev
|
||||||
|
|
||||||
|
RUN mkdir /root/natter
|
||||||
|
WORKDIR /root/natter
|
||||||
|
COPY --link . .
|
||||||
|
# TODO: Add static build, which currently errors due to proc_macro. RUSTFLAGS="-C target-feature=+crt-static"
|
||||||
|
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 runner
|
||||||
|
|
||||||
|
COPY --link --from=builder /target/release-lto/natter /usr/bin/
|
||||||
32
docker/natter/Makefile
Normal file
32
docker/natter/Makefile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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:=natter
|
||||||
|
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: shell
|
||||||
|
shell: ## Launch an interactive shell inside the docker image.
|
||||||
|
shell: build
|
||||||
|
> docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp $(IMAGE_NAME)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
> docker rmi $(IMAGE_NAME)
|
||||||
14
docker/natter_development/Dockerfile
Normal file
14
docker/natter_development/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
ARG ALPINE_VERSION="3.20"
|
||||||
|
|
||||||
|
FROM rustlang/rust:nightly-alpine$ALPINE_VERSION AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache musl-dev
|
||||||
|
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
||||||
|
RUN rustup component add rustfmt
|
||||||
|
RUN rustup component add clippy
|
||||||
|
|
||||||
|
FROM builder AS javascript
|
||||||
|
|
||||||
|
RUN apk add --no-cache npm
|
||||||
|
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/npmcache,sharing=locked npm set cache /npmcache && npm install --global prettier@3.1.0
|
||||||
33
docker/natter_development/Makefile
Normal file
33
docker/natter_development/Makefile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
SHELL := bash
|
||||||
|
.ONESHELL:
|
||||||
|
.SHELLFLAGS := -eu -o pipefail -c
|
||||||
|
.DELETE_ON_ERROR:
|
||||||
|
MAKEFLAGS += --warn-undefined-variables
|
||||||
|
MAKEFLAGS += --no-builtin-rules
|
||||||
|
|
||||||
|
ifeq ($(origin .RECIPEPREFIX), undefined)
|
||||||
|
$(error This Make does not support .RECIPEPREFIX. Please use GNU Make 4.0 or later)
|
||||||
|
endif
|
||||||
|
.RECIPEPREFIX = >
|
||||||
|
|
||||||
|
IMAGE_NAME:=natter-development
|
||||||
|
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 .
|
||||||
|
> docker volume create natter-cargo-registry
|
||||||
|
|
||||||
|
.PHONY: shell
|
||||||
|
shell: ## Launch an interactive shell inside the docker image with the source repository mounted at /source.
|
||||||
|
shell: build
|
||||||
|
> docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" $(IMAGE_NAME)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
> docker rmi $(IMAGE_NAME)
|
||||||
|
> docker volume rm natter-cargo-registry
|
||||||
22
org_test_documents/regular_link/image_links.org
Normal file
22
org_test_documents/regular_link/image_links.org
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[[file:image.svg]]
|
||||||
|
|
||||||
|
[[file:/image.svg]]
|
||||||
|
|
||||||
|
[[file:./image.svg]]
|
||||||
|
|
||||||
|
[[/image.svg]]
|
||||||
|
|
||||||
|
[[./image.svg]]
|
||||||
|
|
||||||
|
# Check capitalization of extension
|
||||||
|
[[./image.SVG]]
|
||||||
|
|
||||||
|
# Check spaces in path
|
||||||
|
[[./image and stuff.SVG]]
|
||||||
|
|
||||||
|
[[/ssh:admin@test.example:important/file.svg]]
|
||||||
|
|
||||||
|
[[file:/ssh:admin@test.example:important/file.svg]]
|
||||||
|
|
||||||
|
# Check multiple parts in the path
|
||||||
|
[[file:/foo/bar/baz/image.svg]]
|
||||||
4
rust-toolchain.toml
Normal file
4
rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
|
profile = "default"
|
||||||
|
components = ["clippy", "rustfmt"]
|
||||||
@@ -4,7 +4,7 @@ use clap::Subcommand;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "Writer")]
|
#[command(name = "Natter")]
|
||||||
#[command(version = env!("CARGO_PKG_VERSION"))]
|
#[command(version = env!("CARGO_PKG_VERSION"))]
|
||||||
#[command(about = "Generate a static site.", long_about = None)]
|
#[command(about = "Generate a static site.", long_about = None)]
|
||||||
#[command(propagate_version = true)]
|
#[command(propagate_version = true)]
|
||||||
@@ -24,14 +24,14 @@ pub(crate) enum Commands {
|
|||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
pub(crate) struct InitArgs {
|
pub(crate) struct InitArgs {
|
||||||
/// Path where you want the initial writer structure to be located.
|
/// Path where you want the initial natter structure to be located.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub(crate) path: PathBuf,
|
pub(crate) path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
pub(crate) struct BuildArgs {
|
pub(crate) struct BuildArgs {
|
||||||
/// Path to the writer config file.
|
/// Path to the natter config file.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub(crate) config: PathBuf,
|
pub(crate) config: PathBuf,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,29 @@
|
|||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use include_dir::include_dir;
|
|
||||||
use include_dir::Dir;
|
use include_dir::Dir;
|
||||||
|
use include_dir::include_dir;
|
||||||
|
use tokio::fs::DirEntry;
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::context::DependencyManager;
|
||||||
use crate::context::RenderBlogPostPage;
|
use crate::context::RenderBlogPostPage;
|
||||||
use crate::context::RenderBlogPostPageInput;
|
use crate::context::RenderBlogPostPageInput;
|
||||||
use crate::context::RenderBlogStream;
|
use crate::context::RenderBlogStream;
|
||||||
use crate::context::RenderBlogStreamInput;
|
use crate::context::RenderBlogStreamInput;
|
||||||
use crate::context::RenderContext;
|
use crate::context::RenderContext;
|
||||||
|
use crate::context::RenderPage;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::get_web_path;
|
|
||||||
use crate::intermediate::BlogPost;
|
use crate::intermediate::BlogPost;
|
||||||
|
use crate::intermediate::IPage;
|
||||||
|
use crate::intermediate::PublishStatus;
|
||||||
|
use crate::intermediate::get_web_path;
|
||||||
use crate::render::DusterRenderer;
|
use crate::render::DusterRenderer;
|
||||||
use crate::render::RendererIntegration;
|
use crate::render::RendererIntegration;
|
||||||
|
use crate::walk_fs::WalkAction;
|
||||||
|
use crate::walk_fs::WalkFsFilterResult;
|
||||||
|
use crate::walk_fs::walk_fs;
|
||||||
|
|
||||||
use super::stylesheet::Stylesheet;
|
use super::stylesheet::Stylesheet;
|
||||||
|
|
||||||
@@ -24,6 +33,7 @@ pub(crate) struct SiteRenderer {
|
|||||||
output_directory: PathBuf,
|
output_directory: PathBuf,
|
||||||
blog_posts: Vec<BlogPost>,
|
blog_posts: Vec<BlogPost>,
|
||||||
stylesheets: Vec<Stylesheet>,
|
stylesheets: Vec<Stylesheet>,
|
||||||
|
pages: Vec<IPage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SiteRenderer {
|
impl SiteRenderer {
|
||||||
@@ -31,11 +41,13 @@ impl SiteRenderer {
|
|||||||
output_directory: P,
|
output_directory: P,
|
||||||
blog_posts: Vec<BlogPost>,
|
blog_posts: Vec<BlogPost>,
|
||||||
stylesheets: Vec<Stylesheet>,
|
stylesheets: Vec<Stylesheet>,
|
||||||
|
pages: Vec<IPage>,
|
||||||
) -> SiteRenderer {
|
) -> SiteRenderer {
|
||||||
SiteRenderer {
|
SiteRenderer {
|
||||||
output_directory: output_directory.into(),
|
output_directory: output_directory.into(),
|
||||||
blog_posts,
|
blog_posts,
|
||||||
stylesheets,
|
stylesheets,
|
||||||
|
pages,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,30 +82,83 @@ impl SiteRenderer {
|
|||||||
Ok(renderer_integration)
|
Ok(renderer_integration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn render_pages(&self, config: &Config) -> Result<(), CustomError> {
|
||||||
|
let renderer_integration = self.init_renderer_integration()?;
|
||||||
|
|
||||||
|
for page in self.pages.iter().filter(|page| match page.natter_publish {
|
||||||
|
PublishStatus::Full => true,
|
||||||
|
PublishStatus::Unlisted => true,
|
||||||
|
PublishStatus::Unpublished => false,
|
||||||
|
}) {
|
||||||
|
let output_path = self.output_directory.join(page.get_output_path());
|
||||||
|
let dependency_manager =
|
||||||
|
std::sync::Arc::new(std::sync::Mutex::new(DependencyManager::new()));
|
||||||
|
let render_context = RenderContext::new(
|
||||||
|
config,
|
||||||
|
self.output_directory.as_path(),
|
||||||
|
output_path.as_path(),
|
||||||
|
None,
|
||||||
|
dependency_manager.clone(),
|
||||||
|
)?;
|
||||||
|
let dust_context = RenderPage::new(render_context.clone(), page)?;
|
||||||
|
let rendered_output = renderer_integration.render(dust_context)?;
|
||||||
|
let parent_directory = output_path
|
||||||
|
.parent()
|
||||||
|
.ok_or("Output file should have a containing directory.")?;
|
||||||
|
tokio::fs::create_dir_all(parent_directory).await?;
|
||||||
|
tokio::fs::write(&output_path, rendered_output).await?;
|
||||||
|
|
||||||
|
let dependencies = dependency_manager.lock().unwrap().take_dependencies();
|
||||||
|
for dependency in dependencies {
|
||||||
|
dependency.perform(render_context.clone()).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> {
|
pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> {
|
||||||
let renderer_integration = self.init_renderer_integration()?;
|
let renderer_integration = self.init_renderer_integration()?;
|
||||||
|
|
||||||
for blog_post in &self.blog_posts {
|
for blog_post in self.blog_posts.iter().filter(|blog_post| {
|
||||||
|
match blog_post
|
||||||
|
.get_index_page()
|
||||||
|
.expect("Blog posts should have an index page.")
|
||||||
|
.natter_publish
|
||||||
|
{
|
||||||
|
PublishStatus::Full => true,
|
||||||
|
PublishStatus::Unlisted => true,
|
||||||
|
PublishStatus::Unpublished => false,
|
||||||
|
}
|
||||||
|
}) {
|
||||||
for blog_post_page in &blog_post.pages {
|
for blog_post_page in &blog_post.pages {
|
||||||
let output_path = self
|
let output_path = self
|
||||||
.output_directory
|
.output_directory
|
||||||
.join(config.get_relative_path_to_post(&blog_post.id))
|
.join(config.get_relative_path_to_post(&blog_post.id))
|
||||||
.join(blog_post_page.get_output_path());
|
.join(blog_post_page.get_output_path());
|
||||||
|
|
||||||
|
let dependency_manager =
|
||||||
|
std::sync::Arc::new(std::sync::Mutex::new(DependencyManager::new()));
|
||||||
let convert_input = RenderBlogPostPageInput::new(blog_post, blog_post_page);
|
let convert_input = RenderBlogPostPageInput::new(blog_post, blog_post_page);
|
||||||
let render_context = RenderContext::new(
|
let render_context = RenderContext::new(
|
||||||
config,
|
config,
|
||||||
self.output_directory.as_path(),
|
self.output_directory.as_path(),
|
||||||
output_path.as_path(),
|
output_path.as_path(),
|
||||||
None,
|
None,
|
||||||
|
dependency_manager.clone(),
|
||||||
)?;
|
)?;
|
||||||
let render_context = RenderBlogPostPage::new(render_context, &convert_input)?;
|
let dust_context = RenderBlogPostPage::new(render_context.clone(), &convert_input)?;
|
||||||
let rendered_output = renderer_integration.render(render_context)?;
|
let rendered_output = renderer_integration.render(dust_context)?;
|
||||||
let parent_directory = output_path
|
let parent_directory = output_path
|
||||||
.parent()
|
.parent()
|
||||||
.ok_or("Output file should have a containing directory.")?;
|
.ok_or("Output file should have a containing directory.")?;
|
||||||
tokio::fs::create_dir_all(parent_directory).await?;
|
tokio::fs::create_dir_all(parent_directory).await?;
|
||||||
tokio::fs::write(output_path, rendered_output).await?;
|
tokio::fs::write(&output_path, rendered_output).await?;
|
||||||
|
|
||||||
|
let dependencies = dependency_manager.lock().unwrap().take_dependencies();
|
||||||
|
for dependency in dependencies {
|
||||||
|
dependency.perform(render_context.clone()).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +170,21 @@ impl SiteRenderer {
|
|||||||
|
|
||||||
// Sort blog posts by date, newest first.
|
// Sort blog posts by date, newest first.
|
||||||
let sorted_blog_posts = {
|
let sorted_blog_posts = {
|
||||||
let mut sorted_blog_posts: Vec<_> = self.blog_posts.iter().collect();
|
let mut sorted_blog_posts: Vec<_> = self
|
||||||
|
.blog_posts
|
||||||
|
.iter()
|
||||||
|
.filter(|blog_post| {
|
||||||
|
match blog_post
|
||||||
|
.get_index_page()
|
||||||
|
.expect("Blog posts should have an index page.")
|
||||||
|
.natter_publish
|
||||||
|
{
|
||||||
|
PublishStatus::Full => true,
|
||||||
|
PublishStatus::Unlisted => false,
|
||||||
|
PublishStatus::Unpublished => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
sorted_blog_posts
|
sorted_blog_posts
|
||||||
.sort_by_key(|blog_post| (blog_post.get_date(), blog_post.id.as_str()));
|
.sort_by_key(|blog_post| (blog_post.get_date(), blog_post.id.as_str()));
|
||||||
sorted_blog_posts.reverse();
|
sorted_blog_posts.reverse();
|
||||||
@@ -161,14 +240,17 @@ impl SiteRenderer {
|
|||||||
)?)
|
)?)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let dependency_manager =
|
||||||
|
std::sync::Arc::new(std::sync::Mutex::new(DependencyManager::new()));
|
||||||
let convert_input = RenderBlogStreamInput::new(chunk, older_link, newer_link);
|
let convert_input = RenderBlogStreamInput::new(chunk, older_link, newer_link);
|
||||||
let render_context = RenderContext::new(
|
let render_context = RenderContext::new(
|
||||||
config,
|
config,
|
||||||
self.output_directory.as_path(),
|
self.output_directory.as_path(),
|
||||||
output_file.as_path(),
|
output_file.as_path(),
|
||||||
None,
|
None,
|
||||||
|
dependency_manager.clone(),
|
||||||
)?;
|
)?;
|
||||||
let blog_stream = RenderBlogStream::new(render_context, &convert_input)?;
|
let blog_stream = RenderBlogStream::new(render_context.clone(), &convert_input)?;
|
||||||
|
|
||||||
// Pass each RenderBlogStream to dust as the context to render index.html and any additional stream pages.
|
// Pass each RenderBlogStream to dust as the context to render index.html and any additional stream pages.
|
||||||
let rendered_output = renderer_integration.render(blog_stream)?;
|
let rendered_output = renderer_integration.render(blog_stream)?;
|
||||||
@@ -176,7 +258,12 @@ impl SiteRenderer {
|
|||||||
.parent()
|
.parent()
|
||||||
.ok_or("Output file should have a containing directory.")?;
|
.ok_or("Output file should have a containing directory.")?;
|
||||||
tokio::fs::create_dir_all(parent_directory).await?;
|
tokio::fs::create_dir_all(parent_directory).await?;
|
||||||
tokio::fs::write(output_file, rendered_output).await?;
|
tokio::fs::write(&output_file, rendered_output).await?;
|
||||||
|
|
||||||
|
let dependencies = dependency_manager.lock().unwrap().take_dependencies();
|
||||||
|
for dependency in dependencies {
|
||||||
|
dependency.perform(render_context.clone()).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -196,6 +283,27 @@ impl SiteRenderer {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn copy_static_files(&self, config: &Config) -> Result<(), CustomError> {
|
||||||
|
let static_files_directory = config
|
||||||
|
.get_root_directory()
|
||||||
|
.join(config.get_relative_path_to_static_files());
|
||||||
|
if !static_files_directory.exists() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let static_files = get_all_files(&static_files_directory).await?;
|
||||||
|
for entry in static_files {
|
||||||
|
let (path, contents) = entry.await??;
|
||||||
|
let relative_path = path.strip_prefix(&static_files_directory)?;
|
||||||
|
let output_path = self.output_directory.join(relative_path);
|
||||||
|
let parent_directory = output_path
|
||||||
|
.parent()
|
||||||
|
.ok_or("Output file should have a containing directory.")?;
|
||||||
|
tokio::fs::create_dir_all(parent_directory).await?;
|
||||||
|
tokio::fs::write(output_path, contents).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_name_contents_pairs<'a>(
|
fn build_name_contents_pairs<'a>(
|
||||||
@@ -210,3 +318,32 @@ fn build_name_contents_pairs<'a>(
|
|||||||
let contents = std::str::from_utf8(entry.contents())?;
|
let contents = std::str::from_utf8(entry.contents())?;
|
||||||
Ok((name, contents))
|
Ok((name, contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReadFileResult = std::io::Result<(PathBuf, Vec<u8>)>;
|
||||||
|
|
||||||
|
async fn filter_to_files(entry: &DirEntry) -> WalkFsFilterResult {
|
||||||
|
let file_type = entry.file_type().await?;
|
||||||
|
if file_type.is_dir() {
|
||||||
|
return Ok(WalkAction::Recurse);
|
||||||
|
}
|
||||||
|
if file_type.is_file() {
|
||||||
|
return Ok(WalkAction::HaltAndCapture);
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!("Unhandled file type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_all_files<P: Into<PathBuf>>(
|
||||||
|
root_dir: P,
|
||||||
|
) -> Result<impl Iterator<Item = JoinHandle<ReadFileResult>>, CustomError> {
|
||||||
|
let files = walk_fs(root_dir, filter_to_files).await?;
|
||||||
|
let files_and_content = files
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| tokio::spawn(read_file(entry.path())));
|
||||||
|
Ok(files_and_content)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_file(path: PathBuf) -> ReadFileResult {
|
||||||
|
let contents = tokio::fs::read(&path).await?;
|
||||||
|
Ok((path, contents))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use super::stylesheet::Stylesheet;
|
use super::stylesheet::Stylesheet;
|
||||||
use crate::cli::parameters::BuildArgs;
|
use crate::cli::parameters::BuildArgs;
|
||||||
@@ -7,8 +9,17 @@ use crate::command::build::render::SiteRenderer;
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::BlogPost;
|
use crate::intermediate::BlogPost;
|
||||||
use include_dir::include_dir;
|
use crate::intermediate::IPage;
|
||||||
|
use crate::intermediate::IntermediateContext;
|
||||||
|
use crate::intermediate::PageInput;
|
||||||
|
use crate::intermediate::Registry;
|
||||||
|
use crate::intermediate::get_org_files;
|
||||||
|
use crate::walk_fs::WalkAction;
|
||||||
|
use crate::walk_fs::WalkFsFilterResult;
|
||||||
|
use crate::walk_fs::walk_fs;
|
||||||
use include_dir::Dir;
|
use include_dir::Dir;
|
||||||
|
use include_dir::include_dir;
|
||||||
|
use tokio::fs::DirEntry;
|
||||||
|
|
||||||
static DEFAULT_STYLESHEETS: Dir =
|
static DEFAULT_STYLESHEETS: Dir =
|
||||||
include_dir!("$CARGO_MANIFEST_DIR/default_environment/stylesheet");
|
include_dir!("$CARGO_MANIFEST_DIR/default_environment/stylesheet");
|
||||||
@@ -17,14 +28,18 @@ pub(crate) async fn build_site(args: BuildArgs) -> Result<(), CustomError> {
|
|||||||
let config = Config::load_from_file(args.config).await?;
|
let config = Config::load_from_file(args.config).await?;
|
||||||
let blog_posts = load_blog_posts(&config).await?;
|
let blog_posts = load_blog_posts(&config).await?;
|
||||||
let stylesheets = load_stylesheets().await?;
|
let stylesheets = load_stylesheets().await?;
|
||||||
|
let pages = load_pages(&config).await?;
|
||||||
let renderer = SiteRenderer::new(
|
let renderer = SiteRenderer::new(
|
||||||
get_output_directory(&config).await?,
|
get_output_directory(&config).await?,
|
||||||
blog_posts,
|
blog_posts,
|
||||||
stylesheets,
|
stylesheets,
|
||||||
|
pages,
|
||||||
);
|
);
|
||||||
renderer.render_blog_posts(&config).await?;
|
renderer.render_blog_posts(&config).await?;
|
||||||
renderer.render_blog_stream(&config).await?;
|
renderer.render_blog_stream(&config).await?;
|
||||||
|
renderer.render_pages(&config).await?;
|
||||||
renderer.render_stylesheets().await?;
|
renderer.render_stylesheets().await?;
|
||||||
|
renderer.copy_static_files(&config).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -48,24 +63,54 @@ async fn get_output_directory(config: &Config) -> Result<PathBuf, CustomError> {
|
|||||||
Ok(output_directory)
|
Ok(output_directory)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_post_directories(config: &Config) -> Result<Vec<PathBuf>, CustomError> {
|
async fn filter_to_highest_folders_containing_org_files(entry: &DirEntry) -> WalkFsFilterResult {
|
||||||
let mut ret = Vec::new();
|
|
||||||
let mut entries = tokio::fs::read_dir(config.get_posts_directory()).await?;
|
|
||||||
while let Some(entry) = entries.next_entry().await? {
|
|
||||||
let file_type = entry.file_type().await?;
|
let file_type = entry.file_type().await?;
|
||||||
if file_type.is_dir() {
|
if !file_type.is_dir() {
|
||||||
ret.push(entry.path());
|
return Ok(WalkAction::Halt);
|
||||||
|
}
|
||||||
|
let mut entries = tokio::fs::read_dir(entry.path()).await?;
|
||||||
|
while let Some(entry) = entries.next_entry().await? {
|
||||||
|
let entry_type = entry.file_type().await?;
|
||||||
|
if !entry_type.is_file() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match entry.path().extension().and_then(OsStr::to_str) {
|
||||||
|
Some(ext) if ext.eq_ignore_ascii_case("org") => {
|
||||||
|
return Ok(WalkAction::HaltAndCapture);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(ret)
|
Ok(WalkAction::Recurse)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_post_directories(config: &Config) -> Result<Vec<PathBuf>, CustomError> {
|
||||||
|
if !config.get_posts_directory().exists() {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let top_level_org_folders = walk_fs(
|
||||||
|
config.get_posts_directory(),
|
||||||
|
filter_to_highest_folders_containing_org_files,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(top_level_org_folders
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| entry.path())
|
||||||
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_blog_posts(config: &Config) -> Result<Vec<BlogPost>, CustomError> {
|
async fn load_blog_posts(config: &Config) -> Result<Vec<BlogPost>, CustomError> {
|
||||||
let root_directory = config.get_root_directory().to_owned();
|
let root_directory = config.get_root_directory().to_owned();
|
||||||
let post_directories = get_post_directories(&config).await?;
|
let posts_directory = config.get_posts_directory();
|
||||||
let load_jobs = post_directories
|
let post_directories = get_post_directories(config).await?;
|
||||||
.into_iter()
|
let load_jobs = post_directories.into_iter().map(|path| {
|
||||||
.map(|path| tokio::spawn(BlogPost::load_blog_post(root_directory.clone(), path)));
|
tokio::spawn(BlogPost::load_blog_post(
|
||||||
|
root_directory.clone(),
|
||||||
|
posts_directory.clone(),
|
||||||
|
path,
|
||||||
|
))
|
||||||
|
});
|
||||||
let mut blog_posts = Vec::new();
|
let mut blog_posts = Vec::new();
|
||||||
for job in load_jobs {
|
for job in load_jobs {
|
||||||
blog_posts.push(job.await??);
|
blog_posts.push(job.await??);
|
||||||
@@ -73,6 +118,62 @@ async fn load_blog_posts(config: &Config) -> Result<Vec<BlogPost>, CustomError>
|
|||||||
Ok(blog_posts)
|
Ok(blog_posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn load_pages(config: &Config) -> Result<Vec<IPage>, CustomError> {
|
||||||
|
let pages_source = config
|
||||||
|
.get_root_directory()
|
||||||
|
.join(config.get_relative_path_to_pages());
|
||||||
|
if !pages_source.exists() {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
let page_files = get_org_files(&pages_source).await?;
|
||||||
|
let org_files = {
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
for page in page_files {
|
||||||
|
ret.push(page.await??);
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
let parsed_org_files = {
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
for (path, contents) in org_files.iter() {
|
||||||
|
let parsed = organic::parser::parse_file(contents.as_str(), Some(path))
|
||||||
|
.map_err(|_| CustomError::Static("Failed to parse org-mode document."))?;
|
||||||
|
ret.push((path, contents, parsed));
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
let pages = {
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
for (real_path, _contents, parsed_document) in parsed_org_files.iter() {
|
||||||
|
let mut registry = Registry::new();
|
||||||
|
|
||||||
|
// Assign IDs to the targets
|
||||||
|
organic::types::AstNode::from(parsed_document)
|
||||||
|
.iter_all_ast_nodes()
|
||||||
|
.for_each(|node| {
|
||||||
|
if let organic::types::AstNode::Target(target) = node {
|
||||||
|
registry.get_target(target.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let registry = Arc::new(Mutex::new(registry));
|
||||||
|
let intermediate_context = IntermediateContext::new(registry)?;
|
||||||
|
let relative_to_pages_dir_path = real_path.strip_prefix(&pages_source)?;
|
||||||
|
ret.push(
|
||||||
|
IPage::new(
|
||||||
|
intermediate_context,
|
||||||
|
PageInput::new(relative_to_pages_dir_path, real_path, parsed_document),
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(pages)
|
||||||
|
}
|
||||||
|
|
||||||
async fn load_stylesheets() -> Result<Vec<Stylesheet>, CustomError> {
|
async fn load_stylesheets() -> Result<Vec<Stylesheet>, CustomError> {
|
||||||
let sources: Vec<_> = DEFAULT_STYLESHEETS
|
let sources: Vec<_> = DEFAULT_STYLESHEETS
|
||||||
.files()
|
.files()
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
mod runner;
|
mod runner;
|
||||||
|
|
||||||
pub(crate) use runner::init_writer_folder;
|
pub(crate) use runner::init_natter_folder;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::cli::parameters::InitArgs;
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
|
|
||||||
pub(crate) async fn init_writer_folder(args: InitArgs) -> Result<(), CustomError> {
|
pub(crate) async fn init_natter_folder(args: InitArgs) -> Result<(), CustomError> {
|
||||||
if args.path.exists() && !args.path.is_dir() {
|
if args.path.exists() && !args.path.is_dir() {
|
||||||
return Err("The supplied path exists but is not a directory. Aborting.".into());
|
return Err("The supplied path exists but is not a directory. Aborting.".into());
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ pub(crate) async fn init_writer_folder(args: InitArgs) -> Result<(), CustomError
|
|||||||
|
|
||||||
let mut existing_entries = tokio::fs::read_dir(&args.path).await?;
|
let mut existing_entries = tokio::fs::read_dir(&args.path).await?;
|
||||||
let first_entry = existing_entries.next_entry().await?;
|
let first_entry = existing_entries.next_entry().await?;
|
||||||
if let Some(_) = first_entry {
|
if first_entry.is_some() {
|
||||||
return Err("The directory is not empty. Aborting.".into());
|
return Err("The directory is not empty. Aborting.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub(crate) struct Config {
|
|||||||
impl Config {
|
impl Config {
|
||||||
pub(crate) fn new<P: AsRef<Path>>(root_dir: P) -> Result<Config, CustomError> {
|
pub(crate) fn new<P: AsRef<Path>>(root_dir: P) -> Result<Config, CustomError> {
|
||||||
fn inner(root_dir: &Path) -> Result<Config, CustomError> {
|
fn inner(root_dir: &Path) -> Result<Config, CustomError> {
|
||||||
let file_path = root_dir.join("writer.toml");
|
let file_path = root_dir.join("natter.toml");
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
raw: RawConfig::default(),
|
raw: RawConfig::default(),
|
||||||
config_path: file_path,
|
config_path: file_path,
|
||||||
@@ -47,8 +47,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_root_directory(&self) -> &Path {
|
pub(crate) fn get_root_directory(&self) -> &Path {
|
||||||
&self
|
self.config_path
|
||||||
.config_path
|
|
||||||
.parent()
|
.parent()
|
||||||
.expect("Config file must exist inside a directory.")
|
.expect("Config file must exist inside a directory.")
|
||||||
}
|
}
|
||||||
@@ -86,8 +85,15 @@ impl Config {
|
|||||||
self.raw
|
self.raw
|
||||||
.stream
|
.stream
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|stream| stream.entries_per_page)
|
.and_then(|stream| stream.entries_per_page)
|
||||||
.flatten()
|
|
||||||
.unwrap_or(5)
|
.unwrap_or(5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_relative_path_to_static_files(&self) -> PathBuf {
|
||||||
|
Path::new("static").into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_relative_path_to_pages(&self) -> PathBuf {
|
||||||
|
Path::new("pages").into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
/// This is the struct for the writer.toml config file that ends up in each site's root directory.
|
/// This is the struct for the natter.toml config file that ends up in each site's root directory.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
pub(crate) struct RawConfig {
|
pub(crate) struct RawConfig {
|
||||||
pub(super) site_title: Option<String>,
|
pub(super) site_title: Option<String>,
|
||||||
author: Option<String>,
|
author: Option<String>,
|
||||||
@@ -12,28 +12,7 @@ pub(crate) struct RawConfig {
|
|||||||
pub(super) stream: Option<RawConfigStream>,
|
pub(super) stream: Option<RawConfigStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RawConfig {
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
fn default() -> Self {
|
|
||||||
RawConfig {
|
|
||||||
site_title: None,
|
|
||||||
author: None,
|
|
||||||
email: None,
|
|
||||||
use_relative_paths: None,
|
|
||||||
web_root: None,
|
|
||||||
stream: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub(crate) struct RawConfigStream {
|
pub(crate) struct RawConfigStream {
|
||||||
pub(super) entries_per_page: Option<usize>,
|
pub(super) entries_per_page: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RawConfigStream {
|
|
||||||
fn default() -> Self {
|
|
||||||
RawConfigStream {
|
|
||||||
entries_per_page: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "angle_link")]
|
#[serde(rename = "angle_link")]
|
||||||
pub(crate) struct RenderAngleLink {}
|
pub(crate) struct RenderAngleLink {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderAngleLink, IAngleLink);
|
rnoop!(RenderAngleLink, IAngleLink);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use serde::Serialize;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::IAstNode;
|
use crate::intermediate::IAstNode;
|
||||||
|
|
||||||
|
use super::RenderHeading;
|
||||||
|
use super::RenderSection;
|
||||||
use super::angle_link::RenderAngleLink;
|
use super::angle_link::RenderAngleLink;
|
||||||
use super::babel_call::RenderBabelCall;
|
use super::babel_call::RenderBabelCall;
|
||||||
use super::bold::RenderBold;
|
use super::bold::RenderBold;
|
||||||
@@ -55,8 +57,6 @@ use super::timestamp::RenderTimestamp;
|
|||||||
use super::underline::RenderUnderline;
|
use super::underline::RenderUnderline;
|
||||||
use super::verbatim::RenderVerbatim;
|
use super::verbatim::RenderVerbatim;
|
||||||
use super::verse_block::RenderVerseBlock;
|
use super::verse_block::RenderVerseBlock;
|
||||||
use super::RenderHeading;
|
|
||||||
use super::RenderSection;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
@@ -117,14 +117,14 @@ pub(crate) enum RenderAstNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait IntoRenderAstNode {
|
pub(crate) trait IntoRenderAstNode {
|
||||||
fn into_render_ast_node(
|
fn as_render_ast_node(
|
||||||
&self,
|
&self,
|
||||||
render_context: RenderContext<'_>,
|
render_context: RenderContext<'_>,
|
||||||
) -> Result<RenderAstNode, CustomError>;
|
) -> Result<RenderAstNode, CustomError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoRenderAstNode for IAstNode {
|
impl IntoRenderAstNode for IAstNode {
|
||||||
fn into_render_ast_node(
|
fn as_render_ast_node(
|
||||||
&self,
|
&self,
|
||||||
render_context: RenderContext<'_>,
|
render_context: RenderContext<'_>,
|
||||||
) -> Result<RenderAstNode, CustomError> {
|
) -> Result<RenderAstNode, CustomError> {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "babel_call")]
|
#[serde(rename = "babel_call")]
|
||||||
pub(crate) struct RenderBabelCall {}
|
pub(crate) struct RenderBabelCall {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderBabelCall, IBabelCall);
|
rnoop!(RenderBabelCall, IBabelCall);
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
|
use crate::context::macros::push_file;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::get_web_path;
|
|
||||||
use crate::intermediate::BlogPost;
|
use crate::intermediate::BlogPost;
|
||||||
use crate::intermediate::BlogPostPage;
|
use crate::intermediate::BlogPostPage;
|
||||||
|
use crate::intermediate::get_web_path;
|
||||||
|
|
||||||
use super::footnote_definition::RenderRealFootnoteDefinition;
|
|
||||||
use super::macros::render;
|
|
||||||
use super::GlobalSettings;
|
use super::GlobalSettings;
|
||||||
use super::PageHeader;
|
use super::PageHeader;
|
||||||
use super::RenderDocumentElement;
|
use super::RenderDocumentElement;
|
||||||
|
use super::footnote_definition::RenderRealFootnoteDefinition;
|
||||||
|
use super::macros::render;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct RenderBlogPostPageInput<'a> {
|
pub(crate) struct RenderBlogPostPageInput<'a> {
|
||||||
|
#[allow(dead_code)]
|
||||||
post: &'a BlogPost,
|
post: &'a BlogPost,
|
||||||
page: &'a BlogPostPage,
|
page: &'a BlogPostPage,
|
||||||
}
|
}
|
||||||
@@ -48,27 +52,7 @@ render!(
|
|||||||
original,
|
original,
|
||||||
render_context,
|
render_context,
|
||||||
{
|
{
|
||||||
let css_files = vec![
|
push_file!(render_context, &original.page.src, {
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/reset.css",
|
|
||||||
)?,
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/main.css",
|
|
||||||
)?,
|
|
||||||
];
|
|
||||||
let js_files = vec![get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"blog_post.js",
|
|
||||||
)?];
|
|
||||||
let global_settings = GlobalSettings::new(original.page.title.clone(), css_files, js_files);
|
|
||||||
let page_header = PageHeader::new(
|
let page_header = PageHeader::new(
|
||||||
render_context.config.get_site_title().map(str::to_string),
|
render_context.config.get_site_title().map(str::to_string),
|
||||||
Some(get_web_path(
|
Some(get_web_path(
|
||||||
@@ -77,6 +61,12 @@ render!(
|
|||||||
render_context.output_file,
|
render_context.output_file,
|
||||||
"",
|
"",
|
||||||
)?),
|
)?),
|
||||||
|
Some(get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"about_me",
|
||||||
|
)?),
|
||||||
);
|
);
|
||||||
let link_to_blog_post = get_web_path(
|
let link_to_blog_post = get_web_path(
|
||||||
render_context.config,
|
render_context.config,
|
||||||
@@ -110,6 +100,46 @@ render!(
|
|||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut css_files = vec![
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/reset.css",
|
||||||
|
)?,
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/main.css",
|
||||||
|
)?,
|
||||||
|
];
|
||||||
|
let additional_css_files = render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.list_css()?
|
||||||
|
.map(|css_name| {
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
format!("stylesheet/{}", css_name),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<HashSet<_>, _>>()?;
|
||||||
|
css_files.extend(additional_css_files.into_iter());
|
||||||
|
|
||||||
|
let js_files = vec![get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"blog_post.js",
|
||||||
|
)?];
|
||||||
|
|
||||||
|
let global_settings =
|
||||||
|
GlobalSettings::new(original.page.title.clone(), css_files, js_files);
|
||||||
|
|
||||||
let ret = RenderBlogPostPage {
|
let ret = RenderBlogPostPage {
|
||||||
global_settings,
|
global_settings,
|
||||||
page_header: Some(page_header),
|
page_header: Some(page_header),
|
||||||
@@ -119,5 +149,6 @@ render!(
|
|||||||
footnotes,
|
footnotes,
|
||||||
};
|
};
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::macros::render;
|
use super::macros::render;
|
||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
use crate::context::RenderDocumentElement;
|
use crate::context::RenderDocumentElement;
|
||||||
use crate::context::RenderRealFootnoteDefinition;
|
use crate::context::RenderRealFootnoteDefinition;
|
||||||
|
use crate::context::macros::push_file;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::get_web_path;
|
|
||||||
use crate::intermediate::BlogPost;
|
use crate::intermediate::BlogPost;
|
||||||
|
use crate::intermediate::get_web_path;
|
||||||
|
|
||||||
use super::GlobalSettings;
|
use super::GlobalSettings;
|
||||||
use super::PageHeader;
|
use super::PageHeader;
|
||||||
@@ -48,31 +51,6 @@ render!(
|
|||||||
original,
|
original,
|
||||||
render_context,
|
render_context,
|
||||||
{
|
{
|
||||||
let css_files = vec![
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/reset.css",
|
|
||||||
)?,
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/main.css",
|
|
||||||
)?,
|
|
||||||
];
|
|
||||||
let js_files = vec![get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"blog_post.js",
|
|
||||||
)?];
|
|
||||||
let global_settings = GlobalSettings::new(
|
|
||||||
render_context.config.get_site_title().map(str::to_string),
|
|
||||||
css_files,
|
|
||||||
js_files,
|
|
||||||
);
|
|
||||||
let page_header = PageHeader::new(
|
let page_header = PageHeader::new(
|
||||||
render_context.config.get_site_title().map(str::to_string),
|
render_context.config.get_site_title().map(str::to_string),
|
||||||
Some(get_web_path(
|
Some(get_web_path(
|
||||||
@@ -81,11 +59,17 @@ render!(
|
|||||||
render_context.output_file,
|
render_context.output_file,
|
||||||
"",
|
"",
|
||||||
)?),
|
)?),
|
||||||
|
Some(get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"about_me",
|
||||||
|
)?),
|
||||||
);
|
);
|
||||||
|
|
||||||
let children = original
|
let children = original
|
||||||
.original
|
.original
|
||||||
.into_iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, blog_post)| {
|
.map(|(i, blog_post)| {
|
||||||
RenderBlogStreamEntry::new(
|
RenderBlogStreamEntry::new(
|
||||||
@@ -104,6 +88,49 @@ render!(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut css_files = vec![
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/reset.css",
|
||||||
|
)?,
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/main.css",
|
||||||
|
)?,
|
||||||
|
];
|
||||||
|
let additional_css_files = render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.list_css()?
|
||||||
|
.map(|css_name| {
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
format!("stylesheet/{}", css_name),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<HashSet<_>, _>>()?;
|
||||||
|
css_files.extend(additional_css_files.into_iter());
|
||||||
|
|
||||||
|
let js_files = vec![get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"blog_post.js",
|
||||||
|
)?];
|
||||||
|
|
||||||
|
let global_settings = GlobalSettings::new(
|
||||||
|
render_context.config.get_site_title().map(str::to_string),
|
||||||
|
css_files,
|
||||||
|
js_files,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(RenderBlogStream {
|
Ok(RenderBlogStream {
|
||||||
global_settings,
|
global_settings,
|
||||||
page_header: Some(page_header),
|
page_header: Some(page_header),
|
||||||
@@ -164,6 +191,7 @@ render!(
|
|||||||
.get_index_page()
|
.get_index_page()
|
||||||
.ok_or_else(|| format!("Blog post {} needs an index page.", original.original.id))?;
|
.ok_or_else(|| format!("Blog post {} needs an index page.", original.original.id))?;
|
||||||
|
|
||||||
|
push_file!(render_context, &index_page.src, {
|
||||||
let title = index_page.title.clone();
|
let title = index_page.title.clone();
|
||||||
|
|
||||||
let children = index_page
|
let children = index_page
|
||||||
@@ -191,6 +219,7 @@ render!(
|
|||||||
children,
|
children,
|
||||||
footnotes,
|
footnotes,
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ use serde::Serialize;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::IBold;
|
use crate::intermediate::IBold;
|
||||||
|
|
||||||
|
use super::RenderObject;
|
||||||
use super::macros::render;
|
use super::macros::render;
|
||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
use super::RenderObject;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "bold")]
|
#[serde(rename = "bold")]
|
||||||
pub(crate) struct RenderBold {
|
pub(crate) struct RenderBold {
|
||||||
children: Vec<RenderObject>,
|
children: Vec<RenderObject>,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(RenderBold, IBold, original, render_context, {
|
render!(RenderBold, IBold, original, render_context, {
|
||||||
@@ -23,5 +24,8 @@ render!(RenderBold, IBold, original, render_context, {
|
|||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(RenderBold { children })
|
Ok(RenderBold {
|
||||||
|
children,
|
||||||
|
post_blank: original.post_blank,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "center_block")]
|
#[serde(rename = "center_block")]
|
||||||
pub(crate) struct RenderCenterBlock {}
|
pub(crate) struct RenderCenterBlock {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderCenterBlock, ICenterBlock);
|
rnoop!(RenderCenterBlock, ICenterBlock);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "citation")]
|
#[serde(rename = "citation")]
|
||||||
pub(crate) struct RenderCitation {}
|
pub(crate) struct RenderCitation {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderCitation, ICitation);
|
rnoop!(RenderCitation, ICitation);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "citation_reference")]
|
#[serde(rename = "citation_reference")]
|
||||||
pub(crate) struct RenderCitationReference {}
|
pub(crate) struct RenderCitationReference {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderCitationReference, ICitationReference);
|
rnoop!(RenderCitationReference, ICitationReference);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "clock")]
|
#[serde(rename = "clock")]
|
||||||
pub(crate) struct RenderClock {}
|
pub(crate) struct RenderClock {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderClock, IClock);
|
rnoop!(RenderClock, IClock);
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ use super::macros::render;
|
|||||||
#[serde(rename = "code")]
|
#[serde(rename = "code")]
|
||||||
pub(crate) struct RenderCode {
|
pub(crate) struct RenderCode {
|
||||||
contents: String,
|
contents: String,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(RenderCode, ICode, original, _render_context, {
|
render!(RenderCode, ICode, original, _render_context, {
|
||||||
Ok(RenderCode {
|
Ok(RenderCode {
|
||||||
contents: original.contents.clone(),
|
contents: original.contents.clone(),
|
||||||
|
post_blank: original.post_blank,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "comment")]
|
#[serde(rename = "comment")]
|
||||||
pub(crate) struct RenderComment {}
|
pub(crate) struct RenderComment {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderComment, IComment);
|
rnoop!(RenderComment, IComment);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "comment_block")]
|
#[serde(rename = "comment_block")]
|
||||||
pub(crate) struct RenderCommentBlock {}
|
pub(crate) struct RenderCommentBlock {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderCommentBlock, ICommentBlock);
|
rnoop!(RenderCommentBlock, ICommentBlock);
|
||||||
|
|||||||
51
src/context/dependency.rs
Normal file
51
src/context/dependency.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::error::CustomError;
|
||||||
|
|
||||||
|
use super::RenderContext;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum Dependency {
|
||||||
|
StaticFile { absolute_path: PathBuf },
|
||||||
|
CssFile { name: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dependency {
|
||||||
|
pub(crate) async fn perform(
|
||||||
|
&self,
|
||||||
|
render_context: RenderContext<'_>,
|
||||||
|
) -> Result<(), CustomError> {
|
||||||
|
match self {
|
||||||
|
Dependency::StaticFile { absolute_path } => {
|
||||||
|
let input_root_directory = render_context.config.get_root_directory();
|
||||||
|
let relative_path_to_file = absolute_path.strip_prefix(input_root_directory)?;
|
||||||
|
let path_to_output = render_context
|
||||||
|
.output_root_directory
|
||||||
|
.join(relative_path_to_file);
|
||||||
|
tokio::fs::create_dir_all(
|
||||||
|
path_to_output
|
||||||
|
.parent()
|
||||||
|
.ok_or("Output file should have a containing directory.")?,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if tokio::fs::metadata(&path_to_output).await.is_ok() {
|
||||||
|
// TODO: compare hash and error out if they do not match.
|
||||||
|
println!(
|
||||||
|
"Not copying {} to {} because the output file already exists.",
|
||||||
|
absolute_path.display(),
|
||||||
|
path_to_output.display()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
tokio::fs::copy(absolute_path, path_to_output).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Dependency::CssFile { name: _ } => {
|
||||||
|
// We don't do anything because the CSS files are already copied into the output from natter's default environment.
|
||||||
|
// TODO: When we add support for CSS outside the default environment, we should add support for dynamically picking which ones to copy here.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/context/dependency_manager.rs
Normal file
84
src/context/dependency_manager.rs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::error::CustomError;
|
||||||
|
|
||||||
|
use super::dependency::Dependency;
|
||||||
|
|
||||||
|
pub(crate) type RefDependencyManager = std::sync::Arc<std::sync::Mutex<DependencyManager>>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct DependencyManager {
|
||||||
|
/// A stack of paths for the files being visited.
|
||||||
|
///
|
||||||
|
/// The last entry is the current file being processed. This can be used for handling relative-path links.
|
||||||
|
file_stack: Vec<PathBuf>,
|
||||||
|
|
||||||
|
dependencies: Vec<Dependency>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DependencyManager {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
DependencyManager {
|
||||||
|
file_stack: Vec::new(),
|
||||||
|
dependencies: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push_file<P>(&mut self, path: P) -> Result<(), CustomError>
|
||||||
|
where
|
||||||
|
P: Into<PathBuf>,
|
||||||
|
{
|
||||||
|
self.file_stack.push(path.into());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pop_file(&mut self) -> Result<(), CustomError> {
|
||||||
|
self.file_stack
|
||||||
|
.pop()
|
||||||
|
.expect("Popped more files off the dependency manager file stack than exist.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_current_folder(&self) -> Result<&Path, CustomError> {
|
||||||
|
Ok(self
|
||||||
|
.file_stack
|
||||||
|
.last()
|
||||||
|
.ok_or("No current file")?
|
||||||
|
.parent()
|
||||||
|
.ok_or("Current file was not in a directory")?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mark_file_for_copying<P>(&mut self, path: P) -> Result<(), CustomError>
|
||||||
|
where
|
||||||
|
P: Into<PathBuf>,
|
||||||
|
{
|
||||||
|
self.dependencies.push(Dependency::StaticFile {
|
||||||
|
absolute_path: path.into(),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the dependencies and forget about them.
|
||||||
|
pub(crate) fn take_dependencies(&mut self) -> Vec<Dependency> {
|
||||||
|
let mut dependencies = Vec::new();
|
||||||
|
std::mem::swap(&mut self.dependencies, &mut dependencies);
|
||||||
|
dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn include_css<N>(&mut self, name: N) -> Result<(), CustomError>
|
||||||
|
where
|
||||||
|
std::string::String: From<N>,
|
||||||
|
{
|
||||||
|
self.dependencies
|
||||||
|
.push(Dependency::CssFile { name: name.into() });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn list_css(&self) -> Result<impl Iterator<Item = &String>, CustomError> {
|
||||||
|
Ok(self.dependencies.iter().filter_map(|dep| match dep {
|
||||||
|
Dependency::CssFile { name } => Some(name),
|
||||||
|
_ => None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "diary_sexp")]
|
#[serde(rename = "diary_sexp")]
|
||||||
pub(crate) struct RenderDiarySexp {}
|
pub(crate) struct RenderDiarySexp {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderDiarySexp, IDiarySexp);
|
rnoop!(RenderDiarySexp, IDiarySexp);
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use super::render_context::RenderContext;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::IDocumentElement;
|
use crate::intermediate::IDocumentElement;
|
||||||
|
|
||||||
use super::macros::render;
|
|
||||||
use super::RenderHeading;
|
use super::RenderHeading;
|
||||||
use super::RenderSection;
|
use super::RenderSection;
|
||||||
|
use super::macros::render;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "drawer")]
|
#[serde(rename = "drawer")]
|
||||||
pub(crate) struct RenderDrawer {}
|
pub(crate) struct RenderDrawer {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderDrawer, IDrawer);
|
rnoop!(RenderDrawer, IDrawer);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "dynamic_block")]
|
#[serde(rename = "dynamic_block")]
|
||||||
pub(crate) struct RenderDynamicBlock {}
|
pub(crate) struct RenderDynamicBlock {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderDynamicBlock, IDynamicBlock);
|
rnoop!(RenderDynamicBlock, IDynamicBlock);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::plain_list_simple_item::RenderPlainListSimpleItem;
|
||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::IElement;
|
use crate::intermediate::IElement;
|
||||||
@@ -35,6 +36,7 @@ use super::verse_block::RenderVerseBlock;
|
|||||||
pub(crate) enum RenderElement {
|
pub(crate) enum RenderElement {
|
||||||
Paragraph(RenderParagraph),
|
Paragraph(RenderParagraph),
|
||||||
PlainList(RenderPlainList),
|
PlainList(RenderPlainList),
|
||||||
|
PlainListSimpleItem(RenderPlainListSimpleItem),
|
||||||
CenterBlock(RenderCenterBlock),
|
CenterBlock(RenderCenterBlock),
|
||||||
QuoteBlock(RenderQuoteBlock),
|
QuoteBlock(RenderQuoteBlock),
|
||||||
SpecialBlock(RenderSpecialBlock),
|
SpecialBlock(RenderSpecialBlock),
|
||||||
@@ -69,6 +71,9 @@ render!(RenderElement, IElement, original, render_context, {
|
|||||||
render_context.clone(),
|
render_context.clone(),
|
||||||
inner,
|
inner,
|
||||||
)?)),
|
)?)),
|
||||||
|
IElement::PlainListSimpleItem(inner) => Ok(RenderElement::PlainListSimpleItem(
|
||||||
|
RenderPlainListSimpleItem::new(render_context.clone(), inner)?,
|
||||||
|
)),
|
||||||
IElement::CenterBlock(inner) => Ok(RenderElement::CenterBlock(RenderCenterBlock::new(
|
IElement::CenterBlock(inner) => Ok(RenderElement::CenterBlock(RenderCenterBlock::new(
|
||||||
render_context.clone(),
|
render_context.clone(),
|
||||||
inner,
|
inner,
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ use super::macros::render;
|
|||||||
#[serde(rename = "entity")]
|
#[serde(rename = "entity")]
|
||||||
pub(crate) struct RenderEntity {
|
pub(crate) struct RenderEntity {
|
||||||
html: String,
|
html: String,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(RenderEntity, IEntity, original, _render_context, {
|
render!(RenderEntity, IEntity, original, _render_context, {
|
||||||
Ok(RenderEntity {
|
Ok(RenderEntity {
|
||||||
html: original.html.clone(),
|
html: original.html.clone(),
|
||||||
|
post_blank: original.post_blank,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "example_block")]
|
#[serde(rename = "example_block")]
|
||||||
pub(crate) struct RenderExampleBlock {}
|
pub(crate) struct RenderExampleBlock {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderExampleBlock, IExampleBlock);
|
rnoop!(RenderExampleBlock, IExampleBlock);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "export_block")]
|
#[serde(rename = "export_block")]
|
||||||
pub(crate) struct RenderExportBlock {}
|
pub(crate) struct RenderExportBlock {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderExportBlock, IExportBlock);
|
rnoop!(RenderExportBlock, IExportBlock);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "export_snippet")]
|
#[serde(rename = "export_snippet")]
|
||||||
pub(crate) struct RenderExportSnippet {}
|
pub(crate) struct RenderExportSnippet {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderExportSnippet, IExportSnippet);
|
rnoop!(RenderExportSnippet, IExportSnippet);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "fixed_width_area")]
|
#[serde(rename = "fixed_width_area")]
|
||||||
pub(crate) struct RenderFixedWidthArea {}
|
pub(crate) struct RenderFixedWidthArea {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderFixedWidthArea, IFixedWidthArea);
|
rnoop!(RenderFixedWidthArea, IFixedWidthArea);
|
||||||
|
|||||||
@@ -13,18 +13,21 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "footnote_definition")]
|
#[serde(rename = "footnote_definition")]
|
||||||
pub(crate) struct RenderFootnoteDefinition {}
|
pub(crate) struct RenderFootnoteDefinition {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderFootnoteDefinition, IFootnoteDefinition);
|
rnoop!(RenderFootnoteDefinition, IFootnoteDefinition);
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "footnote_reference")]
|
#[serde(rename = "real_footnote_definition")]
|
||||||
pub(crate) struct RenderRealFootnoteDefinition {
|
pub(crate) struct RenderRealFootnoteDefinition {
|
||||||
definition_id: String,
|
definition_id: String,
|
||||||
reference_link: String,
|
reference_link: String,
|
||||||
label: String,
|
label: String,
|
||||||
contents: Vec<RenderAstNode>,
|
contents: Vec<RenderAstNode>,
|
||||||
|
// TODO: Do I need post_blank for real footnote definitions?
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(
|
render!(
|
||||||
@@ -36,7 +39,7 @@ render!(
|
|||||||
let contents = {
|
let contents = {
|
||||||
let mut ret = Vec::new();
|
let mut ret = Vec::new();
|
||||||
for obj in original.contents.iter() {
|
for obj in original.contents.iter() {
|
||||||
ret.push(obj.into_render_ast_node(render_context.clone())?);
|
ret.push(obj.as_render_ast_node(render_context.clone())?);
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pub(crate) struct RenderFootnoteReference {
|
|||||||
reference_id: String,
|
reference_id: String,
|
||||||
definition_link: String,
|
definition_link: String,
|
||||||
label: String,
|
label: String,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(
|
render!(
|
||||||
@@ -28,6 +29,7 @@ render!(
|
|||||||
original.get_definition_id(render_context.id_addition)
|
original.get_definition_id(render_context.id_addition)
|
||||||
),
|
),
|
||||||
label: original.get_display_label(),
|
label: original.get_display_label(),
|
||||||
|
post_blank: original.post_blank,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use super::render_context::RenderContext;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::IHeading;
|
use crate::intermediate::IHeading;
|
||||||
|
|
||||||
use super::macros::render;
|
|
||||||
use super::RenderDocumentElement;
|
use super::RenderDocumentElement;
|
||||||
use super::RenderObject;
|
use super::RenderObject;
|
||||||
|
use super::macros::render;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
@@ -15,6 +15,7 @@ pub(crate) struct RenderHeading {
|
|||||||
level: organic::types::HeadlineLevel,
|
level: organic::types::HeadlineLevel,
|
||||||
title: Vec<RenderObject>,
|
title: Vec<RenderObject>,
|
||||||
children: Vec<RenderDocumentElement>,
|
children: Vec<RenderDocumentElement>,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(RenderHeading, IHeading, original, render_context, {
|
render!(RenderHeading, IHeading, original, render_context, {
|
||||||
@@ -38,5 +39,6 @@ render!(RenderHeading, IHeading, original, render_context, {
|
|||||||
level: original.level + 1, // Adding 1 because the page title is going to be h1.
|
level: original.level + 1, // Adding 1 because the page title is going to be h1.
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
|
post_blank: original.post_blank,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "horizontal_rule")]
|
#[serde(rename = "horizontal_rule")]
|
||||||
pub(crate) struct RenderHorizontalRule {}
|
pub(crate) struct RenderHorizontalRule {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderHorizontalRule, IHorizontalRule);
|
rnoop!(RenderHorizontalRule, IHorizontalRule);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "inline_babel_call")]
|
#[serde(rename = "inline_babel_call")]
|
||||||
pub(crate) struct RenderInlineBabelCall {}
|
pub(crate) struct RenderInlineBabelCall {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderInlineBabelCall, IInlineBabelCall);
|
rnoop!(RenderInlineBabelCall, IInlineBabelCall);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use super::macros::render;
|
|||||||
#[serde(rename = "inline_source_block")]
|
#[serde(rename = "inline_source_block")]
|
||||||
pub(crate) struct RenderInlineSourceBlock {
|
pub(crate) struct RenderInlineSourceBlock {
|
||||||
value: String,
|
value: String,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(
|
render!(
|
||||||
@@ -21,6 +22,7 @@ render!(
|
|||||||
{
|
{
|
||||||
Ok(RenderInlineSourceBlock {
|
Ok(RenderInlineSourceBlock {
|
||||||
value: original.value.clone(),
|
value: original.value.clone(),
|
||||||
|
post_blank: original.post_blank,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ use super::render_context::RenderContext;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::IItalic;
|
use crate::intermediate::IItalic;
|
||||||
|
|
||||||
use super::macros::render;
|
|
||||||
use super::RenderObject;
|
use super::RenderObject;
|
||||||
|
use super::macros::render;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "italic")]
|
#[serde(rename = "italic")]
|
||||||
pub(crate) struct RenderItalic {
|
pub(crate) struct RenderItalic {
|
||||||
children: Vec<RenderObject>,
|
children: Vec<RenderObject>,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(RenderItalic, IItalic, original, render_context, {
|
render!(RenderItalic, IItalic, original, render_context, {
|
||||||
@@ -23,5 +24,8 @@ render!(RenderItalic, IItalic, original, render_context, {
|
|||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(RenderItalic { children })
|
Ok(RenderItalic {
|
||||||
|
children,
|
||||||
|
post_blank: original.post_blank,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "keyword")]
|
#[serde(rename = "keyword")]
|
||||||
pub(crate) struct RenderKeyword {}
|
pub(crate) struct RenderKeyword {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderKeyword, IKeyword);
|
rnoop!(RenderKeyword, IKeyword);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "latex_environment")]
|
#[serde(rename = "latex_environment")]
|
||||||
pub(crate) struct RenderLatexEnvironment {}
|
pub(crate) struct RenderLatexEnvironment {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderLatexEnvironment, ILatexEnvironment);
|
rnoop!(RenderLatexEnvironment, ILatexEnvironment);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use super::macros::render;
|
|||||||
#[serde(rename = "latex_fragment")]
|
#[serde(rename = "latex_fragment")]
|
||||||
pub(crate) struct RenderLatexFragment {
|
pub(crate) struct RenderLatexFragment {
|
||||||
value: String,
|
value: String,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(
|
render!(
|
||||||
@@ -21,6 +22,7 @@ render!(
|
|||||||
{
|
{
|
||||||
Ok(RenderLatexFragment {
|
Ok(RenderLatexFragment {
|
||||||
value: original.value.clone(),
|
value: original.value.clone(),
|
||||||
|
post_blank: original.post_blank,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "line_break")]
|
#[serde(rename = "line_break")]
|
||||||
pub(crate) struct RenderLineBreak {}
|
pub(crate) struct RenderLineBreak {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderLineBreak, ILineBreak);
|
rnoop!(RenderLineBreak, ILineBreak);
|
||||||
|
|||||||
@@ -24,12 +24,34 @@ macro_rules! rnoop {
|
|||||||
impl $rstruct {
|
impl $rstruct {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
_render_context: RenderContext<'_>,
|
_render_context: RenderContext<'_>,
|
||||||
_original: &$istruct,
|
original: &$istruct,
|
||||||
) -> Result<$rstruct, CustomError> {
|
) -> Result<$rstruct, CustomError> {
|
||||||
Ok($rstruct {})
|
Ok($rstruct {
|
||||||
|
post_blank: original.post_blank,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use rnoop;
|
pub(crate) use rnoop;
|
||||||
|
|
||||||
|
/// Push a file onto the render DependencyManager's file stack while inside the code block.
|
||||||
|
macro_rules! push_file {
|
||||||
|
($render_context:ident, $path:expr, $body:tt) => {{
|
||||||
|
$render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push_file($path)?;
|
||||||
|
let ret = (|| $body)();
|
||||||
|
$render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.pop_file()?;
|
||||||
|
ret
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use push_file;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ mod clock;
|
|||||||
mod code;
|
mod code;
|
||||||
mod comment;
|
mod comment;
|
||||||
mod comment_block;
|
mod comment_block;
|
||||||
|
mod dependency;
|
||||||
|
mod dependency_manager;
|
||||||
mod diary_sexp;
|
mod diary_sexp;
|
||||||
mod document_element;
|
mod document_element;
|
||||||
mod drawer;
|
mod drawer;
|
||||||
@@ -36,11 +38,13 @@ mod line_break;
|
|||||||
mod macros;
|
mod macros;
|
||||||
mod object;
|
mod object;
|
||||||
mod org_macro;
|
mod org_macro;
|
||||||
|
mod page;
|
||||||
mod page_header;
|
mod page_header;
|
||||||
mod paragraph;
|
mod paragraph;
|
||||||
mod plain_link;
|
mod plain_link;
|
||||||
mod plain_list;
|
mod plain_list;
|
||||||
mod plain_list_item;
|
mod plain_list_item;
|
||||||
|
mod plain_list_simple_item;
|
||||||
mod plain_text;
|
mod plain_text;
|
||||||
mod planning;
|
mod planning;
|
||||||
mod property_drawer;
|
mod property_drawer;
|
||||||
@@ -58,6 +62,7 @@ mod subscript;
|
|||||||
mod superscript;
|
mod superscript;
|
||||||
mod table;
|
mod table;
|
||||||
mod table_cell;
|
mod table_cell;
|
||||||
|
mod table_group;
|
||||||
mod table_row;
|
mod table_row;
|
||||||
mod target;
|
mod target;
|
||||||
mod timestamp;
|
mod timestamp;
|
||||||
@@ -69,12 +74,14 @@ pub(crate) use blog_post_page::RenderBlogPostPage;
|
|||||||
pub(crate) use blog_post_page::RenderBlogPostPageInput;
|
pub(crate) use blog_post_page::RenderBlogPostPageInput;
|
||||||
pub(crate) use blog_stream::RenderBlogStream;
|
pub(crate) use blog_stream::RenderBlogStream;
|
||||||
pub(crate) use blog_stream::RenderBlogStreamInput;
|
pub(crate) use blog_stream::RenderBlogStreamInput;
|
||||||
|
pub(crate) use dependency_manager::DependencyManager;
|
||||||
pub(crate) use document_element::RenderDocumentElement;
|
pub(crate) use document_element::RenderDocumentElement;
|
||||||
pub(crate) use element::RenderElement;
|
pub(crate) use element::RenderElement;
|
||||||
pub(crate) use footnote_definition::RenderRealFootnoteDefinition;
|
pub(crate) use footnote_definition::RenderRealFootnoteDefinition;
|
||||||
pub(crate) use global_settings::GlobalSettings;
|
pub(crate) use global_settings::GlobalSettings;
|
||||||
pub(crate) use heading::RenderHeading;
|
pub(crate) use heading::RenderHeading;
|
||||||
pub(crate) use object::RenderObject;
|
pub(crate) use object::RenderObject;
|
||||||
|
pub(crate) use page::RenderPage;
|
||||||
pub(crate) use page_header::PageHeader;
|
pub(crate) use page_header::PageHeader;
|
||||||
pub(crate) use render_context::RenderContext;
|
pub(crate) use render_context::RenderContext;
|
||||||
pub(crate) use section::RenderSection;
|
pub(crate) use section::RenderSection;
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use super::macros::rnoop;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "org_macro")]
|
#[serde(rename = "org_macro")]
|
||||||
pub(crate) struct RenderOrgMacro {}
|
pub(crate) struct RenderOrgMacro {
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
|
}
|
||||||
|
|
||||||
rnoop!(RenderOrgMacro, IOrgMacro);
|
rnoop!(RenderOrgMacro, IOrgMacro);
|
||||||
|
|||||||
131
src/context/page.rs
Normal file
131
src/context/page.rs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use super::GlobalSettings;
|
||||||
|
use super::PageHeader;
|
||||||
|
use super::RenderDocumentElement;
|
||||||
|
use super::footnote_definition::RenderRealFootnoteDefinition;
|
||||||
|
use super::macros::render;
|
||||||
|
use super::render_context::RenderContext;
|
||||||
|
use crate::context::macros::push_file;
|
||||||
|
use crate::error::CustomError;
|
||||||
|
use crate::intermediate::IPage;
|
||||||
|
use crate::intermediate::get_web_path;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
#[serde(rename = "page")]
|
||||||
|
pub(crate) struct RenderPage {
|
||||||
|
global_settings: GlobalSettings,
|
||||||
|
|
||||||
|
page_header: Option<PageHeader>,
|
||||||
|
|
||||||
|
/// The title that will be shown visibly on the page.
|
||||||
|
title: Option<String>,
|
||||||
|
|
||||||
|
self_link: Option<String>,
|
||||||
|
|
||||||
|
children: Vec<RenderDocumentElement>,
|
||||||
|
|
||||||
|
footnotes: Vec<RenderRealFootnoteDefinition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
render!(RenderPage, IPage, original, render_context, {
|
||||||
|
push_file!(render_context, &original.src, {
|
||||||
|
let page_header = PageHeader::new(
|
||||||
|
render_context.config.get_site_title().map(str::to_string),
|
||||||
|
Some(get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"",
|
||||||
|
)?),
|
||||||
|
Some(get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"about_me",
|
||||||
|
)?),
|
||||||
|
);
|
||||||
|
let link_to_blog_post = get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
render_context
|
||||||
|
.output_file
|
||||||
|
.strip_prefix(render_context.output_root_directory)?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let children = {
|
||||||
|
let mut children = Vec::new();
|
||||||
|
|
||||||
|
for child in original.children.iter() {
|
||||||
|
children.push(RenderDocumentElement::new(render_context.clone(), child)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
children
|
||||||
|
};
|
||||||
|
|
||||||
|
let footnotes = {
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
|
||||||
|
for footnote in original.footnotes.iter() {
|
||||||
|
ret.push(RenderRealFootnoteDefinition::new(
|
||||||
|
render_context.clone(),
|
||||||
|
footnote,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut css_files = vec![
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/reset.css",
|
||||||
|
)?,
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/main.css",
|
||||||
|
)?,
|
||||||
|
];
|
||||||
|
let additional_css_files = render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.list_css()?
|
||||||
|
.map(|css_name| {
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
format!("stylesheet/{}", css_name),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<HashSet<_>, _>>()?;
|
||||||
|
css_files.extend(additional_css_files.into_iter());
|
||||||
|
|
||||||
|
let js_files = vec![get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"blog_post.js",
|
||||||
|
)?];
|
||||||
|
|
||||||
|
let global_settings = GlobalSettings::new(original.title.clone(), css_files, js_files);
|
||||||
|
|
||||||
|
let ret = RenderPage {
|
||||||
|
global_settings,
|
||||||
|
page_header: Some(page_header),
|
||||||
|
title: original.title.clone(),
|
||||||
|
self_link: Some(link_to_blog_post),
|
||||||
|
children,
|
||||||
|
footnotes,
|
||||||
|
};
|
||||||
|
Ok(ret)
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -7,13 +7,35 @@ use serde::Serialize;
|
|||||||
pub(crate) struct PageHeader {
|
pub(crate) struct PageHeader {
|
||||||
website_title: Option<String>,
|
website_title: Option<String>,
|
||||||
home_link: Option<String>,
|
home_link: Option<String>,
|
||||||
|
nav_links: Vec<NavLink>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A link in the top-right of the page.
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
#[serde(rename = "nav_link")]
|
||||||
|
pub(crate) struct NavLink {
|
||||||
|
text: Option<String>,
|
||||||
|
url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PageHeader {
|
impl PageHeader {
|
||||||
pub(crate) fn new(website_title: Option<String>, home_link: Option<String>) -> PageHeader {
|
pub(crate) fn new(
|
||||||
|
website_title: Option<String>,
|
||||||
|
home_link: Option<String>,
|
||||||
|
about_me_link: Option<String>,
|
||||||
|
) -> PageHeader {
|
||||||
PageHeader {
|
PageHeader {
|
||||||
website_title,
|
website_title,
|
||||||
home_link,
|
home_link,
|
||||||
|
nav_links: about_me_link
|
||||||
|
.map(|url| {
|
||||||
|
vec![NavLink {
|
||||||
|
text: Some("About Me".to_owned()),
|
||||||
|
url: Some(url),
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ use super::render_context::RenderContext;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::IParagraph;
|
use crate::intermediate::IParagraph;
|
||||||
|
|
||||||
use super::macros::render;
|
|
||||||
use super::RenderObject;
|
use super::RenderObject;
|
||||||
|
use super::macros::render;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "paragraph")]
|
#[serde(rename = "paragraph")]
|
||||||
pub(crate) struct RenderParagraph {
|
pub(crate) struct RenderParagraph {
|
||||||
children: Vec<RenderObject>,
|
children: Vec<RenderObject>,
|
||||||
|
is_single_image: bool,
|
||||||
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(RenderParagraph, IParagraph, original, render_context, {
|
render!(RenderParagraph, IParagraph, original, render_context, {
|
||||||
@@ -23,5 +25,9 @@ render!(RenderParagraph, IParagraph, original, render_context, {
|
|||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(RenderParagraph { children })
|
Ok(RenderParagraph {
|
||||||
|
children,
|
||||||
|
is_single_image: original.is_single_image(),
|
||||||
|
post_blank: original.post_blank,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user