mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 16:54:58 +00:00
Compare commits
350 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c53e00f609 | ||
|
|
5e2c0e7d64 | ||
|
|
adbffe34d8 | ||
|
|
5e62008d78 | ||
|
|
af1c009dad | ||
|
|
53cb9ba7fb | ||
|
|
4cc139b372 | ||
|
|
8bc7804a9c | ||
|
|
a39807a858 | ||
|
|
5170650760 | ||
|
|
1d81b3c3b4 | ||
|
|
33970dafe8 | ||
|
|
faa46c54d8 | ||
|
|
42509a34cf | ||
|
|
ef369d4860 | ||
|
|
747ed3462a | ||
|
|
1fb1798f60 | ||
|
|
accf80200f | ||
|
|
4522ee1d4e | ||
|
|
313ebc6817 | ||
|
|
e0f439515f | ||
|
|
caa7560ab9 | ||
|
|
bbcf280da7 | ||
|
|
6d2daacb7b | ||
|
|
62f96a2d6c | ||
|
|
50f414d520 | ||
|
|
882f3894f3 | ||
|
|
30ddde7b49 | ||
|
|
5cced6fb51 | ||
|
|
a82ebf43b6 | ||
|
|
ebb6d6205a | ||
|
|
58950c469a | ||
|
|
0eebdaf0c7 | ||
|
|
54e2f28f4c | ||
|
|
d4d50ef12b | ||
|
|
075f2b16a4 | ||
|
|
6f8008a53c | ||
|
|
0618b52bae | ||
|
|
f1951c5db3 | ||
|
|
dad12acd8d | ||
|
|
a4503e076f | ||
|
|
09ddd339b8 | ||
|
|
bc94f4b6b8 | ||
|
|
564406f60f | ||
|
|
7845ce62e0 | ||
|
|
a1542752b7 | ||
|
|
7956475363 | ||
|
|
004ed56591 | ||
|
|
d497df3c27 | ||
|
|
3a8d46234f | ||
|
|
4d24dc5149 | ||
|
|
8eb7fbf7dc | ||
|
|
ffeeb7c553 | ||
|
|
c3c174155c | ||
|
|
2c2d2a9fd9 | ||
|
|
d692e2a6d5 | ||
|
|
e4cebddd0c | ||
|
|
0e48537d65 | ||
|
|
a461a96b9c | ||
|
|
9524c4f7c3 | ||
|
|
64b04d9cfd | ||
|
|
b419ad8caf | ||
|
|
8036c17916 | ||
|
|
220d498be0 | ||
|
|
0f5b6a2d6e | ||
|
|
36369d75c7 | ||
|
|
059b8283fd | ||
|
|
386eb2452a | ||
|
|
38e9a97fd2 | ||
|
|
ecca40e9d5 | ||
|
|
9d08e27e31 | ||
|
|
969bf5e867 | ||
|
|
3b5f28f4df | ||
|
|
df5619f988 | ||
|
|
a6d8383176 | ||
|
|
dbc7e374cd | ||
|
|
d81729857b | ||
|
|
d3d8a7b73c | ||
|
|
d9e6776b95 | ||
|
|
bde98faffa | ||
|
|
c2847554e0 | ||
|
|
9411a65dd8 | ||
|
|
9c1165e77e | ||
|
|
a02c7a4015 | ||
|
|
bdcde88e6f | ||
|
|
a32b100192 | ||
|
|
670d716403 | ||
|
|
bba3895f35 | ||
|
|
9e60ddbe85 | ||
|
|
44334fca52 | ||
|
|
2e2ed398c6 | ||
|
|
34f2a52cb7 | ||
|
|
d3888a884f | ||
|
|
3845871368 | ||
|
|
a3b2d19adb | ||
|
|
e4e7cd8c47 | ||
|
|
fb22e5521b | ||
|
|
d2ae766ae3 | ||
|
|
c0db949729 | ||
|
|
d2d4337ffd | ||
|
|
b0ca04635e | ||
|
|
198c66e6cd | ||
|
|
24346b9a38 | ||
|
|
0639562f1c | ||
|
|
c1fa9cc531 | ||
|
|
18653534ad | ||
|
|
2b89c33067 | ||
|
|
aee26d9375 | ||
|
|
7b04d492ab | ||
|
|
8abd03e165 | ||
|
|
2df843bc98 | ||
|
|
be4d2c29a5 | ||
|
|
8ce88bf491 | ||
|
|
b05571a595 | ||
|
|
4edfc71d68 | ||
|
|
3049694a0a | ||
|
|
5860c5747b | ||
|
|
d3c2d55706 | ||
|
|
ac2ff47a9c | ||
|
|
9a2773c618 | ||
|
|
51b6575d9f | ||
|
|
68a43f0682 | ||
|
|
9da88c4d1b | ||
|
|
19df2ec0fb | ||
|
|
bac7aec0ba | ||
|
|
42f916af41 | ||
|
|
47a5e237e0 | ||
|
|
1279e2351a | ||
|
|
adb8177026 | ||
|
|
4e8fa5abda | ||
|
|
6894f45b6c | ||
|
|
89d25be837 | ||
|
|
5b33e48552 | ||
|
|
79e8ab639a | ||
|
|
a0cc673736 | ||
|
|
d06ecfadc8 | ||
|
|
64a117b0c3 | ||
|
|
cf006be9cf | ||
|
|
45ad3d8e63 | ||
|
|
441c968c4f | ||
|
|
99f2763fdf | ||
|
|
1312395a11 | ||
|
|
615f147bcb | ||
|
|
d0ad3bfc69 | ||
|
|
fe58a33747 | ||
|
|
26e537e768 | ||
|
|
ad70a6c4fe | ||
|
|
22a04da6bb | ||
|
|
dc97fe0ff0 | ||
|
|
9ca97c6e01 | ||
|
|
924653e240 | ||
|
|
59d48f8599 | ||
|
|
90f8eab816 | ||
|
|
9ae45e260d | ||
|
|
aee15f95e2 | ||
|
|
1c540ac375 | ||
|
|
5b0e883ac9 | ||
|
|
9a2519af71 | ||
|
|
9b9ff601c0 | ||
|
|
2f798296dc | ||
|
|
2b2ff63cad | ||
|
|
3b135c09f3 | ||
|
|
28813eba5b | ||
|
|
02ff765f54 | ||
|
|
fe8a806132 | ||
|
|
6626d58301 | ||
|
|
cb0911e890 | ||
|
|
3745a68dc5 | ||
|
|
fd64830476 | ||
|
|
1ee0fa9c1f | ||
|
|
8fab325b0a | ||
|
|
858ffad913 | ||
|
|
62735b99e7 | ||
|
|
29ab9c6300 | ||
|
|
debc01c7de | ||
|
|
c07f35ecd6 | ||
|
|
c8f528700c | ||
|
|
896248c237 | ||
|
|
74a181db21 | ||
|
|
ba10113342 | ||
|
|
eb0207c92e | ||
|
|
0fbcee8119 | ||
|
|
ce8bc798f6 | ||
|
|
996195d651 | ||
|
|
f00b29d3a3 | ||
|
|
a6756ecf11 | ||
|
|
ef7ec30037 | ||
|
|
1642787825 | ||
|
|
f83781480f | ||
|
|
9b70205855 | ||
|
|
0e4bf3c144 | ||
|
|
1241c74b41 | ||
|
|
44fea098de | ||
|
|
52286efa25 | ||
|
|
a1b2179442 | ||
|
|
cffc430738 | ||
|
|
d76454272b | ||
|
|
9d1e77c00f | ||
|
|
2050abdc6c | ||
|
|
3ea013c503 | ||
|
|
309db96138 | ||
|
|
283b4be71c | ||
|
|
ab9bca0750 | ||
|
|
ac2be67c3c | ||
|
|
6ffafb8cca | ||
|
|
590e6b556c | ||
|
|
5dc5c6fbdf | ||
|
|
cd8c9f2c32 | ||
|
|
3490941d4c | ||
|
|
3f201df0c2 | ||
|
|
4c22d515bd | ||
|
|
ec0ef1c4b3 | ||
|
|
1e51e2d49a | ||
|
|
5186499b90 | ||
|
|
fc275e4164 | ||
|
|
9fa466f7cf | ||
|
|
efd6f256a5 | ||
|
|
f7f9d6b5f0 | ||
|
|
0526062ab2 | ||
|
|
a1ededa722 | ||
|
|
9b09a02912 | ||
|
|
657a5e82a5 | ||
|
|
1eece02254 | ||
|
|
c866b47dcb | ||
|
|
ff52ec750b | ||
|
|
4184619afc | ||
|
|
954dbd1ec8 | ||
|
|
30b70e76aa | ||
|
|
ec6d112711 | ||
|
|
e796c6a763 | ||
|
|
be32201294 | ||
|
|
5ebc54b5b7 | ||
|
|
2954a57872 | ||
|
|
df705bd79f | ||
|
|
4e8ce3585d | ||
|
|
ab5f17c3d2 | ||
|
|
a8918c74d4 | ||
|
|
ae5738d82d | ||
|
|
bb99aedffa | ||
|
|
d6ee1864c8 | ||
|
|
8a4be66277 | ||
|
|
79ce2f8458 | ||
|
|
3d4ae44ddd | ||
|
|
1efb1faa40 | ||
|
|
bfd6a56397 | ||
|
|
a788f6d8e8 | ||
|
|
80fa91d74d | ||
|
|
db34562e9a | ||
|
|
d6af089ab3 | ||
|
|
b3a70c5911 | ||
|
|
bf5765c9cb | ||
|
|
6c7697648c | ||
|
|
04158485c7 | ||
|
|
cf83c77280 | ||
|
|
6e545b13dd | ||
|
|
bf258a1f41 | ||
|
|
6f06c73d6f | ||
|
|
a689c53a9c | ||
|
|
58cdda1d35 | ||
|
|
bcb02b5c5c | ||
|
|
0736c7de1a | ||
|
|
28be9f9fbb | ||
|
|
647681de38 | ||
|
|
81beb5c539 | ||
|
|
5c1e0a29c8 | ||
|
|
01ac5276a9 | ||
|
|
e40594ad8e | ||
|
|
bc9c6a622a | ||
|
|
17f22199b7 | ||
|
|
7e93fe4535 | ||
|
|
2e98df3572 | ||
|
|
3b561de413 | ||
|
|
0d0dae8742 | ||
|
|
61b4b5bd17 | ||
|
|
0060740b76 | ||
|
|
bf8a4af579 | ||
|
|
dc057fe39d | ||
|
|
9877ab131a | ||
|
|
13aba64cb7 | ||
|
|
fe9bc2dbfc | ||
|
|
c53b682852 | ||
|
|
3aca6a9e4c | ||
|
|
40fdfc0317 | ||
|
|
25d9415b0a | ||
|
|
43c8702398 | ||
|
|
ce94776dbb | ||
|
|
78ab6cd848 | ||
|
|
0a7001f868 | ||
|
|
2db7be671b | ||
|
|
b51870f7bf | ||
|
|
0227dfa7e5 | ||
|
|
05b59d9af0 | ||
|
|
04f9f3b3a8 | ||
|
|
b58be8b38c | ||
|
|
10493d754a | ||
|
|
cf28516b8b | ||
|
|
d81e25c4e4 | ||
|
|
c4612ff5d2 | ||
|
|
d70acbb47e | ||
|
|
3c977d22fe | ||
|
|
e34db2b583 | ||
|
|
71de57ac88 | ||
|
|
c7cc661018 | ||
|
|
09f39c02fe | ||
|
|
23c76a73e0 | ||
|
|
1cafe08237 | ||
|
|
45b07964ef | ||
|
|
6cc4a57b62 | ||
|
|
31280acbae | ||
|
|
6f0928b12c | ||
|
|
c6e9250d60 | ||
|
|
104bda459f | ||
|
|
bd2effa183 | ||
|
|
562412fbe7 | ||
|
|
fd6fbe5598 | ||
|
|
67472e1e1c | ||
|
|
d64c3e8c66 | ||
|
|
f344f97374 | ||
|
|
770088b912 | ||
|
|
cb9b39c3ca | ||
|
|
0f34b20546 | ||
|
|
0d7bbbb659 | ||
|
|
3f3e36bbe9 | ||
|
|
9f41478d33 | ||
|
|
b86fee04fc | ||
|
|
c78da6949f | ||
|
|
7b215bb8f1 | ||
|
|
78cec76a96 | ||
|
|
0e98ad2ecb | ||
|
|
30bb3f183a | ||
|
|
1369338f3c | ||
|
|
ac03f64724 | ||
|
|
bea4468972 | ||
|
|
224adffa15 | ||
|
|
b4d34ffdbc | ||
|
|
a85db9cf5e | ||
|
|
990c82432c | ||
|
|
e3673b594c | ||
|
|
f8cf278a24 | ||
|
|
b0bc596a49 | ||
|
|
4501ceec05 | ||
|
|
4183fdfd42 | ||
|
|
2595793019 | ||
|
|
aa3d1914b1 | ||
|
|
679ddbb6bf | ||
|
|
b1d7d07a04 | ||
|
|
5f58263232 | ||
|
|
b6bdd072f7 | ||
|
|
509f2141ac | ||
|
|
4c2bc34d58 |
25
.github/workflows/lint.yaml
vendored
25
.github/workflows/lint.yaml
vendored
@@ -15,14 +15,31 @@ permissions:
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: [self-hosted, k8s]
|
||||
runs-on: gha-rs
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
cache: false
|
||||
|
||||
- name: Install Packages
|
||||
run: sudo apt update && sudo apt -qq -y install git curl zip unzip tar bzip2 make
|
||||
|
||||
- name: Install Tools
|
||||
run: |
|
||||
set -x
|
||||
make tools
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
with:
|
||||
version: latest
|
||||
skip-pkg-cache: true
|
||||
|
||||
37
.github/workflows/release.yaml
vendored
37
.github/workflows/release.yaml
vendored
@@ -2,32 +2,57 @@ name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
# Run only against tags
|
||||
tags:
|
||||
- '*'
|
||||
branches:
|
||||
- release
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: [self-hosted, k8s]
|
||||
runs-on: gha-rs
|
||||
steps:
|
||||
# Must come before Checkout, otherwise goreleaser fails
|
||||
- name: Provide GPG and Git
|
||||
run: sudo apt update && sudo apt -qq -y install gnupg git curl zip unzip tar bzip2 make
|
||||
|
||||
# Must come after git executable is provided
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
# Necessary to run these outside of goreleaser, otherwise
|
||||
# /home/runner/_work/holos/holos/internal/frontend/node_modules/.bin/protoc-gen-connect-query is not in PATH
|
||||
- name: Install Tools
|
||||
run: |
|
||||
set -x
|
||||
make tools
|
||||
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_CODE_SIGNING_SECRETKEY }}
|
||||
passphrase: ${{ secrets.GPG_CODE_SIGNING_PASSPHRASE }}
|
||||
|
||||
- name: List keys
|
||||
run: gpg -K
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
- name: Git diff
|
||||
run: git diff
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
|
||||
22
.github/workflows/test.yaml
vendored
22
.github/workflows/test.yaml
vendored
@@ -13,20 +13,34 @@ permissions:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: [self-hosted, k8s]
|
||||
runs-on: gha-rs
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
- name: Install Packages
|
||||
run: sudo apt update && sudo apt -qq -y install git curl zip unzip tar bzip2 make
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4.1.0
|
||||
with:
|
||||
version: 'latest'
|
||||
uses: azure/setup-helm@v4
|
||||
|
||||
- name: Set up Kubectl
|
||||
uses: azure/setup-kubectl@v3
|
||||
|
||||
- name: Install Tools
|
||||
run: |
|
||||
set -x
|
||||
make tools
|
||||
|
||||
- name: Test
|
||||
run: ./scripts/test
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,7 +1,9 @@
|
||||
bin/
|
||||
/bin/
|
||||
vendor/
|
||||
.idea/
|
||||
coverage.out
|
||||
dist/
|
||||
/dist/
|
||||
*.hold/
|
||||
/deploy/
|
||||
.vscode/
|
||||
tmp/
|
||||
|
||||
@@ -10,10 +10,8 @@ version: 1
|
||||
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
- go mod tidy
|
||||
# you may remove this if you don't need go generate
|
||||
- go generate ./...
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- main: ./cmd/holos
|
||||
@@ -23,6 +21,9 @@ builds:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
|
||||
signs:
|
||||
- artifacts: checksum
|
||||
|
||||
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM quay.io/holos-run/debian:bullseye AS final
|
||||
USER root
|
||||
WORKDIR /app
|
||||
ADD bin bin
|
||||
RUN chown -R app: /app
|
||||
# Kubernetes requires the user to be numeric
|
||||
USER 8192
|
||||
ENTRYPOINT bin/holos server
|
||||
202
LICENSE
Normal file
202
LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
55
Makefile
55
Makefile
@@ -4,19 +4,24 @@ PROJ=holos
|
||||
ORG_PATH=github.com/holos-run
|
||||
REPO_PATH=$(ORG_PATH)/$(PROJ)
|
||||
|
||||
VERSION := $(shell grep "const Version " pkg/version/version.go | sed -E 's/.*"(.+)"$$/\1/')
|
||||
VERSION := $(shell cat version/embedded/major version/embedded/minor version/embedded/patch | xargs printf "%s.%s.%s")
|
||||
BIN_NAME := holos
|
||||
|
||||
DOCKER_REPO=quay.io/openinfrastructure/holos
|
||||
DOCKER_REPO=quay.io/holos-run/holos
|
||||
IMAGE_NAME=$(DOCKER_REPO)
|
||||
|
||||
$( shell mkdir -p bin)
|
||||
|
||||
# For buf plugin protoc-gen-connect-es
|
||||
export PATH := $(PWD)/internal/frontend/holos/node_modules/.bin:$(PATH)
|
||||
|
||||
GIT_COMMIT=$(shell git rev-parse HEAD)
|
||||
GIT_SUFFIX=$(shell test -n "`git status --porcelain`" && echo "-dirty" || echo "")
|
||||
GIT_DETAIL=$(shell git describe --tags HEAD)
|
||||
GIT_TREE_STATE=$(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean")
|
||||
BUILD_DATE=$(shell date -Iseconds)
|
||||
|
||||
LD_FLAGS="-w -X ${ORG_PATH}/${PROJ}/pkg/version.GitCommit=${GIT_COMMIT} -X ${ORG_PATH}/${PROJ}/pkg/version.GitTreeState=${GIT_TREE_STATE} -X ${ORG_PATH}/${PROJ}/pkg/version.BuildDate=${BUILD_DATE}"
|
||||
LD_FLAGS="-w -X ${ORG_PATH}/${PROJ}/version.GitDescribe=${GIT_DETAIL}${GIT_SUFFIX} -X ${ORG_PATH}/${PROJ}/version.GitCommit=${GIT_COMMIT} -X ${ORG_PATH}/${PROJ}/version.GitTreeState=${GIT_TREE_STATE} -X ${ORG_PATH}/${PROJ}/version.BuildDate=${BUILD_DATE}"
|
||||
|
||||
.PHONY: default
|
||||
default: test
|
||||
@@ -39,12 +44,18 @@ bumpmajor: ## Bump the major version.
|
||||
scripts/bump minor 0
|
||||
scripts/bump patch 0
|
||||
|
||||
.PHONY: show-version
|
||||
show-version: ## Print the full version.
|
||||
@echo $(VERSION)
|
||||
|
||||
.PHONY: tidy
|
||||
tidy: ## Tidy go module.
|
||||
go mod tidy
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## Format Go code.
|
||||
fmt: ## Format code.
|
||||
cd docs/examples && cue fmt ./...
|
||||
cd internal/generate/platforms && cue fmt ./...
|
||||
go fmt ./...
|
||||
|
||||
.PHONY: vet
|
||||
@@ -56,7 +67,7 @@ generate: ## Generate code.
|
||||
go generate ./...
|
||||
|
||||
.PHONY: build
|
||||
build: generate ## Build holos executable.
|
||||
build: ## Build holos executable.
|
||||
@echo "building ${BIN_NAME} ${VERSION}"
|
||||
@echo "GOPATH=${GOPATH}"
|
||||
go build -trimpath -o bin/$(BIN_NAME) -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/$(BIN_NAME)
|
||||
@@ -75,6 +86,8 @@ test: ## Run tests.
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Run linters.
|
||||
buf lint
|
||||
cd internal/frontend/holos && ng lint
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: coverage
|
||||
@@ -85,6 +98,38 @@ coverage: test ## Test coverage profile.
|
||||
snapshot: ## Go release snapshot
|
||||
goreleaser release --snapshot --clean
|
||||
|
||||
.PHONY: tools
|
||||
tools: go-deps frontend-deps website-deps ## install tool dependencies
|
||||
|
||||
.PHONY: go-deps
|
||||
go-deps: ## tool versions pinned in tools.go
|
||||
go install cuelang.org/go/cmd/cue
|
||||
go install github.com/bufbuild/buf/cmd/buf
|
||||
go install github.com/fullstorydev/grpcurl/cmd/grpcurl
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go
|
||||
go install connectrpc.com/connect/cmd/protoc-gen-connect-go
|
||||
go install honnef.co/go/tools/cmd/staticcheck
|
||||
go install golang.org/x/tools/cmd/godoc
|
||||
# curl https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash
|
||||
|
||||
.PHONY: frontend-deps
|
||||
frontend-deps: ## Install Angular deps for go generate
|
||||
cd internal/frontend/holos && npm install
|
||||
cd internal/frontend/holos && npm install --save-dev @bufbuild/buf @connectrpc/protoc-gen-connect-es
|
||||
cd internal/frontend/holos && npm install @connectrpc/connect @connectrpc/connect-web @bufbuild/protobuf
|
||||
# https://github.com/connectrpc/connect-query-es/blob/1350b6f07b6aead81793917954bdb1cc3ce09df9/packages/protoc-gen-connect-query/README.md?plain=1#L23
|
||||
cd internal/frontend/holos && npm install --save-dev @connectrpc/protoc-gen-connect-query @bufbuild/protoc-gen-es
|
||||
cd internal/frontend/holos && npm install @connectrpc/connect-query @bufbuild/protobuf
|
||||
|
||||
.PHONY: website-deps
|
||||
website-deps: ## Install Docusaurus deps for go generate
|
||||
cd doc/website && npm install
|
||||
|
||||
.PHONY: image
|
||||
image: build ## Docker image build
|
||||
docker build . -t ${DOCKER_REPO}:v$(shell ./bin/holos --version)
|
||||
docker push ${DOCKER_REPO}:v$(shell ./bin/holos --version)
|
||||
|
||||
.PHONY: help
|
||||
help: ## Display this help menu.
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
|
||||
32
README.md
Normal file
32
README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
## Holos - A Holostic Development Platform
|
||||
|
||||
<img width="50%"
|
||||
align="right"
|
||||
style="display: block; margin: 40px auto;"
|
||||
src="https://openinfrastructure.co/blog/2016/02/27/logo/logorectangle.png">
|
||||
|
||||
Building and maintaining a software development platform is a complex and time
|
||||
consuming endeavour. Organizations often dedicate a team of 3-4 who need 6-12
|
||||
months to build the platform.
|
||||
|
||||
Holos is a tool and a reference platform to reduce the compexity and speed up
|
||||
the process of building a modern, cloud native software development platform.
|
||||
|
||||
- **Accelerate new projects** - Reduce time to market and operational complexity by starting your new project on top of the Holos reference platform.
|
||||
- **Modernize existing projects** - Incrementally incorporate your existing platform services into Holos for simpler integration.
|
||||
- **Unified configuration model** - Increase safety and reduce the risk of config changes with CUE.
|
||||
- **First class Helm and Kustomize support** - Leverage and reuse your existing investment in existing configuration tools such as Helm and Kustomize.
|
||||
- **Modern Authentication and Authorization** - Holos seamlessly integrates platform identity and access mangement with zero-trust beyond corp style authorization policy.
|
||||
|
||||
## Quick Installation
|
||||
```console
|
||||
go install github.com/holos-run/holos/cmd/holos@latest
|
||||
```
|
||||
|
||||
## Docs and Support
|
||||
The documentation for developing and using Holos is avaialble at: https://holos.run
|
||||
|
||||
For discussion and support, [open a discussion](https://github.com/orgs/holos-run/discussions/new/choose).
|
||||
|
||||
## License
|
||||
Holos is licensed under Apache 2.0 as found in the [LICENSE file](LICENSE).
|
||||
311
Tiltfile
Normal file
311
Tiltfile
Normal file
@@ -0,0 +1,311 @@
|
||||
# -*- mode: Python -*-
|
||||
# This Tiltfile manages a Go project with live leload in Kubernetes
|
||||
|
||||
listen_port = 3000
|
||||
metrics_port = 9090
|
||||
|
||||
# Use our wrapper to set the kube namespace
|
||||
if os.getenv('TILT_WRAPPER') != '1':
|
||||
fail("could not run, ./hack/tilt/bin/tilt was not used to start tilt")
|
||||
|
||||
# AWS Account to work in
|
||||
aws_account = '271053619184'
|
||||
aws_region = 'us-east-2'
|
||||
|
||||
# Resource ids
|
||||
holos_backend = 'Holos Backend'
|
||||
pg_admin = 'pgAdmin'
|
||||
pg_cluster = 'PostgresCluster'
|
||||
pg_svc = 'Database Pod'
|
||||
compile_id = 'Go Build'
|
||||
auth_id = 'Auth Policy'
|
||||
lint_id = 'Run Linters'
|
||||
tests_id = 'Run Tests'
|
||||
|
||||
# PostgresCluster resource name in k8s
|
||||
pg_cluster_name = 'holos'
|
||||
# Database name inside the PostgresCluster
|
||||
pg_database_name = 'holos'
|
||||
# PGAdmin name
|
||||
pg_admin_name = 'pgadmin'
|
||||
|
||||
# Default Registry.
|
||||
# See: https://github.com/tilt-dev/tilt.build/blob/master/docs/choosing_clusters.md#manual-configuration
|
||||
# Note, Tilt will append the image name to the registry uri path
|
||||
default_registry('{account}.dkr.ecr.{region}.amazonaws.com/holos-run/holos-server'.format(account=aws_account, region=aws_region))
|
||||
|
||||
# Set a name prefix specific to the user. Multiple developers share the tilt-holos namespace.
|
||||
developer = os.getenv('USER')
|
||||
holos_server = 'holos'
|
||||
# See ./hack/tilt/bin/tilt
|
||||
namespace = os.getenv('NAMESPACE')
|
||||
# We always develop against the k1 cluster.
|
||||
os.putenv('KUBECONFIG', os.path.abspath('./hack/tilt/kubeconfig'))
|
||||
# The context defined in ./hack/tilt/kubeconfig
|
||||
allow_k8s_contexts('sso@k1')
|
||||
allow_k8s_contexts('sso@k2')
|
||||
allow_k8s_contexts('sso@k3')
|
||||
allow_k8s_contexts('sso@k4')
|
||||
allow_k8s_contexts('sso@k5')
|
||||
# PG db connection for localhost -> k8s port-forward
|
||||
os.putenv('PGHOST', 'localhost')
|
||||
os.putenv('PGPORT', '15432')
|
||||
# We always develop in the dev aws account.
|
||||
os.putenv('AWS_CONFIG_FILE', os.path.abspath('./hack/tilt/aws.config'))
|
||||
os.putenv('AWS_ACCOUNT', aws_account)
|
||||
os.putenv('AWS_DEFAULT_REGION', aws_region)
|
||||
os.putenv('AWS_PROFILE', 'dev-holos')
|
||||
os.putenv('AWS_SDK_LOAD_CONFIG', '1')
|
||||
# Authenticate to AWS ECR when tilt up is run by the developer
|
||||
local_resource('AWS Credentials', './hack/tilt/aws-login.sh', auto_init=True)
|
||||
|
||||
# Extensions are open-source, pre-packaged functions that extend Tilt
|
||||
#
|
||||
# More info: https://github.com/tilt-dev/tilt-extensions
|
||||
# More info: https://docs.tilt.dev/extensions.html
|
||||
load('ext://restart_process', 'docker_build_with_restart')
|
||||
load('ext://k8s_attach', 'k8s_attach')
|
||||
load('ext://git_resource', 'git_checkout')
|
||||
load('ext://uibutton', 'cmd_button')
|
||||
|
||||
# Paths edited by the developer Tilt watches to trigger compilation.
|
||||
# Generated files should be excluded to avoid an infinite build loop.
|
||||
developer_paths = [
|
||||
'./cmd',
|
||||
'./internal/server',
|
||||
'./internal/ent/schema',
|
||||
'./frontend/package-lock.json',
|
||||
'./frontend/src',
|
||||
'./go.mod',
|
||||
'./pkg',
|
||||
'./service/holos',
|
||||
]
|
||||
|
||||
# Builds the holos-server executable
|
||||
local_resource(compile_id, 'make build', deps=developer_paths)
|
||||
|
||||
# Build Docker image
|
||||
# Tilt will automatically associate image builds with the resource(s)
|
||||
# that reference them (e.g. via Kubernetes or Docker Compose YAML).
|
||||
#
|
||||
# More info: https://docs.tilt.dev/api.html#api.docker_build
|
||||
#
|
||||
docker_build_with_restart(
|
||||
'holos',
|
||||
context='.',
|
||||
entrypoint=[
|
||||
'/app/bin/holos',
|
||||
'server',
|
||||
'--listen-port={}'.format(listen_port),
|
||||
'--oidc-issuer=https://login.ois.run',
|
||||
'--oidc-audience=262096764402729854@holos_platform',
|
||||
'--log-level=debug',
|
||||
'--metrics-port={}'.format(metrics_port),
|
||||
],
|
||||
dockerfile='./hack/tilt/Dockerfile',
|
||||
only=['./bin'],
|
||||
# (Recommended) Updating a running container in-place
|
||||
# https://docs.tilt.dev/live_update_reference.html
|
||||
live_update=[
|
||||
# Sync files from host to container
|
||||
sync('./bin', '/app/bin'),
|
||||
# Wait for aws-login https://github.com/tilt-dev/tilt/issues/3048
|
||||
sync('./tilt/aws-login.last', '/dev/null'),
|
||||
# Execute commands in the container when paths change
|
||||
# run('/app/hack/codegen.sh', trigger=['./app/api'])
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
# Run local commands
|
||||
# Local commands can be helpful for one-time tasks like installing
|
||||
# project prerequisites. They can also manage long-lived processes
|
||||
# for non-containerized services or dependencies.
|
||||
#
|
||||
# More info: https://docs.tilt.dev/local_resource.html
|
||||
#
|
||||
# local_resource('install-helm',
|
||||
# cmd='which helm > /dev/null || brew install helm',
|
||||
# # `cmd_bat`, when present, is used instead of `cmd` on Windows.
|
||||
# cmd_bat=[
|
||||
# 'powershell.exe',
|
||||
# '-Noninteractive',
|
||||
# '-Command',
|
||||
# '& {if (!(Get-Command helm -ErrorAction SilentlyContinue)) {scoop install helm}}'
|
||||
# ]
|
||||
# )
|
||||
|
||||
# Teach tilt about our custom resources (Note, this may be intended for workloads)
|
||||
# k8s_kind('authorizationpolicy')
|
||||
# k8s_kind('requestauthentication')
|
||||
# k8s_kind('virtualservice')
|
||||
k8s_kind('pgadmin')
|
||||
|
||||
|
||||
# Troubleshooting
|
||||
def resource_name(id):
|
||||
print('resource: {}'.format(id))
|
||||
return id.name
|
||||
|
||||
|
||||
workload_to_resource_function(resource_name)
|
||||
|
||||
# Apply Kubernetes manifests
|
||||
# Tilt will build & push any necessary images, re-deploying your
|
||||
# resources as they change.
|
||||
#
|
||||
# More info: https://docs.tilt.dev/api.html#api.k8s_yaml
|
||||
#
|
||||
|
||||
def holos_yaml():
|
||||
"""Return a k8s Deployment personalized for the developer."""
|
||||
k8s_yaml_template = str(read_file('./hack/tilt/k8s.yaml'))
|
||||
return k8s_yaml_template.format(
|
||||
name=holos_server,
|
||||
developer=developer,
|
||||
namespace=namespace,
|
||||
listen_port=listen_port,
|
||||
metrics_port=metrics_port,
|
||||
tz=os.getenv('TZ'),
|
||||
)
|
||||
|
||||
# Customize a Kubernetes resource
|
||||
# By default, Kubernetes resource names are automatically assigned
|
||||
# based on objects in the YAML manifests, e.g. Deployment name.
|
||||
#
|
||||
# Tilt strives for sane defaults, so calling k8s_resource is
|
||||
# optional, and you only need to pass the arguments you want to
|
||||
# override.
|
||||
#
|
||||
# More info: https://docs.tilt.dev/api.html#api.k8s_resource
|
||||
#
|
||||
k8s_yaml(blob(holos_yaml()))
|
||||
|
||||
# Backend server process
|
||||
k8s_resource(
|
||||
workload=holos_server,
|
||||
new_name=holos_backend,
|
||||
objects=[
|
||||
'{}:serviceaccount'.format(holos_server),
|
||||
'{}:servicemonitor'.format(holos_server),
|
||||
],
|
||||
resource_deps=[compile_id],
|
||||
links=[
|
||||
link('https://{}.app.dev.k2.holos.run/ui/'.format(developer), "Holos Web UI")
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
# AuthorizationPolicy - Beyond Corp functionality
|
||||
k8s_resource(
|
||||
new_name=auth_id,
|
||||
objects=[
|
||||
'{}:virtualservice'.format(holos_server),
|
||||
],
|
||||
)
|
||||
|
||||
# Database
|
||||
# Note: Tilt confuses the backup pods with the database server pods, so this code is careful to tease the pods
|
||||
# apart so logs are streamed correctly.
|
||||
# See: https://github.com/tilt-dev/tilt.specs/blob/master/resource_assembly.md
|
||||
|
||||
# pgAdmin Web UI
|
||||
k8s_resource(
|
||||
workload=pg_admin_name,
|
||||
new_name=pg_admin,
|
||||
port_forwards=[
|
||||
port_forward(15050, 5050, pg_admin),
|
||||
],
|
||||
)
|
||||
|
||||
# Disabled because these don't group resources nicely
|
||||
# k8s_kind('postgrescluster')
|
||||
|
||||
# Postgres database in-cluster
|
||||
k8s_resource(
|
||||
new_name=pg_cluster,
|
||||
objects=['holos:postgrescluster'],
|
||||
)
|
||||
|
||||
# Needed to select the database by label
|
||||
# https://docs.tilt.dev/api.html#api.k8s_custom_deploy
|
||||
k8s_custom_deploy(
|
||||
pg_svc,
|
||||
apply_cmd=['./hack/tilt/k8s-get-db-sts', pg_cluster_name],
|
||||
delete_cmd=['echo', 'Skipping delete. Object managed by custom resource.'],
|
||||
deps=[],
|
||||
)
|
||||
k8s_resource(
|
||||
pg_svc,
|
||||
port_forwards=[
|
||||
port_forward(15432, 5432, 'psql'),
|
||||
],
|
||||
resource_deps=[pg_cluster]
|
||||
)
|
||||
|
||||
|
||||
# Run tests
|
||||
local_resource(
|
||||
tests_id,
|
||||
'make test',
|
||||
allow_parallel=True,
|
||||
auto_init=False,
|
||||
deps=developer_paths,
|
||||
)
|
||||
|
||||
# Run linter
|
||||
local_resource(
|
||||
lint_id,
|
||||
'make lint',
|
||||
allow_parallel=True,
|
||||
auto_init=False,
|
||||
deps=developer_paths,
|
||||
)
|
||||
|
||||
# UI Buttons for helpful things.
|
||||
# Icons: https://fonts.google.com/icons
|
||||
os.putenv("GH_FORCE_TTY", "80%")
|
||||
cmd_button(
|
||||
'{}:go-test-failfast'.format(tests_id),
|
||||
argv=['./hack/tilt/go-test-failfast'],
|
||||
resource=tests_id,
|
||||
icon_name='quiz',
|
||||
text='Fail Fast',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:issues'.format(holos_server),
|
||||
argv=['./hack/tilt/gh-issues'],
|
||||
resource=holos_backend,
|
||||
icon_name='folder_data',
|
||||
text='Issues',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:gh-issue-view'.format(holos_server),
|
||||
argv=['./hack/tilt/gh-issue-view'],
|
||||
resource=holos_backend,
|
||||
icon_name='task',
|
||||
text='View Issue',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:get-pgdb-creds'.format(holos_server),
|
||||
argv=['./hack/tilt/get-pgdb-creds', pg_cluster_name, pg_database_name],
|
||||
resource=pg_svc,
|
||||
icon_name='lock_open_right',
|
||||
text='DB Creds',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:get-pgdb-creds'.format(pg_admin_name),
|
||||
argv=['./hack/tilt/get-pgdb-creds', pg_cluster_name, pg_database_name],
|
||||
resource=pg_admin,
|
||||
icon_name='lock_open_right',
|
||||
text='DB Creds',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:get-pgadmin-creds'.format(pg_admin_name),
|
||||
argv=['./hack/tilt/get-pgadmin-creds', pg_admin_name],
|
||||
resource=pg_admin,
|
||||
icon_name='lock_open_right',
|
||||
text='pgAdmin Login',
|
||||
)
|
||||
|
||||
print("✨ Tiltfile evaluated")
|
||||
44
api/core/v1alpha2/apiobjects.go
Normal file
44
api/core/v1alpha2/apiobjects.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package v1alpha2
|
||||
|
||||
import "google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
// Label is an arbitrary unique identifier internal to holos itself. The holos
|
||||
// cli is expected to never write a Label value to rendered output files,
|
||||
// therefore use a [Label] then the identifier must be unique and internal.
|
||||
// Defined as a type for clarity and type checking.
|
||||
//
|
||||
// A Label is useful to convert a CUE struct to a list, for example producing a list of [APIObject] resources from an [APIObjectMap]. A CUE struct using
|
||||
// Label keys is guaranteed to not lose data when rendering output because a
|
||||
// Label is expected to never be written to the final output.
|
||||
type Label string
|
||||
|
||||
// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking.
|
||||
type Kind string
|
||||
|
||||
// APIObject represents the most basic generic form of a single kubernetes api
|
||||
// object. Represented as a JSON object internally for compatibility between
|
||||
// tools, for example loading from CUE.
|
||||
type APIObject structpb.Struct
|
||||
|
||||
// APIObjectMap represents the marshalled yaml representation of kubernetes api
|
||||
// objects. Do not produce an APIObjectMap directly, instead use [APIObjects]
|
||||
// to produce the marshalled yaml representation from CUE data, then provide the
|
||||
// result to [HolosComponent].
|
||||
type APIObjectMap map[Kind]map[Label]string
|
||||
|
||||
// APIObjects represents Kubernetes API objects defined directly from CUE code.
|
||||
// Useful to mix in resources to any kind of [HolosComponent], for example
|
||||
// adding an ExternalSecret resource to a [HelmChart].
|
||||
//
|
||||
// [Kind] must be the resource kind, e.g. Deployment or Service.
|
||||
//
|
||||
// [Label] is an arbitrary internal identifier to uniquely identify the resource
|
||||
// within the context of a `holos` command. Holos will never write the
|
||||
// intermediate label to rendered output.
|
||||
//
|
||||
// Refer to [HolosComponent] which accepts an [APIObjectMap] field provided by
|
||||
// [APIObjects].
|
||||
type APIObjects struct {
|
||||
APIObjects map[Kind]map[Label]APIObject `json:"apiObjects"`
|
||||
APIObjectMap APIObjectMap `json:"apiObjectMap"`
|
||||
}
|
||||
96
api/core/v1alpha2/buildplan.go
Normal file
96
api/core/v1alpha2/buildplan.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package v1alpha2
|
||||
|
||||
// FilePath represents a file path.
|
||||
type FilePath string
|
||||
|
||||
// FileContent represents file contents.
|
||||
type FileContent string
|
||||
|
||||
// FileContentMap represents a mapping of file paths to file contents. Paths
|
||||
// are relative to the `holos` output "deploy" directory, and may contain
|
||||
// sub-directories.
|
||||
type FileContentMap map[FilePath]FileContent
|
||||
|
||||
// BuildPlan represents a build plan for the holos cli to execute. The purpose
|
||||
// of a BuildPlan is to define one or more [HolosComponent] kinds. For example a
|
||||
// [HelmChart], [KustomizeBuild], or [KubernetesObjects].
|
||||
//
|
||||
// A BuildPlan usually has an additional empty [KubernetesObjects] for the
|
||||
// purpose of using the [HolosComponent] DeployFiles field to deploy an ArgoCD
|
||||
// or Flux gitops resource for the holos component.
|
||||
type BuildPlan struct {
|
||||
Kind string `json:"kind" cue:"\"BuildPlan\""`
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
Spec BuildPlanSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// BuildPlanSpec represents the specification of the build plan.
|
||||
type BuildPlanSpec struct {
|
||||
// Disabled causes the holos cli to take no action over the [BuildPlan].
|
||||
Disabled bool `json:"disabled,omitempty"`
|
||||
// Components represents multiple [HolosComponent] kinds to manage.
|
||||
Components BuildPlanComponents `json:"components,omitempty"`
|
||||
}
|
||||
|
||||
type BuildPlanComponents struct {
|
||||
Resources map[Label]KubernetesObjects `json:"resources,omitempty"`
|
||||
KubernetesObjectsList []KubernetesObjects `json:"kubernetesObjectsList,omitempty"`
|
||||
HelmChartList []HelmChart `json:"helmChartList,omitempty"`
|
||||
KustomizeBuildList []KustomizeBuild `json:"kustomizeBuildList,omitempty"`
|
||||
}
|
||||
|
||||
// HolosComponent defines the fields common to all holos component kinds. Every
|
||||
// holos component kind should embed HolosComponent.
|
||||
type HolosComponent struct {
|
||||
// Kind is a string value representing the resource this object represents.
|
||||
Kind string `json:"kind"`
|
||||
// APIVersion represents the versioned schema of this representation of an object.
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
// Metadata represents data about the holos component such as the Name.
|
||||
Metadata Metadata `json:"metadata"`
|
||||
|
||||
// APIObjectMap holds the marshalled representation of api objects. Useful to
|
||||
// mix in resources to each HolosComponent type, for example adding an
|
||||
// ExternalSecret to a HelmChart HolosComponent. Refer to [APIObjects].
|
||||
APIObjectMap APIObjectMap `json:"apiObjectMap,omitempty"`
|
||||
|
||||
// DeployFiles represents file paths relative to the cluster deploy directory
|
||||
// with the value representing the file content. Intended for defining the
|
||||
// ArgoCD Application resource or Flux Kustomization resource from within CUE,
|
||||
// but may be used to render any file related to the build plan from CUE.
|
||||
DeployFiles FileContentMap `json:"deployFiles,omitempty"`
|
||||
|
||||
// Kustomize represents a kubectl kustomize build post-processing step.
|
||||
Kustomize `json:"kustomize,omitempty"`
|
||||
|
||||
// Skip causes holos to take no action regarding this component.
|
||||
Skip bool `json:"skip" cue:"bool | *false"`
|
||||
}
|
||||
|
||||
// Metadata represents data about the holos component such as the Name.
|
||||
type Metadata struct {
|
||||
// Name represents the name of the holos component.
|
||||
Name string `json:"name"`
|
||||
// Namespace is the primary namespace of the holos component. A holos
|
||||
// component may manage resources in multiple namespaces, in this case
|
||||
// consider setting the component namespace to default.
|
||||
//
|
||||
// This field is optional because not all resources require a namespace,
|
||||
// particularly CRD's and DeployFiles functionality.
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// Kustomize represents resources necessary to execute a kustomize build.
|
||||
// Intended for at least two use cases:
|
||||
//
|
||||
// 1. Process a [KustomizeBuild] [HolosComponent] which represents raw yaml
|
||||
// file resources in a holos component directory.
|
||||
// 2. Post process a [HelmChart] [HolosComponent] to inject istio, patch jobs,
|
||||
// add custom labels, etc...
|
||||
type Kustomize struct {
|
||||
// KustomizeFiles holds file contents for kustomize, e.g. patch files.
|
||||
KustomizeFiles FileContentMap `json:"kustomizeFiles,omitempty"`
|
||||
// ResourcesFile is the file name used for api objects in kustomization.yaml
|
||||
ResourcesFile string `json:"resourcesFile,omitempty"`
|
||||
}
|
||||
11
api/core/v1alpha2/constants.go
Normal file
11
api/core/v1alpha2/constants.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package v1alpha2
|
||||
|
||||
const (
|
||||
APIVersion = "v1alpha2"
|
||||
BuildPlanKind = "BuildPlan"
|
||||
HelmChartKind = "HelmChart"
|
||||
// ChartDir is the directory name created in the holos component directory to cache a chart.
|
||||
ChartDir = "vendor"
|
||||
// ResourcesFile is the file name used to store component output when post-processing with kustomize.
|
||||
ResourcesFile = "resources.yaml"
|
||||
)
|
||||
44
api/core/v1alpha2/core.go
Normal file
44
api/core/v1alpha2/core.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package v1alpha2
|
||||
|
||||
import "google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
type PlatformMetadata struct {
|
||||
// Name represents the Platform name.
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Platform represents a platform to manage. A Platform resource informs holos
|
||||
// which components to build. The platform resource also acts as a container
|
||||
// for the platform model form values provided by the PlatformService. The
|
||||
// primary use case is to collect the cluster names, cluster types, platform
|
||||
// model, and holos components to build into one resource.
|
||||
type Platform struct {
|
||||
// Kind is a string value representing the resource this object represents.
|
||||
Kind string `json:"kind" cue:"\"Platform\""`
|
||||
// APIVersion represents the versioned schema of this representation of an object.
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
// Metadata represents data about the object such as the Name.
|
||||
Metadata PlatformMetadata `json:"metadata"`
|
||||
|
||||
// Spec represents the specification.
|
||||
Spec PlatformSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// PlatformSpec represents the specification of a Platform. Think of a platform
|
||||
// specification as a list of platform components to apply to a list of
|
||||
// kubernetes clusters combined with the user-specified Platform Model.
|
||||
type PlatformSpec struct {
|
||||
// Model represents the platform model holos gets from from the
|
||||
// PlatformService.GetPlatform rpc method and provides to CUE using a tag.
|
||||
Model structpb.Struct `json:"model"`
|
||||
// Components represents a list of holos components to manage.
|
||||
Components []PlatformSpecComponent `json:"components"`
|
||||
}
|
||||
|
||||
// PlatformSpecComponent represents a holos component to build or render.
|
||||
type PlatformSpecComponent struct {
|
||||
// Path is the path of the component relative to the platform root.
|
||||
Path string `json:"path"`
|
||||
// Cluster is the cluster name to provide when rendering the component.
|
||||
Cluster string `json:"cluster"`
|
||||
}
|
||||
24
api/core/v1alpha2/doc.go
Normal file
24
api/core/v1alpha2/doc.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Package v1alpha2 contains the core API contract between the holos cli and CUE
|
||||
// configuration code. Platform designers, operators, and software developers
|
||||
// use this API to write configuration in CUE which `holos` loads. The overall
|
||||
// shape of the API defines imperative actions `holos` should carry out to
|
||||
// render the complete yaml that represents a Platform.
|
||||
//
|
||||
// [Platform] defines the complete configuration of a platform. With the holos
|
||||
// reference platform this takes the shape of one management cluster and at
|
||||
// least two workload cluster. Each cluster has multiple [HolosComponent]
|
||||
// resources applied to it.
|
||||
//
|
||||
// Each holos component path, e.g. `components/namespaces` produces exactly one
|
||||
// [BuildPlan] which in turn contains a set of [HolosComponent] kinds.
|
||||
//
|
||||
// The primary kinds of [HolosComponent] are:
|
||||
//
|
||||
// 1. [HelmChart] to render config from a helm chart.
|
||||
// 2. [KustomizeBuild] to render config from [Kustomize]
|
||||
// 3. [KubernetesObjects] to render [APIObjects] defined directly in CUE
|
||||
// configuration.
|
||||
//
|
||||
// Note that Holos operates as a data pipeline, so the output of a [HelmChart]
|
||||
// may be provided to [Kustomize] for post-processing.
|
||||
package v1alpha2
|
||||
38
api/core/v1alpha2/helm.go
Normal file
38
api/core/v1alpha2/helm.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package v1alpha2
|
||||
|
||||
// HelmChart represents a holos component which wraps around an upstream helm
|
||||
// chart. Holos orchestrates helm by providing values obtained from CUE,
|
||||
// renders the output using `helm template`, then post-processes the helm output
|
||||
// yaml using the general functionality provided by [HolosComponent], for
|
||||
// example [Kustomize] post-rendering and mixing in additional kubernetes api
|
||||
// objects.
|
||||
type HelmChart struct {
|
||||
HolosComponent `json:",inline"`
|
||||
Kind string `json:"kind" cue:"\"HelmChart\""`
|
||||
|
||||
// Chart represents a helm chart to manage.
|
||||
Chart Chart `json:"chart"`
|
||||
// ValuesContent represents the values.yaml file holos passes to the `helm
|
||||
// template` command.
|
||||
ValuesContent string `json:"valuesContent"`
|
||||
// EnableHooks enables helm hooks when executing the `helm template` command.
|
||||
EnableHooks bool `json:"enableHooks" cue:"bool | *false"`
|
||||
}
|
||||
|
||||
// Chart represents a helm chart.
|
||||
type Chart struct {
|
||||
// Name represents the chart name.
|
||||
Name string `json:"name"`
|
||||
// Version represents the chart version.
|
||||
Version string `json:"version"`
|
||||
// Release represents the chart release when executing helm template.
|
||||
Release string `json:"release"`
|
||||
// Repository represents the repository to fetch the chart from.
|
||||
Repository Repository `json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
// Repository represents a helm chart repository.
|
||||
type Repository struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
10
api/core/v1alpha2/kubernetesobjects.go
Normal file
10
api/core/v1alpha2/kubernetesobjects.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package v1alpha2
|
||||
|
||||
const KubernetesObjectsKind = "KubernetesObjects"
|
||||
|
||||
// KubernetesObjects represents a [HolosComponent] composed of Kubernetes API
|
||||
// objects provided directly from CUE using [APIObjects].
|
||||
type KubernetesObjects struct {
|
||||
HolosComponent `json:",inline"`
|
||||
Kind string `json:"kind" cue:"\"KubernetesObjects\""`
|
||||
}
|
||||
8
api/core/v1alpha2/kustomizebuild.go
Normal file
8
api/core/v1alpha2/kustomizebuild.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package v1alpha2
|
||||
|
||||
// KustomizeBuild represents a [HolosComponent] that renders plain yaml files in
|
||||
// the holos component directory using `kubectl kustomize build`.
|
||||
type KustomizeBuild struct {
|
||||
HolosComponent `json:",inline"`
|
||||
Kind string `json:"kind" cue:"\"KustomizeBuild\""`
|
||||
}
|
||||
37
api/meta/v1alpha2/meta.go
Normal file
37
api/meta/v1alpha2/meta.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package v1alpha2
|
||||
|
||||
// TypeMeta describes an individual object in an API response or request with
|
||||
// strings representing the type of the object and its API schema version.
|
||||
// Structures that are versioned or persisted should inline TypeMeta.
|
||||
type TypeMeta struct {
|
||||
// Kind is a string value representing the resource this object represents.
|
||||
Kind string `json:"kind"`
|
||||
// APIVersion defines the versioned schema of this representation of an object.
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
}
|
||||
|
||||
func (tm *TypeMeta) GetKind() string {
|
||||
return tm.Kind
|
||||
}
|
||||
|
||||
func (tm *TypeMeta) GetAPIVersion() string {
|
||||
return tm.APIVersion
|
||||
}
|
||||
|
||||
// Discriminator discriminates the kind of an api object.
|
||||
type Discriminator interface {
|
||||
// GetKind returns Kind.
|
||||
GetKind() string
|
||||
// GetAPIVersion returns APIVersion.
|
||||
GetAPIVersion() string
|
||||
}
|
||||
|
||||
// ObjectMeta represents metadata of a holos component object. The fields are a
|
||||
// copy of upstream kubernetes api machinery but are holos objects distinct from
|
||||
// kubernetes api objects.
|
||||
type ObjectMeta struct {
|
||||
// Name uniquely identifies the holos component instance and must be suitable as a file name.
|
||||
Name string `json:"name,omitempty"`
|
||||
// Namespace confines a holos component to a single namespace via kustomize if set.
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
55
api/v1alpha1/buildplan.go
Normal file
55
api/v1alpha1/buildplan.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BuildPlan is the primary interface between CUE and the Holos cli.
|
||||
type BuildPlan struct {
|
||||
TypeMeta `json:",inline" yaml:",inline"`
|
||||
// Metadata represents the holos component name
|
||||
Metadata ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
Spec BuildPlanSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type BuildPlanSpec struct {
|
||||
Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"`
|
||||
Components BuildPlanComponents `json:"components,omitempty" yaml:"components,omitempty"`
|
||||
// DeployFiles keys represent file paths relative to the cluster deploy
|
||||
// directory. Map values represent the string encoded file contents. Used to
|
||||
// write the argocd Application, but may be used to render any file from CUE.
|
||||
DeployFiles FileContentMap `json:"deployFiles,omitempty" yaml:"deployFiles,omitempty"`
|
||||
}
|
||||
|
||||
type BuildPlanComponents struct {
|
||||
HelmChartList []HelmChart `json:"helmChartList,omitempty" yaml:"helmChartList,omitempty"`
|
||||
KubernetesObjectsList []KubernetesObjects `json:"kubernetesObjectsList,omitempty" yaml:"kubernetesObjectsList,omitempty"`
|
||||
KustomizeBuildList []KustomizeBuild `json:"kustomizeBuildList,omitempty" yaml:"kustomizeBuildList,omitempty"`
|
||||
Resources map[string]KubernetesObjects `json:"resources,omitempty" yaml:"resources,omitempty"`
|
||||
}
|
||||
|
||||
func (bp *BuildPlan) Validate() error {
|
||||
errs := make([]string, 0, 2)
|
||||
if bp.Kind != BuildPlanKind {
|
||||
errs = append(errs, fmt.Sprintf("kind invalid: want: %s have: %s", BuildPlanKind, bp.Kind))
|
||||
}
|
||||
if bp.APIVersion != APIVersion {
|
||||
errs = append(errs, fmt.Sprintf("apiVersion invalid: want: %s have: %s", APIVersion, bp.APIVersion))
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("invalid BuildPlan: " + strings.Join(errs, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *BuildPlan) ResultCapacity() (count int) {
|
||||
if bp == nil {
|
||||
return 0
|
||||
}
|
||||
count = len(bp.Spec.Components.HelmChartList) +
|
||||
len(bp.Spec.Components.KubernetesObjectsList) +
|
||||
len(bp.Spec.Components.KustomizeBuildList) +
|
||||
len(bp.Spec.Components.Resources)
|
||||
return count
|
||||
}
|
||||
30
api/v1alpha1/component.go
Normal file
30
api/v1alpha1/component.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package v1alpha1
|
||||
|
||||
// HolosComponent defines the fields common to all holos component kinds including the Render Result.
|
||||
type HolosComponent struct {
|
||||
TypeMeta `json:",inline" yaml:",inline"`
|
||||
// Metadata represents the holos component name
|
||||
Metadata ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
// APIObjectMap holds the marshalled representation of api objects. Think of
|
||||
// these as resources overlaid at the back of the render pipeline.
|
||||
APIObjectMap APIObjectMap `json:"apiObjectMap,omitempty" yaml:"apiObjectMap,omitempty"`
|
||||
// Kustomization holds the marshalled representation of the flux kustomization
|
||||
// which reconciles resources in git with the api server.
|
||||
Kustomization `json:",inline" yaml:",inline"`
|
||||
// Kustomize represents a kubectl kustomize build post-processing step.
|
||||
Kustomize `json:",inline" yaml:",inline"`
|
||||
// Skip causes holos to take no action regarding the component.
|
||||
Skip bool
|
||||
}
|
||||
|
||||
func (hc *HolosComponent) NewResult() *Result {
|
||||
return &Result{HolosComponent: *hc}
|
||||
}
|
||||
|
||||
func (hc *HolosComponent) GetAPIVersion() string {
|
||||
return hc.APIVersion
|
||||
}
|
||||
|
||||
func (hc *HolosComponent) GetKind() string {
|
||||
return hc.Kind
|
||||
}
|
||||
11
api/v1alpha1/constants.go
Normal file
11
api/v1alpha1/constants.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package v1alpha1
|
||||
|
||||
const (
|
||||
APIVersion = "holos.run/v1alpha1"
|
||||
BuildPlanKind = "BuildPlan"
|
||||
HelmChartKind = "HelmChart"
|
||||
// ChartDir is the directory name created in the holos component directory to cache a chart.
|
||||
ChartDir = "vendor"
|
||||
// ResourcesFile is the file name used to store component output when post-processing with kustomize.
|
||||
ResourcesFile = "resources.yaml"
|
||||
)
|
||||
2
api/v1alpha1/doc.go
Normal file
2
api/v1alpha1/doc.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package v1alpha1 defines the api boundary between CUE and Holos.
|
||||
package v1alpha1
|
||||
13
api/v1alpha1/form.go
Normal file
13
api/v1alpha1/form.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package v1alpha1
|
||||
|
||||
import object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
|
||||
|
||||
// Form represents a collection of Formly json powered form.
|
||||
type Form struct {
|
||||
TypeMeta `json:",inline" yaml:",inline"`
|
||||
Spec FormSpec `json:"spec" yaml:"spec"`
|
||||
}
|
||||
|
||||
type FormSpec struct {
|
||||
Form object.Form `json:"form" yaml:"form"`
|
||||
}
|
||||
184
api/v1alpha1/helm.go
Normal file
184
api/v1alpha1/helm.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/util"
|
||||
)
|
||||
|
||||
// A HelmChart represents a helm command to provide chart values in order to render kubernetes api objects.
|
||||
type HelmChart struct {
|
||||
HolosComponent `json:",inline" yaml:",inline"`
|
||||
// Namespace is the namespace to install into. TODO: Use metadata.namespace instead.
|
||||
Namespace string `json:"namespace"`
|
||||
Chart Chart `json:"chart"`
|
||||
ValuesContent string `json:"valuesContent"`
|
||||
EnableHooks bool `json:"enableHooks"`
|
||||
}
|
||||
|
||||
type Chart struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Release string `json:"release"`
|
||||
Repository Repository `json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
func (hc *HelmChart) Render(ctx context.Context, path holos.InstancePath) (*Result, error) {
|
||||
result := Result{HolosComponent: hc.HolosComponent}
|
||||
if err := hc.helm(ctx, &result, path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.addObjectMap(ctx, hc.APIObjectMap)
|
||||
if err := result.kustomize(ctx); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not kustomize: %w", err))
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// runHelm provides the values produced by CUE to helm template and returns
|
||||
// the rendered kubernetes api objects in the result.
|
||||
func (hc *HelmChart) helm(ctx context.Context, r *Result, path holos.InstancePath) error {
|
||||
log := logger.FromContext(ctx).With("chart", hc.Chart.Name)
|
||||
if hc.Chart.Name == "" {
|
||||
log.WarnContext(ctx, "skipping helm: no chart name specified, use a different component type")
|
||||
return nil
|
||||
}
|
||||
|
||||
cachedChartPath := filepath.Join(string(path), ChartDir, filepath.Base(hc.Chart.Name))
|
||||
if isNotExist(cachedChartPath) {
|
||||
// Add repositories
|
||||
repo := hc.Chart.Repository
|
||||
if repo.URL != "" {
|
||||
out, err := util.RunCmd(ctx, "helm", "repo", "add", repo.Name, repo.URL)
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, "could not run helm", "stderr", out.Stderr.String(), "stdout", out.Stdout.String())
|
||||
return errors.Wrap(fmt.Errorf("could not run helm repo add: %w", err))
|
||||
}
|
||||
// Update repository
|
||||
out, err = util.RunCmd(ctx, "helm", "repo", "update", repo.Name)
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, "could not run helm", "stderr", out.Stderr.String(), "stdout", out.Stdout.String())
|
||||
return errors.Wrap(fmt.Errorf("could not run helm repo update: %w", err))
|
||||
}
|
||||
} else {
|
||||
log.DebugContext(ctx, "no chart repository url proceeding assuming oci chart")
|
||||
}
|
||||
|
||||
// Cache the chart
|
||||
if err := cacheChart(ctx, path, ChartDir, hc.Chart); err != nil {
|
||||
return fmt.Errorf("could not cache chart: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Write values file
|
||||
tempDir, err := os.MkdirTemp("", "holos")
|
||||
if err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not make temp dir: %w", err))
|
||||
}
|
||||
defer util.Remove(ctx, tempDir)
|
||||
|
||||
valuesPath := filepath.Join(tempDir, "values.yaml")
|
||||
if err := os.WriteFile(valuesPath, []byte(hc.ValuesContent), 0644); err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not write values: %w", err))
|
||||
}
|
||||
log.DebugContext(ctx, "helm: wrote values", "path", valuesPath, "bytes", len(hc.ValuesContent))
|
||||
|
||||
// Run charts
|
||||
chart := hc.Chart
|
||||
args := []string{"template"}
|
||||
if !hc.EnableHooks {
|
||||
args = append(args, "--no-hooks")
|
||||
}
|
||||
namespace := hc.Namespace
|
||||
args = append(args, "--include-crds", "--values", valuesPath, "--namespace", namespace, "--kubeconfig", "/dev/null", "--version", chart.Version, chart.Release, cachedChartPath)
|
||||
helmOut, err := util.RunCmd(ctx, "helm", args...)
|
||||
if err != nil {
|
||||
stderr := helmOut.Stderr.String()
|
||||
lines := strings.Split(stderr, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "Error:") {
|
||||
err = fmt.Errorf("%s: %w", line, err)
|
||||
}
|
||||
}
|
||||
return errors.Wrap(fmt.Errorf("could not run helm template: %w", err))
|
||||
}
|
||||
|
||||
r.accumulatedOutput = helmOut.Stdout.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cacheChart stores a cached copy of Chart in the chart subdirectory of path.
|
||||
//
|
||||
// It is assumed that the only method responsible for writing to chartDir is
|
||||
// cacheChart itself.
|
||||
//
|
||||
// This relies on the atomicity of moving temporary directories into place on
|
||||
// the same filesystem via os.Rename. If a syscall.EEXIST error occurs during
|
||||
// renaming, it indicates that the cached chart already exists, which is an
|
||||
// expected scenario when this function is called concurrently.
|
||||
func cacheChart(ctx context.Context, path holos.InstancePath, chartDir string, chart Chart) error {
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
cacheTemp, err := os.MkdirTemp(string(path), chartDir)
|
||||
if err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not make temp dir: %w", err))
|
||||
}
|
||||
defer util.Remove(ctx, cacheTemp)
|
||||
|
||||
chartName := chart.Name
|
||||
if chart.Repository.Name != "" {
|
||||
chartName = fmt.Sprintf("%s/%s", chart.Repository.Name, chart.Name)
|
||||
}
|
||||
helmOut, err := util.RunCmd(ctx, "helm", "pull", "--destination", cacheTemp, "--untar=true", "--version", chart.Version, chartName)
|
||||
if err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not run helm pull: %w", err))
|
||||
}
|
||||
log.Debug("helm pull", "stdout", helmOut.Stdout, "stderr", helmOut.Stderr)
|
||||
|
||||
cachePath := filepath.Join(string(path), chartDir)
|
||||
|
||||
if err := os.MkdirAll(cachePath, 0777); err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not mkdir: %w", err))
|
||||
}
|
||||
|
||||
items, err := os.ReadDir(cacheTemp)
|
||||
if err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not read directory: %w", err))
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
src := filepath.Join(cacheTemp, item.Name())
|
||||
dst := filepath.Join(cachePath, item.Name())
|
||||
log.DebugContext(ctx, "rename", "src", src, "dst", dst)
|
||||
if err := os.Rename(src, dst); err != nil {
|
||||
var linkErr *os.LinkError
|
||||
if errors.As(err, &linkErr) && errors.Is(linkErr.Err, syscall.EEXIST) {
|
||||
log.DebugContext(ctx, "cache already exists", "chart", chart.Name, "chart_version", chart.Version, "path", cachePath)
|
||||
} else {
|
||||
return errors.Wrap(fmt.Errorf("could not rename: %w", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "cached", "chart", chart.Name, "chart_version", chart.Version, "path", cachePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
func isNotExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return os.IsNotExist(err)
|
||||
}
|
||||
21
api/v1alpha1/kubernetesobjects.go
Normal file
21
api/v1alpha1/kubernetesobjects.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
)
|
||||
|
||||
const KubernetesObjectsKind = "KubernetesObjects"
|
||||
|
||||
// KubernetesObjects represents CUE output which directly provides Kubernetes api objects to holos.
|
||||
type KubernetesObjects struct {
|
||||
HolosComponent `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
// Render produces kubernetes api objects from the APIObjectMap
|
||||
func (o *KubernetesObjects) Render(ctx context.Context, path holos.InstancePath) (*Result, error) {
|
||||
result := Result{HolosComponent: o.HolosComponent}
|
||||
result.addObjectMap(ctx, o.APIObjectMap)
|
||||
return &result, nil
|
||||
}
|
||||
7
api/v1alpha1/kustomization.go
Normal file
7
api/v1alpha1/kustomization.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package v1alpha1
|
||||
|
||||
// Kustomization holds the rendered flux kustomization api object content for git ops.
|
||||
type Kustomization struct {
|
||||
// KsContent is the yaml representation of the flux kustomization for gitops.
|
||||
KsContent string `json:"ksContent,omitempty" yaml:"ksContent,omitempty"`
|
||||
}
|
||||
47
api/v1alpha1/kustomize.go
Normal file
47
api/v1alpha1/kustomize.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/util"
|
||||
)
|
||||
|
||||
const KustomizeBuildKind = "KustomizeBuild"
|
||||
|
||||
// Kustomize represents resources necessary to execute a kustomize build.
|
||||
// Intended for at least two use cases:
|
||||
//
|
||||
// 1. Process raw yaml file resources in a holos component directory.
|
||||
// 2. Post process a HelmChart to inject istio, add custom labels, etc...
|
||||
type Kustomize struct {
|
||||
// KustomizeFiles holds file contents for kustomize, e.g. patch files.
|
||||
KustomizeFiles FileContentMap `json:"kustomizeFiles,omitempty" yaml:"kustomizeFiles,omitempty"`
|
||||
// ResourcesFile is the file name used for api objects in kustomization.yaml
|
||||
ResourcesFile string `json:"resourcesFile,omitempty" yaml:"resourcesFile,omitempty"`
|
||||
}
|
||||
|
||||
// KustomizeBuild renders plain yaml files in the holos component directory using kubectl kustomize build.
|
||||
type KustomizeBuild struct {
|
||||
HolosComponent `json:",inline" yaml:",inline"`
|
||||
}
|
||||
|
||||
// Render produces a Result by executing kubectl kustomize on the holos
|
||||
// component path. Useful for processing raw yaml files.
|
||||
func (kb *KustomizeBuild) Render(ctx context.Context, path holos.InstancePath) (*Result, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
result := Result{HolosComponent: kb.HolosComponent}
|
||||
// Run kustomize.
|
||||
kOut, err := util.RunCmd(ctx, "kubectl", "kustomize", string(path))
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, kOut.Stderr.String())
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
// Replace the accumulated output
|
||||
result.accumulatedOutput = kOut.Stdout.String()
|
||||
// Add CUE based api objects.
|
||||
result.addObjectMap(ctx, kb.APIObjectMap)
|
||||
return &result, nil
|
||||
}
|
||||
14
api/v1alpha1/objectmap.go
Normal file
14
api/v1alpha1/objectmap.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package v1alpha1
|
||||
|
||||
// Label is an arbitrary unique identifier. Defined as a type for clarity and type checking.
|
||||
type Label string
|
||||
|
||||
// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking.
|
||||
type Kind string
|
||||
|
||||
// APIObjectMap is the shape of marshalled api objects returned from cue to the
|
||||
// holos cli. A map is used to improve the clarity of error messages from cue.
|
||||
type APIObjectMap map[Kind]map[Label]string
|
||||
|
||||
// FileContentMap is a map of file names to file contents.
|
||||
type FileContentMap map[string]string
|
||||
15
api/v1alpha1/objectmeta.go
Normal file
15
api/v1alpha1/objectmeta.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package v1alpha1
|
||||
|
||||
// ObjectMeta represents metadata of a holos component object. The fields are a
|
||||
// copy of upstream kubernetes api machinery but are by holos objects distinct
|
||||
// from kubernetes api objects.
|
||||
type ObjectMeta struct {
|
||||
// Name uniquely identifies the holos component instance and must be suitable as a file name.
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
// Namespace confines a holos component to a single namespace via kustomize if set.
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
// Labels are not used but are copied from api machinery ObjectMeta for completeness.
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
// Annotations are not used but are copied from api machinery ObjectMeta for completeness.
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
}
|
||||
32
api/v1alpha1/platform.go
Normal file
32
api/v1alpha1/platform.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package v1alpha1
|
||||
|
||||
import "google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
// Platform represents a platform to manage. A Platform resource informs holos
|
||||
// which components to build. The platform resource also acts as a container
|
||||
// for the platform model form values provided by the PlatformService. The
|
||||
// primary use case is to collect the cluster names, cluster types, platform
|
||||
// model, and holos components to build into one resource.
|
||||
type Platform struct {
|
||||
TypeMeta `json:",inline" yaml:",inline"`
|
||||
Metadata ObjectMeta `json:"metadata" yaml:"metadata"`
|
||||
Spec PlatformSpec `json:"spec" yaml:"spec"`
|
||||
}
|
||||
|
||||
// PlatformSpec represents the platform build plan specification.
|
||||
type PlatformSpec struct {
|
||||
// Model represents the platform model holos gets from from the
|
||||
// holos.platform.v1alpha1.PlatformService.GetPlatform method and provides to
|
||||
// CUE using a tag.
|
||||
Model structpb.Struct `json:"model" yaml:"model"`
|
||||
Components []PlatformSpecComponent `json:"components" yaml:"components"`
|
||||
}
|
||||
|
||||
// PlatformSpecComponent represents a component to build or render with flags to
|
||||
// pass, for example the cluster name.
|
||||
type PlatformSpecComponent struct {
|
||||
// Path is the path of the component relative to the platform root.
|
||||
Path string `json:"path" yaml:"path"`
|
||||
// Cluster is the cluster name to use when building the component.
|
||||
Cluster string `json:"cluster" yaml:"cluster"`
|
||||
}
|
||||
22
api/v1alpha1/render.go
Normal file
22
api/v1alpha1/render.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
)
|
||||
|
||||
type Renderer interface {
|
||||
GetKind() string
|
||||
Render(ctx context.Context, path holos.InstancePath) (*Result, error)
|
||||
}
|
||||
|
||||
// Render produces a Result representing the kubernetes api objects to
|
||||
// configure. Each of the various holos component types, e.g. Helm, Kustomize,
|
||||
// et al, should implement the Renderer interface. This process is best
|
||||
// conceptualized as a data pipeline, for example a component may render a
|
||||
// result by first calling helm template, then passing the result through
|
||||
// kustomize, then mixing in overlay api objects.
|
||||
func Render(ctx context.Context, r Renderer, path holos.InstancePath) (*Result, error) {
|
||||
return r.Render(ctx, path)
|
||||
}
|
||||
165
api/v1alpha1/result.go
Normal file
165
api/v1alpha1/result.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/util"
|
||||
)
|
||||
|
||||
// Result is the build result for display or writing. Holos components Render the Result as a data pipeline.
|
||||
type Result struct {
|
||||
HolosComponent
|
||||
// accumulatedOutput accumulates rendered api objects.
|
||||
accumulatedOutput string
|
||||
// DeployFiles keys represent file paths relative to the cluster deploy
|
||||
// directory. Map values represent the string encoded file contents. Used to
|
||||
// write the argocd Application, but may be used to render any file from CUE.
|
||||
DeployFiles FileContentMap `json:"deployFiles,omitempty" yaml:"deployFiles,omitempty"`
|
||||
}
|
||||
|
||||
// Continue returns true if Skip is true indicating the result is to be skipped over.
|
||||
func (r *Result) Continue() bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return r.Skip
|
||||
}
|
||||
|
||||
func (r *Result) Name() string {
|
||||
return r.Metadata.Name
|
||||
}
|
||||
|
||||
func (r *Result) Filename(writeTo string, cluster string) string {
|
||||
name := r.Metadata.Name
|
||||
return filepath.Join(writeTo, "clusters", cluster, "components", name, name+".gen.yaml")
|
||||
}
|
||||
|
||||
func (r *Result) KustomizationFilename(writeTo string, cluster string) string {
|
||||
return filepath.Join(writeTo, "clusters", cluster, "holos", "components", r.Metadata.Name+"-kustomization.gen.yaml")
|
||||
}
|
||||
|
||||
// AccumulatedOutput returns the accumulated rendered output.
|
||||
func (r *Result) AccumulatedOutput() string {
|
||||
return r.accumulatedOutput
|
||||
}
|
||||
|
||||
// addObjectMap renders the provided APIObjectMap into the accumulated output.
|
||||
func (r *Result) addObjectMap(ctx context.Context, objectMap APIObjectMap) {
|
||||
log := logger.FromContext(ctx)
|
||||
b := []byte(r.AccumulatedOutput())
|
||||
kinds := make([]Kind, 0, len(objectMap))
|
||||
// Sort the keys
|
||||
for kind := range objectMap {
|
||||
kinds = append(kinds, kind)
|
||||
}
|
||||
slices.Sort(kinds)
|
||||
|
||||
for _, kind := range kinds {
|
||||
v := objectMap[kind]
|
||||
// Sort the keys
|
||||
names := make([]Label, 0, len(v))
|
||||
for name := range v {
|
||||
names = append(names, name)
|
||||
}
|
||||
slices.Sort(names)
|
||||
|
||||
for _, name := range names {
|
||||
yamlString := v[name]
|
||||
log.Debug(fmt.Sprintf("%s/%s", kind, name), "kind", kind, "name", name)
|
||||
b = util.EnsureNewline(b)
|
||||
header := fmt.Sprintf("---\n# Source: CUE apiObjects.%s.%s\n", kind, name)
|
||||
b = append(b, []byte(header+yamlString)...)
|
||||
b = util.EnsureNewline(b)
|
||||
}
|
||||
}
|
||||
r.accumulatedOutput = string(b)
|
||||
}
|
||||
|
||||
// kustomize replaces the accumulated output with the output of kustomize build
|
||||
func (r *Result) kustomize(ctx context.Context) error {
|
||||
log := logger.FromContext(ctx)
|
||||
if r.ResourcesFile == "" {
|
||||
log.DebugContext(ctx, "skipping kustomize: no resourcesFile")
|
||||
return nil
|
||||
}
|
||||
if len(r.KustomizeFiles) < 1 {
|
||||
log.DebugContext(ctx, "skipping kustomize: no kustomizeFiles")
|
||||
return nil
|
||||
}
|
||||
tempDir, err := os.MkdirTemp("", "holos.kustomize")
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
defer util.Remove(ctx, tempDir)
|
||||
|
||||
// Write the main api object resources file for kustomize.
|
||||
target := filepath.Join(tempDir, r.ResourcesFile)
|
||||
b := []byte(r.AccumulatedOutput())
|
||||
b = util.EnsureNewline(b)
|
||||
if err := os.WriteFile(target, b, 0644); err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not write resources: %w", err))
|
||||
}
|
||||
log.DebugContext(ctx, "wrote: "+target, "op", "write", "path", target, "bytes", len(b))
|
||||
|
||||
// Write the kustomization tree, kustomization.yaml must be in this map for kustomize to work.
|
||||
for file, content := range r.KustomizeFiles {
|
||||
target := filepath.Join(tempDir, file)
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
b := []byte(content)
|
||||
b = util.EnsureNewline(b)
|
||||
if err := os.WriteFile(target, b, 0644); err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not write: %w", err))
|
||||
}
|
||||
log.DebugContext(ctx, "wrote: "+target, "op", "write", "path", target, "bytes", len(b))
|
||||
}
|
||||
|
||||
// Run kustomize.
|
||||
kOut, err := util.RunCmd(ctx, "kubectl", "kustomize", tempDir)
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, kOut.Stderr.String())
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Replace the accumulated output
|
||||
r.accumulatedOutput = kOut.Stdout.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Result) WriteDeployFiles(ctx context.Context, path string) error {
|
||||
log := logger.FromContext(ctx)
|
||||
if len(r.DeployFiles) == 0 {
|
||||
return nil
|
||||
}
|
||||
for k, content := range r.DeployFiles {
|
||||
path := filepath.Join(path, k)
|
||||
if err := r.Save(ctx, path, content); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.InfoContext(ctx, "wrote deploy file", "path", path, "bytes", len(content))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save writes the content to the filesystem for git ops.
|
||||
func (r *Result) Save(ctx context.Context, path string, content string) error {
|
||||
log := logger.FromContext(ctx)
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, os.FileMode(0775)); err != nil {
|
||||
log.WarnContext(ctx, "could not mkdir", "path", dir, "err", err)
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Write the file content
|
||||
if err := os.WriteFile(path, []byte(content), os.FileMode(0644)); err != nil {
|
||||
log.WarnContext(ctx, "could not write", "path", path, "err", err)
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.DebugContext(ctx, "out: wrote "+path, "action", "write", "path", path, "status", "ok")
|
||||
return nil
|
||||
}
|
||||
20
api/v1alpha1/typemeta.go
Normal file
20
api/v1alpha1/typemeta.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package v1alpha1
|
||||
|
||||
type TypeMeta struct {
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
|
||||
}
|
||||
|
||||
func (tm *TypeMeta) GetKind() string {
|
||||
return tm.Kind
|
||||
}
|
||||
|
||||
func (tm *TypeMeta) GetAPIVersion() string {
|
||||
return tm.APIVersion
|
||||
}
|
||||
|
||||
// Discriminator is an interface to discriminate the kind api object.
|
||||
type Discriminator interface {
|
||||
GetKind() string
|
||||
GetAPIVersion() string
|
||||
}
|
||||
20
buf.gen.yaml
Normal file
20
buf.gen.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
# Generates gRPC and ConnectRPC bindings for Go and TypeScript
|
||||
#
|
||||
# Note: protoc-gen-connect-query is the primary method of wiring up the React
|
||||
# frontend.
|
||||
version: v1
|
||||
plugins:
|
||||
- plugin: go
|
||||
out: service/gen
|
||||
opt: paths=source_relative
|
||||
- plugin: connect-go
|
||||
out: service/gen
|
||||
opt: paths=source_relative
|
||||
- plugin: es
|
||||
out: internal/frontend/holos/src/app/gen
|
||||
opt:
|
||||
- target=ts
|
||||
- plugin: connect-es
|
||||
out: internal/frontend/holos/src/app/gen
|
||||
opt:
|
||||
- target=ts
|
||||
8
buf.lock
Normal file
8
buf.lock
Normal file
@@ -0,0 +1,8 @@
|
||||
# Generated by buf. DO NOT EDIT.
|
||||
version: v1
|
||||
deps:
|
||||
- remote: buf.build
|
||||
owner: bufbuild
|
||||
repository: protovalidate
|
||||
commit: b983156c5e994cc9892e0ce3e64e17e0
|
||||
digest: shake256:fb47a62989d38c2529bcc5cd86ded43d800eb84cee82b42b9e8a9e815d4ee8134a0fb9d0ce8299b27c2d2bbb7d6ade0c4ad5a8a4d467e1e2c7ca619ae9f634e2
|
||||
3
buf.work.yaml
Normal file
3
buf.work.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
version: v1
|
||||
directories:
|
||||
- service
|
||||
@@ -1,8 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/holos-run/holos/pkg/cli"
|
||||
"os"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/holos-run/holos/pkg/cli"
|
||||
"github.com/rogpeppe/go-internal/testscript"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli"
|
||||
"github.com/rogpeppe/go-internal/testscript"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
||||
32
cmd/holos/testdata/constraints.txt
vendored
32
cmd/holos/testdata/constraints.txt
vendored
@@ -1,8 +1,9 @@
|
||||
# Want support for intermediary constraints
|
||||
exec holos build ./foo/... --log-level debug
|
||||
stdout '^bf2bc7f9-9ba0-4f9e-9bd2-9a205627eb0b$'
|
||||
stderr 'processing holos component kind Skip'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- foo/constraints.cue --
|
||||
@@ -12,31 +13,22 @@ metadata: name: "jeff"
|
||||
-- foo/bar/bar.cue --
|
||||
package holos
|
||||
|
||||
#KubernetesObjects & {
|
||||
apiObjectMap: foo: bar: "bf2bc7f9-9ba0-4f9e-9bd2-9a205627eb0b"
|
||||
}
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
apiObjectMap: foo: bar: "bf2bc7f9-9ba0-4f9e-9bd2-9a205627eb0b"
|
||||
}
|
||||
]
|
||||
-- schema.cue --
|
||||
package holos
|
||||
|
||||
cluster: string @tag(cluster, string)
|
||||
|
||||
// #OutputTypeMeta is shared among all output types
|
||||
#OutputTypeMeta: {
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: #KubernetesObjects.kind | #NoOutput.kind
|
||||
metadata: name: string
|
||||
}
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
#KubernetesObjects: {
|
||||
#OutputTypeMeta
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "KubernetesObjects"
|
||||
apiObjectMap: {...}
|
||||
}
|
||||
|
||||
#NoOutput: {
|
||||
#OutputTypeMeta
|
||||
kind: string | *"Skip"
|
||||
metadata: name: string | *"skipped"
|
||||
}
|
||||
|
||||
#NoOutput & {}
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "BuildPlan"
|
||||
|
||||
18
cmd/holos/testdata/issue15_cue_errors.txt
vendored
18
cmd/holos/testdata/issue15_cue_errors.txt
vendored
@@ -1,16 +1,20 @@
|
||||
# Want cue errors to show files and lines
|
||||
! exec holos build .
|
||||
stderr '^apiObjectMap.foo.bar: cannot convert non-concrete value string'
|
||||
stderr '/component.cue:7:20$'
|
||||
stderr 'apiObjectMap.foo.bar: cannot convert incomplete value'
|
||||
stderr '/component.cue:\d+:\d+$'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
package holos
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "KubernetesObjects"
|
||||
cluster: string @tag(cluster, string)
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
apiObjectMap: foo: bar: baz
|
||||
baz: string
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "BuildPlan"
|
||||
spec: components: KubernetesObjectsList: [{apiObjectMap: foo: bar: _baz}]
|
||||
|
||||
_baz: string
|
||||
|
||||
12
cmd/holos/testdata/issue25_apiobjects_cue.txt
vendored
12
cmd/holos/testdata/issue25_apiobjects_cue.txt
vendored
@@ -3,21 +3,26 @@ exec holos build .
|
||||
stdout '^kind: SecretStore$'
|
||||
stdout '# Source: CUE apiObjects.SecretStore.default'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
package holos
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "KubernetesObjects"
|
||||
cluster: string @tag(cluster, string)
|
||||
kind: "BuildPlan"
|
||||
spec: components: KubernetesObjectsList: [{apiObjectMap: #APIObjects.apiObjectMap}]
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
#SecretStore: {
|
||||
kind: string
|
||||
metadata: name: string
|
||||
}
|
||||
|
||||
#APIObjects & {
|
||||
#APIObjects: {
|
||||
apiObjects: {
|
||||
SecretStore: {
|
||||
default: #SecretStore & { metadata: name: "default" }
|
||||
@@ -54,4 +59,3 @@ import "encoding/yaml"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
cmd/holos/testdata/issue25_apiobjects_helm.txt
vendored
12
cmd/holos/testdata/issue25_apiobjects_helm.txt
vendored
@@ -4,21 +4,26 @@ stdout '^kind: SecretStore$'
|
||||
stdout '# Source: CUE apiObjects.SecretStore.default'
|
||||
stderr 'skipping helm: no chart name specified'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
package holos
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "HelmChart"
|
||||
cluster: string @tag(cluster, string)
|
||||
kind: "BuildPlan"
|
||||
spec: components: HelmChartList: [{apiObjectMap: #APIObjects.apiObjectMap}]
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
#SecretStore: {
|
||||
kind: string
|
||||
metadata: name: string
|
||||
}
|
||||
|
||||
#APIObjects & {
|
||||
#APIObjects: {
|
||||
apiObjects: {
|
||||
SecretStore: {
|
||||
default: #SecretStore & { metadata: name: "default" }
|
||||
@@ -55,4 +60,3 @@ import "encoding/yaml"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
! exec holos build .
|
||||
stderr 'apiObjects.secretstore.default.foo: field not allowed'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
@@ -10,6 +12,7 @@ package holos
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "KubernetesObjects"
|
||||
cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
#SecretStore: {
|
||||
metadata: name: string
|
||||
|
||||
35
cmd/holos/testdata/issue33_helm_stderr.txt
vendored
35
cmd/holos/testdata/issue33_helm_stderr.txt
vendored
@@ -2,26 +2,35 @@
|
||||
! exec holos build .
|
||||
stderr 'Error: execution error at \(zitadel/templates/secret_zitadel-masterkey.yaml:2:4\): Either set .Values.zitadel.masterkey xor .Values.zitadel.masterkeySecretName'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- zitadel.cue --
|
||||
package holos
|
||||
|
||||
cluster: string @tag(cluster, string)
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "HelmChart"
|
||||
metadata: name: "zitadel"
|
||||
namespace: "zitadel"
|
||||
chart: {
|
||||
name: "zitadel"
|
||||
version: "7.9.0"
|
||||
repository: {
|
||||
name: "zitadel"
|
||||
url: "https://charts.zitadel.com"
|
||||
}
|
||||
}
|
||||
kind: "BuildPlan"
|
||||
spec: components: HelmChartList: [_HelmChart]
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
_HelmChart: {
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "HelmChart"
|
||||
metadata: name: "zitadel"
|
||||
namespace: "zitadel"
|
||||
chart: {
|
||||
name: "zitadel"
|
||||
version: "7.9.0"
|
||||
release: name
|
||||
repository: {
|
||||
name: "zitadel"
|
||||
url: "https://charts.zitadel.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- vendor/zitadel/templates/secret_zitadel-masterkey.yaml --
|
||||
{{- if (or (and .Values.zitadel.masterkey .Values.zitadel.masterkeySecretName) (and (not .Values.zitadel.masterkey) (not .Values.zitadel.masterkeySecretName)) ) }}
|
||||
|
||||
39
cmd/holos/testdata/issue42_kustomize_build_kind.txt
vendored
Normal file
39
cmd/holos/testdata/issue42_kustomize_build_kind.txt
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Kustomize is a supported holos component kind
|
||||
exec holos render component --cluster-name=mycluster . --log-level=debug
|
||||
|
||||
# Want generated output
|
||||
cmp want.yaml deploy/clusters/mycluster/components/kstest/kstest.gen.yaml
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
package holos
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "BuildPlan"
|
||||
spec: components: KustomizeBuildList: [{metadata: name: "kstest"}]
|
||||
|
||||
-- kustomization.yaml --
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: mynamespace
|
||||
resources:
|
||||
- serviceaccount.yaml
|
||||
|
||||
-- serviceaccount.yaml --
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: test
|
||||
|
||||
-- want.yaml --
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: test
|
||||
namespace: mynamespace
|
||||
17
cmd/holos/testdata/issue72_disallow_unknown_fields.txt
vendored
Normal file
17
cmd/holos/testdata/issue72_disallow_unknown_fields.txt
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# https://github.com/holos-run/holos/issues/72
|
||||
# Want holos to fail on unknown fields to catch typos and aid refactors
|
||||
! exec holos build .
|
||||
stderr 'unknown field \\"TypoKubernetesObjectsList\\"'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
package holos
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "BuildPlan"
|
||||
spec: components: TypoKubernetesObjectsList: []
|
||||
2
cmd/holos/testdata/version.txt
vendored
2
cmd/holos/testdata/version.txt
vendored
@@ -1,5 +1,3 @@
|
||||
exec holos --version
|
||||
# want version with no v on stdout
|
||||
stdout -count=1 '^\d+\.\d+\.\d+$'
|
||||
# want nothing on stderr
|
||||
! stderr .
|
||||
|
||||
47
doc/md/intro.md
Normal file
47
doc/md/intro.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Tutorial Intro
|
||||
|
||||
Let's discover **Docusaurus in less than 5 minutes**.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Get started by **creating a new site**.
|
||||
|
||||
Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.
|
||||
|
||||
### What you'll need
|
||||
|
||||
- [Node.js](https://nodejs.org/en/download/) version 18.0 or above:
|
||||
- When installing Node.js, you are recommended to check all checkboxes related to dependencies.
|
||||
|
||||
## Generate a new site
|
||||
|
||||
Generate a new Docusaurus site using the **classic template**.
|
||||
|
||||
The classic template will automatically be added to your project after you run the command:
|
||||
|
||||
```bash
|
||||
npm init docusaurus@latest my-website classic
|
||||
```
|
||||
|
||||
You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor.
|
||||
|
||||
The command also installs all necessary dependencies you need to run Docusaurus.
|
||||
|
||||
## Start your site
|
||||
|
||||
Run the development server:
|
||||
|
||||
```bash
|
||||
cd my-website
|
||||
npm run start
|
||||
```
|
||||
|
||||
The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there.
|
||||
|
||||
The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/.
|
||||
|
||||
Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes.
|
||||
8
doc/md/tutorial-basics/_category_.json
Normal file
8
doc/md/tutorial-basics/_category_.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"label": "Tutorial - Basics",
|
||||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "5 minutes to learn the most important Docusaurus concepts."
|
||||
}
|
||||
}
|
||||
23
doc/md/tutorial-basics/congratulations.md
Normal file
23
doc/md/tutorial-basics/congratulations.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Congratulations!
|
||||
|
||||
You have just learned the **basics of Docusaurus** and made some changes to the **initial template**.
|
||||
|
||||
Docusaurus has **much more to offer**!
|
||||
|
||||
Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**.
|
||||
|
||||
Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610)
|
||||
|
||||
## What's next?
|
||||
|
||||
- Read the [official documentation](https://docusaurus.io/)
|
||||
- Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config)
|
||||
- Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration)
|
||||
- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout)
|
||||
- Add a [search bar](https://docusaurus.io/docs/search)
|
||||
- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase)
|
||||
- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support)
|
||||
34
doc/md/tutorial-basics/create-a-blog-post.md
Normal file
34
doc/md/tutorial-basics/create-a-blog-post.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Create a Blog Post
|
||||
|
||||
Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed...
|
||||
|
||||
## Create your first Post
|
||||
|
||||
Create a file at `blog/2021-02-28-greetings.md`:
|
||||
|
||||
```md title="blog/2021-02-28-greetings.md"
|
||||
---
|
||||
slug: greetings
|
||||
title: Greetings!
|
||||
authors:
|
||||
- name: Joel Marcey
|
||||
title: Co-creator of Docusaurus 1
|
||||
url: https://github.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
- name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
tags: [greetings]
|
||||
---
|
||||
|
||||
Congratulations, you have made your first post!
|
||||
|
||||
Feel free to play around and edit this post as much as you like.
|
||||
```
|
||||
|
||||
A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).
|
||||
57
doc/md/tutorial-basics/create-a-document.md
Normal file
57
doc/md/tutorial-basics/create-a-document.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Create a Document
|
||||
|
||||
Documents are **groups of pages** connected through:
|
||||
|
||||
- a **sidebar**
|
||||
- **previous/next navigation**
|
||||
- **versioning**
|
||||
|
||||
## Create your first Doc
|
||||
|
||||
Create a Markdown file at `docs/hello.md`:
|
||||
|
||||
```md title="docs/hello.md"
|
||||
# Hello
|
||||
|
||||
This is my **first Docusaurus document**!
|
||||
```
|
||||
|
||||
A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello).
|
||||
|
||||
## Configure the Sidebar
|
||||
|
||||
Docusaurus automatically **creates a sidebar** from the `docs` folder.
|
||||
|
||||
Add metadata to customize the sidebar label and position:
|
||||
|
||||
```md title="docs/hello.md" {1-4}
|
||||
---
|
||||
sidebar_label: 'Hi!'
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
This is my **first Docusaurus document**!
|
||||
```
|
||||
|
||||
It is also possible to create your sidebar explicitly in `sidebars.js`:
|
||||
|
||||
```js title="sidebars.js"
|
||||
export default {
|
||||
tutorialSidebar: [
|
||||
'intro',
|
||||
// highlight-next-line
|
||||
'hello',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorial',
|
||||
items: ['tutorial-basics/create-a-document'],
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
43
doc/md/tutorial-basics/create-a-page.md
Normal file
43
doc/md/tutorial-basics/create-a-page.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Create a Page
|
||||
|
||||
Add **Markdown or React** files to `src/pages` to create a **standalone page**:
|
||||
|
||||
- `src/pages/index.js` → `localhost:3000/`
|
||||
- `src/pages/foo.md` → `localhost:3000/foo`
|
||||
- `src/pages/foo/bar.js` → `localhost:3000/foo/bar`
|
||||
|
||||
## Create your first React Page
|
||||
|
||||
Create a file at `src/pages/my-react-page.js`:
|
||||
|
||||
```jsx title="src/pages/my-react-page.js"
|
||||
import React from 'react';
|
||||
import Layout from '@theme/Layout';
|
||||
|
||||
export default function MyReactPage() {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>My React page</h1>
|
||||
<p>This is a React page</p>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page).
|
||||
|
||||
## Create your first Markdown Page
|
||||
|
||||
Create a file at `src/pages/my-markdown-page.md`:
|
||||
|
||||
```mdx title="src/pages/my-markdown-page.md"
|
||||
# My Markdown page
|
||||
|
||||
This is a Markdown page
|
||||
```
|
||||
|
||||
A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page).
|
||||
31
doc/md/tutorial-basics/deploy-your-site.md
Normal file
31
doc/md/tutorial-basics/deploy-your-site.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Deploy your site
|
||||
|
||||
Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**).
|
||||
|
||||
It builds your site as simple **static HTML, JavaScript and CSS files**.
|
||||
|
||||
## Build your site
|
||||
|
||||
Build your site **for production**:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The static files are generated in the `build` folder.
|
||||
|
||||
## Deploy your site
|
||||
|
||||
Test your production build locally:
|
||||
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/).
|
||||
|
||||
You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**).
|
||||
152
doc/md/tutorial-basics/markdown-features.mdx
Normal file
152
doc/md/tutorial-basics/markdown-features.mdx
Normal file
@@ -0,0 +1,152 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# Markdown Features
|
||||
|
||||
Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**.
|
||||
|
||||
## Front Matter
|
||||
|
||||
Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/):
|
||||
|
||||
```text title="my-doc.md"
|
||||
// highlight-start
|
||||
---
|
||||
id: my-doc-id
|
||||
title: My document title
|
||||
description: My document description
|
||||
slug: /my-custom-url
|
||||
---
|
||||
// highlight-end
|
||||
|
||||
## Markdown heading
|
||||
|
||||
Markdown text with [links](./hello.md)
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
Regular Markdown links are supported, using url paths or relative file paths.
|
||||
|
||||
```md
|
||||
Let's see how to [Create a page](/create-a-page).
|
||||
```
|
||||
|
||||
```md
|
||||
Let's see how to [Create a page](./create-a-page.md).
|
||||
```
|
||||
|
||||
**Result:** Let's see how to [Create a page](./create-a-page.md).
|
||||
|
||||
## Images
|
||||
|
||||
Regular Markdown images are supported.
|
||||
|
||||
You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`):
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them:
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
## Code Blocks
|
||||
|
||||
Markdown code blocks are supported with Syntax highlighting.
|
||||
|
||||
````md
|
||||
```jsx title="src/components/HelloDocusaurus.js"
|
||||
function HelloDocusaurus() {
|
||||
return <h1>Hello, Docusaurus!</h1>;
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
```jsx title="src/components/HelloDocusaurus.js"
|
||||
function HelloDocusaurus() {
|
||||
return <h1>Hello, Docusaurus!</h1>;
|
||||
}
|
||||
```
|
||||
|
||||
## Admonitions
|
||||
|
||||
Docusaurus has a special syntax to create admonitions and callouts:
|
||||
|
||||
```md
|
||||
:::tip My tip
|
||||
|
||||
Use this awesome feature option
|
||||
|
||||
:::
|
||||
|
||||
:::danger Take care
|
||||
|
||||
This action is dangerous
|
||||
|
||||
:::
|
||||
```
|
||||
|
||||
:::tip My tip
|
||||
|
||||
Use this awesome feature option
|
||||
|
||||
:::
|
||||
|
||||
:::danger Take care
|
||||
|
||||
This action is dangerous
|
||||
|
||||
:::
|
||||
|
||||
## MDX and React Components
|
||||
|
||||
[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**:
|
||||
|
||||
```jsx
|
||||
export const Highlight = ({children, color}) => (
|
||||
<span
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
borderRadius: '20px',
|
||||
color: '#fff',
|
||||
padding: '10px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => {
|
||||
alert(`You clicked the color ${color} with label ${children}`)
|
||||
}}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
|
||||
|
||||
This is <Highlight color="#1877F2">Facebook blue</Highlight> !
|
||||
```
|
||||
|
||||
export const Highlight = ({children, color}) => (
|
||||
<span
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
borderRadius: '20px',
|
||||
color: '#fff',
|
||||
padding: '10px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => {
|
||||
alert(`You clicked the color ${color} with label ${children}`);
|
||||
}}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
|
||||
|
||||
This is <Highlight color="#1877F2">Facebook blue</Highlight> !
|
||||
7
doc/md/tutorial-extras/_category_.json
Normal file
7
doc/md/tutorial-extras/_category_.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "Tutorial - Extras",
|
||||
"position": 3,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
BIN
doc/md/tutorial-extras/img/docsVersionDropdown.png
Normal file
BIN
doc/md/tutorial-extras/img/docsVersionDropdown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
doc/md/tutorial-extras/img/localeDropdown.png
Normal file
BIN
doc/md/tutorial-extras/img/localeDropdown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
55
doc/md/tutorial-extras/manage-docs-versions.md
Normal file
55
doc/md/tutorial-extras/manage-docs-versions.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Manage Docs Versions
|
||||
|
||||
Docusaurus can manage multiple versions of your docs.
|
||||
|
||||
## Create a docs version
|
||||
|
||||
Release a version 1.0 of your project:
|
||||
|
||||
```bash
|
||||
npm run docusaurus docs:version 1.0
|
||||
```
|
||||
|
||||
The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.
|
||||
|
||||
Your docs now have 2 versions:
|
||||
|
||||
- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs
|
||||
- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**
|
||||
|
||||
## Add a Version Dropdown
|
||||
|
||||
To navigate seamlessly across versions, add a version dropdown.
|
||||
|
||||
Modify the `docusaurus.config.js` file:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
items: [
|
||||
// highlight-start
|
||||
{
|
||||
type: 'docsVersionDropdown',
|
||||
},
|
||||
// highlight-end
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The docs version dropdown appears in your navbar:
|
||||
|
||||

|
||||
|
||||
## Update an existing version
|
||||
|
||||
It is possible to edit versioned docs in their respective folder:
|
||||
|
||||
- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`
|
||||
- `docs/hello.md` updates `http://localhost:3000/docs/next/hello`
|
||||
88
doc/md/tutorial-extras/translate-your-site.md
Normal file
88
doc/md/tutorial-extras/translate-your-site.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Translate your site
|
||||
|
||||
Let's translate `docs/intro.md` to French.
|
||||
|
||||
## Configure i18n
|
||||
|
||||
Modify `docusaurus.config.js` to add support for the `fr` locale:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'fr'],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Translate a doc
|
||||
|
||||
Copy the `docs/intro.md` file to the `i18n/fr` folder:
|
||||
|
||||
```bash
|
||||
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
|
||||
|
||||
cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
|
||||
```
|
||||
|
||||
Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
|
||||
|
||||
## Start your localized site
|
||||
|
||||
Start your site on the French locale:
|
||||
|
||||
```bash
|
||||
npm run start -- --locale fr
|
||||
```
|
||||
|
||||
Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
|
||||
|
||||
:::caution
|
||||
|
||||
In development, you can only use one locale at a time.
|
||||
|
||||
:::
|
||||
|
||||
## Add a Locale Dropdown
|
||||
|
||||
To navigate seamlessly across languages, add a locale dropdown.
|
||||
|
||||
Modify the `docusaurus.config.js` file:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
items: [
|
||||
// highlight-start
|
||||
{
|
||||
type: 'localeDropdown',
|
||||
},
|
||||
// highlight-end
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The locale dropdown now appears in your navbar:
|
||||
|
||||

|
||||
|
||||
## Build your localized site
|
||||
|
||||
Build your site for a specific locale:
|
||||
|
||||
```bash
|
||||
npm run build -- --locale fr
|
||||
```
|
||||
|
||||
Or build your site to include all the locales at once:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
17
doc/website/.gitignore
vendored
Normal file
17
doc/website/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
41
doc/website/README.md
Normal file
41
doc/website/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Website
|
||||
|
||||
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
|
||||
|
||||
### Installation
|
||||
|
||||
```
|
||||
$ yarn
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
```
|
||||
$ yarn start
|
||||
```
|
||||
|
||||
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
$ yarn build
|
||||
```
|
||||
|
||||
This command generates static content into the `build` directory and can be served using any static contents hosting service.
|
||||
|
||||
### Deployment
|
||||
|
||||
Using SSH:
|
||||
|
||||
```
|
||||
$ USE_SSH=true yarn deploy
|
||||
```
|
||||
|
||||
Not using SSH:
|
||||
|
||||
```
|
||||
$ GIT_USER=<Your GitHub username> yarn deploy
|
||||
```
|
||||
|
||||
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
|
||||
3
doc/website/babel.config.js
Normal file
3
doc/website/babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
||||
12
doc/website/blog/2019-05-28-first-blog-post.md
Normal file
12
doc/website/blog/2019-05-28-first-blog-post.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
slug: first-blog-post
|
||||
title: First Blog Post
|
||||
authors:
|
||||
name: Gao Wei
|
||||
title: Docusaurus Core Team
|
||||
url: https://github.com/wgao19
|
||||
image_url: https://github.com/wgao19.png
|
||||
tags: [hola, docusaurus]
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
44
doc/website/blog/2019-05-29-long-blog-post.md
Normal file
44
doc/website/blog/2019-05-29-long-blog-post.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
slug: long-blog-post
|
||||
title: Long Blog Post
|
||||
authors: endi
|
||||
tags: [hello, docusaurus]
|
||||
---
|
||||
|
||||
This is the summary of a very long blog post,
|
||||
|
||||
Use a `<!--` `truncate` `-->` comment to limit blog post size in the list view.
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
20
doc/website/blog/2021-08-01-mdx-blog-post.mdx
Normal file
20
doc/website/blog/2021-08-01-mdx-blog-post.mdx
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
slug: mdx-blog-post
|
||||
title: MDX Blog Post
|
||||
authors: [slorber]
|
||||
tags: [docusaurus]
|
||||
---
|
||||
|
||||
Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
|
||||
|
||||
:::tip
|
||||
|
||||
Use the power of React to create interactive blog posts.
|
||||
|
||||
```js
|
||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
||||
```
|
||||
|
||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
||||
|
||||
:::
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
25
doc/website/blog/2021-08-26-welcome/index.md
Normal file
25
doc/website/blog/2021-08-26-welcome/index.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
slug: welcome
|
||||
title: Welcome
|
||||
authors: [slorber, yangshun]
|
||||
tags: [facebook, hello, docusaurus]
|
||||
---
|
||||
|
||||
[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
|
||||
|
||||
Simply add Markdown files (or folders) to the `blog` directory.
|
||||
|
||||
Regular blog authors can be added to `authors.yml`.
|
||||
|
||||
The blog post date can be extracted from filenames, such as:
|
||||
|
||||
- `2019-05-30-welcome.md`
|
||||
- `2019-05-30-welcome/index.md`
|
||||
|
||||
A blog post folder can be convenient to co-locate blog post images:
|
||||
|
||||

|
||||
|
||||
The blog supports tags as well!
|
||||
|
||||
**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.
|
||||
17
doc/website/blog/authors.yml
Normal file
17
doc/website/blog/authors.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
endi:
|
||||
name: Endilie Yacop Sucipto
|
||||
title: Maintainer of Docusaurus
|
||||
url: https://github.com/endiliey
|
||||
image_url: https://github.com/endiliey.png
|
||||
|
||||
yangshun:
|
||||
name: Yangshun Tay
|
||||
title: Front End Engineer @ Facebook
|
||||
url: https://github.com/yangshun
|
||||
image_url: https://github.com/yangshun.png
|
||||
|
||||
slorber:
|
||||
name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
16
doc/website/blog/tags.yml
Normal file
16
doc/website/blog/tags.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
facebook:
|
||||
label: Facebook
|
||||
permalink: /facebook
|
||||
description: Facebook tag description
|
||||
hello:
|
||||
label: Hello
|
||||
permalink: /hello
|
||||
description: Hello tag description
|
||||
docusaurus:
|
||||
label: Docusaurus
|
||||
permalink: /docusaurus
|
||||
description: Docusaurus tag description
|
||||
hola:
|
||||
label: Hola
|
||||
permalink: /hola
|
||||
description: Hola tag description
|
||||
0
doc/website/build/.nojekyll
Normal file
0
doc/website/build/.nojekyll
Normal file
15
doc/website/build/404.html
Normal file
15
doc/website/build/404.html
Normal file
File diff suppressed because one or more lines are too long
1
doc/website/build/assets/css/styles.eaccac5c.css
Normal file
1
doc/website/build/assets/css/styles.eaccac5c.css
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
1
doc/website/build/assets/js/0058b4c6.6cd30c00.js
Normal file
1
doc/website/build/assets/js/0058b4c6.6cd30c00.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[849],{6164:a=>{a.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Tutorial Intro","href":"/docs/intro","docId":"intro","unlisted":false},{"type":"category","label":"Tutorial - Basics","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Create a Page","href":"/docs/tutorial-basics/create-a-page","docId":"tutorial-basics/create-a-page","unlisted":false},{"type":"link","label":"Create a Document","href":"/docs/tutorial-basics/create-a-document","docId":"tutorial-basics/create-a-document","unlisted":false},{"type":"link","label":"Create a Blog Post","href":"/docs/tutorial-basics/create-a-blog-post","docId":"tutorial-basics/create-a-blog-post","unlisted":false},{"type":"link","label":"Markdown Features","href":"/docs/tutorial-basics/markdown-features","docId":"tutorial-basics/markdown-features","unlisted":false},{"type":"link","label":"Deploy your site","href":"/docs/tutorial-basics/deploy-your-site","docId":"tutorial-basics/deploy-your-site","unlisted":false},{"type":"link","label":"Congratulations!","href":"/docs/tutorial-basics/congratulations","docId":"tutorial-basics/congratulations","unlisted":false}],"href":"/docs/category/tutorial---basics"},{"type":"category","label":"Tutorial - Extras","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Manage Docs Versions","href":"/docs/tutorial-extras/manage-docs-versions","docId":"tutorial-extras/manage-docs-versions","unlisted":false},{"type":"link","label":"Translate your site","href":"/docs/tutorial-extras/translate-your-site","docId":"tutorial-extras/translate-your-site","unlisted":false}],"href":"/docs/category/tutorial---extras"}]},"docs":{"intro":{"id":"intro","title":"Tutorial Intro","description":"Let\'s discover Docusaurus in less than 5 minutes.","sidebar":"tutorialSidebar"},"tutorial-basics/congratulations":{"id":"tutorial-basics/congratulations","title":"Congratulations!","description":"You have just learned the basics of Docusaurus and made some changes to the initial template.","sidebar":"tutorialSidebar"},"tutorial-basics/create-a-blog-post":{"id":"tutorial-basics/create-a-blog-post","title":"Create a Blog Post","description":"Docusaurus creates a page for each blog post, but also a blog index page, a tag system, an RSS feed...","sidebar":"tutorialSidebar"},"tutorial-basics/create-a-document":{"id":"tutorial-basics/create-a-document","title":"Create a Document","description":"Documents are groups of pages connected through:","sidebar":"tutorialSidebar"},"tutorial-basics/create-a-page":{"id":"tutorial-basics/create-a-page","title":"Create a Page","description":"Add Markdown or React files to src/pages to create a standalone page:","sidebar":"tutorialSidebar"},"tutorial-basics/deploy-your-site":{"id":"tutorial-basics/deploy-your-site","title":"Deploy your site","description":"Docusaurus is a static-site-generator (also called Jamstack).","sidebar":"tutorialSidebar"},"tutorial-basics/markdown-features":{"id":"tutorial-basics/markdown-features","title":"Markdown Features","description":"Docusaurus supports Markdown and a few additional features.","sidebar":"tutorialSidebar"},"tutorial-extras/manage-docs-versions":{"id":"tutorial-extras/manage-docs-versions","title":"Manage Docs Versions","description":"Docusaurus can manage multiple versions of your docs.","sidebar":"tutorialSidebar"},"tutorial-extras/translate-your-site":{"id":"tutorial-extras/translate-your-site","title":"Translate your site","description":"Let\'s translate docs/intro.md to French.","sidebar":"tutorialSidebar"}}}}')}}]);
|
||||
1
doc/website/build/assets/js/01a85c17.a12e68a4.js
Normal file
1
doc/website/build/assets/js/01a85c17.a12e68a4.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8209],{6535:(e,s,t)=>{t.d(s,{A:()=>v});var a=t(6540),i=t(4164),r=t(781),l=t(4581),n=t(8774),c=t(1312),o=t(6347),d=t(9169);function m(e){const{pathname:s}=(0,o.zy)();return(0,a.useMemo)((()=>e.filter((e=>function(e,s){return!(e.unlisted&&!(0,d.ys)(e.permalink,s))}(e,s)))),[e,s])}const u={sidebar:"sidebar_re4s",sidebarItemTitle:"sidebarItemTitle_pO2u",sidebarItemList:"sidebarItemList_Yudw",sidebarItem:"sidebarItem__DBe",sidebarItemLink:"sidebarItemLink_mo7H",sidebarItemLinkActive:"sidebarItemLinkActive_I1ZP"};var b=t(4848);function g(e){let{sidebar:s}=e;const t=m(s.items);return(0,b.jsx)("aside",{className:"col col--3",children:(0,b.jsxs)("nav",{className:(0,i.A)(u.sidebar,"thin-scrollbar"),"aria-label":(0,c.T)({id:"theme.blog.sidebar.navAriaLabel",message:"Blog recent posts navigation",description:"The ARIA label for recent posts in the blog sidebar"}),children:[(0,b.jsx)("div",{className:(0,i.A)(u.sidebarItemTitle,"margin-bottom--md"),children:s.title}),(0,b.jsx)("ul",{className:(0,i.A)(u.sidebarItemList,"clean-list"),children:t.map((e=>(0,b.jsx)("li",{className:u.sidebarItem,children:(0,b.jsx)(n.A,{isNavLink:!0,to:e.permalink,className:u.sidebarItemLink,activeClassName:u.sidebarItemLinkActive,children:e.title})},e.permalink)))})]})})}var h=t(5600);function j(e){let{sidebar:s}=e;const t=m(s.items);return(0,b.jsx)("ul",{className:"menu__list",children:t.map((e=>(0,b.jsx)("li",{className:"menu__list-item",children:(0,b.jsx)(n.A,{isNavLink:!0,to:e.permalink,className:"menu__link",activeClassName:"menu__link--active",children:e.title})},e.permalink)))})}function p(e){return(0,b.jsx)(h.GX,{component:j,props:e})}function x(e){let{sidebar:s}=e;const t=(0,l.l)();return s?.items.length?"mobile"===t?(0,b.jsx)(p,{sidebar:s}):(0,b.jsx)(g,{sidebar:s}):null}function v(e){const{sidebar:s,toc:t,children:a,...l}=e,n=s&&s.items.length>0;return(0,b.jsx)(r.A,{...l,children:(0,b.jsx)("div",{className:"container margin-vert--lg",children:(0,b.jsxs)("div",{className:"row",children:[(0,b.jsx)(x,{sidebar:s}),(0,b.jsx)("main",{className:(0,i.A)("col",{"col--7":n,"col--9 col--offset-1":!n}),children:a}),t&&(0,b.jsx)("div",{className:"col col--2",children:t})]})})})}},9158:(e,s,t)=>{t.r(s),t.d(s,{default:()=>j});t(6540);var a=t(4164),i=t(1312);const r=()=>(0,i.T)({id:"theme.tags.tagsPageTitle",message:"Tags",description:"The title of the tag list page"});var l=t(1213),n=t(7559),c=t(6535),o=t(6133),d=t(1107);const m={tag:"tag_Nnez"};var u=t(4848);function b(e){let{letterEntry:s}=e;return(0,u.jsxs)("article",{children:[(0,u.jsx)(d.A,{as:"h2",id:s.letter,children:s.letter}),(0,u.jsx)("ul",{className:"padding--none",children:s.tags.map((e=>(0,u.jsx)("li",{className:m.tag,children:(0,u.jsx)(o.A,{...e})},e.permalink)))}),(0,u.jsx)("hr",{})]})}function g(e){let{tags:s}=e;const t=function(e){const s={};return Object.values(e).forEach((e=>{const t=function(e){return e[0].toUpperCase()}(e.label);s[t]??=[],s[t].push(e)})),Object.entries(s).sort(((e,s)=>{let[t]=e,[a]=s;return t.localeCompare(a)})).map((e=>{let[s,t]=e;return{letter:s,tags:t.sort(((e,s)=>e.label.localeCompare(s.label)))}}))}(s);return(0,u.jsx)("section",{className:"margin-vert--lg",children:t.map((e=>(0,u.jsx)(b,{letterEntry:e},e.letter)))})}var h=t(1463);function j(e){let{tags:s,sidebar:t}=e;const i=r();return(0,u.jsxs)(l.e3,{className:(0,a.A)(n.G.wrapper.blogPages,n.G.page.blogTagsListPage),children:[(0,u.jsx)(l.be,{title:i}),(0,u.jsx)(h.A,{tag:"blog_tags_list"}),(0,u.jsxs)(c.A,{sidebar:t,children:[(0,u.jsx)(d.A,{as:"h1",children:i}),(0,u.jsx)(g,{tags:s})]})]})}},6133:(e,s,t)=>{t.d(s,{A:()=>n});t(6540);var a=t(4164),i=t(8774);const r={tag:"tag_zVej",tagRegular:"tagRegular_sFm0",tagWithCount:"tagWithCount_h2kH"};var l=t(4848);function n(e){let{permalink:s,label:t,count:n,description:c}=e;return(0,l.jsxs)(i.A,{href:s,title:c,className:(0,a.A)(r.tag,n?r.tagWithCount:r.tagRegular),children:[t,n&&(0,l.jsx)("span",{children:n})]})}}}]);
|
||||
1
doc/website/build/assets/js/14eb3368.cfdb64b8.js
Normal file
1
doc/website/build/assets/js/14eb3368.cfdb64b8.js
Normal file
File diff suppressed because one or more lines are too long
1
doc/website/build/assets/js/1538.f3fc3a21.js
Normal file
1
doc/website/build/assets/js/1538.f3fc3a21.js
Normal file
File diff suppressed because one or more lines are too long
1
doc/website/build/assets/js/17896441.0eb63a22.js
Normal file
1
doc/website/build/assets/js/17896441.0eb63a22.js
Normal file
File diff suppressed because one or more lines are too long
1
doc/website/build/assets/js/1df93b7f.714a32a7.js
Normal file
1
doc/website/build/assets/js/1df93b7f.714a32a7.js
Normal file
File diff suppressed because one or more lines are too long
1
doc/website/build/assets/js/1f391b9e.feaab466.js
Normal file
1
doc/website/build/assets/js/1f391b9e.feaab466.js
Normal file
File diff suppressed because one or more lines are too long
1
doc/website/build/assets/js/2237.1295f7b8.js
Normal file
1
doc/website/build/assets/js/2237.1295f7b8.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2237],{3363:(e,t,i)=>{i.d(t,{A:()=>a});i(6540);var n=i(4164),s=i(1312),o=i(1107),r=i(4848);function a(e){let{className:t}=e;return(0,r.jsx)("main",{className:(0,n.A)("container margin-vert--xl",t),children:(0,r.jsx)("div",{className:"row",children:(0,r.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,r.jsx)(o.A,{as:"h1",className:"hero__title",children:(0,r.jsx)(s.A,{id:"theme.NotFound.title",description:"The title of the 404 page",children:"Page Not Found"})}),(0,r.jsx)("p",{children:(0,r.jsx)(s.A,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page",children:"We could not find what you were looking for."})}),(0,r.jsx)("p",{children:(0,r.jsx)(s.A,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page",children:"Please contact the owner of the site that linked you to the original URL and let them know their link is broken."})})]})})})}},2237:(e,t,i)=>{i.r(t),i.d(t,{default:()=>l});i(6540);var n=i(1312),s=i(1213),o=i(781),r=i(3363),a=i(4848);function l(){const e=(0,n.T)({id:"theme.NotFound.title",message:"Page Not Found"});return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.be,{title:e}),(0,a.jsx)(o.A,{children:(0,a.jsx)(r.A,{})})]})}}}]);
|
||||
1
doc/website/build/assets/js/258eb27f.e0a556ff.js
Normal file
1
doc/website/build/assets/js/258eb27f.e0a556ff.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2118],{8764:(e,a,t)=>{t.r(a),t.d(a,{assets:()=>i,contentTitle:()=>o,default:()=>p,frontMatter:()=>n,metadata:()=>c,toc:()=>l});var r=t(4848),s=t(8453);const n={sidebar_position:1},o="Create a Page",c={id:"tutorial-basics/create-a-page",title:"Create a Page",description:"Add Markdown or React files to src/pages to create a standalone page:",source:"@site/../md/tutorial-basics/create-a-page.md",sourceDirName:"tutorial-basics",slug:"/tutorial-basics/create-a-page",permalink:"/docs/tutorial-basics/create-a-page",draft:!1,unlisted:!1,editUrl:"https://github.com/holos-run/holos/edit/main/doc/md/../md/tutorial-basics/create-a-page.md",tags:[],version:"current",lastUpdatedBy:"Jeff McCune",lastUpdatedAt:1719897028e3,sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Tutorial - Basics",permalink:"/docs/category/tutorial---basics"},next:{title:"Create a Document",permalink:"/docs/tutorial-basics/create-a-document"}},i={},l=[{value:"Create your first React Page",id:"create-your-first-react-page",level:2},{value:"Create your first Markdown Page",id:"create-your-first-markdown-page",level:2}];function d(e){const a={a:"a",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(a.h1,{id:"create-a-page",children:"Create a Page"}),"\n",(0,r.jsxs)(a.p,{children:["Add ",(0,r.jsx)(a.strong,{children:"Markdown or React"})," files to ",(0,r.jsx)(a.code,{children:"src/pages"})," to create a ",(0,r.jsx)(a.strong,{children:"standalone page"}),":"]}),"\n",(0,r.jsxs)(a.ul,{children:["\n",(0,r.jsxs)(a.li,{children:[(0,r.jsx)(a.code,{children:"src/pages/index.js"})," \u2192 ",(0,r.jsx)(a.code,{children:"localhost:3000/"})]}),"\n",(0,r.jsxs)(a.li,{children:[(0,r.jsx)(a.code,{children:"src/pages/foo.md"})," \u2192 ",(0,r.jsx)(a.code,{children:"localhost:3000/foo"})]}),"\n",(0,r.jsxs)(a.li,{children:[(0,r.jsx)(a.code,{children:"src/pages/foo/bar.js"})," \u2192 ",(0,r.jsx)(a.code,{children:"localhost:3000/foo/bar"})]}),"\n"]}),"\n",(0,r.jsx)(a.h2,{id:"create-your-first-react-page",children:"Create your first React Page"}),"\n",(0,r.jsxs)(a.p,{children:["Create a file at ",(0,r.jsx)(a.code,{children:"src/pages/my-react-page.js"}),":"]}),"\n",(0,r.jsx)(a.pre,{children:(0,r.jsx)(a.code,{className:"language-jsx",metastring:'title="src/pages/my-react-page.js"',children:"import React from 'react';\nimport Layout from '@theme/Layout';\n\nexport default function MyReactPage() {\n return (\n <Layout>\n <h1>My React page</h1>\n <p>This is a React page</p>\n </Layout>\n );\n}\n"})}),"\n",(0,r.jsxs)(a.p,{children:["A new page is now available at ",(0,r.jsx)(a.a,{href:"http://localhost:3000/my-react-page",children:"http://localhost:3000/my-react-page"}),"."]}),"\n",(0,r.jsx)(a.h2,{id:"create-your-first-markdown-page",children:"Create your first Markdown Page"}),"\n",(0,r.jsxs)(a.p,{children:["Create a file at ",(0,r.jsx)(a.code,{children:"src/pages/my-markdown-page.md"}),":"]}),"\n",(0,r.jsx)(a.pre,{children:(0,r.jsx)(a.code,{className:"language-mdx",metastring:'title="src/pages/my-markdown-page.md"',children:"# My Markdown page\n\nThis is a Markdown page\n"})}),"\n",(0,r.jsxs)(a.p,{children:["A new page is now available at ",(0,r.jsx)(a.a,{href:"http://localhost:3000/my-markdown-page",children:"http://localhost:3000/my-markdown-page"}),"."]})]})}function p(e={}){const{wrapper:a}={...(0,s.R)(),...e.components};return a?(0,r.jsx)(a,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}},8453:(e,a,t)=>{t.d(a,{R:()=>o,x:()=>c});var r=t(6540);const s={},n=r.createContext(s);function o(e){const a=r.useContext(n);return r.useMemo((function(){return"function"==typeof e?e(a):{...a,...e}}),[a,e])}function c(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),r.createElement(n.Provider,{value:a},e.children)}}}]);
|
||||
1
doc/website/build/assets/js/3217192f.4095e6b5.js
Normal file
1
doc/website/build/assets/js/3217192f.4095e6b5.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8462],{8174:s=>{s.exports=JSON.parse('{"tag":{"label":"Docusaurus","permalink":"/blog/tags/docusaurus","description":"Docusaurus tag description","allTagsPath":"/blog/tags","count":4,"unlisted":false},"listMetadata":{"permalink":"/blog/tags/docusaurus","page":1,"postsPerPage":10,"totalPages":1,"totalCount":4,"blogDescription":"Blog","blogTitle":"Blog"}}')}}]);
|
||||
1
doc/website/build/assets/js/3242.519f3dae.js
Normal file
1
doc/website/build/assets/js/3242.519f3dae.js
Normal file
File diff suppressed because one or more lines are too long
1
doc/website/build/assets/js/36994c47.8f17e7f5.js
Normal file
1
doc/website/build/assets/js/36994c47.8f17e7f5.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9858],{5516:e=>{e.exports=JSON.parse('{"name":"docusaurus-plugin-content-blog","id":"default"}')}}]);
|
||||
1
doc/website/build/assets/js/393be207.a7014397.js
Normal file
1
doc/website/build/assets/js/393be207.a7014397.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4134],{633:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>l,frontMatter:()=>r,metadata:()=>p,toc:()=>i});var o=n(4848),a=n(8453);const r={title:"Markdown page example"},s="Markdown page example",p={type:"mdx",permalink:"/markdown-page",source:"@site/src/pages/markdown-page.md",title:"Markdown page example",description:"You don't need React to write simple standalone pages.",frontMatter:{title:"Markdown page example"},unlisted:!1},c={},i=[];function d(e){const t={h1:"h1",p:"p",...(0,a.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h1,{id:"markdown-page-example",children:"Markdown page example"}),"\n",(0,o.jsx)(t.p,{children:"You don't need React to write simple standalone pages."})]})}function l(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>s,x:()=>p});var o=n(6540);const a={},r=o.createContext(a);function s(e){const t=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function p(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),o.createElement(r.Provider,{value:t},e.children)}}}]);
|
||||
1
doc/website/build/assets/js/3a2db09e.1964030f.js
Normal file
1
doc/website/build/assets/js/3a2db09e.1964030f.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8121],{8070:e=>{e.exports=JSON.parse('{"tags":[{"label":"Facebook","permalink":"/blog/tags/facebook","description":"Facebook tag description","count":1},{"label":"Hello","permalink":"/blog/tags/hello","description":"Hello tag description","count":2},{"label":"Docusaurus","permalink":"/blog/tags/docusaurus","description":"Docusaurus tag description","count":4},{"label":"Hola","permalink":"/blog/tags/hola","description":"Hola tag description","count":1}]}')}}]);
|
||||
1
doc/website/build/assets/js/59362658.41f3e27f.js
Normal file
1
doc/website/build/assets/js/59362658.41f3e27f.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9325],{1180:(t,e,o)=>{o.r(e),o.d(e,{assets:()=>l,contentTitle:()=>a,default:()=>p,frontMatter:()=>r,metadata:()=>c,toc:()=>u});var s=o(4848),n=o(8453);const r={slug:"mdx-blog-post",title:"MDX Blog Post",authors:["slorber"],tags:["docusaurus"]},a=void 0,c={permalink:"/blog/mdx-blog-post",source:"@site/blog/2021-08-01-mdx-blog-post.mdx",title:"MDX Blog Post",description:"Blog posts support Docusaurus Markdown features, such as MDX.",date:"2021-08-01T00:00:00.000Z",tags:[{inline:!1,label:"Docusaurus",permalink:"/blog/tags/docusaurus",description:"Docusaurus tag description"}],hasTruncateMarker:!1,authors:[{name:"S\xe9bastien Lorber",title:"Docusaurus maintainer",url:"https://sebastienlorber.com",imageURL:"https://github.com/slorber.png",key:"slorber"}],frontMatter:{slug:"mdx-blog-post",title:"MDX Blog Post",authors:["slorber"],tags:["docusaurus"]},unlisted:!1,prevItem:{title:"Welcome",permalink:"/blog/welcome"},nextItem:{title:"Long Blog Post",permalink:"/blog/long-blog-post"}},l={authorsImageUrls:[void 0]},u=[];function i(t){const e={a:"a",admonition:"admonition",code:"code",p:"p",pre:"pre",...(0,n.R)(),...t.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(e.p,{children:["Blog posts support ",(0,s.jsx)(e.a,{href:"https://docusaurus.io/docs/markdown-features",children:"Docusaurus Markdown features"}),", such as ",(0,s.jsx)(e.a,{href:"https://mdxjs.com/",children:"MDX"}),"."]}),"\n",(0,s.jsxs)(e.admonition,{type:"tip",children:[(0,s.jsx)(e.p,{children:"Use the power of React to create interactive blog posts."}),(0,s.jsx)(e.pre,{children:(0,s.jsx)(e.code,{className:"language-js",children:"<button onClick={() => alert('button clicked!')}>Click me!</button>\n"})}),(0,s.jsx)("button",{onClick:()=>alert("button clicked!"),children:"Click me!"})]})]})}function p(t={}){const{wrapper:e}={...(0,n.R)(),...t.components};return e?(0,s.jsx)(e,{...t,children:(0,s.jsx)(i,{...t})}):i(t)}},8453:(t,e,o)=>{o.d(e,{R:()=>a,x:()=>c});var s=o(6540);const n={},r=s.createContext(n);function a(t){const e=s.useContext(r);return s.useMemo((function(){return"function"==typeof t?t(e):{...e,...t}}),[e,t])}function c(t){let e;return e=t.disableParentContext?"function"==typeof t.components?t.components(n):t.components||n:a(t.components),s.createElement(r.Provider,{value:e},t.children)}}}]);
|
||||
1
doc/website/build/assets/js/59dd8638.332ef181.js
Normal file
1
doc/website/build/assets/js/59dd8638.332ef181.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4194],{8285:(e,s,n)=>{n.r(s),n.d(s,{assets:()=>a,contentTitle:()=>t,default:()=>h,frontMatter:()=>i,metadata:()=>d,toc:()=>c});var o=n(4848),r=n(8453);const i={sidebar_position:1},t="Manage Docs Versions",d={id:"tutorial-extras/manage-docs-versions",title:"Manage Docs Versions",description:"Docusaurus can manage multiple versions of your docs.",source:"@site/../md/tutorial-extras/manage-docs-versions.md",sourceDirName:"tutorial-extras",slug:"/tutorial-extras/manage-docs-versions",permalink:"/docs/tutorial-extras/manage-docs-versions",draft:!1,unlisted:!1,editUrl:"https://github.com/holos-run/holos/edit/main/doc/md/../md/tutorial-extras/manage-docs-versions.md",tags:[],version:"current",lastUpdatedBy:"Jeff McCune",lastUpdatedAt:1719897028e3,sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Tutorial - Extras",permalink:"/docs/category/tutorial---extras"},next:{title:"Translate your site",permalink:"/docs/tutorial-extras/translate-your-site"}},a={},c=[{value:"Create a docs version",id:"create-a-docs-version",level:2},{value:"Add a Version Dropdown",id:"add-a-version-dropdown",level:2},{value:"Update an existing version",id:"update-an-existing-version",level:2}];function l(e){const s={code:"code",h1:"h1",h2:"h2",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.h1,{id:"manage-docs-versions",children:"Manage Docs Versions"}),"\n",(0,o.jsx)(s.p,{children:"Docusaurus can manage multiple versions of your docs."}),"\n",(0,o.jsx)(s.h2,{id:"create-a-docs-version",children:"Create a docs version"}),"\n",(0,o.jsx)(s.p,{children:"Release a version 1.0 of your project:"}),"\n",(0,o.jsx)(s.pre,{children:(0,o.jsx)(s.code,{className:"language-bash",children:"npm run docusaurus docs:version 1.0\n"})}),"\n",(0,o.jsxs)(s.p,{children:["The ",(0,o.jsx)(s.code,{children:"docs"})," folder is copied into ",(0,o.jsx)(s.code,{children:"versioned_docs/version-1.0"})," and ",(0,o.jsx)(s.code,{children:"versions.json"})," is created."]}),"\n",(0,o.jsx)(s.p,{children:"Your docs now have 2 versions:"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"1.0"})," at ",(0,o.jsx)(s.code,{children:"http://localhost:3000/docs/"})," for the version 1.0 docs"]}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"current"})," at ",(0,o.jsx)(s.code,{children:"http://localhost:3000/docs/next/"})," for the ",(0,o.jsx)(s.strong,{children:"upcoming, unreleased docs"})]}),"\n"]}),"\n",(0,o.jsx)(s.h2,{id:"add-a-version-dropdown",children:"Add a Version Dropdown"}),"\n",(0,o.jsx)(s.p,{children:"To navigate seamlessly across versions, add a version dropdown."}),"\n",(0,o.jsxs)(s.p,{children:["Modify the ",(0,o.jsx)(s.code,{children:"docusaurus.config.js"})," file:"]}),"\n",(0,o.jsx)(s.pre,{children:(0,o.jsx)(s.code,{className:"language-js",metastring:'title="docusaurus.config.js"',children:"export default {\n themeConfig: {\n navbar: {\n items: [\n // highlight-start\n {\n type: 'docsVersionDropdown',\n },\n // highlight-end\n ],\n },\n },\n};\n"})}),"\n",(0,o.jsx)(s.p,{children:"The docs version dropdown appears in your navbar:"}),"\n",(0,o.jsx)(s.p,{children:(0,o.jsx)(s.img,{alt:"Docs Version Dropdown",src:n(5432).A+"",width:"370",height:"302"})}),"\n",(0,o.jsx)(s.h2,{id:"update-an-existing-version",children:"Update an existing version"}),"\n",(0,o.jsx)(s.p,{children:"It is possible to edit versioned docs in their respective folder:"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"versioned_docs/version-1.0/hello.md"})," updates ",(0,o.jsx)(s.code,{children:"http://localhost:3000/docs/hello"})]}),"\n",(0,o.jsxs)(s.li,{children:[(0,o.jsx)(s.code,{children:"docs/hello.md"})," updates ",(0,o.jsx)(s.code,{children:"http://localhost:3000/docs/next/hello"})]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,r.R)(),...e.components};return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(l,{...e})}):l(e)}},5432:(e,s,n)=>{n.d(s,{A:()=>o});const o=n.p+"assets/images/docsVersionDropdown-35e13cbe46c9923327f30a76a90bff3b.png"},8453:(e,s,n)=>{n.d(s,{R:()=>t,x:()=>d});var o=n(6540);const r={},i=o.createContext(r);function t(e){const s=o.useContext(i);return o.useMemo((function(){return"function"==typeof e?e(s):{...s,...e}}),[s,e])}function d(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:t(e.components),o.createElement(i.Provider,{value:s},e.children)}}}]);
|
||||
1
doc/website/build/assets/js/5e90a9b3.d94f6fe0.js
Normal file
1
doc/website/build/assets/js/5e90a9b3.d94f6fe0.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8025],{7134:a=>{a.exports=JSON.parse('{"tag":{"label":"Hola","permalink":"/blog/tags/hola","description":"Hola tag description","allTagsPath":"/blog/tags","count":1,"unlisted":false},"listMetadata":{"permalink":"/blog/tags/hola","page":1,"postsPerPage":10,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}}')}}]);
|
||||
1
doc/website/build/assets/js/5e95c892.3f95a8ee.js
Normal file
1
doc/website/build/assets/js/5e95c892.3f95a8ee.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9647],{7121:(e,s,r)=>{r.r(s),r.d(s,{default:()=>l});r(6540);var t=r(4164),u=r(1213),a=r(7559),c=r(2831),n=r(781),i=r(4848);function l(e){return(0,i.jsx)(u.e3,{className:(0,t.A)(a.G.wrapper.docsPages),children:(0,i.jsx)(n.A,{children:(0,c.v)(e.route.routes)})})}}}]);
|
||||
1
doc/website/build/assets/js/6875c492.3846038e.js
Normal file
1
doc/website/build/assets/js/6875c492.3846038e.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4813],{7713:(e,t,n)=>{n.d(t,{A:()=>r});n(6540);var s=n(1312),a=n(9022),i=n(4848);function r(e){const{metadata:t}=e,{previousPage:n,nextPage:r}=t;return(0,i.jsxs)("nav",{className:"pagination-nav","aria-label":(0,s.T)({id:"theme.blog.paginator.navAriaLabel",message:"Blog list page navigation",description:"The ARIA label for the blog pagination"}),children:[n&&(0,i.jsx)(a.A,{permalink:n,title:(0,i.jsx)(s.A,{id:"theme.blog.paginator.newerEntries",description:"The label used to navigate to the newer blog posts page (previous page)",children:"Newer Entries"})}),r&&(0,i.jsx)(a.A,{permalink:r,title:(0,i.jsx)(s.A,{id:"theme.blog.paginator.olderEntries",description:"The label used to navigate to the older blog posts page (next page)",children:"Older Entries"}),isNext:!0})]})}},3892:(e,t,n)=>{n.d(t,{A:()=>r});n(6540);var s=n(7131),a=n(4651),i=n(4848);function r(e){let{items:t,component:n=a.A}=e;return(0,i.jsx)(i.Fragment,{children:t.map((e=>{let{content:t}=e;return(0,i.jsx)(s.i,{content:t,children:(0,i.jsx)(n,{children:(0,i.jsx)(t,{})})},t.metadata.permalink)}))})}},3069:(e,t,n)=>{n.r(t),n.d(t,{default:()=>A});n(6540);var s=n(4164),a=n(1312),i=n(5846),r=n(1213),l=n(7559),o=n(8774),c=n(6535),d=n(7713),g=n(1463),u=n(3892),p=n(996),h=n(1107),m=n(4848);function x(e){const t=function(){const{selectMessage:e}=(0,i.W)();return t=>e(t,(0,a.T)({id:"theme.blog.post.plurals",description:'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',message:"One post|{count} posts"},{count:t}))}();return(0,a.T)({id:"theme.blog.tagTitle",description:"The title of the page for a blog tag",message:'{nPosts} tagged with "{tagName}"'},{nPosts:t(e.count),tagName:e.label})}function j(e){let{tag:t}=e;const n=x(t);return(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(r.be,{title:n,description:t.description}),(0,m.jsx)(g.A,{tag:"blog_tags_posts"})]})}function b(e){let{tag:t,items:n,sidebar:s,listMetadata:i}=e;const r=x(t);return(0,m.jsxs)(c.A,{sidebar:s,children:[t.unlisted&&(0,m.jsx)(p.A,{}),(0,m.jsxs)("header",{className:"margin-bottom--xl",children:[(0,m.jsx)(h.A,{as:"h1",children:r}),t.description&&(0,m.jsx)("p",{children:t.description}),(0,m.jsx)(o.A,{href:t.allTagsPath,children:(0,m.jsx)(a.A,{id:"theme.tags.tagsPageLink",description:"The label of the link targeting the tag list page",children:"View All Tags"})})]}),(0,m.jsx)(u.A,{items:n}),(0,m.jsx)(d.A,{metadata:i})]})}function A(e){return(0,m.jsxs)(r.e3,{className:(0,s.A)(l.G.wrapper.blogPages,l.G.page.blogTagPostListPage),children:[(0,m.jsx)(j,{...e}),(0,m.jsx)(b,{...e})]})}},996:(e,t,n)=>{n.d(t,{A:()=>p});n(6540);var s=n(4164),a=n(1312),i=n(5260),r=n(4848);function l(){return(0,r.jsx)(a.A,{id:"theme.unlistedContent.title",description:"The unlisted content banner title",children:"Unlisted page"})}function o(){return(0,r.jsx)(a.A,{id:"theme.unlistedContent.message",description:"The unlisted content banner message",children:"This page is unlisted. Search engines will not index it, and only users having a direct link can access it."})}function c(){return(0,r.jsx)(i.A,{children:(0,r.jsx)("meta",{name:"robots",content:"noindex, nofollow"})})}var d=n(7559),g=n(7293);function u(e){let{className:t}=e;return(0,r.jsx)(g.A,{type:"caution",title:(0,r.jsx)(l,{}),className:(0,s.A)(t,d.G.common.unlistedBanner),children:(0,r.jsx)(o,{})})}function p(e){return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(c,{}),(0,r.jsx)(u,{...e})]})}}}]);
|
||||
1
doc/website/build/assets/js/6bb166bd.08087575.js
Normal file
1
doc/website/build/assets/js/6bb166bd.08087575.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8518],{3618:t=>{t.exports=JSON.parse('{"categoryGeneratedIndex":{"title":"Tutorial - Extras","slug":"/category/tutorial---extras","permalink":"/docs/category/tutorial---extras","sidebar":"tutorialSidebar","navigation":{"previous":{"title":"Congratulations!","permalink":"/docs/tutorial-basics/congratulations"},"next":{"title":"Manage Docs Versions","permalink":"/docs/tutorial-extras/manage-docs-versions"}}}}')}}]);
|
||||
1
doc/website/build/assets/js/73664a40.07e524ad.js
Normal file
1
doc/website/build/assets/js/73664a40.07e524ad.js
Normal file
File diff suppressed because one or more lines are too long
1
doc/website/build/assets/js/7661071f.5802daa3.js
Normal file
1
doc/website/build/assets/js/7661071f.5802daa3.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8737],{4137:(e,o,s)=>{s.r(o),s.d(o,{assets:()=>i,contentTitle:()=>r,default:()=>d,frontMatter:()=>l,metadata:()=>a,toc:()=>c});var n=s(4848),t=s(8453);const l={slug:"welcome",title:"Welcome",authors:["slorber","yangshun"],tags:["facebook","hello","docusaurus"]},r=void 0,a={permalink:"/blog/welcome",source:"@site/blog/2021-08-26-welcome/index.md",title:"Welcome",description:"Docusaurus blogging features are powered by the blog plugin.",date:"2021-08-26T00:00:00.000Z",tags:[{inline:!1,label:"Facebook",permalink:"/blog/tags/facebook",description:"Facebook tag description"},{inline:!1,label:"Hello",permalink:"/blog/tags/hello",description:"Hello tag description"},{inline:!1,label:"Docusaurus",permalink:"/blog/tags/docusaurus",description:"Docusaurus tag description"}],hasTruncateMarker:!1,authors:[{name:"S\xe9bastien Lorber",title:"Docusaurus maintainer",url:"https://sebastienlorber.com",imageURL:"https://github.com/slorber.png",key:"slorber"},{name:"Yangshun Tay",title:"Front End Engineer @ Facebook",url:"https://github.com/yangshun",imageURL:"https://github.com/yangshun.png",key:"yangshun"}],frontMatter:{slug:"welcome",title:"Welcome",authors:["slorber","yangshun"],tags:["facebook","hello","docusaurus"]},unlisted:!1,nextItem:{title:"MDX Blog Post",permalink:"/blog/mdx-blog-post"}},i={authorsImageUrls:[void 0,void 0]},c=[];function u(e){const o={a:"a",code:"code",img:"img",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(o.p,{children:[(0,n.jsx)(o.a,{href:"https://docusaurus.io/docs/blog",children:"Docusaurus blogging features"})," are powered by the ",(0,n.jsx)(o.a,{href:"https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog",children:"blog plugin"}),"."]}),"\n",(0,n.jsxs)(o.p,{children:["Simply add Markdown files (or folders) to the ",(0,n.jsx)(o.code,{children:"blog"})," directory."]}),"\n",(0,n.jsxs)(o.p,{children:["Regular blog authors can be added to ",(0,n.jsx)(o.code,{children:"authors.yml"}),"."]}),"\n",(0,n.jsx)(o.p,{children:"The blog post date can be extracted from filenames, such as:"}),"\n",(0,n.jsxs)(o.ul,{children:["\n",(0,n.jsx)(o.li,{children:(0,n.jsx)(o.code,{children:"2019-05-30-welcome.md"})}),"\n",(0,n.jsx)(o.li,{children:(0,n.jsx)(o.code,{children:"2019-05-30-welcome/index.md"})}),"\n"]}),"\n",(0,n.jsx)(o.p,{children:"A blog post folder can be convenient to co-locate blog post images:"}),"\n",(0,n.jsx)(o.p,{children:(0,n.jsx)(o.img,{alt:"Docusaurus Plushie",src:s(9014).A+"",width:"1500",height:"500"})}),"\n",(0,n.jsx)(o.p,{children:"The blog supports tags as well!"}),"\n",(0,n.jsxs)(o.p,{children:[(0,n.jsx)(o.strong,{children:"And if you don't want a blog"}),": just delete this directory, and use ",(0,n.jsx)(o.code,{children:"blog: false"})," in your Docusaurus config."]})]})}function d(e={}){const{wrapper:o}={...(0,t.R)(),...e.components};return o?(0,n.jsx)(o,{...e,children:(0,n.jsx)(u,{...e})}):u(e)}},9014:(e,o,s)=>{s.d(o,{A:()=>n});const n=s.p+"assets/images/docusaurus-plushie-banner-a60f7593abca1e3eef26a9afa244e4fb.jpeg"},8453:(e,o,s)=>{s.d(o,{R:()=>r,x:()=>a});var n=s(6540);const t={},l=n.createContext(t);function r(e){const o=n.useContext(l);return n.useMemo((function(){return"function"==typeof e?e(o):{...o,...e}}),[o,e])}function a(e){let o;return o=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),n.createElement(l.Provider,{value:o},e.children)}}}]);
|
||||
1
doc/website/build/assets/js/800bab45.03447adc.js
Normal file
1
doc/website/build/assets/js/800bab45.03447adc.js
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user