diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 72e8ffc0d..000000000 --- a/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 979070b1e..462d3b802 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: needs: - build uses: ./.github/workflows/package.yaml - publish-vscode-extension: + publish-vscode-marketplace: needs: - package runs-on: ubuntu-latest @@ -35,24 +35,47 @@ jobs: name: dist path: dist - - id: get-prerelease - run: echo "prerelease=$(just pre-release)" >> "$GITHUB_OUTPUT" + - id: get-extension-path + run: echo "extension_path=dist/$(just name)-$(just version)-${{ matrix.target }}.vsix" >> "$GITHUB_OUTPUT" - - name: Publish to Open VSX Registry + - name: Publish to Visual Studio Marketplace uses: HaaLeo/publish-vscode-extension@v1 with: - pat: ${{ secrets.OPEN_VSX_TOKEN }} - extensionFile: dist/*-${{ matrix.target }}.vsix - target: ${{ matrix.target }} - preRelease: ${{ steps.get-prerelease.outputs.prerelease == 'true' }} + registryUrl: https://marketplace.visualstudio.com + pat: ${{ secrets.VSCE_PAT }} + extensionFile: ${{ steps.get-extension-path.outputs.extension_path }} skipDuplicate: true - - name: Publish to Visual Studio Marketplace + publish-open-vsx: + needs: + - package + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - darwin-amd64 + - darwin-arm64 + - linux-amd64 + - linux-arm64 + - windows-amd64 + steps: + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v2 + - uses: actions/setup-node@v4 + with: + node-version: "20" + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist + + - id: get-extension-path + run: echo "extension_path=dist/$(just name)-$(just version)-${{ matrix.target }}.vsix" >> "$GITHUB_OUTPUT" + + - name: Publish to Open VSX Registry uses: HaaLeo/publish-vscode-extension@v1 with: - registryUrl: https://marketplace.visualstudio.com - pat: ${{ secrets.VSCE_PAT }} - extensionFile: dist/*-${{ matrix.target }}.vsix - target: ${{ matrix.target }} - preRelease: ${{ steps.get-prerelease.outputs.prerelease == 'true' }} + pat: ${{ secrets.OPEN_VSX_TOKEN }} + extensionFile: ${{ steps.get-extension-path.outputs.extension_path }} skipDuplicate: true diff --git a/CHANGELOG.md b/CHANGELOG.md index cc4513adf..8b442b9b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,70 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.3] + +### Added + +- Added links to marketplaces in extension README (#1899) + +### Changed + +- Split extension publishing CI in to two separate jobs to facilitate reruns (#1898) + +## [1.1.2] + +### Fixed + +- Fixed VSIX target OS and architecture (#1895) + +## [1.1.1] + +### Fixed + +- Fixed the release tooling for publishing the VSCode extension (#1893) + +## [1.1.0] + +### Added + +- Added a [VSCode Walkthrough](https://code.visualstudio.com/api/ux-guidelines/walkthroughs) + for the extension (#1834) +- Added the currently-deploying Deployment's title to the Posit Publisher Logs + view for more clarity (#1756) +- Added a confirmation prompt when deleting a Credential (#1733) +- Added a "Get Package Descriptions" deploying stage to the Posit Publisher Logs + view for R projects (#1593) +- Added a link to the GitHub Discussions for the project in the help and + feedback view (#1884) +- Added validation for server URLs and API keys when creating Credentials + (#1610, #1611, #1612) + +### Changed + +- Generated configuration files now omit `has_parameters` if it is `false` + (#1788) +- Adjusted the VSCode extension file watcher to send file information to views + less frequently when large amounts of files are being created or deleted. + Sending is now debounced. (#1735) +- Rendered files are now excluded by default in generated Configurations for + Quarto deployments (#1707) +- Moved the settings for the VSCode extension under the "Posit Publisher" title + (#1869) +- Stopped `.ipynb` from being identified as potential `quarto` projects to avoid + deploying HTML without executing the code cells (#1863) + +### Fixed + +- Deployed Posit Connect manifests no longer contain empty option sections if + they are ommitted in the configuration file (#1823) +- Fixed the "Got To Publishing Log" link in the last deployment message + context menu (#1889) + +### Removed + +- Removed the executable path option now that the extension is bundled with the + publisher binary (#1877) + ## [1.0.beta1] ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 16080627f..dadebcc72 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -136,6 +136,9 @@ minor version number is odd. - the [CHANGELOG.md](CHANGELOG.md) for the repository - the [VSCode Extension CHANGELOG.md](extensions/vscode/CHANGELOG.md) that is bundled with the extension + - Update the Installation instructions in [installation.md](docs/installation.md) + for the new release, using the links to the `.vsix` files uploaded to the CDN. + - Update the release / latest version string in the `install-publisher.bash` script. - Update the license docs in case any new dependencies have been added, by running ``` @@ -173,12 +176,3 @@ This command will trigger the [Release GitHub Action](https://github.com/rstudio Once complete the action has completed, the release will be available on the [Releases page](https://github.com/rstudio/publishing-client/releases), and published to the VSCode Marketplace. - -**Step 4** - -Update the Installation instructions in [installation.md](docs/installation.md) -for the new release, using the links to the `.vsix` files uploaded to the CDN. - -**Step 5** - -Update the release / latest version string in the `install-publisher.bash` script. diff --git a/docs/installation.md b/docs/installation.md index 13b466f30..034013adf 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -7,11 +7,11 @@ VSCode. Download and install the VSCode extension. -- For Arm MacOS: [publisher-1.1.0-darwin-arm64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.0/publisher-1.1.0-darwin-arm64.vsix) -- For Intel MacOS: [publisher-1.1.0-darwin-amd64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.0/publisher-1.1.0-darwin-amd64.vsix) -- For Windows: [publisher-1.1.0-windows-amd64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.0/publisher-1.1.0-windows-amd64.vsix) -- For Arm Linux: [publisher-1.1.0-linux-arm64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.0/publisher-1.1.0-linux-arm64.vsix) -- For Intel Linux: [publisher-1.1.0-linux-amd64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.0/publisher-1.1.0-linux-amd64.vsix) +- For Arm MacOS: [publisher-1.1.3-darwin-arm64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.3/publisher-1.1.3-darwin-arm64.vsix) +- For Intel MacOS: [publisher-1.1.3-darwin-amd64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.3/publisher-1.1.3-darwin-amd64.vsix) +- For Windows: [publisher-1.1.3-windows-amd64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.3/publisher-1.1.3-windows-amd64.vsix) +- For Arm Linux: [publisher-1.1.3-linux-arm64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.3/publisher-1.1.3-linux-arm64.vsix) +- For Intel Linux: [publisher-1.1.3-linux-amd64.vsix](https://cdn.posit.co/publisher/releases/tags/v1.1.3/publisher-1.1.3-linux-amd64.vsix) To learn how to install a `.vsix` file, see the [Install from a VSIX](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix) diff --git a/docs/licenses.md b/docs/licenses.md index accd7866d..9cb0e9d37 100644 --- a/docs/licenses.md +++ b/docs/licenses.md @@ -1,32 +1,5 @@ # Licenses -### @aashutoshrathi/word-wrap - -``` -The MIT License (MIT) - -Copyright (c) 2014-2016, Jon Schlinkert - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -``` - ### @babel/parser ``` @@ -1053,7 +1026,7 @@ SOFTWARE. ``` -### @tsconfig/node18 +### @tsconfig/node20 ``` MIT License @@ -1134,33 +1107,6 @@ SOFTWARE ``` -### @types/json-schema - -``` - MIT License - - Copyright (c) Microsoft Corporation. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE - -``` - ### @types/mocha ``` @@ -1269,33 +1215,6 @@ SOFTWARE ``` -### @types/semver - -``` - MIT License - - Copyright (c) Microsoft Corporation. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE - -``` - ### @types/vscode ``` @@ -2156,6 +2075,33 @@ THE SOFTWARE. ``` +### agent-base + +``` +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + ### ajv ``` @@ -2559,6 +2505,105 @@ SOFTWARE. ``` +### base64-js + +``` +The MIT License (MIT) + +Copyright (c) 2014 Jameson Little + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +### bl + +``` +The MIT License (MIT) +===================== + +Copyright (c) 2013-2019 bl contributors +---------------------------------- + +*bl contributors listed at * + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +### bl/node_modules/readable-stream + +``` +Node.js is licensed for use as follows: + +""" +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +This license applies to parts of Node.js originating from the +https://github.com/joyent/node repository: + +""" +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +``` + ### brace-expansion ``` @@ -2591,7 +2636,7 @@ SOFTWARE. ``` The MIT License (MIT) -Copyright (c) 2014-2018, Jon Schlinkert. +Copyright (c) 2014-present, Jon Schlinkert. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -2624,6 +2669,33 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE ``` +### buffer + +``` +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh, and other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + ### chokidar ``` @@ -5376,15 +5448,16 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -### ignore +### http-proxy-agent ``` -Copyright (c) 2013 Kael Zhang , contributors -http://kael.me/ +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including +'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to @@ -5393,23 +5466,26 @@ the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ``` -### immediate +### https-proxy-agent ``` -Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, Domenic Denicola, Brian Cavalier +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including +'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to @@ -5418,15 +5494,83 @@ the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -``` +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +### ieee754 + +``` +Copyright 2008 Fair Oaks Labs, Inc. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +### ignore + +``` +Copyright (c) 2013 Kael Zhang , contributors +http://kael.me/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +### immediate + +``` +Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, Domenic Denicola, Brian Cavalier + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` ### immutable @@ -5616,7 +5760,7 @@ from liability. ## Acceptance In order to receive this license, you must agree to its -rules. The rules of this license are both obligations +rules. The rules of this license are both obligations under that agreement and conditions to your license. You must not do anything with this software that triggers a rule that you cannot or will not follow. @@ -5639,7 +5783,7 @@ changes, also gets the text of this license or a link to If anyone notifies you in writing that you have not complied with [Notices](#notices), you can keep your license by taking all practical steps to comply within 30 -days after the notice. If you do not do so, your license +days after the notice. If you do not do so, your license ends immediately. ## Patent @@ -5654,10 +5798,10 @@ No contributor can revoke this license. ## No Liability -***As far as the law allows, this software comes as is, +**_As far as the law allows, this software comes as is, without any warranty or condition, and no contributor will be liable to anyone for any damages related to this -software or this license, under any kind of legal claim.*** +software or this license, under any kind of legal claim._** ``` @@ -6564,7 +6708,7 @@ THE SOFTWARE. ``` The ISC License -Copyright (c) Isaac Z. Schlueter and Contributors +Copyright (c) 2010-2023 Isaac Z. Schlueter and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -6773,6 +6917,32 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +### mocha/node_modules/debug + +``` +(The MIT License) + +Copyright (c) 2014-2017 TJ Holowaychuk +Copyright (c) 2018-2021 Josh Junon + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the 'Software'), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + ``` ### mocha/node_modules/glob @@ -6973,6 +7143,32 @@ OTHER DEALINGS IN THE SOFTWARE. ``` +### ora/node_modules/emoji-regex + +``` +Copyright Mathias Bynens + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + ### pako ``` @@ -7510,6 +7706,28 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` +### restore-cursor/node_modules/signal-exit + +``` +The ISC License + +Copyright (c) 2015, Contributors + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + ### reusify ``` @@ -8438,6 +8656,39 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- + +_macros and macros license: + +Copyright 2024, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------------------- analyzer, protobuf and protoc_plugin license: @@ -9528,32 +9779,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- - -pointycastle license: - - -Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -------------------------------------------------------------------------------- pub_api_client license: @@ -10012,6 +10237,39 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- + +web_socket license: + +Copyright 2024, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------------------- yaml license: @@ -10060,27 +10318,6 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ``` -### semver/node_modules/lru-cache - -``` -The ISC License - -Copyright (c) Isaac Z. Schlueter and Contributors - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -``` - ### serialize-javascript ``` @@ -10564,7 +10801,7 @@ SOFTWARE. # Licenses of bundled dependencies The published Vite artifact additionally contains code with the following licenses: -Apache-2.0, BSD-2-Clause, CC0-1.0, ISC, MIT +Apache-2.0, BSD-2-Clause, BlueOak-1.0.0, CC0-1.0, ISC, MIT # Bundled dependencies: ## @ampproject/remapping @@ -11126,65 +11363,7 @@ Repository: rollup/plugins --------------------------------------- -## acorn -License: MIT -By: Marijn Haverbeke, Ingvar Stepanyan, Adrian Heine -Repository: https://github.com/acornjs/acorn.git - -> MIT License -> -> Copyright (C) 2012-2022 by various contributors (see AUTHORS) -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. - ---------------------------------------- - -## acorn-walk -License: MIT -By: Marijn Haverbeke, Ingvar Stepanyan, Adrian Heine -Repository: https://github.com/acornjs/acorn.git - -> MIT License -> -> Copyright (C) 2012-2020 by various contributors (see AUTHORS) -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -> THE SOFTWARE. - ---------------------------------------- - -## ansi-regex +## ansi-regex License: MIT By: Sindre Sorhus Repository: chalk/ansi-regex @@ -11363,7 +11542,7 @@ Repository: micromatch/braces > The MIT License (MIT) > -> Copyright (c) 2014-2018, Jon Schlinkert. +> Copyright (c) 2014-present, Jon Schlinkert. > > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal @@ -12067,57 +12246,6 @@ Repository: git@github.com:follow-redirects/follow-redirects.git --------------------------------------- -## fs.realpath -License: ISC -By: Isaac Z. Schlueter -Repository: git+https://github.com/isaacs/fs.realpath.git - -> The ISC License -> -> Copyright (c) Isaac Z. Schlueter and Contributors -> -> Permission to use, copy, modify, and/or distribute this software for any -> purpose with or without fee is hereby granted, provided that the above -> copyright notice and this permission notice appear in all copies. -> -> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -> -> ---- -> -> This library bundles a version of the `fs.realpath` and `fs.realpathSync` -> methods from Node.js v0.10 under the terms of the Node.js MIT license. -> -> Node's license follows, also included at the header of `old.js` which contains -> the licensed code: -> -> Copyright Joyent, Inc. and other Node contributors. -> -> Permission is hereby granted, free of charge, to any person obtaining a -> copy of this software and associated documentation files (the "Software"), -> to deal in the Software without restriction, including without limitation -> the rights to use, copy, modify, merge, publish, distribute, sublicense, -> and/or sell copies of the Software, and to permit persons to whom the -> Software is furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in -> all copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -> FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -> DEALINGS IN THE SOFTWARE. - ---------------------------------------- - ## generic-names License: MIT By: Alexey Litvinov @@ -12154,7 +12282,7 @@ Repository: git://github.com/isaacs/node-glob.git > The ISC License > -> Copyright (c) 2009-2022 Isaac Z. Schlueter and Contributors +> Copyright (c) 2009-2023 Isaac Z. Schlueter and Contributors > > Permission to use, copy, modify, and/or distribute this software for any > purpose with or without fee is hereby granted, provided that the above @@ -12237,51 +12365,6 @@ Repository: git+https://github.com/css-modules/icss-utils.git --------------------------------------- -## inflight -License: ISC -By: Isaac Z. Schlueter -Repository: https://github.com/npm/inflight.git - -> The ISC License -> -> Copyright (c) Isaac Z. Schlueter -> -> Permission to use, copy, modify, and/or distribute this software for any -> purpose with or without fee is hereby granted, provided that the above -> copyright notice and this permission notice appear in all copies. -> -> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ---------------------------------------- - -## inherits -License: ISC -Repository: git://github.com/isaacs/inherits - -> The ISC License -> -> Copyright (c) Isaac Z. Schlueter -> -> Permission to use, copy, modify, and/or distribute this software for any -> purpose with or without fee is hereby granted, provided that the above -> copyright notice and this permission notice appear in all copies. -> -> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -> FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -> LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -> OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -> PERFORMANCE OF THIS SOFTWARE. - ---------------------------------------- - ## is-binary-path License: MIT By: Sindre Sorhus @@ -12649,6 +12732,29 @@ Repository: lodash/lodash --------------------------------------- +## lru-cache +License: ISC +By: Isaac Z. Schlueter +Repository: git://github.com/isaacs/node-lru-cache.git + +> The ISC License +> +> Copyright (c) 2010-2023 Isaac Z. Schlueter and Contributors +> +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted, provided that the above +> copyright notice and this permission notice appear in all copies. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--------------------------------------- + ## magic-string License: MIT By: Rich Harris @@ -12744,6 +12850,29 @@ Repository: git://github.com/isaacs/minimatch.git --------------------------------------- +## minipass +License: ISC +By: Isaac Z. Schlueter +Repository: https://github.com/isaacs/minipass + +> The ISC License +> +> Copyright (c) 2017-2023 npm, Inc., Isaac Z. Schlueter, and Contributors +> +> Permission to use, copy, modify, and/or distribute this software for any +> purpose with or without fee is hereby granted, provided that the above +> copyright notice and this permission notice appear in all copies. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--------------------------------------- + ## mlly License: MIT Repository: unjs/mlly @@ -12918,29 +13047,6 @@ Repository: jshttp/on-finished --------------------------------------- -## once -License: ISC -By: Isaac Z. Schlueter -Repository: git://github.com/isaacs/once - -> The ISC License -> -> Copyright (c) Isaac Z. Schlueter and Contributors -> -> Permission to use, copy, modify, and/or distribute this software for any -> purpose with or without fee is hereby granted, provided that the above -> copyright notice and this permission notice appear in all copies. -> -> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ---------------------------------------- - ## open License: MIT By: Sindre Sorhus @@ -13033,6 +13139,69 @@ Repository: sindresorhus/path-key --------------------------------------- +## path-scurry +License: BlueOak-1.0.0 +By: Isaac Z. Schlueter +Repository: git+https://github.com/isaacs/path-scurry + +> # Blue Oak Model License +> +> Version 1.0.0 +> +> ## Purpose +> +> This license gives everyone as much permission to work with +> this software as possible, while protecting contributors +> from liability. +> +> ## Acceptance +> +> In order to receive this license, you must agree to its +> rules. The rules of this license are both obligations +> under that agreement and conditions to your license. +> You must not do anything with this software that triggers +> a rule that you cannot or will not follow. +> +> ## Copyright +> +> Each contributor licenses you to do everything with this +> software that would otherwise infringe that contributor's +> copyright in it. +> +> ## Notices +> +> You must ensure that everyone who gets a copy of +> any part of this software from you, with or without +> changes, also gets the text of this license or a link to +> . +> +> ## Excuse +> +> If anyone notifies you in writing that you have not +> complied with [Notices](#notices), you can keep your +> license by taking all practical steps to comply within 30 +> days after the notice. If you do not do so, your license +> ends immediately. +> +> ## Patent +> +> Each contributor licenses you to do everything with this +> software that would otherwise infringe any patent claims +> they can license or become able to license. +> +> ## Reliability +> +> No contributor can revoke this license. +> +> ## No Liability +> +> ***As far as the law allows, this software comes as is, +> without any warranty or condition, and no contributor +> will be liable to anyone for any damages related to this +> software or this license, under any kind of legal claim.*** + +--------------------------------------- + ## periscopic License: MIT Repository: Rich-Harris/periscopic @@ -13982,29 +14151,6 @@ Repository: git://github.com/isaacs/node-which.git --------------------------------------- -## wrappy -License: ISC -By: Isaac Z. Schlueter -Repository: https://github.com/npm/wrappy - -> The ISC License -> -> Copyright (c) Isaac Z. Schlueter and Contributors -> -> Permission to use, copy, modify, and/or distribute this software for any -> purpose with or without fee is hereby granted, provided that the above -> copyright notice and this permission notice appear in all copies. -> -> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -> IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ---------------------------------------- - ## ws License: MIT By: Einar Otto Stangvik @@ -14054,6 +14200,20 @@ Repository: github:eemeli/yaml ``` +### vscode-uri + +``` +The MIT License (MIT) + +Copyright (c) Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + ### vue ``` @@ -14156,6 +14316,33 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ``` +### word-wrap + +``` +The MIT License (MIT) + +Copyright (c) 2014-2016, Jon Schlinkert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + ### workerpool ``` @@ -14428,27 +14615,6 @@ THIS SOFTWARE. ``` -### yallist - -``` -The ISC License - -Copyright (c) Isaac Z. Schlueter and Contributors - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -``` - ### yargs ``` diff --git a/extensions/vscode/CHANGELOG.md b/extensions/vscode/CHANGELOG.md index 5d9b89f9c..45fbb4f51 100644 --- a/extensions/vscode/CHANGELOG.md +++ b/extensions/vscode/CHANGELOG.md @@ -6,6 +6,6 @@ file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [1.1.3] - Initial beta release diff --git a/extensions/vscode/README.md b/extensions/vscode/README.md index dfefc859e..11855a173 100644 --- a/extensions/vscode/README.md +++ b/extensions/vscode/README.md @@ -6,4 +6,6 @@ way to deploy to [Posit Connect](https://posit.co/products/enterprise/connect/). ## Installation -Install Posit Publisher from the [Visual Studio Code Marketplace] +Install Posit Publisher from the +[Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=Posit.publisher) +or from the [Open VSX Registry](https://open-vsx.org/extension/posit/publisher). diff --git a/extensions/vscode/justfile b/extensions/vscode/justfile index 38ae0a6f8..25a27a6cb 100644 --- a/extensions/vscode/justfile +++ b/extensions/vscode/justfile @@ -3,7 +3,7 @@ alias t := test _ci := env_var_or_default("CI", "false") -_debug := env_var_or_default("DEBUG", "false") +_debug := env_var_or_default("DEBUG", _ci) _with_debug := if _debug == "true" { "set -x pipefail" @@ -136,14 +136,19 @@ package os="$(just ../../os)" arch="$(just ../../arch)": {{ _with_debug }} just assert-binary-executable-exists echo "info: getting package target platform..." 1>&2 - target_platform=$(just package-target-platform) + target_platform=$(just package-target-platform {{ os }} {{ arch }}) echo "info: getting package output path..." 1>&2 path="$(just package-path)" mkdir -p "$path" echo "info: getting output package name..." 1>&2 package="$path"/$(just package-name {{ os }} {{ arch }}) echo "info: packaging..." 1>&2 - npx --yes @vscode/vsce package -o "$package" -t "$target_platform" | sed 's/^/debug: /' + if [[ $(just ../../pre-release) == true ]]; then + prerelease_flag="--pre-release" + else + prerelease_flag="" + fi + npx --yes @vscode/vsce package -o "$package" -t "$target_platform" $prerelease_flag | sed 's/^/debug: /' echo "info: writing package to $package..." 1>&2 package-name os="$(just ../../os)" arch="$(just ../../arch)": diff --git a/extensions/vscode/scripts/set-version.py b/extensions/vscode/scripts/set-version.py index 4ac659c71..48890d533 100644 --- a/extensions/vscode/scripts/set-version.py +++ b/extensions/vscode/scripts/set-version.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import json +import os import re import sys @@ -8,9 +9,13 @@ print("Usage: set-version.py ") sys.exit(1) +if os.environ.get("CI") != "true": + print("Not running in CI; skipping version update.") + sys.exit(0) + version = sys.argv[1] -version_re = re.compile(r"\d+\.\d+\.\d+") +version_re = re.compile(r"^\d+\.\d+\.\d+$") if not version_re.match(version): print(f"Version {version} is not a release version; skipping version update.") diff --git a/install-publisher.bash b/install-publisher.bash index 20c928e61..7b66bce69 100755 --- a/install-publisher.bash +++ b/install-publisher.bash @@ -148,7 +148,7 @@ esac # version override, swap out latest with the latest and greatest if [[ $VERSION_TYPE == "release" && $VERSION == "latest" ]]; then - VERSION="1.0.beta1" + VERSION="1.1.3" fi # Variables diff --git a/internal/services/api/api_service.go b/internal/services/api/api_service.go index 64f0e93e7..d635cd156 100644 --- a/internal/services/api/api_service.go +++ b/internal/services/api/api_service.go @@ -102,10 +102,6 @@ func RouterHandlerFunc(base util.AbsolutePath, lister accounts.AccountList, log r.Handle(ToPath("configurations"), GetConfigurationsHandlerFunc(base, log)). Methods(http.MethodGet) - // POST /api/initialize - r.Handle(ToPath("initialize"), PostInitializeHandlerFunc(base, log)). - Methods(http.MethodPost) - // GET /api/configurations/$NAME r.Handle(ToPath("configurations", "{name}"), GetConfigurationHandlerFunc(base, log)). Methods(http.MethodGet) diff --git a/internal/services/api/delete_deployment.go b/internal/services/api/delete_deployment.go index 7f894150b..bde670f59 100644 --- a/internal/services/api/delete_deployment.go +++ b/internal/services/api/delete_deployment.go @@ -16,8 +16,13 @@ import ( func DeleteDeploymentHandlerFunc(base util.AbsolutePath, log logging.Logger) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { name := mux.Vars(req)["name"] - path := deployment.GetDeploymentPath(base, name) - err := path.Remove() + projectDir, _, err := ProjectDirFromRequest(base, w, req, log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } + path := deployment.GetDeploymentPath(projectDir, name) + err = path.Remove() if err != nil { if errors.Is(err, fs.ErrNotExist) { http.NotFound(w, req) diff --git a/internal/services/api/delete_deployment_test.go b/internal/services/api/delete_deployment_test.go index 065c35dec..099e87da6 100644 --- a/internal/services/api/delete_deployment_test.go +++ b/internal/services/api/delete_deployment_test.go @@ -3,8 +3,10 @@ package api // Copyright (C) 2023 by Posit Software, PBC. import ( + "fmt" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/gorilla/mux" @@ -105,3 +107,56 @@ func (s *DeleteDeploymentSuite) TestDeleteDeploymentNotFound() { s.Equal(http.StatusNotFound, rec.Result().StatusCode) } + +func (s *DeleteDeploymentSuite) TestDeleteDeploymentFromSubdir() { + base := s.cwd.Dir().Dir() + targetToDelete := "myTarget" + + // Create a deployment in the upper directory that should be left alone + _, err := createSampleDeployment(base, targetToDelete) + s.NoError(err) + pathToPreserve := deployment.GetDeploymentPath(base, targetToDelete) + s.fileExists(pathToPreserve) + + // Create a deployment in the subdir that should be deleted + _, err = createSampleDeployment(s.cwd, targetToDelete) + s.NoError(err) + targetPath := deployment.GetDeploymentPath(s.cwd, targetToDelete) + s.fileExists(targetPath) + + relProjectDir, err := s.cwd.Rel(base) + s.NoError(err) + + h := DeleteDeploymentHandlerFunc(base, s.log) + + dirParam := url.QueryEscape(relProjectDir.String()) + apiUrl := fmt.Sprintf("/api/deployments/%s?dir=%s", targetToDelete, dirParam) + + rec := httptest.NewRecorder() + req, err := http.NewRequest("DELETE", apiUrl, nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{ + "name": targetToDelete, + }) + h(rec, req) + + s.Equal(http.StatusNoContent, rec.Result().StatusCode) + s.fileDoesNotExist(targetPath) + s.fileExists(pathToPreserve) +} + +func (s *DeleteDeploymentSuite) TestDeleteDeploymentBadDir() { + // It's a Bad Request to try to delete a deployment from a directory outside the project + _, err := createSampleDeployment(s.cwd, "myTargetName") + s.NoError(err) + + rec := httptest.NewRecorder() + req, err := http.NewRequest("DELETE", "/api/deployments/myTargetName?dir=../middleware", nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"id": "myTargetName"}) + + h := DeleteDeploymentHandlerFunc(s.cwd, logging.New()) + h(rec, req) + + s.Equal(http.StatusBadRequest, rec.Result().StatusCode) +} diff --git a/internal/services/api/deployment_dto.go b/internal/services/api/deployment_dto.go index 2ea63fc11..6f81459be 100644 --- a/internal/services/api/deployment_dto.go +++ b/internal/services/api/deployment_dto.go @@ -19,9 +19,10 @@ const ( ) type deploymentLocation struct { - State deploymentState `json:"state"` - Name string `json:"deploymentName"` - Path string `json:"deploymentPath"` + State deploymentState `json:"state"` + Name string `json:"deploymentName"` + Path string `json:"deploymentPath"` + ProjectDir string `json:"projectDir"` // Relative path to the project directory from the global base } type preDeploymentDTO struct { @@ -55,28 +56,30 @@ func getConfigPath(base util.AbsolutePath, configName string) util.AbsolutePath return config.GetConfigPath(base, configName) } -func deploymentAsDTO(d *deployment.Deployment, err error, base util.AbsolutePath, path util.AbsolutePath) any { +func deploymentAsDTO(d *deployment.Deployment, err error, projectDir util.AbsolutePath, relProjectDir util.RelativePath, path util.AbsolutePath) any { saveName := deployment.SaveNameFromPath(path) configPath := "" if err != nil { return &deploymentErrorDTO{ deploymentLocation: deploymentLocation{ - State: deploymentStateError, - Name: saveName, - Path: path.String(), + State: deploymentStateError, + Name: saveName, + Path: path.String(), + ProjectDir: relProjectDir.String(), }, Error: types.AsAgentError(err), } } else if d.ID != "" { if d.ConfigName != "" { - configPath = getConfigPath(base, d.ConfigName).String() + configPath = getConfigPath(projectDir, d.ConfigName).String() } return &fullDeploymentDTO{ deploymentLocation: deploymentLocation{ - State: deploymentStateDeployed, - Name: saveName, - Path: path.String(), + State: deploymentStateDeployed, + Name: saveName, + Path: path.String(), + ProjectDir: relProjectDir.String(), }, Deployment: *d, ConfigPath: configPath, @@ -84,13 +87,14 @@ func deploymentAsDTO(d *deployment.Deployment, err error, base util.AbsolutePath } } else { if d.ConfigName != "" { - configPath = getConfigPath(base, d.ConfigName).String() + configPath = getConfigPath(projectDir, d.ConfigName).String() } return preDeploymentDTO{ deploymentLocation: deploymentLocation{ - State: deploymentStateNew, - Name: saveName, - Path: path.String(), + State: deploymentStateNew, + Name: saveName, + Path: path.String(), + ProjectDir: relProjectDir.String(), }, Schema: d.Schema, ServerType: d.ServerType, diff --git a/internal/services/api/get_config_files.go b/internal/services/api/get_config_files.go index 93717fbf7..d4827bc5c 100644 --- a/internal/services/api/get_config_files.go +++ b/internal/services/api/get_config_files.go @@ -19,25 +19,30 @@ import ( func GetConfigFilesHandlerFunc(base util.AbsolutePath, filesService files.FilesService, log logging.Logger) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { name := mux.Vars(req)["name"] - configPath := config.GetConfigPath(base, name) + projectDir, _, err := ProjectDirFromRequest(base, w, req, log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } + configPath := config.GetConfigPath(projectDir, name) cfg, err := config.FromFile(configPath) if err != nil && errors.Is(err, fs.ErrNotExist) { http.NotFound(w, req) return } - matchList, err := matcher.NewMatchList(base, matcher.StandardExclusions) + matchList, err := matcher.NewMatchList(projectDir, matcher.StandardExclusions) if err != nil { InternalError(w, req, log, err) return } - err = matchList.AddFromFile(base, configPath, cfg.Files) + err = matchList.AddFromFile(projectDir, configPath, cfg.Files) if err != nil { w.WriteHeader(http.StatusUnprocessableEntity) w.Write([]byte("invalid pattern in configuration 'files'")) return } - file, err := filesService.GetFile(base, matchList) + file, err := filesService.GetFile(projectDir, matchList) if err != nil { InternalError(w, req, log, err) return diff --git a/internal/services/api/get_config_files_test.go b/internal/services/api/get_config_files_test.go index ea850df85..429e6ccb9 100644 --- a/internal/services/api/get_config_files_test.go +++ b/internal/services/api/get_config_files_test.go @@ -7,6 +7,7 @@ import ( "errors" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/gorilla/mux" @@ -158,3 +159,79 @@ func (s *GetConfigFilesHandlerFuncSuite) TestHandlerFuncInvalidConfigFiles() { s.Equal(http.StatusUnprocessableEntity, rec.Result().StatusCode) } + +func (s *GetConfigFilesHandlerFuncSuite) TestHandlerFuncSubdir() { + afs := afero.NewMemMapFs() + projectDir, err := util.Getwd(afs) + s.NoError(err) + + // We are requesting files from a project directory two levels down. + base := projectDir.Dir().Dir() + relProjectDir, err := projectDir.Rel(base) + s.NoError(err) + + src := &files.File{Rel: "."} + + filesService := new(MockFilesService) + filesService.On("GetFile", projectDir, mock.Anything).Return(src, nil) + + h := GetConfigFilesHandlerFunc(base, filesService, s.log) + + dirParam := url.QueryEscape(relProjectDir.String()) + rec := httptest.NewRecorder() + + cfg := config.New() + cfg.Type = config.ContentTypeHTML + cfg.Files = []string{"*", "!ignoreme"} + err = cfg.WriteFile(config.GetConfigPath(projectDir, "myConfig")) + s.NoError(err) + + req, err := http.NewRequest("GET", "/api/configurations/myConfig?dir="+dirParam, nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"name": "myConfig"}) + + h(rec, req) + + s.Equal(http.StatusOK, rec.Result().StatusCode) + s.Equal("application/json", rec.Header().Get("content-type")) + + res := &files.File{} + dec := json.NewDecoder(rec.Body) + dec.DisallowUnknownFields() + s.NoError(dec.Decode(res)) + + s.Equal(src.Rel, res.Rel) + s.Equal(src.RelDir, res.RelDir) +} + +func (s *GetConfigFilesHandlerFuncSuite) TestHandlerFuncBadSubdir() { + afs := afero.NewMemMapFs() + projectDir, err := util.Getwd(afs) + s.NoError(err) + + // We are requesting files from a project directory two levels down. + base := projectDir.Dir().Dir() + + src := &files.File{Rel: "."} + + filesService := new(MockFilesService) + filesService.On("GetFile", projectDir, mock.Anything).Return(src, nil) + + h := GetConfigFilesHandlerFunc(base, filesService, s.log) + + rec := httptest.NewRecorder() + + cfg := config.New() + cfg.Type = config.ContentTypeHTML + cfg.Files = []string{"*"} + err = cfg.WriteFile(config.GetConfigPath(projectDir, "myConfig")) + s.NoError(err) + + req, err := http.NewRequest("GET", "/api/configurations/myConfig?dir=../middleware", nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"name": "myConfig"}) + + h(rec, req) + + s.Equal(http.StatusBadRequest, rec.Result().StatusCode) +} diff --git a/internal/services/api/get_config_python_packages.go b/internal/services/api/get_config_python_packages.go index 3f8601934..8f5a328de 100644 --- a/internal/services/api/get_config_python_packages.go +++ b/internal/services/api/get_config_python_packages.go @@ -35,7 +35,12 @@ func NewGetConfigPythonPackagesHandler(base util.AbsolutePath, log logging.Logge func (h *getConfigPythonPackagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { name := mux.Vars(req)["name"] - configPath := config.GetConfigPath(h.base, name) + projectDir, _, err := ProjectDirFromRequest(h.base, w, req, h.log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } + configPath := config.GetConfigPath(projectDir, name) cfg, err := config.FromFile(configPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -59,7 +64,7 @@ func (h *getConfigPythonPackagesHandler) ServeHTTP(w http.ResponseWriter, req *h requirementsFilename = inspect.PythonRequirementsFilename } - path := h.base.Join(requirementsFilename) + path := projectDir.Join(requirementsFilename) reqs, err := h.inspector.ReadRequirementsFile(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { diff --git a/internal/services/api/get_config_python_packages_test.go b/internal/services/api/get_config_python_packages_test.go index f00337f57..5744d8ed0 100644 --- a/internal/services/api/get_config_python_packages_test.go +++ b/internal/services/api/get_config_python_packages_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/gorilla/mux" @@ -125,3 +126,45 @@ func (s *GetConfigRequirementsSuite) TestGetConfigRequirementsNoPythonInConfig() s.Equal(http.StatusConflict, rec.Result().StatusCode) } + +func (s *GetConfigRequirementsSuite) TestGetConfigRequirementsSubdir() { + reqs := []byte("numpy\npandas\n") + s.cwd.Join("requirements-dev.txt").WriteFile(reqs, 0666) + + // We are getting requirements from a project directory two levels down + base := s.cwd.Dir().Dir() + relProjectDir, err := s.cwd.Rel(base) + s.NoError(err) + + cfg := config.New() + cfg.Type = config.ContentTypeHTML + cfg.Python = &config.Python{ + Version: "3.11.3", + PackageManager: "pip", + PackageFile: "requirements-dev.txt", + } + err = cfg.WriteFile(config.GetConfigPath(s.cwd, "myConfig")) + s.NoError(err) + + dirParam := url.QueryEscape(relProjectDir.String()) + rec := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/api/configurations/myConfig/requirements?dir="+dirParam, nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"name": "myConfig"}) + + h := NewGetConfigPythonPackagesHandler(base, s.log) + h.ServeHTTP(rec, req) + + s.Equal(http.StatusOK, rec.Result().StatusCode) + s.Equal("application/json", rec.Header().Get("content-type")) + + res := pythonPackagesDTO{} + dec := json.NewDecoder(rec.Body) + dec.DisallowUnknownFields() + s.NoError(dec.Decode(&res)) + s.NotNil(res.Requirements) + s.Equal([]string{ + "numpy", + "pandas", + }, res.Requirements) +} diff --git a/internal/services/api/get_config_r_packages.go b/internal/services/api/get_config_r_packages.go index fbdd2f828..745cbc18f 100644 --- a/internal/services/api/get_config_r_packages.go +++ b/internal/services/api/get_config_r_packages.go @@ -30,7 +30,12 @@ func NewGetConfigRPackagesHandler(base util.AbsolutePath, log logging.Logger) *g func (h *getConfigRPackagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { name := mux.Vars(req)["name"] - configPath := config.GetConfigPath(h.base, name) + projectDir, _, err := ProjectDirFromRequest(h.base, w, req, h.log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } + configPath := config.GetConfigPath(projectDir, name) cfg, err := config.FromFile(configPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -53,7 +58,7 @@ func (h *getConfigRPackagesHandler) ServeHTTP(w http.ResponseWriter, req *http.R packageFilename = inspect.DefaultRenvLockfile } - path := h.base.Join(packageFilename) + path := projectDir.Join(packageFilename) response, err := renv.ReadLockfile(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { diff --git a/internal/services/api/get_configuration_test.go b/internal/services/api/get_configuration_test.go index 606ba2090..3fb58838c 100644 --- a/internal/services/api/get_configuration_test.go +++ b/internal/services/api/get_configuration_test.go @@ -168,3 +168,18 @@ func (s *GetConfigurationuite) TestGetConfigurationFromSubdir() { s.Nil(res.Error) s.Equal(cfg, res.Configuration) } + +func (s *GetConfigurationuite) TestGetConfigurationBadDir() { + // It's a Bad Request to try to get a config from a directory outside the project + _ = s.makeConfiguration("myConfig") + + h := GetConfigurationHandlerFunc(s.cwd, s.log) + + rec := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/api/configurations/myConfig?dir=../middleware", nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"id": "myConfig"}) + h(rec, req) + + s.Equal(http.StatusBadRequest, rec.Result().StatusCode) +} diff --git a/internal/services/api/get_deployment.go b/internal/services/api/get_deployment.go index ef50ede09..e9d2e6e49 100644 --- a/internal/services/api/get_deployment.go +++ b/internal/services/api/get_deployment.go @@ -17,13 +17,18 @@ import ( func GetDeploymentHandlerFunc(base util.AbsolutePath, log logging.Logger) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { name := mux.Vars(req)["name"] - path := deployment.GetDeploymentPath(base, name) + projectDir, relProjectDir, err := ProjectDirFromRequest(base, w, req, log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } + path := deployment.GetDeploymentPath(projectDir, name) d, err := deployment.FromFile(path) if err != nil && errors.Is(err, fs.ErrNotExist) { http.NotFound(w, req) return } - response := deploymentAsDTO(d, err, base, path) + response := deploymentAsDTO(d, err, projectDir, relProjectDir, path) w.Header().Set("content-type", "application/json") json.NewEncoder(w).Encode(response) } diff --git a/internal/services/api/get_deployment_test.go b/internal/services/api/get_deployment_test.go index a92c78ae7..22056a27f 100644 --- a/internal/services/api/get_deployment_test.go +++ b/internal/services/api/get_deployment_test.go @@ -7,6 +7,7 @@ import ( "errors" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/gorilla/mux" @@ -66,6 +67,7 @@ func (s *GetDeploymentSuite) TestGetDeployment() { s.Equal(deploymentStateDeployed, res.State) s.Equal(*d, res.Deployment) s.Equal("myTargetName", res.Name) + s.Equal(".", res.ProjectDir) s.Equal(s.cwd.Join(".posit", "publish", "myConfig.toml").String(), res.ConfigPath) s.Equal(types.ContentID("12345678"), res.Deployment.ID) } @@ -91,6 +93,8 @@ func (s *GetDeploymentSuite) TestGetDeploymentError() { dec.DisallowUnknownFields() s.NoError(dec.Decode(&res)) s.NotNil(res.Error) + s.Equal("myTargetName", res.Name) + s.Equal(".", res.ProjectDir) } func (s *GetDeploymentSuite) TestGetDeploymentNotFound() { @@ -137,4 +141,57 @@ func (s *GetDeploymentSuite) TestGetPreDeployment() { s.Equal("test error", res.Error.Message) s.Equal(deploymentStateNew, res.State) s.Equal("myTargetName", res.Name) + s.Equal(".", res.ProjectDir) +} + +func (s *GetDeploymentSuite) TestGetDeploymentFromSubdir() { + d, err := createSampleDeployment(s.cwd, "myTargetName") + s.NoError(err) + + // Getting deployment from a subdirectory two levels down + base := s.cwd.Dir().Dir() + relProjectDir, err := s.cwd.Rel(base) + s.NoError(err) + + h := GetDeploymentHandlerFunc(base, s.log) + + dirParam := url.QueryEscape(relProjectDir.String()) + rec := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/api/deployments/myTargetName?dir="+dirParam, nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"name": "myTargetName"}) + h(rec, req) + + s.Equal(http.StatusOK, rec.Result().StatusCode) + s.Equal("application/json", rec.Header().Get("content-type")) + + res := fullDeploymentDTO{} + dec := json.NewDecoder(rec.Body) + dec.DisallowUnknownFields() + s.NoError(dec.Decode(&res)) + s.NotNil(res.Deployment) + s.Nil(res.Error) + s.Equal(deploymentStateDeployed, res.State) + s.Equal(*d, res.Deployment) + s.Equal("myTargetName", res.Name) + s.Equal(relProjectDir.String(), res.ProjectDir) + s.Equal(s.cwd.Join(".posit", "publish", "myConfig.toml").String(), res.ConfigPath) + s.Equal(relProjectDir.String(), res.ProjectDir) + s.Equal(types.ContentID("12345678"), res.Deployment.ID) +} + +func (s *GetDeploymentSuite) TestGetDeploymentBadDir() { + // It's a Bad Request to try to get a deployment from a directory outside the project + _, err := createSampleDeployment(s.cwd, "myTargetName") + s.NoError(err) + + h := GetDeploymentHandlerFunc(s.cwd, s.log) + + rec := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/api/deployments/myTargetName?dir=../middleware", nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"id": "myTargetName"}) + h(rec, req) + + s.Equal(http.StatusBadRequest, rec.Result().StatusCode) } diff --git a/internal/services/api/get_deployments.go b/internal/services/api/get_deployments.go index 22143227f..ca1abd3b0 100644 --- a/internal/services/api/get_deployments.go +++ b/internal/services/api/get_deployments.go @@ -11,22 +11,27 @@ import ( "github.com/posit-dev/publisher/internal/util" ) -func readLatestDeploymentFiles(base util.AbsolutePath) ([]any, error) { - paths, err := deployment.ListDeploymentFiles(base) +func readLatestDeploymentFiles(projectDir util.AbsolutePath, relProjectDir util.RelativePath) ([]any, error) { + paths, err := deployment.ListDeploymentFiles(projectDir) if err != nil { return nil, err } response := make([]any, 0, len(paths)) for _, path := range paths { d, err := deployment.FromFile(path) - response = append(response, deploymentAsDTO(d, err, base, path)) + response = append(response, deploymentAsDTO(d, err, projectDir, relProjectDir, path)) } return response, nil } func GetDeploymentsHandlerFunc(base util.AbsolutePath, log logging.Logger) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { - response, err := readLatestDeploymentFiles(base) + projectDir, relProjectDir, err := ProjectDirFromRequest(base, w, req, log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } + response, err := readLatestDeploymentFiles(projectDir, relProjectDir) if err != nil { InternalError(w, req, log, err) return diff --git a/internal/services/api/get_deployments_test.go b/internal/services/api/get_deployments_test.go index eb62c7b7f..c21940d86 100644 --- a/internal/services/api/get_deployments_test.go +++ b/internal/services/api/get_deployments_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "net/url" "testing" "github.com/posit-dev/publisher/internal/deployment" @@ -60,6 +61,9 @@ func (s *GetDeploymentsSuite) TestGetDeployments() { s.Len(res, 1) s.Nil(res[0].Error) + s.Equal("myTargetName", res[0].Name) + s.Equal(".", res[0].ProjectDir) + s.Equal(s.cwd.Join(".posit", "publish", "deployments", "myTargetName.toml").String(), res[0].Path) s.NotNil(res[0].Deployment) s.Equal(*d, res[0].Deployment) s.Equal(s.cwd.Join(".posit", "publish", "myConfig.toml").String(), res[0].ConfigPath) @@ -97,10 +101,66 @@ func (s *GetDeploymentsSuite) TestGetDeploymentsError() { s.Equal(deploymentStateDeployed, res[0].State) s.NotNil(res[0].Deployment) s.Equal("target1", res[0].Name) + s.Equal(".", res[0].ProjectDir) + s.Equal(s.cwd.Join(".posit", "publish", "deployments", "target1.toml").String(), res[0].Path) s.Equal(*d, res[0].Deployment) s.Equal(types.ContentID("12345678"), res[0].Deployment.ID) - s.Equal("target2", res[1].Name) s.NotNil(res[1].Error) + s.Equal("target2", res[1].Name) + s.Equal(".", res[1].ProjectDir) + s.Equal(s.cwd.Join(".posit", "publish", "deployments", "target2.toml").String(), res[1].Path) s.Equal(deploymentStateError, res[1].State) } + +func (s *GetDeploymentsSuite) TestGetDeploymentsFromSubdir() { + d, err := createSampleDeployment(s.cwd, "myTargetName") + s.NoError(err) + + // Getting deployments from a subdirectory two levels down + base := s.cwd.Dir().Dir() + relProjectDir, err := s.cwd.Rel(base) + s.NoError(err) + + h := GetDeploymentsHandlerFunc(base, s.log) + + dirParam := url.QueryEscape(relProjectDir.String()) + rec := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/api/deployments?dir="+dirParam, nil) + s.NoError(err) + h(rec, req) + + s.Equal(http.StatusOK, rec.Result().StatusCode) + s.Equal("application/json", rec.Header().Get("content-type")) + + res := []fullDeploymentDTO{} + dec := json.NewDecoder(rec.Body) + dec.DisallowUnknownFields() + s.NoError(dec.Decode(&res)) + s.Len(res, 1) + + s.Nil(res[0].Error) + s.Equal("myTargetName", res[0].Name) + s.Equal(relProjectDir.String(), res[0].ProjectDir) + s.Equal(s.cwd.Join(".posit", "publish", "deployments", "myTargetName.toml").String(), res[0].Path) + + s.NotNil(res[0].Deployment) + s.Equal(*d, res[0].Deployment) + s.Equal(s.cwd.Join(".posit", "publish", "myConfig.toml").String(), res[0].ConfigPath) + s.Equal(types.ContentID("12345678"), res[0].Deployment.ID) +} + +func (s *GetDeploymentsSuite) TestGetDeploymentsBadDir() { + // It's a Bad Request to try to list deployments from a directory outside the project + _, err := createSampleDeployment(s.cwd, "myTargetName") + s.NoError(err) + + h := GetDeploymentsHandlerFunc(s.cwd, s.log) + + rec := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/api/deployments/?dir=../middleware", nil) + s.NoError(err) + h(rec, req) + + s.Equal(http.StatusBadRequest, rec.Result().StatusCode) +} diff --git a/internal/services/api/patch_deployment.go b/internal/services/api/patch_deployment.go index 768ed21cf..9da20e28f 100644 --- a/internal/services/api/patch_deployment.go +++ b/internal/services/api/patch_deployment.go @@ -24,17 +24,22 @@ func PatchDeploymentHandlerFunc( return func(w http.ResponseWriter, req *http.Request) { name := mux.Vars(req)["name"] + projectDir, relProjectDir, err := ProjectDirFromRequest(base, w, req, log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } dec := json.NewDecoder(req.Body) dec.DisallowUnknownFields() var b PatchDeploymentRequestBody - err := dec.Decode(&b) + err = dec.Decode(&b) if err != nil { BadRequest(w, req, log, err) return } // Deployment must exist - path := deployment.GetDeploymentPath(base, name) + path := deployment.GetDeploymentPath(projectDir, name) exists, err := path.Exists() if err != nil { InternalError(w, req, log, err) @@ -46,7 +51,7 @@ func PatchDeploymentHandlerFunc( } // Config must exist - configPath := config.GetConfigPath(base, b.ConfigName) + configPath := config.GetConfigPath(projectDir, b.ConfigName) exists, err = configPath.Exists() if err != nil { InternalError(w, req, log, err) @@ -72,7 +77,7 @@ func PatchDeploymentHandlerFunc( InternalError(w, req, log, err) return } - response := deploymentAsDTO(d, err, base, path) + response := deploymentAsDTO(d, err, projectDir, relProjectDir, path) w.Header().Set("content-type", "application/json") json.NewEncoder(w).Encode(response) } diff --git a/internal/services/api/patch_deployment_test.go b/internal/services/api/patch_deployment_test.go index 7f900714e..21f9b46e2 100644 --- a/internal/services/api/patch_deployment_test.go +++ b/internal/services/api/patch_deployment_test.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "strings" "testing" @@ -141,3 +142,55 @@ func (s *PatchDeploymentHandlerFuncSuite) TestPatchDeploymentHandlerBadDeploymen handler(rec, req) s.Equal(http.StatusUnprocessableEntity, rec.Result().StatusCode) } + +func (s *PatchDeploymentHandlerFuncSuite) TestPatchDeploymentSubdir() { + log := logging.New() + + // Deployment is in a subdirectory two levels down + base := s.cwd.Dir().Dir() + relProjectDir, err := s.cwd.Rel(base) + s.NoError(err) + + dirParam := url.QueryEscape(relProjectDir.String()) + rec := httptest.NewRecorder() + req, err := http.NewRequest("PATCH", "/api/deployments/myTargetName?dir="+dirParam, nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"name": "myTargetName"}) + + path := deployment.GetDeploymentPath(s.cwd, "myTargetName") + d := deployment.New() + err = d.WriteFile(path) + s.NoError(err) + + cfg := config.New() + err = cfg.WriteFile(config.GetConfigPath(s.cwd, "myConfig")) + s.NoError(err) + + req.Body = io.NopCloser(strings.NewReader(`{"configurationName": "myConfig"}`)) + + handler := PatchDeploymentHandlerFunc(base, log) + handler(rec, req) + + s.Equal(http.StatusOK, rec.Result().StatusCode) + + updated, err := deployment.FromFile(path) + s.NoError(err) + s.Equal("myConfig", updated.ConfigName) +} + +func (s *PatchDeploymentHandlerFuncSuite) TestPatchDeploymentBadDir() { + // It's a Bad Request to try to patch a deployment from a directory outside the project + _, err := createSampleDeployment(s.cwd, "myTargetName") + s.NoError(err) + + rec := httptest.NewRecorder() + req, err := http.NewRequest("PATCH", "/api/deployments/myTargetName?dir=../middleware", nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"id": "myTargetName"}) + req.Body = io.NopCloser(strings.NewReader(`{"configurationName": "myConfig"}`)) + + h := GetDeploymentHandlerFunc(s.cwd, logging.New()) + h(rec, req) + + s.Equal(http.StatusBadRequest, rec.Result().StatusCode) +} diff --git a/internal/services/api/post_deployment.go b/internal/services/api/post_deployment.go index 76bf668cb..c4d0d7d31 100644 --- a/internal/services/api/post_deployment.go +++ b/internal/services/api/post_deployment.go @@ -36,10 +36,15 @@ func PostDeploymentHandlerFunc( return func(w http.ResponseWriter, req *http.Request) { name := mux.Vars(req)["name"] + projectDir, _, err := ProjectDirFromRequest(base, w, req, log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } dec := json.NewDecoder(req.Body) dec.DisallowUnknownFields() var b PostDeploymentRequestBody - err := dec.Decode(&b) + err = dec.Decode(&b) if err != nil { BadRequest(w, req, log, err) return @@ -49,7 +54,7 @@ func PostDeploymentHandlerFunc( InternalError(w, req, log, err) return } - newState, err := stateFactory(base, b.AccountName, b.ConfigName, name, "", accountList) + newState, err := stateFactory(projectDir, b.AccountName, b.ConfigName, name, "", accountList) if err != nil { if errors.Is(err, accounts.ErrAccountNotFound) { NotFound(w, log, err) diff --git a/internal/services/api/post_deployment_test.go b/internal/services/api/post_deployment_test.go index 0414fbf2f..3b77547ed 100644 --- a/internal/services/api/post_deployment_test.go +++ b/internal/services/api/post_deployment_test.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "strings" "testing" @@ -78,6 +79,8 @@ func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFunc() { path util.AbsolutePath, accountName, configName, targetName, saveName string, accountList accounts.AccountList) (*state.State, error) { + + s.Equal(s.cwd, path) s.Equal("myTargetName", targetName) s.Equal("local", accountName) s.Equal("default", configName) @@ -88,7 +91,7 @@ func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFunc() { st.Target = deployment.New() return st, nil } - handler := PostDeploymentHandlerFunc(util.AbsolutePath{}, log, lister, events.NewNullEmitter()) + handler := PostDeploymentHandlerFunc(s.cwd, log, lister, events.NewNullEmitter()) handler(rec, req) s.Equal(http.StatusAccepted, rec.Result().StatusCode) @@ -103,7 +106,7 @@ func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFuncBadJSON() req.Body = io.NopCloser(strings.NewReader(`{"random": "123"}`)) - handler := PostDeploymentHandlerFunc(util.AbsolutePath{}, log, nil, events.NewNullEmitter()) + handler := PostDeploymentHandlerFunc(s.cwd, log, nil, events.NewNullEmitter()) handler(rec, req) s.Equal(http.StatusBadRequest, rec.Result().StatusCode) } @@ -122,7 +125,7 @@ func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFuncStateErr() return nil, errors.New("test error from state factory") } - handler := PostDeploymentHandlerFunc(util.AbsolutePath{}, log, nil, events.NewNullEmitter()) + handler := PostDeploymentHandlerFunc(s.cwd, log, nil, events.NewNullEmitter()) handler(rec, req) s.Equal(http.StatusBadRequest, rec.Result().StatusCode) } @@ -195,10 +198,58 @@ func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentHandlerFuncPublishErr return publisher, nil } - handler := PostDeploymentHandlerFunc(util.AbsolutePath{}, log, lister, events.NewNullEmitter()) + handler := PostDeploymentHandlerFunc(s.cwd, log, lister, events.NewNullEmitter()) handler(rec, req) // Handler returns 202 Accepted even if publishing errs, // because the publish action is asynchronous. s.Equal(http.StatusAccepted, rec.Result().StatusCode) } + +func (s *PostDeploymentHandlerFuncSuite) TestPostDeploymentSubdir() { + log := logging.New() + + // Deployment is in a subdirectory two levels down + base := s.cwd.Dir().Dir() + relProjectDir, err := s.cwd.Rel(base) + s.NoError(err) + + dirParam := url.QueryEscape(relProjectDir.String()) + rec := httptest.NewRecorder() + req, err := http.NewRequest("POST", "/api/deployments/myTargetName?dir="+dirParam, nil) + s.NoError(err) + req = mux.SetURLVars(req, map[string]string{"name": "myTargetName"}) + + lister := &accounts.MockAccountList{} + req.Body = io.NopCloser(strings.NewReader( + `{ + "account": "local", + "config": "default" + }`)) + + publisher := &mockPublisher{} + publisher.On("PublishDirectory", mock.Anything).Return(nil) + publisherFactory = func(*state.State, events.Emitter, logging.Logger) (publish.Publisher, error) { + return publisher, nil + } + stateFactory = func( + path util.AbsolutePath, + accountName, configName, targetName, saveName string, + accountList accounts.AccountList) (*state.State, error) { + + s.Equal(s.cwd, path) + s.Equal("myTargetName", targetName) + s.Equal("local", accountName) + s.Equal("default", configName) + s.Equal("", saveName) + + st := state.Empty() + st.Account = &accounts.Account{} + st.Target = deployment.New() + return st, nil + } + handler := PostDeploymentHandlerFunc(base, log, lister, events.NewNullEmitter()) + handler(rec, req) + + s.Equal(http.StatusAccepted, rec.Result().StatusCode) +} diff --git a/internal/services/api/post_deployments.go b/internal/services/api/post_deployments.go index 763deb142..ff1a81f18 100644 --- a/internal/services/api/post_deployments.go +++ b/internal/services/api/post_deployments.go @@ -27,10 +27,15 @@ func PostDeploymentsHandlerFunc( accountList accounts.AccountList) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { + projectDir, relProjectDir, err := ProjectDirFromRequest(base, w, req, log) + if err != nil { + // Response already returned by ProjectDirFromRequest + return + } dec := json.NewDecoder(req.Body) dec.DisallowUnknownFields() var b PostDeploymentsRequestBody - err := dec.Decode(&b) + err = dec.Decode(&b) if err != nil { BadRequest(w, req, log, err) return @@ -53,7 +58,7 @@ func PostDeploymentsHandlerFunc( } // Deployment must not exist - path := deployment.GetDeploymentPath(base, b.SaveName) + path := deployment.GetDeploymentPath(projectDir, b.SaveName) exists, err := path.Exists() if err != nil { InternalError(w, req, log, err) @@ -66,7 +71,7 @@ func PostDeploymentsHandlerFunc( if b.ConfigName != "" { // Config must exist - configPath := config.GetConfigPath(base, b.ConfigName) + configPath := config.GetConfigPath(projectDir, b.ConfigName) exists, err = configPath.Exists() if err != nil { InternalError(w, req, log, err) @@ -89,7 +94,7 @@ func PostDeploymentsHandlerFunc( InternalError(w, req, log, err) return } - response := deploymentAsDTO(d, err, base, path) + response := deploymentAsDTO(d, err, projectDir, relProjectDir, path) w.Header().Set("content-type", "application/json") json.NewEncoder(w).Encode(response) } diff --git a/internal/services/api/post_deployments_test.go b/internal/services/api/post_deployments_test.go index 7cfd3cd17..a8875b826 100644 --- a/internal/services/api/post_deployments_test.go +++ b/internal/services/api/post_deployments_test.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "path/filepath" "strings" "testing" @@ -77,6 +78,7 @@ func (s *PostDeploymentsSuite) TestPostDeployments() { s.Equal("newDeployment", res.Name) s.Equal("newDeployment", res.SaveName) + s.Equal(".", res.ProjectDir) s.Equal("myConfig", res.ConfigName) s.Equal("myConfig.toml", filepath.Base(res.ConfigPath)) s.Equal(accounts.ServerTypeConnect, res.ServerType) @@ -122,6 +124,7 @@ func (s *PostDeploymentsSuite) TestPostDeploymentsNoConfig() { s.Equal("newDeployment", res.Name) s.Equal("newDeployment", res.SaveName) + s.Equal(".", res.ProjectDir) s.Equal("", res.ConfigName) s.Equal("", res.ConfigPath) s.Equal(accounts.ServerTypeConnect, res.ServerType) @@ -223,3 +226,56 @@ func (s *PostDeploymentsSuite) TestPostDeploymentsConflict() { s.Equal(http.StatusConflict, rec.Result().StatusCode) } + +func (s *PostDeploymentsSuite) TestPostDeploymentsSubdir() { + // Deployment is in a subdirectory two levels down + base := s.cwd.Dir().Dir() + relProjectDir, err := s.cwd.Rel(base) + s.NoError(err) + + lister := &accounts.MockAccountList{} + acct := &accounts.Account{ + Name: "myAccount", + URL: "https://connect.example.com", + ServerType: accounts.ServerTypeConnect, + } + lister.On("GetAccountByName", "myAccount").Return(acct, nil) + + h := PostDeploymentsHandlerFunc(base, logging.New(), lister) + + cfg := config.New() + err = cfg.WriteFile(config.GetConfigPath(s.cwd, "myConfig")) + s.NoError(err) + + dirParam := url.QueryEscape(relProjectDir.String()) + rec := httptest.NewRecorder() + body := strings.NewReader(`{ + "account": "myAccount", + "config": "myConfig", + "saveName": "newDeployment" + }`) + req, err := http.NewRequest("POST", "/api/deployments?dir="+dirParam, body) + s.NoError(err) + h(rec, req) + + s.Equal(http.StatusOK, rec.Result().StatusCode) + s.Equal("application/json", rec.Header().Get("content-type")) + + res := preDeploymentDTO{} + dec := json.NewDecoder(rec.Body) + dec.DisallowUnknownFields() + s.NoError(dec.Decode(&res)) + + actualPath, err := util.NewPath(res.Path, s.cwd.Fs()).Rel(s.cwd) + s.NoError(err) + s.Equal(filepath.Join(".posit", "publish", "deployments", "newDeployment.toml"), actualPath.String()) + + s.Equal("newDeployment", res.Name) + s.Equal("newDeployment", res.SaveName) + s.Equal(relProjectDir.String(), res.ProjectDir) + s.Equal("myConfig", res.ConfigName) + s.Equal("myConfig.toml", filepath.Base(res.ConfigPath)) + s.Equal(accounts.ServerTypeConnect, res.ServerType) + s.Equal(acct.URL, res.ServerURL) + s.Equal(deploymentStateNew, res.State) +} diff --git a/internal/services/api/post_initialize.go b/internal/services/api/post_initialize.go deleted file mode 100644 index 65b1b4929..000000000 --- a/internal/services/api/post_initialize.go +++ /dev/null @@ -1,61 +0,0 @@ -package api - -// Copyright (C) 2023 by Posit Software, PBC. - -import ( - "encoding/json" - "net/http" - - "github.com/posit-dev/publisher/internal/config" - "github.com/posit-dev/publisher/internal/initialize" - "github.com/posit-dev/publisher/internal/logging" - "github.com/posit-dev/publisher/internal/util" -) - -type PostInitializeRequestBody struct { - ConfigName string `json:"configurationName"` -} - -func PostInitializeHandlerFunc(base util.AbsolutePath, log logging.Logger) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - dec := json.NewDecoder(req.Body) - dec.DisallowUnknownFields() - var b PostInitializeRequestBody - err := dec.Decode(&b) - if err != nil { - BadRequest(w, req, log, err) - return - } - if b.ConfigName == "" { - b.ConfigName = config.DefaultConfigName - } - configPath := config.GetConfigPath(base, b.ConfigName) - exists, err := configPath.Exists() - if err != nil { - InternalError(w, req, log, err) - return - } - if exists { - w.WriteHeader(http.StatusConflict) - return - } - cfg, err := initialize.Init(base, b.ConfigName, util.Path{}, util.Path{}, log) - if err != nil { - cfg = config.New() - } - relPath, err := configPath.Rel(base) - if err != nil { - InternalError(w, req, log, err) - return - } - response := configDTO{ - Name: b.ConfigName, - Path: configPath.String(), - RelPath: relPath.String(), - Configuration: cfg, - } - w.Header().Set("content-type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(response) - } -} diff --git a/internal/services/api/post_initialize_test.go b/internal/services/api/post_initialize_test.go deleted file mode 100644 index 70c50e6c5..000000000 --- a/internal/services/api/post_initialize_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package api - -// Copyright (C) 2023 by Posit Software, PBC. - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "path/filepath" - "strings" - "testing" - - "github.com/posit-dev/publisher/internal/config" - "github.com/posit-dev/publisher/internal/initialize" - "github.com/posit-dev/publisher/internal/inspect" - "github.com/posit-dev/publisher/internal/logging" - "github.com/posit-dev/publisher/internal/util" - "github.com/posit-dev/publisher/internal/util/utiltest" - "github.com/spf13/afero" - "github.com/stretchr/testify/suite" -) - -type PostInitializeSuite struct { - utiltest.Suite - log logging.Logger - cwd util.AbsolutePath -} - -func TestPostInitializeSuite(t *testing.T) { - suite.Run(t, new(PostInitializeSuite)) -} - -var expectedPyConfig = &config.Python{ - Version: "3.4.5", - PackageManager: "pip", - PackageFile: "requirements.txt", -} - -func makeMockPythonInspector(util.AbsolutePath, util.Path, logging.Logger) inspect.PythonInspector { - pyInspector := inspect.NewMockPythonInspector() - pyInspector.On("InspectPython").Return(expectedPyConfig, nil) - return pyInspector -} - -func (s *PostInitializeSuite) SetupSuite() { - s.log = logging.New() - initialize.PythonInspectorFactory = makeMockPythonInspector -} - -func (s *PostInitializeSuite) SetupTest() { - fs := afero.NewMemMapFs() - cwd, err := util.Getwd(fs) - s.Nil(err) - s.cwd = cwd - s.cwd.MkdirAll(0700) -} - -func (s *PostInitializeSuite) createAppPy() { - appPath := s.cwd.Join("app.py") - err := appPath.WriteFile([]byte(` - from flask import Flask - app = Flask(__name__) - app.run() - `), 0666) - s.NoError(err) -} - -func (s *PostInitializeSuite) TestPostInitializeDefault() { - s.createAppPy() - h := PostInitializeHandlerFunc(s.cwd, s.log) - - rec := httptest.NewRecorder() - body := strings.NewReader("{}") - req, err := http.NewRequest("POST", "/api/initialize", body) - s.NoError(err) - h(rec, req) - - s.Equal(http.StatusOK, rec.Result().StatusCode) - s.Equal("application/json", rec.Header().Get("content-type")) - - res := configDTO{} - dec := json.NewDecoder(rec.Body) - dec.DisallowUnknownFields() - s.NoError(dec.Decode(&res)) - - relPath := filepath.Join(".posit", "publish", "default.toml") - s.Equal(s.cwd.Join(relPath).String(), res.Path) - s.Equal(relPath, res.RelPath) - - s.Equal("default", res.Name) - s.Equal(config.ContentTypePythonFlask, res.Configuration.Type) - s.Equal(expectedPyConfig, res.Configuration.Python) -} - -func (s *PostInitializeSuite) TestPostInitializeNamed() { - s.createAppPy() - h := PostInitializeHandlerFunc(s.cwd, s.log) - - rec := httptest.NewRecorder() - body := strings.NewReader(`{"configurationName": "newConfig"}`) - req, err := http.NewRequest("POST", "/api/configurations", body) - s.NoError(err) - h(rec, req) - - s.Equal(http.StatusOK, rec.Result().StatusCode) - s.Equal("application/json", rec.Header().Get("content-type")) - - res := configDTO{} - dec := json.NewDecoder(rec.Body) - dec.DisallowUnknownFields() - s.NoError(dec.Decode(&res)) - - relPath := filepath.Join(".posit", "publish", "newConfig.toml") - s.Equal(s.cwd.Join(relPath).String(), res.Path) - s.Equal(relPath, res.RelPath) - - s.Equal("newConfig", res.Name) - s.Equal(config.ContentTypePythonFlask, res.Configuration.Type) - s.Equal(expectedPyConfig, res.Configuration.Python) -} - -func (s *PostInitializeSuite) TestPostInitializeConflict() { - s.TestPostInitializeNamed() - h := PostInitializeHandlerFunc(s.cwd, s.log) - - rec := httptest.NewRecorder() - body := strings.NewReader(`{"configurationName": "newConfig"}`) - req, err := http.NewRequest("POST", "/api/configurations", body) - s.NoError(err) - h(rec, req) - - s.Equal(http.StatusConflict, rec.Result().StatusCode) -} - -func (s *PostInitializeSuite) TestPostInitializeInspectionFails() { - h := PostInitializeHandlerFunc(s.cwd, s.log) - - rec := httptest.NewRecorder() - body := strings.NewReader(`{}`) - req, err := http.NewRequest("POST", "/api/configurations", body) - s.NoError(err) - h(rec, req) - - s.Equal(http.StatusOK, rec.Result().StatusCode) - s.Equal("application/json", rec.Header().Get("content-type")) - - res := configDTO{} - dec := json.NewDecoder(rec.Body) - dec.DisallowUnknownFields() - s.NoError(dec.Decode(&res)) - - relPath := filepath.Join(".posit", "publish", "default.toml") - s.Equal(s.cwd.Join(relPath).String(), res.Path) - s.Equal(relPath, res.RelPath) - - s.Equal("default", res.Name) - expected := config.New() - expected.Title = s.cwd.Base() - - // Assert that there are comments; don't require a specific value. - s.NotEmpty(res.Configuration.Comments) - res.Configuration.Comments = nil - - s.Equal(expected, res.Configuration) - s.Nil(res.Configuration.Python) -}