Compare commits

..

1 commit

Author SHA1 Message Date
github-actions
398c10b83b Automated documentation update 2022-08-17 18:37:37 +00:00
351 changed files with 15097 additions and 15722 deletions

3
.devcontainer.json Normal file
View file

@ -0,0 +1,3 @@
{
"postCreateCommand": "npm install -g @devcontainers/cli"
}

View file

@ -1,17 +0,0 @@
{
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-18",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"postCreateCommand": "npm install -g @devcontainers/cli",
"hostRequirements": {
"cpus": 4
},
"customizations": {
"vscode": {
"extensions": [
"mads-hartmann.bash-ide-vscode"
]
}
}
}

View file

@ -0,0 +1,3 @@
# Devcontainers/action
Temporary copy of [devcontainers/action](https://github.com/devcontainers/action).

32
.github/devcontainers-action/action.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: 'Dev Container'
description: 'Publish custom development container assets'
author: 'GitHub'
branding:
icon: box
color: blue
inputs:
# Global options
publish-features:
required: false
default: "false"
description: "['true'/'false'] Publish dev container 'feature' artifacts"
generate-docs:
required: false
default: "false"
description: "Parse machine-readable (.json) configuration files and commit standardized documentation"
# 'features' options
base-path-to-features:
required: false
default: ''
description: "Relative path to the 'src' folder containing dev container 'feature(s)'"
oci-registry:
required: false
description: "Name of the OCI registry that implements the OCI Artifact Distribution Specification"
features-namespace:
required: false
description: "A unique indentifier for the collection of features"
runs:
using: 'node16'
main: 'dist/index.js'

11354
.github/devcontainers-action/dist/index.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,659 @@
@actions/core
MIT
The MIT License (MIT)
Copyright 2019 GitHub
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.
@actions/exec
MIT
The MIT License (MIT)
Copyright 2019 GitHub
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.
@actions/github
MIT
The MIT License (MIT)
Copyright 2019 GitHub
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.
@actions/http-client
MIT
Actions Http Client for Node.js
Copyright (c) GitHub, Inc.
All rights reserved.
MIT License
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.
@actions/io
MIT
The MIT License (MIT)
Copyright 2019 GitHub
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.
@octokit/auth-token
MIT
The MIT License
Copyright (c) 2019 Octokit 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.
@octokit/core
MIT
The MIT License
Copyright (c) 2019 Octokit 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.
@octokit/endpoint
MIT
The MIT License
Copyright (c) 2018 Octokit 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.
@octokit/graphql
MIT
The MIT License
Copyright (c) 2018 Octokit 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.
@octokit/plugin-paginate-rest
MIT
MIT License Copyright (c) 2019 Octokit 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 (including the next paragraph) 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.
@octokit/plugin-rest-endpoint-methods
MIT
MIT License Copyright (c) 2019 Octokit 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 (including the next paragraph) 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.
@octokit/request
MIT
The MIT License
Copyright (c) 2018 Octokit 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.
@octokit/request-error
MIT
The MIT License
Copyright (c) 2019 Octokit 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.
@vercel/ncc
MIT
Copyright 2018 ZEIT, Inc.
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.
before-after-hook
Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Gregor Martynus and other contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
deprecation
ISC
The ISC License
Copyright (c) Gregor Martynus 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.
is-plain-object
MIT
The MIT License (MIT)
Copyright (c) 2014-2017, 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.
node-fetch
MIT
The MIT License (MIT)
Copyright (c) 2016 David Frank
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.
once
ISC
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.
tr46
MIT
tunnel
MIT
The MIT License (MIT)
Copyright (c) 2012 Koichi Kobayashi
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.
universal-user-agent
ISC
# [ISC License](https://spdx.org/licenses/ISC)
Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m)
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.
uuid
MIT
The MIT License (MIT)
Copyright (c) 2010-2020 Robert Kieffer 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.
webidl-conversions
BSD-2-Clause
# The BSD 2-Clause License
Copyright (c) 2014, Domenic Denicola
All rights reserved.
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.
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.
whatwg-url
MIT
The MIT License (MIT)
Copyright (c) 20152016 Sebastian Mayr
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.
wrappy
ISC
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.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,166 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateTemplateDocumentation = exports.generateFeaturesDocumentation = void 0;
const fs = __importStar(require("fs"));
const core = __importStar(require("@actions/core"));
const path = __importStar(require("path"));
const utils_1 = require("./utils");
const FEATURES_README_TEMPLATE = `
# #{Name}
#{Description}
## Example Usage
\`\`\`json
"features": {
"#{Registry}/#{Namespace}/#{Id}:#{Version}": {
"version": "latest"
}
}
\`\`\`
## Options
#{OptionsTable}
#{Notes}
---
_Note: This file was auto-generated from the [devcontainer-feature.json](#{RepoUrl}). Add additional notes to a \`NOTES.md\`._
`;
const TEMPLATE_README_TEMPLATE = `
# #{Name}
#{Description}
## Options
#{OptionsTable}
`;
function generateFeaturesDocumentation(basePath, ociRegistry, namespace) {
return __awaiter(this, void 0, void 0, function* () {
yield _generateDocumentation(basePath, FEATURES_README_TEMPLATE, 'devcontainer-feature.json', ociRegistry, namespace);
});
}
exports.generateFeaturesDocumentation = generateFeaturesDocumentation;
function generateTemplateDocumentation(basePath) {
return __awaiter(this, void 0, void 0, function* () {
yield _generateDocumentation(basePath, TEMPLATE_README_TEMPLATE, 'devcontainer-template.json');
});
}
exports.generateTemplateDocumentation = generateTemplateDocumentation;
function _generateDocumentation(basePath, readmeTemplate, metadataFile, ociRegistry = '', namespace = '') {
return __awaiter(this, void 0, void 0, function* () {
const directories = fs.readdirSync(basePath);
yield Promise.all(directories.map((f) => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
if (!f.startsWith('.')) {
const readmePath = path.join(basePath, f, 'README.md');
// Reads in feature.json
const jsonPath = path.join(basePath, f, metadataFile);
if (!fs.existsSync(jsonPath)) {
core.error(`${metadataFile} not found at path '${jsonPath}'`);
return;
}
let parsedJson = undefined;
try {
parsedJson = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
}
catch (err) {
core.error(`Failed to parse ${jsonPath}: ${err}`);
return;
}
if (!parsedJson || !(parsedJson === null || parsedJson === void 0 ? void 0 : parsedJson.id)) {
core.error(`${metadataFile} for '${f}' does not contain an 'id'`);
return;
}
const srcInfo = (0, utils_1.getGitHubMetadata)();
// Add version
let version = 'latest';
const parsedVersion = parsedJson === null || parsedJson === void 0 ? void 0 : parsedJson.version;
if (parsedVersion) {
// example - 1.0.0
const splitVersion = parsedVersion.split('.');
version = splitVersion[0];
}
const generateOptionsMarkdown = () => {
const options = parsedJson === null || parsedJson === void 0 ? void 0 : parsedJson.options;
if (!options) {
return '';
}
const keys = Object.keys(options);
const contents = keys
.map(k => {
const val = options[k];
return `| ${k} | ${val.description || '-'} | ${val.type || '-'} | ${val.default || '-'} |`;
})
.join('\n');
return '| Options Id | Description | Type | Default Value |\n' + '|-----|-----|-----|-----|\n' + contents;
};
const generateNotesMarkdown = () => {
const notesPath = path.join(basePath, f, 'NOTES.md');
return fs.existsSync(notesPath) ? fs.readFileSync(path.join(notesPath), 'utf8') : '';
};
let urlToConfig = './devcontainer-feature.json';
const basePathTrimmed = basePath.startsWith('./') ? basePath.substring(2) : basePath;
if (srcInfo.owner && srcInfo.repo) {
urlToConfig = `https://github.com/${srcInfo.owner}/${srcInfo.repo}/blob/main/${basePathTrimmed}/${f}/devcontainer-feature.json`;
}
const newReadme = readmeTemplate
// Templates & Features
.replace('#{Id}', parsedJson.id)
.replace('#{Name}', parsedJson.name ? `${parsedJson.name} (${parsedJson.id})` : `${parsedJson.id}`)
.replace('#{Description}', (_a = parsedJson.description) !== null && _a !== void 0 ? _a : '')
.replace('#{OptionsTable}', generateOptionsMarkdown())
.replace('#{Notes}', generateNotesMarkdown())
// Features Only
.replace('#{Registry}', ociRegistry)
.replace('#{Namespace}', namespace)
.replace('#{Version}', version)
// Templates Only
.replace('#{ManifestName}', (_c = (_b = parsedJson === null || parsedJson === void 0 ? void 0 : parsedJson.image) === null || _b === void 0 ? void 0 : _b.manifest) !== null && _c !== void 0 ? _c : '')
.replace('#{RepoUrl}', urlToConfig);
// Remove previous readme
if (fs.existsSync(readmePath)) {
fs.unlinkSync(readmePath);
}
// Write new readme
fs.writeFileSync(readmePath, newReadme);
}
})));
});
}

View file

@ -0,0 +1,93 @@
"use strict";
/*--------------------------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
*-------------------------------------------------------------------------------------------------------------*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const generateDocs_1 = require("./generateDocs");
const utils_1 = require("./utils");
const exec = __importStar(require("@actions/exec"));
function run() {
return __awaiter(this, void 0, void 0, function* () {
core.debug('Reading input parameters...');
// Read inputs
const shouldPublishFeatures = core.getInput('publish-features').toLowerCase() === 'true';
const shouldGenerateDocumentation = core.getInput('generate-docs').toLowerCase() === 'true';
const featuresBasePath = core.getInput('base-path-to-features');
const sourceMetadata = (0, utils_1.getGitHubMetadata)();
const inputOciRegistry = core.getInput('oci-registry');
const ociRegistry = inputOciRegistry && inputOciRegistry !== '' ? inputOciRegistry : 'ghcr.io';
const inputNamespace = core.getInput('namespace');
const namespace = inputNamespace && inputNamespace !== '' ? inputNamespace : `${sourceMetadata.owner}/${sourceMetadata.repo}`;
const cliDebugMode = core.getInput('devcontainer-cli-debug-mode').toLowerCase() === 'true';
// -- Publish
if (shouldPublishFeatures) {
core.info('Publishing features...');
yield publishFeatures(featuresBasePath, ociRegistry, namespace, cliDebugMode);
}
// -- Generate Documentation
if (shouldGenerateDocumentation && featuresBasePath) {
core.info('Generating documentation for features...');
yield (0, generateDocs_1.generateFeaturesDocumentation)(featuresBasePath, ociRegistry, namespace);
}
});
}
function publishFeatures(basePath, ociRegistry, namespace, cliDebugMode = false) {
return __awaiter(this, void 0, void 0, function* () {
// Ensures we have the devcontainer CLI installed.
if (!(yield (0, utils_1.ensureDevcontainerCliPresent)(cliDebugMode))) {
core.setFailed('Failed to install devcontainer CLI');
return false;
}
try {
let cmd = 'devcontainer';
let args = ['features', 'publish', '-r', ociRegistry, '-n', namespace, basePath];
if (cliDebugMode) {
cmd = 'npx';
args = ['-y', './devcontainer.tgz', ...args];
}
const res = yield exec.getExecOutput(cmd, args, {
ignoreReturnCode: true
});
return res.exitCode === 0;
}
catch (err) {
core.setFailed(err === null || err === void 0 ? void 0 : err.message);
return false;
}
});
}
run();

View file

@ -0,0 +1,107 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureDevcontainerCliPresent = exports.isDevcontainerCliAvailable = exports.getGitHubMetadata = exports.renameLocal = exports.mkdirLocal = exports.writeLocalFile = exports.readLocalFile = void 0;
const github = __importStar(require("@actions/github"));
const fs = __importStar(require("fs"));
const util_1 = require("util");
const core = __importStar(require("@actions/core"));
const exec = __importStar(require("@actions/exec"));
exports.readLocalFile = (0, util_1.promisify)(fs.readFile);
exports.writeLocalFile = (0, util_1.promisify)(fs.writeFile);
exports.mkdirLocal = (0, util_1.promisify)(fs.mkdir);
exports.renameLocal = (0, util_1.promisify)(fs.rename);
function getGitHubMetadata() {
// Insert github repo metadata
const ref = github.context.ref;
let metadata = {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
ref,
sha: github.context.sha
};
// Add tag if parseable
if (ref.includes('refs/tags/')) {
const tag = ref.replace('refs/tags/', '');
metadata = Object.assign(Object.assign({}, metadata), { tag });
}
return metadata;
}
exports.getGitHubMetadata = getGitHubMetadata;
function isDevcontainerCliAvailable(cliDebugMode = false) {
return __awaiter(this, void 0, void 0, function* () {
try {
let cmd = 'devcontainer';
let args = ['--version'];
if (cliDebugMode) {
cmd = 'npx';
args = ['-y', './devcontainer.tgz', ...args];
}
const res = yield exec.getExecOutput(cmd, args, {
ignoreReturnCode: true,
silent: true
});
core.info(`Devcontainer CLI version '${res.stdout}' is installed.`);
return res.exitCode === 0;
}
catch (err) {
return false;
}
});
}
exports.isDevcontainerCliAvailable = isDevcontainerCliAvailable;
function ensureDevcontainerCliPresent(cliDebugMode = false) {
return __awaiter(this, void 0, void 0, function* () {
if (yield isDevcontainerCliAvailable(cliDebugMode)) {
core.info('devcontainer CLI is already installed');
return true;
}
if (cliDebugMode) {
core.error('Cannot remotely fetch CLI in debug mode');
return false;
}
try {
core.info('Fetching the latest @devcontainer/cli...');
const res = yield exec.getExecOutput('npm', ['install', '-g', '@devcontainers/cli'], {
ignoreReturnCode: true,
silent: true
});
return res.exitCode === 0;
}
catch (err) {
return false;
}
});
}
exports.ensureDevcontainerCliPresent = ensureDevcontainerCliPresent;

13
.github/devcontainers-action/sync-action.sh vendored Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash
# Temporary!
pushd /workspaces/features/.github/devcontainers-action
rm ./action.yml
rm -rf ./dist
rm -rf ./lib
cp /home/codespace/action/action.yml ./action.yml
cp -r /home/codespace/action/dist ./dist
cp -r /home/codespace/action/lib ./lib

View file

@ -1,37 +0,0 @@
name: "Stress test - Docker in Docker"
on:
pull_request:
paths:
- 'src/docker-in-docker/**'
workflow_dispatch:
jobs:
test:
strategy:
matrix:
test-pass: [ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50 ]
fail-fast: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli
- name: "Generating tests for 'docker-in-docker' which validates if docker daemon is running"
run: devcontainer features test --skip-scenarios -f docker-in-docker -i mcr.microsoft.com/devcontainers/base:ubuntu .
test-onCreate:
strategy:
matrix:
test-pass: [ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50 ]
fail-fast: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli
- name: "Generating tests for 'docker-in-docker' which validates if docker daemon is available within 'onCreateCommand'"
run: devcontainer features test -f docker-in-docker --skip-autogenerated --filter "docker_with_on_create_command"

View file

@ -1,4 +1,4 @@
name: "CI - Shell Script Linter" name: "(CI) Shell Script Linter"
on: on:
push: push:
branches: branches:
@ -8,8 +8,9 @@ on:
jobs: jobs:
shellchecker: shellchecker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'no-ci') && !contains(github.event.head_commit.message, 'CI ignore')"
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Shell Linter - name: Shell Linter
uses: azohra/shell-linter@v0.6.0 uses: azohra/shell-linter@v0.6.0

View file

@ -1,4 +1,4 @@
name: "Manual - Shell Script Linter" name: "(Manual) Shell Script Linter"
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
@ -15,7 +15,7 @@ jobs:
shellchecker: shellchecker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Shell Linter - name: Shell Linter
uses: azohra/shell-linter@v0.6.0 uses: azohra/shell-linter@v0.6.0

View file

@ -1,19 +1,16 @@
name: "Release dev container features" name: "(Release) Release dev container features (v2)"
on: on:
workflow_dispatch: workflow_dispatch:
push:
branches:
- main
jobs: jobs:
deploy: deploy:
if: ${{ github.ref == 'refs/heads/main' }} if: ${{ github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: "Publish" - name: "Publish features to OCI"
uses: devcontainers/action@v1 uses: ./.github/devcontainers-action # TODO: Once 'devcontainers/action' is published, use that.
with: with:
publish-features: "true" publish-features: "true"
base-path-to-features: "./src" base-path-to-features: "./src"

View file

@ -1,4 +1,4 @@
name: "CI - Test Features" name: "(CI) Test Features"
on: on:
push: push:
branches: branches:
@ -8,6 +8,7 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'no-ci') && !contains(github.event.head_commit.message, 'CI ignore') && !contains(github.event.head_commit.message, 'Automated documentation update')"
continue-on-error: true continue-on-error: true
strategy: strategy:
matrix: matrix:
@ -16,9 +17,8 @@ jobs:
"aws-cli", "aws-cli",
"azure-cli", "azure-cli",
"common-utils", "common-utils",
"conda",
"desktop-lite", "desktop-lite",
"docker-outside-of-docker", "docker-from-docker",
"docker-in-docker", "docker-in-docker",
"dotnet", "dotnet",
"git", "git",
@ -29,7 +29,6 @@ jobs:
"java", "java",
"kubectl-helm-minikube", "kubectl-helm-minikube",
"node", "node",
"nvidia-cuda",
"oryx", "oryx",
"php", "php",
"powershell", "powershell",
@ -38,77 +37,13 @@ jobs:
"rust", "rust",
"sshd", "sshd",
"terraform", "terraform",
"nix",
]
baseImage:
[
"ubuntu:focal",
"ubuntu:jammy",
"debian:11",
"debian:12",
"mcr.microsoft.com/devcontainers/base:ubuntu",
"mcr.microsoft.com/devcontainers/base:debian",
] ]
baseImage: [ "ubuntu:focal", "ubuntu:jammy", "debian:11", "mcr.microsoft.com/vscode/devcontainers/base:ubuntu", "mcr.microsoft.com/vscode/devcontainers/base:debian" ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: "Install latest devcontainer CLI" - name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli run: npm install -g @devcontainers/cli
- name: "Generating tests for '${{ matrix.features }}' against '${{ matrix.baseImage }}'" - name: " Testing '${{ matrix.features }}' against '${{ matrix.baseImage }}'"
run: devcontainer features test --skip-scenarios -f ${{ matrix.features }} -i ${{ matrix.baseImage }} . run: devcontainer features test -f ${{ matrix.features }} -i ${{ matrix.baseImage }} -c `pwd`
test-scenarios:
runs-on: ubuntu-latest
continue-on-error: true
strategy:
matrix:
features: [
"anaconda",
"aws-cli",
"azure-cli",
"common-utils",
"conda",
"desktop-lite",
"docker-outside-of-docker",
"docker-in-docker",
"dotnet",
"git",
"git-lfs",
"github-cli",
"go",
"hugo",
"java",
"kubectl-helm-minikube",
"node",
"nvidia-cuda",
"oryx",
"php",
"powershell",
"python",
"ruby",
"rust",
"sshd",
"terraform",
"nix",
]
steps:
- uses: actions/checkout@v3
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli
- name: "Testing '${{ matrix.features }}' scenarios"
run: devcontainer features test -f ${{ matrix.features }} --skip-autogenerated .
test-global:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli
- name: "Testing global scenarios"
run: devcontainer features test --global-scenarios-only .

View file

@ -1,9 +1,9 @@
name: "Manual - Test Features" name: "(Manual) Test Features"
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
features: features:
description: "List of features to execute tests against" description: "list of features to execute tests against"
required: true required: true
default: "go dotnet" default: "go dotnet"
baseImage: baseImage:
@ -19,10 +19,10 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: "Install latest devcontainer CLI" - name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli run: npm install -g @devcontainers/cli
- name: "Testing '${{ github.event.inputs.features }}' against '${{ github.event.inputs.baseImage }}'" - name: "Testing '${{ github.event.inputs.features }}' against '${{ github.event.inputs.baseImage }}'"
run: devcontainer features test --features ${{ github.event.inputs.features }} --base-image ${{ github.event.inputs.baseImage }} --log-level ${{ github.event.inputs.logLevel }} . run: devcontainer features test --features ${{ github.event.inputs.features }} --base-image ${{ github.event.inputs.baseImage }} --collection-folder `pwd` --log-level ${{ github.event.inputs.logLevel }}

View file

@ -1,10 +1,11 @@
name: "PR - Test Updated Features" name: "(PR) Test Updated Features"
on: on:
pull_request: pull_request:
jobs: jobs:
detect-changes: detect-changes:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'no-ci') && !contains(github.event.head_commit.message, 'CI ignore') && !contains(github.event.head_commit.message, 'Automated documentation update')"
outputs: outputs:
features: ${{ steps.filter.outputs.changes }} features: ${{ steps.filter.outputs.changes }}
steps: steps:
@ -16,9 +17,8 @@ jobs:
aws-cli: ./**/aws-cli/** aws-cli: ./**/aws-cli/**
azure-cli: ./**/azure-cli/** azure-cli: ./**/azure-cli/**
common-utils: ./**/common-utils/** common-utils: ./**/common-utils/**
conda: ./**/conda/**
desktop-lite: ./**/desktop-lite/** desktop-lite: ./**/desktop-lite/**
docker-outside-of-docker: ./**/docker-outside-of-docker/** docker-from-docker: ./**/docker-from-docker/**
docker-in-docker: ./**/docker-in-docker/** docker-in-docker: ./**/docker-in-docker/**
dotnet: ./**/dotnet/** dotnet: ./**/dotnet/**
git: ./**/git/** git: ./**/git/**
@ -29,7 +29,6 @@ jobs:
java: ./**/java/** java: ./**/java/**
kubectl-helm-minikube: ./**/kubectl-helm-minikube/** kubectl-helm-minikube: ./**/kubectl-helm-minikube/**
node: ./**/node/** node: ./**/node/**
nvidia-cuda: ./**/nvidia-cuda/**
oryx: ./**/oryx/** oryx: ./**/oryx/**
php: ./**/php/** php: ./**/php/**
powershell: ./**/powershell/** powershell: ./**/powershell/**
@ -38,7 +37,6 @@ jobs:
rust: ./**/rust/** rust: ./**/rust/**
sshd: ./**/sshd/** sshd: ./**/sshd/**
terraform: ./**/terraform/** terraform: ./**/terraform/**
nix: ./**/nix/**
test: test:
needs: [detect-changes] needs: [detect-changes]
@ -52,31 +50,14 @@ jobs:
"ubuntu:focal", "ubuntu:focal",
"ubuntu:jammy", "ubuntu:jammy",
"debian:11", "debian:11",
"debian:12", "mcr.microsoft.com/vscode/devcontainers/base:ubuntu",
"mcr.microsoft.com/devcontainers/base:ubuntu", "mcr.microsoft.com/vscode/devcontainers/base:debian",
"mcr.microsoft.com/devcontainers/base:debian",
] ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: "Install latest devcontainer CLI" - name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli run: npm install -g @devcontainers/cli
- name: "Generating tests for '${{ matrix.features }}' against '${{ matrix.baseImage }}'" - name: "Testing '${{ matrix.features }}' against '${{ matrix.baseImage }}'"
run: devcontainer features test --skip-scenarios -f ${{ matrix.features }} -i ${{ matrix.baseImage }} . run: devcontainer features test -f ${{ matrix.features }} -i ${{ matrix.baseImage }} -c `pwd`
test-scenarios:
needs: [detect-changes]
runs-on: ubuntu-latest
continue-on-error: true
strategy:
matrix:
features: ${{ fromJSON(needs.detect-changes.outputs.features) }}
steps:
- uses: actions/checkout@v3
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli
- name: "Testing '${{ matrix.features }}' scenarios"
run: devcontainer features test -f ${{ matrix.features }} --skip-autogenerated .

20
.github/workflows/test-scenarios.yaml vendored Normal file
View file

@ -0,0 +1,20 @@
name: "Test Scenarios"
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
jobs:
scenarios:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'no-ci') && !contains(github.event.head_commit.message, 'CI ignore') && !contains(github.event.head_commit.message, 'Automated documentation update')"
steps:
- uses: actions/checkout@v2
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli
- name: "Running predefined test scenarios"
run: devcontainer features test --scenarios `pwd`/test-scenarios -c `pwd`

View file

@ -8,21 +8,20 @@ on:
jobs: jobs:
generate: generate:
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: documentation if: "github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'no-ci') && !contains(github.event.head_commit.message, 'CI ignore')"
if: "github.ref == 'refs/heads/main'"
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Generate Documentation - name: Generate Documentation
uses: devcontainers/action@v1 uses: ./.github/devcontainers-action # devcontainers/action
with: with:
generate-docs: "true" generate-docs: "true"
base-path-to-features: "./src" base-path-to-features: "./src"
- name: Create a PR for Documentation - name: Add and Commit Documentation
id: push_image_info id: push_image_info
env: env:
GITHUB_TOKEN: ${{ secrets.PAT }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
set -e set -e
echo "Start." echo "Start."
@ -38,17 +37,10 @@ jobs:
# Add / update and commit # Add / update and commit
git add */**/README.md git add */**/README.md
git commit -m 'Automated documentation update [skip ci]' || export NO_UPDATES=true git commit -m 'Automated documentation update' || export NO_UPDATES=true
# Push # Push
if [ "$NO_UPDATES" != "true" ] ; then if [ "$NO_UPDATES" != "true" ] ; then
git push origin "$branch" git push origin "$branch"
gh api \ gh pr create --title "$message" --body "$message"
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/${GITHUB_REPOSITORY}/pulls \
-f title="$message" \
-f body="$message" \
-f head="$branch" \
-f base='main'
fi fi

View file

@ -1,52 +0,0 @@
name: "Updates vendor 'dotnet-install' script"
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # Runs every Sunday at midnight UTC (adjust as needed)
jobs:
fetch-latest-dotnet-install:
runs-on: ubuntu-latest
environment: documentation # grants access to secrets.PAT, for creating pull requests
steps:
- uses: actions/checkout@v3
- name: Run fetch-latest-dotnet-install.sh
run: src/dotnet/scripts/fetch-latest-dotnet-install.sh
- name: Create a PR for dotnet-install.sh
id: push_image_info
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
run: |
set -e
echo "Start."
# Configure git and Push updates
git config --global user.email github-actions@github.com
git config --global user.name github-actions
git config pull.rebase false
branch=automated-script-update-$GITHUB_RUN_ID
git checkout -b $branch
message='[Updates] Automated vendor 'dotnet-install' script'
# Add / update and commit
git add src/dotnet/scripts/vendor/dotnet-install.sh
git commit -m 'Automated dotnet-install script update' || export NO_UPDATES=true
# Bump version and push
if [ "$NO_UPDATES" != "true" ] ; then
echo "$(jq --indent 4 '.version = (.version | split(".") | map(tonumber) | .[2] += 1 | join("."))' src/dotnet/devcontainer-feature.json)" > src/dotnet/devcontainer-feature.json
git add src/dotnet/devcontainer-feature.json
git commit -m 'Bump version'
git push origin "$branch"
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/${GITHUB_REPOSITORY}/pulls \
-f title="$message" \
-f body="$message" \
-f head="$branch" \
-f base="$GITHUB_REF_NAME"
fi

View file

@ -1,16 +0,0 @@
name: "Validate devcontainer-feature.json files"
on:
workflow_dispatch:
pull_request:
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Validate devcontainer-feature.json files"
uses: devcontainers/action@v1
with:
validate-only: "true"
base-path-to-features: "./src"

View file

@ -1,60 +0,0 @@
**Thanks for taking the time to contribute! ❤️**
All types of contributions are encouraged and valued, no matter if it's a bug
report 🐛, a feature request 💡, or a Pull Request 🚀.
- **❓ I have a question:** Ask in [our dev container community Slack channel]
- **🐛 I found a bug:** [Open an Issue]
- **💡 I have an idea:** [Open an Issue]
- **💻 I want to code:** See below
If you like the project, but just don't have time to contribute, that's OK too!
You can also star the project ⭐, rave about it online 💬, or add a link to us
🔗 in your project's readme.
⚠️ You must never report security 🔒 related issues, vulnerabilities or bugs
including sensitive information to the issue tracker, or elsewhere in public.
Instead, please [report them to the Microsoft Security Response Center]. You can
read more about our security policy in [`SECURITY.md`].
## Contributing code
👨‍💼 This repository is a collection of dev container Features managed by dev
container spec maintainers. This repository will **only accept improvements and
bug fixes** for the [current set of maintained Features].
🚀 If you're looking to create a new Feature, then we encourage adding it to a
separate repository of your control. The [devcontainers/feature-starter]
repository has more guidance on self authoring Features.
💡 Once you've self authored the Features and if you'd like to share them with
the community, then we'd recommend [adding it to the index].
👷‍ If you've identified an issue and you want to fix it, here's how you can get
started:
1. 🔀 Fork the repo
2. 💻 Open the repo in your editor
3. 👨‍💻 Add your changes to your workspace
4. ✨ [Test your changes using `devcontainer features test`] to make sure
everything still works
5. 🆚 Bump the version of the feature you changed according to [semver]
6. 🔖 Commit & push your changes
7. 🔁 Open a PR to get your changes merged
8. 🚀 Profit!
👩‍⚖️ When contributing code to this project, you may be asked to agree to our
[Contributor License Agreement].
<!-- prettier-ignore-start -->
[our dev container community Slack channel]: https://aka.ms/devcontainer_community
[open an issue]: https://github.com/devcontainers/features/issues/new
[current set of maintained Features]: https://github.com/devcontainers/features/tree/main/src
[devcontainers/feature-starter]: https://github.com/devcontainers/feature-starter#readme
[adding it to the index]: https://github.com/devcontainers/feature-starter#adding-features-to-the-index
[report them to the Microsoft Security Response Center]: https://msrc.microsoft.com/create-report
[`SECURITY.md`]: https://github.com/devcontainers/spec/blob/main/SECURITY.md
[contributor license agreement]: https://opensource.microsoft.com/cla/
[Test your changes using `devcontainer features test`]: https://github.com/devcontainers/cli/blob/main/docs/features/test.md
[semver]: https://semver.org/
<!-- prettier-ignore-end -->

View file

