---
version: '3'

vars:
  REPOSITORY_LIST: .cache/repository-list.txt

tasks:
  access-token:
    deps:
      - :install:software:glab
      - :install:software:jq
      - create
    vars:
      REPO_ID:
        sh: glab api projects/:fullpath | jq '.id'
      REQUEST_DATA: '{ "name":"PROJECT_CI_ACCESS_TOKEN", "scopes":["write_repository", "read_repository"] }'
      TOKEN_STATUS:
        sh: type glab > /dev/null && glab api projects/:fullpath/access_tokens | jq -r '.[] | select(.name=="PROJECT_CI_ACCESS_TOKENX") | .id' || true
    run: once
    log:
      error: Failed to configure project-specific access token
      start: Configuring project-specific access token
      success: Configured project-specific access token
    cmds:
      - |
        curl -sSL --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" --header \
        'Content-Type:application/json' --data '{{.REQUEST_DATA}}' \
        'https://gitlab.com/api/v4/projects/{{.REPO_ID}}/access_tokens' > /dev/null
    status:
      - '[ -n "{{.TOKEN_STATUS}}" ] || ! glab api projects/:fullpath/access_tokens'

  branches:
    deps:
      - :install:software:glab
      - create
    vars:
      REPO_ID:
        sh: glab api projects/:fullpath | jq '.id'
    run: once
    log:
      error: Error encountered while setting up protected branches
      start: Setting up protected branches
      success: Set up protected branches
    cmds:
      - |
        glab api projects/:fullpath/protected_branches -X POST -f name=master -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=main -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=main -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=next -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=next -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=alpha -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=alpha -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=beta -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=beta -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=protected/* -f code_owner_approval_required=true &> /dev/null &
        glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=synchronize -f ref=master &> /dev/null &
        glab api projects/:fullpath/protected_branches -X POST -f name=synchronize -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
        if [[ '{{.REPOSITORY_TYPE}}' == 'ansible' ]]; then
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/linux -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=test/linux \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/darwin -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=test/darwin \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/windows -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=test/windows \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
        fi
        if [[ '{{.REPOSITORY_TYPE}}' == 'packer' ]]; then
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=hyperv -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=hyperv \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=kvm -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=kvm \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=parallels -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=parallels \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=virtualbox -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=virtualbox \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
          glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=vmware -f ref=master &> /dev/null &
          glab api projects/:fullpath/protected_branches -X POST -f name=vmware \
            -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null &
        fi
        wait

  create:
    deps:
      - :install:software:glab
      - :install:software:jq
      - :git:remotes
    vars:
      DESCRIPTION:
        sh: jq -r '.description' package.json
      GITLAB_GROUP:
        sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///' | sed 's!/[^/]*$!!'
      GITLAB_PATH:
        sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///'
      NAME:
        sh: jq -r '.blueprint.name' package.json
      PROJECT_TYPE:
        sh: if [[ $(jq -r '.private' package.json) == 'true' ]]; then echo '--private'; else echo '--public'; fi
      TMP:
        sh: mktemp
    run: once
    log:
      error: Encountered error while creating GitLab repository
      start: Ensuring GitLab repository has been created
      success: Ensured GitLab repository exists
    cmds:
      - cmd: |
          NO_PROMPT=1 glab repo create '{{.GITLAB_PATH}}' --group '{{.GITLAB_GROUP}}' \
          --description '{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}' --name '{{.NAME}}' \
          {{.PROJECT_TYPE}} --tag "$(jq -r '.keywords | tostring' package.json | sed 's/\[//' | sed 's/\]//')"
          task --list > /dev/null || (echo "ERROR: Invalid Taskfiles!" && exit 1)
          git add --all
          HUSKY=0 git commit --quiet -m "🎂 Birth" -n || true
          git push gitlab master
        ignore_error: true
    status:
      - '[ -z "$GITLAB_TOKEN" ] || (test -d .git && glab repo view "{{.GITLAB_PATH}}" > /dev/null)'
    preconditions:
      - sh: '[ "{{.DESCRIPTION}}" != "null" ]'
        msg: The `.description` in `package.json` must be set.
      - sh: '[ "{{.NAME}}" != "null" ]'
        msg: The `.blueprint.name` variable in `package.json` must be set.

  group:exec:
    cmds:
      - task: group:exec:{{if .CLI_ARGS}}cli{{else}}prompt{{end}}

  group:exec:cli:
    log:
      error: Failed to run group:exec logic
      start: Running group:exec logic
      success: Successfully ran group:exec logic
    cmds:
      - task git:gitlab:group:repositories -- {{index (splitList " ---- " .CLI_ARGS) 0}}
      - |
        BASE_PWD="$PWD"
        function execRepo() {
          REPO_DETAILS="$1"
          REPO_URL="$(echo "$REPO_DETAILS" | sed 's/   .*$//')"
          REPO_PATH="$(echo "$REPO_DETAILS" | sed 's/^.*   //')"
          DIR_NAME="$(dirname "$REPO_PATH")"
          mkdir -p "$DIR_NAME"
          if [ -d "$REPO_PATH" ]; then
            cd "$REPO_PATH"
            git pull origin master || continue
            cd "$BASE_PWD"
          else
            git clone "$REPO_URL" "$REPO_PATH"
          fi
          .config/log info "Running bash command on $REPO_PATH"
          (
            cd "$REPO_PATH"
            {{trimPrefix "'" (trimSuffix "'" (index (splitList " ---- " .CLI_ARGS) 1))}}
            cd "$BASE_PWD"
          )
          .config/log success "Finished running bash command on $REPO_PATH"
        }
        cat {{.REPOSITORY_LIST}} | (while IFS= read -r REPO_DETAILS; do
          .config/log info "Executing logic on $REPO_DETAILS"
          execRepo "$REPO_DETAILS"{{if eq .GROUP_EXEC_ASYNC "true"}} &{{end}}
        done{{if eq .GROUP_EXEC_ASYNC "true"}}
        wait){{else}}){{end}}

  group:exec:prompt:
    deps:
      - :install:software:glab
      - :install:software:jq
    interactive: true
    cmds:
      - |
        GITLAB_GROUPS="$(glab api --paginate groups | jq -r '.[].full_path')"
        GROUP_OPTION="$(gum choose "$ENVIRONMENT_OPTIONS")"
        GUM_CONFIRM_MSG="Would you like to use the previously run script (i.e. .cache/gitlab-group.sh)?"
        if [ -f .cache/gitlab-group.sh ] && gum confirm "$GUM_CONFIRM_MSG"; then
          bash .cache/gitlab-group.sh
        else
          TMP="$(mktemp)"
          gum write --placeholder "Enter the script you would like to run including the hashbang (CTRL+D to finish)" --show-cursor-line --show-line-numbers > "$TMP"
          bash "$TMP"
          mkdir -p .cache
          mv "$TMP" > .cache/gitlab-group.sh
        fi

  group:repositories:
    deps:
      - :install:software:glab
      - :install:software:jq
    summary: |
      # Return repositories belonging to group and sub-groups

      Given a GitLab group path (which can include subgroups as well), this task will cycle through
      the group and its' subgroups and generate a list of repositories. The repositories are
      saved to a file located at `{{.REPOSITORY_LIST}}`.

      **Example specifying a group and subgroup:**
      `task {{.TASK}} -- megabyte-labs/ansible-roles`
    log:
      error: Encountered error while generating a list of repositories
      start: Generating list of repositories
      success: List of repositories generated
    cmds:
      - mkdir -p .cache
      - rm -f {{.REPOSITORY_LIST}}
      - task: group:repositories:loop

  group:repositories:loop:
    cmds:
      - glab api "groups/{{urlquery .CLI_ARGS}}" | jq -r '.projects[] | .ssh_url_to_repo + "   " + .path_with_namespace' >> {{.REPOSITORY_LIST}}
      - |
        .config/log info 'Recursively acquiring repository details from subgroups'
        SUBGROUPS="$(glab api groups/{{urlquery .CLI_ARGS}}/subgroups | jq -r '.[] | .full_path')"
        if [ -n "$SUBGROUPS" ]; then
          echo "$SUBGROUPS" | (while IFS= read -r SUBGROUP; do
            task {{.TASK}} -- "$SUBGROUP" &
          done
          wait)
        fi

  ids:
    deps:
      - :install:software:glab
      - :install:software:jq
      - create
    log:
      error: Error acquiring GitLab project and group IDs
      start: Acquiring GitLab project and group IDs
      success: Saved GitLab project and group IDs to package.json
    cmds:
      - |
        API_RES="$(glab api projects/:fullpath)"
        PROJECT_ID="$(echo "$API_RES" | jq '.id')"
        TMP="$(mktemp)" && jq --arg projectId "$PROJECT_ID" '.blueprint.gitlab_project_id = $projectId' package.json > "$TMP"
        mv "$TMP" package.json
        GROUP_ID="$(echo "$API_RES" | jq '.namespace.id')"
        TMP_GROUP="$(mktemp)" && jq --arg groupId "$GROUP_ID" '.blueprint.gitlab_group_id = $groupId' package.json > "$TMP_GROUP"
        mv "$TMP_GROUP" package.json

  integrations:
    deps:
      - create
    run: once
    cmds:
      - task: integrations:github

  integrations:github:
    deps:
      - :install:software:glab
      - :install:software:jq
    log:
      error: Error enabling GitLab's GitHub integration
      start: Ensuring GitLab's GitHub integration is enabled
      success: GitLab's GitHub integration is enabled
    cmds:
      - glab api projects/:fullpath/integrations/github -X PUT -f token="$GITHUB_TOKEN"
        -f repository_url="$(jq -r '.blueprint.repository.github' package.json)" --silent
    status:
      - '[ -z "$GITLAB_TOKEN" ] || [ -z $GITHUB_TOKEN ]'

  mirror:
    deps:
      - :install:software:glab
      - :install:software:jq
      - :git:github:create
      - create
    vars:
      GITHUB_SLUG:
        sh: jq -r '.blueprint.repository.github' package.json | sed 's/.*\///'
      GITLAB_REPO_ID:
        sh: glab api projects/:fullpath | jq -r '.id'
      PUSH_MIRROR_COUNT:
        sh: glab api projects/:fullpath/remote_mirrors | jq '. | length'
    log:
      error: Error ensuring push/pull mirroring is enabled between GitLab and GitHub
      start: Ensuring push/pull mirroring from GitLab to GitHub is set up
      success: Push and pull mirroring from GitLab to GitHub are enabled
    cmds:
      - |
        if [[ "{{.PUSH_MIRROR_COUNT}}" == '0' ]]; then
          glab api projects/:fullpath/remote_mirrors --method POST --header "Content-Type: application/json" \
            -f "url=https://{{.GITHUB_USER}}:$GITHUB_TOKEN@github.com/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}.git" \
            -f 'enabled=true' > /dev/null
          .config/log success 'Successfully set up push mirroring from GitLab to GitHub'
        fi
      - >
        curl -s -H 'Content-Type: application/json' -H "Authorization: Bearer $GITLAB_TOKEN" -X PUT --data
        "{\"mirror\": true, \"import_url\": \"https://{{.GITHUB_USER}}:$GITHUB_TOKEN@github.com/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}.git\"}"
        'https://gitlab.com/api/v4/projects/{{.GITLAB_REPO_ID}}' > /dev/null
    status:
      - '[ -z "$GITLAB_TOKEN ] || [ -z "GITHUB_TOKEN ]'

  pipelines:
    deps:
      - :install:software:glab
      - :install:software:jq
      - create
    vars:
      PIPELINE_COUNT:
        sh: jq -r '.gitlab_pipelines | length' .variables.json
    log:
      error: Error setting up GitLab pipelines
      start: Ensuring GitLab pipelines are set up according to the configuration
      success: GitLab pipelines are set up
    cmds:
      - |
        PIPELINES="$(jq -r '.gitlab_pipelines' .variables.json)"
        PIPELINE_RES="$(glab api projects/:fullpath/pipeline_schedules)"
        for INDEX in {1..{{.PIPELINE_COUNT}}}; do
          PIPELINE_INDEX="$((INDEX - 1))"
          ACTIVE="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].active')"
          CRON="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].cron' | sed 's/"//g')"
          DESC="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].description')"
          REF="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].ref')"
          if (! echo "$PIPELINE_RES" | grep "$DESC") > /dev/null; then
            glab api projects/:fullpath/pipeline_schedules -X POST -f active="$ACTIVE" -f description="$DESC" -f ref="$REF" \
              -f cron="$CRON" -f cron_timezone='{{.TIMEZONE}}' --silent
            if [ "$DESC" != 'null' ]; then .config/log success "Pipeline with description of '${DESC}' successfully added"; fi
          else
            if [ "$DESC" != 'null' ]; then .config/log info "Pipeline with description of '${DESC}' already added"; fi
          fi
        done
    status:
      - '[ -z "$GITLAB_TOKEN" ]'

  pipelines:clear:
    deps:
      - create
    log:
      error: Error clearing GitLab pipelines
      start: Clearing GitLab pipelines
      success: Cleared GitLab pipelines
    cmds:
      - |
        TMP="$(mktemp)"
        glab api projects/:id/pipeline_schedules -X GET > "$TMP"
        for PIPELINE_ID in $(jq -r '.[].id' "$TMP"); do
          glab api projects/:fullpath/pipeline_schedules/"$PIPELINE_ID" -X DELETE
        done
    preconditions:
      - sh: '[ ! -z "$GITLAB_TOKEN" ]'
        msg: The `GITLAB_TOKEN` environment variable must be set to run this task

  protected:off:
    deps:
      - :install:software:glab
    vars:
      BRANCH: '{{if .BRANCH}}{{.BRANCH}}{{else}}{{.CLI_ARGS}}{{end}}'
    todo: figure out how to do it without deleteing the protection first
    cmds:
      - glab api projects/:fullpath/protected_branches/{{.BRANCH}} -X DELETE
      - glab api projects/:fullpath/protected_branches -X POST -f name={{.BRANCH}} -f allow_force_push=true > /dev/null
    status:
      - '[ -z "$GITLAB_TOKEN" ]'

  protected:on:
    deps:
      - :install:software:glab
    vars:
      BRANCH: '{{if .BRANCH}}{{.BRANCH}}{{else}}{{.CLI_ARGS}}{{end}}'
    todo: figure out how to do it without deleteing the protection first
    cmds:
      - glab api projects/:fullpath/protected_branches/{{.BRANCH}} -X DELETE
      - glab api projects/:fullpath/protected_branches -X POST -f name={{.BRANCH}} -f allow_force_push=false > /dev/null
    status:
      - '[ -z "$GITLAB_TOKEN" ]'

  ssh-keys:
    deps:
      - :install:software:glab
      - :install:software:jq
    cmds:
      - .config/log info 'Clearing GitLab SSH keys'
      - |
        KEY_URL='https://gitlab.com/api/v4/user/keys'
        KEYIDS="$(curl -sSL --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$KEY_URL" | jq '.[].id')"
        for KEYID in $KEYIDS; do
          curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" -X DELETE "$KEY_URL/$KEYID"
        done
      - .config/log start 'Syncing SSH keys with GitLab'
      - |
        for KEY in `ls $HOME/.ssh/*.pub`; do
          TITLE="$(cat $KEY | sed 's/\(.*\=\) \(.*\)$/\2/')"
          glab ssh-key add $KEY -t "$TITLE"
        done
      - .config/log success 'Finished syncing SSH keys with GitLab'
    preconditions:
      - sh: '[ -n "$GITLAB_TOKEN" ]'
        msg: The GITLAB_TOKEN must be set to a personal access token.

  update:
    run: once
    cmds:
      - task: update:deps
    status:
      - '[ -z "$GITLAB_TOKEN" ] || ! type glab &> /dev/null'

  update:deps:
    deps:
      - access-token
      - branches
      - mirror
      - integrations
      - ids
      - pipelines
      - update:meta
      - wiki

  update:meta:
    deps:
      - :install:software:glab
      - :install:software:jq
      - create
    vars:
      DESCRIPTION:
        sh: jq -r '.description' package.json
      ISSUES_TEMPLATE: '## Summary\r\n\r\n(Summarize the bug encountered concisely)\r\n\r\n##
        Steps to reproduce\r\n\r\n(How one can reproduce the issue - this is very important)\r\n\r\n##
        Example Project\r\n\r\n(If possible, please create an example project here on GitLab.com that
        exhibits the problematic\r\nbehavior, and link to it here in the bug report.\r\nIf you are using
        an older version of GitLab, this will also determine whether the bug has been fixed\r\nin a more
        recent version)\r\n\r\n## What is the current bug behavior?\r\n\r\n(What actually happens)\r\n\r\n##
        What is the expected correct behavior?\r\n\r\n(What you should see instead)\r\n\r\n## Relevant
        logs and/or screenshots\r\n\r\n(Paste any relevant logs - please use code blocks (```) to format
        console output, logs, and code, as\r\nit is very hard to read otherwise.)\r\n\r\n## Possible
        fixes\r\n\r\n(If you can, link to the line of code that might be responsible for the problem)'
      NAME:
        sh: jq -r '.blueprint.name' package.json
      PROJECT_TYPE:
        sh: if [[ $(jq -r '.private' package.json) == 'true' ]]; then echo 'private'; else echo 'public'; fi
      TEST_COVERAGE_REGEX:
        sh: jq -r '.build_coverage_regex' .variables.json | sed 's/^null$//'
    log:
      error: Error ensuring GitLab metadata is up-to-date
      start: Ensuring GitLab metadata is up-to-date
      success: GitLab metadata is up-to-date
    cmds:
      - |
        KEYWORDS="$(jq -r '.keywords | tostring' package.json | sed 's/\[//' | sed 's/\]//' | sed 's/"//g')"
        PROJECT_ID="$(glab api projects/:fullpath -X PUT -f build_coverage_regex="{{.TEST_COVERAGE_REGEX}}" \
        -f wiki_enabled={{.GITLAB_WIKI}} -f visibility="{{.PROJECT_TYPE}}" -f topics="$KEYWORDS" | jq '.id')"
        curl -s -H 'Content-Type: application/json' -H "Authorization: Bearer $GITLAB_TOKEN" -X PUT --data \
        '{"description": "{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}", "issues_template": "{{.ISSUES_TEMPLATE}}", "name": "{{.NAME}}"}' \
        "https://gitlab.com/api/v4/projects/$PROJECT_ID" > /dev/null
    status:
      - '[ -z "$GITLAB_TOKEN" ]'
    preconditions:
      - sh: '[ "{{.DESCRIPTION}}" != "null" ]'
        msg: The `.description` in `package.json` must be set.
      - sh: '[ "{{.NAME}}" != "null" ]'
        msg: The `.blueprint.name` variable in `package.json` must be set.

  wiki:
    deps:
      - :common:update:variables
      - :install:software:glab
      - :install:software:jq
      - create
    vars:
      DOCS_URL:
        sh: jq -r '.docs.link' .variables.json
    log:
      error: Failed to update GitLab wiki settings
      start: Setting GitLab wiki settings
      success: GitLab wiki settings are up-to-date
    cmds:
      - glab api projects/:fullpath/services/external-wiki -X PUT -f external_wiki_url="{{.DOCS_URL}}" --silent
    status:
      - '[ -z "$GITLAB_TOKEN" ]'
    preconditions:
      - sh: '[ "{{.DOCS_URL}}" != "null" ]'
        msg: The `.docs.link` variable in `.variables.json` must be set.
      - sh: '[ "{{.GROUP}}" != "null" ]'
        msg: The `.group` variable in `.variables.json` must be set.
