├── .all-contributorsrc ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ └── pull-request.yml ├── .gitignore ├── .pre-commit.config.yaml ├── .vscode └── tasks.json ├── README.md ├── build.ps1 ├── build ├── GitVersion.yml ├── functions │ ├── New-SlackMessage.tasks.ps1 │ ├── Update-SlackMessage.tasks.ps1 │ └── check.tasks.ps1 ├── requirements.psd1 ├── settings │ ├── PSScriptAnalyzerSettings.psd1 │ └── powershell-formatting-settings.psd1 └── tasks │ ├── tasks-aws │ ├── s3.tasks.ps1 │ ├── ssm.automation.tasks.ps1 │ └── ssm.commands.tasks.ps1 │ ├── tasks-ci │ ├── git.tasks.ps1 │ └── tasks.secrets.tasks.ps1 │ ├── tasks-configuration │ ├── check-apps.tasks.ps1 │ └── check-powershell-configuration.tasks.ps1 │ ├── tasks-docker │ └── docker.codespaces.tasks.ps1 │ ├── tasks-docs │ ├── asciidoc.tasks.ps1 │ ├── changelog.tasks.ps1 │ └── markdown.tasks.ps1 │ ├── tasks-invokebuild │ └── output.tasks.ps1 │ ├── tasks-notify │ ├── README.md │ └── datadog.tasks.ps1 │ ├── tasks-powershell-modules │ ├── check-powershell-modules.tasks.ps1 │ └── check-psdepend.tasks.ps1 │ ├── tasks-powershell │ ├── powershell.format.tasks.ps1 │ ├── powershell.install.tasks.ps1 │ ├── powershell.lint.tasks.ps1 │ └── powershell.pester.tasks.ps1 │ ├── tasks-terraform │ └── terraform.fmt.tasks.ps1 │ └── tasks-tidy │ ├── clean.tasks.ps1 │ └── vscode.tasks.ps1 ├── renovate.json └── tasks.build.ps1 /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "invoke-build-tasks", 3 | "projectOwner": "sheldonhull", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": true, 11 | "commitConvention": "gitmoji", 12 | "contributors": [ 13 | { 14 | "login": "sheldonhull", 15 | "name": "sheldonhull", 16 | "avatar_url": "https://avatars.githubusercontent.com/u/3526320?v=4", 17 | "profile": "https://www.sheldonhull.com/", 18 | "contributions": [ 19 | "code" 20 | ] 21 | } 22 | ], 23 | "contributorsPerLine": 7 24 | } 25 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/codespaces-linux/.devcontainer/base.Dockerfile 2 | FROM homebrew/brew:latest AS DOCKERBREW 3 | FROM mcr.microsoft.com/vscode/devcontainers/universal:2-focal AS BASE 4 | ARG USERNAME="codespace" 5 | # ** [Optional] Uncomment this section to install additional packages. ** 6 | # USER root 7 | # 8 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | # && apt-get -y install --no-install-recommends 10 | # 11 | # USER codespace 12 | USER root 13 | RUN useradd -m -s /bin/bash linuxbrew && \ 14 | echo 'linuxbrew ALL=(ALL) NOPASSWD:ALL' >>/etc/sudoers 15 | 16 | USER linuxbrew 17 | COPY --from=DOCKERBREW /home/linuxbrew/.linuxbrew /usr/local/bin/brew 18 | 19 | 20 | USER root 21 | RUN chmod -R a+rwx /usr/local/bin/brew 22 | USER $USERNAME 23 | ENV PATH="/usr/local/bin/brew/bin:${PATH}" 24 | RUN echo "Verifying brew installed" && brew --version 25 | 26 | RUN sudo sh -c "$(curl -ssL https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin 27 | 28 | RUN mkdir -p /home/$USERNAME/.vscode-server/extensions \ 29 | /home/$USERNAME/.vscode-server/extensions \ 30 | && chown -R $USERNAME \ 31 | /home/$USERNAME/.vscode-server \ 32 | /home/$USERNAME/.vscode-server 33 | 34 | # Task Is a CLI Tool cross platform compatible alternative to Make 35 | RUN sudo sh -c "$(curl -ssL https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin 36 | 37 | #ENV SHELL=/opt/microsoft/powershell/7/pwsh 38 | # needs github token 39 | #RUN curl -s "https://raw.githubusercontent.com/owenrumney/squealer/main/scripts/install.sh" | bash 40 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/codespaces-linux 3 | { 4 | "name": "GitHub Codespaces (Default)", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | "INSTALL_DOCKER": "true" 9 | } 10 | }, 11 | "settings": { 12 | "terminal.integrated.shell.linux": "/opt/microsoft/powershell/7/pwsh", 13 | "go.useGoProxyToCheckForToolUpdates": false, 14 | "go.useLanguageServer": true, 15 | "go.gopath": "/go", 16 | "go.goroot": "/usr/local/go", 17 | "go.toolsGopath": "/go/bin", 18 | "python.pythonPath": "/opt/python/latest/bin/python", 19 | "python.linting.enabled": true, 20 | "python.linting.pylintEnabled": true, 21 | "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", 22 | "python.formatting.blackPath": "/usr/local/py-utils/bin/black", 23 | "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", 24 | "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", 25 | "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", 26 | "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", 27 | "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", 28 | "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", 29 | "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint", 30 | "lldb.executable": "/usr/bin/lldb", 31 | "files.watcherExclude": { 32 | "**/target/**": true 33 | } 34 | }, 35 | "remoteUser": "codespace", 36 | "overrideCommand": false, 37 | "workspaceMount": "source=${localWorkspaceFolder},target=/home/codespace/workspace,type=bind,consistency=cached", 38 | "workspaceFolder": "/home/codespace/workspace", 39 | "mounts": [ 40 | "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind", 41 | "source=${localEnv:HOME}${localEnv:USERPROFILE}/.aws/credentials,target=/home/codespace/.aws/credentials,type=bind,consistency=cached", 42 | "source=default-vscode-extensions,target=/root/.vscode-server/extensions,type=volume", 43 | ], 44 | "runArgs": [ 45 | "--cap-add=SYS_PTRACE", 46 | "--security-opt", 47 | "seccomp=unconfined", 48 | "--privileged", 49 | "--init" 50 | ], 51 | // Add the IDs of extensions you want installed when the container is created. 52 | "extensions": [ 53 | "GitHub.vscode-pull-request-github", 54 | "ms-vscode.powershell-preview" 55 | ], 56 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 57 | // "forwardPorts": [], 58 | // "oryx build" will automatically install your dependencies and attempt to build your project 59 | // "postCreateCommand": [ 60 | // "oryx build -p virtualenv_name=.venv || echo 'Could not auto-build. Skipping.'" 61 | // "new-alias ib 'invoke-build' -force", 62 | // "pwsh -c \"&./build.ps1 -tasks job-bootstrap -LoadConstants\"" 63 | // ], 64 | // "containerEnv": { 65 | // "PATH": "${containerEnv:PATH}:/home/linuxbrew/.linuxbrew/bin", 66 | // } 67 | } 68 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: 'Run Pull Request Update Checks' 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - reopened 7 | - edited 8 | - synchronize 9 | 10 | defaults: 11 | run: 12 | shell: pwsh 13 | working-directory: . 14 | jobs: 15 | github-pr: 16 | name: Linting 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: aslafy-z/conventional-pr-title-action@master 20 | name: 'Validate Title Adheres to Conventional Commit Format' 21 | with: 22 | success-state: Title follows the specification. 23 | failure-state: Title does not follow the specification. 24 | context-name: conventional-pr-title 25 | preset: conventional-changelog-angular@latest 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | run-linting: 29 | name: 'Apply FMT & Scan For Secrets' 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v3 33 | with: 34 | fetch-depth: 0 # don't use shallow fetch as I'm trying to commit and push back up changed files to the main 35 | # token: ${{ secrets.GITHUB_TOKEN }} 36 | - name: Super-Linter 37 | uses: github/super-linter@v4 38 | env: 39 | VALIDATE_ALL_CODEBASE: false #check only the changed files 40 | VALIDATE_POWERSHELL: true 41 | #VALIDATE_MARKDOWN: true 42 | VALIDATE_SHELL_SHFMT: true 43 | VALIDATE_TERRAFORM: true 44 | VALIDATE_TERRAFORM_TERRASCAN: true 45 | VALIDATE_YAML: true 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | FILTER_REGEX_EXCLUDE: (.*Install-RequiredModules.tasks.ps1)|(.*ConvertTo-MarkdownTable.ps1)|(.*ParseExtraParameters.ps1) 48 | continue-on-error: true 49 | - name: Format Code 50 | shell: pwsh 51 | run: '& (Join-Path $ENV:GITHUB_WORKSPACE "build.ps1") -Task "tidy","git-commit-push"' 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | - name: git status 55 | run: git status 56 | - name: Git Auto Commit 57 | uses: stefanzweifel/git-auto-commit-action@v4.15.0 58 | with: 59 | commit_message: "chore: github action cleanup" 60 | - name: gitleaks-action 61 | uses: zricethezav/gitleaks-action@master 62 | - name: FIXME alert 63 | uses: bbugh/action-fixme-check@v1.1.0 64 | - uses: codespell-project/codespell-problem-matcher@v1 65 | # - uses: xt0rted/markdownlint-problem-matcher@v1 66 | # run: markdownlint **/*.md --ignore node_modules 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/macos,terraform,windows,powershell,linux 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,terraform,windows,powershell,linux 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### macOS ### 20 | # General 21 | .DS_Store 22 | .AppleDouble 23 | .LSOverride 24 | 25 | # Icon must end with two \r 26 | Icon 27 | 28 | # Thumbnails 29 | ._* 30 | 31 | # Files that might appear in the root of a volume 32 | .DocumentRevisions-V100 33 | .fseventsd 34 | .Spotlight-V100 35 | .TemporaryItems 36 | .Trashes 37 | .VolumeIcon.icns 38 | .com.apple.timemachine.donotpresent 39 | 40 | # Directories potentially created on remote AFP share 41 | .AppleDB 42 | .AppleDesktop 43 | Network Trash Folder 44 | Temporary Items 45 | .apdisk 46 | 47 | ### PowerShell ### 48 | # Exclude packaged modules 49 | *.zip 50 | 51 | # Exclude .NET assemblies from source 52 | *.dll 53 | 54 | ### Terraform ### 55 | # Local .terraform directories 56 | **/.terraform/* 57 | 58 | # .tfstate files 59 | *.tfstate 60 | *.tfstate.* 61 | 62 | # Crash log files 63 | crash.log 64 | 65 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 66 | # .tfvars files are managed as part of configuration and so should be included in 67 | # version control. 68 | # 69 | # example.tfvars 70 | 71 | # Ignore override files as they are usually used to override resources locally and so 72 | # are not checked in 73 | override.tf 74 | override.tf.json 75 | *_override.tf 76 | *_override.tf.json 77 | 78 | # Include override files you do wish to add to version control using negated pattern 79 | # !example_override.tf 80 | 81 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 82 | # example: *tfplan* 83 | 84 | ### Windows ### 85 | # Windows thumbnail cache files 86 | Thumbs.db 87 | Thumbs.db:encryptable 88 | ehthumbs.db 89 | ehthumbs_vista.db 90 | 91 | # Dump file 92 | *.stackdump 93 | 94 | # Folder config file 95 | [Dd]esktop.ini 96 | 97 | # Recycle Bin used on file shares 98 | $RECYCLE.BIN/ 99 | 100 | # Windows Installer files 101 | *.cab 102 | *.msi 103 | *.msix 104 | *.msm 105 | *.msp 106 | 107 | # Windows shortcuts 108 | *.lnk 109 | 110 | # End of https://www.toptal.com/developers/gitignore/api/macos,terraform,windows,powershell,linux 111 | 112 | packages/ 113 | artifacts/ 114 | *.clixml 115 | .artifacts 116 | .cache/ 117 | *.DS_Store 118 | -------------------------------------------------------------------------------- /.pre-commit.config.yaml: -------------------------------------------------------------------------------- 1 | # Setup: pre-commit install 2 | # Upgrade: pre-commut autoupgrade 3 | # Run: pre-commit run --all-files 4 | # https://pre-commit.com/hooks.html 5 | default_language_version: 6 | # force all unspecified python hooks to run python3 7 | python: python3 8 | repos: 9 | - repo: https://github.com/pre-commit/pre-commit-hooks 10 | rev: v3.4.0 # Use the ref you want to point at 11 | hooks: 12 | - id: trailing-whitespace 13 | args: [--markdown-linebreak-ext=md] 14 | - id: check-byte-order-marker 15 | - id: check-case-conflict 16 | - id: detect-aws-credentials 17 | args: [--allow-missing-credentials] 18 | - id: mixed-line-ending 19 | args: [--fix=lf] 20 | - id: pretty-format-json 21 | - id: check-yaml 22 | - id: detect-private-key 23 | - id: end-of-file-fixer 24 | - id: fix-byte-order-marker 25 | 26 | # https://pre-commit.com/#supported-languages 27 | - repo: local 28 | hooks: 29 | # FIRST TIME SETUP: GO111MODULE=on go get github.com/zricethezav/gitleaks/v4@latest 30 | - id: local-run 31 | name: Scan for secrets 32 | entry: gitleaks --pretty -v 33 | language: system 34 | #entry: bash -c gitleaks 35 | #entry: docker run --rm -v ${pwd}:/code/ zricethezav/gitleaks -v --repo-path=/code/gitleaks 36 | # docker_image: zricethezav/gitleaks:latest 37 | # language: docker_image 38 | # entry: zricethezav/gitleaks:latest -v 39 | # curl --silent https://raw.githubusercontent.com/thoughtworks/talisman/master/global_install_scripts/install.bash > /tmp/install_talisman.bash && /bin/bash /tmp/install_talisman.bash 40 | # &/Users/$(whoami)/.talisman/bin/talisman_darwin_amd64 --scanWithHtml 41 | # cd talisman_html_report && python -m SimpleHTTPServer 3000 42 | - repo: local 43 | hooks: 44 | - id: talisman-precommit 45 | name: talisman 46 | entry: bash -c 'if [ -n "${TALISMAN_HOME:-}" ]; then ${TALISMAN_HOME}/talisman_hook_script pre-commit; else echo "TALISMAN does not exist. Consider installing from https://github.com/thoughtworks/talisman . If you already have talisman installed, please ensure TALISMAN_HOME variable is set to where talisman_hook_script resides, for example, TALISMAN_HOME=${HOME}/.talisman/bin"; fi' 47 | language: system 48 | pass_filenames: false 49 | types: [text] 50 | verbose: false 51 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Do not edit! This file is generated by New-VSCodeTask.ps1 2 | // Modify the build script or tasks-merge.json and recreate. 3 | { 4 | "version": "2.0.0", 5 | "windows": { 6 | "options": { 7 | "shell": { 8 | "executable": "pwsh", 9 | "args": [ 10 | "-NoProfile", 11 | "-ExecutionPolicy", 12 | "Bypass", 13 | "-Command" 14 | ] 15 | } 16 | } 17 | }, 18 | "linux": { 19 | "options": { 20 | "shell": { 21 | "executable": "/usr/bin/pwsh", 22 | "args": [ 23 | "-NoProfile", 24 | "-Command" 25 | ] 26 | } 27 | } 28 | }, 29 | "osx": { 30 | "options": { 31 | "shell": { 32 | "executable": "/usr/local/bin/pwsh", 33 | "args": [ 34 | "-NoProfile", 35 | "-Command" 36 | ] 37 | } 38 | } 39 | }, 40 | "tasks": [ 41 | { 42 | "label": "s3-upload-directory", 43 | "type": "shell", 44 | "command": "./build.ps1 -LoadConstants -Task s3-upload-directory -File './tasks.build.ps1'", 45 | "problemMatcher": "$msCompile", 46 | "presentation": { 47 | "echo": false, 48 | "showReuseMessage": false 49 | }, 50 | "group": { 51 | "kind": "build", 52 | "isDefault": true 53 | } 54 | }, 55 | { 56 | "label": "ssm-run-automation-example", 57 | "type": "shell", 58 | "command": "./build.ps1 -LoadConstants -Task ssm-run-automation-example -File './tasks.build.ps1'", 59 | "problemMatcher": "$msCompile", 60 | "presentation": { 61 | "echo": false, 62 | "showReuseMessage": false 63 | } 64 | }, 65 | { 66 | "label": "ssm-run-command-doc", 67 | "type": "shell", 68 | "command": "./build.ps1 -LoadConstants -Task ssm-run-command-doc -File './tasks.build.ps1'", 69 | "problemMatcher": "$msCompile", 70 | "presentation": { 71 | "echo": false, 72 | "showReuseMessage": false 73 | } 74 | }, 75 | { 76 | "label": "terraform-fmt", 77 | "type": "shell", 78 | "command": "./build.ps1 -LoadConstants -Task terraform-fmt -File './tasks.build.ps1'", 79 | "problemMatcher": "$msCompile", 80 | "presentation": { 81 | "echo": false, 82 | "showReuseMessage": false 83 | } 84 | }, 85 | { 86 | "label": "docs-generate-invoke-build-docs", 87 | "type": "shell", 88 | "command": "./build.ps1 -LoadConstants -Task docs-generate-invoke-build-docs -File './tasks.build.ps1'", 89 | "problemMatcher": "$msCompile", 90 | "presentation": { 91 | "echo": false, 92 | "showReuseMessage": false 93 | } 94 | }, 95 | { 96 | "label": "changelog-github", 97 | "type": "shell", 98 | "command": "./build.ps1 -LoadConstants -Task changelog-github -File './tasks.build.ps1'", 99 | "problemMatcher": "$msCompile", 100 | "presentation": { 101 | "echo": false, 102 | "showReuseMessage": false 103 | } 104 | }, 105 | { 106 | "label": "docs-initialize-readme", 107 | "type": "shell", 108 | "command": "./build.ps1 -LoadConstants -Task docs-initialize-readme -File './tasks.build.ps1'", 109 | "problemMatcher": "$msCompile", 110 | "presentation": { 111 | "echo": false, 112 | "showReuseMessage": false 113 | } 114 | }, 115 | { 116 | "label": "powershell-format-code", 117 | "type": "shell", 118 | "command": "./build.ps1 -LoadConstants -Task powershell-format-code -File './tasks.build.ps1'", 119 | "problemMatcher": "$msCompile", 120 | "presentation": { 121 | "echo": false, 122 | "showReuseMessage": false 123 | } 124 | }, 125 | { 126 | "label": "powershell-installmodules", 127 | "type": "shell", 128 | "command": "./build.ps1 -LoadConstants -Task powershell-installmodules -File './tasks.build.ps1'", 129 | "problemMatcher": "$msCompile", 130 | "presentation": { 131 | "echo": false, 132 | "showReuseMessage": false 133 | } 134 | }, 135 | { 136 | "label": "powershell-lint", 137 | "type": "shell", 138 | "command": "./build.ps1 -LoadConstants -Task powershell-lint -File './tasks.build.ps1'", 139 | "problemMatcher": "$msCompile", 140 | "presentation": { 141 | "echo": false, 142 | "showReuseMessage": false 143 | } 144 | }, 145 | { 146 | "label": "powershell-pester", 147 | "type": "shell", 148 | "command": "./build.ps1 -LoadConstants -Task powershell-pester -File './tasks.build.ps1'", 149 | "problemMatcher": "$msCompile", 150 | "presentation": { 151 | "echo": false, 152 | "showReuseMessage": false 153 | } 154 | }, 155 | { 156 | "label": "git-commit-push", 157 | "type": "shell", 158 | "command": "./build.ps1 -LoadConstants -Task git-commit-push -File './tasks.build.ps1'", 159 | "problemMatcher": "$msCompile", 160 | "presentation": { 161 | "echo": false, 162 | "showReuseMessage": false 163 | } 164 | }, 165 | { 166 | "label": "git-add-fixup", 167 | "type": "shell", 168 | "command": "./build.ps1 -LoadConstants -Task git-add-fixup -File './tasks.build.ps1'", 169 | "problemMatcher": "$msCompile", 170 | "presentation": { 171 | "echo": false, 172 | "showReuseMessage": false 173 | } 174 | }, 175 | { 176 | "label": "git_quick_commit_and_push", 177 | "type": "shell", 178 | "command": "./build.ps1 -LoadConstants -Task git_quick_commit_and_push -File './tasks.build.ps1'", 179 | "problemMatcher": "$msCompile", 180 | "presentation": { 181 | "echo": false, 182 | "showReuseMessage": false 183 | } 184 | }, 185 | { 186 | "label": "git-ci-commit-push", 187 | "type": "shell", 188 | "command": "./build.ps1 -LoadConstants -Task git-ci-commit-push -File './tasks.build.ps1'", 189 | "problemMatcher": "$msCompile", 190 | "presentation": { 191 | "echo": false, 192 | "showReuseMessage": false 193 | } 194 | }, 195 | { 196 | "label": "secrets-squealer-setup", 197 | "type": "shell", 198 | "command": "./build.ps1 -LoadConstants -Task secrets-squealer-setup -File './tasks.build.ps1'", 199 | "problemMatcher": "$msCompile", 200 | "presentation": { 201 | "echo": false, 202 | "showReuseMessage": false 203 | } 204 | }, 205 | { 206 | "label": "secrets-squealer-scan", 207 | "type": "shell", 208 | "command": "./build.ps1 -LoadConstants -Task secrets-squealer-scan -File './tasks.build.ps1'", 209 | "problemMatcher": "$msCompile", 210 | "presentation": { 211 | "echo": false, 212 | "showReuseMessage": false 213 | } 214 | }, 215 | { 216 | "label": "docs-output-invoke-build-docs", 217 | "type": "shell", 218 | "command": "./build.ps1 -LoadConstants -Task docs-output-invoke-build-docs -File './tasks.build.ps1'", 219 | "problemMatcher": "$msCompile", 220 | "presentation": { 221 | "echo": false, 222 | "showReuseMessage": false 223 | } 224 | }, 225 | { 226 | "label": "check-powershell-modules", 227 | "type": "shell", 228 | "command": "./build.ps1 -LoadConstants -Task check-powershell-modules -File './tasks.build.ps1'", 229 | "problemMatcher": "$msCompile", 230 | "presentation": { 231 | "echo": false, 232 | "showReuseMessage": false 233 | } 234 | }, 235 | { 236 | "label": "load-powershell-modules", 237 | "type": "shell", 238 | "command": "./build.ps1 -LoadConstants -Task load-powershell-modules -File './tasks.build.ps1'", 239 | "problemMatcher": "$msCompile", 240 | "presentation": { 241 | "echo": false, 242 | "showReuseMessage": false 243 | } 244 | }, 245 | { 246 | "label": "check-run-psdepend-install", 247 | "type": "shell", 248 | "command": "./build.ps1 -LoadConstants -Task check-run-psdepend-install -File './tasks.build.ps1'", 249 | "problemMatcher": "$msCompile", 250 | "presentation": { 251 | "echo": false, 252 | "showReuseMessage": false 253 | } 254 | }, 255 | { 256 | "label": "powershell-import-requirements", 257 | "type": "shell", 258 | "command": "./build.ps1 -LoadConstants -Task powershell-import-requirements -File './tasks.build.ps1'", 259 | "problemMatcher": "$msCompile", 260 | "presentation": { 261 | "echo": false, 262 | "showReuseMessage": false 263 | } 264 | }, 265 | { 266 | "label": "clean", 267 | "type": "shell", 268 | "command": "./build.ps1 -LoadConstants -Task clean -File './tasks.build.ps1'", 269 | "problemMatcher": "$msCompile", 270 | "presentation": { 271 | "echo": false, 272 | "showReuseMessage": false 273 | } 274 | }, 275 | { 276 | "label": "deepclean", 277 | "type": "shell", 278 | "command": "./build.ps1 -LoadConstants -Task deepclean -File './tasks.build.ps1'", 279 | "problemMatcher": "$msCompile", 280 | "presentation": { 281 | "echo": false, 282 | "showReuseMessage": false 283 | } 284 | }, 285 | { 286 | "label": "vscode-rebuild-tasks", 287 | "type": "shell", 288 | "command": "./build.ps1 -LoadConstants -Task vscode-rebuild-tasks -File './tasks.build.ps1'", 289 | "problemMatcher": "$msCompile", 290 | "presentation": { 291 | "echo": false, 292 | "showReuseMessage": false 293 | } 294 | }, 295 | { 296 | "label": "bootstrap-install-aws-vault", 297 | "type": "shell", 298 | "command": "./build.ps1 -LoadConstants -Task bootstrap-install-aws-vault -File './tasks.build.ps1'", 299 | "problemMatcher": "$msCompile", 300 | "presentation": { 301 | "echo": false, 302 | "showReuseMessage": false 303 | } 304 | }, 305 | { 306 | "label": "check_powershell_configuration", 307 | "type": "shell", 308 | "command": "./build.ps1 -LoadConstants -Task check_powershell_configuration -File './tasks.build.ps1'", 309 | "problemMatcher": "$msCompile", 310 | "presentation": { 311 | "echo": false, 312 | "showReuseMessage": false 313 | } 314 | }, 315 | { 316 | "label": "teams-notify-datadog", 317 | "type": "shell", 318 | "command": "./build.ps1 -LoadConstants -Task teams-notify-datadog -File './tasks.build.ps1'", 319 | "problemMatcher": "$msCompile", 320 | "presentation": { 321 | "echo": false, 322 | "showReuseMessage": false 323 | } 324 | }, 325 | { 326 | "label": "job-docker-codespaces-rebuild", 327 | "type": "shell", 328 | "command": "./build.ps1 -LoadConstants -Task job-docker-codespaces-rebuild -File './tasks.build.ps1'", 329 | "problemMatcher": "$msCompile", 330 | "presentation": { 331 | "echo": false, 332 | "showReuseMessage": false 333 | } 334 | }, 335 | { 336 | "label": "docker-codespaces-pull", 337 | "type": "shell", 338 | "command": "./build.ps1 -LoadConstants -Task docker-codespaces-pull -File './tasks.build.ps1'", 339 | "problemMatcher": "$msCompile", 340 | "presentation": { 341 | "echo": false, 342 | "showReuseMessage": false 343 | } 344 | }, 345 | { 346 | "label": "docker-codespaces-build", 347 | "type": "shell", 348 | "command": "./build.ps1 -LoadConstants -Task docker-codespaces-build -File './tasks.build.ps1'", 349 | "problemMatcher": "$msCompile", 350 | "presentation": { 351 | "echo": false, 352 | "showReuseMessage": false 353 | } 354 | }, 355 | { 356 | "label": "tidy", 357 | "type": "shell", 358 | "command": "./build.ps1 -LoadConstants -Task tidy -File './tasks.build.ps1'", 359 | "problemMatcher": "$msCompile", 360 | "presentation": { 361 | "echo": false, 362 | "showReuseMessage": false 363 | } 364 | }, 365 | { 366 | "label": "bootstrap", 367 | "type": "shell", 368 | "command": "./build.ps1 -LoadConstants -Task bootstrap -File './tasks.build.ps1'", 369 | "problemMatcher": "$msCompile", 370 | "presentation": { 371 | "echo": false, 372 | "showReuseMessage": false 373 | } 374 | }, 375 | { 376 | "label": "init", 377 | "type": "shell", 378 | "command": "./build.ps1 -LoadConstants -Task init -File './tasks.build.ps1'", 379 | "problemMatcher": "$msCompile", 380 | "presentation": { 381 | "echo": false, 382 | "showReuseMessage": false 383 | } 384 | }, 385 | { 386 | "label": "job-tidy", 387 | "type": "shell", 388 | "command": "./build.ps1 -LoadConstants -Task job-tidy -File './tasks.build.ps1'", 389 | "problemMatcher": "$msCompile", 390 | "presentation": { 391 | "echo": false, 392 | "showReuseMessage": false 393 | } 394 | }, 395 | { 396 | "label": "job-bootstrap", 397 | "type": "shell", 398 | "command": "./build.ps1 -LoadConstants -Task job-bootstrap -File './tasks.build.ps1'", 399 | "problemMatcher": "$msCompile", 400 | "presentation": { 401 | "echo": false, 402 | "showReuseMessage": false 403 | } 404 | }, 405 | { 406 | "label": "github-tidy", 407 | "type": "shell", 408 | "command": "./build.ps1 -LoadConstants -Task github-tidy -File './tasks.build.ps1'", 409 | "problemMatcher": "$msCompile", 410 | "presentation": { 411 | "echo": false, 412 | "showReuseMessage": false 413 | } 414 | }, 415 | { 416 | "label": "?", 417 | "type": "shell", 418 | "command": "./build.ps1 -LoadConstants -Task ? -File './tasks.build.ps1'", 419 | "problemMatcher": "$msCompile", 420 | "presentation": { 421 | "echo": false, 422 | "showReuseMessage": false 423 | } 424 | } 425 | ] 426 | } 427 | 428 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Welcome to invoke-build-tasks 👋