@ -4,28 +4,36 @@
<td style="width: 140px; text-align: center;"><a href="https://github.com/devcontainers"><img width="128px" src="https://raw.githubusercontent.com/microsoft/fluentui-system-icons/78c9587b995299d5bfc007a0077773556ecb0994/assets/Cube/SVG/ic_fluent_cube_32_filled.svg" alt="devcontainers organization logo"/></a></td> <td style="width: 140px; text-align: center;"><a href="https://github.com/devcontainers"><img width="128px" src="https://raw.githubusercontent.com/microsoft/fluentui-system-icons/78c9587b995299d5bfc007a0077773556ecb0994/assets/Cube/SVG/ic_fluent_cube_32_filled.svg" alt="devcontainers organization logo"/></a></td>
<td> <td>
<strong>Development Container 'Features'</strong><br /> <strong>Development Container 'Features'</strong><br />
<i>A set of simple and reusable Features. Quickly add a language/tool/CLI to a development container. <i>A set of simple and reusable 'features'. Quickly add a language/tool/CLI to a development container.
</td> </td>
</tr></table> </tr></table>
'Features' are self-contained units of installation code and development container configuration. Features are designed 'Features' are self-contained units of installation code and development container configuration. Features are designed
to install atop a wide-range of base container images. to install atop a wide-range of base container images (**this repo focuses on `debian` based images**).
Missing a CLI or language in your otherwise _perfect_ container image? Add the relevant Feature to the `features` Missing a CLI or language in your otherwise _perfect_ container image? Add the relevant 'feature' to the `features`
property of a [`devcontainer.json`](https://containers.dev/implementors/json_reference/#general-properties). A property of a [`devcontainer.json`](https://containers.dev/implementors/json_reference/#general-properties). A
[tool supporting the dev container specification](https://containers.dev/supporting) is required to build a development [tool supporting the dev container specification](https://containers.dev/supporting) is required to build a development
container. container.
You may learn about Features at [containers.dev](https://containers.dev/implementors/features/), which is the website for the dev container specification. ⚠️ Development container 'features' are a
[**proposed**](https://github.com/devcontainers/spec/blob/main/proposals/devcontainer-features.md) addition to the
[development container specification](https://containers.dev/implementors/spec/). **Please note that 'features' are in
preview and subject to breaking changes**.
Once the [**proposed**](https://github.com/devcontainers/spec/blob/main/proposals/devcontainer-features.md)
specification is accepted, implementation details will be published at
[https://containers.dev](https://containers.dev/).
## Usage ## Usage
To reference a Feature from this repository, add the desired Features to a `devcontainer.json`. Each Feature has a `README.md` that shows how to reference the Feature and which options are available for that Feature. To reference a feature from this repository, add the desired features to a `devcontainer.json`. Each feature has a `README.md` that shows how to reference the feature and which options are available for that feature.
The example below installs the `go` and `docker-in-docker` declared in the [`./src`](./src) directory of this The example below installs the `go` and `docker-in-docker` declared in the [`./src`](./src) directory of this
repository. repository.
See the relevant Feature's README for supported options. See the relevant feature's README for supported options.
```jsonc ```jsonc
"name": "my-project-devcontainer", "name": "my-project-devcontainer",
@ -43,7 +51,7 @@ See the relevant Feature's README for supported options.
The `:latest` version annotation is added implicitly if omitted. To pin to a specific package version The `:latest` version annotation is added implicitly if omitted. To pin to a specific package version
([example](https://github.com/devcontainers/features/pkgs/container/features/go/versions)), append it to the end of the ([example](https://github.com/devcontainers/features/pkgs/container/features/go/versions)), append it to the end of the
Feature. Features follow semantic versioning conventions, so you can pin to a major version `:1`, minor version `:1.0`, or patch version `:1.0.0` by specifying the appropriate label. feature. Features follow semantic versioning conventions, so you can pin to a major version `:1`, minor version `:1.0`, or patch version `:1.0.0` by specifying the appropriate label.
```jsonc ```jsonc
"features": { "features": {
@ -55,7 +63,7 @@ Feature. Features follow semantic versioning conventions, so you can pin to a ma
The [devcontainer CLI reference implementation](https://github.com/devcontainers/cli) (or a The [devcontainer CLI reference implementation](https://github.com/devcontainers/cli) (or a
[supporting tool](https://containers.dev/supporting)) can be used to build a project's dev container declaring [supporting tool](https://containers.dev/supporting)) can be used to build a project's dev container declaring
Features. 'features'.
```bash ```bash
git clone <my-project-with-devcontainer> git clone <my-project-with-devcontainer>
@ -80,36 +88,40 @@ devcontainer build --workspace-folder <path-to-my-project-with-devcontainer>
├── test ├── test
│ ├── dotnet │ ├── dotnet
│ │ └── test.sh │ │ └── test.sh
│ ├── go │ └── go
| | ├── scenarios.json
| | ├── test_scenario_1.json
│ | └── test.sh │ | └── test.sh
| ├── ... | ├── ...
│ │ └── test.sh │ │ └── test.sh
... ├── test-scenarios
│ ├── install_jupyterlab.sh
│ ├── install_python_twice.sh
| ├── ...
│ └── scenarios.json
``` ```
- [`src`](src) - A collection of subfolders, each declaring a Feature. Each subfolder contains at least a - [`src`](src) - A collection of subfolders, each declaring a feature. Each subfolder contains at least a
`devcontainer-feature.json` and an `install.sh` script. `devcontainer-feature.json` and an `install.sh` script.
- [`test`](test) - Mirroring `src`, a folder-per-feature with at least a `test.sh` script. The - [`test`](test) - Mirroring `src`, a folder-per-feature with at least a `test.sh` script. The
[`devcontainer` CLI](https://github.com/devcontainers/cli) will execute [`devcontainer` CLI](https://github.com/devcontainers/cli) will execute
[these tests in CI](https://github.com/devcontainers/features/blob/main/.github/workflows/test-all.yaml). [these tests in CI](https://github.com/devcontainers/features/blob/main/.github/workflows/test-all.yaml).
- [`test-scenarios`](test-scenarios) - More complex scenarios involving a set of features from this repo. The
[`devcontainer` CLI](https://github.com/devcontainers/cli) will execute
[these tests in CI](https://github.com/devcontainers/features/blob/main/.github/workflows/test-scenarios.yaml).
## Contributions ## Contributions
### Creating your own collection of Features ### Creating your own collection of features
The [Feature distribution specification](https://containers.dev/implementors/features-distribution/) outlines a pattern for community members and organizations to self-author Features in repositories they control. Please see the
[proposed specification](https://github.com/devcontainers/spec/blob/main/proposals/devcontainer-features-distribution.md)
on how to start to author and distribute your own features.
A template repo [`devcontainers/feature-template`](https://github.com/devcontainers/feature-template) and [GitHub Action](https://github.com/devcontainers/action) are available to help bootstrap self-authored Features. We're excited for you to create features! Our team is actively iterating on tools and examples to help members of the
community author their own dev container features. If you have any feedback along the way, please let us know in the
We are eager to hear your feedback on self-authoring! Please provide comments and feedback on [spec issue #70](https://github.com/devcontainers/spec/issues/70). specification repo's issues on [features](https://github.com/devcontainers/spec/issues/61) or
[feature distribution](https://github.com/devcontainers/spec/issues/70).
### Contributing to this repository ### Contributing to this repository
This repository will accept improvement and bug fix contributions related to the This repository will accept improvement and bug fix contributions related to the
[current set of maintained Features](./src). [current set of maintained features](./src).
🤝 You can read more about how to contribute in [`CONTRIBUTING.md`]. ❤️
[`CONTRIBUTING.md`]: CONTRIBUTING.md

View file

@ -11,10 +11,3 @@ As covered in the [user FAQ](https://docs.anaconda.com/anaconda/user-guide/faq)
```bash ```bash
conda install python=3.7 conda install python=3.7
``` ```
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/anaconda:1": {} "ghcr.io/devcontainers/features/anaconda:1": {
"version": "latest"
}
} }
``` ```
@ -32,13 +34,6 @@ conda install python=3.7
``` ```
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/anaconda/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/anaconda/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,8 +1,7 @@
{ {
"id": "anaconda", "id": "anaconda",
"version": "1.0.11", "version": "1.0.0",
"name": "Anaconda", "name": "Anaconda",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/anaconda",
"options": { "options": {
"version": { "version": {
"type": "string", "type": "string",
@ -15,9 +14,6 @@
}, },
"containerEnv": { "containerEnv": {
"CONDA_DIR": "/usr/local/conda", "CONDA_DIR": "/usr/local/conda",
"PATH": "/usr/local/conda/bin:${PATH}" "PATH": "${PATH}:${CONDA_DIR}/bin:"
}, }
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
} }

View file

@ -8,17 +8,14 @@
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The VS Code and Codespaces Teams
VERSION="${VERSION:-"latest"}" VERSION=${VERSION:-"latest"}
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" USERNAME=${USERNAME:-"automatic"}
UPDATE_RC="${UPDATE_RC:-"true"}" UPDATE_RC=${UPDATE_RC:-"true"}
CONDA_DIR="${CONDA_DIR:-"/usr/local/conda"}" CONDA_DIR=${CONDA_DIR:-"/usr/local/conda"}
set -eux set -eux
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
@ -67,10 +64,7 @@ updaterc() {
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then apt-get update
echo "Running apt-get update..."
apt-get update -y
fi
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -89,7 +83,7 @@ if ! conda --version &> /dev/null ; then
chown -R "${USERNAME}:conda" "${CONDA_DIR}" chown -R "${USERNAME}:conda" "${CONDA_DIR}"
chmod -R g+r+w "${CONDA_DIR}" chmod -R g+r+w "${CONDA_DIR}"
find "${CONDA_DIR}" -type d -print0 | xargs -n 1 -0 chmod g+s find "${CONDA_DIR}" -type d | xargs -n 1 chmod g+s
echo "Installing Anaconda..." echo "Installing Anaconda..."
CONDA_VERSION=$VERSION CONDA_VERSION=$VERSION
@ -109,32 +103,4 @@ if ! conda --version &> /dev/null ; then
updaterc "export CONDA_DIR=${CONDA_DIR}/bin" updaterc "export CONDA_DIR=${CONDA_DIR}/bin"
fi fi
# Display a notice on conda when not running in GitHub Codespaces echo "Done!"
mkdir -p /usr/local/etc/vscode-dev-containers
cat << 'EOF' > /usr/local/etc/vscode-dev-containers/conda-notice.txt
When using "conda" from outside of GitHub Codespaces, note the Anaconda repository contains
restrictions on commercial use that may impact certain organizations. See https://aka.ms/ghcs-conda
EOF
notice_script="$(cat << 'EOF'
if [ -t 1 ] && [ "${IGNORE_NOTICE}" != "true" ] && [ "${TERM_PROGRAM}" = "vscode" ] && [ "${CODESPACES}" != "true" ] && [ ! -f "$HOME/.config/vscode-dev-containers/conda-notice-already-displayed" ]; then
cat "/usr/local/etc/vscode-dev-containers/conda-notice.txt"
mkdir -p "$HOME/.config/vscode-dev-containers"
((sleep 10s; touch "$HOME/.config/vscode-dev-containers/conda-notice-already-displayed") &)
fi
EOF
)"
if [ -f "/etc/zsh/zshrc" ]; then
echo "${notice_script}" | tee -a /etc/zsh/zshrc
fi
if [ -f "/etc/bash.bashrc" ]; then
echo "${notice_script}" | tee -a /etc/bash.bashrc
fi
# Clean up
rm -rf /var/lib/apt/lists/*
echo "Done!"

View file

@ -1,7 +0,0 @@
Available versions of the AWS CLI can be found here: https://github.com/aws/aws-cli/blob/v2/CHANGELOG.rst.
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@ Installs the AWS CLI along with needed dependencies. Useful for base Dockerfiles
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/aws-cli:1": {} "ghcr.io/devcontainers/features/aws-cli:1": {
"version": "latest"
}
} }
``` ```
@ -15,21 +17,8 @@ Installs the AWS CLI along with needed dependencies. Useful for base Dockerfiles
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select or enter an AWS CLI version. | string | latest | | version | Select or enter an AWS CLI version. (Available versions here: https://github.com/aws/aws-cli/blob/v2/CHANGELOG.rst) | string | latest |
## Customizations
### VS Code Extensions
- `AmazonWebServices.aws-toolkit-vscode`
Available versions of the AWS CLI can be found here: https://github.com/aws/aws-cli/blob/v2/CHANGELOG.rst.
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---

View file

@ -1,8 +1,7 @@
{ {
"id": "aws-cli", "id": "aws-cli",
"version": "1.0.7", "version": "1.0.0",
"name": "AWS CLI", "name": "AWS CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/aws-cli",
"description": "Installs the AWS CLI along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.", "description": "Installs the AWS CLI along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.",
"options": { "options": {
"version": { "version": {
@ -11,17 +10,10 @@
"latest" "latest"
], ],
"default": "latest", "default": "latest",
"description": "Select or enter an AWS CLI version." "description": "Select or enter an AWS CLI version. (Available versions here: https://github.com/aws/aws-cli/blob/v2/CHANGELOG.rst)"
} }
}, },
"customizations": { "extensions": [
"vscode": { "AmazonWebServices.aws-toolkit-vscode"
"extensions": [
"AmazonWebServices.aws-toolkit-vscode"
]
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
] ]
} }

View file

@ -9,9 +9,6 @@
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
VERSION=${VERSION:-"latest"} VERSION=${VERSION:-"latest"}
AWSCLI_GPG_KEY=FB5DB77FD5C118B80511ADA8A6310ACC4672475C AWSCLI_GPG_KEY=FB5DB77FD5C118B80511ADA8A6310ACC4672475C
@ -50,19 +47,36 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1 exit 1
fi fi
apt_get_update() # Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Function to run apt-get if needed
apt_get_update_if_needed()
{ {
echo "Running apt-get update..." if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
apt-get update -y echo "Running apt-get update..."
apt-get update
else
echo "Skipping apt-get update."
fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then apt_get_update_if_needed
echo "Running apt-get update..."
apt-get update -y
fi
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -74,6 +88,9 @@ check_packages curl ca-certificates gnupg2 dirmngr unzip
verify_aws_cli_gpg_signature() { verify_aws_cli_gpg_signature() {
local filePath=$1 local filePath=$1
local sigFilePath=$2 local sigFilePath=$2
get_common_setting AWSCLI_GPG_KEY
get_common_setting AWSCLI_GPG_KEY_MATERIAL true
local awsGpgKeyring=aws-cli-public-key.gpg local awsGpgKeyring=aws-cli-public-key.gpg
echo "${AWSCLI_GPG_KEY_MATERIAL}" | gpg --dearmor > "./${awsGpgKeyring}" echo "${AWSCLI_GPG_KEY_MATERIAL}" | gpg --dearmor > "./${awsGpgKeyring}"
@ -121,7 +138,4 @@ echo "(*) Installing AWS CLI..."
install install
# Clean up
rm -rf /var/lib/apt/lists/*
echo "Done!" echo "Done!"

View file

@ -1,7 +0,0 @@
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@ Installs the Azure CLI along with needed dependencies. Useful for base Dockerfil
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/azure-cli:1": {} "ghcr.io/devcontainers/features/azure-cli:1": {
"version": "latest"
}
} }
``` ```
@ -16,23 +18,7 @@ Installs the Azure CLI along with needed dependencies. Useful for base Dockerfil
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select or enter an Azure CLI version. (Available versions may vary by Linux distribution.) | string | latest | | version | Select or enter an Azure CLI version. (Available versions may vary by Linux distribution.) | string | latest |
| extensions | Optional comma separated list of Azure CLI extensions to install in profile. | string | - |
| installBicep | Optionally install Azure Bicep | boolean | false |
| installUsingPython | Install Azure CLI using Python instead of pipx | boolean | false |
## Customizations
### VS Code Extensions
- `ms-vscode.azurecli`
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---

View file

@ -1,8 +1,7 @@
{ {
"id": "azure-cli", "id": "azure-cli",
"version": "1.2.2", "version": "1.0.0",
"name": "Azure CLI", "name": "Azure CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/azure-cli",
"description": "Installs the Azure CLI along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.", "description": "Installs the Azure CLI along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.",
"options": { "options": {
"version": { "version": {
@ -12,31 +11,9 @@
], ],
"default": "latest", "default": "latest",
"description": "Select or enter an Azure CLI version. (Available versions may vary by Linux distribution.)" "description": "Select or enter an Azure CLI version. (Available versions may vary by Linux distribution.)"
},
"extensions": {
"type": "string",
"default": "",
"description": "Optional comma separated list of Azure CLI extensions to install in profile."
},
"installBicep": {
"type": "boolean",
"description": "Optionally install Azure Bicep",
"default": false
},
"installUsingPython": {
"type": "boolean",
"description": "Install Azure CLI using Python instead of pipx",
"default": false
} }
}, },
"customizations": { "extensions": [
"vscode": { "ms-vscode.azurecli"
"extensions": [
"ms-vscode.azurecli"
]
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
] ]
} }

View file

@ -9,13 +9,8 @@
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
AZ_VERSION=${VERSION:-"latest"} AZ_VERSION=${VERSION:-"latest"}
AZ_EXTENSIONS=${EXTENSIONS}
AZ_INSTALLBICEP=${INSTALLBICEP:-false}
INSTALL_USING_PYTHON=${INSTALL_USING_PYTHON:-true}
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
AZCLI_ARCHIVE_ARCHITECTURES="amd64" AZCLI_ARCHIVE_ARCHITECTURES="amd64"
AZCLI_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal jammy" AZCLI_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal jammy"
@ -25,30 +20,42 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1 exit 1
fi fi
if [ -z "${_REMOTE_USER}" ]; then # Get central common setting
echo -e 'Feature script must be executed by a tool that implements the dev container specification. See https://containers.dev/ for more information.' get_common_setting() {
exit 1 if [ "${common_settings_file_loaded}" != "true" ]; then
fi curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
echo "Effective REMOTE_USER: ${_REMOTE_USER}" # Function to run apt-get if needed
apt_get_update_if_needed()
apt_get_update()
{ {
echo "Running apt-get update..." if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
apt-get update -y echo "Running apt-get update..."
apt-get update
else
echo "Skipping apt-get update."
fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then apt_get_update_if_needed
echo "Running apt-get update..."
apt-get update -y
fi
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
export DEBIAN_FRONTEND=noninteractive
# Soft version matching that resolves a version for a given package in the *current apt-cache* # Soft version matching that resolves a version for a given package in the *current apt-cache*
# Return value is stored in first argument (the unprocessed version) # Return value is stored in first argument (the unprocessed version)
apt_cache_version_soft_match() { apt_cache_version_soft_match() {
@ -95,6 +102,7 @@ install_using_apt() {
# Install dependencies # Install dependencies
check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr
# Import key safely (new 'signed-by' method rather than deprecated apt-key approach) and install # Import key safely (new 'signed-by' method rather than deprecated apt-key approach) and install
get_common_setting MICROSOFT_GPG_KEYS_URI
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/azure-cli/ ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/azure-cli.list echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/azure-cli/ ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/azure-cli.list
apt-get update apt-get update
@ -116,46 +124,10 @@ install_using_apt() {
fi fi
} }
install_using_pip_strategy() { install_using_pip() {
local ver=""
if [ "${AZ_VERSION}" = "latest" ] || [ "${AZ_VERSION}" = "lts" ] || [ "${AZ_VERSION}" = "stable" ]; then
# Empty, meaning grab the "latest" in the apt repo
ver=""
else
ver="==${AZ_VERSION}"
fi
if [ "${INSTALL_USING_PYTHON}" = "true" ]; then
install_with_complete_python_installation "${ver}" || install_with_pipx "${ver}" || return 1
else
install_with_pipx "${ver}" || install_with_complete_python_installation "${ver}" || return 1
fi
}
install_with_pipx() {
echo "(*) Attempting to install globally with pipx..."
local ver="$1"
export
local
if ! type pipx > /dev/null 2>&1; then
echo "(*) Installing pipx..."
check_packages pipx
pipx ensurepath # Ensures PIPX_BIN_DIR is on the PATH
fi
PIPX_HOME="/usr/local/pipx" \
PIPX_BIN_DIR=/usr/local/bin \
pipx install azure-cli${ver}
echo "(*) Finished installing globally with pipx."
}
install_with_complete_python_installation() {
local ver="$1"
echo "(*) No pre-built binaries available in apt-cache. Installing via pip3." echo "(*) No pre-built binaries available in apt-cache. Installing via pip3."
if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then
apt_get_update apt_get_update_if_needed
apt-get -y install python3-minimal python3-pip libffi-dev python3-venv apt-get -y install python3-minimal python3-pip libffi-dev python3-venv
fi fi
export PIPX_HOME=/usr/local/pipx export PIPX_HOME=/usr/local/pipx
@ -169,20 +141,25 @@ install_with_complete_python_installation() {
pipx_bin=/tmp/pip-tmp/bin/pipx pipx_bin=/tmp/pip-tmp/bin/pipx
fi fi
if [ "${AZ_VERSION}" = "latest" ] || [ "${AZ_VERSION}" = "lts" ] || [ "${AZ_VERSION}" = "stable" ]; then
# Empty, meaning grab the "latest" in the apt repo
ver=""
else
ver="==${AZ_VERSION}"
fi
set +e set +e
${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' -f azure-cli${ver} ${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' -f azure-cli${ver}
# Fail gracefully # Fail gracefully
if [ "$?" != 0 ]; then if [ "$?" != 0 ]; then
echo "Could not install azure-cli${ver} via pip3" echo "Could not install azure-cli${ver} via pip"
rm -rf /tmp/pip-tmp rm -rf /tmp/pip-tmp
return 1 return 1
fi fi
set -e set -e
} }
export DEBIAN_FRONTEND=noninteractive
# See if we're on x86_64 and if so, install via apt-get, otherwise use pip3 # See if we're on x86_64 and if so, install via apt-get, otherwise use pip3
echo "(*) Installing Azure CLI..." echo "(*) Installing Azure CLI..."
. /etc/os-release . /etc/os-release
@ -196,7 +173,7 @@ fi
if [ "${use_pip}" = "true" ]; then if [ "${use_pip}" = "true" ]; then
AZ_VERSION=${CACHED_AZURE_VERSION} AZ_VERSION=${CACHED_AZURE_VERSION}
install_using_pip_strategy install_using_pip
if [ "$?" != 0 ]; then if [ "$?" != 0 ]; then
echo "Please provide a valid version for your distribution ${ID} ${VERSION_CODENAME} (${architecture})." echo "Please provide a valid version for your distribution ${ID} ${VERSION_CODENAME} (${architecture})."
@ -207,42 +184,4 @@ if [ "${use_pip}" = "true" ]; then
fi fi
fi fi
# If Azure CLI extensions are requested, loop through and install echo "Done!"
if [ ${#AZ_EXTENSIONS[@]} -gt 0 ]; then
echo "Installing Azure CLI extensions: ${AZ_EXTENSIONS}"
extensions=(`echo ${AZ_EXTENSIONS} | tr ',' ' '`)
for i in "${extensions[@]}"
do
echo "Installing ${i}"
su ${_REMOTE_USER} -c "az extension add --name ${i} -y" || continue
done
fi
if [ "${AZ_INSTALLBICEP}" = "true" ]; then
# Install dependencies
check_packages apt-transport-https curl
# Properly install Azure Bicep based on current architecture
# The `az bicep install` command installs the linux-x64 binary even on arm64 devcontainers
# The `az bicep install --target-platform` could be a solution; however, linux-arm64 is not an allowed value for this argument yet
# Manually installing Bicep and moving to the appropriate directory where az expects it to be
if [ "${architecture}" = "arm64" ]; then
curl -Lo bicep https://github.com/Azure/bicep/releases/latest/download/bicep-linux-arm64
else
curl -Lo bicep https://github.com/Azure/bicep/releases/latest/download/bicep-linux-x64
fi
chmod +x ./bicep
mv ./bicep /usr/local/bin/bicep
# Add a symlink so bicep can be accessed as a standalone executable or as part of az
mkdir -p ${_REMOTE_USER_HOME}/.azure/bin
chown -hR ${_REMOTE_USER}:${_REMOTE_USER} ${_REMOTE_USER_HOME}/.azure
ln -s /usr/local/bin/bicep ${_REMOTE_USER_HOME}/.azure/bin/bicep
fi
# Clean up
rm -rf /var/lib/apt/lists/*
echo "Done!"

View file

@ -1,26 +1,12 @@
## OS Support ## Speeding up the command prompt in large repositories
This Feature should work on recent versions of Debian/Ubuntu, RedHat Enterprise Linux, Fedora, RockyLinux, and Alpine Linux. This script provides a custom command prompt that includes information about the git repository for the current folder. However, with certain large repositories, this can result in a slow command prompt since the required git status command can be slow. To resolve this, you can update a git setting to remove the git portion of the command prompt.
## Using with dev container images To disable the prompt for the current folder's repository, enter the following in a terminal or add it to your `postCreateCommand` or dotfiles:
This Feature is used in many of the [dev container images](https://github.com/search?q=repo%3Adevcontainers%2Fimages+%22ghcr.io%2Fdevcontainers%2Ffeatures%2Fcommon-utils%22&type=code), as a result
these images have already allocated UID & GID 1000. Attempting to add this Feature with UID 1000 and/or GID 1000 on top of such a dev container image will result in an error when building the dev container.
## Customizing the command prompt
By default, this script provides a custom command prompt that includes information about the git repository for the current folder. However, with certain large repositories, this can result in a slow command prompt due to the performance of needed git operations.
For performance reasons, a "dirty" indicator that tells you whether or not there are uncommitted changes is disabled by default. You can opt to turn this on for smaller repositories by entering the following in a terminal or adding it to your `postCreateCommand`:
```bash ```bash
git config devcontainers-theme.show-dirty 1 git config codespaces-theme.hide-status 1
``` ```
To completely disable the git portion of the prompt for the current folder's repository, you can use this configuration setting instead: This setting will survive a rebuild since it is applied to the repository rather than the container.
```bash
git config devcontainers-theme.hide-status 1
```
For `zsh`, the default theme is a [standard Oh My Zsh! theme](https://ohmyz.sh/). You may pick a different one by modifying the `ZSH_THEME` variable in `~/.zshrc`.

View file

@ -1,5 +1,5 @@
# Common Utilities (common-utils) # Common Debian Utilities (common-utils)
Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-root user. Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-root user.
@ -7,7 +7,9 @@ Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-r
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/common-utils:2": {} "ghcr.io/devcontainers/features/common-utils:1": {
"version": "latest"
}
} }
``` ```
@ -16,41 +18,25 @@ Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-r
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| installZsh | Install ZSH? | boolean | true | | installZsh | Install ZSH? | boolean | true |
| configureZshAsDefaultShell | Change default shell to ZSH? | boolean | false |
| installOhMyZsh | Install Oh My Zsh!? | boolean | true | | installOhMyZsh | Install Oh My Zsh!? | boolean | true |
| installOhMyZshConfig | Allow installing the default dev container .zshrc templates? | boolean | true |
| upgradePackages | Upgrade OS packages? | boolean | true | | upgradePackages | Upgrade OS packages? | boolean | true |
| username | Enter name of a non-root user to configure or none to skip | string | automatic | | username | Enter name of non-root user to configure or none to skip | string | automatic |
| userUid | Enter UID for non-root user | string | automatic | | uid | Enter uid for non-root user | string | automatic |
| userGid | Enter GID for non-root user | string | automatic | | gid | Enter gid for non-root user | string | automatic |
| nonFreePackages | Add packages from non-free Debian repository? (Debian only) | boolean | false | | nonFreePackages | Add packages from non-free Debian repository? | boolean | - |
## OS Support ## Speeding up the command prompt in large repositories
This Feature should work on recent versions of Debian/Ubuntu, RedHat Enterprise Linux, Fedora, RockyLinux, and Alpine Linux. This script provides a custom command prompt that includes information about the git repository for the current folder. However, with certain large repositories, this can result in a slow command prompt since the required git status command can be slow. To resolve this, you can update a git setting to remove the git portion of the command prompt.
## Using with dev container images To disable the prompt for the current folder's repository, enter the following in a terminal or add it to your `postCreateCommand` or dotfiles:
This Feature is used in many of the [dev container images](https://github.com/search?q=repo%3Adevcontainers%2Fimages+%22ghcr.io%2Fdevcontainers%2Ffeatures%2Fcommon-utils%22&type=code), as a result
these images have already allocated UID & GID 1000. Attempting to add this Feature with UID 1000 and/or GID 1000 on top of such a dev container image will result in an error when building the dev container.
## Customizing the command prompt
By default, this script provides a custom command prompt that includes information about the git repository for the current folder. However, with certain large repositories, this can result in a slow command prompt due to the performance of needed git operations.
For performance reasons, a "dirty" indicator that tells you whether or not there are uncommitted changes is disabled by default. You can opt to turn this on for smaller repositories by entering the following in a terminal or adding it to your `postCreateCommand`:
```bash ```bash
git config devcontainers-theme.show-dirty 1 git config codespaces-theme.hide-status 1
``` ```
To completely disable the git portion of the prompt for the current folder's repository, you can use this configuration setting instead: This setting will survive a rebuild since it is applied to the repository rather than the container.
```bash
git config devcontainers-theme.hide-status 1
```
For `zsh`, the default theme is a [standard Oh My Zsh! theme](https://ohmyz.sh/). You may pick a different one by modifying the `ZSH_THEME` variable in `~/.zshrc`.
--- ---

View file

@ -1,16 +0,0 @@
#!/bin/sh
get_in_path_except_current() {
which -a "$1" | grep -A1 "$0" | grep -v "$0"
}
code="$(get_in_path_except_current code)"
if [ -n "$code" ]; then
exec "$code" "$@"
elif [ "$(command -v code-insiders)" ]; then
exec code-insiders "$@"
else
echo "code or code-insiders is not installed" >&2
exit 127
fi

View file

@ -1,35 +0,0 @@
#!/bin/sh
# Load meta.env
if [ -f "/usr/local/etc/vscode-dev-containers/meta.env" ]; then
. /usr/local/etc/vscode-dev-containers/meta.env
fi
if [ -f "/usr/local/etc/dev-containers/meta.env" ]; then
. /usr/local/etc/dev-containers/meta.env
fi
# Minimal output
if [ "$1" = "version" ] || [ "$1" = "image-version" ]; then
echo "${VERSION}"
exit 0
elif [ "$1" = "release" ]; then
echo "${GIT_REPOSITORY_RELEASE}"
exit 0
elif [ "$1" = "content" ] || [ "$1" = "content-url" ] || [ "$1" = "contents" ] || [ "$1" = "contents-url" ]; then
echo "${CONTENTS_URL}"
exit 0
fi
#Full output
echo
echo "Development container image information"
echo
if [ ! -z "${VERSION}" ]; then echo "- Image version: ${VERSION}"; fi
if [ ! -z "${DEFINITION_ID}" ]; then echo "- Definition ID: ${DEFINITION_ID}"; fi
if [ ! -z "${VARIANT}" ]; then echo "- Variant: ${VARIANT}"; fi
if [ ! -z "${GIT_REPOSITORY}" ]; then echo "- Source code repository: ${GIT_REPOSITORY}"; fi
if [ ! -z "${GIT_REPOSITORY_RELEASE}" ]; then echo "- Source code release/branch: ${GIT_REPOSITORY_RELEASE}"; fi
if [ ! -z "${GIT_REPOSITORY_REVISION}" ]; then echo "- Source code revision: ${GIT_REPOSITORY_REVISION}"; fi
if [ ! -z "${BUILD_TIMESTAMP}" ]; then echo "- Timestamp: ${BUILD_TIMESTAMP}"; fi
if [ ! -z "${CONTENTS_URL}" ]; then echo && echo "More info: ${CONTENTS_URL}"; fi
echo

View file

@ -1,7 +0,0 @@
#!/bin/sh
set -e
if [ -d "/run/systemd/system" ]; then
exec /bin/systemctl "$@"
else
echo '\n"systemd" is not running in this container due to its overhead.\nUse the "service" command to start services instead. e.g.: \n\nservice --status-all'
fi

View file

@ -1,8 +1,7 @@
{ {
"id": "common-utils", "id": "common-utils",
"version": "2.3.1", "name": "Common Debian Utilities",
"name": "Common Utilities", "version": "1.0.1",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/common-utils",
"description": "Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-root user.", "description": "Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-root user.",
"options": { "options": {
"installZsh": { "installZsh": {
@ -10,21 +9,11 @@
"default": true, "default": true,
"description": "Install ZSH?" "description": "Install ZSH?"
}, },
"configureZshAsDefaultShell": {
"type": "boolean",
"default": false,
"description": "Change default shell to ZSH?"
},
"installOhMyZsh": { "installOhMyZsh": {
"type": "boolean", "type": "boolean",
"default": true, "default": true,
"description": "Install Oh My Zsh!?" "description": "Install Oh My Zsh!?"
}, },
"installOhMyZshConfig": {
"type": "boolean",
"default": true,
"description": "Allow installing the default dev container .zshrc templates?"
},
"upgradePackages": { "upgradePackages": {
"type": "boolean", "type": "boolean",
"default": true, "default": true,
@ -33,37 +22,39 @@
"username": { "username": {
"type": "string", "type": "string",
"proposals": [ "proposals": [
"devcontainer",
"vscode", "vscode",
"codespace", "codespace",
"none", "none",
"automatic" "automatic"
], ],
"default": "automatic", "default": "automatic",
"description": "Enter name of a non-root user to configure or none to skip" "description": "Enter name of non-root user to configure or none to skip"
}, },
"userUid": { "uid": {
"type": "string", "type": "string",
"proposals": [ "proposals": [
"1001", "1000",
"automatic" "automatic"
], ],
"default": "automatic", "default": "automatic",
"description": "Enter UID for non-root user" "description": "Enter uid for non-root user"
}, },
"userGid": { "gid": {
"type": "string", "type": "string",
"proposals": [ "proposals": [
"1001", "1000",
"automatic" "automatic"
], ],
"default": "automatic", "default": "automatic",
"description": "Enter GID for non-root user" "description": "Enter gid for non-root user"
}, },
"nonFreePackages": { "nonFreePackages": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Add packages from non-free Debian repository? (Debian only)" "description": "Add packages from non-free Debian repository?"
} }
} },
"extensions": [
"ms-dotnettools.csharp"
]
} }

View file

@ -1,36 +1,474 @@
#!/bin/sh #!/usr/bin/env bash
#------------------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://github.com/devcontainers/features/blob/main/LICENSE for license information. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#------------------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------
# #
# Docs: https://github.com/devcontainers/features/tree/main/src/common-utils # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/common.md
# Maintainer: The Dev Container spec maintainers # Maintainer: The VS Code and Codespaces Teams
set -e set -e
INSTALL_ZSH="${INSTALLZSH:-"true"}" INSTALL_ZSH=${INSTALLZSH:-"true"}
CONFIGURE_ZSH_AS_DEFAULT_SHELL="${CONFIGUREZSHASDEFAULTSHELL:-"false"}" INSTALL_OH_MY_ZSH=${INSTALLOHMYZSH:-"true"}
INSTALL_OH_MY_ZSH="${INSTALLOHMYZSH:-"true"}" UPGRADE_PACKAGES=${UPGRADEPACKAGES:-"true"}
INSTALL_OH_MY_ZSH_CONFIG="${INSTALLOHMYZSHCONFIG:-"true"}" USERNAME=${USERNAME:-"automatic"}
UPGRADE_PACKAGES="${UPGRADEPACKAGES:-"true"}" USER_UID=${UID:-"automatic"}
USERNAME="${USERNAME:-"automatic"}" USER_GID=${GID:-"automatic"}
USER_UID="${UID:-"automatic"}" ADD_NON_FREE_PACKAGES=${NONFREEPACKAGES:-"false"}
USER_GID="${GID:-"automatic"}"
ADD_NON_FREE_PACKAGES="${NONFREEPACKAGES:-"false"}" DEV_CONTAINERS_DIR="/usr/local/etc/vscode-dev-containers"
MARKER_FILE="${DEV_CONTAINERS_DIR}/common"
MARKER_FILE="/usr/local/etc/vscode-dev-containers/common"
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
fi fi
# If we're using Alpine, install bash before executing # Ensure that login shells get the correct path if the user updated the PATH using ENV.
. /etc/os-release rm -f /etc/profile.d/00-restore-env.sh
if [ "${ID}" = "alpine" ]; then echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh
apk add --no-cache bash chmod +x /etc/profile.d/00-restore-env.sh
# If in automatic mode, determine if a user already exists, if not use vscode
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
USERNAME=""
POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
if id -u ${CURRENT_USER} > /dev/null 2>&1; then
USERNAME=${CURRENT_USER}
break
fi
done
if [ "${USERNAME}" = "" ]; then
USERNAME=vscode
fi
elif [ "${USERNAME}" = "none" ]; then
USERNAME=root
USER_UID=0
USER_GID=0
fi fi
exec /bin/bash "$(dirname $0)/main.sh" "$@" # Load markers to see which steps have already run
exit $? if [ -f "${MARKER_FILE}" ]; then
echo "Marker file found:"
cat "${MARKER_FILE}"
source "${MARKER_FILE}"
fi
# Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive
# Function to call apt-get if needed
apt_get_update_if_needed()
{
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update
else
echo "Skipping apt-get update."
fi
}
# Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
package_list="apt-utils \
openssh-client \
gnupg2 \
dirmngr \
iproute2 \
procps \
lsof \
htop \
net-tools \
psmisc \
curl \
tree \
wget \
rsync \
ca-certificates \
unzip \
zip \
nano \
vim-tiny \
less \
jq \
lsb-release \
apt-transport-https \
dialog \
libc6 \
libgcc1 \
libkrb5-3 \
libgssapi-krb5-2 \
libicu[0-9][0-9] \
liblttng-ust[0-9] \
libstdc++6 \
zlib1g \
locales \
sudo \
ncdu \
man-db \
strace \
manpages \
manpages-dev \
init-system-helpers"
# Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian
if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then
# Bring in variables from /etc/os-release like VERSION_CODENAME
. /etc/os-release
sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list
sed -i -E "s/deb-src http:\/\/(deb|httredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list
sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list
sed -i -E "s/deb-src http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list
# Handle bullseye location for security https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html
sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list
echo "Running apt-get update..."
apt-get update
package_list="${package_list} manpages-posix manpages-posix-dev"
else
apt_get_update_if_needed
fi
# Install libssl1.1 if available
if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then
package_list="${package_list} libssl1.1"
fi
# Install appropriate version of libssl1.0.x if available
libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '')
if [ "$(echo "$LIlibssl_packageBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then
if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then
# Debian 9
package_list="${package_list} libssl1.0.2"
elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then
# Ubuntu 18.04, 16.04, earlier
package_list="${package_list} libssl1.0.0"
fi
fi
echo "Packages to verify are installed: ${package_list}"
apt-get -y install --no-install-recommends ${package_list} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 )
# Install git if not already installed (may be more recent than distro version)
if ! type git > /dev/null 2>&1; then
apt-get -y install --no-install-recommends git
fi
PACKAGES_ALREADY_INSTALLED="true"
fi
# Get to latest versions of all packages
if [ "${UPGRADE_PACKAGES}" = "true" ]; then
apt_get_update_if_needed
apt-get -y upgrade --no-install-recommends
apt-get autoremove -y
fi
# Ensure at least the en_US.UTF-8 UTF-8 locale is available.
# Common need for both applications and things like the agnoster ZSH theme.
if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
LOCALE_ALREADY_SET="true"
fi
# Create or update a non-root user to match UID/GID.
group_name="${USERNAME}"
if id -u ${USERNAME} > /dev/null 2>&1; then
# User exists, update if needed
if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -g $USERNAME)" ]; then
group_name="$(id -gn $USERNAME)"
groupmod --gid $USER_GID ${group_name}
usermod --gid $USER_GID $USERNAME
fi
if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then
usermod --uid $USER_UID $USERNAME
fi
else
# Create user
if [ "${USER_GID}" = "automatic" ]; then
groupadd $USERNAME
else
groupadd --gid $USER_GID $USERNAME
fi
if [ "${USER_UID}" = "automatic" ]; then
useradd -s /bin/bash --gid $USERNAME -m $USERNAME
else
useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME
fi
fi
# Add add sudo support for non-root user
if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME
chmod 0440 /etc/sudoers.d/$USERNAME
EXISTING_NON_ROOT_USER="${USERNAME}"
fi
# ** Shell customization section **
if [ "${USERNAME}" = "root" ]; then
user_rc_path="/root"
else
user_rc_path="/home/${USERNAME}"
fi
# Restore user .bashrc defaults from skeleton file if it doesn't exist or is empty
if [ ! -f "${user_rc_path}/.bashrc" ] || [ ! -s "${user_rc_path}/.bashrc" ] ; then
cp /etc/skel/.bashrc "${user_rc_path}/.bashrc"
fi
# Restore user .profile defaults from skeleton file if it doesn't exist or is empty
if [ ! -f "${user_rc_path}/.profile" ] || [ ! -s "${user_rc_path}/.profile" ] ; then
cp /etc/skel/.profile "${user_rc_path}/.profile"
fi
# .bashrc/.zshrc snippet
rc_snippet="$(cat << 'EOF'
if [ -z "${USER}" ]; then export USER=$(whoami); fi
if [[ "${PATH}" != *"$HOME/.local/bin"* ]]; then export PATH="${PATH}:$HOME/.local/bin"; fi
# Display optional first run image specific notice if configured and terminal is interactive
if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "codespaces" ]] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then
if [ -f "${DEV_CONTAINERS_DIR}/first-run-notice.txt" ]; then
cat "${DEV_CONTAINERS_DIR}/first-run-notice.txt"
elif [ -f "/workspaces/.codespaces/shared/first-run-notice.txt" ]; then
cat "/workspaces/.codespaces/shared/first-run-notice.txt"
fi
mkdir -p "$HOME/.config/vscode-dev-containers"
# Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it
((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &)
fi
# Set the default git editor if not already set
if [ -z "$(git config --get core.editor)" ] && [ -z "${GIT_EDITOR}" ]; then
if [ "${TERM_PROGRAM}" = "vscode" ]; then
if [[ -n $(command -v code-insiders) && -z $(command -v code) ]]; then
export GIT_EDITOR="code-insiders --wait"
else
export GIT_EDITOR="code --wait"
fi
fi
fi
EOF
)"
# code shim, it fallbacks to code-insiders if code is not available
cat << 'EOF' > /usr/local/bin/code
#!/bin/sh
get_in_path_except_current() {
which -a "$1" | grep -A1 "$0" | grep -v "$0"
}
code="$(get_in_path_except_current code)"
if [ -n "$code" ]; then
exec "$code" "$@"
elif [ "$(command -v code-insiders)" ]; then
exec code-insiders "$@"
else
echo "code or code-insiders is not installed" >&2
exit 127
fi
EOF
chmod +x /usr/local/bin/code
# systemctl shim - tells people to use 'service' if systemd is not running
cat << 'EOF' > /usr/local/bin/systemctl
#!/bin/sh
set -e
if [ -d "/run/systemd/system" ]; then
exec /bin/systemctl/systemctl "$@"
else
echo '\n"systemd" is not running in this container due to its overhead.\nUse the "service" command to start services instead. e.g.: \n\nservice --status-all'
fi
EOF
chmod +x /usr/local/bin/systemctl
# Codespaces bash and OMZ themes - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme
codespaces_bash="$(cat \
<<'EOF'
# Codespaces bash prompt theme
__bash_prompt() {
local userpart='`export XIT=$? \
&& [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \
&& [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`'
local gitbranch='`\
if [ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ]; then \
export BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null); \
if [ "${BRANCH}" != "" ]; then \
echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \
&& if git ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \
echo -n " \[\033[1;33m\]✗"; \
fi \
&& echo -n "\[\033[0;36m\]) "; \
fi; \
fi`'
local lightblue='\[\033[1;34m\]'
local removecolor='\[\033[0m\]'
PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ "
unset -f __bash_prompt
}
__bash_prompt
EOF
)"
codespaces_zsh="$(cat \
<<'EOF'
# Codespaces zsh prompt theme
__zsh_prompt() {
local prompt_username
if [ ! -z "${GITHUB_USER}" ]; then
prompt_username="@${GITHUB_USER}"
else
prompt_username="%n"
fi
PROMPT="%{$fg[green]%}${prompt_username} %(?:%{$reset_color%}➜ :%{$fg_bold[red]%}➜ )" # User/exit code arrow
PROMPT+='%{$fg_bold[blue]%}%(5~|%-1~/…/%3~|%4~)%{$reset_color%} ' # cwd
PROMPT+='$([ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ] && git_prompt_info)' # Git status
PROMPT+='%{$fg[white]%}$ %{$reset_color%}'
unset -f __zsh_prompt
}
ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[cyan]%}(%{$fg_bold[red]%}"
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} "
ZSH_THEME_GIT_PROMPT_DIRTY=" %{$fg_bold[yellow]%}✗%{$fg_bold[cyan]%})"
ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg_bold[cyan]%})"
__zsh_prompt
EOF
)"
# Add RC snippet and custom bash prompt
if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then
echo "${rc_snippet}" >> /etc/bash.bashrc
echo "${codespaces_bash}" >> "${user_rc_path}/.bashrc"
echo 'export PROMPT_DIRTRIM=4' >> "${user_rc_path}/.bashrc"
if [ "${USERNAME}" != "root" ]; then
echo "${codespaces_bash}" >> "/root/.bashrc"
echo 'export PROMPT_DIRTRIM=4' >> "/root/.bashrc"
fi
chown ${USERNAME}:${group_name} "${user_rc_path}/.bashrc"
RC_SNIPPET_ALREADY_ADDED="true"
fi
# Optionally install and configure zsh and Oh My Zsh!
if [ "${INSTALL_ZSH}" = "true" ]; then
if ! type zsh > /dev/null 2>&1; then
apt_get_update_if_needed
apt-get install -y zsh
fi
if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then
echo "${rc_snippet}" >> /etc/zsh/zshrc
ZSH_ALREADY_INSTALLED="true"
fi
# Adapted, simplified inline Oh My Zsh! install steps that adds, defaults to a codespaces theme.
# See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for official script.
oh_my_install_dir="${user_rc_path}/.oh-my-zsh"
if [ ! -d "${oh_my_install_dir}" ] && [ "${INSTALL_OH_MY_ZSH}" = "true" ]; then
template_path="${oh_my_install_dir}/templates/zshrc.zsh-template"
user_rc_file="${user_rc_path}/.zshrc"
umask g-w,o-w
mkdir -p ${oh_my_install_dir}
git clone --depth=1 \
-c core.eol=lf \
-c core.autocrlf=false \
-c fsck.zeroPaddedFilemode=ignore \
-c fetch.fsck.zeroPaddedFilemode=ignore \
-c receive.fsck.zeroPaddedFilemode=ignore \
"https://github.com/ohmyzsh/ohmyzsh" "${oh_my_install_dir}" 2>&1
echo -e "$(cat "${template_path}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${user_rc_file}
sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="codespaces"/g' ${user_rc_file}
mkdir -p ${oh_my_install_dir}/custom/themes
echo "${codespaces_zsh}" > "${oh_my_install_dir}/custom/themes/codespaces.zsh-theme"
# Shrink git while still enabling updates
cd "${oh_my_install_dir}"
git repack -a -d -f --depth=1 --window=1
# Copy to non-root user if one is specified
if [ "${USERNAME}" != "root" ]; then
cp -rf "${user_rc_file}" "${oh_my_install_dir}" /root
chown -R ${USERNAME}:${group_name} "${user_rc_path}"
fi
fi
fi
# Persist image metadata info, script if meta.env found in same directory
meta_info_script="$(cat << 'EOF'
#!/bin/sh
. /usr/local/etc/vscode-dev-containers/meta.env
# Minimal output
if [ "$1" = "version" ] || [ "$1" = "image-version" ]; then
echo "${VERSION}"
exit 0
elif [ "$1" = "release" ]; then
echo "${GIT_REPOSITORY_RELEASE}"
exit 0
elif [ "$1" = "content" ] || [ "$1" = "content-url" ] || [ "$1" = "contents" ] || [ "$1" = "contents-url" ]; then
echo "${CONTENTS_URL}"
exit 0
fi
#Full output
echo
echo "Development container image information"
echo
if [ ! -z "${VERSION}" ]; then echo "- Image version: ${VERSION}"; fi
if [ ! -z "${DEFINITION_ID}" ]; then echo "- Definition ID: ${DEFINITION_ID}"; fi
if [ ! -z "${VARIANT}" ]; then echo "- Variant: ${VARIANT}"; fi
if [ ! -z "${GIT_REPOSITORY}" ]; then echo "- Source code repository: ${GIT_REPOSITORY}"; fi
if [ ! -z "${GIT_REPOSITORY_RELEASE}" ]; then echo "- Source code release/branch: ${GIT_REPOSITORY_RELEASE}"; fi
if [ ! -z "${BUILD_TIMESTAMP}" ]; then echo "- Timestamp: ${BUILD_TIMESTAMP}"; fi
if [ ! -z "${CONTENTS_URL}" ]; then echo && echo "More info: ${CONTENTS_URL}"; fi
echo
EOF
)"
if [ -f "${DEV_CONTAINERS_DIR}/meta.env" ]; then
echo "${meta_info_script}" > /usr/local/bin/devcontainer-info
chmod +x /usr/local/bin/devcontainer-info
fi
if [ ! -d "${DEV_CONTAINERS_DIR}" ]; then
mkdir -p "$(dirname "${MARKER_FILE}")"
fi
# Write marker file
echo -e "\
PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\
LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\
EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\
RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\
ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}"
# Display a notice on conda when not running in GitHub Codespaces
cat << 'EOF' > ${DEV_CONTAINERS_DIR}/conda-notice.txt
When using "conda" from outside of GitHub Codespaces, note the Anaconda repository contains
restrictions on commercial use that may impact certain organizations. See https://aka.ms/ghcs-conda
EOF
notice_script="$(cat << 'EOF'
if [ -t 1 ] && [ "${IGNORE_NOTICE}" != "true" ] && [ "${TERM_PROGRAM}" = "vscode" ] && [ "${CODESPACES}" != "true" ] && [ ! -f "$HOME/.config/vscode-dev-containers/conda-notice-already-displayed" ]; then
cat "${DEV_CONTAINERS_DIR}/conda-notice.txt"
mkdir -p "$HOME/.config/vscode-dev-containers"
((sleep 10s; touch "$HOME/.config/vscode-dev-containers/conda-notice-already-displayed") &)
fi
EOF
)"
echo "${notice_script}" | tee -a /etc/bash.bashrc >> /etc/zsh/zshrc
echo "Done!"

View file

@ -1,573 +0,0 @@
#!/bin/bash
#-------------------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://github.com/devcontainers/features/blob/main/LICENSE for license information.
#-------------------------------------------------------------------------------------------------------------------------
#
# Docs: https://github.com/devcontainers/features/tree/main/src/common-utils
# Maintainer: The Dev Container spec maintainers
set -e
INSTALL_ZSH="${INSTALLZSH:-"true"}"
CONFIGURE_ZSH_AS_DEFAULT_SHELL="${CONFIGUREZSHASDEFAULTSHELL:-"false"}"
INSTALL_OH_MY_ZSH="${INSTALLOHMYZSH:-"true"}"
INSTALL_OH_MY_ZSH_CONFIG="${INSTALLOHMYZSHCONFIG:-"true"}"
UPGRADE_PACKAGES="${UPGRADEPACKAGES:-"true"}"
USERNAME="${USERNAME:-"automatic"}"
USER_UID="${USERUID:-"automatic"}"
USER_GID="${USERGID:-"automatic"}"
ADD_NON_FREE_PACKAGES="${NONFREEPACKAGES:-"false"}"
MARKER_FILE="/usr/local/etc/vscode-dev-containers/common"
FEATURE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Debian / Ubuntu packages
install_debian_packages() {
# Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive
local package_list=""
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
package_list="${package_list} \
apt-utils \
openssh-client \
gnupg2 \
dirmngr \
iproute2 \
procps \
lsof \
htop \
net-tools \
psmisc \
curl \
tree \
wget \
rsync \
ca-certificates \
unzip \
bzip2 \
zip \
nano \
vim-tiny \
less \
jq \
lsb-release \
apt-transport-https \
dialog \
libc6 \
libgcc1 \
libkrb5-3 \
libgssapi-krb5-2 \
libicu[0-9][0-9] \
liblttng-ust[0-9] \
libstdc++6 \
zlib1g \
locales \
sudo \
ncdu \
man-db \
strace \
manpages \
manpages-dev \
init-system-helpers"
# Include libssl1.1 if available
if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then
package_list="${package_list} libssl1.1"
fi
# Include libssl3 if available
if [[ ! -z $(apt-cache --names-only search ^libssl3$) ]]; then
package_list="${package_list} libssl3"
fi
# Include appropriate version of libssl1.0.x if available
local libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '')
if [ "$(echo "$libssl_package" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then
if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then
# Debian 9
package_list="${package_list} libssl1.0.2"
elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then
# Ubuntu 18.04
package_list="${package_list} libssl1.0.0"
fi
fi
# Include git if not already installed (may be more recent than distro version)
if ! type git > /dev/null 2>&1; then
package_list="${package_list} git"
fi
fi
# Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian
if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then
# Bring in variables from /etc/os-release like VERSION_CODENAME
sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list
sed -i -E "s/deb-src http:\/\/(deb|httredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list
sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list
sed -i -E "s/deb-src http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list
# Handle bullseye location for security https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html
sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list
sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list
echo "Running apt-get update..."
package_list="${package_list} manpages-posix manpages-posix-dev"
fi
# Install the list of packages
echo "Packages to verify are installed: ${package_list}"
rm -rf /var/lib/apt/lists/*
apt-get update -y
apt-get -y install --no-install-recommends ${package_list} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 )
# Install zsh (and recommended packages) if needed
if [ "${INSTALL_ZSH}" = "true" ] && ! type zsh > /dev/null 2>&1; then
apt-get install -y zsh
fi
# Get to latest versions of all packages
if [ "${UPGRADE_PACKAGES}" = "true" ]; then
apt-get -y upgrade --no-install-recommends
apt-get autoremove -y
fi
# Ensure at least the en_US.UTF-8 UTF-8 locale is available = common need for both applications and things like the agnoster ZSH theme.
if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
LOCALE_ALREADY_SET="true"
fi
PACKAGES_ALREADY_INSTALLED="true"
# Clean up
apt-get -y clean
rm -rf /var/lib/apt/lists/*
}
# RedHat / RockyLinux / CentOS / Fedora packages
install_redhat_packages() {
local package_list=""
local remove_epel="false"
local install_cmd=dnf
if ! type dnf > /dev/null 2>&1; then
install_cmd=yum
fi
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
package_list="${package_list} \
gawk \
openssh-clients \
gnupg2 \
iproute \
procps \
lsof \
net-tools \
psmisc \
wget \
ca-certificates \
rsync \
unzip \
zip \
nano \
vim-minimal \
less \
jq \
openssl-libs \
krb5-libs \
libicu \
zlib \
sudo \
sed \
grep \
which \
man-db \
strace"
# rockylinux:9 installs 'curl-minimal' which clashes with 'curl'
# Install 'curl' for every OS except this rockylinux:9
if [[ "${ID}" = "rocky" ]] && [[ "${VERSION}" != *"9."* ]]; then
package_list="${package_list} curl"
fi
# Install OpenSSL 1.0 compat if needed
if ${install_cmd} -q list compat-openssl10 >/dev/null 2>&1; then
package_list="${package_list} compat-openssl10"
fi
# Install lsb_release if available
if ${install_cmd} -q list redhat-lsb-core >/dev/null 2>&1; then
package_list="${package_list} redhat-lsb-core"
fi
# Install git if not already installed (may be more recent than distro version)
if ! type git > /dev/null 2>&1; then
package_list="${package_list} git"
fi
# Install EPEL repository if needed (required to install 'jq' for CentOS)
if ! ${install_cmd} -q list jq >/dev/null 2>&1; then
${install_cmd} -y install epel-release
remove_epel="true"
fi
fi
# Install zsh if needed
if [ "${INSTALL_ZSH}" = "true" ] && ! type zsh > /dev/null 2>&1; then
package_list="${package_list} zsh"
fi
if [ -n "${package_list}" ]; then
${install_cmd} -y install ${package_list}
fi
# Get to latest versions of all packages
if [ "${UPGRADE_PACKAGES}" = "true" ]; then
${install_cmd} upgrade -y
fi
if [[ "${remove_epel}" = "true" ]]; then
${install_cmd} -y remove epel-release
fi
PACKAGES_ALREADY_INSTALLED="true"
}
# Alpine Linux packages
install_alpine_packages() {
apk update
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
apk add --no-cache \
openssh-client \
gnupg \
procps \
lsof \
htop \
net-tools \
psmisc \
curl \
wget \
rsync \
ca-certificates \
unzip \
zip \
nano \
vim \
less \
jq \
libgcc \
libstdc++ \
krb5-libs \
libintl \
libssl1.1 \
lttng-ust \
tzdata \
userspace-rcu \
zlib \
sudo \
coreutils \
sed \
grep \
which \
ncdu \
shadow \
strace
# Install man pages - package name varies between 3.12 and earlier versions
if apk info man > /dev/null 2>&1; then
apk add --no-cache man man-pages
else
apk add --no-cache mandoc man-pages
fi
# Install git if not already installed (may be more recent than distro version)
if ! type git > /dev/null 2>&1; then
apk add --no-cache git
fi
fi
# Install zsh if needed
if [ "${INSTALL_ZSH}" = "true" ] && ! type zsh > /dev/null 2>&1; then
apk add --no-cache zsh
fi
PACKAGES_ALREADY_INSTALLED="true"
}
# ******************
# ** Main section **
# ******************
if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi
# Load markers to see which steps have already run
if [ -f "${MARKER_FILE}" ]; then
echo "Marker file found:"
cat "${MARKER_FILE}"
source "${MARKER_FILE}"
fi
# Ensure that login shells get the correct path if the user updated the PATH using ENV.
rm -f /etc/profile.d/00-restore-env.sh
echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh
chmod +x /etc/profile.d/00-restore-env.sh
# Bring in ID, ID_LIKE, VERSION_ID, VERSION_CODENAME
. /etc/os-release
# Get an adjusted ID independent of distro variants
if [ "${ID}" = "debian" ] || [ "${ID_LIKE}" = "debian" ]; then
ADJUSTED_ID="debian"
elif [[ "${ID}" = "rhel" || "${ID}" = "fedora" || "${ID}" = "mariner" || "${ID_LIKE}" = *"rhel"* || "${ID_LIKE}" = *"fedora"* || "${ID_LIKE}" = *"mariner"* ]]; then
ADJUSTED_ID="rhel"
elif [ "${ID}" = "alpine" ]; then
ADJUSTED_ID="alpine"
else
echo "Linux distro ${ID} not supported."
exit 1
fi
# Install packages for appropriate OS
case "${ADJUSTED_ID}" in
"debian")
install_debian_packages
;;
"rhel")
install_redhat_packages
;;
"alpine")
install_alpine_packages
;;
esac
# If in automatic mode, determine if a user already exists, if not use vscode
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
if [ "${_REMOTE_USER}" != "root" ]; then
USERNAME="${_REMOTE_USER}"
else
USERNAME=""
POSSIBLE_USERS=("devcontainer" "vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
if id -u ${CURRENT_USER} > /dev/null 2>&1; then
USERNAME=${CURRENT_USER}
break
fi
done
if [ "${USERNAME}" = "" ]; then
USERNAME=vscode
fi
fi
elif [ "${USERNAME}" = "none" ]; then
USERNAME=root
USER_UID=0
USER_GID=0
fi
# Create or update a non-root user to match UID/GID.
group_name="${USERNAME}"
if id -u ${USERNAME} > /dev/null 2>&1; then
# User exists, update if needed
if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -g $USERNAME)" ]; then
group_name="$(id -gn $USERNAME)"
groupmod --gid $USER_GID ${group_name}
usermod --gid $USER_GID $USERNAME
fi
if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then
usermod --uid $USER_UID $USERNAME
fi
else
# Create user
if [ "${USER_GID}" = "automatic" ]; then
groupadd $USERNAME
else
groupadd --gid $USER_GID $USERNAME
fi
if [ "${USER_UID}" = "automatic" ]; then
useradd -s /bin/bash --gid $USERNAME -m $USERNAME
else
useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME
fi
fi
# Add add sudo support for non-root user
if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME
chmod 0440 /etc/sudoers.d/$USERNAME
EXISTING_NON_ROOT_USER="${USERNAME}"
fi
# *********************************
# ** Shell customization section **
# *********************************
if [ "${USERNAME}" = "root" ]; then
user_home="/root"
# Check if user already has a home directory other than /home/${USERNAME}
elif [ "/home/${USERNAME}" != $( getent passwd $USERNAME | cut -d: -f6 ) ]; then
user_home=$( getent passwd $USERNAME | cut -d: -f6 )
else
user_home="/home/${USERNAME}"
if [ ! -d "${user_home}" ]; then
mkdir -p "${user_home}"
chown ${USERNAME}:${group_name} "${user_home}"
fi
fi
# Restore user .bashrc / .profile / .zshrc defaults from skeleton file if it doesn't exist or is empty
possible_rc_files=( ".bashrc" ".profile" )
[ "$INSTALL_OH_MY_ZSH_CONFIG" == "true" ] && possible_rc_files+=('.zshrc')
[ "$INSTALL_ZSH" == "true" ] && possible_rc_files+=('.zprofile')
for rc_file in "${possible_rc_files[@]}"; do
if [ -f "/etc/skel/${rc_file}" ]; then
if [ ! -e "${user_home}/${rc_file}" ] || [ ! -s "${user_home}/${rc_file}" ]; then
cp "/etc/skel/${rc_file}" "${user_home}/${rc_file}"
chown ${USERNAME}:${group_name} "${user_home}/${rc_file}"
fi
fi
done
# Add RC snippet and custom bash prompt
if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then
case "${ADJUSTED_ID}" in
"debian")
global_rc_path="/etc/bash.bashrc"
;;
"rhel")
global_rc_path="/etc/bashrc"
;;
"alpine")
global_rc_path="/etc/bash/bashrc"
# /etc/bash/bashrc does not exist in alpine 3.14 & 3.15
mkdir -p /etc/bash
;;
esac
cat "${FEATURE_DIR}/scripts/rc_snippet.sh" >> ${global_rc_path}
cat "${FEATURE_DIR}/scripts/bash_theme_snippet.sh" >> "${user_home}/.bashrc"
if [ "${USERNAME}" != "root" ]; then
cat "${FEATURE_DIR}/scripts/bash_theme_snippet.sh" >> "/root/.bashrc"
chown ${USERNAME}:${group_name} "${user_home}/.bashrc"
fi
RC_SNIPPET_ALREADY_ADDED="true"
fi
# Optionally configure zsh and Oh My Zsh!
if [ "${INSTALL_ZSH}" = "true" ]; then
if [ ! -f "${user_home}/.zprofile" ]; then
touch "${user_home}/.zprofile"
echo 'source $HOME/.profile' >> "${user_home}/.zprofile" # TODO: Reconsider adding '.profile' to '.zprofile'
chown ${USERNAME}:${group_name} "${user_home}/.zprofile"
fi
if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then
if [ "${ADJUSTED_ID}" = "rhel" ]; then
global_rc_path="/etc/zshrc"
else
global_rc_path="/etc/zsh/zshrc"
fi
cat "${FEATURE_DIR}/scripts/rc_snippet.sh" >> ${global_rc_path}
ZSH_ALREADY_INSTALLED="true"
fi
if [ "${CONFIGURE_ZSH_AS_DEFAULT_SHELL}" == "true" ]; then
# Fixing chsh always asking for a password on alpine linux
# ref: https://askubuntu.com/questions/812420/chsh-always-asking-a-password-and-get-pam-authentication-failure.
if [ ! -f "/etc/pam.d/chsh" ] || ! grep -Eq '^auth(.*)pam_rootok\.so$' /etc/pam.d/chsh; then
echo "auth sufficient pam_rootok.so" >> /etc/pam.d/chsh
elif [[ -n "$(awk '/^auth(.*)pam_rootok\.so$/ && !/^auth[[:blank:]]+sufficient[[:blank:]]+pam_rootok\.so$/' /etc/pam.d/chsh)" ]]; then
awk '/^auth(.*)pam_rootok\.so$/ { $2 = "sufficient" } { print }' /etc/pam.d/chsh > /tmp/chsh.tmp && mv /tmp/chsh.tmp /etc/pam.d/chsh
fi
chsh --shell /bin/zsh ${USERNAME}
fi
# Adapted, simplified inline Oh My Zsh! install steps that adds, defaults to a codespaces theme.
# See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for official script.
if [ "${INSTALL_OH_MY_ZSH}" = "true" ]; then
user_rc_file="${user_home}/.zshrc"
oh_my_install_dir="${user_home}/.oh-my-zsh"
template_path="${oh_my_install_dir}/templates/zshrc.zsh-template"
if [ ! -d "${oh_my_install_dir}" ]; then
umask g-w,o-w
mkdir -p ${oh_my_install_dir}
git clone --depth=1 \
-c core.eol=lf \
-c core.autocrlf=false \
-c fsck.zeroPaddedFilemode=ignore \
-c fetch.fsck.zeroPaddedFilemode=ignore \
-c receive.fsck.zeroPaddedFilemode=ignore \
"https://github.com/ohmyzsh/ohmyzsh" "${oh_my_install_dir}" 2>&1
# Shrink git while still enabling updates
cd "${oh_my_install_dir}"
git repack -a -d -f --depth=1 --window=1
fi
# Add Dev Containers theme
mkdir -p ${oh_my_install_dir}/custom/themes
cp -f "${FEATURE_DIR}/scripts/devcontainers.zsh-theme" "${oh_my_install_dir}/custom/themes/devcontainers.zsh-theme"
ln -sf "${oh_my_install_dir}/custom/themes/devcontainers.zsh-theme" "${oh_my_install_dir}/custom/themes/codespaces.zsh-theme"
# Add devcontainer .zshrc template
if [ "$INSTALL_OH_MY_ZSH_CONFIG" = "true" ]; then
echo -e "$(cat "${template_path}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${user_rc_file}
sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="devcontainers"/g' ${user_rc_file}
fi
# Copy to non-root user if one is specified
if [ "${USERNAME}" != "root" ]; then
copy_to_user_files=("${oh_my_install_dir}")
[ -f "$user_rc_file" ] && copy_to_user_files+=("$user_rc_file")
cp -rf "${copy_to_user_files[@]}" /root
chown -R ${USERNAME}:${group_name} "${copy_to_user_files[@]}"
fi
fi
fi
# *********************************
# ** Ensure config directory **
# *********************************
user_config_dir="${user_home}/.config"
if [ ! -d "${user_config_dir}" ]; then
mkdir -p "${user_config_dir}"
chown ${USERNAME}:${group_name} "${user_config_dir}"
fi
# ****************************
# ** Utilities and commands **
# ****************************
# code shim, it fallbacks to code-insiders if code is not available
cp -f "${FEATURE_DIR}/bin/code" /usr/local/bin/
chmod +rx /usr/local/bin/code
# systemctl shim for Debian/Ubuntu - tells people to use 'service' if systemd is not running
if [ "${ADJUSTED_ID}" = "debian" ]; then
cp -f "${FEATURE_DIR}/bin/systemctl" /usr/local/bin/systemctl
chmod +rx /usr/local/bin/systemctl
fi
# Persist image metadata info, script if meta.env found in same directory
if [ -f "/usr/local/etc/vscode-dev-containers/meta.env" ] || [ -f "/usr/local/etc/dev-containers/meta.env" ]; then
cp -f "${FEATURE_DIR}/bin/devcontainer-info" /usr/local/bin/devcontainer-info
chmod +rx /usr/local/bin/devcontainer-info
fi
# Write marker file
if [ ! -d "/usr/local/etc/vscode-dev-containers" ]; then
mkdir -p "$(dirname "${MARKER_FILE}")"
fi
echo -e "\
PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\
LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\
EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\
RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\
ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}"
echo "Done!"

View file

@ -1,25 +0,0 @@
# bash theme - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme
__bash_prompt() {
local userpart='`export XIT=$? \
&& [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \
&& [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`'
local gitbranch='`\
if [ "$(git config --get devcontainers-theme.hide-status 2>/dev/null)" != 1 ] && [ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ]; then \
export BRANCH=$(git --no-optional-locks symbolic-ref --short HEAD 2>/dev/null || git --no-optional-locks rev-parse --short HEAD 2>/dev/null); \
if [ "${BRANCH}" != "" ]; then \
echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \
&& if [ "$(git config --get devcontainers-theme.show-dirty 2>/dev/null)" = 1 ] && \
git --no-optional-locks ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \
echo -n " \[\033[1;33m\]✗"; \
fi \
&& echo -n "\[\033[0;36m\]) "; \
fi; \
fi`'
local lightblue='\[\033[1;34m\]'
local removecolor='\[\033[0m\]'
PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ "
unset -f __bash_prompt
}
__bash_prompt
export PROMPT_DIRTRIM=4

View file

@ -1,26 +0,0 @@
# Oh My Zsh! theme - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme
__zsh_prompt() {
local prompt_username
if [ ! -z "${GITHUB_USER}" ]; then
prompt_username="@${GITHUB_USER}"
else
prompt_username="%n"
fi
PROMPT="%{$fg[green]%}${prompt_username} %(?:%{$reset_color%}➜ :%{$fg_bold[red]%}➜ )" # User/exit code arrow
PROMPT+='%{$fg_bold[blue]%}%(5~|%-1~/…/%3~|%4~)%{$reset_color%} ' # cwd
PROMPT+='`\
if [ "$(git config --get devcontainers-theme.hide-status 2>/dev/null)" != 1 ] && [ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ]; then \
export BRANCH=$(git --no-optional-locks symbolic-ref --short HEAD 2>/dev/null || git --no-optional-locks rev-parse --short HEAD 2>/dev/null); \
if [ "${BRANCH}" != "" ]; then \
echo -n "%{$fg_bold[cyan]%}(%{$fg_bold[red]%}${BRANCH}" \
&& if [ "$(git config --get devcontainers-theme.show-dirty 2>/dev/null)" = 1 ] && \
git --no-optional-locks ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \
echo -n " %{$fg_bold[yellow]%}✗"; \
fi \
&& echo -n "%{$fg_bold[cyan]%})%{$reset_color%} "; \
fi; \
fi`'
PROMPT+='%{$fg[white]%}$ %{$reset_color%}'
unset -f __zsh_prompt
}
__zsh_prompt

View file

@ -1,26 +0,0 @@
if [ -z "${USER}" ]; then export USER=$(whoami); fi
if [[ "${PATH}" != *"$HOME/.local/bin"* ]]; then export PATH="${PATH}:$HOME/.local/bin"; fi
# Display optional first run image specific notice if configured and terminal is interactive
if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "codespaces" ]] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then
if [ -f "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" ]; then
cat "/usr/local/etc/vscode-dev-containers/first-run-notice.txt"
elif [ -f "/workspaces/.codespaces/shared/first-run-notice.txt" ]; then
cat "/workspaces/.codespaces/shared/first-run-notice.txt"
fi
mkdir -p "$HOME/.config/vscode-dev-containers"
# Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it
((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &)
fi
# Set the default git editor if not already set
if [ -z "$(git config --get core.editor)" ] && [ -z "${GIT_EDITOR}" ]; then
if [ "${TERM_PROGRAM}" = "vscode" ]; then
if [[ -n $(command -v code-insiders) && -z $(command -v code) ]]; then
export GIT_EDITOR="code-insiders --wait"
else
export GIT_EDITOR="code --wait"
fi
fi
fi

View file

@ -1,20 +0,0 @@
## Using Conda
This Feature includes [the `conda` package manager](https://docs.conda.io/projects/conda/en/latest/index.html) which is a part of the [Anaconda Distribution](https://repo.anaconda.com). Additional packages installed using Conda will be downloaded from Anaconda or another repository if you configure one. To reconfigure Conda in this container to access an alternative repository, please see information on [configuring Conda channels here](https://docs.conda.io/projects/conda/en/latest/user-guide/concepts/channels.html ).
Access to the Anaconda repository is covered by the [Anaconda Terms of Service](https://legal.anaconda.com/policies/en/?name=terms-of-service), which may require some organizations to obtain a commercial license from Anaconda. **However**, when used with GitHub Codespaces or GitHub Actions, **all users are permitted** to use the Anaconda Repository through the service, including organizations normally required by Anaconda to obtain a paid license for commercial activities. Note that third-party packages may be licensed by their publishers in ways that impact your intellectual property, and are used at your own risk.
## Installing a different version of Python
As covered in the [user FAQ](https://docs.anaconda.com/anaconda/user-guide/faq) for Anaconda, you can install different versions of Python than the one in this image by running the following from a terminal:
```bash
conda install python=3.7
```
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -1,45 +0,0 @@
# Conda (conda)
A cross-platform, language-agnostic binary package manager
## Example Usage
```json
"features": {
"ghcr.io/devcontainers/features/conda:1": {}
}
```
## Options
| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| version | Select or enter a conda version. | string | latest |
| addCondaForge | Add conda-forge channel to the config? | boolean | false |
## Using Conda
This Feature includes [the `conda` package manager](https://docs.conda.io/projects/conda/en/latest/index.html) which is a part of the [Anaconda Distribution](https://repo.anaconda.com). Additional packages installed using Conda will be downloaded from Anaconda or another repository if you configure one. To reconfigure Conda in this container to access an alternative repository, please see information on [configuring Conda channels here](https://docs.conda.io/projects/conda/en/latest/user-guide/concepts/channels.html ).
Access to the Anaconda repository is covered by the [Anaconda Terms of Service](https://legal.anaconda.com/policies/en/?name=terms-of-service), which may require some organizations to obtain a commercial license from Anaconda. **However**, when used with GitHub Codespaces or GitHub Actions, **all users are permitted** to use the Anaconda Repository through the service, including organizations normally required by Anaconda to obtain a paid license for commercial activities. Note that third-party packages may be licensed by their publishers in ways that impact your intellectual property, and are used at your own risk.
## Installing a different version of Python
As covered in the [user FAQ](https://docs.anaconda.com/anaconda/user-guide/faq) for Anaconda, you can install different versions of Python than the one in this image by running the following from a terminal:
```bash
conda install python=3.7
```
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/conda/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,32 +0,0 @@
{
"id": "conda",
"version": "1.0.9",
"name": "Conda",
"description": "A cross-platform, language-agnostic binary package manager",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/conda",
"options": {
"version": {
"type": "string",
"proposals": [
"latest",
"4.11.0",
"4.12.0"
],
"default": "latest",
"description": "Select or enter a conda version."
},
"addCondaForge": {
"type": "boolean",
"default": false,
"description": "Add conda-forge channel to the config?"
}
},
"containerEnv": {
"CONDA_DIR": "/opt/conda",
"CONDA_SCRIPT":"/opt/conda/etc/profile.d/conda.sh",
"PATH": "/opt/conda/bin:${PATH}"
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
}

View file

@ -1,154 +0,0 @@
#!/usr/bin/env bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
VERSION=${VERSION:-"latest"}
ADD_CONDA_FORGE=$ADDCONDAFORGE
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
UPDATE_RC="true"
CONDA_DIR="/opt/conda"
set -eux
export DEBIAN_FRONTEND=noninteractive
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi
# Ensure that login shells get the correct path if the user updated the PATH using ENV.
rm -f /etc/profile.d/00-restore-env.sh
echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh
chmod +x /etc/profile.d/00-restore-env.sh
# Determine the appropriate non-root user
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
USERNAME=""
POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
if id -u "${CURRENT_USER}" > /dev/null 2>&1; then
USERNAME="${CURRENT_USER}"
break
fi
done
if [ "${USERNAME}" = "" ]; then
USERNAME=root
fi
elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root
fi
architecture="$(uname -m)"
if [ "${architecture}" != "x86_64" ]; then
echo "(!) Architecture $architecture unsupported"
exit 1
fi
# Checks if packages are installed and installs them if not
check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update -y
fi
apt-get -y install --no-install-recommends "$@"
fi
}
sudo_if() {
COMMAND="$*"
if [ "$(id -u)" -eq 0 ] && [ "$USERNAME" != "root" ]; then
su - "$USERNAME" -c "$COMMAND"
else
$COMMAND
fi
}
install_user_package() {
PACKAGE="$1"
sudo_if "${CONDA_DIR}/bin/python3" -m pip install --user --upgrade "$PACKAGE"
}
# Install Conda if it's missing
if ! conda --version &> /dev/null ; then
if ! cat /etc/group | grep -e "^conda:" > /dev/null 2>&1; then
groupadd -r conda
fi
usermod -a -G conda "${USERNAME}"
# Install dependencies
check_packages curl ca-certificates gnupg2
echo "Installing Conda..."
curl -sS https://repo.anaconda.com/pkgs/misc/gpgkeys/anaconda.asc | gpg --dearmor > /usr/share/keyrings/conda-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/conda-archive-keyring.gpg] https://repo.anaconda.com/pkgs/misc/debrepo/conda stable main" > /etc/apt/sources.list.d/conda.list
apt-get update -y
CONDA_PKG="conda=${VERSION}-0"
if [ "${VERSION}" = "latest" ]; then
CONDA_PKG="conda"
fi
check_packages $CONDA_PKG
CONDA_SCRIPT="/opt/conda/etc/profile.d/conda.sh"
. $CONDA_SCRIPT
if [ "${ADD_CONDA_FORGE}" = "true" ]; then
conda config --add channels conda-forge
fi
conda config --set channel_priority strict
conda config --set env_prompt '({name})'
echo "source ${CONDA_SCRIPT}" >> ~/.bashrc
chown -R "${USERNAME}:conda" "${CONDA_DIR}"
chmod -R g+r+w "${CONDA_DIR}"
find "${CONDA_DIR}" -type d -print0 | xargs -n 1 -0 chmod g+s
# Temporary fixes
# Due to https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-23491
install_user_package certifi
# Due to https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-0286 and https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-23931
install_user_package cryptography
# Due to https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-40897
install_user_package setuptools
fi
# Display a notice on conda when not running in GitHub Codespaces
mkdir -p /usr/local/etc/vscode-dev-containers
cat << 'EOF' > /usr/local/etc/vscode-dev-containers/conda-notice.txt
When using "conda" from outside of GitHub Codespaces, note the Anaconda repository contains
restrictions on commercial use that may impact certain organizations. See https://aka.ms/ghcs-conda
EOF
notice_script="$(cat << 'EOF'
if [ -t 1 ] && [ "${IGNORE_NOTICE}" != "true" ] && [ "${TERM_PROGRAM}" = "vscode" ] && [ "${CODESPACES}" != "true" ] && [ ! -f "$HOME/.config/vscode-dev-containers/conda-notice-already-displayed" ]; then
cat "/usr/local/etc/vscode-dev-containers/conda-notice.txt"
mkdir -p "$HOME/.config/vscode-dev-containers"
((sleep 10s; touch "$HOME/.config/vscode-dev-containers/conda-notice-already-displayed") &)
fi
EOF
)"
if [ -f "/etc/zsh/zshrc" ]; then
echo "${notice_script}" | tee -a /etc/zsh/zshrc
fi
if [ -f "/etc/bash.bashrc" ]; then
echo "${notice_script}" | tee -a /etc/bash.bashrc
fi
# Clean up
rm -rf /var/lib/apt/lists/*
echo "Done!"

View file

@ -6,16 +6,6 @@ This feature provides two ways of connecting to the desktop environment it adds.
1. Open the ports view in your tool, select the noVNC port, and click the Globe icon. 1. Open the ports view in your tool, select the noVNC port, and click the Globe icon.
1. In the browser that appears, click the **Connect** button and enter the desktop password (`vscode` by default). 1. In the browser that appears, click the **Connect** button and enter the desktop password (`vscode` by default).
To set up the `6080` port from your `devcontainer.json` file, include the following:
```json
"forwardPorts": [6080],
"portsAttributes": {
"6080": {
"label": "desktop"
}
}
```
You can also connect to the desktop using a [VNC viewer](https://www.realvnc.com/en/connect/download/viewer/). To do so: You can also connect to the desktop using a [VNC viewer](https://www.realvnc.com/en/connect/download/viewer/). To do so:
1. Connect to the environment from a desktop tool that supports the dev container spec (e.g., VS Code client). 1. Connect to the environment from a desktop tool that supports the dev container spec (e.g., VS Code client).
@ -25,7 +15,7 @@ You can also connect to the desktop using a [VNC viewer](https://www.realvnc.com
## Customizing Fluxbox ## Customizing Fluxbox
The window manager installed is [Fluxbox](http://fluxbox.org/). **Right-click** to see the application menu. In addition, any UI-based commands you execute inside the dev container will automatically appear on the desktop. The window manager is installed is [Fluxbox](http://fluxbox.org/). **Right-click** to see the application menu. In addition, any UI-based commands you execute inside the dev container will automatically appear on the desktop.
You can customize the desktop using Fluxbox configuration files. The configuration files are located in the `.fluxbox` folder of the home directory of the user you using to connect to the dev container (`$HOME/.fluxbox`). You can customize the desktop using Fluxbox configuration files. The configuration files are located in the `.fluxbox` folder of the home directory of the user you using to connect to the dev container (`$HOME/.fluxbox`).
@ -37,19 +27,19 @@ See the [Fluxbox menu documentation](http://www.fluxbox.org/help/man-fluxbox-men
If you run into applications crashing, you may need to increase the size of the shared memory space allocated to your container. For example, this will bump it up to 1 GB in `devcontainer.json`: If you run into applications crashing, you may need to increase the size of the shared memory space allocated to your container. For example, this will bump it up to 1 GB in `devcontainer.json`:
```json ```json
"runArgs": ["--shm-size=1g"] "runArgs": ["--shm-size=1g"]
``` ```
Or using Docker Compose: Or using Docker Compose:
```yaml ```yaml
services: services:
your-service-here: your-service-here:
# ... # ...
shm_size: '1gb' shm_size: '1gb'
# ... # ...
``` ```
## Installing a browser ## Installing a browser
@ -69,13 +59,6 @@ If you want the full version of **Google Chrome** in the desktop:
&& apt-get -y install /tmp/chrome.deb && apt-get -y install /tmp/chrome.deb
``` ```
2. Chrome sandbox support requires you set up and run as a non-root user. The [`common-utils`](https://github.com/devcontainers/features/tree/main/src/common-utils) script can do this for you, or you [set one up yourself](https://aka.ms/vscode-remote/containers/non-root). Alternatively, you can start Chrome using `google-chrome --no-sandbox` 2. Chrome sandbox support requires you set up and run as a non-root user. The [`debian-common.sh`](common.md) script can do this for you, or you [set one up yourself](https://aka.ms/vscode-remote/containers/non-root). Alternatively, you can start Chrome using `google-chrome --no-sandbox`
That's it! That's it!
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@ Adds a lightweight Fluxbox based desktop to the container that can be accessed u
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/desktop-lite:1": {} "ghcr.io/devcontainers/features/desktop-lite:1": {
"version": "latest"
}
} }
``` ```
@ -29,16 +31,6 @@ This feature provides two ways of connecting to the desktop environment it adds.
1. Open the ports view in your tool, select the noVNC port, and click the Globe icon. 1. Open the ports view in your tool, select the noVNC port, and click the Globe icon.
1. In the browser that appears, click the **Connect** button and enter the desktop password (`vscode` by default). 1. In the browser that appears, click the **Connect** button and enter the desktop password (`vscode` by default).
To set up the `6080` port from your `devcontainer.json` file, include the following:
```json
"forwardPorts": [6080],
"portsAttributes": {
"6080": {
"label": "desktop"
}
}
```
You can also connect to the desktop using a [VNC viewer](https://www.realvnc.com/en/connect/download/viewer/). To do so: You can also connect to the desktop using a [VNC viewer](https://www.realvnc.com/en/connect/download/viewer/). To do so:
1. Connect to the environment from a desktop tool that supports the dev container spec (e.g., VS Code client). 1. Connect to the environment from a desktop tool that supports the dev container spec (e.g., VS Code client).
@ -48,7 +40,7 @@ You can also connect to the desktop using a [VNC viewer](https://www.realvnc.com
## Customizing Fluxbox ## Customizing Fluxbox
The window manager installed is [Fluxbox](http://fluxbox.org/). **Right-click** to see the application menu. In addition, any UI-based commands you execute inside the dev container will automatically appear on the desktop. The window manager is installed is [Fluxbox](http://fluxbox.org/). **Right-click** to see the application menu. In addition, any UI-based commands you execute inside the dev container will automatically appear on the desktop.
You can customize the desktop using Fluxbox configuration files. The configuration files are located in the `.fluxbox` folder of the home directory of the user you using to connect to the dev container (`$HOME/.fluxbox`). You can customize the desktop using Fluxbox configuration files. The configuration files are located in the `.fluxbox` folder of the home directory of the user you using to connect to the dev container (`$HOME/.fluxbox`).
@ -60,19 +52,19 @@ See the [Fluxbox menu documentation](http://www.fluxbox.org/help/man-fluxbox-men
If you run into applications crashing, you may need to increase the size of the shared memory space allocated to your container. For example, this will bump it up to 1 GB in `devcontainer.json`: If you run into applications crashing, you may need to increase the size of the shared memory space allocated to your container. For example, this will bump it up to 1 GB in `devcontainer.json`:
```json ```json
"runArgs": ["--shm-size=1g"] "runArgs": ["--shm-size=1g"]
``` ```
Or using Docker Compose: Or using Docker Compose:
```yaml ```yaml
services: services:
your-service-here: your-service-here:
# ... # ...
shm_size: '1gb' shm_size: '1gb'
# ... # ...
``` ```
## Installing a browser ## Installing a browser
@ -92,18 +84,11 @@ If you want the full version of **Google Chrome** in the desktop:
&& apt-get -y install /tmp/chrome.deb && apt-get -y install /tmp/chrome.deb
``` ```
2. Chrome sandbox support requires you set up and run as a non-root user. The [`common-utils`](https://github.com/devcontainers/features/tree/main/src/common-utils) script can do this for you, or you [set one up yourself](https://aka.ms/vscode-remote/containers/non-root). Alternatively, you can start Chrome using `google-chrome --no-sandbox` 2. Chrome sandbox support requires you set up and run as a non-root user. The [`debian-common.sh`](common.md) script can do this for you, or you [set one up yourself](https://aka.ms/vscode-remote/containers/non-root). Alternatively, you can start Chrome using `google-chrome --no-sandbox`
That's it! That's it!
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/desktop-lite/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/desktop-lite/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,8 +1,7 @@
{ {
"id": "desktop-lite", "id": "desktop-lite",
"version": "1.0.8", "version": "1.0.1",
"name": "Light-weight Desktop", "name": "Light-weight Desktop",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/desktop-lite",
"description": "Adds a lightweight Fluxbox based desktop to the container that can be accessed using a VNC viewer or the web. GUI-based commands executed from the built-in VS code terminal will open on the desktop automatically.", "description": "Adds a lightweight Fluxbox based desktop to the container that can be accessed using a VNC viewer or the web. GUI-based commands executed from the built-in VS code terminal will open on the desktop automatically.",
"options": { "options": {
"version": { "version": {
@ -52,8 +51,5 @@
"entrypoint": "/usr/local/share/desktop-init.sh", "entrypoint": "/usr/local/share/desktop-init.sh",
"containerEnv": { "containerEnv": {
"DISPLAY": ":1" "DISPLAY": ":1"
}, }
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
} }

View file

@ -7,13 +7,14 @@
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/desktop-lite.md # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/desktop-lite.md
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The VS Code and Codespaces Teams
NOVNC_VERSION="${NOVNCVERSION:-"1.2.0"}" # TODO: Add in a 'latest' auto-detect and swap name to 'version' NOVNC_VERSION=${NOVNCVERSION:-"1.2.0"} # TODO: Add in a 'latest' auto-detect and swap name to 'version'
VNC_PASSWORD=${PASSWORD:-"vscode"} VNC_PASSWORD=${PASSWORD:-"vscode"}
NOVNC_PORT="${WEBPORT:-6080}" NOVNC_PORT="${WEBPORT:-6080}"
VNC_PORT="${VNCPORT:-5901}" VNC_PORT="${VNCPORT:-5901}"
INSTALL_NOVNC="${INSTALL_NOVNC:-"true"}" INSTALL_NOVNC=${INSTALL_NOVNC:-"true"}
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" USERNAME=${USERNAME:-"automatic"}
WEBSOCKETIFY_VERSION=0.10.0 WEBSOCKETIFY_VERSION=0.10.0
@ -62,9 +63,6 @@ package_list_additional="
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
@ -151,18 +149,22 @@ copy_fluxbox_config() {
fi fi
} }
apt_get_update()
# Function to run apt-get if needed
apt_get_update_if_needed()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..." echo "Running apt-get update..."
apt-get update -y apt-get update
else
echo "Skipping apt-get update."
fi fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -174,13 +176,13 @@ check_packages() {
# Ensure apt is in non-interactive to avoid prompts # Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt_get_update apt_get_update_if_needed
# On older Ubuntu, Tilix is in a PPA. on Debian stretch its in backports. # On older Ubuntu, Tilix is in a PPA. on Debian strech its in backports.
if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then
. /etc/os-release . /etc/os-release
if [ "${ID}" = "ubuntu" ]; then if [ "${ID}" = "ubuntu" ]; then
check_packages apt-transport-https software-properties-common apt-get install -y --no-install-recommends apt-transport-https software-properties-common
add-apt-repository -y ppa:webupd8team/terminix add-apt-repository -y ppa:webupd8team/terminix
elif [ "${VERSION_CODENAME}" = "stretch" ]; then elif [ "${VERSION_CODENAME}" = "stretch" ]; then
echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/stretch-backports.list echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/stretch-backports.list
@ -236,7 +238,9 @@ if [ "${INSTALL_NOVNC}" = "true" ] && [ ! -d "/usr/local/novnc" ]; then
rm -f /tmp/websockify-install.zip /tmp/novnc-install.zip rm -f /tmp/websockify-install.zip /tmp/novnc-install.zip
# Install noVNC dependencies and use them. # Install noVNC dependencies and use them.
check_packages python3-minimal python3-numpy if ! dpkg -s python3-minimal python3-numpy > /dev/null 2>&1; then
apt-get -y install --no-install-recommends python3-minimal python3-numpy
fi
sed -i -E 's/^python /python3 /' /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION}/run sed -i -E 's/^python /python3 /' /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION}/run
fi fi
@ -299,9 +303,9 @@ startInBackgroundIfNotRunning()
{ {
log "Starting \$1." log "Starting \$1."
echo -e "\n** \$(date) **" | sudoIf tee -a /tmp/\$1.log > /dev/null echo -e "\n** \$(date) **" | sudoIf tee -a /tmp/\$1.log > /dev/null
if ! pgrep -x \$1 > /dev/null; then if ! pidof \$1 > /dev/null; then
keepRunningInBackground "\$@" keepRunningInBackground "\$@"
while ! pgrep -x \$1 > /dev/null; do while ! pidof \$1 > /dev/null; do
sleep 1 sleep 1
done done
log "\$1 started." log "\$1 started."
@ -347,16 +351,16 @@ log "** SCRIPT START **"
# Start dbus. # Start dbus.
log 'Running "/etc/init.d/dbus start".' log 'Running "/etc/init.d/dbus start".'
if [ -f "/var/run/dbus/pid" ] && ! pgrep -x dbus-daemon > /dev/null; then if [ -f "/var/run/dbus/pid" ] && ! pidof dbus-daemon > /dev/null; then
sudoIf rm -f /var/run/dbus/pid sudoIf rm -f /var/run/dbus/pid
fi fi
sudoIf /etc/init.d/dbus start 2>&1 | sudoIf tee -a /tmp/dbus-daemon-system.log > /dev/null sudoIf /etc/init.d/dbus start 2>&1 | sudoIf tee -a /tmp/dbus-daemon-system.log > /dev/null
while ! pgrep -x dbus-daemon > /dev/null; do while ! pidof dbus-daemon > /dev/null; do
sleep 1 sleep 1
done done
# Startup tigervnc server and fluxbox # Startup tigervnc server and fluxbox
sudoIf rm -rf /tmp/.X11-unix /tmp/.X*-lock sudo rm -rf /tmp/.X11-unix /tmp/.X*-lock
mkdir -p /tmp/.X11-unix mkdir -p /tmp/.X11-unix
sudoIf chmod 1777 /tmp/.X11-unix sudoIf chmod 1777 /tmp/.X11-unix
sudoIf chown root:\${group_name} /tmp/.X11-unix sudoIf chown root:\${group_name} /tmp/.X11-unix
@ -365,7 +369,7 @@ screen_geometry="\${VNC_RESOLUTION%*x*}"
screen_depth="\${VNC_RESOLUTION##*x}" screen_depth="\${VNC_RESOLUTION##*x}"
startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "tigervncserver \${DISPLAY} -geometry \${screen_geometry} -depth \${screen_depth} -rfbport ${VNC_PORT} -dpi \${VNC_DPI:-96} -localhost -desktop fluxbox -fg -passwd /usr/local/etc/vscode-dev-containers/vnc-passwd" startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "tigervncserver \${DISPLAY} -geometry \${screen_geometry} -depth \${screen_depth} -rfbport ${VNC_PORT} -dpi \${VNC_DPI:-96} -localhost -desktop fluxbox -fg -passwd /usr/local/etc/vscode-dev-containers/vnc-passwd"
# Spin up noVNC if installed and not running. # Spin up noVNC if installed and not runnning.
if [ -d "/usr/local/novnc" ] && [ "\$(ps -ef | grep /usr/local/novnc/noVNC*/utils/launch.sh | grep -v grep)" = "" ]; then if [ -d "/usr/local/novnc" ] && [ "\$(ps -ef | grep /usr/local/novnc/noVNC*/utils/launch.sh | grep -v grep)" = "" ]; then
keepRunningInBackground "noVNC" sudoIf "/usr/local/novnc/noVNC*/utils/launch.sh --listen ${NOVNC_PORT} --vnc localhost:${VNC_PORT}" keepRunningInBackground "noVNC" sudoIf "/usr/local/novnc/noVNC*/utils/launch.sh --listen ${NOVNC_PORT} --vnc localhost:${VNC_PORT}"
log "noVNC started." log "noVNC started."
@ -389,9 +393,6 @@ if [ "${USERNAME}" != "root" ]; then
chown -R ${USERNAME} /home/${USERNAME}/.Xmodmap /home/${USERNAME}/.fluxbox chown -R ${USERNAME} /home/${USERNAME}/.Xmodmap /home/${USERNAME}/.fluxbox
fi fi
# Clean up
rm -rf /var/lib/apt/lists/*
cat << EOF cat << EOF

View file

@ -0,0 +1,27 @@
## Supporting bind mounts from the workspace folder
A common question that comes up is how you can use `bind` mounts from the Docker CLI from within the a dev container using this Feature (e.g. via `-v`). The trick is that, since you're actually using the Docker engine sitting outside of the container, the filesystem paths will be different than those in the container. You need to use the **host**'s paths instead.
> **Note:** The docker-from-docker approach does not currently enable bind mounting locations outside of the workspace folder.
### GitHub Codespaces
In GitHub Codespaces, the workspace folder should work with bind mounts by default, so no further action is required.
### Remote - Containers
A simple way to do this is to put `${localWorkspaceFolder}` in an environment variable that you then use when doing bind mounts inside the container.
Add the following to `devcontainer.json`:
```json
"remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }
```
Then reference the env var when running Docker commands from the terminal inside the container.
```bash
docker run -it --rm -v ${LOCAL_WORKSPACE_FOLDER}:/workspace debian bash
```
> **Note:** There is no `${localWorkspaceFolder}` when using the **Clone Repository in Container Volume** command ([info](https://github.com/microsoft/vscode-remote-release/issues/6160#issuecomment-1014701007)).

View file

@ -0,0 +1,54 @@
# Docker (Docker-from-Docker) (docker-from-docker)
## Example Usage
```json
"features": {
"ghcr.io/devcontainers/features/docker-from-docker:1": {
"version": "latest"
}
}
```
## Options
| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| version | Select or enter a Docker/Moby CLI version. (Availability can vary by OS version.) | string | latest |
| moby | Install OSS Moby build instead of Docker CE | boolean | true |
| dockerDashComposeVersion | Compose version to use for docker-compose (v1 or v2) | string | v1 |
## Supporting bind mounts from the workspace folder
A common question that comes up is how you can use `bind` mounts from the Docker CLI from within the a dev container using this Feature (e.g. via `-v`). The trick is that, since you're actually using the Docker engine sitting outside of the container, the filesystem paths will be different than those in the container. You need to use the **host**'s paths instead.
> **Note:** The docker-from-docker approach does not currently enable bind mounting locations outside of the workspace folder.
### GitHub Codespaces
In GitHub Codespaces, the workspace folder should work with bind mounts by default, so no further action is required.
### Remote - Containers
A simple way to do this is to put `${localWorkspaceFolder}` in an environment variable that you then use when doing bind mounts inside the container.
Add the following to `devcontainer.json`:
```json
"remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }
```
Then reference the env var when running Docker commands from the terminal inside the container.
```bash
docker run -it --rm -v ${LOCAL_WORKSPACE_FOLDER}:/workspace debian bash
```
> **Note:** There is no `${localWorkspaceFolder}` when using the **Clone Repository in Container Volume** command ([info](https://github.com/microsoft/vscode-remote-release/issues/6160#issuecomment-1014701007)).
---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/docker-from-docker/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,9 +1,8 @@
{ {
"id": "docker-outside-of-docker", "id": "docker-from-docker",
"version": "1.3.1", "version": "1.0.1",
"name": "Docker (docker-outside-of-docker)", "name": "Docker (Docker-from-Docker)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker", "descripton": "Re-use the host docker socket, adding the Docker CLI to a container. Feature invokes a script to enable using a forwarded Docker socket within a container to run Docker commands.",
"description": "Re-use the host docker socket, adding the Docker CLI to a container. Feature invokes a script to enable using a forwarded Docker socket within a container to run Docker commands.",
"options": { "options": {
"version": { "version": {
"type": "string", "type": "string",
@ -23,38 +22,25 @@
"dockerDashComposeVersion": { "dockerDashComposeVersion": {
"type": "string", "type": "string",
"enum": [ "enum": [
"none",
"v1", "v1",
"v2" "v2"
], ],
"default": "v2", "default": "v1",
"description": "Compose version to use for docker-compose (v1 or v2 or none)" "description": "Compose version to use for docker-compose (v1 or v2)"
},
"installDockerBuildx": {
"type": "boolean",
"default": true,
"description": "Install Docker Buildx"
} }
}, },
"entrypoint": "/usr/local/share/docker-init.sh", "entrypoint": "/usr/local/share/docker-init.sh",
"customizations": { "containerEnv": {
"vscode": { "DOCKER_BUILDKIT": "1"
"extensions": [
"ms-azuretools.vscode-docker"
]
}
}, },
"extensions": [
"ms-azuretools.vscode-docker"
],
"mounts": [ "mounts": [
{ {
"source": "/var/run/docker.sock", "source": "/var/run/docker.sock",
"target": "/var/run/docker-host.sock", "target": "/var/run/docker-host.sock",
"type": "bind" "type": "bind"
} }
],
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
],
"legacyIds": [
"docker-from-docker"
] ]
} }

View file

@ -7,25 +7,21 @@
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The VS Code and Codespaces Teams
DOCKER_VERSION="${VERSION:-"latest"}" DOCKER_VERSION=${VERSION:-"latest"}
USE_MOBY="${MOBY:-"true"}" USE_MOBY=${MOBY:-"true"}
DOCKER_DASH_COMPOSE_VERSION="${DOCKERDASHCOMPOSEVERSION:-"v1"}" # v1 or v2 or none DOCKER_DASH_COMPOSE_VERSION=${DOCKERDASHCOMPOSEVERSION:-"v1"} # v1 or v2
ENABLE_NONROOT_DOCKER="${ENABLE_NONROOT_DOCKER:-"true"}" ENABLE_NONROOT_DOCKER=${ENABLE_NONROOT_DOCKER:-"true"}
SOURCE_SOCKET="${SOURCE_SOCKET:-"/var/run/docker-host.sock"}" SOURCE_SOCKET=${SOURCE_SOCKET:-"/var/run/docker-host.sock"}
TARGET_SOCKET="${TARGET_SOCKET:-"/var/run/docker.sock"}" TARGET_SOCKET=${TARGET_SOCKET:-"/var/run/docker.sock"}
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" USERNAME=${USERNAME:-"automatic"}
INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}"
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal jammy" DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy"
DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal hirsute impish jammy" DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy"
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
@ -48,18 +44,36 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root USERNAME=root
fi fi
apt_get_update() # Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Function to run apt-get if needed
apt_get_update_if_needed()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..." echo "Running apt-get update..."
apt-get update -y apt-get update
else
echo "Skipping apt-get update."
fi fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -72,7 +86,7 @@ find_version_from_git_tags() {
local repository=$2 local repository=$2
local prefix=${3:-"tags/v"} local prefix=${3:-"tags/v"}
local separator=${4:-"."} local separator=${4:-"."}
local last_part_optional=${5:-"false"} local last_part_optional=${5:-"false"}
if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then
local escaped_separator=${separator//./\\.} local escaped_separator=${separator//./\\.}
local last_part local last_part
@ -102,9 +116,10 @@ find_version_from_git_tags() {
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
# Install dependencies # Install dependencies
check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr wget check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr
if ! type git > /dev/null 2>&1; then if ! type git > /dev/null 2>&1; then
check_packages git apt_get_update_if_needed
apt-get -y install git
fi fi
# Source /etc/os-release to get OS info # Source /etc/os-release to get OS info
@ -112,8 +127,10 @@ fi
# Fetch host/container arch. # Fetch host/container arch.
architecture="$(dpkg --print-architecture)" architecture="$(dpkg --print-architecture)"
# Check if distro is supported # Check if distro is suppported
if [ "${USE_MOBY}" = "true" ]; then if [ "${USE_MOBY}" = "true" ]; then
# 'get_common_setting' allows attribute to be updated remotely
get_common_setting DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES
if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution" err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution"
err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}"
@ -121,6 +138,7 @@ if [ "${USE_MOBY}" = "true" ]; then
fi fi
echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'" echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'"
else else
get_common_setting DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES
if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution" err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution"
err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}"
@ -135,6 +153,7 @@ if [ "${USE_MOBY}" = "true" ]; then
cli_package_name="moby-cli" cli_package_name="moby-cli"
# Import key safely and import Microsoft apt repo # Import key safely and import Microsoft apt repo
get_common_setting MICROSOFT_GPG_KEYS_URI
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list
else else
@ -153,7 +172,7 @@ apt-get update
if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then
# Empty, meaning grab whatever "latest" is in apt repo # Empty, meaning grab whatever "latest" is in apt repo
cli_version_suffix="" cli_version_suffix=""
else else
# Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...) # Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...)
docker_version_dot_escaped="${DOCKER_VERSION//./\\.}" docker_version_dot_escaped="${DOCKER_VERSION//./\\.}"
docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}" docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}"
@ -175,93 +194,75 @@ if type docker > /dev/null 2>&1; then
echo "Docker / Moby CLI already installed." echo "Docker / Moby CLI already installed."
else else
if [ "${USE_MOBY}" = "true" ]; then if [ "${USE_MOBY}" = "true" ]; then
buildx=() apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx
if [ "${INSTALL_DOCKER_BUILDX}" = "true" ]; then
buildx=(moby-buildx)
fi
apt-get -y install --no-install-recommends ${cli_package_name}${cli_version_suffix} "${buildx[@]}"
apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping."
else else
buildx=() apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix}
if [ "${INSTALL_DOCKER_BUILDX}" = "true" ]; then
buildx=(docker-buildx-plugin)
fi
apt-get -y install --no-install-recommends ${cli_package_name}${cli_version_suffix} "${buildx[@]}" docker-compose-plugin
buildx_path="/usr/libexec/docker/cli-plugins/docker-buildx"
# Older versions of Docker CE installs buildx as part of the CLI package
if [ "${INSTALL_DOCKER_BUILDX}" = "false" ] && [ -f "${buildx_path}" ]; then
echo "(*) Removing docker-buildx installed from docker-ce-cli since installDockerBuildx is disabled..."
rm -f "${buildx_path}"
fi
fi fi
unset buildx buildx_path
fi fi
# If 'docker-compose' command is to be included # Install Docker Compose if not already installed and is on a supported architecture
if [ "${DOCKER_DASH_COMPOSE_VERSION}" != "none" ]; then if type docker-compose > /dev/null 2>&1; then
# Install Docker Compose if not already installed and is on a supported architecture echo "Docker Compose already installed."
if type docker-compose > /dev/null 2>&1; then else
echo "Docker Compose already installed." TARGET_COMPOSE_ARCH="$(uname -m)"
elif [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then if [ "${TARGET_COMPOSE_ARCH}" = "amd64" ]; then
TARGET_COMPOSE_ARCH="$(uname -m)" TARGET_COMPOSE_ARCH="x86_64"
if [ "${TARGET_COMPOSE_ARCH}" = "amd64" ]; then fi
TARGET_COMPOSE_ARCH="x86_64" if [ "${TARGET_COMPOSE_ARCH}" != "x86_64" ]; then
# Use pip to get a version that runns on this architecture
if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then
apt_get_update_if_needed
apt-get -y install python3-minimal python3-pip libffi-dev python3-venv
fi fi
if [ "${TARGET_COMPOSE_ARCH}" != "x86_64" ]; then export PIPX_HOME=/usr/local/pipx
# Use pip to get a version that runs on this architecture mkdir -p ${PIPX_HOME}
check_packages python3-minimal python3-pip libffi-dev python3-venv export PIPX_BIN_DIR=/usr/local/bin
export PIPX_HOME=/usr/local/pipx export PYTHONUSERBASE=/tmp/pip-tmp
mkdir -p ${PIPX_HOME} export PIP_CACHE_DIR=/tmp/pip-tmp/cache
export PIPX_BIN_DIR=/usr/local/bin pipx_bin=pipx
export PYTHONUSERBASE=/tmp/pip-tmp if ! type pipx > /dev/null 2>&1; then
export PIP_CACHE_DIR=/tmp/pip-tmp/cache pip3 install --disable-pip-version-check --no-cache-dir --user pipx
pipx_bin=pipx pipx_bin=/tmp/pip-tmp/bin/pipx
if ! type pipx > /dev/null 2>&1; then
pip3 install --disable-pip-version-check --no-cache-dir --user pipx
pipx_bin=/tmp/pip-tmp/bin/pipx
fi
${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose
rm -rf /tmp/pip-tmp
else
compose_v1_version="1"
find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/"
echo "(*) Installing docker-compose ${compose_v1_version}..."
curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
fi fi
else ${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose
echo "(*) Installing compose-switch as docker-compose..." rm -rf /tmp/pip-tmp
compose_switch_version="latest" else
find_version_from_git_tags compose_switch_version "https://github.com/docker/compose-switch" compose_v1_version="1"
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/docker-compose find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/"
echo "(*) Installing docker-compose ${compose_v1_version}..."
curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose
# TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11
fi fi
fi fi
# Setup a docker group in the event the docker socket's group is not root # Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation
if ! grep -qE '^docker:' /etc/group; then current_v1_compose_path="$(which docker-compose)"
echo "(*) Creating missing docker group..." target_v1_compose_path="$(dirname "${current_v1_compose_path}")/docker-compose-v1"
groupadd --system docker if ! type compose-switch > /dev/null 2>&1; then
echo "(*) Installing compose-switch..."
compose_switch_version="latest"
find_version_from_git_tags compose_switch_version "https://github.com/docker/compose-switch"
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch
chmod +x /usr/local/bin/compose-switch
# TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11
# Setup v1 CLI as alternative in addition to compose-switch (which maps to v2)
mv "${current_v1_compose_path}" "${target_v1_compose_path}"
update-alternatives --install /usr/local/bin/docker-compose docker-compose /usr/local/bin/compose-switch 99
update-alternatives --install /usr/local/bin/docker-compose docker-compose "${target_v1_compose_path}" 1
fi
if [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then
update-alternatives --set docker-compose "${target_v1_compose_path}"
else
update-alternatives --set docker-compose /usr/local/bin/compose-switch
fi fi
# Remarking this out to restore functionality in Azure VMs. ID 999 is a reserved group ID
# Ensure docker group gid is 999
# if [ "$(getent group docker | cut -d: -f3)" != "999" ]; then
# echo "(*) Updating docker group gid to 999..."
# groupmod -g 999 docker
# fi
usermod -aG docker "${USERNAME}"
# If init file already exists, exit # If init file already exists, exit
if [ -f "/usr/local/share/docker-init.sh" ]; then if [ -f "/usr/local/share/docker-init.sh" ]; then
# Clean up
rm -rf /var/lib/apt/lists/*
exit 0 exit 0
fi fi
echo "docker-init doesn't exist, adding..." echo "docker-init doesnt exist, adding..."
# By default, make the source and target sockets the same # By default, make the source and target sockets the same
if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then
@ -273,18 +274,24 @@ fi
if [ "${ENABLE_NONROOT_DOCKER}" = "false" ] || [ "${USERNAME}" = "root" ]; then if [ "${ENABLE_NONROOT_DOCKER}" = "false" ] || [ "${USERNAME}" = "root" ]; then
echo -e '#!/usr/bin/env bash\nexec "$@"' > /usr/local/share/docker-init.sh echo -e '#!/usr/bin/env bash\nexec "$@"' > /usr/local/share/docker-init.sh
chmod +x /usr/local/share/docker-init.sh chmod +x /usr/local/share/docker-init.sh
# Clean up
rm -rf /var/lib/apt/lists/*
exit 0 exit 0
fi fi
# Setup a docker group in the event the docker socket's group is not root
if ! grep -qE '^docker:' /etc/group; then
groupadd --system docker
fi
usermod -aG docker "${USERNAME}"
DOCKER_GID="$(grep -oP '^docker:x:\K[^:]+' /etc/group)" DOCKER_GID="$(grep -oP '^docker:x:\K[^:]+' /etc/group)"
# If enabling non-root access and specified user is found, setup socat and add script # If enabling non-root access and specified user is found, setup socat and add script
chown -h "${USERNAME}":root "${TARGET_SOCKET}" chown -h "${USERNAME}":root "${TARGET_SOCKET}"
check_packages socat if ! dpkg -s socat > /dev/null 2>&1; then
apt_get_update_if_needed
apt-get -y install socat
fi
tee /usr/local/share/docker-init.sh > /dev/null \ tee /usr/local/share/docker-init.sh > /dev/null \
<< EOF << EOF
#!/usr/bin/env bash #!/usr/bin/env bash
#------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
@ -316,8 +323,8 @@ log()
echo -e "\n** \$(date) **" | sudoIf tee -a \${SOCAT_LOG} > /dev/null echo -e "\n** \$(date) **" | sudoIf tee -a \${SOCAT_LOG} > /dev/null
log "Ensuring ${USERNAME} has access to ${SOURCE_SOCKET} via ${TARGET_SOCKET}" log "Ensuring ${USERNAME} has access to ${SOURCE_SOCKET} via ${TARGET_SOCKET}"
# If enabled, try to update the docker group with the right GID. If the group is root, # If enabled, try to update the docker group with the right GID. If the group is root,
# fall back on using socat to forward the docker socket to another unix socket so # fall back on using socat to forward the docker socket to another unix socket so
# that we can set permissions on it without affecting the host. # that we can set permissions on it without affecting the host.
if [ "${ENABLE_NONROOT_DOCKER}" = "true" ] && [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ] && [ "${USERNAME}" != "root" ] && [ "${USERNAME}" != "0" ]; then if [ "${ENABLE_NONROOT_DOCKER}" = "true" ] && [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ] && [ "${USERNAME}" != "root" ] && [ "${USERNAME}" != "0" ]; then
SOCKET_GID=\$(stat -c '%g' ${SOURCE_SOCKET}) SOCKET_GID=\$(stat -c '%g' ${SOURCE_SOCKET})
@ -337,15 +344,11 @@ if [ "${ENABLE_NONROOT_DOCKER}" = "true" ] && [ "${SOURCE_SOCKET}" != "${TARGET_
log "Success" log "Success"
fi fi
# Execute whatever commands were passed in (if any). This allows us # Execute whatever commands were passed in (if any). This allows us
# to set this script to ENTRYPOINT while still executing the default CMD. # to set this script to ENTRYPOINT while still executing the default CMD.
set +e set +e
exec "\$@" exec "\$@"
EOF EOF
chmod +x /usr/local/share/docker-init.sh chmod +x /usr/local/share/docker-init.sh
chown ${USERNAME}:root /usr/local/share/docker-init.sh chown ${USERNAME}:root /usr/local/share/docker-init.sh
# Clean up
rm -rf /var/lib/apt/lists/*
echo "Done!" echo "Done!"

View file

@ -1,16 +0,0 @@
## Limitations
This docker-in-docker Dev Container Feature is roughly based on the [official docker-in-docker wrapper script](https://github.com/moby/moby/blob/master/hack/dind) that is part of the [Moby project](https://mobyproject.org/). With this in mind:
* As the name implies, the Feature is expected to work when the host is running Docker (or the OSS Moby container engine it is built on). It may be possible to get running in other container engines, but it has not been tested with them.
* The host and the container must be running on the same chip architecture. You will not be able to use it with an emulated x86 image with Docker Desktop on an Apple Silicon Mac, like in this example:
```
FROM --platform=linux/amd64 mcr.microsoft.com/devcontainers/typescript-node:16
```
See [Issue #219](https://github.com/devcontainers/features/issues/219) for more details.
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@ Create child containers *inside* a container, independent from the host's docker
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {} "ghcr.io/devcontainers/features/docker-in-docker:1": {
"version": "latest"
}
} }
``` ```
@ -17,33 +19,8 @@ Create child containers *inside* a container, independent from the host's docker
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select or enter a Docker/Moby Engine version. (Availability can vary by OS version.) | string | latest | | version | Select or enter a Docker/Moby Engine version. (Availability can vary by OS version.) | string | latest |
| moby | Install OSS Moby build instead of Docker CE | boolean | true | | moby | Install OSS Moby build instead of Docker CE | boolean | true |
| dockerDashComposeVersion | Default version of Docker Compose (v1 or v2 or none) | string | v1 | | dockerDashComposeVersion | Default version of Docker Compose (v1 or v2) | string | v1 |
| azureDnsAutoDetection | Allow automatically setting the dockerd DNS server when the installation script detects it is running in Azure | boolean | true |
| dockerDefaultAddressPool | Define default address pools for Docker networks. e.g. base=192.168.0.0/16,size=24 | string | - |
| installDockerBuildx | Install Docker Buildx | boolean | true |
## Customizations
### VS Code Extensions
- `ms-azuretools.vscode-docker`
## Limitations
This docker-in-docker Dev Container Feature is roughly based on the [official docker-in-docker wrapper script](https://github.com/moby/moby/blob/master/hack/dind) that is part of the [Moby project](https://mobyproject.org/). With this in mind:
* As the name implies, the Feature is expected to work when the host is running Docker (or the OSS Moby container engine it is built on). It may be possible to get running in other container engines, but it has not been tested with them.
* The host and the container must be running on the same chip architecture. You will not be able to use it with an emulated x86 image with Docker Desktop on an Apple Silicon Mac, like in this example:
```
FROM --platform=linux/amd64 mcr.microsoft.com/devcontainers/typescript-node:16
```
See [Issue #219](https://github.com/devcontainers/features/issues/219) for more details.
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---

View file

@ -1,8 +1,7 @@
{ {
"id": "docker-in-docker", "id": "docker-in-docker",
"version": "2.7.1", "version": "1.0.1",
"name": "Docker (Docker-in-Docker)", "name": "Docker (Docker-in-Docker)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-in-docker",
"description": "Create child containers *inside* a container, independent from the host's docker instance. Installs Docker extension in the container along with needed CLIs.", "description": "Create child containers *inside* a container, independent from the host's docker instance. Installs Docker extension in the container along with needed CLIs.",
"options": { "options": {
"version": { "version": {
@ -23,28 +22,11 @@
"dockerDashComposeVersion": { "dockerDashComposeVersion": {
"type": "string", "type": "string",
"enum": [ "enum": [
"none",
"v1", "v1",
"v2" "v2"
], ],
"default": "v1", "default": "v1",
"description": "Default version of Docker Compose (v1 or v2 or none)" "description": "Default version of Docker Compose (v1 or v2)"
},
"azureDnsAutoDetection": {
"type": "boolean",
"default": true,
"description": "Allow automatically setting the dockerd DNS server when the installation script detects it is running in Azure"
},
"dockerDefaultAddressPool": {
"type": "string",
"default": "",
"proposals": [],
"description": "Define default address pools for Docker networks. e.g. base=192.168.0.0/16,size=24"
},
"installDockerBuildx": {
"type": "boolean",
"default": true,
"description": "Install Docker Buildx"
} }
}, },
"entrypoint": "/usr/local/share/docker-init.sh", "entrypoint": "/usr/local/share/docker-init.sh",
@ -52,21 +34,14 @@
"containerEnv": { "containerEnv": {
"DOCKER_BUILDKIT": "1" "DOCKER_BUILDKIT": "1"
}, },
"customizations": { "extensions": [
"vscode": { "ms-azuretools.vscode-docker"
"extensions": [ ],
"ms-azuretools.vscode-docker"
]
}
},
"mounts": [ "mounts": [
{ {
"source": "dind-var-lib-docker-${devcontainerId}", "source": "dind-var-lib-docker",
"target": "/var/lib/docker", "target": "/var/lib/docker",
"type": "volume" "type": "volume"
} }
],
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
] ]
} }

View file

@ -5,26 +5,23 @@
#------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------
# #
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker-in-docker.md # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker-in-docker.md
# Maintainer: The Dev Container spec maintainers # Maintainer: The VS Code and Codespaces Teams
DOCKER_VERSION="${VERSION:-"latest"}" # The Docker/Moby Engine + CLI should match in version DOCKER_VERSION=${VERSION:-"latest"} # The Docker/Moby Engine + CLI should match in version
USE_MOBY="${MOBY:-"true"}" USE_MOBY=${MOBY:-"true"}
DOCKER_DASH_COMPOSE_VERSION="${DOCKERDASHCOMPOSEVERSION:-"v1"}" # v1 or v2 or none DOCKER_DASH_COMPOSE_VERSION=${DOCKERDASHCOMPOSEVERSION:-"v1"} # v1 or v2
AZURE_DNS_AUTO_DETECTION="${AZUREDNSAUTODETECTION:-"true"}"
DOCKER_DEFAULT_ADDRESS_POOL="${DOCKERDEFAULTADDRESSPOOL}" ENABLE_NONROOT_DOCKER=${ENABLE_NONROOT_DOCKER:-"true"}
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" USERNAME=${USERNAME:-"automatic"}
INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}"
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal jammy" DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy"
DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal hirsute impish jammy" DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy"
# Default: Exit on any failure. # Default: Exit on any failure.
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
# Setup STDERR. # Setup STDERR.
err() { err() {
echo "(!) $*" >&2 echo "(!) $*" >&2
@ -57,18 +54,36 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root USERNAME=root
fi fi
apt_get_update() # Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Function to run apt-get if needed
apt_get_update_if_needed()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..." echo "Running apt-get update..."
apt-get update -y apt-get update
else
echo "Skipping apt-get update."
fi fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -120,8 +135,10 @@ export DEBIAN_FRONTEND=noninteractive
# Fetch host/container arch. # Fetch host/container arch.
architecture="$(dpkg --print-architecture)" architecture="$(dpkg --print-architecture)"
# Check if distro is supported # Check if distro is suppported
if [ "${USE_MOBY}" = "true" ]; then if [ "${USE_MOBY}" = "true" ]; then
# 'get_common_setting' allows attribute to be updated remotely
get_common_setting DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES
if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution" err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution"
err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}"
@ -129,6 +146,7 @@ if [ "${USE_MOBY}" = "true" ]; then
fi fi
echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'" echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'"
else else
get_common_setting DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES
if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution" err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution"
err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}"
@ -138,9 +156,10 @@ else
fi fi
# Install dependencies # Install dependencies
check_packages apt-transport-https curl ca-certificates pigz iptables gnupg2 dirmngr wget check_packages apt-transport-https curl ca-certificates pigz iptables gnupg2 dirmngr
if ! type git > /dev/null 2>&1; then if ! type git > /dev/null 2>&1; then
check_packages git apt_get_update_if_needed
apt-get -y install git
fi fi
# Swap to legacy iptables for compatibility # Swap to legacy iptables for compatibility
@ -159,6 +178,7 @@ if [ "${USE_MOBY}" = "true" ]; then
cli_package_name="moby-cli" cli_package_name="moby-cli"
# Import key safely and import Microsoft apt repo # Import key safely and import Microsoft apt repo
get_common_setting MICROSOFT_GPG_KEYS_URI
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list
else else
@ -223,111 +243,81 @@ fi
echo "Finished installing docker / moby!" echo "Finished installing docker / moby!"
# If 'docker-compose' command is to be included # Install Docker Compose if not already installed and is on a supported architecture
if [ "${DOCKER_DASH_COMPOSE_VERSION}" != "none" ]; then if type docker-compose > /dev/null 2>&1; then
# Install Docker Compose if not already installed and is on a supported architecture echo "Docker Compose v1 already installed."
if type docker-compose > /dev/null 2>&1; then else
echo "Docker Compose v1 already installed." target_compose_arch="${architecture}"
else if [ "${target_compose_arch}" = "amd64" ]; then
target_compose_arch="${architecture}" target_compose_arch="x86_64"
if [ "${target_compose_arch}" = "amd64" ]; then fi
target_compose_arch="x86_64" if [ "${target_compose_arch}" != "x86_64" ]; then
# Use pip to get a version that runs on this architecture
if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then
apt_get_update_if_needed
apt-get -y install python3-minimal python3-pip libffi-dev python3-venv
fi fi
if [ "${target_compose_arch}" != "x86_64" ]; then export PIPX_HOME=/usr/local/pipx
# Use pip to get a version that runs on this architecture mkdir -p ${PIPX_HOME}
check_packages python3-minimal python3-pip libffi-dev python3-venv export PIPX_BIN_DIR=/usr/local/bin
export PIPX_HOME=/usr/local/pipx export PYTHONUSERBASE=/tmp/pip-tmp
mkdir -p ${PIPX_HOME} export PIP_CACHE_DIR=/tmp/pip-tmp/cache
export PIPX_BIN_DIR=/usr/local/bin pipx_bin=pipx
export PYTHONUSERBASE=/tmp/pip-tmp if ! type pipx > /dev/null 2>&1; then
export PIP_CACHE_DIR=/tmp/pip-tmp/cache pip3 install --disable-pip-version-check --no-cache-dir --user pipx
pipx_bin=pipx pipx_bin=/tmp/pip-tmp/bin/pipx
if ! type pipx > /dev/null 2>&1; then
pip3 install --disable-pip-version-check --no-cache-dir --user pipx
pipx_bin=/tmp/pip-tmp/bin/pipx
fi
set +e
${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose
exit_code=$?
set -e
if [ ${exit_code} -ne 0 ]; then
# Temporary: https://github.com/devcontainers/features/issues/616
# See https://github.com/yaml/pyyaml/issues/601
echo "(*) Failed to install docker-compose via pipx. Trying via pip3..."
export PYTHONUSERBASE=/usr/local
pip3 install --disable-pip-version-check --no-cache-dir --user "Cython<3.0" pyyaml wheel docker-compose --no-build-isolation
fi
rm -rf /tmp/pip-tmp
else
compose_v1_version="1"
find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/"
echo "(*) Installing docker-compose ${compose_v1_version}..."
curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
fi fi
fi ${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose
rm -rf /tmp/pip-tmp
# Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation
current_v1_compose_path="$(which docker-compose)"
target_v1_compose_path="$(dirname "${current_v1_compose_path}")/docker-compose-v1"
if ! type compose-switch > /dev/null 2>&1; then
echo "(*) Installing compose-switch..."
compose_switch_version="latest"
find_version_from_git_tags compose_switch_version "https://github.com/docker/compose-switch"
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch
chmod +x /usr/local/bin/compose-switch
# TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11
# Setup v1 CLI as alternative in addition to compose-switch (which maps to v2)
mv "${current_v1_compose_path}" "${target_v1_compose_path}"
update-alternatives --install /usr/local/bin/docker-compose docker-compose /usr/local/bin/compose-switch 99
update-alternatives --install /usr/local/bin/docker-compose docker-compose "${target_v1_compose_path}" 1
fi
if [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then
update-alternatives --set docker-compose "${target_v1_compose_path}"
else else
update-alternatives --set docker-compose /usr/local/bin/compose-switch compose_v1_version="1"
find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/"
echo "(*) Installing docker-compose ${compose_v1_version}..."
curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
fi fi
fi fi
# Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation
current_v1_compose_path="$(which docker-compose)"
target_v1_compose_path="$(dirname "${current_v1_compose_path}")/docker-compose-v1"
if ! type compose-switch > /dev/null 2>&1; then
echo "(*) Installing compose-switch..."
compose_switch_version="latest"
find_version_from_git_tags compose_switch_version "https://github.com/docker/compose-switch"
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch
chmod +x /usr/local/bin/compose-switch
# TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11
# Setup v1 CLI as alternative in addition to compose-switch (which maps to v2)
mv "${current_v1_compose_path}" "${target_v1_compose_path}"
update-alternatives --install /usr/local/bin/docker-compose docker-compose /usr/local/bin/compose-switch 99
update-alternatives --install /usr/local/bin/docker-compose docker-compose "${target_v1_compose_path}" 1
fi
if [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then
update-alternatives --set docker-compose "${target_v1_compose_path}"
else
update-alternatives --set docker-compose /usr/local/bin/compose-switch
fi
# If init file already exists, exit # If init file already exists, exit
if [ -f "/usr/local/share/docker-init.sh" ]; then if [ -f "/usr/local/share/docker-init.sh" ]; then
echo "/usr/local/share/docker-init.sh already exists, so exiting." echo "/usr/local/share/docker-init.sh already exists, so exiting."
# Clean up
rm -rf /var/lib/apt/lists/*
exit 0 exit 0
fi fi
echo "docker-init doesn't exist, adding..." echo "docker-init doesnt exist, adding..."
if ! cat /etc/group | grep -e "^docker:" > /dev/null 2>&1; then # Add user to the docker group
groupadd -r docker if [ "${ENABLE_NONROOT_DOCKER}" = "true" ]; then
fi if ! getent group docker > /dev/null 2>&1; then
groupadd docker
fi
usermod -aG docker ${USERNAME} usermod -aG docker ${USERNAME}
if [ "${INSTALL_DOCKER_BUILDX}" = "true" ]; then
buildx_version="latest"
find_version_from_git_tags buildx_version "https://github.com/docker/buildx" "refs/tags/v"
echo "(*) Installing buildx ${buildx_version}..."
buildx_file_name="buildx-v${buildx_version}.linux-${architecture}"
cd /tmp && wget "https://github.com/docker/buildx/releases/download/v${buildx_version}/${buildx_file_name}"
mkdir -p ${_REMOTE_USER_HOME}/.docker/cli-plugins
mv ${buildx_file_name} ${_REMOTE_USER_HOME}/.docker/cli-plugins/docker-buildx
chmod +x ${_REMOTE_USER_HOME}/.docker/cli-plugins/docker-buildx
chown -R "${USERNAME}:docker" "${_REMOTE_USER_HOME}/.docker"
chmod -R g+r+w "${_REMOTE_USER_HOME}/.docker"
find "${_REMOTE_USER_HOME}/.docker" -type d -print0 | xargs -n 1 -0 chmod g+s
fi fi
tee /usr/local/share/docker-init.sh > /dev/null \ tee /usr/local/share/docker-init.sh > /dev/null \
<< EOF << 'EOF'
#!/bin/sh #!/bin/sh
#------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
@ -336,18 +326,13 @@ tee /usr/local/share/docker-init.sh > /dev/null \
set -e set -e
AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} dockerd_start="$(cat << 'INNEREOF'
DOCKER_DEFAULT_ADDRESS_POOL=${DOCKER_DEFAULT_ADDRESS_POOL}
EOF
tee -a /usr/local/share/docker-init.sh > /dev/null \
<< 'EOF'
dockerd_start="AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} DOCKER_DEFAULT_ADDRESS_POOL=${DOCKER_DEFAULT_ADDRESS_POOL} $(cat << 'INNEREOF'
# explicitly remove dockerd and containerd PID file to ensure that it can start properly if it was stopped uncleanly # explicitly remove dockerd and containerd PID file to ensure that it can start properly if it was stopped uncleanly
# ie: docker kill <ID>
find /run /var/run -iname 'docker*.pid' -delete || : find /run /var/run -iname 'docker*.pid' -delete || :
find /run /var/run -iname 'container*.pid' -delete || : find /run /var/run -iname 'container*.pid' -delete || :
# -- Start: dind wrapper script -- ## Dind wrapper script from docker team, adapted to a function
# Maintained: https://github.com/moby/moby/blob/master/hack/dind # Maintained: https://github.com/moby/moby/blob/master/hack/dind
export container=docker export container=docker
@ -364,109 +349,45 @@ dockerd_start="AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} DOCKER_DEFAU
mount -t tmpfs none /tmp mount -t tmpfs none /tmp
fi fi
set_cgroup_nesting() # cgroup v2: enable nesting
{ if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
# cgroup v2: enable nesting # move the processes from the root group to the /init group,
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then # otherwise writing subtree_control fails with EBUSY.
# move the processes from the root group to the /init group, # An error during moving non-existent process (i.e., "cat") is ignored.
# otherwise writing subtree_control fails with EBUSY. mkdir -p /sys/fs/cgroup/init
# An error during moving non-existent process (i.e., "cat") is ignored. xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || :
mkdir -p /sys/fs/cgroup/init # enable controllers
xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || : sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \
# enable controllers > /sys/fs/cgroup/cgroup.subtree_control
sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ fi
> /sys/fs/cgroup/cgroup.subtree_control ## Dind wrapper over.
fi
}
# Set cgroup nesting, retrying if necessary
retry_cgroup_nesting=0
until [ "${retry_cgroup_nesting}" -eq "5" ];
do
set +e
set_cgroup_nesting
if [ $? -ne 0 ]; then
echo "(*) cgroup v2: Failed to enable nesting, retrying..."
else
break
fi
retry_cgroup_nesting=`expr $retry_cgroup_nesting + 1`
set -e
done
# -- End: dind wrapper script --
# Handle DNS # Handle DNS
set +e set +e
cat /etc/resolv.conf | grep -i 'internal.cloudapp.net' > /dev/null 2>&1 cat /etc/resolv.conf | grep -i 'internal.cloudapp.net'
if [ $? -eq 0 ] && [ "${AZURE_DNS_AUTO_DETECTION}" = "true" ] if [ $? -eq 0 ]
then then
echo "Setting dockerd Azure DNS." echo "Setting dockerd Azure DNS."
CUSTOMDNS="--dns 168.63.129.16" CUSTOMDNS="--dns 168.63.129.16"
else else
echo "Not setting dockerd DNS manually." echo "Not setting dockerd DNS manually."
CUSTOMDNS="" CUSTOMDNS=""
fi fi
set -e set -e
if [ -z "$DOCKER_DEFAULT_ADDRESS_POOL" ]
then
DEFAULT_ADDRESS_POOL=""
else
DEFAULT_ADDRESS_POOL="--default-address-pool $DOCKER_DEFAULT_ADDRESS_POOL"
fi
# Start docker/moby engine # Start docker/moby engine
( dockerd $CUSTOMDNS $DEFAULT_ADDRESS_POOL > /tmp/dockerd.log 2>&1 ) & ( dockerd $CUSTOMDNS > /tmp/dockerd.log 2>&1 ) &
INNEREOF INNEREOF
)" )"
sudo_if() { # Start using sudo if not invoked as root
COMMAND="$*" if [ "$(id -u)" -ne 0 ]; then
sudo /bin/sh -c "${dockerd_start}"
else
eval "${dockerd_start}"
fi
if [ "$(id -u)" -ne 0 ]; then set +e
sudo $COMMAND
else
$COMMAND
fi
}
retry_docker_start_count=0
docker_ok="false"
until [ "${docker_ok}" = "true" ] || [ "${retry_docker_start_count}" -eq "5" ];
do
# Start using sudo if not invoked as root
if [ "$(id -u)" -ne 0 ]; then
sudo /bin/sh -c "${dockerd_start}"
else
eval "${dockerd_start}"
fi
retry_count=0
until [ "${docker_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
do
sleep 1s
set +e
docker info > /dev/null 2>&1 && docker_ok="true"
set -e
retry_count=`expr $retry_count + 1`
done
if [ "${docker_ok}" != "true" ] && [ "${retry_docker_start_count}" != "4" ]; then
echo "(*) Failed to start docker, retrying..."
set +e
sudo_if pkill dockerd
sudo_if pkill containerd
set -e
fi
retry_docker_start_count=`expr $retry_docker_start_count + 1`
done
# Execute whatever commands were passed in (if any). This allows us # Execute whatever commands were passed in (if any). This allows us
# to set this script to ENTRYPOINT while still executing the default CMD. # to set this script to ENTRYPOINT while still executing the default CMD.
@ -476,7 +397,4 @@ EOF
chmod +x /usr/local/share/docker-init.sh chmod +x /usr/local/share/docker-init.sh
chown ${USERNAME}:root /usr/local/share/docker-init.sh chown ${USERNAME}:root /usr/local/share/docker-init.sh
# Clean up
rm -rf /var/lib/apt/lists/*
echo 'docker-in-docker-debian script has completed!' echo 'docker-in-docker-debian script has completed!'

View file

@ -1,61 +0,0 @@
## Limitations
- As the name implies, the Feature is expected to work when the host is running Docker (or the OSS Moby container engine it is built on). It may be possible to get running in other container engines, but it has not been tested with them.
- The host and the container must be running on the same chip architecture. You will not be able to use it with an emulated x86 image with Docker Desktop on an Apple Silicon Mac, for example.
- This approach does not currently enable bind mounting the workspace folder by default, and cannot support folders outside of the workspace folder. Consider whether the [Docker-in-Docker Feature](../docker-in-docker) would better meet your needs given it does not have this limitation.
## Supporting bind mounts from the workspace folder
A common question that comes up is how you can use `bind` mounts from the Docker CLI from within the a dev container using this Feature (e.g. via `-v`). If you cannot use the [Docker-in-Docker Feature](../docker-in-docker), the only way to work around this is to use the **host**'s folder paths instead of the container's paths. There are 2 ways to do this
### 1. Use the `${localWorkspaceFolder}` as environment variable in your code
1. Add the following to `devcontainer.json`:
```json
"remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }
```
2. Usage with Docker commands
```bash
docker run -it --rm -v ${LOCAL_WORKSPACE_FOLDER}:/workspace debian bash
```
3. Usage with Docker-compose
```yaml
version: "3.9"
services:
debian:
image: debian
volumes:
- ${LOCAL_WORKSPACE_FOLDER:-./}:/workspace
```
- The defaults value `./` is added so that the `docker-compose.yaml` file can work when it is run outside of the container
### Change the workspace to `${localWorkspaceFolder}`
- This is useful if we don't want to edit the `docker-compose.yaml` file
1. Add the following to `devcontainer.json`
```json
"workspaceFolder": "${localWorkspaceFolder}",
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind"
```
2. Rebuild the container.
3. When the container first started with this settings, select the Workspace with the absolute path to the working directory inside the container
4. Docker commands with bind mount should work as they did outside of the devcontainer
> **Note:** There is no `${localWorkspaceFolder}` when using the **Clone Repository in Container Volume** command in the VS Code Dev Containers extension ([info](https://github.com/microsoft/vscode-remote-release/issues/6160#issuecomment-1014701007)).
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -1,96 +0,0 @@
### **IMPORTANT NOTE**
- **Ids used to publish this Feature in the past - 'docker-from-docker'**
# Docker (docker-outside-of-docker) (docker-outside-of-docker)
Re-use the host docker socket, adding the Docker CLI to a container. Feature invokes a script to enable using a forwarded Docker socket within a container to run Docker commands.
## Example Usage
```json
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
}
```
## Options
| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| version | Select or enter a Docker/Moby CLI version. (Availability can vary by OS version.) | string | latest |
| moby | Install OSS Moby build instead of Docker CE | boolean | true |
| dockerDashComposeVersion | Compose version to use for docker-compose (v1 or v2 or none) | string | v2 |
| installDockerBuildx | Install Docker Buildx | boolean | true |
## Customizations
### VS Code Extensions
- `ms-azuretools.vscode-docker`
## Limitations
- As the name implies, the Feature is expected to work when the host is running Docker (or the OSS Moby container engine it is built on). It may be possible to get running in other container engines, but it has not been tested with them.
- The host and the container must be running on the same chip architecture. You will not be able to use it with an emulated x86 image with Docker Desktop on an Apple Silicon Mac, for example.
- This approach does not currently enable bind mounting the workspace folder by default, and cannot support folders outside of the workspace folder. Consider whether the [Docker-in-Docker Feature](../docker-in-docker) would better meet your needs given it does not have this limitation.
## Supporting bind mounts from the workspace folder
A common question that comes up is how you can use `bind` mounts from the Docker CLI from within the a dev container using this Feature (e.g. via `-v`). If you cannot use the [Docker-in-Docker Feature](../docker-in-docker), the only way to work around this is to use the **host**'s folder paths instead of the container's paths. There are 2 ways to do this
### 1. Use the `${localWorkspaceFolder}` as environment variable in your code
1. Add the following to `devcontainer.json`:
```json
"remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }
```
2. Usage with Docker commands
```bash
docker run -it --rm -v ${LOCAL_WORKSPACE_FOLDER}:/workspace debian bash
```
3. Usage with Docker-compose
```yaml
version: "3.9"
services:
debian:
image: debian
volumes:
- ${LOCAL_WORKSPACE_FOLDER:-./}:/workspace
```
- The defaults value `./` is added so that the `docker-compose.yaml` file can work when it is run outside of the container
### Change the workspace to `${localWorkspaceFolder}`
- This is useful if we don't want to edit the `docker-compose.yaml` file
1. Add the following to `devcontainer.json`
```json
"workspaceFolder": "${localWorkspaceFolder}",
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind"
```
2. Rebuild the container.
3. When the container first started with this settings, select the Workspace with the absolute path to the working directory inside the container
4. Docker commands with bind mount should work as they did outside of the devcontainer
> **Note:** There is no `${localWorkspaceFolder}` when using the **Clone Repository in Container Volume** command in the VS Code Dev Containers extension ([info](https://github.com/microsoft/vscode-remote-release/issues/6160#issuecomment-1014701007)).
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/docker-outside-of-docker/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,74 +0,0 @@
## Configuration examples
Installing only the latest .NET SDK version (the default).
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": "latest" // or "" or {}
}
```
Installing an additional SDK version. Multiple versions can be specified as comma-separated values.
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"additionalVersions": "lts"
}
}
```
Installing specific SDK versions.
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0",
"additionalVersions": "7.0, 8.0"
}
}
```
Installing a specific SDK feature band.
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0.4xx",
}
}
```
Installing a specific SDK patch version.
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0.412",
}
}
```
Installing only the .NET Runtime or the ASP.NET Core Runtime. (The SDK includes all runtimes so this configuration is only useful if you need to run .NET apps without building them from source.)
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "none",
"dotnetRuntimeVersions": "latest, lts",
"aspnetCoreRuntimeVersions": "latest, lts",
}
}
```
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -1,13 +1,15 @@
# Dotnet CLI (dotnet) # Dotnet CLI (dotnet)
This Feature installs the latest .NET SDK, which includes the .NET CLI and the shared runtime. Options are provided to choose a different version or additional versions. Installs the .NET CLI. Provides option of installing sdk or runtime, and option of versions to install. Uses latest version of .NET sdk as defaults to install.
## Example Usage ## Example Usage
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/dotnet:2": {} "ghcr.io/devcontainers/features/dotnet:1": {
"version": "latest"
}
} }
``` ```
@ -15,91 +17,10 @@ This Feature installs the latest .NET SDK, which includes the .NET CLI and the s
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select or enter a .NET SDK version. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | latest | | version | Select or enter a dotnet CLI version. (Available versions may vary by Linux distribution.) | string | latest |
| additionalVersions | Enter additional .NET SDK versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | - | | runtimeOnly | Install just the dotnet runtime if true, and sdk if false. | boolean | - |
| dotnetRuntimeVersions | Enter additional .NET runtime versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | - | | installUsingApt | If true, it installs using apt instead of the release URL | boolean | true |
| aspNetCoreRuntimeVersions | Enter additional ASP.NET Core runtime versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | - |
## Customizations
### VS Code Extensions
- `ms-dotnettools.csharp`
## Configuration examples
Installing only the latest .NET SDK version (the default).
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": "latest" // or "" or {}
}
```
Installing an additional SDK version. Multiple versions can be specified as comma-separated values.
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"additionalVersions": "lts"
}
}
```
Installing specific SDK versions.
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0",
"additionalVersions": "7.0, 8.0"
}
}
```
Installing a specific SDK feature band.
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0.4xx",
}
}
```
Installing a specific SDK patch version.
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0.412",
}
}
```
Installing only the .NET Runtime or the ASP.NET Core Runtime. (The SDK includes all runtimes so this configuration is only useful if you need to run .NET apps without building them from source.)
``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "none",
"dotnetRuntimeVersions": "latest, lts",
"aspnetCoreRuntimeVersions": "latest, lts",
}
}
```
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---

View file

@ -1,53 +1,36 @@
{ {
"id": "dotnet", "id": "dotnet",
"version": "2.0.2", "version": "1.0.1",
"name": "Dotnet CLI", "name": "Dotnet CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/dotnet", "description": "Installs the .NET CLI. Provides option of installing sdk or runtime, and option of versions to install. Uses latest version of .NET sdk as defaults to install.",
"description": "This Feature installs the latest .NET SDK, which includes the .NET CLI and the shared runtime. Options are provided to choose a different version or additional versions.",
"options": { "options": {
"version": { "version": {
"type": "string", "type": "string",
"proposals": [ "proposals": [
"latest", "latest",
"lts", "6",
"none", "5",
"8.0", "3.1"
"7.0",
"6.0"
], ],
"default": "latest", "default": "latest",
"description": "Select or enter a .NET SDK version. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version." "description": "Select or enter a dotnet CLI version. (Available versions may vary by Linux distribution.)"
}, },
"additionalVersions": { "runtimeOnly": {
"type": "string", "type": "boolean",
"default": "", "default": false,
"description": "Enter additional .NET SDK versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version." "description": "Install just the dotnet runtime if true, and sdk if false."
}, },
"dotnetRuntimeVersions": { "installUsingApt": {
"type": "string", "type": "boolean",
"default": "", "default": "true",
"description": "Enter additional .NET runtime versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version." "description": "If true, it installs using apt instead of the release URL"
},
"aspNetCoreRuntimeVersions": {
"type": "string",
"default": "",
"description": "Enter additional ASP.NET Core runtime versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version."
} }
}, },
"containerEnv": { "containerEnv": {
"DOTNET_ROOT": "/usr/share/dotnet", "DOTNET_ROOT": "/usr/local/dotnet/current",
"PATH": "$PATH:$DOTNET_ROOT:~/.dotnet/tools", "PATH": "${PATH}:${DOTNET_ROOT}"
"DOTNET_RUNNING_IN_CONTAINER": "true",
"DOTNET_USE_POLLING_FILE_WATCHER": "true"
}, },
"customizations": { "extensions": [
"vscode": { "ms-dotnettools.csharp"
"extensions": [
"ms-dotnettools.csharp"
]
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
] ]
} }

504
src/dotnet/install.sh Normal file → Executable file
View file

@ -4,115 +4,467 @@
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------
# #
# Docs: https://github.com/devcontainers/features/tree/main/src/dotnet # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/dotnet.md
# Maintainer: The Dev Container spec maintainers # Maintainer: The VS Code and Codespaces Teams
DOTNET_VERSION="${VERSION:-"latest"}"
ADDITIONAL_VERSIONS="${ADDITIONALVERSIONS}"
DOTNET_RUNTIME_VERSIONS="${DOTNETRUNTIMEVERSIONS}"
ASPNETCORE_RUNTIME_VERSIONS="${ASPNETCORERUNTIMEVERSIONS}"
set -e DOTNET_VERSION=${VERSION:-"latest"}
DOTNET_RUNTIME_ONLY=${RUNTIMEONLY:-"false"}
OVERRIDE_DEFAULT_VERSION=${OVERRIDEDEFAULTVERSION:-"true"}
INSTALL_USING_APT=${INSTALLUSINGAPT:-"true"}
# Import trim_whitespace and split_csv USERNAME=${USERNAME:-"automatic"}
source "scripts/string-helpers.sh" UPDATE_RC=${UPDATE_RC:-"true"}
TARGET_DOTNET_ROOT=${TARGET_DOTNET_ROOT:-"/usr/local/dotnet"}
ACCESS_GROUP=${ACCESS_GROUP:-"dotnet"}
# Import install_sdk and install_runtime MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
source "scripts/dotnet-helpers.sh" DOTNET_ARCHIVE_ARCHITECTURES="amd64"
DOTNET_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute jammy"
# Feed URI sourced from the official dotnet-install.sh
# https://github.com/dotnet/install-scripts/blob/1b98b94a6f6d81cc4845eb88e0195fac67caa0a6/src/dotnet-install.sh#L1342-L1343
DOTNET_CDN_FEED_URI="https://dotnetcli.azureedge.net"
# Ubuntu 22.04 and on do not ship with libssl1.1, which is required for versions of .NET < 6.0
DOTNET_VERSION_CODENAMES_REQUIRE_OLDER_LIBSSL_1="buster bullseye bionic focal hirsute"
# Clean up # Comma-separated list of dotnet versions to be installed
rm -rf /var/lib/apt/lists/* # alongside DOTNET_VERSION, but not set as default.
ADDITIONAL_VERSIONS=${ADDITIONALVERSIONS:-""}
# Setup STDERR. # Setup STDERR.
err() { err() {
echo "(!) $*" >&2 echo "(!) $*" >&2
} }
apt_get_update() { # Ensure the appropriate root user is running the script.
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update -y
fi
}
# Checks if packages are installed and installs them if not
check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update
apt-get -y install --no-install-recommends "$@"
fi
}
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
err 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' err 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
fi fi
# For our own convenience, combine DOTNET_VERSION and ADDITIONAL_VERSIONS into a single 'versions' array # Ensure that login shells get the correct path if the user updated the PATH using ENV.
versions=() rm -f /etc/profile.d/00-restore-env.sh
echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh
chmod +x /etc/profile.d/00-restore-env.sh
# The version can be set to 'none' for runtime-only installations, then the array will just remain empty # Determine the appropriate non-root user.
# Ensure there are no leading or trailing spaces that can break regex pattern matching if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
if [ "$DOTNET_VERSION" != "none" ]; then USERNAME=""
versions+=("$(trim_whitespace "$DOTNET_VERSION")") POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
for additional_version in $(split_csv "$ADDITIONAL_VERSIONS"); do for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
versions+=("$additional_version") if id -u "${CURRENT_USER}" > /dev/null 2>&1; then
USERNAME="${CURRENT_USER}"
break
fi
done done
if [ "${USERNAME}" = "" ]; then
USERNAME=root
fi
elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root
fi fi
dotnetRuntimeVersions=() ###################
for dotnetRuntimeVersion in $(split_csv "$DOTNET_RUNTIME_VERSIONS"); do # Helper Functions
dotnetRuntimeVersions+=("$dotnetRuntimeVersion") ###################
done
aspNetCoreRuntimeVersions=() # Cleanup temporary directory and associated files when exiting the script.
for aspNetCoreRuntimeVersion in $(split_csv "$ASPNETCORE_RUNTIME_VERSIONS"); do cleanup() {
aspNetCoreRuntimeVersions+=("$aspNetCoreRuntimeVersion") EXIT_CODE=$?
done set +e
if [[ -n "${TMP_DIR}" ]]; then
echo "Executing cleanup of tmp files"
rm -Rf "${TMP_DIR}"
fi
exit $EXIT_CODE
}
trap cleanup EXIT
# Fail fast in case of bad input to avoid unneccesary work # Get central common setting
# v1 of the .NET feature allowed specifying only a major version 'X' like '3' get_common_setting() {
# v2 removed this ability if [ "${common_settings_file_loaded}" != "true" ]; then
# - because install-dotnet.sh does not support it directly curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
# - because the previous behavior installed an old version like '3.0.103', not the newest version '3.1.426', which was counterintuitive common_settings_file_loaded=true
for version in "${versions[@]}"; do fi
if [[ "$version" =~ ^[0-9]+$ ]]; then if [ -f "/tmp/vsdc-settings.env" ]; then
err "Unsupported .NET SDK version '${version}'. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version." local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Add dotnet directory to PATH in bashrc/zshrc files if OVERRIDE_DEFAULT_VERSION=true.
updaterc() {
if [ "${UPDATE_RC}" = "true" ]; then
echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..."
if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then
echo -e "$1" >> /etc/bash.bashrc
fi
if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then
echo -e "$1" >> /etc/zsh/zshrc
fi
fi
}
# Run apt-get if needed.
apt_get_update_if_needed() {
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update
else
echo "Skipping apt-get update."
fi
}
# Check if packages are installed and installs them if not.
check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@"
fi
}
# Get appropriate architecture name for .NET binaries for the target OS
get_architecture_name_for_target_os() {
local architecture
architecture="$(uname -m)"
case $architecture in
x86_64) architecture="x64";;
aarch64 | armv8*) architecture="arm64";;
*) err "Architecture ${architecture} unsupported"; exit 1 ;;
esac
echo "${architecture}"
}
# Soft version matching that resolves a version for a given package in the *current apt-cache*
# Return value is stored in first argument (the unprocessed version)
apt_cache_package_and_version_soft_match() {
# Version
local version_variable_name="$1"
local requested_version=${!version_variable_name}
# Package Name
local package_variable_name="$2"
local partial_package_name=${!package_variable_name}
local package_name
# Exit on no match?
local exit_on_no_match="${3:-true}"
local major_minor_version
# Ensure we've exported useful variables
. /etc/os-release
local architecture="$(dpkg --print-architecture)"
major_minor_version="$(echo "${requested_version}" | cut -d "." --field=1,2)"
package_name="$(apt-cache search "${partial_package_name}-[0-9].[0-9]" | awk -F" - " '{print $1}' | grep -m 1 "${partial_package_name}-${major_minor_version}")"
dot_escaped="${requested_version//./\\.}"
dot_plus_escaped="${dot_escaped//+/\\+}"
# Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/
version_regex="^(.+:)?${dot_plus_escaped}([\\.\\+ ~:-]|$)"
set +e # Don't exit if finding version fails - handle gracefully
fuzzy_version="$(apt-cache madison ${package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${version_regex}")"
set -e
if [ -z "${fuzzy_version}" ]; then
echo "(!) No full or partial for package \"${partial_package_name}\" (resolved: \"${package_name}\") match found in apt-cache for \"${requested_version}\" on OS ${ID} ${VERSION_CODENAME} (${architecture})."
if $exit_on_no_match; then
echo "Available versions:"
apt-cache madison ${package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+'
exit 1 # Fail entire script
else
echo "Continuing to fallback method if available"
return 1;
fi
fi
# Globally assign fuzzy_version to this value
# Use this value as the return value of this function
declare -g ${version_variable_name}="=${fuzzy_version}"
echo "${version_variable_name} ${!version_variable_name}"
# Globally assign package to this value
# Use this value as the return value of this function
declare -g ${package_variable_name}="${package_name}"
echo "${package_variable_name} ${!package_variable_name}"
}
# Install .NET CLI using apt-get package installer
install_using_apt_from_microsoft_repo() {
local sdk_or_runtime="$1"
local dotnet_major_minor_version
export DOTNET_PACKAGE="dotnet-${sdk_or_runtime}"
# Install dependencies
check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr
# Import key safely and import Microsoft apt repo
get_common_setting MICROSOFT_GPG_KEYS_URI
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list
apt-get update -y
if [ "${DOTNET_VERSION}" = "latest" ] || [ "${DOTNET_VERSION}" = "lts" ]; then
DOTNET_VERSION=""
DOTNET_PACKAGE="${DOTNET_PACKAGE}-6.0"
else
# Sets DOTNET_VERSION and DOTNET_PACKAGE if matches found.
apt_cache_package_and_version_soft_match DOTNET_VERSION DOTNET_PACKAGE false
if [ "$?" != 0 ]; then
echo "Failed to find requested version."
return 1
fi
if [[ $(dotnet --version) == *"${DOTNET_VERSION}"* ]] ; then
echo "Dotnet version ${DOTNET_VERSION} is already installed"
return 1
fi
fi
echo "Installing '${DOTNET_PACKAGE}${DOTNET_VERSION}'..."
apt-get install -yq ${DOTNET_PACKAGE}${DOTNET_VERSION}
if [ "$?" != 0 ]; then
echo "Failed to complete apt install of ${DOTNET_PACKAGE}${DOTNET_VERSION}"
return 1
fi
}
install_using_default_apt_repo() {
DOTNET_PACKAGE="dotnet6"
apt_get_update_if_needed
if [[ "${DOTNET_VERSION}" = "latest" ]] || [[ "${DOTNET_VERSION}" = "lts" ]] || [[ ${DOTNET_VERSION} = "6"* ]]; then
if ! (apt-get install -yq ${DOTNET_PACKAGE}); then
echo "Failed to install 'dotnet6' package from default apt repo."
return 1
fi
else
echo "The provided dotnet version is not distributed in this distro's default apt repo."
return 1
fi
}
# Find and extract .NET binary download details based on user-requested version
# args:
# sdk_or_runtime $1
# exports:
# DOTNET_DOWNLOAD_URL
# DOTNET_DOWNLOAD_HASH
# DOTNET_DOWNLOAD_NAME
get_full_version_details() {
local sdk_or_runtime="$1"
local VERSION="$2"
local architecture
local dotnet_channel_version
local dotnet_releases_url
local dotnet_releases_json
local dotnet_latest_version
local dotnet_download_details
export DOTNET_DOWNLOAD_URL
export DOTNET_DOWNLOAD_HASH
export DOTNET_DOWNLOAD_NAME
# Set architecture variable to current user's architecture (x64 or ARM64).
architecture="$(get_architecture_name_for_target_os)"
# Set VERSION to empty string to ensure jq includes all .NET versions in reverse sort below
if [ "${VERSION}" = "latest" ]; then
VERSION=""
fi
dotnet_patchless_version="$(echo "${VERSION}" | cut -d "." --field=1,2)"
set +e
dotnet_channel_version="$(curl -s "${DOTNET_CDN_FEED_URI}/dotnet/release-metadata/releases-index.json" | jq -r --arg channel_version "${dotnet_patchless_version}" '[."releases-index"[]] | sort_by(."channel-version") | reverse | map( select(."channel-version" | startswith($channel_version))) | first | ."channel-version"')"
set -e
# Construct the releases URL using the official channel-version if one was found. Otherwise make a best-effort using the user input.
if [ -n "${dotnet_channel_version}" ] && [ "${dotnet_channel_version}" != "null" ]; then
dotnet_releases_url="${DOTNET_CDN_FEED_URI}/dotnet/release-metadata/${dotnet_channel_version}/releases.json"
else
dotnet_releases_url="${DOTNET_CDN_FEED_URI}/dotnet/release-metadata/${dotnet_patchless_version}/releases.json"
fi
set +e
dotnet_releases_json="$(curl -s "${dotnet_releases_url}")"
set -e
if [ -n "${dotnet_releases_json}" ] && [[ ! "${dotnet_releases_json}" = *"Error"* ]]; then
dotnet_latest_version="$(echo "${dotnet_releases_json}" | jq -r --arg sdk_or_runtime "${sdk_or_runtime}" '."latest-\($sdk_or_runtime)"')"
# If user-specified version has 2 or more dots, use it as is. Otherwise use latest version.
if [ "$(echo "${VERSION}" | grep -o "\." | wc -l)" -lt "2" ]; then
VERSION="${dotnet_latest_version}"
fi
dotnet_download_details="$(echo "${dotnet_releases_json}" | jq -r --arg sdk_or_runtime "${sdk_or_runtime}" --arg dotnet_version "${VERSION}" --arg arch "${architecture}" '.releases[]."\($sdk_or_runtime)" | select(.version==$dotnet_version) | .files[] | select(.name=="dotnet-\($sdk_or_runtime)-linux-\($arch).tar.gz")')"
if [ -n "${dotnet_download_details}" ]; then
echo "Found .NET binary version ${VERSION}"
DOTNET_DOWNLOAD_URL="$(echo "${dotnet_download_details}" | jq -r '.url')"
DOTNET_DOWNLOAD_HASH="$(echo "${dotnet_download_details}" | jq -r '.hash')"
DOTNET_DOWNLOAD_NAME="$(echo "${dotnet_download_details}" | jq -r '.name')"
else
err "Unable to find .NET binary for version ${VERSION}"
exit 1
fi
else
err "Unable to find .NET release details for version ${VERSION} at ${dotnet_releases_url}"
exit 1 exit 1
fi fi
done }
for version in "${dotnetRuntimeVersions[@]}"; do # Install .NET CLI using the .NET releases url
if [[ "$version" =~ ^[0-9]+$ ]]; then install_using_dotnet_releases_url() {
err "Unsupported .NET Runtime version '${version}'. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version." local sdk_or_runtime="$1"
local VERSION="$2"
# Check listed package dependecies and install them if they are not already installed.
# NOTE: icu-devtools is a small package with similar dependecies to .NET.
# It will install the appropriate dependencies based on the OS:
# - libgcc-s1 OR libgcc1 depending on OS
# - the latest libicuXX depending on OS (eg libicu57 for stretch)
# - also installs libc6 and libstdc++6 which are required by .NET
check_packages curl ca-certificates tar jq icu-devtools libgssapi-krb5-2 zlib1g
# Starting with Ubuntu 22.04 (jammy), libssl1.1 does not ship with the OS anymore.
if [[ "${DOTNET_VERSION_CODENAMES_REQUIRE_OLDER_LIBSSL_1}" = *"${VERSION_CODENAME}"* ]]; then
check_packages libssl1.1
else
check_packages libssl3.0
fi
get_full_version_details "${sdk_or_runtime}" "${VERSION}"
DOTNET_INSTALL_PATH="${TARGET_DOTNET_ROOT}/${VERSION}"
if [ -d "${DOTNET_INSTALL_PATH}" ]; then
echo "(!) Dotnet version ${VERSION} already exists."
exit 1 exit 1
fi fi
done # exports DOTNET_DOWNLOAD_URL, DOTNET_DOWNLOAD_HASH, DOTNET_DOWNLOAD_NAME
echo "DOWNLOAD LINK: ${DOTNET_DOWNLOAD_URL}"
for version in "${aspNetCoreRuntimeVersions[@]}"; do # Setup the access group and add the user to it.
if [[ "$version" =~ ^[0-9]+$ ]]; then umask 0002
err "Unsupported ASP.NET Core Runtime version '${version}'. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version." if ! cat /etc/group | grep -e "^${ACCESS_GROUP}:" > /dev/null 2>&1; then
groupadd -r "${ACCESS_GROUP}"
fi
usermod -a -G "${ACCESS_GROUP}" "${USERNAME}"
# Download the .NET binaries.
echo "DOWNLOADING BINARY..."
TMP_DIR="/tmp/dotnetinstall"
mkdir -p "${TMP_DIR}"
curl -sSL "${DOTNET_DOWNLOAD_URL}" -o "${TMP_DIR}/${DOTNET_DOWNLOAD_NAME}"
# Get checksum from .NET CLI blob storage using the runtime version and
# run validation (sha512sum) of checksum against the expected checksum hash.
echo "VERIFY CHECKSUM"
cd "${TMP_DIR}"
echo "${DOTNET_DOWNLOAD_HASH} *${DOTNET_DOWNLOAD_NAME}" | sha512sum -c -
# Extract binaries and add to path.
mkdir -p "${DOTNET_INSTALL_PATH}"
echo "Extract Binary to ${DOTNET_INSTALL_PATH}"
tar -xzf "${TMP_DIR}/${DOTNET_DOWNLOAD_NAME}" -C "${DOTNET_INSTALL_PATH}" --strip-components=1
CURRENT_DIR="${TARGET_DOTNET_ROOT}/current"
if [[ ! -d "${CURRENT_DIR}" ]]; then
ln -s "${DOTNET_INSTALL_PATH}" "${CURRENT_DIR}"
fi
# Give write permissions to the user.
chown -R "${USERNAME}:${USERNAME}" "${CURRENT_DIR}"
chmod g+r+w+s "${CURRENT_DIR}"
chmod -R g+r+w "${CURRENT_DIR}"
if [[ "${OVERRIDE_DEFAULT_VERSION}" = "true" ]]; then
if [[ $(ls -l ${CURRENT_DIR}) != *"-> ${DOTNET_INSTALL_PATH}"* ]] ; then
rm "${CURRENT_DIR}"
ln -s "${DOTNET_INSTALL_PATH}" "${CURRENT_DIR}"
fi
fi
updaterc "if [[ \"\${PATH}\" != *\"${CURRENT_DIR}\"* ]]; then export PATH=${CURRENT_DIR}:\${PATH}; fi"
}
###########################
# Start .NET installation
###########################
export DEBIAN_FRONTEND=noninteractive
. /etc/os-release
architecture="$(dpkg --print-architecture)"
# Dotnet 3.1 and 5 are not supported on Ubuntu 22.04 (jammy)+,
# due to lack of libssl3.0 support.
# See: https://github.com/microsoft/vscode-dev-containers/issues/1458#issuecomment-1135077775
# NOTE: This will only guard against installation of the dotnet versions we propose via 'features'.
# The user can attempt to install any other version at their own risk.
if [[ "${DOTNET_VERSION}" = "3"* ]] || [[ "${DOTNET_VERSION}" = "5"* ]]; then
if [[ ! "${DOTNET_VERSION_CODENAMES_REQUIRE_OLDER_LIBSSL_1}" = *"${VERSION_CODENAME}"* ]]; then
err "Dotnet ${DOTNET_VERSION} is not supported on Ubuntu ${VERSION_CODENAME} due to a change in the 'libssl' dependency across distributions.\n Please upgrade your version of dotnet, or downgrade your OS version."
exit 1 exit 1
fi fi
done fi
# Install .NET versions and dependencies # Determine if the user wants to download .NET Runtime only, or .NET SDK & Runtime
# icu-devtools includes dependencies for .NET # and set the appropriate variables.
check_packages wget ca-certificates icu-devtools if [ "${DOTNET_RUNTIME_ONLY}" = "true" ]; then
DOTNET_SDK_OR_RUNTIME="runtime"
elif [ "${DOTNET_RUNTIME_ONLY}" = "false" ]; then
DOTNET_SDK_OR_RUNTIME="sdk"
else
err "Expected true for installing dotnet Runtime only or false for installing SDK and Runtime. Received ${DOTNET_RUNTIME_ONLY}."
exit 1
fi
for version in "${versions[@]}"; do # Install the .NET CLI
install_sdk "$version" echo "(*) Installing .NET CLI..."
done
for version in "${dotnetRuntimeVersions[@]}"; do CHANGE_OWNERSHIP="false"
install_runtime "dotnet" "$version" if [[ "${DOTNET_ARCHIVE_ARCHITECTURES}" = *"${architecture}"* ]] && [[ "${DOTNET_ARCHIVE_VERSION_CODENAMES}" = *"${VERSION_CODENAME}"* ]] && [[ "${INSTALL_USING_APT}" = "true" ]]; then
done echo "Detected ${VERSION_CODENAME} on ${architecture}. Attempting to install dotnet from apt"
for version in "${aspNetCoreRuntimeVersions[@]}"; do install_using_default_apt_repo || install_using_apt_from_microsoft_repo "${DOTNET_SDK_OR_RUNTIME}"
install_runtime "aspnetcore" "$version" if [ "$?" != 0 ]; then
done echo "Could not install requested version from apt on current distribution."
exit 1
fi
else
if [[ "${INSTALL_USING_APT}" = "false" ]]; then
echo "Installing dotnet from releases url"
else
echo "Attempting to install dotnet from releases url"
fi
install_using_dotnet_releases_url "${DOTNET_SDK_OR_RUNTIME}" "${DOTNET_VERSION}"
CHANGE_OWNERSHIP="true"
fi
# Clean up # Additional dotnet versions to be installed but not be set as default.
rm -rf /var/lib/apt/lists/* if [ ! -z "${ADDITIONAL_VERSIONS}" ]; then
rm -rf scripts OLDIFS=$IFS
IFS=","
read -a additional_versions <<< "$ADDITIONAL_VERSIONS"
for version in "${additional_versions[@]}"; do
OVERRIDE_DEFAULT_VERSION="false"
install_using_dotnet_releases_url "${DOTNET_SDK_OR_RUNTIME}" "${version}"
done
IFS=$OLDIFS
fi
if [ "${CHANGE_OWNERSHIP}" = "true" ]; then
if ! cat /etc/group | grep -e "^dotnet:" > /dev/null 2>&1; then
groupadd -r dotnet
fi
usermod -a -G dotnet "${USERNAME}"
chown -R "${USERNAME}:dotnet" "${TARGET_DOTNET_ROOT}"
chmod -R g+r+w "${TARGET_DOTNET_ROOT}"
find "${TARGET_DOTNET_ROOT}" -type d | xargs -n 1 chmod g+s
fi
echo "Done!" echo "Done!"

View file

@ -1,119 +0,0 @@
#!/bin/bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
# Docs: https://github.com/devcontainers/features/tree/main/src/dotnet
# Maintainer: The Dev Container spec maintainers
DOTNET_SCRIPTS=$(dirname "${BASH_SOURCE[0]}")
DOTNET_INSTALL_SCRIPT="$DOTNET_SCRIPTS/vendor/dotnet-install.sh"
DOTNET_INSTALL_DIR='/usr/share/dotnet'
# Prints the latest dotnet version in the specified channel
# Usage: fetch_latest_version_in_channel <channel> [<runtime>]
# Example: fetch_latest_version_in_channel "LTS"
# Example: fetch_latest_version_in_channel "6.0" "dotnet"
# Example: fetch_latest_version_in_channel "6.0" "aspnetcore"
fetch_latest_version_in_channel() {
local channel="$1"
local runtime="$2"
if [ "$runtime" = "dotnet" ]; then
wget -qO- "https://dotnetcli.azureedge.net/dotnet/Runtime/$channel/latest.version"
elif [ "$runtime" = "aspnetcore" ]; then
wget -qO- "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$channel/latest.version"
else
wget -qO- "https://dotnetcli.azureedge.net/dotnet/Sdk/$channel/latest.version"
fi
}
# Prints the latest dotnet version
# Usage: fetch_latest_version [<runtime>]
# Example: fetch_latest_version
# Example: fetch_latest_version "dotnet"
# Example: fetch_latest_version "aspnetcore"
fetch_latest_version() {
local runtime="$1"
local sts_version
local lts_version
sts_version=$(fetch_latest_version_in_channel "STS" "$runtime")
lts_version=$(fetch_latest_version_in_channel "LTS" "$runtime")
if [[ "$sts_version" > "$lts_version" ]]; then
echo "$sts_version"
else
echo "$lts_version"
fi
}
# Installs a version of the .NET SDK
# Usage: install_sdk <version>
install_sdk() {
local inputVersion="$1"
local version=""
local channel=""
if [[ "$inputVersion" == "latest" ]]; then
# Fetch the latest version manually, because dotnet-install.sh does not support it directly
version=$(fetch_latest_version)
elif [[ "$inputVersion" == "lts" ]]; then
# When user input is 'lts'
# Then version=latest, channel=LTS
version="latest"
channel="LTS"
elif [[ "$inputVersion" =~ ^[0-9]+\.[0-9]+$ ]]; then
# When user input is form 'A.B' like '3.1'
# Then version=latest, channel=3.1
version="latest"
channel="$inputVersion"
elif [[ "$inputVersion" =~ ^[0-9]+\.[0-9]+\.[0-9]xx$ ]]; then
# When user input is form 'A.B.Cxx' like '6.0.4xx'
# Then version=latest, channel=6.0.4xx
version="latest"
channel="$inputVersion"
else
# Assume version is an exact version string like '6.0.413' or '8.0.100-rc.2.23425.18'
version="$inputVersion"
fi
# Currently this script does not make it possible to qualify the version, 'GA' is always implied
echo "Executing $DOTNET_INSTALL_SCRIPT --version $version --channel $channel --install-dir $DOTNET_INSTALL_DIR --no-path"
"$DOTNET_INSTALL_SCRIPT" \
--version "$version" \
--channel "$channel" \
--install-dir "$DOTNET_INSTALL_DIR" \
--no-path
}
# Installs a version of the .NET Runtime
# Usage: install_runtime <runtime> <version>
install_runtime() {
local runtime="$1"
local inputVersion="$2"
local version=""
local channel=""
if [[ "$inputVersion" == "latest" ]]; then
# Fetch the latest version manually, because dotnet-install.sh does not support it directly
version=$(fetch_latest_version "$runtime")
elif [[ "$inputVersion" == "lts" ]]; then
# When user input is 'lts'
# Then version=latest, channel=LTS
version="latest"
channel="LTS"
elif [[ "$inputVersion" =~ ^[0-9]+\.[0-9]+$ ]]; then
# When user input is form 'A.B' like '3.1'
# Then version=latest, channel=3.1
version="latest"
channel="$inputVersion"
else
# Assume version is an exact version string like '6.0.21' or '8.0.0-preview.7.23375.6'
version="$inputVersion"
fi
echo "Executing $DOTNET_INSTALL_SCRIPT --runtime $runtime --version $version --channel $channel --install-dir $DOTNET_INSTALL_DIR --no-path"
"$DOTNET_INSTALL_SCRIPT" \
--runtime "$runtime" \
--version "$version" \
--channel "$channel" \
--install-dir "$DOTNET_INSTALL_DIR" \
--no-path
}

View file

@ -1,16 +0,0 @@
#!/bin/bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
# Docs: https://github.com/devcontainers/features/tree/main/src/dotnet
# Maintainer: The Dev Container spec maintainers
#
# Run this script to replace dotnet-install.sh with the latest and greatest available version
#
DOTNET_SCRIPTS=$(dirname "${BASH_SOURCE[0]}")
DOTNET_INSTALL_SCRIPT="$DOTNET_SCRIPTS/vendor/dotnet-install.sh"
wget https://dot.net/v1/dotnet-install.sh -O "$DOTNET_INSTALL_SCRIPT"
chmod +x "$DOTNET_INSTALL_SCRIPT"

View file

@ -1,42 +0,0 @@
#!/bin/bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
# Docs: https://github.com/devcontainers/features/tree/main/src/dotnet
# Maintainer: The Dev Container spec maintainers
# Removes leading and trailing whitespace from an input string
# Usage: trim_whitespace <text>
trim_whitespace() {
text="$1"
# Remove leading spaces
while [ "${text:0:1}" == " " ]; do
text="${text:1}"
done
# Remove trailing spaces
while [ "${text: -1}" == " " ]; do
text="${text:0:-1}"
done
echo "$text"
}
# Splits comma-separated values into an array while ignoring empty entries
# Usage: split_csv <comma-separated-values>
split_csv() {
local -a values=()
while IFS="," read -ra entries; do
for entry in "${entries[@]}"; do
entry="$(trim_whitespace "$entry")"
if [ -n "$entry" ]; then
values+=("$entry")
fi
done
done <<< "$1"
echo "${values[@]}"
}

View file

@ -1,27 +0,0 @@
### **IMPORTANT NOTE**
Scripts in this directory are sourced externally and not maintained by the Dev Container spec maintainers. Do not make changes directly as they might be overwritten at any moment.
## dotnet-install.sh
`dotnet-install.sh` is a copy of <https://dot.net/v1/dotnet-install.sh>. ([Script reference](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script))
Quick options reminder for `dotnet-install.sh`:
- `--version`: `"latest"` (default) or an exact version in the form A.B.C like `"6.0.413"`
- `--channel`: `"LTS"` (default), `"STS"`, a two-part version in the form A.B like `"6.0"` or three-part form A.B.Cxx like `"6.0.1xx"`
- `--quality`: `"daily"`, `"preview"` or `"GA"`
- The channel option is only used when version is 'latest' because an exact version overrides the channel option
- The quality option is only used when channel is 'A.B' or 'A.B.Cxx' because it can't be used with STS or LTS
Examples
```
dotnet-install.sh [--version latest] [--channel LTS]
dotnet-install.sh [--version latest] --channel STS
dotnet-install.sh [--version latest] --channel 6.0 [--quality GA]
dotnet-install.sh [--version latest] --channel 6.0.4xx [--quality GA]
dotnet-install.sh [--version latest] --channel 8.0 --quality preview
dotnet-install.sh [--version latest] --channel 8.0 --quality daily
dotnet-install.sh --version 6.0.413
```

File diff suppressed because it is too large Load diff

View file

@ -1,7 +0,0 @@
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@ Installs Git Large File Support (Git LFS) along with needed dependencies. Useful
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/git-lfs:1": {} "ghcr.io/devcontainers/features/git-lfs:1": {
"version": "latest"
}
} }
``` ```
@ -16,17 +18,9 @@ Installs Git Large File Support (Git LFS) along with needed dependencies. Useful
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select version of Git LFS to install | string | latest | | version | Select version of Git LFS to install | string | latest |
| autoPull | Automatically pull LFS files when creating the container. When false, running 'git lfs pull' in the container will have the same effect. | boolean | true |
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/git-lfs/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/git-lfs/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,27 +1,17 @@
{ {
"id": "git-lfs", "id": "git-lfs",
"version": "1.1.1", "version": "1.0.0",
"name": "Git Large File Support (LFS)", "name": "Git Large File Support (LFS)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/git-lfs",
"description": "Installs Git Large File Support (Git LFS) along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like git and curl.", "description": "Installs Git Large File Support (Git LFS) along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like git and curl.",
"options": { "options": {
"version": { "version": {
"type": "string", "type": "string",
"proposals": [ "enum": [
"latest", "latest",
"none" "none"
], ],
"default": "latest", "default": "latest",
"description": "Select version of Git LFS to install" "description": "Select version of Git LFS to install"
},
"autoPull": {
"type": "boolean",
"default": true,
"description": "Automatically pull LFS files when creating the container. When false, running 'git lfs pull' in the container will have the same effect."
} }
}, }
"postCreateCommand": "/usr/local/share/pull-git-lfs-artifacts.sh",
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
} }

View file

@ -8,27 +8,37 @@
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The VS Code and Codespaces Teams
GIT_LFS_VERSION=${VERSION:-"latest"} GIT_LFS_VERSION=${VERSION:-"latest"}
AUTO_PULL=${AUTOPULL:="true"}
GIT_LFS_ARCHIVE_GPG_KEY_URI="https://packagecloud.io/github/git-lfs/gpgkey" GIT_LFS_ARCHIVE_GPG_KEY_URI="https://packagecloud.io/github/git-lfs/gpgkey"
GIT_LFS_ARCHIVE_ARCHITECTURES="amd64 arm64" GIT_LFS_ARCHIVE_ARCHITECTURES="amd64 arm64"
GIT_LFS_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal jammy" GIT_LFS_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal jammy"
GIT_LFS_CHECKSUM_GPG_KEYS="0x88ace9b29196305ba9947552f1ba225c0223b187 0x86cd3297749375bcf8206715f54fe648088335a9 0xaa3b3450295830d2de6db90caba67be5a5795889" GIT_LFS_CHECKSUM_GPG_KEYS="0x88ace9b29196305ba9947552f1ba225c0223b187 0x86cd3297749375bcf8206715f54fe648088335a9 0xaa3b3450295830d2de6db90caba67be5a5795889"
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
fi fi
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Figure out correct version of a three part version number is not passed # Figure out correct version of a three part version number is not passed
find_version_from_git_tags() { find_version_from_git_tags() {
local variable_name=$1 local variable_name=$1
@ -65,9 +75,11 @@ find_version_from_git_tags() {
# Import the specified key in a variable name passed in as # Import the specified key in a variable name passed in as
receive_gpg_keys() { receive_gpg_keys() {
get_common_setting $1
local keys=${!1} local keys=${!1}
get_common_setting GPG_KEY_SERVERS true
# Use a temporary location for gpg keys to avoid polluting image # Use a temporary locaiton for gpg keys to avoid polluting image
export GNUPGHOME="/tmp/tmp-gnupg" export GNUPGHOME="/tmp/tmp-gnupg"
mkdir -p ${GNUPGHOME} mkdir -p ${GNUPGHOME}
chmod 700 ${GNUPGHOME} chmod 700 ${GNUPGHOME}
@ -93,18 +105,21 @@ receive_gpg_keys() {
fi fi
} }
apt_get_update() # Function to run apt-get if needed
apt_get_update_if_needed()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..." echo "Running apt-get update..."
apt-get update -y apt-get update
else
echo "Skipping apt-get update."
fi fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -118,6 +133,7 @@ install_using_apt() {
version_suffix="" version_suffix=""
fi fi
# Install # Install
get_common_setting GIT_LFS_ARCHIVE_GPG_KEY_URI
curl -sSL "${GIT_LFS_ARCHIVE_GPG_KEY_URI}" | gpg --dearmor > /usr/share/keyrings/gitlfs-archive-keyring.gpg curl -sSL "${GIT_LFS_ARCHIVE_GPG_KEY_URI}" | gpg --dearmor > /usr/share/keyrings/gitlfs-archive-keyring.gpg
echo -e "deb [arch=${architecture} signed-by=/usr/share/keyrings/gitlfs-archive-keyring.gpg] https://packagecloud.io/github/git-lfs/${ID} ${VERSION_CODENAME} main\ndeb-src [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gitlfs-archive-keyring.gpg] https://packagecloud.io/github/git-lfs/${ID} ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/git-lfs.list echo -e "deb [arch=${architecture} signed-by=/usr/share/keyrings/gitlfs-archive-keyring.gpg] https://packagecloud.io/github/git-lfs/${ID} ${VERSION_CODENAME} main\ndeb-src [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gitlfs-archive-keyring.gpg] https://packagecloud.io/github/git-lfs/${ID} ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/git-lfs.list
@ -167,7 +183,8 @@ export DEBIAN_FRONTEND=noninteractive
. /etc/os-release . /etc/os-release
check_packages curl ca-certificates gnupg2 dirmngr apt-transport-https check_packages curl ca-certificates gnupg2 dirmngr apt-transport-https
if ! type git > /dev/null 2>&1; then if ! type git > /dev/null 2>&1; then
check_packages git apt_get_update_if_needed
apt-get -y install --no-install-recommends git
fi fi
if [ "${ID}" = "debian" ]; then if [ "${ID}" = "debian" ]; then
check_packages debian-archive-keyring check_packages debian-archive-keyring
@ -187,38 +204,4 @@ if [ "${use_github}" = "true" ]; then
install_using_github install_using_github
fi fi
# --- Generate a 'pull-git-lfs-artifacts.sh' script to be executed by the 'postCreateCommand' lifecycle hook echo "Done!"
PULL_GIT_LFS_SCRIPT_PATH="/usr/local/share/pull-git-lfs-artifacts.sh"
tee "$PULL_GIT_LFS_SCRIPT_PATH" > /dev/null \
<< EOF
#!/bin/sh
set -e
AUTO_PULL=${AUTO_PULL}
EOF
tee -a "$PULL_GIT_LFS_SCRIPT_PATH" > /dev/null \
<< 'EOF'
echo "Fetching git lfs artifacts..."
if [ "${AUTO_PULL}" != "true" ]; then
echo "(!) Skipping 'git lfs pull' because 'autoPull' is not set to 'true'"
exit 0
fi
# Check if repo is a git lfs repo.
if ! git lfs ls-files > /dev/null 2>&1; then
echo "(!) Skipping automatic 'git lfs pull' because no git lfs files were detected"
exit 0
fi
git lfs pull
EOF
chmod 755 "$PULL_GIT_LFS_SCRIPT_PATH"
# Clean up
rm -rf /var/lib/apt/lists/*
echo "Done!"

View file

@ -1,7 +0,0 @@
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@ Install an up-to-date version of Git, built from source as needed. Useful for wh
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/git:1": {} "ghcr.io/devcontainers/features/git:1": {
"version": "latest"
}
} }
``` ```
@ -20,13 +22,6 @@ Install an up-to-date version of Git, built from source as needed. Useful for wh
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/git/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/git/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,8 +1,7 @@
{ {
"id": "git", "id": "git",
"version": "1.1.6", "version": "1.0.0",
"name": "Git (from source)", "name": "Git (from source)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/git",
"description": "Install an up-to-date version of Git, built from source as needed. Useful for when you want the latest and greatest features. Auto-detects latest stable version and installs needed dependencies.", "description": "Install an up-to-date version of Git, built from source as needed. Useful for when you want the latest and greatest features. Auto-detects latest stable version and installs needed dependencies.",
"options": { "options": {
"version": { "version": {
@ -19,8 +18,5 @@
"default": true, "default": true,
"description": "Install from PPA if available" "description": "Install from PPA if available"
} }
}, }
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
} }

View file

@ -7,35 +7,48 @@
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/git-from-src.md # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/git-from-src.md
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The VS Code and Codespaces Teams
GIT_VERSION=${VERSION} # 'system' checks the base image first, else installs 'latest' GIT_VERSION=${VERSION:-"latest"} # 'system' checks the base image first, else installs 'latest'
USE_PPA_IF_AVAILABLE=${PPA} USE_PPA_IF_AVAILABLE=${PPA:-"false"}
GIT_CORE_PPA_ARCHIVE_GPG_KEY=E1DD270288B4E6030699E45FA1715D88E1DF1F24 GIT_CORE_PPA_ARCHIVE_GPG_KEY=E1DD270288B4E6030699E45FA1715D88E1DF1F24
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
fi fi
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Import the specified key in a variable name passed in as # Import the specified key in a variable name passed in as
receive_gpg_keys() { receive_gpg_keys() {
get_common_setting $1
local keys=${!1} local keys=${!1}
get_common_setting GPG_KEY_SERVERS true
local keyring_args="" local keyring_args=""
if [ ! -z "$2" ]; then if [ ! -z "$2" ]; then
mkdir -p "$(dirname \"$2\")" mkdir -p "$(dirname \"$2\")"
keyring_args="--no-default-keyring --keyring $2" keyring_args="--no-default-keyring --keyring $2"
fi fi
# Use a temporary location for gpg keys to avoid polluting image # Use a temporary locaiton for gpg keys to avoid polluting image
export GNUPGHOME="/tmp/tmp-gnupg" export GNUPGHOME="/tmp/tmp-gnupg"
mkdir -p ${GNUPGHOME} mkdir -p ${GNUPGHOME}
chmod 700 ${GNUPGHOME} chmod 700 ${GNUPGHOME}
@ -61,18 +74,21 @@ receive_gpg_keys() {
fi fi
} }
apt_get_update() # Function to run apt-get if needed
apt_get_update_if_needed()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..." echo "Running apt-get update..."
apt-get update -y apt-get update
else
echo "Skipping apt-get update."
fi fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -85,16 +101,12 @@ export DEBIAN_FRONTEND=noninteractive
# If the os provided version is "good enough", just install that. # If the os provided version is "good enough", just install that.
if [ ${GIT_VERSION} = "os-provided" ] || [ ${GIT_VERSION} = "system" ]; then if [ ${GIT_VERSION} = "os-provided" ] || [ ${GIT_VERSION} = "system" ]; then
if type git > /dev/null 2>&1; then if type git > /dev/null 2>&1; then
echo "Detected existing system install: $(git version)" echo "Detected existing system install: $(git version)"
# Clean up exit 0
rm -rf /var/lib/apt/lists/*
exit 0
fi fi
echo "Installing git from OS apt repository" echo "Installing git from OS apt repository"
check_packages git check_packages git
# Clean up
rm -rf /var/lib/apt/lists/*
exit 0 exit 0
fi fi
@ -107,7 +119,6 @@ if ([ "${GIT_VERSION}" = "latest" ] || [ "${GIT_VERSION}" = "lts" ] || [ "${GIT_
apt-get update apt-get update
apt-get -y install --no-install-recommends git apt-get -y install --no-install-recommends git
rm -rf "/tmp/tmp-gnupg" rm -rf "/tmp/tmp-gnupg"
rm -rf /var/lib/apt/lists/*
exit 0 exit 0
fi fi
@ -131,21 +142,10 @@ if [ "$(echo "${GIT_VERSION}" | grep -o '\.' | wc -l)" != "2" ]; then
fi fi
fi fi
check_packages libpcre2-dev
if [ "${VERSION_CODENAME}" = "focal" ] || [ "${VERSION_CODENAME}" = "bullseye" ]; then
check_packages libpcre2-posix2
elif [ "${VERSION_CODENAME}" = "bionic" ] || [ "${VERSION_CODENAME}" = "buster" ]; then
check_packages libpcre2-posix0
else
check_packages libpcre2-posix3
fi
echo "Downloading source for ${GIT_VERSION}..." echo "Downloading source for ${GIT_VERSION}..."
curl -sL https://github.com/git/git/archive/v${GIT_VERSION}.tar.gz | tar -xzC /tmp 2>&1 curl -sL https://github.com/git/git/archive/v${GIT_VERSION}.tar.gz | tar -xzC /tmp 2>&1
echo "Building..." echo "Building..."
cd /tmp/git-${GIT_VERSION} cd /tmp/git-${GIT_VERSION}
make -s USE_LIBPCRE=YesPlease prefix=/usr/local sysconfdir=/etc all && make -s USE_LIBPCRE=YesPlease prefix=/usr/local sysconfdir=/etc install 2>&1 make -s prefix=/usr/local all && make -s prefix=/usr/local install 2>&1
rm -rf /tmp/git-${GIT_VERSION} rm -rf /tmp/git-${GIT_VERSION}
rm -rf /var/lib/apt/lists/*
echo "Done!" echo "Done!"

View file

@ -1,7 +0,0 @@
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@ Installs the GitHub CLI. Auto-detects latest version and installs needed depende
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/github-cli:1": {} "ghcr.io/devcontainers/features/github-cli:1": {
"version": "latest"
}
} }
``` ```
@ -16,17 +18,9 @@ Installs the GitHub CLI. Auto-detects latest version and installs needed depende
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select version of the GitHub CLI, if not latest. | string | latest | | version | Select version of the GitHub CLI, if not latest. | string | latest |
| installDirectlyFromGitHubRelease | - | boolean | true |
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/github-cli/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/github-cli/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,8 +1,7 @@
{ {
"id": "github-cli", "id": "github-cli",
"version": "1.0.11", "version": "1.0.0",
"name": "GitHub CLI", "name": "GitHub CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/github-cli",
"description": "Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.", "description": "Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.",
"options": { "options": {
"version": { "version": {
@ -13,15 +12,6 @@
], ],
"default": "latest", "default": "latest",
"description": "Select version of the GitHub CLI, if not latest." "description": "Select version of the GitHub CLI, if not latest."
},
"installDirectlyFromGitHubRelease": {
"type": "boolean",
"default": true
} }
}, }
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils",
"ghcr.io/devcontainers/features/git"
]
} }

View file

@ -8,34 +8,41 @@
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The VS Code and Codespaces Teams
CLI_VERSION=${VERSION:-"latest"} CLI_VERSION=${VERSION:-"latest"}
INSTALL_DIRECTLY_FROM_GITHUB_RELEASE=${INSTALLDIRECTLYFROMGITHUBRELEASE:-"true"}
GITHUB_CLI_ARCHIVE_GPG_KEY=23F3D4EA75716059 GITHUB_CLI_ARCHIVE_GPG_KEY=C99B11DEB97541F0
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
fi fi
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Import the specified key in a variable name passed in as # Import the specified key in a variable name passed in as
receive_gpg_keys() { receive_gpg_keys() {
get_common_setting $1
local keys=${!1} local keys=${!1}
local keyring_args="" get_common_setting GPG_KEY_SERVERS true
if [ ! -z "$2" ]; then
keyring_args="--no-default-keyring --keyring $2"
fi
# Use a temporary location for gpg keys to avoid polluting image # Use a temporary locaiton for gpg keys to avoid polluting image
export GNUPGHOME="/tmp/tmp-gnupg" export GNUPGHOME="/tmp/tmp-gnupg"
mkdir -p ${GNUPGHOME} mkdir -p ${GNUPGHOME}
chmod 700 ${GNUPGHOME} chmod 700 ${GNUPGHOME}
@ -47,7 +54,7 @@ receive_gpg_keys() {
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ]; until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
do do
echo "(*) Downloading GPG key..." echo "(*) Downloading GPG key..."
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true" ( echo "${keys}" | xargs -n 1 gpg --recv-keys) 2>&1 && gpg_ok="true"
if [ "${gpg_ok}" != "true" ]; then if [ "${gpg_ok}" != "true" ]; then
echo "(*) Failed getting key, retring in 10s..." echo "(*) Failed getting key, retring in 10s..."
(( retry_count++ )) (( retry_count++ ))
@ -61,22 +68,6 @@ receive_gpg_keys() {
fi fi
} }
apt_get_update()
{
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update -y
fi
}
# Checks if packages are installed and installs them if not
check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update
apt-get -y install --no-install-recommends "$@"
fi
}
# Figure out correct version of a three part version number is not passed # Figure out correct version of a three part version number is not passed
find_version_from_git_tags() { find_version_from_git_tags() {
local variable_name=$1 local variable_name=$1
@ -111,71 +102,59 @@ find_version_from_git_tags() {
echo "${variable_name}=${!variable_name}" echo "${variable_name}=${!variable_name}"
} }
# Use semver logic to decrement a version number then look for the closest match # Import the specified key in a variable name passed in as
find_prev_version_from_git_tags() { receive_gpg_keys() {
local variable_name=$1 get_common_setting $1
local current_version=${!variable_name} local keys=${!1}
local repository=$2 get_common_setting GPG_KEY_SERVERS true
# Normally a "v" is used before the version number, but support alternate cases local keyring_args=""
local prefix=${3:-"tags/v"} if [ ! -z "$2" ]; then
# Some repositories use "_" instead of "." for version number part separation, support that keyring_args="--no-default-keyring --keyring $2"
local separator=${4:-"."}
# Some tools release versions that omit the last digit (e.g. go)
local last_part_optional=${5:-"false"}
# Some repositories may have tags that include a suffix (e.g. actions/node-versions)
local version_suffix_regex=$6
# Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios.
set +e
major="$(echo "${current_version}" | grep -oE '^[0-9]+' || echo '')"
minor="$(echo "${current_version}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')"
breakfix="$(echo "${current_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')"
if [ "${minor}" = "0" ] && [ "${breakfix}" = "0" ]; then
((major=major-1))
declare -g ${variable_name}="${major}"
# Look for latest version from previous major release
find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}"
# Handle situations like Go's odd version pattern where "0" releases omit the last part
elif [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then
((minor=minor-1))
declare -g ${variable_name}="${major}.${minor}"
# Look for latest version from previous minor release
find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}"
else
((breakfix=breakfix-1))
if [ "${breakfix}" = "0" ] && [ "${last_part_optional}" = "true" ]; then
declare -g ${variable_name}="${major}.${minor}"
else
declare -g ${variable_name}="${major}.${minor}.${breakfix}"
fi
fi
set -e
}
# Fall back on direct download if no apt package exists
# Fetches .deb file to be installed with dpkg
install_deb_using_github() {
check_packages wget
arch=$(dpkg --print-architecture)
find_version_from_git_tags CLI_VERSION https://github.com/cli/cli
cli_filename="gh_${CLI_VERSION}_linux_${arch}.deb"
mkdir -p /tmp/ghcli
pushd /tmp/ghcli
wget https://github.com/cli/cli/releases/download/v${CLI_VERSION}/${cli_filename}
exit_code=$?
set -e
if [ "$exit_code" != "0" ]; then
# Handle situation where git tags are ahead of what was is available to actually download
echo "(!) github-cli version ${CLI_VERSION} failed to download. Attempting to fall back one version to retry..."
find_prev_version_from_git_tags CLI_VERSION https://github.com/cli/cli
wget https://github.com/cli/cli/releases/download/v${CLI_VERSION}/${cli_filename}
fi fi
dpkg -i /tmp/ghcli/${cli_filename} # Use a temporary locaiton for gpg keys to avoid polluting image
popd export GNUPGHOME="/tmp/tmp-gnupg"
rm -rf /tmp/ghcli mkdir -p ${GNUPGHOME}
chmod 700 ${GNUPGHOME}
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
# GPG key download sometimes fails for some reason and retrying fixes it.
local retry_count=0
local gpg_ok="false"
set +e
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
do
echo "(*) Downloading GPG key..."
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true"
if [ "${gpg_ok}" != "true" ]; then
echo "(*) Failed getting key, retring in 10s..."
(( retry_count++ ))
sleep 10s
fi
done
set -e
if [ "${gpg_ok}" = "false" ]; then
echo "(!) Failed to get gpg key."
exit 1
fi
}
# Function to run apt-get if needed
apt_get_update_if_needed()
{
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update
else
echo "Skipping apt-get update."
fi
}
# Checks if packages are installed and installs them if not
check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@"
fi
} }
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
@ -183,7 +162,8 @@ export DEBIAN_FRONTEND=noninteractive
# Install curl, apt-transport-https, curl, gpg, or dirmngr, git if missing # Install curl, apt-transport-https, curl, gpg, or dirmngr, git if missing
check_packages curl ca-certificates apt-transport-https dirmngr gnupg2 check_packages curl ca-certificates apt-transport-https dirmngr gnupg2
if ! type git > /dev/null 2>&1; then if ! type git > /dev/null 2>&1; then
check_packages git apt_get_update_if_needed
apt-get -y install --no-install-recommends git
fi fi
# Soft version matching # Soft version matching
@ -196,19 +176,11 @@ fi
# Install the GitHub CLI # Install the GitHub CLI
echo "Downloading github CLI..." echo "Downloading github CLI..."
# Import key safely (new method rather than deprecated apt-key approach) and install
if [ "${INSTALL_DIRECTLY_FROM_GITHUB_RELEASE}" = "true" ]; then . /etc/os-release
install_deb_using_github receive_gpg_keys GITHUB_CLI_ARCHIVE_GPG_KEY /usr/share/keyrings/githubcli-archive-keyring.gpg
else echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list
# Import key safely (new method rather than deprecated apt-key approach) and install apt-get update
. /etc/os-release apt-get -y install "gh${version_suffix}"
receive_gpg_keys GITHUB_CLI_ARCHIVE_GPG_KEY /usr/share/keyrings/githubcli-archive-keyring.gpg rm -rf "/tmp/gh/gnupg"
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list echo "Done!"
apt-get update
apt-get -y install "gh${version_suffix}"
rm -rf "/tmp/gh/gnupg"
echo "Done!"
fi
# Clean up
rm -rf /var/lib/apt/lists/*

View file

@ -1,7 +0,0 @@
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@ Installs Go and common Go utilities. Auto-detects latest version and installs ne
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/go:1": {} "ghcr.io/devcontainers/features/go:1": {
"version": "latest"
}
} }
``` ```
@ -16,21 +18,7 @@ Installs Go and common Go utilities. Auto-detects latest version and installs ne
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select or enter a Go version to install | string | latest | | version | Select or enter a Go version to install | string | latest |
| golangciLintVersion | Version of golangci-lint to install | string | latest |
## Customizations
### VS Code Extensions
- `golang.Go`
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---

View file

@ -1,8 +1,7 @@
{ {
"id": "go", "id": "go",
"version": "1.2.2", "version": "1.0.0",
"name": "Go", "name": "Go",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/go",
"description": "Installs Go and common Go utilities. Auto-detects latest version and installs needed dependencies.", "description": "Installs Go and common Go utilities. Auto-detects latest version and installs needed dependencies.",
"options": { "options": {
"version": { "version": {
@ -10,38 +9,25 @@
"proposals": [ "proposals": [
"latest", "latest",
"none", "none",
"1.21", "1.19",
"1.20" "1.18"
], ],
"default": "latest", "default": "latest",
"description": "Select or enter a Go version to install" "description": "Select or enter a Go version to install"
},
"golangciLintVersion": {
"type": "string",
"default": "latest",
"description": "Version of golangci-lint to install"
} }
}, },
"init": true, "init": true,
"customizations": { "extensions": [
"vscode": { "golang.Go"
"extensions": [ ],
"golang.Go"
]
}
},
"containerEnv": { "containerEnv": {
"GOROOT": "/usr/local/go", "GOPATH": "/usr/local/go",
"GOPATH": "/go", "PATH": "${GOPATH}/bin:${PATH}"
"PATH": "/usr/local/go/bin:/go/bin:${PATH}"
}, },
"capAdd": [ "capAdd": [
"SYS_PTRACE" "SYS_PTRACE"
], ],
"securityOpt": [ "securityOpt": [
"seccomp=unconfined" "seccomp=unconfined"
],
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
] ]
} }

View file

@ -7,22 +7,19 @@
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/go.md # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/go.md
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The VS Code and Codespaces Teams
TARGET_GO_VERSION="${VERSION:-"latest"}" TARGET_GO_VERSION=${VERSION:-"latest"}
GOLANGCILINT_VERSION="${GOLANGCILINTVERSION:-"latest"}"
TARGET_GOROOT="${TARGET_GOROOT:-"/usr/local/go"}" TARGET_GOROOT=${TARGET_GOROOT:-"/usr/local/go"}
TARGET_GOPATH="${TARGET_GOPATH:-"/go"}" TARGET_GOPATH=${TARGET_GOPATH:-"/go"}
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" USERNAME=${USERNAME:-"automatic"}
INSTALL_GO_TOOLS="${INSTALL_GO_TOOLS:-"true"}" UPDATE_RC=${UPDATE_RC:-"true"}
INSTALL_GO_TOOLS=${INSTALL_GO_TOOLS:-"true"}
# https://www.google.com/linuxrepositories/ # https://www.google.com/linuxrepositories/
GO_GPG_KEY_URI="https://dl.google.com/linux/linux_signing_key.pub" GO_GPG_KEY_URI="https://dl.google.com/linux/linux_signing_key.pub"
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
@ -50,6 +47,17 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root USERNAME=root
fi fi
updaterc() {
if [ "${UPDATE_RC}" = "true" ]; then
echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..."
if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then
echo -e "$1" >> /etc/bash.bashrc
fi
if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then
echo -e "$1" >> /etc/zsh/zshrc
fi
fi
}
# Figure out correct version of a three part version number is not passed # Figure out correct version of a three part version number is not passed
find_version_from_git_tags() { find_version_from_git_tags() {
local variable_name=$1 local variable_name=$1
@ -84,18 +92,36 @@ find_version_from_git_tags() {
echo "${variable_name}=${!variable_name}" echo "${variable_name}=${!variable_name}"
} }
apt_get_update() # Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Function to run apt-get if needed
apt_get_update_if_needed()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..." echo "Running apt-get update..."
apt-get update -y apt-get update
else
echo "Skipping apt-get update."
fi fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -105,7 +131,8 @@ export DEBIAN_FRONTEND=noninteractive
# Install curl, tar, git, other dependencies if missing # Install curl, tar, git, other dependencies if missing
check_packages curl ca-certificates gnupg2 tar g++ gcc libc6-dev make pkg-config check_packages curl ca-certificates gnupg2 tar g++ gcc libc6-dev make pkg-config
if ! type git > /dev/null 2>&1; then if ! type git > /dev/null 2>&1; then
check_packages git apt_get_update_if_needed
apt-get -y install --no-install-recommends git
fi fi
# Get closest match for version number specified # Get closest match for version number specified
@ -126,13 +153,13 @@ if ! cat /etc/group | grep -e "^golang:" > /dev/null 2>&1; then
groupadd -r golang groupadd -r golang
fi fi
usermod -a -G golang "${USERNAME}" usermod -a -G golang "${USERNAME}"
mkdir -p "${TARGET_GOROOT}" "${TARGET_GOPATH}" mkdir -p "${TARGET_GOROOT}" "${TARGET_GOPATH}"
if [ "${TARGET_GO_VERSION}" != "none" ] && ! type go > /dev/null 2>&1; then
if [[ "${TARGET_GO_VERSION}" != "none" ]] && [[ "$(go version)" != *"${TARGET_GO_VERSION}"* ]]; then # Use a temporary locaiton for gpg keys to avoid polluting image
# Use a temporary location for gpg keys to avoid polluting image
export GNUPGHOME="/tmp/tmp-gnupg" export GNUPGHOME="/tmp/tmp-gnupg"
mkdir -p ${GNUPGHOME} mkdir -p ${GNUPGHOME}
chmod 700 ${GNUPGHOME} chmod 700 ${GNUPGHOME}
get_common_setting GO_GPG_KEY_URI
curl -sSL -o /tmp/tmp-gnupg/golang_key "${GO_GPG_KEY_URI}" curl -sSL -o /tmp/tmp-gnupg/golang_key "${GO_GPG_KEY_URI}"
gpg -q --import /tmp/tmp-gnupg/golang_key gpg -q --import /tmp/tmp-gnupg/golang_key
echo "Downloading Go ${TARGET_GO_VERSION}..." echo "Downloading Go ${TARGET_GO_VERSION}..."
@ -171,22 +198,20 @@ if [[ "${TARGET_GO_VERSION}" != "none" ]] && [[ "$(go version)" != *"${TARGET_GO
tar -xzf /tmp/go.tar.gz -C "${TARGET_GOROOT}" --strip-components=1 tar -xzf /tmp/go.tar.gz -C "${TARGET_GOROOT}" --strip-components=1
rm -rf /tmp/go.tar.gz /tmp/go.tar.gz.asc /tmp/tmp-gnupg rm -rf /tmp/go.tar.gz /tmp/go.tar.gz.asc /tmp/tmp-gnupg
else else
echo "(!) Go is already installed with version ${TARGET_GO_VERSION}. Skipping." echo "Go already installed. Skipping."
fi fi
# Install Go tools that are isImportant && !replacedByGopls based on # Install Go tools that are isImportant && !replacedByGopls based on
# https://github.com/golang/vscode-go/blob/v0.38.0/src/goToolsInformation.ts # https://github.com/golang/vscode-go/blob/v0.31.1/src/goToolsInformation.ts
GO_TOOLS="\ GO_TOOLS="\
golang.org/x/tools/gopls@latest \ golang.org/x/tools/gopls@latest \
honnef.co/go/tools/cmd/staticcheck@latest \ honnef.co/go/tools/cmd/staticcheck@latest \
golang.org/x/lint/golint@latest \ golang.org/x/lint/golint@latest \
github.com/mgechev/revive@latest \ github.com/mgechev/revive@latest \
github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest \
github.com/ramya-rao-a/go-outline@latest \
github.com/go-delve/delve/cmd/dlv@latest \ github.com/go-delve/delve/cmd/dlv@latest \
github.com/fatih/gomodifytags@latest \ github.com/golangci/golangci-lint/cmd/golangci-lint@latest"
github.com/haya14busa/goplay/cmd/goplay@latest \
github.com/cweill/gotests/gotests@latest \
github.com/josharian/impl@latest"
if [ "${INSTALL_GO_TOOLS}" = "true" ]; then if [ "${INSTALL_GO_TOOLS}" = "true" ]; then
echo "Installing common Go tools..." echo "Installing common Go tools..."
export PATH=${TARGET_GOROOT}/bin:${PATH} export PATH=${TARGET_GOROOT}/bin:${PATH}
@ -206,30 +231,24 @@ if [ "${INSTALL_GO_TOOLS}" = "true" ]; then
(echo "${GO_TOOLS}" | xargs -n 1 go ${go_install_command} -v )2>&1 | tee -a /usr/local/etc/vscode-dev-containers/go.log (echo "${GO_TOOLS}" | xargs -n 1 go ${go_install_command} -v )2>&1 | tee -a /usr/local/etc/vscode-dev-containers/go.log
# Move Go tools into path and clean up # Move Go tools into path and clean up
if [ -d /tmp/gotools/bin ]; then mv /tmp/gotools/bin/* ${TARGET_GOPATH}/bin/
mv /tmp/gotools/bin/* ${TARGET_GOPATH}/bin/
rm -rf /tmp/gotools
fi
# Install golangci-lint from precompiled binares rm -rf /tmp/gotools
if [ "$GOLANGCILINT_VERSION" = "latest" ] || [ "$GOLANGCILINT_VERSION" = "" ]; then
echo "Installing golangci-lint latest..."
curl -fsSL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
sh -s -- -b "${TARGET_GOPATH}/bin"
else
echo "Installing golangci-lint ${GOLANGCILINT_VERSION}..."
curl -fsSL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | \
sh -s -- -b "${TARGET_GOPATH}/bin" "v${GOLANGCILINT_VERSION}"
fi
fi fi
# Add GOPATH variable and bin directory into PATH in bashrc/zshrc files (unless disabled)
updaterc "$(cat << EOF
export GOPATH="${TARGET_GOPATH}"
if [[ "\${PATH}" != *"\${GOPATH}/bin"* ]]; then export PATH="\${PATH}:\${GOPATH}/bin"; fi
export GOROOT="${TARGET_GOROOT}"
if [[ "\${PATH}" != *"\${GOROOT}/bin"* ]]; then export PATH="\${PATH}:\${GOROOT}/bin"; fi
EOF
)"
chown -R "${USERNAME}:golang" "${TARGET_GOROOT}" "${TARGET_GOPATH}" chown -R "${USERNAME}:golang" "${TARGET_GOROOT}" "${TARGET_GOPATH}"
chmod -R g+r+w "${TARGET_GOROOT}" "${TARGET_GOPATH}" chmod -R g+r+w "${TARGET_GOROOT}" "${TARGET_GOPATH}"
find "${TARGET_GOROOT}" -type d -print0 | xargs -n 1 -0 chmod g+s find "${TARGET_GOROOT}" -type d | xargs -n 1 chmod g+s
find "${TARGET_GOPATH}" -type d -print0 | xargs -n 1 -0 chmod g+s find "${TARGET_GOPATH}" -type d | xargs -n 1 chmod g+s
# Clean up
rm -rf /var/lib/apt/lists/*
echo "Done!" echo "Done!"

View file

@ -1,7 +0,0 @@
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

View file

@ -7,7 +7,9 @@
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/hugo:1": {} "ghcr.io/devcontainers/features/hugo:1": {
"version": "latest"
}
} }
``` ```
@ -16,17 +18,9 @@
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select or enter a version. | string | latest | | version | Select or enter a version. | string | latest |
| extended | Install Hugo extended for SASS/SCSS changes | boolean | false |
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.
--- ---
_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/hugo/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ _Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/devcontainers/features/blob/main/src/hugo/devcontainer-feature.json). Add additional notes to a `NOTES.md`._

View file

@ -1,8 +1,7 @@
{ {
"id": "hugo", "id": "hugo",
"version": "1.1.2", "version": "1.0.0",
"name": "Hugo", "name": "Hugo",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/hugo",
"options": { "options": {
"version": { "version": {
"type": "string", "type": "string",
@ -11,18 +10,10 @@
], ],
"default": "latest", "default": "latest",
"description": "Select or enter a version." "description": "Select or enter a version."
},
"extended": {
"type": "boolean",
"default": false,
"description": "Install Hugo extended for SASS/SCSS changes"
} }
}, },
"containerEnv": { "containerEnv": {
"HUGO_DIR": "/usr/local/hugo", "HUGO_DIR": "/usr/local/hugo",
"PATH": "/usr/local/hugo/bin:${PATH}" "PATH": "${HUGO_DIR}/bin:${PATH}"
}, }
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
} }

View file

@ -7,18 +7,15 @@
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/hugo.md # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/hugo.md
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The VS Code and Codespaces Teams
VERSION="${VERSION:-"latest"}" VERSION=${VERSION:-"latest"}
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" USERNAME=${USERNAME:-"automatic"}
UPDATE_RC="${UPDATE_RC:-"true"}" UPDATE_RC=${UPDATE_RC:-"true"}
HUGO_DIR="${HUGO_DIR:-"/usr/local/hugo"}" HUGO_DIR=${HUGO_DIR:-"/usr/local/hugo"}
set -e set -e
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1 exit 1
@ -64,18 +61,21 @@ updaterc() {
fi fi
} }
apt_get_update() # Function to run apt-get if needed
apt_get_update_if_needed()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
echo "Running apt-get update..." echo "Running apt-get update..."
apt-get update -y apt-get update
else
echo "Skipping apt-get update."
fi fi
} }
# Checks if packages are installed and installs them if not # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update apt_get_update_if_needed
apt-get -y install --no-install-recommends "$@" apt-get -y install --no-install-recommends "$@"
fi fi
} }
@ -106,14 +106,7 @@ if ! hugo version &> /dev/null ; then
arch="64bit" arch="64bit"
fi fi
# Install extended version of hugo if desired hugo_filename="hugo_${VERSION}_Linux-${arch}.tar.gz"
if [ "${EXTENDED}" = "true" ]; then
extended="extended_"
else
extended=""
fi
hugo_filename="hugo_${extended}${VERSION}_Linux-${arch}.tar.gz"
curl -fsSLO --compressed "https://github.com/gohugoio/hugo/releases/download/v${VERSION}/${hugo_filename}" curl -fsSLO --compressed "https://github.com/gohugoio/hugo/releases/download/v${VERSION}/${hugo_filename}"
tar -xzf "$hugo_filename" -C "$installation_dir" tar -xzf "$hugo_filename" -C "$installation_dir"
@ -123,10 +116,7 @@ if ! hugo version &> /dev/null ; then
chown -R "${USERNAME}:hugo" "${HUGO_DIR}" chown -R "${USERNAME}:hugo" "${HUGO_DIR}"
chmod -R g+r+w "${HUGO_DIR}" chmod -R g+r+w "${HUGO_DIR}"
find "${HUGO_DIR}" -type d -print0 | xargs -n 1 -0 chmod g+s find "${HUGO_DIR}" -type d | xargs -n 1 chmod g+s
fi fi
# Clean up
rm -rf /var/lib/apt/lists/*
echo "Done!" echo "Done!"

View file

@ -1,10 +0,0 @@
## License
For the Java Feature from this repository, see [NOTICE.txt](https://github.com/devcontainers/features/tree/main/src/java/NOTICE.txt) for licensing information on JDK distributions.
## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.
`bash` is required to execute the `install.sh` script.

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more