diff --git a/.github/workflows/docs-image-build-push.yml b/.github/workflows/docs-image-build-push.yml new file mode 100644 index 0000000..4c5eadf --- /dev/null +++ b/.github/workflows/docs-image-build-push.yml @@ -0,0 +1,70 @@ +name: Docs Image Build and Push + +on: + push: + branches: + - mkdocs-publish + paths: + - '**' + + repository_dispatch: + types: + # FULL WORKFLOW UNAVAILABLE + # - trigger-workflow-from-repo1 + # - trigger-workflow-from-repo2 + # - trigger-workflow-from-repo3 + + workflow_dispatch: + inputs: + tag: + description: 'Docker image tag (optional for manual trigger), e.g.: v0.2.22' + required: false + +jobs: + build_and_push: + runs-on: ubuntu-latest + + steps: + - name: Get GitHub App Installation Access Token + id: token + run: | + TOKEN="$(npx obtain-github-app-installation-access-token ci ${{ secrets.GH_APP_CREDENTIALS_TOKEN }})" + echo "::add-mask::$TOKEN" + echo "token=$( echo "$TOKEN" )" >> $GITHUB_OUTPUT + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Checkout repository + uses: actions/checkout@v3 + with: + token: ${{ steps.token.outputs.token }} + + - name: Login to GAR + uses: docker/login-action@v2 + with: + registry: PLACEHOLDER-docker.dev + username: _json_key + password: ${{ secrets.ARTIFACT_REGISTRY }} + + - name: Login to ACR + uses: docker/login-action@v2 + with: + registry:PLACEHOLDER-.azurecr.io + username: ${{ secrets.AZURE_CLIENT_ID }} + password: ${{ secrets.AZURE_CLIENT_SECRET }} + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v1 + with: + credentials_json: ${{ secrets.ARTIFACT_REGISTRY }} + + - name: Build and push Docs image + run: | + if [ -z "${{ github.event.inputs.tag }}" ]; then \ + make build GIT_TOKEN="x-access-token:${{ steps.token.outputs.token }}"; \ + make push GIT_TOKEN="x-access-token:${{ steps.token.outputs.token }}" ; \ + else \ + make build GIT_TOKEN="x-access-token:${{ steps.token.outputs.token }}" IMAGE_VERSION_OVERRIDE=${{ github.event.inputs.tag }}; \ + make push GIT_TOKEN="x-access-token:${{ steps.token.outputs.token }}" IMAGE_VERSION_OVERRIDE=${{ github.event.inputs.tag }}; \ + fi diff --git a/.github/workflows/update-dependencies-with-gilt.yml b/.github/workflows/update-dependencies-with-gilt.yml new file mode 100644 index 0000000..3b2a90e --- /dev/null +++ b/.github/workflows/update-dependencies-with-gilt.yml @@ -0,0 +1,50 @@ +name: Update dependencies with Gilt (Go) + +on: + + repository_dispatch: + types: + - trigger-workflow-from-repo1 + - trigger-workflow-from-repo2 + - trigger-workflow-from-repo3 + + + workflow_dispatch: + + + +jobs: + update-depdencies: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: '1.21.x' + + - name: Install Gilt + run: | + go install github.com/retr0h/gilt/v2@latest + export PATH=$PATH:$(go env GOPATH)/bin + + - name: Use Gilt to pull dependencies + run: | + gilt --debug overlay + + - name: Commit and push changes + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add . + if ! git diff-index --quiet HEAD; then + git commit -m "Update dependencies with Gilt" + git push origin main + else + echo "No changes to commit" + fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d10bfed --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +repo* \ No newline at end of file diff --git a/Giltfile.yaml b/Giltfile.yaml new file mode 100644 index 0000000..7a6838c --- /dev/null +++ b/Giltfile.yaml @@ -0,0 +1,19 @@ +lt vdebug: false +giltdir: ~/.gilt/clone +giltfile: Giltfile.yaml +parallel: true +repositories: + - git: git@github.com:Klarrio/documentation-repo-1.git + version: main + dstDir: repo1-docs + - git: git@github.com:Klarrio/documentation-repo-2.git + version: main + dstDir: repo2-docs + - git: git@github.com:Klarrio/documentation-repo-3.git + version: main + dstDir: repo3-docs + commands: + - cmd: go + args: + - run + - main.go \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9eebcf5 --- /dev/null +++ b/Makefile @@ -0,0 +1,62 @@ +.PHONY: build push build-no-cache serve-local serve-docker + +IMAGE_NAME :=PLACEHOLDER +IMAGE_REPO := PLACEHOLDER +IMAGE_REPO_ACR :=PLACEHOLDER + +# Fetch the latest version from Google Artifact Registry +# tr is used to transform the comma-separated tags into a newline-separated list +# sort -V and tail -n 1 are used to get the latest version +LATEST_VERSION := $(shell gcloud container images list-tags $(IMAGE_REPO)/$(IMAGE_NAME) --format='value(tags)' | tr ',' '\n' | sort -V | tail -n 1) + +# Increment the last digit of the version +IMAGE_VERSION := $(shell echo $(LATEST_VERSION) | awk -F. '{print $$1"."$$2"."$$3+1}') + +# Allow manual override of IMAGE_VERSION via environment or make command +ifdef IMAGE_VERSION_OVERRIDE +IMAGE_VERSION := $(IMAGE_VERSION_OVERRIDE) +endif + + + +# While building locally, you need to pass the token (e.g., make build GIT_TOKEN=yourtoken) +# While building via github workflows, it will get the GIT_TOKEN value assigned there (.github/workflows/makefile.yml) +GIT_TOKEN ?= "" +export GIT_TOKEN +# make a combination of version and the copyright statement for display on site +COPY=Copyright © 2023, +COPY_VERSION=${COPY} Version: ${IMAGE_VERSION} +export COPY_VERSION + +# Usage for local builds: make build GIT_TOKEN=yourtoken +build: + docker build --build-arg GIT_TOKEN="${GIT_TOKEN}" --build-arg COPY_VERSION="${COPY_VERSION}" -f Dockerfile -t $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_VERSION) . + +build-no-cache: + docker build --no-cache --build-arg GIT_TOKEN="${GIT_TOKEN}" --build-arg COPY_VERSION="${COPY_VERSION}" -f Dockerfile -t $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_VERSION) . + +push: + # Push to Google Artifact Registry (GAR) (if login is needed locally, uncomment the below line) + # gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin PLACEHOLDER + docker push $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_VERSION) + + # Push to Azure Container Registry (ACR) (if login is needed locally, uncomment the below line) + + # Tag the Docker image for ACR + docker tag $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_VERSION) $(IMAGE_REPO_ACR)/$(IMAGE_NAME):$(IMAGE_VERSION) + + # Push the Docker image to ACR + docker push $(IMAGE_REPO_ACR)/$(IMAGE_NAME):$(IMAGE_VERSION) + +serve-local: + rm -rf ~/.gilt/clone + rm -rf repo* + gilt overlay + mkdocs serve + +serve-docker: + # gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin PLACEHOLDER + @if ! docker images $(IMAGE_REPO)/$(IMAGE_NAME):$(LATEST_VERSION) | awk '{ print $$2 }' | grep -q -F $(LATEST_VERSION); then \ + docker pull $(IMAGE_REPO)/$(IMAGE_NAME):$(LATEST_VERSION); \ + fi + docker run -p 8080:8088 --rm $(IMAGE_REPO)/$(IMAGE_NAME):$(LATEST_VERSION) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..debb62c --- /dev/null +++ b/docs/index.md @@ -0,0 +1,3 @@ +# DOCUMENTATION SITE + +- [repo1](repo1-documentation/index.md) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..926a8dc --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module go.mod + +go 1.22.3 diff --git a/main.go b/main.go new file mode 100644 index 0000000..4083a11 --- /dev/null +++ b/main.go @@ -0,0 +1,174 @@ +package main + +import ( + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + replacementFullPath = map[string]string{ + "@repo1": "repo1-docs/", + "@repo2": "repo2-docs/", + "@repo3": "repo3-docs/", + } +) + +func find(root, ext string) []string { + var files []string + filepath.WalkDir(root, func(path string, d fs.DirEntry, e error) error { + if e != nil { + return e + } + if filepath.Ext(d.Name()) == ext { + files = append(files, path) + } + return nil + }) + return files +} + +func main() { + + for _, s := range find("./", ".md") { + convertReferences(s) + } + +} + +func convertReferences(file string) { + + bytes, err := os.ReadFile(file) + if err != nil { + fmt.Println(err) + return + } + + fileContents := string(bytes) // convert content to a 'string' + + fullLinkRegex := regexp.MustCompile(`\[([^\[]+)\]\((.[^)]*)\)`) + referenceRegex := regexp.MustCompile(`(@[a-zA-Z0-9]*):.*`) + + res := fullLinkRegex.FindAllStringSubmatch(fileContents, -1) + for _, item := range res { + if len(item) != 3 { + continue // invalid content + } + + fullLink := item[0] + linkText := item[1] + linkUri := item[2] + + found := false + for keyInDocs, newDirectory := range replacementFullPath { + newFileContents := replaceReferenceWithCorrectPath(fullLink, linkText, linkUri, keyInDocs, newDirectory, fileContents) + if newFileContents != "" { + fileContents = newFileContents + } + } + if !found && referenceRegex.Match([]byte(linkUri)) { + components := referenceRegex.FindAllStringSubmatch(linkUri, -1) + if len(components) == 0 { + fmt.Println("No valid reference to other repositories in link") + } + if len(components[0]) < 2 { + fmt.Println("No valid reference to other repositories in link") + } + fmt.Println(" ==> unknown component, no replacement found for", fmt.Sprintf("%s:", components[0][1]), "in", linkUri) + } + } + + writeUpdatedContentsBackToFile(file, fileContents) +} + +func replaceReferenceWithCorrectPath(fullLink, linkText, linkUri, keyInDocs, newDirectory, originalFileContents string) string { + if strings.HasPrefix(linkUri, fmt.Sprintf("%s:", keyInDocs)) { + trimmed := strings.TrimPrefix(linkUri, fmt.Sprintf("%s:", keyInDocs)) + + var replacement string + fileExists := doesReplacementFileExist(newDirectory + trimmed) + if !fileExists { + fileExists = doesReplacementFileExist(newDirectory + "docs/" + trimmed) + + if !fileExists { + fmt.Println(" ==> couldn't find replacement file for trimmed") + return "" + } + + mkDocsReference, err := getMkDocsSiteNameForRepo(newDirectory) + if err != nil { + fmt.Println("Error during getting mkdocs site name: ", err) + return "" + } + + replacement = fmt.Sprintf("[%s](%s/%s)", linkText, mkDocsReference, trimmed) + } else { + + mkDocsReference, err := getMkDocsSiteNameForRepo(newDirectory) + if err != nil { + fmt.Println("Error during getting mkdocs site name: ", err) + return "" + } + replacement = fmt.Sprintf("[%s](%s/%s)", linkText, mkDocsReference, trimmed) + } + + fmt.Println(" ==> fixing up", fullLink, "with", replacement) + fileContents := strings.ReplaceAll(originalFileContents, fullLink, replacement) + return fileContents + } + + return "" +} + +func getMkDocsSiteNameForRepo(folder string) (string, error) { + var mkDocsReference string + fileIO, err := os.OpenFile(fmt.Sprintf("%s/mkdocs.yml", folder), os.O_RDWR, 0600) + if err != nil { + return "", err + } + defer fileIO.Close() + rawBytes, err := io.ReadAll(fileIO) + if err != nil { + return "", err + } + lines := strings.Split(string(rawBytes), "\n") + for _, line := range lines { + if strings.Contains(line, "site_name") { + mkDocsReference = strings.ReplaceAll(strings.ToLower(strings.TrimPrefix(line, "site_name: ")), " ", "-") + } + } + + return mkDocsReference, nil +} + +func doesReplacementFileExist(file string) bool { + _, err := os.OpenFile(file, os.O_WRONLY, os.ModeAppend) + if err != nil { + return false + } + + return true +} + +func writeUpdatedContentsBackToFile(file, fileContents string) { + f, err := os.OpenFile(file, os.O_WRONLY, os.ModeAppend) + if err != nil { + fmt.Println("Error during opening file to write contents back: ", err) + return + } + + defer f.Close() + + _, err = f.WriteString(fileContents) + if err != nil { + fmt.Println("Error during writing contents back to file: ", err) + return + } + + // Issue a `Sync` to flush writes to stable storage. + f.Sync() +} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..4dc83bf --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,45 @@ +site_name: main documentation +plugins: + - search + - monorepo + + +nav: + - repo1: '!include ./repo1-docs/mkdocs.yml' + - repo2: '!include ./repo2-docs/mkdocs.yml' + - repo3: '!include ./repo3-docs/mkdocs.yml' + +theme: + name: material + features: + - navigation.tabs + - navigation.sections + # - toc.integrate + - navigation.top + - search.suggest + # - content.action.edit + # - content.action.view + - search.highlight + - content.tabs.link + - content.code.annotation + - content.code.copy + - navigation.footer + - toc.follow + + language: en + palette: + - scheme: default + toggle: + icon: material/toggle-switch-off-outline + name: Switch to dark mode + primary: navy + accent: purple + - scheme: slate + toggle: + icon: material/toggle-switch + name: Switch to light mode + primary: teal + accent: lime + + +repo_url: https://github.com/PLACEHOLDER