2 |

3 |

4 | 5 | ## Purpose 6 | 7 | This is a set of various [InvokeBuild](https://github.com/nightroman/Invoke-Build) tasks that do generic task/automation work. 8 | 9 | For setting up a project, having some nice tasks setup like this can increase velocity and ease of standardization, formatting, and more. 10 | 11 | Tasks can include items such as: 12 | 13 | - Formatting go, powershell 14 | - Docker build for codespaces 15 | - build a markdown page containing details on tasks for references 16 | - bootstrap a projects documentation layout 17 | - install powershell module dependencies 18 | - cleanup artifacts 19 | - generic build notifications for github, datadog, teams, slack or whatever else gets added. 20 | 21 | 22 | ## Why InvokeBuild 23 | 24 | The short answer is it's a pretty powerful framework for running tasks and has proven to be extremely reliable for me. 25 | It's also relatively easy to quickly extend once you work through understanding how it works. 26 | 27 | Long term I probably will explore more, but for now this provides a rapid productivity gain in a project to improve local development workflow. 28 | 29 | Add this to your `$PROFILE` file: `New-Alias ib 'invoke-build' -force` and then you can benefit from quickly running commands such as: 30 | 31 | Format your code across multiple languages. 32 | 33 | ```powershell 34 | ib tidy 35 | ``` 36 | 37 | Example worklow that might be created: 38 | 39 | ```powershell 40 | ib clean, build, s3-upload-directory, notify-slack 41 | ``` 42 | 43 | In addition to easy commands from the terminal, this project uses the VSCode integration so you can have all the tasks show up as VSCode tasks. 44 | If you leverage Task Explorer Extension, you can just hit the run button as well and trigger activities from there. 45 | ## Show your support 46 | 47 | Give a ⭐️ if this project helped you! 48 | 49 | *** 50 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ 51 | 52 | ## Contributors ✨ 53 | 54 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |

sheldonhull

💻
64 | 65 | 66 | 67 | 68 | 69 | 70 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 71 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Description 3 | Combination of: 4 | - https://github.com/nightroman/Invoke-Build/wiki/Build-Analysis 5 | - https://github.com/HowellIT/PSPDFGen/blob/master/build.ps1 6 | #> 7 | 8 | [cmdletbinding()] 9 | param( 10 | [string[]]$Tasks = '.' 11 | 12 | , [switch]$LoadConstants 13 | , $File 14 | , $Configuration 15 | 16 | ) 17 | ($File) ? ( Join-Path $PSScriptRoot 'tasks.build.ps1') : (Write-Verbose "Task File Override: '$File'") 18 | 19 | $DependentModules = @('InvokeBuild', 'NameIt') # add pester when pester tests are added 20 | Foreach ($Module in $DependentModules) { 21 | If (-not (Get-InstalledModule $module -ErrorAction SilentlyContinue)) { 22 | Install-Module -Name $Module -Scope CurrentUser -Force -Confirm:$false 23 | } 24 | Import-Module $module -ErrorAction Stop -Force 25 | } 26 | 27 | # Builds the module by invoking psake on the build.psake.ps1 script. 28 | # $OptionalArgs = @{} 29 | # if () 30 | # { 31 | # Write-Host "OptionalArg provided: [] setting to []" 32 | # $OptionalArgs.Add('', ) 33 | # } 34 | 35 | Invoke-Build -File $File -Result Result -Task $Tasks -LoadConstants:$LoadConstants -Configuration $Configuration @OptionalArgs 36 | 37 | # Show invoked tasks ordered by Elapsed with ScriptName included 38 | $Result.Tasks | 39 | Sort-Object Elapsed | 40 | Format-Table -AutoSize Elapsed, @{ 41 | Name = 'Task' 42 | Expression = { $_.Name + ' @ ' + $_.InvocationInfo.ScriptName } 43 | } 44 | -------------------------------------------------------------------------------- /build/GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: Mainline 2 | branches: {} 3 | ignore: 4 | sha: [] 5 | merge-message-formats: {} 6 | -------------------------------------------------------------------------------- /build/functions/New-SlackMessage.tasks.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | A brief description of the function or script. This keyword can be used only once in each topic. 4 | .DESCRIPTION 5 | Send the start message of an action and return the info on the message so it can be updated if required 6 | .PARAMETER Text 7 | Message to log 8 | .PARAMETER Single 9 | Don't include the white check mark at beginning for status updates 10 | 11 | .EXAMPLE 12 | PS> New-SlackMessage 13 | Runs New-SlackMessage 14 | 15 | #> 16 | function New-SlackMessage 17 | { 18 | [CmdletBinding()] 19 | #[OutputType([pscustomobject[]])] 20 | Param ( 21 | [Parameter(mandatory)][string]$Text 22 | , [switch]$Single 23 | ) 24 | 25 | begin 26 | { 27 | $StopWatch = [diagnostics.stopwatch]::StartNew() 28 | Write-PSFMessage -Level VeryVerbose -Message ( '{0:hh\:mm\:ss\.fff} {1}: starting' -f $StopWatch.Elapsed, 'New-SlackMessage') 29 | } 30 | process 31 | { 32 | if (-not (Get-PSFConfigValue 'Slack.Enabled')) 33 | { 34 | Write-PSFMessage -Level Verbose -Message "Bypassing New-SlackMessage per Slack.Enabled = `$false" 35 | return 36 | } 37 | 38 | try 39 | { 40 | $StopwatchProcess = [diagnostics.stopwatch]::StartNew() 41 | Write-PSFMessage -Level Debug -Message ( '{0:hh\:mm\:ss\.fff} {1}: process start' -f $StopwatchProcess.Elapsed, 'New-SlackMessage') 42 | $blocks = @() 43 | $body = @{ } 44 | # if ($ENV:BUILD_BUILDID -and $ENV:BUILD_BUILDNUMBER) 45 | # { 46 | # Write-PSFMessage -Level Verbose -Message "Identified running in azure devops pipeline. Constructing context from $ENV:BUILD_BUILDID and $ENV:BUILD_BUILDNUMBER)" 47 | # $blocks = @( 48 | # [pscustomobject]@{ 49 | # type = 'section' 50 | # text = 51 | # [pscustomobject]@{ 52 | # type = 'mrkdwn' 53 | # text = [string](("{0}$Text" -f @(':white_check_mark: ', '')[[bool]$Single])).Trim('"') 54 | # } 55 | 56 | # } 57 | 58 | # [pscustomobject]@{ 59 | # type = 'context' 60 | # elements = @( 61 | # [pscustomobject]@{ 62 | # type = 'mrkdwn' 63 | # text = "" 64 | # } 65 | # ) 66 | # } 67 | # ) 68 | # Write-PSFMessage -Level Debug -Message "----- Blocks Property`n -----$($Blocks | Format-List -Force | Out-String)" 69 | # } 70 | $Body.Add('channel', (Get-PSFConfigValue 'Slack.Channel')) 71 | $Body.Add('parse', 'full') 72 | 73 | 74 | # if ($ENV:BUILD_BUILDID -and $ENV:BUILD_BUILDNUMBER) 75 | # { 76 | # Write-PSFMessage -Level Verbose -Message "Identified running in azure devops pipeline. Ang context block with link to build" 77 | # $Body.Add('blocks', ("[$($blocks | ConvertTo-Json -Depth 3)]").Replace('[[', '[').Replace(']]', ']')) 78 | # } 79 | # else 80 | # { 81 | # $Body.Add('text', (("{0}$Text" -f @(':white_check_mark: ', '')[[bool]$Single]) ).Trim('"')) 82 | # } 83 | 84 | 85 | if ($single) 86 | { 87 | $Body.Add('text', ((":information_source: $Text").Trim('"'))) 88 | } 89 | else 90 | { 91 | $Body.Add('text', ((":white_check_mark: $Text" ).Trim('"'))) 92 | } 93 | Write-PSFMessage -Level Debug -Message "----- Body Of Request ----- `n$($Body | Format-List -Force | Out-String)" 94 | $response = Send-SlackApi -Method chat.postMessage -Body $body -Token (Get-PSFConfigValue 'Slack.Authentication').GetNetworkCredential().Password 95 | 96 | 97 | 98 | if (-not $Single) 99 | { 100 | $response | Add-Member -NotePropertyName MessageStopWatch -NotePropertyValue ([diagnostics.stopwatch]::StartNew()) 101 | } 102 | $response 103 | Write-PSFMessage -Level Debug -Message ( '{0:hh\:mm\:ss\.fff} {1}: process end' -f $StopwatchProcess.Elapsed, 'New-SlackMessage') 104 | } 105 | catch 106 | { 107 | Write-PSFMessage -Level Warning -Message ( '{0:hh\:mm\:ss\.fff} {1}: error experienced' -f $StopwatchProcess.Elapsed, 'New-SlackMessage') -Exception $_.Exception 108 | } 109 | } 110 | end 111 | { 112 | Write-PSFMessage -Level Verbose -Message ( '{0:hh\:mm\:ss\.fff} {1}: finished' -f $StopWatch.Elapsed, 'New-SlackMessage') 113 | $StopWatch.Stop() 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /build/functions/Update-SlackMessage.tasks.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | A brief description of the function or script. This keyword can be used only once in each topic. 4 | .DESCRIPTION 5 | Send the start message of an action and return the info on the message so it can be updated if required 6 | .PARAMETER thread_ts 7 | Timestamp of the message to be updated. (ts in slack documentation) 8 | .PARAMETER text 9 | New text for the message, using the default formatting rules. 10 | .PARAMETER Parse 11 | Change how messages are treated. Defaults to client, unlike chat.postMessage. See below. 12 | .PARAMETER Parse 13 | Channel. Defaults to the configuration provided value by psconfig. Can override here if needed 14 | .EXAMPLE 15 | PS> Update-SlackMessage 16 | Runs Update-SlackMessage 17 | .LINK 18 | https://api.slack.com/methods/chat.update 19 | #> 20 | function Update-SlackMessage 21 | { 22 | [CmdletBinding()] 23 | #[OutputType([pscustomobject[]])] 24 | Param ( 25 | [Parameter(ValueFromPipelineByPropertyName)][AllowNull()]$ts, 26 | [Parameter(ValueFromPipelineByPropertyName)][AllowNull()]$Channel, 27 | [Parameter(ValueFromPipelineByPropertyName)][AllowNull()]$message, 28 | [Parameter(ValueFromPipelineByPropertyName)][AllowNull()]$MessageStopWatch, 29 | [ValidateSet('full', 'client')][string]$parse = 'full' 30 | ) 31 | begin 32 | { 33 | $StopWatch = [diagnostics.stopwatch]::StartNew() 34 | Write-PSFMessage -Level VeryVerbose -Message ( '{0:hh\:mm\:ss\.fff} {1}: starting' -f $StopWatch.Elapsed, 'Update-SlackMessage') 35 | } 36 | process 37 | { 38 | $StopwatchProcess = [diagnostics.stopwatch]::StartNew() 39 | 40 | Write-PSFMessage -Level Debug -Message ( '{0:hh\:mm\:ss\.fff} {1}: process start' -f $StopwatchProcess.Elapsed, 'Update-SlackMessage') 41 | 42 | if (-not (Get-PSFConfigValue 'Slack.Enabled')) 43 | { 44 | Write-PSFMessage -Level Verbose -Message "Bypassing New-SlackMessage per Slack.Enabled = `$false" 45 | return 46 | } 47 | 48 | 49 | try { [string]$Duration = $MessageStopWatch.Elapsed.Humanize() } 50 | catch { Write-PSFMessage -Level Warning -Message 'Error in humanizing timestamp' } 51 | 52 | try 53 | { 54 | 55 | $Body = @{ 56 | ts = $ts 57 | text = ( [string]::Concat(($message.Text -replace 'start\w*', '' -replace '^\:\w*\:', ':heavy_check_mark:'), ' ', $duration) | ConvertTo-Json -Depth 1 -Compress).Trim('"') 58 | parse = 'full' 59 | channel = $Channel 60 | } 61 | $response = Send-SlackApi -Method chat.update -Body $body -Token (Get-PSFConfigValue 'Slack.Authentication').GetNetworkCredential().Password 62 | $response 63 | Write-PSFMessage -Level Debug -Message ( '{0:hh\:mm\:ss\.fff} {1}: process end' -f $StopwatchProcess.Elapsed, 'Update-SlackMessage') 64 | } 65 | catch 66 | { 67 | Write-PSFMessage -Level Warning -Message ( '{0:hh\:mm\:ss\.fff} {1}: error experienced' -f $StopwatchProcess.Elapsed, 'Update-SlackMessage') -Exception $_.Exception 68 | } 69 | } 70 | end 71 | { 72 | Write-PSFMessage -Level Verbose -Message ( '{0:hh\:mm\:ss\.fff} {1}: finished' -f $StopWatch.Elapsed, 'Update-SlackMessage') 73 | $StopWatch.Stop() 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /build/functions/check.tasks.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Defines the custom task "check". 4 | .Description 5 | Build scripts dot-source this script in order to use the task "check". 6 | The build file with checks represents a check-list and checks are normally 7 | invoked together (*). But they can be invoked individually, too. Tasks can 8 | reference checks. Checks can reference tasks. 9 | In other words, checks are normal tasks with an extra feature: as soon as a 10 | check is passed, it is never checked again, even in builds invoked after. 11 | Check-task parameters are Name, Jobs, Inputs, Outputs, and Partial. 12 | If and Done are already used for the definition of "check". 13 | Script scope names: 14 | Alias: check 15 | Variables: CheckClixml, CheckData 16 | Functions: Add-CheckTask, Test-CheckTask, Set-CheckDone 17 | .Parameter CheckClixml 18 | Specifies the file where passed checks are stored. 19 | Default: "$BuildFile.Check.clixml" 20 | .Example 21 | > 22 | # Dot-source "check" definitions 23 | . []Check.tasks.ps1 24 | # Add "check" tasks 25 | check CheckSomething { 26 | ... 27 | } 28 | .LINK 29 | https://github.com/nightroman/Invoke-Build/tree/91effb0db9930d60de30fbfef0686fa6a536f83d/Tasks/Check 30 | #> 31 | 32 | param( 33 | $CheckClixml = "$BuildFile.$($PSVERSIONTABLE.PSVersion).Check.clixml" 34 | ) 35 | 36 | # New DSL word. 37 | Set-Alias check Add-CheckTask 38 | 39 | # Wrapper of "task" which adds a customized task used as "check". 40 | # Mind setting "Source" for error messages and help comments. 41 | function Add-CheckTask( 42 | [Parameter(Position = 0, Mandatory = 1)][string]$Name, 43 | [Parameter(Position = 1)][object[]]$Jobs, 44 | $Inputs, 45 | $Outputs, 46 | [switch]$Partial 47 | ) 48 | { 49 | Task $Name $Jobs -If: { Test-CheckTask } -Done:Set-CheckDone -Source:$MyInvocation -Inputs:$Inputs -Outputs:$Outputs -Partial:$Partial 50 | } 51 | 52 | # This function is called as If for custom tasks. 53 | function Test-CheckTask 54 | { 55 | !$CheckData[$Task.Name] 56 | } 57 | 58 | # This function is called as Done for custom tasks. 59 | # For the current task it stores its done time. 60 | # Then it writes all information to the file. 61 | function Set-CheckDone 62 | { 63 | if ($Task.Error) { return } 64 | $CheckData[$Task.Name] = [DateTime]::Now 65 | $CheckData | Export-Clixml $CheckClixml 66 | } 67 | 68 | # Import information about passed tasks from the file. 69 | # Note that this action is skipped in WhatIf mode. 70 | if (!$WhatIf) 71 | { 72 | $CheckData = if (Test-Path $CheckClixml) { Import-Clixml $CheckClixml } else 73 | { 74 | @{ } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /build/requirements.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | psdepend = 'latest' 3 | PsDependoptions = @{ 4 | Version = 'Latest' 5 | Target = 'currentuser' 6 | } 7 | 8 | # 'PSGalleryModule::AWS.Tools.Common' = 'latest' 9 | # 'PSGalleryModule::AWS.Tools.EC2' = 'latest' 10 | # 'PSGalleryModule::AWS.Tools.SimpleSystemsManagement' = 'latest' 11 | 'PSGalleryModule::PSReadLine' = 'latest' 12 | 'PSGalleryModule::PSScriptAnalyzer' = 'latest' 13 | 'PSGalleryModule::Pester' = 'latest' 14 | 'PSGalleryModule::PSFramework' = 'latest' 15 | 16 | # 'PSGalleryModule::PSGitHub' = 'latest' 17 | # 'PSGalleryModule::PSTeams' = 'latest' 18 | # 'PSGalleryModule::PSTeams' = 'latest' 19 | # 'PSGalleryModule::PSSlack' = 'latest' 20 | # 'PSGalleryModule::AWS.Tools.Installer' = 'latest' 21 | # 'EnsureRequiredAWSToolsModulesAreInstalled' = @{ 22 | # DependencyType = 'Command' 23 | # Source = "Install-AWSToolsModule 'AWS.Tools.Common', 'AWS.Tools.EC2','AWS.Tools.SimpleSystemsManagement' -MinimumVersion 4.0.6 -CleanUp -Confirm:`$false -Scope CurrentUser -SkipPublisherCheck -AllowClobber" 24 | # DependsOn = 'AWS.Tools.Installer' 25 | # } 26 | } 27 | -------------------------------------------------------------------------------- /build/settings/PSScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | # PSScriptAnalyzerSettings.psd1 2 | @{ 3 | #Severity = @('Error', 'Warning') 4 | 5 | ExcludeRules = @( 6 | 'PSAvoidUsingCmdletAliases', 7 | 'PSAvoidUsingWriteHost' #This is ok per using invokebuild, don't want to avoid this for now 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /build/settings/powershell-formatting-settings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # IncludeRules = @( 3 | # 'PSPlaceOpenBrace', 4 | # 'PSPlaceCloseBrace', 5 | # 'PSUseConsistentWhitespace', 6 | # 'PSUseConsistentIndentation', 7 | # 'PSAlignAssignmentStatement' 8 | # ) 9 | # Only diagnostic records of the specified severity will be generated. 10 | # Uncomment the following line if you only want Errors and Warnings but 11 | # not Information diagnostic records. 12 | #Severity = @('Error','Warning') 13 | # Analyze **only** the following rules. Use IncludeRules when you want 14 | # to invoke only a small subset of the defualt rules. 15 | # IncludeRules = @( 16 | # 'PSAvoidDefaultValueSwitchParameter', 17 | # 'PSMisleadingBacktick', 18 | # 'PSMissingModuleManifestField', 19 | # 'PSReservedCmdletChar', 20 | # 'PSReservedParams', 21 | # 'PSShouldProcess', 22 | # 'PSUseApprovedVerbs', 23 | # 'PSAvoidUsingCmdletAliases', 24 | # 'PSUseDeclaredVarsMoreThanAssignments' 25 | # ) 26 | # Do not analyze the following rules. Use ExcludeRules when you have 27 | # commented out the IncludeRules settings above and want to include all 28 | # the default rules except for those you exclude below. 29 | # Note: if a rule is in both IncludeRules and ExcludeRules, the rule 30 | # will be excluded. 31 | ExcludeRules = @( 32 | #'PSAvoidGlobalVars', 33 | 'PSAvoidUsingWriteHost', 34 | 'PSAvoidUsingUserNameAndPassWordParams', 35 | 'PSAvoidUsingConvertToSecureStringWithPlainText' 36 | ) 37 | # You can use rule configuration to configure rules that support it: 38 | #Rules = @{ 39 | # PSAvoidUsingCmdletAliases = @{ 40 | # Whitelist = @("cd") 41 | # } 42 | #} 43 | Rules = @{ 44 | PSPlaceOpenBrace = @{ 45 | Enable = $true 46 | OnSameLine = $false 47 | NewLineAfter = $true 48 | IgnoreOneLineBlock = $true 49 | } 50 | 51 | PSPlaceCloseBrace = @{ 52 | Enable = $true 53 | NewLineAfter = $true 54 | IgnoreOneLineBlock = $true 55 | NoEmptyLineBefore = $false 56 | } 57 | 58 | PSUseConsistentIndentation = @{ 59 | Enable = $true 60 | Kind = 'space' 61 | IndentationSize = 4 62 | } 63 | 64 | PSUseConsistentWhitespace = @{ 65 | Enable = $true 66 | CheckOpenBrace = $true 67 | CheckOpenParen = $true 68 | CheckOperator = $true 69 | CheckSeparator = $true 70 | } 71 | 72 | PSAlignAssignmentStatement = @{ 73 | Enable = $true 74 | CheckHashtable = $true 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /build/tasks/tasks-aws/s3.tasks.ps1: -------------------------------------------------------------------------------- 1 | #Synopsis: Using s5cmd (for now assuming it's installed) sync an updated directory. Note uses PowerShell 7 chain operator. woot woot. 2 | Task s3-upload-directory { 3 | requires -Environment S3_BUCKET 4 | $MyCustomArtifactPath = Join-Path $ArtifactDirectory 'my-artifacts' 5 | $null = New-Item $MyCustomArtifactPath -ItemType Directory -Force 6 | switch -Wildcard ($PSVersionTable.OS) 7 | { 8 | '*Windows*' { $s5cmd = 's5cmd.exe' } 9 | '*Darwin*' { $s5cmd = 's5cmd' } 10 | '*Linux*' { $s5cmd = (Join-Path $BuildRoot 'binaries/linux/s5cmd') } 11 | } 12 | s5cmd version && Write-Build Green "s5cmd version matched: $(s5cmd version)" || Write-Build DarkYellow 'S5cmd not found so not running the sync commands. Install first'; return 13 | 14 | Write-Build Green 'Running sync command with following syntax:' 15 | Write-Build DarkGray "Exec { & $s5cmd cp --if-size-differ --if-source-newer `"$MyCustomArtifactPath/`" `"s3://$ENV:S3_BUCKET/modules/`" }" 16 | Exec { & $s5cmd cp --if-size-differ --if-source-newer "$MyCustomArtifactPath" "s3://$ENV:S3_BUCKET/modules/" } 17 | } 18 | -------------------------------------------------------------------------------- /build/tasks/tasks-aws/ssm.automation.tasks.ps1: -------------------------------------------------------------------------------- 1 | #Synposis: Run the AWS SSM Automation Document 2 | task ssm-run-automation-example { 3 | @( 4 | 'Aws.Tools.Common' 5 | 'AWS.Tools.SimpleSystemsManagement' 6 | ) | ForEach-Object { 7 | try 8 | { 9 | Import-Module $_ -Scope Global 10 | } 11 | catch 12 | { 13 | Write-Build DarkYellow "error noted on trying to import: $($_.Exception.Message.ToString()) . Likely assembly already loaded" 14 | } 15 | } 16 | $StopWatch = [diagnostics.stopwatch]::StartNew() 17 | 18 | ################################################# 19 | # Invoke Database Restore Command On SQL Server # 20 | ################################################# 21 | 22 | $Params = @{ 23 | 'Foo' = $ENV:FOO 24 | 'bar' = $ENV:BAR 25 | 'AnotherTaco' = $ENV:ANOTHER_TACO 26 | } 27 | $sendSSMCommandSplat = @{ 28 | DocumentName = 'AWS-AutomatioNDocumentExample' 29 | Parameter = $params 30 | DocumentVersion = '$LATEST' 31 | } 32 | 33 | $result = Start-SSMAutomationExecution -Mode Auto @sendSSMCommandSplat 34 | $result | Get-SSMAutomationExecution | Select-Object -ExpandProperty StepExecutions | Select-Object StepName, Action, StepStatus, ValidNextSteps 35 | 36 | Write-Build Green "Issued command with following parameters: $(($result | Get-SSMAutomationExecution).Parameters | Format-List | Out-String)" 37 | do 38 | { 39 | $execution = $result | Get-SSMAutomationExecution 40 | Write-Build DarkGray "SSMAutomationExecution: $($execution.AutomationExecutionStatus) $($Execution.CurrentStepName) $($Execution.CurrentAction) $($StopWatch.Elapsed.ToString('hh\:mm\:ss'))" 41 | Start-Sleep -Seconds 5 42 | } 43 | while ([string]::IsNullOrWhiteSpace($execution.CurrentAction) -eq $false -or $execution.AutomationExecutionStatus -eq 'InProgress') 44 | 45 | Write-Build DarkGreen "$($result | Get-SSMAutomationExecution | Select-Object -ExpandProperty StepExecutions | Select-Object StepName, Action, StepStatus, ValidNextSteps,FailureMessage | Format-List -Force | Out-String)" 46 | $result | Get-SSMAutomationExecution | Select-Object -ExpandProperty StepExecutions | ForEach-Object { 47 | $e = $_ 48 | Write-Build DarkGray "$($e | Select-Object StepName, Action, StepStatus, ValidNextSteps | Format-Table -AutoSize -Wrap | Out-String)" 49 | Write-Build Green "Output Payload: `n$($e.Outputs.OutputPayload | ConvertFrom-Json -ErrorAction SilentlyContinue | Format-List -Force | Out-String)" 50 | } 51 | if ($result.FailureMessage) 52 | { 53 | Write-Build Red "SSM Failure: $($result.FailureMessage)" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /build/tasks/tasks-aws/ssm.commands.tasks.ps1: -------------------------------------------------------------------------------- 1 | #Synopsis: Run an SSM command document via AWS Tools 2 | Task ssm-run-command-doc { 3 | requires -Environment AWS_ACCESS_KEY, AWS_SECRET_KEY 4 | 5 | } 6 | -------------------------------------------------------------------------------- /build/tasks/tasks-ci/git.tasks.ps1: -------------------------------------------------------------------------------- 1 | #Synopsis: Run quick commit action, for example, to allow updated formatting to be reapplied by CICD tool to all files after check-in, even if someone forgot to run this manually. 2 | Task git-commit-push { 3 | git commit -am"ci: [$(git branch --show-current)] ci commit" 4 | git push 5 | } 6 | #Synopsis: used for quick fixes to existing commit 7 | task git-add-fixup { 8 | # add this git alias fixup = "!f() { TARGET=$(git rev-parse "$1"); git commit --fixup=$TARGET ${@:2} && EDITOR=true git rebase -i --autostash --autosquash $TARGET^; }; f" 9 | # https://blog.filippo.io/git-fixup-amending-an-older-commit/ 10 | git add . 11 | git commit --fixup 12 | } 13 | 14 | #Synopsis: quick commit and push. Use git town ideally 15 | task git_quick_commit_and_push { 16 | Write-Build Gray 'Quick Commit for Personal Branch [before merge squash and cleanup]' 17 | git add . 18 | git commit -am"$(git branch --show-current) : $((Invoke-Generate '[adjective]-[noun]').ToLower()) : dev commit #tobesquashed" 19 | git pull --rebase 20 | git push 21 | Write-Build Gray 'All your work has been pushed to current branch' 22 | } 23 | 24 | #Synopsis: If the CI job has changes, this will allow the CI job to commit back those linting or build artifacts to the job 25 | Task git-ci-commit-push { 26 | Write-Build DarkGray '' 27 | $UserName = $(git log -1 --pretty=format:"%an") 28 | $UserEmail = $(git log -1 --pretty=format:"%ae") 29 | 30 | Write-Build Green "`$UserName : [$UserName]" 31 | Write-Build Green "`$UserEmail : [$UserEmail]" 32 | 33 | $status = git status --porcelain 34 | Write-Build DarkGray "Status: `t[`n`t$($status -split ' ' -join "`n`t")`n]" 35 | if (-not $status) 36 | { 37 | Write-Build DarkYellow 'No changes detected for ci, exiting task' 38 | return 39 | } 40 | 41 | 42 | git add . 43 | git -c user.name="$UserName" -c user.email="$UserEmail" commit -m"ci: agent automated commit [ci skip]" 44 | $BranchRef = $ENV:BUILD_SOURCEBRANCH ?? $(git branch --show-current) 45 | 46 | Write-Build Green "Current BranchRef: $BranchRef" 47 | if ($ENV:BUILD_SOURCEBRANCH) 48 | { 49 | Write-Build DarkGray "running push from detatched head with following command: git push $ENV:BUILD_REPOSITORY_URI HEAD:refs/heads/$ENV:BUILD_SOURCEBRANCHNAME" 50 | git push $ENV:BUILD_REPOSITORY_URI HEAD:refs/heads/$ENV:BUILD_SOURCEBRANCHNAME 51 | return 52 | } 53 | git push 54 | 55 | } 56 | -------------------------------------------------------------------------------- /build/tasks/tasks-ci/tasks.secrets.tasks.ps1: -------------------------------------------------------------------------------- 1 | #Synopsis: Setup squealer to scan for secrets 2 | check secrets-squealer-setup { 3 | $ENV:GOPATH = Join-Path $ENV:HOME 'go' 4 | $ENV:PATH = "$($ENV:PATH):/Users/$(whoami)/go/bin" 5 | curl -s 'https://raw.githubusercontent.com/owenrumney/squealer/main/scripts/install.sh' | bash 6 | 7 | } 8 | #Synopsis: Use squealer to test for secrets 9 | Task secrets-squealer-scan { 10 | $ENV:PATH = "$($ENV:PATH):/Users/$(whoami)/go/bin" 11 | &squealer 12 | } 13 | -------------------------------------------------------------------------------- /build/tasks/tasks-configuration/check-apps.tasks.ps1: -------------------------------------------------------------------------------- 1 | check bootstrap-install-aws-vault { 2 | Write-Build DarkGray "Bootstraping aws-vault for: $($PSVersionTable.OS)" 3 | switch -Wildcard ($PSVersionTable.OS) 4 | { 5 | 'Windows' { choco install aws-vault -y --no-progress --quiet } 6 | 'Darwin' { brew cask install aws-vault } 7 | 'Linux' 8 | { 9 | $releases = Get-GitHubRelease -Owner 99designs -RepositoryName 'aws-vault' -Latest | Get-GitHubReleaseAsset 10 | Write-Build DarkGray "Github Releases found: $(@($releases).count)" 11 | $downloadurl = $releases.Where{ $_.Name -match 'linux\-amd64' }.url 12 | sudo curl -L -o /usr/local/bin/aws-vault $downloadurl 13 | sudo chmod 755 /usr/local/bin/aws-vault 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /build/tasks/tasks-configuration/check-powershell-configuration.tasks.ps1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # List Any Composite "Jobs" for this category at the top for easy review # 3 | ########################################################################## 4 | 5 | #Synopsis: Ensure that PSGallery is marked as trusted. Additionally remove PowerShellGet\1.0.0.1 as causes issues with PowerShellGet parameters if imports by accident 6 | task check_powershell_configuration { 7 | 8 | 9 | if (-not $WhatIf ) #$PSCmdlet.ShouldProcess("Install-PackageProvider -Name Nuget -Scope AllUsers -Force -MinimumVersion 2.8.5.201") 10 | { 11 | $null = Install-PackageProvider -Name Nuget -Scope AllUsers -Force -MinimumVersion 2.8.5.201 -Confirm:$false 12 | } 13 | 14 | 15 | #----------------------------------------------------------------------------# 16 | # Up to Date PowershellGet Installed # 17 | #----------------------------------------------------------------------------# 18 | $InstalledPowershellGet = Get-InstalledModule PowershellGet -ErrorAction SilentlyContinue | Sort-Object Version | Select-Object -First 1 19 | 20 | if (-not $WhatIf ) #$PSCmdlet.ShouldProcess("Install-Module PowershellGet -Force -Scope AllUsers -AllowClobber") 21 | { 22 | if ($InstalledPowershelLGet.Version -lt [Version]::New(2, 2, 3)) 23 | { 24 | Install-Module PowershellGet -Scope AllUsers -Force -AllowClobber 25 | Import-Module PowershellGet -MinimumVersion '2.2.3' -Force -Verbose 26 | } 27 | } 28 | if (-not $WhatIf ) #$PSCmdlet.ShouldProcess("Enable-PSRemoting -Force") 29 | { 30 | Enable-PSRemoting -Force 31 | } 32 | if (-not $WhatIf ) #$PSCmdlet.ShouldProcess("Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force") 33 | { 34 | Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force -ErrorAction SilentlyContinue # Process Scope will cause error, so supressing this from output 35 | } 36 | Set-PSRepository -Name PSGallery -InstallationPolicy Trusted 37 | # Write-Build DarkYellow "Removing prebuilt powershellget 1.0.0.1 as this causes issues in new environments" 38 | # remove "C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1" 39 | } 40 | -------------------------------------------------------------------------------- /build/tasks/tasks-docker/docker.codespaces.tasks.ps1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # List Any Composite "Jobs" for this category at the top for easy review # 3 | ########################################################################## 4 | Task job-docker-codespaces-rebuild @{ 5 | Jobs = 'docker-codespaces-pull', 'docker-codespaces-build' 6 | } 7 | #Synopsis: Pull latest docker image for codespace 8 | Task docker-codespaces-pull { 9 | &docker --pull -f '.devcontainer/Dockerfile' 10 | } 11 | 12 | #Synposis: Run docker build to ensure codespace container can be successfully built by docker 13 | Task docker-codespaces-build { 14 | &docker build --pull --rm -f '.devcontainer/Dockerfile' -t invokebuildtasks:latest '.devcontainer' 15 | } 16 | -------------------------------------------------------------------------------- /build/tasks/tasks-docs/asciidoc.tasks.ps1: -------------------------------------------------------------------------------- 1 | # Synopsis: Generate an asciidoc file of the build tasks included in this project for friendly reference and review. 2 | Task docs-generate-invoke-build-docs { 3 | ## $result = Invoke-Build ? -File (Join-Path $BuildRoot '/build/tasks.build.ps1' 4 | $null = New-Item (Join-Path $BuildRoot 'docs') -ItemType Directory -Force 5 | $result = Invoke-Build ? -File (Join-Path $BuildRoot 'tasks.build.ps1') 6 | $TargetFile = Join-Path $BuildRoot 'docs/task-helper-docs.adoc' 7 | [string[]]$output = $result | Sort-Object Jobs -Descending | ForEach-Object { 8 | $i = $_ 9 | "|$($i.Jobs.Count -gt 1)|$($i.Name)|$($i.Synopsis)|$(($i.Jobs.Count -ge 2) ? ($i.Jobs -join ',') : '')" 10 | } 11 | 12 | '= Task Helper Reference' | Out-File $TargetFile -Force 13 | 14 | @' 15 | 16 | This is a reference for what tasks are included in this project that can be invoked easily by Task Explorer in Visual Studio Code or by running: 17 | 18 | == More Detail 19 | 20 | [source,powershell] 21 | ---- 22 | Invoke-Build ./build/build.ps1 -Task Tidy 23 | ---- 24 | 25 | [cols=4] 26 | [%header] 27 | [caption="Invoke Build Tasks "] 28 | |=== 29 | |Is This A Job|Task Name|Task Description|Task List 30 | '@ | Out-File $TargetFile -Append 31 | $output -join "`n" | Out-File $TargetFile -Append 32 | 33 | '|===' | Out-File $TargetFile -Append 34 | } 35 | -------------------------------------------------------------------------------- /build/tasks/tasks-docs/changelog.tasks.ps1: -------------------------------------------------------------------------------- 1 | # Synopsis: Use docker driven command to generate a changelog that takes into account the pull request history to build a changelog markdown file 2 | task changelog-github { 3 | docker run -it -e CHANGELOG_GITHUB_TOKEN=$ENV:GITHUB_TOKEN --rm -v ${pwd}:/usr/local/src/your-app ferrarimarco/github-changelog-generator --user $ENV:GITHUB_ORG --project $ENV:REPO 4 | } 5 | -------------------------------------------------------------------------------- /build/tasks/tasks-docs/markdown.tasks.ps1: -------------------------------------------------------------------------------- 1 | # synopsis: Requires Node. NPX command to use read-me-generator to generate a beautiful starting point for a landing readme. Issues with powershell prompt in vscode, so best to run in external window 2 | task docs-initialize-readme { 3 | npx readme-md-generator 4 | } 5 | -------------------------------------------------------------------------------- /build/tasks/tasks-invokebuild/output.tasks.ps1: -------------------------------------------------------------------------------- 1 | # Define headers as separator, task path, synopsis, and location, e.g. for Ctrl+Click in VSCode. 2 | # Also change the default color to Green. If you need task start times, use `$Task.Started`. 3 | Set-BuildHeader { 4 | param($Path) 5 | # separator line 6 | Write-Build Green ('=' * 140) 7 | #Write-Build Green ('=' * 140) 8 | # default header + synopsis 9 | Write-Build Green "Task $Path : $(Get-BuildSynopsis $Task)" 10 | # task location in a script 11 | Write-Build Green "At $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" 12 | } 13 | 14 | # Define footers similar to default but change the color to DarkGray. 15 | Set-BuildFooter { 16 | param($Path) 17 | Write-Build DarkGray "Done $Path, $($Task.Elapsed)" 18 | Write-Build Green ('=' * 140) 19 | } 20 | 21 | task docs-output-invoke-build-docs { 22 | if (-not (Get-InstalledModule PSWriteHtml -ErrorAction SilentlyContinue)) 23 | { 24 | throw "Can't do this without [Install-Module PSWriteHTML -Scope CurrentUser ]" 25 | } 26 | Invoke-Build ? .\tasks.build.ps1 | Where-Object { @($_.Jobs).Count -gt 1 } | ForEach-Object { 27 | [pscustomobject]@{ 28 | JobName = $_.Name 29 | Synopsis = $_.Synopsis 30 | Tasks = $_.Jobs.ForEach{ [string]$_ } | Format-Table -AutoSize -Wrap | Out-String 31 | } 32 | 33 | } | Out-HtmlView 34 | } 35 | -------------------------------------------------------------------------------- /build/tasks/tasks-notify/README.md: -------------------------------------------------------------------------------- 1 | ## Tasks Notify 2 | 3 | 4 | ## Datadog Example 5 | 6 | With Azure Pipelines, this could be called like below, noting that you'd want to provide a sensitive environment variable into the task for it to be visible. 7 | 8 | ```yaml 9 | - task: PowerShell@2 10 | displayName: Send Datadog Final Result 11 | inputs: 12 | filePath: build.ps1 13 | arguments: '-Task notify-datadog -Configuration ${{ parameters.CONFIGURATION }}' 14 | errorActionPreference: 'Continue' 15 | pwsh: true 16 | failOnStderr: true 17 | condition: always() 18 | env: 19 | DD_API_KEY: $(DD_API_KEY) 20 | DD_APPEND_MESSAGE: | 21 | finished azure-pipelines 22 | 23 | Special Comment and Parameter value: `${{ parameters.CONFIGURATION }}` 24 | ``` 25 | -------------------------------------------------------------------------------- /build/tasks/tasks-notify/datadog.tasks.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Function to create a nicely formatted build notification for Azure Pipelines that is generic enough to use in any context, while still allowing appending of additional info. 4 | .Description 5 | Pretty markdown formatted messages 6 | .Example 7 | PS> Write-DatadogEvent -title 'Test Pipeline Invocation' -text "This is important... but only a test `n [Google](Google.com)" -Status 'Fail' 8 | 9 | #> 10 | function Write-DatadogEvent 11 | { 12 | [cmdletbinding()] 13 | param( 14 | [Parameter(mandatory)] 15 | [string] 16 | $title 17 | 18 | , [Parameter(mandatory)] 19 | [string] 20 | $text 21 | , [Parameter()] 22 | [string]$status 23 | ) 24 | if (-not $ENV:DD_API_KEY) 25 | { 26 | Write-Warning 'No DD_API_KEY environment variable found, bypassing event' 27 | return 28 | } 29 | switch -regex ($status) 30 | { 31 | '(failed)|(warning)|(issues)' { $DatadogStatus = 'warning'; break } 32 | '^succeeded' { $DatadogStatus = 'success'; break } 33 | default { $DatadogStatus = 'success' } 34 | } 35 | Write-Host "Datadog Status: $DatadogStatus" 36 | $SplatRestMethod = @{ 37 | 38 | Method = 'POST' 39 | Uri = "https://api.datadoghq.com/api/v1/events?api_key=$env:DD_API_KEY" 40 | ContentType = 'application/json' 41 | Body = @{ 42 | text = ('%%%', $text, '%%%' -join "`n") 43 | title = $title 44 | alert_type = $DatadogStatus 45 | aggregation_key = $title 46 | tags = @( 47 | 'code-pipeline:azure-devops' 48 | "code-repository:${env:Build_Repository_Name}" 49 | ) 50 | } | ConvertTo-Json -Depth 2 -Compress 51 | UseDefaultCredentials = $true 52 | } 53 | Invoke-RestMethod @SplatRestMethod 54 | } 55 | 56 | #Synopsis: Notify datadog of the result when running in CI. Title and remaining message is dynamically set from the azure pipelines build variables 57 | Task teams-notify-datadog { 58 | # List of Predefined Variables: https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml 59 | 60 | $Message = @" 61 | ## $ENV:BUILD_BUILDNUMBER 62 | 63 | [${ENV:BUILD_BUILDNUMBER}](${ENV:SYSTEM_COLLECTIONURI}${ENV:System_TeamProject}/_build/results?buildId=${ENV:BUILD_BUILDID}&view=results) _${ENV:AGENT_JOBSTATUS}_ 64 | 65 | $ENV:DD_APPEND_MESSAGE 66 | "@ 67 | Write-DatadogEvent -title $ENV:BUILD_BUILDNUMBER -text $Message -status $ENV:AGENT_JOBSTATUS 68 | 69 | } 70 | -------------------------------------------------------------------------------- /build/tasks/tasks-powershell-modules/check-powershell-modules.tasks.ps1: -------------------------------------------------------------------------------- 1 | #Synopsis: Bootstrap all the required dependencies 2 | check check-powershell-modules { 3 | Write-Build Gray 'Running Invoke-PSDepend' 4 | if (-not (Get-InstalledModule PSDepend -ListAvailable -ErrorAction SilentlyContinue)) 5 | { 6 | Write-Build Gray 'Installing PSDepend' 7 | Install-Module PSDepend -Scope CurrentUser -Force -AllowClobber -Confirm:$false 8 | } 9 | else 10 | { 11 | Write-Build Gray 'PSDepend already installed. Going to run installation of identified dependencies now' 12 | } 13 | try 14 | { 15 | Import-Module PSDepend -Force 16 | } 17 | catch 18 | { 19 | Write-Warning 'Issue in importing normally. Going to find the psd1 file to create correctly' 20 | $PSDepend = (Get-ChildItem @($ENV:PSModulePath -split ';') -Filter PSDepend.psd1 -Recurse | Sort-Object $_.CreationTime -Descending | Select-Object -First 1).FullName 21 | Write-Warning "Found PSDepend to import at: $PSDepend" 22 | Import-Module $PSDepend -Force 23 | } 24 | Invoke-PSDepend -Path $BuildRoot -Confirm:$false -Install 25 | } 26 | 27 | #Synopsis: manually run through PSDepend config and load modules. This can be faster than PSDepend at times 28 | task load-powershell-modules { 29 | 30 | $manifest = ((Get-Content requirements.psd1) -join "`n") | Invoke-Expression 31 | $RequiredModules = $manifest.GetEnumerator().Where{ $_.Key -notmatch 'PSDependOptions' }.ForEach{ 32 | $m = $_ 33 | @{ 34 | Name = $m.key 35 | MinimumVersion = $m.value 36 | } 37 | } 38 | $StopWatchModuleLoad = [diagnostics.stopwatch]::StartNew() 39 | $RequiredModules | ForEach-Object { 40 | [void]$StopWatchModuleLoad.Restart() 41 | $m = $_ 42 | $module = Get-InstalledModule @m -ErrorAction SilentlyContinue 43 | 44 | if (-not $module) 45 | { 46 | Write-Host -NoNewline "$($m.Name) not installed, installing now..." -ForegroundColor DarkYellow 47 | $null = Install-Module @m -Repository PSGallery -Scope CurrentUser -Force -AllowClobber 48 | Write-Host 'installed' -NoNewline -ForegroundColor Green 49 | } 50 | else 51 | { 52 | Write-Host "$($m.name) Module $($m.MinimumVersion)" -ForegroundColor Gray -NoNewline 53 | Write-Host -NoNewline '...' -ForegroundColor DarkYellow 54 | try 55 | { 56 | Import-Module @m -Force -ErrorAction Continue -Scope Global #To speed up installation from other custom galleries 57 | } 58 | catch 59 | { 60 | Write-Warning "$($m.Name) // $($M.MinimumVersion) // warning: $($_.Exception.Message)" 61 | } 62 | Write-Host 'imported' -NoNewline -ForegroundColor Green 63 | } 64 | Write-Host ('...{0:mm\:ss\.fff}' -f $StopWatchModuleLoad.Elapsed) -ForegroundColor Green 65 | } 66 | [void]$StopWatchModuleLoad.Stop() 67 | } 68 | -------------------------------------------------------------------------------- /build/tasks/tasks-powershell-modules/check-psdepend.tasks.ps1: -------------------------------------------------------------------------------- 1 | #Synopsis: This is a very slow task. use this only if you fail to get the modules to install through other methods. This might take 10-15 mins to do what the other does in a few minutes. 2 | 3 | check check-run-psdepend-install { 4 | Write-Build Gray 'Running Invoke-PSDepend' 5 | if (-not (Get-InstalledModule PSDepend -ErrorAction SilentlyContinue)) 6 | { 7 | Write-Build Gray 'Installing PSDepend' 8 | Install-Module PSDepend -Scope CurrentUser -Force -AllowClobber -Confirm:$false 9 | } 10 | else 11 | { 12 | Write-Build Gray 'PSDepend already installed. Going to run installation of identified dependencies now' 13 | } 14 | try 15 | { 16 | Import-Module PSDepend -Force 17 | } 18 | catch 19 | { 20 | Write-Warning 'Issue in importing normally. Going to find the psd1 file to create correctly' 21 | $PSDepend = (Get-ChildItem @($ENV:PSModulePath -split ';') -Filter PSDepend.psd1 -Recurse | Sort-Object $_.CreationTime -Descending | Select-Object -First 1).FullName 22 | Write-Warning "Found PSDepend to import at: $PSDepend" 23 | Import-Module $PSDepend -Force 24 | } 25 | Invoke-PSDepend -Path (Join-Path $BuildRoot 'build') -Confirm:$false -Install -Target (Join-Path $BuildRoot 'packages/RequiredModules') 26 | } 27 | 28 | #Synopsis: Use the locally cached modules from prior task and load them using PSDepend 29 | task powershell-import-requirements { 30 | Invoke-PSDepend -Path (Join-Path $BuildRoot 'build') -Confirm:$false -Import 31 | Write-Build DarkGray "$('#' * 20) LOADED MODULES`n$(Get-Module | Format-Table -AutoSize -Wrap -Property ModuleType, Version, PreRelease, Name| Out-String)`n$('#' * 20)" 32 | 33 | } 34 | -------------------------------------------------------------------------------- /build/tasks/tasks-powershell/powershell.format.tasks.ps1: -------------------------------------------------------------------------------- 1 | # Synopsis: Format code using PSScriptAnalyzer 2 | Task powershell-format-code { 3 | $PSScriptAnalyzerExclusions = '(artifacts)|(packages)' 4 | $Settings = Join-Path $BuildRoot 'build/settings/powershell-formatting-settings.psd1' 5 | Write-Build DarkGray 'Applying Formatting to All PS1 Files' 6 | $files = & git status --porcelain | Where-Object { $_ -notmatch '^D' } | 7 | ForEach-Object { $_.TrimStart('ADMR ') } | 8 | ForEach-Object { Get-Item -Path $_ } | 9 | Where-Object { $_ -like '*.ps1' } | 10 | Where-Object FullName -NotMatch $PSScriptAnalyzerExclusions 11 | Write-Build DarkGray "Total Files to Process: $(@($Files).Count)" 12 | $x = 0 13 | $id = Get-Random 14 | Write-Progress -Id $id -Activity 'Formatting Files' -PercentComplete 0 15 | $files | ForEach-Object { 16 | $f = $_ 17 | [string]$content = ([System.IO.File]::ReadAllText($f.FullName)).Trim() 18 | if (-not $Content) { 19 | Write-Build DarkGray "Bypassed: $($f.Name) per empty" 20 | continue 21 | } 22 | $formattedContent = Invoke-Formatter -ScriptDefinition $content -Settings $Settings 23 | $UTF8NoBOM = [System.Text.UTF8Encoding]::new($false) 24 | [System.IO.File]::WriteAllLines($f.FullName, $formattedContent, $UTF8NoBOM) #no bom by default here or could use 6.1 out-file with this built in 25 | Write-Progress -Id $id -Activity 'Formatting Files' -PercentComplete ([math]::Floor(($x / @($files).Count)) * 100) -CurrentOperation "Formatted $($f.FullName)" -ErrorAction SilentlyContinue 26 | $x++ 27 | } -End { 28 | Write-Progress -Id $id -Activity 'Formatting Files' -Completed 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /build/tasks/tasks-powershell/powershell.install.tasks.ps1: -------------------------------------------------------------------------------- 1 | # Synopsis: install all required modules for powershell 2 | task powershell-installmodules { 3 | $RequiredModules = @( 4 | 'Pester' 5 | 'PSScriptAnalyzer' 6 | ) 7 | $RequiredModules | ForEach-Object { 8 | $module = $_ 9 | if (-not (Get-InstalledModule -Name $module -ErrorAction SilentlyContinue)) { 10 | Write-Build DarkGray "$module module not installed, installing now" 11 | Install-Module -Name $module -Force -Scope CurrentUser -AllowClobber -SkipPublisherCheck 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /build/tasks/tasks-powershell/powershell.lint.tasks.ps1: -------------------------------------------------------------------------------- 1 | # Synopsis: Lint code using PSScriptAnalyzer 2 | Task powershell-lint { 3 | $Settings = Join-Path $BuildRoot -ChildPath 'build' -AdditionalChildPath 'settings', 'PSScriptAnalyzerSettings.psd1' 4 | $list = [System.Collections.Generic.List[pscustomobject]]::new() 5 | $scriptAnalyzerRules = Get-ScriptAnalyzerRule 6 | $files = & git diff --name-only --diff-filter=d | Where-Object { $_ -like '*.ps1' } | ForEach-Object { Get-Item -Path $_ } 7 | # generate a progress bar for the files to process 8 | $ProgressPreference = 'Continue' 9 | $id = Get-Random 10 | Write-Progress -Id $id -Activity 'Linting Files' -PercentComplete 0 11 | $x = 0 12 | $rules = @($scriptAnalyzerRules).Count 13 | $scriptAnalyzerRules | ForEach-Object { 14 | $rule = $_ 15 | $percentcomplete = [math]::Floor(($x / $rules) * 100) 16 | Write-Progress -Id $id -Activity 'Linting Files' -Status "${rule.RuleName} ${percentcomplete}" -PercentComplete $percentcomplete -CurrentOperation "Linting $($rule.RuleName)" -ErrorAction SilentlyContinue 17 | $x++ 18 | $files | ForEach-Object { 19 | $file = $_ 20 | Invoke-ScriptAnalyzer -Path $file -Settings $Settings -IncludeRule $rule | ForEach-Object { 21 | $list.Add([pscustomobject]@{ 22 | RuleName = $_.RuleName 23 | ScriptName = $_.ScriptName 24 | Line = $_.Line 25 | Message = $_.Message 26 | }) 27 | } 28 | } 29 | } 30 | $list | Format-Table -AutoSize 31 | Write-Progress -Id $id -Activity 'Linting Files' -Completed 32 | } 33 | -------------------------------------------------------------------------------- /build/tasks/tasks-powershell/powershell.pester.tasks.ps1: -------------------------------------------------------------------------------- 1 | 2 | # Synopsis: run pester against any test files in the repo 3 | task powershell-pester { 4 | Import-Module Pester -MinimumVersion 5.4 5 | 6 | $configuration = [PesterConfiguration]@{ 7 | Run = @{ 8 | Path = '*.tests.ps1' 9 | ExcludePath = '*PSFramework*', '*_tmp*' 10 | PassThru = $True 11 | Container = $pc 12 | } 13 | Should = @{ 14 | ErrorAction = 'Continue' 15 | } 16 | TestResult = @{ 17 | Enabled = $true 18 | OutputPath = (Join-Path $ArtifactDirectory 'TEST-PESTER-RESULTS.xml') 19 | OutputFormat = 'NUnitXml' 20 | } 21 | Output = @{ 22 | Verbosity = 'Diagnostic' 23 | } 24 | } 25 | $files = Get-ChildItem $configuration.Run.Path -Recurse 26 | if ($files.Count -eq 0) { 27 | Write-Build DarkYellow "No test files found in $($configuration.Run.Path)" 28 | return 29 | } 30 | $testresults = @() 31 | $testresults += Invoke-Pester -Configuration $Configuration 32 | 33 | Write-Host '======= TEST RESULT OBJECT =======' 34 | 35 | foreach ($result in $testresults) { 36 | $totalRun += $result.TotalCount 37 | $totalFailed += $result.FailedCount 38 | $result.Tests | Where-Object Result | ForEach-Object { 39 | $testresults += [pscustomobject]@{ 40 | Block = $_.Block 41 | Name = "It $($_.Name)" 42 | Result = $_.Result 43 | Message = $_.ErrorRecord.DisplayErrorMessage 44 | } 45 | } 46 | } 47 | 48 | if ($totalFailed -eq 0) { Write-Build Green "All $totalRun tests executed without a single failure!" } 49 | else { Write-Build Red "$totalFailed tests out of $totalRun tests failed!" } 50 | if ($totalFailed -gt 0) { 51 | throw "$totalFailed / $totalRun tests failed!" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /build/tasks/tasks-terraform/terraform.fmt.tasks.ps1: -------------------------------------------------------------------------------- 1 | # Synopsis: recursively format terraform files 2 | task terraform-fmt { 3 | try { 4 | &terraform fmt -recursive 5 | } 6 | catch { 7 | Write-Build DarkYellow "Terraform fmt didn't complete as terraform is likely not installed." 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /build/tasks/tasks-tidy/clean.tasks.ps1: -------------------------------------------------------------------------------- 1 | #Synposis: cleanup local artifacts 2 | Task clean { 3 | Write-Build DarkGray 'removing artifacts' 4 | Assert { $ArtifactDirectory -match '.artifacts' } 5 | remove $ArtifactDirectory 6 | New-Item -Path $ArtifactDirectory -ItemType Directory -Force -ErrorAction SilentlyContinue *> $null 7 | } 8 | #Synposis: cleanup local artifacts and even the local cached modules 9 | Task deepclean clean, { 10 | Write-Build DarkGray 'removing packages, which includes RequiredModules' 11 | remove (Join-Path $BuildRoot 'packages') 12 | $CheckpointFiles = Get-ChildItem $BuildRoot -Filter *.clixml 13 | remove $CheckpointFiles 14 | } 15 | -------------------------------------------------------------------------------- /build/tasks/tasks-tidy/vscode.tasks.ps1: -------------------------------------------------------------------------------- 1 | # Synopsis: Build tasks for vscode task explorer 2 | Task vscode-rebuild-tasks { 3 | if (-not (Get-InstalledScript 'New-VSCodeTask' -ErrorAction SilentlyContinue)) 4 | { 5 | Write-Build DarkGray 'Installing New-VscodeTask' 6 | Install-Script -Name New-VsCodeTask -Force -Confirm:$false -Scope CurrentUser 7 | } 8 | #New-VSCodeTask.ps1 -BuildFile 'tasks.build.ps1' -Shell 'pwsh' #-WhereTask{ $_.Jobs.Count -gt 1 } 9 | . (Join-Path (Get-InstalledScript 'New-VSCodeTask').InstalledLocation 'New-VSCodeTask.ps1') -BuildFile ./tasks.build.ps1 -Shell 'pwsh' #-WhereTask{ $_.Jobs.Count -gt 1 } 10 | $TasksJsonFile = Join-Path $BuildRoot '.vscode\tasks.json' 11 | $Content = Get-Content $TasksJsonFile -Raw 12 | $NewContent = $Content.Replace('"command": "Invoke-Build -Task', '"command": "./build.ps1 -LoadConstants -Task') 13 | $UTF8NoBOM = [System.Text.UTF8Encoding]::new($false) 14 | [System.IO.File]::WriteAllLines($TasksJsonFile, $NewContent, $UTF8NoBOM) 15 | } 16 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /tasks.build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Description 3 | Initialize and build project 4 | .Notes 5 | PS> New-Alias ib -value "invoke-build" 6 | Set this value in your profile to have a quick alias to call 7 | #> 8 | 9 | [cmdletbinding()] 10 | param( 11 | $BuildRoot = $BuildRoot, 12 | 13 | $Tasks, 14 | [switch]$LoadConstants, 15 | $Configuration 16 | 17 | ) 18 | 19 | foreach ($file in (Get-ChildItem -Path (Join-Path $BuildRoot 'build/functions') -Filter '*.ps1').FullName) { . $file } 20 | foreach ($file in (Get-ChildItem -Path (Join-Path $BuildRoot 'build/tasks') -Filter '*.tasks.ps1' -Recurse).FullName) { . $file } 21 | 22 | # Can handle both windows and mac if powershell core is setup on mac 23 | if ([System.IO.Path]::GetFileName($MyInvocation.ScriptName) -ne 'Invoke-Build.ps1') { 24 | $ErrorActionPreference = 'Stop' 25 | if (!(Get-InstalledModule InvokeBuild -ErrorAction SilentlyContinue)) { 26 | Install-Module InvokeBuild 27 | 'Installed and imported InvokeBuild as was not available' 28 | } 29 | Import-Module InvokeBuild 30 | # call Invoke-Build 31 | Write-Build DarkGray '===InvokeBuild Invocation === ' 32 | Write-Build DarkGray "Invoke-Build $Tasks -File $($MyInvocation.MyCommand.Path) $($PSBoundParameters.GetEnumerator() | Format-List -Force | Out-String)" 33 | & Invoke-Build -Task $Tasks -File $MyInvocation.MyCommand.Path @PSBoundParameters 34 | return 35 | } 36 | 37 | ################################### 38 | # Load All Custom & Project Tasks # 39 | ################################### 40 | 41 | Enter-Build { 42 | # $ProjectDirectory = $BuildRoot | Split-Path -Leaf 43 | $script:ArtifactDirectory = Join-Path $BuildRoot '.artifacts' 44 | $null = New-Item (Join-Path $BuildRoot '.artifacts' ) -ItemType Directory -Force -ErrorAction SilentlyContinue 45 | ## TODO: Reevaluate this approach to use something like like direnv, but in a cross-platform way, if possible. Not happy with this 46 | # if ($PSVersionTable.PSVersion.Major -eq 5) { 47 | # $HOME = $env:USERPROFILE 48 | # } 49 | 50 | # if ($LoadConstants) { 51 | # $ENV:XDG_CONFIG_HOME = $ENV:XDG_CONFIG_HOME ? $ENV:XDG_CONFIG_HOME : (Join-Path $HOME '.config') 52 | # $ENV:XDG_CACHE_HOME = $ENV:XDG_CACHE_HOME ? $ENV:XDG_CACHE_HOME : (Join-Path $HOME '.cache') 53 | # $ENV:XDG_DATA_HOME = $ENV:XDG_DATA_HOME ? $ENV:XDG_DATA_HOME : ($HOME, '.local', 'share' -join [IO.Path]::DirectorySeparatorChar) 54 | 55 | # if (-not (Test-Path (Join-Path $ENV:XDG_CONFIG_HOME '.invokebuild')) ) { 56 | # New-Item (Join-Path $ENV:XDG_CONFIG_HOME '.invokebuild') -ItemType Directory -Force -ErrorAction SilentlyContinue 57 | # Write-Build DarkGray "Created: $(Join-Path $ENV:XDG_CONFIG_HOME '.invokebuild') for storing local overrides" 58 | # } 59 | # $ConstantsFile = Join-Path $ENV:XDG_CONFIG_HOME ".invokebuild/$ProjectDirectory.constants.ps1" 60 | # if (Test-Path $ConstantsFile) { 61 | # . $ConstantsFile 62 | # Write-Build DarkYellow "Loaded: $ConstantsFile" 63 | # } 64 | # else { 65 | # New-Item $ConstantsFile -ItemType File -Force 66 | # Write-Build DarkYellow "Created Constants file $ConstantsFile" 67 | # } 68 | # } 69 | } 70 | 71 | #################################### 72 | # Job Aliases for quick typing # 73 | #################################### 74 | Task tidy job-tidy 75 | Task bootstrap job-bootstrap 76 | Task init job-bootstrap 77 | 78 | 79 | ################## 80 | # Standard Tasks # 81 | ################## 82 | Task job-tidy vscode-rebuild-tasks, powershell-format-code 83 | Task job-bootstrap clean, tidy, check-run-psdepend-install, powershell-import-requirements 84 | # Task lint TDB 85 | 86 | ######## 87 | # CICD # 88 | ######## 89 | Task github-tidy tidy, git-commit-push 90 | --------------------------------------------------------------------------------